aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--accounts/abi/bind/auth.go4
-rw-r--r--accounts/account_manager.go350
-rw-r--r--accounts/accounts.go155
-rw-r--r--accounts/accounts_test.go224
-rw-r--r--accounts/errors.go68
-rw-r--r--accounts/hd.go130
-rw-r--r--accounts/hd_test.go79
-rw-r--r--accounts/keystore/account_cache.go (renamed from accounts/addrcache.go)91
-rw-r--r--accounts/keystore/account_cache_test.go (renamed from accounts/addrcache_test.go)146
-rw-r--r--accounts/keystore/key.go (renamed from accounts/key.go)11
-rw-r--r--accounts/keystore/keystore.go494
-rw-r--r--accounts/keystore/keystore_passphrase.go (renamed from accounts/key_store_passphrase.go)2
-rw-r--r--accounts/keystore/keystore_passphrase_test.go (renamed from accounts/key_store_passphrase_test.go)2
-rw-r--r--accounts/keystore/keystore_plain.go (renamed from accounts/key_store_plain.go)2
-rw-r--r--accounts/keystore/keystore_plain_test.go (renamed from accounts/key_store_test.go)30
-rw-r--r--accounts/keystore/keystore_test.go365
-rw-r--r--accounts/keystore/keystore_wallet.go139
-rw-r--r--accounts/keystore/presale.go (renamed from accounts/presale.go)11
-rw-r--r--accounts/keystore/testdata/dupes/1 (renamed from accounts/testdata/dupes/1)0
-rw-r--r--accounts/keystore/testdata/dupes/2 (renamed from accounts/testdata/dupes/2)0
-rw-r--r--accounts/keystore/testdata/dupes/foo (renamed from accounts/testdata/dupes/foo)0
-rw-r--r--accounts/keystore/testdata/keystore/.hiddenfile (renamed from accounts/testdata/keystore/.hiddenfile)0
-rw-r--r--accounts/keystore/testdata/keystore/README (renamed from accounts/testdata/keystore/README)0
-rw-r--r--accounts/keystore/testdata/keystore/UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8 (renamed from accounts/testdata/keystore/UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8)0
-rw-r--r--accounts/keystore/testdata/keystore/aaa (renamed from accounts/testdata/keystore/aaa)0
-rw-r--r--accounts/keystore/testdata/keystore/empty (renamed from accounts/testdata/keystore/empty)0
-rw-r--r--accounts/keystore/testdata/keystore/foo/fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e (renamed from accounts/testdata/keystore/foo/fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e)0
-rw-r--r--accounts/keystore/testdata/keystore/garbage (renamed from accounts/testdata/keystore/garbage)bin300 -> 300 bytes
-rw-r--r--accounts/keystore/testdata/keystore/no-address (renamed from accounts/testdata/keystore/no-address)0
-rw-r--r--accounts/keystore/testdata/keystore/zero (renamed from accounts/testdata/keystore/zero)0
-rw-r--r--accounts/keystore/testdata/keystore/zzz (renamed from accounts/testdata/keystore/zzz)0
-rw-r--r--accounts/keystore/testdata/v1/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e (renamed from accounts/testdata/v1/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e)0
-rw-r--r--accounts/keystore/testdata/v1_test_vector.json (renamed from accounts/testdata/v1_test_vector.json)0
-rw-r--r--accounts/keystore/testdata/v3_test_vector.json (renamed from accounts/testdata/v3_test_vector.json)0
-rw-r--r--accounts/keystore/testdata/very-light-scrypt.json (renamed from accounts/testdata/very-light-scrypt.json)0
-rw-r--r--accounts/keystore/watch.go (renamed from accounts/watch.go)6
-rw-r--r--accounts/keystore/watch_fallback.go (renamed from accounts/watch_fallback.go)8
-rw-r--r--accounts/manager.go198
-rw-r--r--accounts/url.go79
-rw-r--r--accounts/usbwallet/ledger_hub.go209
-rw-r--r--accounts/usbwallet/ledger_wallet.go945
-rw-r--r--accounts/usbwallet/usbwallet.go29
-rw-r--r--accounts/usbwallet/usbwallet_ios.go38
-rw-r--r--build/ci.go8
-rw-r--r--cmd/geth/accountcmd.go49
-rw-r--r--cmd/geth/accountcmd_test.go30
-rw-r--r--cmd/geth/main.go47
-rw-r--r--cmd/gethrpctest/main.go8
-rw-r--r--cmd/swarm/main.go22
-rw-r--r--cmd/utils/flags.go20
-rw-r--r--eth/backend.go14
-rw-r--r--internal/ethapi/api.go180
-rw-r--r--internal/guide/guide_test.go35
-rw-r--r--internal/web3ext/web3ext.go12
-rw-r--r--miner/worker.go7
-rw-r--r--mobile/accounts.go103
-rw-r--r--mobile/android_test.go34
-rw-r--r--mobile/types.go5
-rw-r--r--node/config.go32
-rw-r--r--vendor.conf1
-rw-r--r--vendor/github.com/karalabe/gousb/.gitignore1
-rw-r--r--vendor/github.com/karalabe/gousb/.travis.yml12
-rw-r--r--vendor/github.com/karalabe/gousb/LICENSE202
-rw-r--r--vendor/github.com/karalabe/gousb/README.md47
-rw-r--r--vendor/github.com/karalabe/gousb/appveyor.yml34
-rw-r--r--vendor/github.com/karalabe/gousb/internal/libusb/AUTHORS89
-rw-r--r--vendor/github.com/karalabe/gousb/internal/libusb/COPYING504
-rw-r--r--vendor/github.com/karalabe/gousb/internal/libusb/libusb/config.h3
-rw-r--r--vendor/github.com/karalabe/gousb/internal/libusb/libusb/core.c2523
-rw-r--r--vendor/github.com/karalabe/gousb/internal/libusb/libusb/descriptor.c1191
-rw-r--r--vendor/github.com/karalabe/gousb/internal/libusb/libusb/hotplug.c350
-rw-r--r--vendor/github.com/karalabe/gousb/internal/libusb/libusb/hotplug.h90
-rw-r--r--vendor/github.com/karalabe/gousb/internal/libusb/libusb/io.c2819
-rw-r--r--vendor/github.com/karalabe/gousb/internal/libusb/libusb/libusb.h2008
-rw-r--r--vendor/github.com/karalabe/gousb/internal/libusb/libusb/libusbi.h1149
-rw-r--r--vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/darwin_usb.c2094
-rw-r--r--vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/darwin_usb.h164
-rw-r--r--vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/haiku_pollfs.cpp367
-rw-r--r--vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/haiku_usb.h112
-rw-r--r--vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/haiku_usb_backend.cpp517
-rw-r--r--vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/haiku_usb_raw.cpp250
-rw-r--r--vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/haiku_usb_raw.h180
-rw-r--r--vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/linux_netlink.c400
-rw-r--r--vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/linux_udev.c311
-rw-r--r--vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/linux_usbfs.c2738
-rw-r--r--vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/linux_usbfs.h193
-rw-r--r--vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/netbsd_usb.c677
-rw-r--r--vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/openbsd_usb.c771
-rw-r--r--vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/poll_posix.c53
-rw-r--r--vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/poll_posix.h11
-rw-r--r--vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/poll_windows.c728
-rw-r--r--vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/poll_windows.h131
-rw-r--r--vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/sunos_usb.c1292
-rw-r--r--vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/sunos_usb.h74
-rw-r--r--vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/threads_posix.c79
-rw-r--r--vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/threads_posix.h55
-rw-r--r--vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/threads_windows.c259
-rw-r--r--vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/threads_windows.h76
-rw-r--r--vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/wince_usb.c899
-rw-r--r--vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/wince_usb.h126
-rw-r--r--vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/windows_common.h124
-rw-r--r--vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/windows_nt_common.c591
-rw-r--r--vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/windows_nt_common.h63
-rw-r--r--vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/windows_usbdk.c905
-rw-r--r--vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/windows_usbdk.h146
-rw-r--r--vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/windows_winusb.c4290
-rw-r--r--vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/windows_winusb.h937
-rw-r--r--vendor/github.com/karalabe/gousb/internal/libusb/libusb/strerror.c202
-rw-r--r--vendor/github.com/karalabe/gousb/internal/libusb/libusb/sync.c327
-rw-r--r--vendor/github.com/karalabe/gousb/internal/libusb/libusb/version.h18
-rw-r--r--vendor/github.com/karalabe/gousb/internal/libusb/libusb/version_nano.h1
-rw-r--r--vendor/github.com/karalabe/gousb/usb/config.go153
-rw-r--r--vendor/github.com/karalabe/gousb/usb/constants.go183
-rw-r--r--vendor/github.com/karalabe/gousb/usb/debug.go36
-rw-r--r--vendor/github.com/karalabe/gousb/usb/descriptor.go77
-rw-r--r--vendor/github.com/karalabe/gousb/usb/device.go295
-rw-r--r--vendor/github.com/karalabe/gousb/usb/endpoint.go100
-rw-r--r--vendor/github.com/karalabe/gousb/usb/error.go92
-rw-r--r--vendor/github.com/karalabe/gousb/usb/iso.c79
-rw-r--r--vendor/github.com/karalabe/gousb/usb/iso.go148
-rw-r--r--vendor/github.com/karalabe/gousb/usb/misc.go47
-rw-r--r--vendor/github.com/karalabe/gousb/usb/usb.go178
122 files changed, 36084 insertions, 909 deletions
diff --git a/accounts/abi/bind/auth.go b/accounts/abi/bind/auth.go
index dbb235c14..e6bb0c3b5 100644
--- a/accounts/abi/bind/auth.go
+++ b/accounts/abi/bind/auth.go
@@ -22,7 +22,7 @@ import (
"io"
"io/ioutil"
- "github.com/ethereum/go-ethereum/accounts"
+ "github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
@@ -35,7 +35,7 @@ func NewTransactor(keyin io.Reader, passphrase string) (*TransactOpts, error) {
if err != nil {
return nil, err
}
- key, err := accounts.DecryptKey(json, passphrase)
+ key, err := keystore.DecryptKey(json, passphrase)
if err != nil {
return nil, err
}
diff --git a/accounts/account_manager.go b/accounts/account_manager.go
deleted file mode 100644
index 01dd62e25..000000000
--- a/accounts/account_manager.go
+++ /dev/null
@@ -1,350 +0,0 @@
-// Copyright 2015 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum 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 go-ethereum 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 go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
-
-// Package accounts implements encrypted storage of secp256k1 private keys.
-//
-// Keys are stored as encrypted JSON files according to the Web3 Secret Storage specification.
-// See https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition for more information.
-package accounts
-
-import (
- "crypto/ecdsa"
- crand "crypto/rand"
- "encoding/json"
- "errors"
- "fmt"
- "os"
- "path/filepath"
- "runtime"
- "sync"
- "time"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/crypto"
-)
-
-var (
- ErrLocked = errors.New("account is locked")
- ErrNoMatch = errors.New("no key for given address or file")
- ErrDecrypt = errors.New("could not decrypt key with given passphrase")
-)
-
-// Account represents a stored key.
-// When used as an argument, it selects a unique key file to act on.
-type Account struct {
- Address common.Address // Ethereum account address derived from the key
-
- // File contains the key file name.
- // When Acccount is used as an argument to select a key, File can be left blank to
- // select just by address or set to the basename or absolute path of a file in the key
- // directory. Accounts returned by Manager will always contain an absolute path.
- File string
-}
-
-func (acc *Account) MarshalJSON() ([]byte, error) {
- return []byte(`"` + acc.Address.Hex() + `"`), nil
-}
-
-func (acc *Account) UnmarshalJSON(raw []byte) error {
- return json.Unmarshal(raw, &acc.Address)
-}
-
-// Manager manages a key storage directory on disk.
-type Manager struct {
- cache *addrCache
- keyStore keyStore
- mu sync.RWMutex
- unlocked map[common.Address]*unlocked
-}
-
-type unlocked struct {
- *Key
- abort chan struct{}
-}
-
-// NewManager creates a manager for the given directory.
-func NewManager(keydir string, scryptN, scryptP int) *Manager {
- keydir, _ = filepath.Abs(keydir)
- am := &Manager{keyStore: &keyStorePassphrase{keydir, scryptN, scryptP}}
- am.init(keydir)
- return am
-}
-
-// NewPlaintextManager creates a manager for the given directory.
-// Deprecated: Use NewManager.
-func NewPlaintextManager(keydir string) *Manager {
- keydir, _ = filepath.Abs(keydir)
- am := &Manager{keyStore: &keyStorePlain{keydir}}
- am.init(keydir)
- return am
-}
-
-func (am *Manager) init(keydir string) {
- am.unlocked = make(map[common.Address]*unlocked)
- am.cache = newAddrCache(keydir)
- // TODO: In order for this finalizer to work, there must be no references
- // to am. addrCache doesn't keep a reference but unlocked keys do,
- // so the finalizer will not trigger until all timed unlocks have expired.
- runtime.SetFinalizer(am, func(m *Manager) {
- m.cache.close()
- })
-}
-
-// HasAddress reports whether a key with the given address is present.
-func (am *Manager) HasAddress(addr common.Address) bool {
- return am.cache.hasAddress(addr)
-}
-
-// Accounts returns all key files present in the directory.
-func (am *Manager) Accounts() []Account {
- return am.cache.accounts()
-}
-
-// Delete deletes the key matched by account if the passphrase is correct.
-// If the account contains no filename, the address must match a unique key.
-func (am *Manager) Delete(a Account, passphrase string) error {
- // Decrypting the key isn't really necessary, but we do
- // it anyway to check the password and zero out the key
- // immediately afterwards.
- a, key, err := am.getDecryptedKey(a, passphrase)
- if key != nil {
- zeroKey(key.PrivateKey)
- }
- if err != nil {
- return err
- }
- // The order is crucial here. The key is dropped from the
- // cache after the file is gone so that a reload happening in
- // between won't insert it into the cache again.
- err = os.Remove(a.File)
- if err == nil {
- am.cache.delete(a)
- }
- return err
-}
-
-// Sign calculates a ECDSA signature for the given hash. The produced signature
-// is in the [R || S || V] format where V is 0 or 1.
-func (am *Manager) Sign(addr common.Address, hash []byte) ([]byte, error) {
- am.mu.RLock()
- defer am.mu.RUnlock()
-
- unlockedKey, found := am.unlocked[addr]
- if !found {
- return nil, ErrLocked
- }
- return crypto.Sign(hash, unlockedKey.PrivateKey)
-}
-
-// SignWithPassphrase signs hash if the private key matching the given address
-// can be decrypted with the given passphrase. The produced signature is in the
-// [R || S || V] format where V is 0 or 1.
-func (am *Manager) SignWithPassphrase(a Account, passphrase string, hash []byte) (signature []byte, err error) {
- _, key, err := am.getDecryptedKey(a, passphrase)
- if err != nil {
- return nil, err
- }
- defer zeroKey(key.PrivateKey)
- return crypto.Sign(hash, key.PrivateKey)
-}
-
-// Unlock unlocks the given account indefinitely.
-func (am *Manager) Unlock(a Account, passphrase string) error {
- return am.TimedUnlock(a, passphrase, 0)
-}
-
-// Lock removes the private key with the given address from memory.
-func (am *Manager) Lock(addr common.Address) error {
- am.mu.Lock()
- if unl, found := am.unlocked[addr]; found {
- am.mu.Unlock()
- am.expire(addr, unl, time.Duration(0)*time.Nanosecond)
- } else {
- am.mu.Unlock()
- }
- return nil
-}
-
-// TimedUnlock unlocks the given account with the passphrase. The account
-// stays unlocked for the duration of timeout. A timeout of 0 unlocks the account
-// until the program exits. The account must match a unique key file.
-//
-// If the account address is already unlocked for a duration, TimedUnlock extends or
-// shortens the active unlock timeout. If the address was previously unlocked
-// indefinitely the timeout is not altered.
-func (am *Manager) TimedUnlock(a Account, passphrase string, timeout time.Duration) error {
- a, key, err := am.getDecryptedKey(a, passphrase)
- if err != nil {
- return err
- }
-
- am.mu.Lock()
- defer am.mu.Unlock()
- u, found := am.unlocked[a.Address]
- if found {
- if u.abort == nil {
- // The address was unlocked indefinitely, so unlocking
- // it with a timeout would be confusing.
- zeroKey(key.PrivateKey)
- return nil
- } else {
- // Terminate the expire goroutine and replace it below.
- close(u.abort)
- }
- }
- if timeout > 0 {
- u = &unlocked{Key: key, abort: make(chan struct{})}
- go am.expire(a.Address, u, timeout)
- } else {
- u = &unlocked{Key: key}
- }
- am.unlocked[a.Address] = u
- return nil
-}
-
-// Find resolves the given account into a unique entry in the keystore.
-func (am *Manager) Find(a Account) (Account, error) {
- am.cache.maybeReload()
- am.cache.mu.Lock()
- a, err := am.cache.find(a)
- am.cache.mu.Unlock()
- return a, err
-}
-
-func (am *Manager) getDecryptedKey(a Account, auth string) (Account, *Key, error) {
- a, err := am.Find(a)
- if err != nil {
- return a, nil, err
- }
- key, err := am.keyStore.GetKey(a.Address, a.File, auth)
- return a, key, err
-}
-
-func (am *Manager) expire(addr common.Address, u *unlocked, timeout time.Duration) {
- t := time.NewTimer(timeout)
- defer t.Stop()
- select {
- case <-u.abort:
- // just quit
- case <-t.C:
- am.mu.Lock()
- // only drop if it's still the same key instance that dropLater
- // was launched with. we can check that using pointer equality
- // because the map stores a new pointer every time the key is
- // unlocked.
- if am.unlocked[addr] == u {
- zeroKey(u.PrivateKey)
- delete(am.unlocked, addr)
- }
- am.mu.Unlock()
- }
-}
-
-// NewAccount generates a new key and stores it into the key directory,
-// encrypting it with the passphrase.
-func (am *Manager) NewAccount(passphrase string) (Account, error) {
- _, account, err := storeNewKey(am.keyStore, crand.Reader, passphrase)
- if err != nil {
- return Account{}, err
- }
- // Add the account to the cache immediately rather
- // than waiting for file system notifications to pick it up.
- am.cache.add(account)
- return account, nil
-}
-
-// AccountByIndex returns the ith account.
-func (am *Manager) AccountByIndex(i int) (Account, error) {
- accounts := am.Accounts()
- if i < 0 || i >= len(accounts) {
- return Account{}, fmt.Errorf("account index %d out of range [0, %d]", i, len(accounts)-1)
- }
- return accounts[i], nil
-}
-
-// Export exports as a JSON key, encrypted with newPassphrase.
-func (am *Manager) Export(a Account, passphrase, newPassphrase string) (keyJSON []byte, err error) {
- _, key, err := am.getDecryptedKey(a, passphrase)
- if err != nil {
- return nil, err
- }
- var N, P int
- if store, ok := am.keyStore.(*keyStorePassphrase); ok {
- N, P = store.scryptN, store.scryptP
- } else {
- N, P = StandardScryptN, StandardScryptP
- }
- return EncryptKey(key, newPassphrase, N, P)
-}
-
-// Import stores the given encrypted JSON key into the key directory.
-func (am *Manager) Import(keyJSON []byte, passphrase, newPassphrase string) (Account, error) {
- key, err := DecryptKey(keyJSON, passphrase)
- if key != nil && key.PrivateKey != nil {
- defer zeroKey(key.PrivateKey)
- }
- if err != nil {
- return Account{}, err
- }
- return am.importKey(key, newPassphrase)
-}
-
-// ImportECDSA stores the given key into the key directory, encrypting it with the passphrase.
-func (am *Manager) ImportECDSA(priv *ecdsa.PrivateKey, passphrase string) (Account, error) {
- key := newKeyFromECDSA(priv)
- if am.cache.hasAddress(key.Address) {
- return Account{}, fmt.Errorf("account already exists")
- }
-
- return am.importKey(key, passphrase)
-}
-
-func (am *Manager) importKey(key *Key, passphrase string) (Account, error) {
- a := Account{Address: key.Address, File: am.keyStore.JoinPath(keyFileName(key.Address))}
- if err := am.keyStore.StoreKey(a.File, key, passphrase); err != nil {
- return Account{}, err
- }
- am.cache.add(a)
- return a, nil
-}
-
-// Update changes the passphrase of an existing account.
-func (am *Manager) Update(a Account, passphrase, newPassphrase string) error {
- a, key, err := am.getDecryptedKey(a, passphrase)
- if err != nil {
- return err
- }
- return am.keyStore.StoreKey(a.File, key, newPassphrase)
-}
-
-// ImportPreSaleKey decrypts the given Ethereum presale wallet and stores
-// a key file in the key directory. The key file is encrypted with the same passphrase.
-func (am *Manager) ImportPreSaleKey(keyJSON []byte, passphrase string) (Account, error) {
- a, _, err := importPreSaleKey(am.keyStore, keyJSON, passphrase)
- if err != nil {
- return a, err
- }
- am.cache.add(a)
- return a, nil
-}
-
-// zeroKey zeroes a private key in memory.
-func zeroKey(k *ecdsa.PrivateKey) {
- b := k.D.Bits()
- for i := range b {
- b[i] = 0
- }
-}
diff --git a/accounts/accounts.go b/accounts/accounts.go
new file mode 100644
index 000000000..640de5220
--- /dev/null
+++ b/accounts/accounts.go
@@ -0,0 +1,155 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum 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 go-ethereum 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 go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+// Package accounts implements high level Ethereum account management.
+package accounts
+
+import (
+ "math/big"
+
+ ethereum "github.com/ethereum/go-ethereum"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/event"
+)
+
+// Account represents an Ethereum account located at a specific location defined
+// by the optional URL field.
+type Account struct {
+ Address common.Address `json:"address"` // Ethereum account address derived from the key
+ URL URL `json:"url"` // Optional resource locator within a backend
+}
+
+// Wallet represents a software or hardware wallet that might contain one or more
+// accounts (derived from the same seed).
+type Wallet interface {
+ // URL retrieves the canonical path under which this wallet is reachable. It is
+ // user by upper layers to define a sorting order over all wallets from multiple
+ // backends.
+ URL() URL
+
+ // Status returns a textual status to aid the user in the current state of the
+ // wallet.
+ Status() string
+
+ // Open initializes access to a wallet instance. It is not meant to unlock or
+ // decrypt account keys, rather simply to establish a connection to hardware
+ // wallets and/or to access derivation seeds.
+ //
+ // The passphrase parameter may or may not be used by the implementation of a
+ // particular wallet instance. The reason there is no passwordless open method
+ // is to strive towards a uniform wallet handling, oblivious to the different
+ // backend providers.
+ //
+ // Please note, if you open a wallet, you must close it to release any allocated
+ // resources (especially important when working with hardware wallets).
+ Open(passphrase string) error
+
+ // Close releases any resources held by an open wallet instance.
+ Close() error
+
+ // Accounts retrieves the list of signing accounts the wallet is currently aware
+ // of. For hierarchical deterministic wallets, the list will not be exhaustive,
+ // rather only contain the accounts explicitly pinned during account derivation.
+ Accounts() []Account
+
+ // Contains returns whether an account is part of this particular wallet or not.
+ Contains(account Account) bool
+
+ // Derive attempts to explicitly derive a hierarchical deterministic account at
+ // the specified derivation path. If requested, the derived account will be added
+ // to the wallet's tracked account list.
+ Derive(path DerivationPath, pin bool) (Account, error)
+
+ // SelfDerive sets a base account derivation path from which the wallet attempts
+ // to discover non zero accounts and automatically add them to list of tracked
+ // accounts.
+ //
+ // Note, self derivaton will increment the last component of the specified path
+ // opposed to decending into a child path to allow discovering accounts starting
+ // from non zero components.
+ //
+ // You can disable automatic account discovery by calling SelfDerive with a nil
+ // chain state reader.
+ SelfDerive(base DerivationPath, chain ethereum.ChainStateReader)
+
+ // SignHash requests the wallet to sign the given hash.
+ //
+ // It looks up the account specified either solely via its address contained within,
+ // or optionally with the aid of any location metadata from the embedded URL field.
+ //
+ // If the wallet requires additional authentication to sign the request (e.g.
+ // a password to decrypt the account, or a PIN code o verify the transaction),
+ // an AuthNeededError instance will be returned, containing infos for the user
+ // about which fields or actions are needed. The user may retry by providing
+ // the needed details via SignHashWithPassphrase, or by other means (e.g. unlock
+ // the account in a keystore).
+ SignHash(account Account, hash []byte) ([]byte, error)
+
+ // SignTx requests the wallet to sign the given transaction.
+ //
+ // It looks up the account specified either solely via its address contained within,
+ // or optionally with the aid of any location metadata from the embedded URL field.
+ //
+ // If the wallet requires additional authentication to sign the request (e.g.
+ // a password to decrypt the account, or a PIN code o verify the transaction),
+ // an AuthNeededError instance will be returned, containing infos for the user
+ // about which fields or actions are needed. The user may retry by providing
+ // the needed details via SignTxWithPassphrase, or by other means (e.g. unlock
+ // the account in a keystore).
+ SignTx(account Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error)
+
+ // SignHashWithPassphrase requests the wallet to sign the given hash with the
+ // given passphrase as extra authentication information.
+ //
+ // It looks up the account specified either solely via its address contained within,
+ // or optionally with the aid of any location metadata from the embedded URL field.
+ SignHashWithPassphrase(account Account, passphrase string, hash []byte) ([]byte, error)
+
+ // SignTxWithPassphrase requests the wallet to sign the given transaction, with the
+ // given passphrase as extra authentication information.
+ //
+ // It looks up the account specified either solely via its address contained within,
+ // or optionally with the aid of any location metadata from the embedded URL field.
+ SignTxWithPassphrase(account Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error)
+}
+
+// Backend is a "wallet provider" that may contain a batch of accounts they can
+// sign transactions with and upon request, do so.
+type Backend interface {
+ // Wallets retrieves the list of wallets the backend is currently aware of.
+ //
+ // The returned wallets are not opened by default. For software HD wallets this
+ // means that no base seeds are decrypted, and for hardware wallets that no actual
+ // connection is established.
+ //
+ // The resulting wallet list will be sorted alphabetically based on its internal
+ // URL assigned by the backend. Since wallets (especially hardware) may come and
+ // go, the same wallet might appear at a different positions in the list during
+ // subsequent retrievals.
+ Wallets() []Wallet
+
+ // Subscribe creates an async subscription to receive notifications when the
+ // backend detects the arrival or departure of a wallet.
+ Subscribe(sink chan<- WalletEvent) event.Subscription
+}
+
+// WalletEvent is an event fired by an account backend when a wallet arrival or
+// departure is detected.
+type WalletEvent struct {
+ Wallet Wallet // Wallet instance arrived or departed
+ Arrive bool // Whether the wallet was added or removed
+}
diff --git a/accounts/accounts_test.go b/accounts/accounts_test.go
deleted file mode 100644
index b3ab87d50..000000000
--- a/accounts/accounts_test.go
+++ /dev/null
@@ -1,224 +0,0 @@
-// Copyright 2015 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum 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 go-ethereum 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 go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
-
-package accounts
-
-import (
- "io/ioutil"
- "os"
- "runtime"
- "strings"
- "testing"
- "time"
-
- "github.com/ethereum/go-ethereum/common"
-)
-
-var testSigData = make([]byte, 32)
-
-func TestManager(t *testing.T) {
- dir, am := tmpManager(t, true)
- defer os.RemoveAll(dir)
-
- a, err := am.NewAccount("foo")
- if err != nil {
- t.Fatal(err)
- }
- if !strings.HasPrefix(a.File, dir) {
- t.Errorf("account file %s doesn't have dir prefix", a.File)
- }
- stat, err := os.Stat(a.File)
- if err != nil {
- t.Fatalf("account file %s doesn't exist (%v)", a.File, err)
- }
- if runtime.GOOS != "windows" && stat.Mode() != 0600 {
- t.Fatalf("account file has wrong mode: got %o, want %o", stat.Mode(), 0600)
- }
- if !am.HasAddress(a.Address) {
- t.Errorf("HasAccount(%x) should've returned true", a.Address)
- }
- if err := am.Update(a, "foo", "bar"); err != nil {
- t.Errorf("Update error: %v", err)
- }
- if err := am.Delete(a, "bar"); err != nil {
- t.Errorf("Delete error: %v", err)
- }
- if common.FileExist(a.File) {
- t.Errorf("account file %s should be gone after Delete", a.File)
- }
- if am.HasAddress(a.Address) {
- t.Errorf("HasAccount(%x) should've returned true after Delete", a.Address)
- }
-}
-
-func TestSign(t *testing.T) {
- dir, am := tmpManager(t, true)
- defer os.RemoveAll(dir)
-
- pass := "" // not used but required by API
- a1, err := am.NewAccount(pass)
- if err != nil {
- t.Fatal(err)
- }
- if err := am.Unlock(a1, ""); err != nil {
- t.Fatal(err)
- }
- if _, err := am.Sign(a1.Address, testSigData); err != nil {
- t.Fatal(err)
- }
-}
-
-func TestSignWithPassphrase(t *testing.T) {
- dir, am := tmpManager(t, true)
- defer os.RemoveAll(dir)
-
- pass := "passwd"
- acc, err := am.NewAccount(pass)
- if err != nil {
- t.Fatal(err)
- }
-
- if _, unlocked := am.unlocked[acc.Address]; unlocked {
- t.Fatal("expected account to be locked")
- }
-
- _, err = am.SignWithPassphrase(acc, pass, testSigData)
- if err != nil {
- t.Fatal(err)
- }
-
- if _, unlocked := am.unlocked[acc.Address]; unlocked {
- t.Fatal("expected account to be locked")
- }
-
- if _, err = am.SignWithPassphrase(acc, "invalid passwd", testSigData); err == nil {
- t.Fatal("expected SignHash to fail with invalid password")
- }
-}
-
-func TestTimedUnlock(t *testing.T) {
- dir, am := tmpManager(t, true)
- defer os.RemoveAll(dir)
-
- pass := "foo"
- a1, err := am.NewAccount(pass)
- if err != nil {
- t.Fatal(err)
- }
-
- // Signing without passphrase fails because account is locked
- _, err = am.Sign(a1.Address, testSigData)
- if err != ErrLocked {
- t.Fatal("Signing should've failed with ErrLocked before unlocking, got ", err)
- }
-
- // Signing with passphrase works
- if err = am.TimedUnlock(a1, pass, 100*time.Millisecond); err != nil {
- t.Fatal(err)
- }
-
- // Signing without passphrase works because account is temp unlocked
- _, err = am.Sign(a1.Address, testSigData)
- if err != nil {
- t.Fatal("Signing shouldn't return an error after unlocking, got ", err)
- }
-
- // Signing fails again after automatic locking
- time.Sleep(250 * time.Millisecond)
- _, err = am.Sign(a1.Address, testSigData)
- if err != ErrLocked {
- t.Fatal("Signing should've failed with ErrLocked timeout expired, got ", err)
- }
-}
-
-func TestOverrideUnlock(t *testing.T) {
- dir, am := tmpManager(t, false)
- defer os.RemoveAll(dir)
-
- pass := "foo"
- a1, err := am.NewAccount(pass)
- if err != nil {
- t.Fatal(err)
- }
-
- // Unlock indefinitely.
- if err = am.TimedUnlock(a1, pass, 5*time.Minute); err != nil {
- t.Fatal(err)
- }
-
- // Signing without passphrase works because account is temp unlocked
- _, err = am.Sign(a1.Address, testSigData)
- if err != nil {
- t.Fatal("Signing shouldn't return an error after unlocking, got ", err)
- }
-
- // reset unlock to a shorter period, invalidates the previous unlock
- if err = am.TimedUnlock(a1, pass, 100*time.Millisecond); err != nil {
- t.Fatal(err)
- }
-
- // Signing without passphrase still works because account is temp unlocked
- _, err = am.Sign(a1.Address, testSigData)
- if err != nil {
- t.Fatal("Signing shouldn't return an error after unlocking, got ", err)
- }
-
- // Signing fails again after automatic locking
- time.Sleep(250 * time.Millisecond)
- _, err = am.Sign(a1.Address, testSigData)
- if err != ErrLocked {
- t.Fatal("Signing should've failed with ErrLocked timeout expired, got ", err)
- }
-}
-
-// This test should fail under -race if signing races the expiration goroutine.
-func TestSignRace(t *testing.T) {
- dir, am := tmpManager(t, false)
- defer os.RemoveAll(dir)
-
- // Create a test account.
- a1, err := am.NewAccount("")
- if err != nil {
- t.Fatal("could not create the test account", err)
- }
-
- if err := am.TimedUnlock(a1, "", 15*time.Millisecond); err != nil {
- t.Fatal("could not unlock the test account", err)
- }
- end := time.Now().Add(500 * time.Millisecond)
- for time.Now().Before(end) {
- if _, err := am.Sign(a1.Address, testSigData); err == ErrLocked {
- return
- } else if err != nil {
- t.Errorf("Sign error: %v", err)
- return
- }
- time.Sleep(1 * time.Millisecond)
- }
- t.Errorf("Account did not lock within the timeout")
-}
-
-func tmpManager(t *testing.T, encrypted bool) (string, *Manager) {
- d, err := ioutil.TempDir("", "eth-keystore-test")
- if err != nil {
- t.Fatal(err)
- }
- new := NewPlaintextManager
- if encrypted {
- new = func(kd string) *Manager { return NewManager(kd, veryLightScryptN, veryLightScryptP) }
- }
- return d, new(d)
-}
diff --git a/accounts/errors.go b/accounts/errors.go
new file mode 100644
index 000000000..9ecc1eafd
--- /dev/null
+++ b/accounts/errors.go
@@ -0,0 +1,68 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum 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 go-ethereum 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 go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package accounts
+
+import (
+ "errors"
+ "fmt"
+)
+
+// ErrUnknownAccount is returned for any requested operation for which no backend
+// provides the specified account.
+var ErrUnknownAccount = errors.New("unknown account")
+
+// ErrUnknownWallet is returned for any requested operation for which no backend
+// provides the specified wallet.
+var ErrUnknownWallet = errors.New("unknown wallet")
+
+// ErrNotSupported is returned when an operation is requested from an account
+// backend that it does not support.
+var ErrNotSupported = errors.New("not supported")
+
+// ErrInvalidPassphrase is returned when a decryption operation receives a bad
+// passphrase.
+var ErrInvalidPassphrase = errors.New("invalid passphrase")
+
+// ErrWalletAlreadyOpen is returned if a wallet is attempted to be opened the
+// secodn time.
+var ErrWalletAlreadyOpen = errors.New("wallet already open")
+
+// ErrWalletClosed is returned if a wallet is attempted to be opened the
+// secodn time.
+var ErrWalletClosed = errors.New("wallet closed")
+
+// AuthNeededError is returned by backends for signing requests where the user
+// is required to provide further authentication before signing can succeed.
+//
+// This usually means either that a password needs to be supplied, or perhaps a
+// one time PIN code displayed by some hardware device.
+type AuthNeededError struct {
+ Needed string // Extra authentication the user needs to provide
+}
+
+// NewAuthNeededError creates a new authentication error with the extra details
+// about the needed fields set.
+func NewAuthNeededError(needed string) error {
+ return &AuthNeededError{
+ Needed: needed,
+ }
+}
+
+// Error implements the standard error interfacel.
+func (err *AuthNeededError) Error() string {
+ return fmt.Sprintf("authentication needed: %s", err.Needed)
+}
diff --git a/accounts/hd.go b/accounts/hd.go
new file mode 100644
index 000000000..e8bc191af
--- /dev/null
+++ b/accounts/hd.go
@@ -0,0 +1,130 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum 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 go-ethereum 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 go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package accounts
+
+import (
+ "errors"
+ "fmt"
+ "math"
+ "math/big"
+ "strings"
+)
+
+// DefaultRootDerivationPath is the root path to which custom derivation endpoints
+// are appended. As such, the first account will be at m/44'/60'/0'/0, the second
+// at m/44'/60'/0'/1, etc.
+var DefaultRootDerivationPath = DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0}
+
+// DefaultBaseDerivationPath is the base path from which custom derivation endpoints
+// are incremented. As such, the first account will be at m/44'/60'/0'/0, the second
+// at m/44'/60'/0'/1, etc.
+var DefaultBaseDerivationPath = DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0}
+
+// DerivationPath represents the computer friendly version of a hierarchical
+// deterministic wallet account derivaion path.
+//
+// The BIP-32 spec https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
+// defines derivation paths to be of the form:
+//
+// m / purpose' / coin_type' / account' / change / address_index
+//
+// The BIP-44 spec https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki
+// defines that the `purpose` be 44' (or 0x8000002C) for crypto currencies, and
+// SLIP-44 https://github.com/satoshilabs/slips/blob/master/slip-0044.md assigns
+// the `coin_type` 60' (or 0x8000003C) to Ethereum.
+//
+// The root path for Ethereum is m/44'/60'/0'/0 according to the specification
+// from https://github.com/ethereum/EIPs/issues/84, albeit it's not set in stone
+// yet whether accounts should increment the last component or the children of
+// that. We will go with the simpler approach of incrementing the last component.
+type DerivationPath []uint32
+
+// ParseDerivationPath converts a user specified derivation path string to the
+// internal binary representation.
+//
+// Full derivation paths need to start with the `m/` prefix, relative derivation
+// paths (which will get appended to the default root path) must not have prefixes
+// in front of the first element. Whitespace is ignored.
+func ParseDerivationPath(path string) (DerivationPath, error) {
+ var result DerivationPath
+
+ // Handle absolute or relative paths
+ components := strings.Split(path, "/")
+ switch {
+ case len(components) == 0:
+ return nil, errors.New("empty derivation path")
+
+ case strings.TrimSpace(components[0]) == "":
+ return nil, errors.New("ambiguous path: use 'm/' prefix for absolute paths, or no leading '/' for relative ones")
+
+ case strings.TrimSpace(components[0]) == "m":
+ components = components[1:]
+
+ default:
+ result = append(result, DefaultRootDerivationPath...)
+ }
+ // All remaining components are relative, append one by one
+ if len(components) == 0 {
+ return nil, errors.New("empty derivation path") // Empty relative paths
+ }
+ for _, component := range components {
+ // Ignore any user added whitespace
+ component = strings.TrimSpace(component)
+ var value uint32
+
+ // Handle hardened paths
+ if strings.HasSuffix(component, "'") {
+ value = 0x80000000
+ component = strings.TrimSpace(strings.TrimSuffix(component, "'"))
+ }
+ // Handle the non hardened component
+ bigval, ok := new(big.Int).SetString(component, 0)
+ if !ok {
+ return nil, fmt.Errorf("invalid component: %s", component)
+ }
+ max := math.MaxUint32 - value
+ if bigval.Sign() < 0 || bigval.Cmp(big.NewInt(int64(max))) > 0 {
+ if value == 0 {
+ return nil, fmt.Errorf("component %v out of allowed range [0, %d]", bigval, max)
+ }
+ return nil, fmt.Errorf("component %v out of allowed hardened range [0, %d]", bigval, max)
+ }
+ value += uint32(bigval.Uint64())
+
+ // Append and repeat
+ result = append(result, value)
+ }
+ return result, nil
+}
+
+// String implements the stringer interface, converting a binary derivation path
+// to its canonical representation.
+func (path DerivationPath) String() string {
+ result := "m"
+ for _, component := range path {
+ var hardened bool
+ if component >= 0x80000000 {
+ component -= 0x80000000
+ hardened = true
+ }
+ result = fmt.Sprintf("%s/%d", result, component)
+ if hardened {
+ result += "'"
+ }
+ }
+ return result
+}
diff --git a/accounts/hd_test.go b/accounts/hd_test.go
new file mode 100644
index 000000000..83ec34adb
--- /dev/null
+++ b/accounts/hd_test.go
@@ -0,0 +1,79 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum 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 go-ethereum 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 go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package accounts
+
+import (
+ "reflect"
+ "testing"
+)
+
+// Tests that HD derivation paths can be correctly parsed into our internal binary
+// representation.
+func TestHDPathParsing(t *testing.T) {
+ tests := []struct {
+ input string
+ output DerivationPath
+ }{
+ // Plain absolute derivation paths
+ {"m/44'/60'/0'/0", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0}},
+ {"m/44'/60'/0'/128", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 128}},
+ {"m/44'/60'/0'/0'", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0x80000000 + 0}},
+ {"m/44'/60'/0'/128'", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0x80000000 + 128}},
+ {"m/2147483692/2147483708/2147483648/0", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0}},
+ {"m/2147483692/2147483708/2147483648/2147483648", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0x80000000 + 0}},
+
+ // Plain relative derivation paths
+ {"0", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0}},
+ {"128", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 128}},
+ {"0'", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0x80000000 + 0}},
+ {"128'", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0x80000000 + 128}},
+ {"2147483648", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0x80000000 + 0}},
+
+ // Hexadecimal absolute derivation paths
+ {"m/0x2C'/0x3c'/0x00'/0x00", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0}},
+ {"m/0x2C'/0x3c'/0x00'/0x80", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 128}},
+ {"m/0x2C'/0x3c'/0x00'/0x00'", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0x80000000 + 0}},
+ {"m/0x2C'/0x3c'/0x00'/0x80'", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0x80000000 + 128}},
+ {"m/0x8000002C/0x8000003c/0x80000000/0x00", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0}},
+ {"m/0x8000002C/0x8000003c/0x80000000/0x80000000", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0x80000000 + 0}},
+
+ // Hexadecimal relative derivation paths
+ {"0x00", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0}},
+ {"0x80", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 128}},
+ {"0x00'", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0x80000000 + 0}},
+ {"0x80'", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0x80000000 + 128}},
+ {"0x80000000", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0x80000000 + 0}},
+
+ // Weird inputs just to ensure they work
+ {" m / 44 '\n/\n 60 \n\n\t' /\n0 ' /\t\t 0", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0}},
+
+ // Invaid derivation paths
+ {"", nil}, // Empty relative derivation path
+ {"m", nil}, // Empty absolute derivation path
+ {"m/", nil}, // Missing last derivation component
+ {"/44'/60'/0'/0", nil}, // Absolute path without m prefix, might be user error
+ {"m/2147483648'", nil}, // Overflows 32 bit integer
+ {"m/-1'", nil}, // Cannot contain negative number
+ }
+ for i, tt := range tests {
+ if path, err := ParseDerivationPath(tt.input); !reflect.DeepEqual(path, tt.output) {
+ t.Errorf("test %d: parse mismatch: have %v (%v), want %v", i, path, err, tt.output)
+ } else if path == nil && err == nil {
+ t.Errorf("test %d: nil path and error: %v", i, err)
+ }
+ }
+}
diff --git a/accounts/addrcache.go b/accounts/keystore/account_cache.go
index a99f23606..3fae3ef5b 100644
--- a/accounts/addrcache.go
+++ b/accounts/keystore/account_cache.go
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
-package accounts
+package keystore
import (
"bufio"
@@ -28,6 +28,7 @@ import (
"sync"
"time"
+ "github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
@@ -38,23 +39,23 @@ import (
// exist yet, the code will attempt to create a watcher at most this often.
const minReloadInterval = 2 * time.Second
-type accountsByFile []Account
+type accountsByURL []accounts.Account
-func (s accountsByFile) Len() int { return len(s) }
-func (s accountsByFile) Less(i, j int) bool { return s[i].File < s[j].File }
-func (s accountsByFile) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+func (s accountsByURL) Len() int { return len(s) }
+func (s accountsByURL) Less(i, j int) bool { return s[i].URL.Cmp(s[j].URL) < 0 }
+func (s accountsByURL) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
// AmbiguousAddrError is returned when attempting to unlock
// an address for which more than one file exists.
type AmbiguousAddrError struct {
Addr common.Address
- Matches []Account
+ Matches []accounts.Account
}
func (err *AmbiguousAddrError) Error() string {
files := ""
for i, a := range err.Matches {
- files += a.File
+ files += a.URL.Path
if i < len(err.Matches)-1 {
files += ", "
}
@@ -62,60 +63,63 @@ func (err *AmbiguousAddrError) Error() string {
return fmt.Sprintf("multiple keys match address (%s)", files)
}
-// addrCache is a live index of all accounts in the keystore.
-type addrCache struct {
+// accountCache is a live index of all accounts in the keystore.
+type accountCache struct {
keydir string
watcher *watcher
mu sync.Mutex
- all accountsByFile
- byAddr map[common.Address][]Account
+ all accountsByURL
+ byAddr map[common.Address][]accounts.Account
throttle *time.Timer
+ notify chan struct{}
}
-func newAddrCache(keydir string) *addrCache {
- ac := &addrCache{
+func newAccountCache(keydir string) (*accountCache, chan struct{}) {
+ ac := &accountCache{
keydir: keydir,
- byAddr: make(map[common.Address][]Account),
+ byAddr: make(map[common.Address][]accounts.Account),
+ notify: make(chan struct{}, 1),
}
ac.watcher = newWatcher(ac)
- return ac
+ return ac, ac.notify
}
-func (ac *addrCache) accounts() []Account {
+func (ac *accountCache) accounts() []accounts.Account {
ac.maybeReload()
ac.mu.Lock()
defer ac.mu.Unlock()
- cpy := make([]Account, len(ac.all))
+ cpy := make([]accounts.Account, len(ac.all))
copy(cpy, ac.all)
return cpy
}
-func (ac *addrCache) hasAddress(addr common.Address) bool {
+func (ac *accountCache) hasAddress(addr common.Address) bool {
ac.maybeReload()
ac.mu.Lock()
defer ac.mu.Unlock()
return len(ac.byAddr[addr]) > 0
}
-func (ac *addrCache) add(newAccount Account) {
+func (ac *accountCache) add(newAccount accounts.Account) {
ac.mu.Lock()
defer ac.mu.Unlock()
- i := sort.Search(len(ac.all), func(i int) bool { return ac.all[i].File >= newAccount.File })
+ i := sort.Search(len(ac.all), func(i int) bool { return ac.all[i].URL.Cmp(newAccount.URL) >= 0 })
if i < len(ac.all) && ac.all[i] == newAccount {
return
}
// newAccount is not in the cache.
- ac.all = append(ac.all, Account{})
+ ac.all = append(ac.all, accounts.Account{})
copy(ac.all[i+1:], ac.all[i:])
ac.all[i] = newAccount
ac.byAddr[newAccount.Address] = append(ac.byAddr[newAccount.Address], newAccount)
}
// note: removed needs to be unique here (i.e. both File and Address must be set).
-func (ac *addrCache) delete(removed Account) {
+func (ac *accountCache) delete(removed accounts.Account) {
ac.mu.Lock()
defer ac.mu.Unlock()
+
ac.all = removeAccount(ac.all, removed)
if ba := removeAccount(ac.byAddr[removed.Address], removed); len(ba) == 0 {
delete(ac.byAddr, removed.Address)
@@ -124,7 +128,7 @@ func (ac *addrCache) delete(removed Account) {
}
}
-func removeAccount(slice []Account, elem Account) []Account {
+func removeAccount(slice []accounts.Account, elem accounts.Account) []accounts.Account {
for i := range slice {
if slice[i] == elem {
return append(slice[:i], slice[i+1:]...)
@@ -134,43 +138,44 @@ func removeAccount(slice []Account, elem Account) []Account {
}
// find returns the cached account for address if there is a unique match.
-// The exact matching rules are explained by the documentation of Account.
+// The exact matching rules are explained by the documentation of accounts.Account.
// Callers must hold ac.mu.
-func (ac *addrCache) find(a Account) (Account, error) {
+func (ac *accountCache) find(a accounts.Account) (accounts.Account, error) {
// Limit search to address candidates if possible.
matches := ac.all
if (a.Address != common.Address{}) {
matches = ac.byAddr[a.Address]
}
- if a.File != "" {
+ if a.URL.Path != "" {
// If only the basename is specified, complete the path.
- if !strings.ContainsRune(a.File, filepath.Separator) {
- a.File = filepath.Join(ac.keydir, a.File)
+ if !strings.ContainsRune(a.URL.Path, filepath.Separator) {
+ a.URL.Path = filepath.Join(ac.keydir, a.URL.Path)
}
for i := range matches {
- if matches[i].File == a.File {
+ if matches[i].URL == a.URL {
return matches[i], nil
}
}
if (a.Address == common.Address{}) {
- return Account{}, ErrNoMatch
+ return accounts.Account{}, ErrNoMatch
}
}
switch len(matches) {
case 1:
return matches[0], nil
case 0:
- return Account{}, ErrNoMatch
+ return accounts.Account{}, ErrNoMatch
default:
- err := &AmbiguousAddrError{Addr: a.Address, Matches: make([]Account, len(matches))}
+ err := &AmbiguousAddrError{Addr: a.Address, Matches: make([]accounts.Account, len(matches))}
copy(err.Matches, matches)
- return Account{}, err
+ return accounts.Account{}, err
}
}
-func (ac *addrCache) maybeReload() {
+func (ac *accountCache) maybeReload() {
ac.mu.Lock()
defer ac.mu.Unlock()
+
if ac.watcher.running {
return // A watcher is running and will keep the cache up-to-date.
}
@@ -188,18 +193,22 @@ func (ac *addrCache) maybeReload() {
ac.throttle.Reset(minReloadInterval)
}
-func (ac *addrCache) close() {
+func (ac *accountCache) close() {
ac.mu.Lock()
ac.watcher.close()
if ac.throttle != nil {
ac.throttle.Stop()
}
+ if ac.notify != nil {
+ close(ac.notify)
+ ac.notify = nil
+ }
ac.mu.Unlock()
}
// reload caches addresses of existing accounts.
// Callers must hold ac.mu.
-func (ac *addrCache) reload() {
+func (ac *accountCache) reload() {
accounts, err := ac.scan()
if err != nil && glog.V(logger.Debug) {
glog.Errorf("can't load keys: %v", err)
@@ -212,10 +221,14 @@ func (ac *addrCache) reload() {
for _, a := range accounts {
ac.byAddr[a.Address] = append(ac.byAddr[a.Address], a)
}
+ select {
+ case ac.notify <- struct{}{}:
+ default:
+ }
glog.V(logger.Debug).Infof("reloaded keys, cache has %d accounts", len(ac.all))
}
-func (ac *addrCache) scan() ([]Account, error) {
+func (ac *accountCache) scan() ([]accounts.Account, error) {
files, err := ioutil.ReadDir(ac.keydir)
if err != nil {
return nil, err
@@ -223,7 +236,7 @@ func (ac *addrCache) scan() ([]Account, error) {
var (
buf = new(bufio.Reader)
- addrs []Account
+ addrs []accounts.Account
keyJSON struct {
Address string `json:"address"`
}
@@ -250,7 +263,7 @@ func (ac *addrCache) scan() ([]Account, error) {
case (addr == common.Address{}):
glog.V(logger.Debug).Infof("can't decode key %s: missing or zero address", path)
default:
- addrs = append(addrs, Account{Address: addr, File: path})
+ addrs = append(addrs, accounts.Account{Address: addr, URL: accounts.URL{Scheme: KeyStoreScheme, Path: path}})
}
fd.Close()
}
diff --git a/accounts/addrcache_test.go b/accounts/keystore/account_cache_test.go
index e5f08cffc..554196321 100644
--- a/accounts/addrcache_test.go
+++ b/accounts/keystore/account_cache_test.go
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
-package accounts
+package keystore
import (
"fmt"
@@ -28,23 +28,24 @@ import (
"github.com/cespare/cp"
"github.com/davecgh/go-spew/spew"
+ "github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
)
var (
cachetestDir, _ = filepath.Abs(filepath.Join("testdata", "keystore"))
- cachetestAccounts = []Account{
+ cachetestAccounts = []accounts.Account{
{
Address: common.HexToAddress("7ef5a6135f1fd6a02593eedc869c6d41d934aef8"),
- File: filepath.Join(cachetestDir, "UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8"),
+ URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(cachetestDir, "UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8")},
},
{
Address: common.HexToAddress("f466859ead1932d743d622cb74fc058882e8648a"),
- File: filepath.Join(cachetestDir, "aaa"),
+ URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(cachetestDir, "aaa")},
},
{
Address: common.HexToAddress("289d485d9771714cce91d3393d764e1311907acc"),
- File: filepath.Join(cachetestDir, "zzz"),
+ URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(cachetestDir, "zzz")},
},
}
)
@@ -52,29 +53,36 @@ var (
func TestWatchNewFile(t *testing.T) {
t.Parallel()
- dir, am := tmpManager(t, false)
+ dir, ks := tmpKeyStore(t, false)
defer os.RemoveAll(dir)
// Ensure the watcher is started before adding any files.
- am.Accounts()
+ ks.Accounts()
time.Sleep(200 * time.Millisecond)
// Move in the files.
- wantAccounts := make([]Account, len(cachetestAccounts))
+ wantAccounts := make([]accounts.Account, len(cachetestAccounts))
for i := range cachetestAccounts {
- a := cachetestAccounts[i]
- a.File = filepath.Join(dir, filepath.Base(a.File))
- wantAccounts[i] = a
- if err := cp.CopyFile(a.File, cachetestAccounts[i].File); err != nil {
+ wantAccounts[i] = accounts.Account{
+ Address: cachetestAccounts[i].Address,
+ URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(dir, filepath.Base(cachetestAccounts[i].URL.Path))},
+ }
+ if err := cp.CopyFile(wantAccounts[i].URL.Path, cachetestAccounts[i].URL.Path); err != nil {
t.Fatal(err)
}
}
- // am should see the accounts.
- var list []Account
+ // ks should see the accounts.
+ var list []accounts.Account
for d := 200 * time.Millisecond; d < 5*time.Second; d *= 2 {
- list = am.Accounts()
+ list = ks.Accounts()
if reflect.DeepEqual(list, wantAccounts) {
+ // ks should have also received change notifications
+ select {
+ case <-ks.changes:
+ default:
+ t.Fatalf("wasn't notified of new accounts")
+ }
return
}
time.Sleep(d)
@@ -85,12 +93,12 @@ func TestWatchNewFile(t *testing.T) {
func TestWatchNoDir(t *testing.T) {
t.Parallel()
- // Create am but not the directory that it watches.
+ // Create ks but not the directory that it watches.
rand.Seed(time.Now().UnixNano())
dir := filepath.Join(os.TempDir(), fmt.Sprintf("eth-keystore-watch-test-%d-%d", os.Getpid(), rand.Int()))
- am := NewManager(dir, LightScryptN, LightScryptP)
+ ks := NewKeyStore(dir, LightScryptN, LightScryptP)
- list := am.Accounts()
+ list := ks.Accounts()
if len(list) > 0 {
t.Error("initial account list not empty:", list)
}
@@ -100,16 +108,22 @@ func TestWatchNoDir(t *testing.T) {
os.MkdirAll(dir, 0700)
defer os.RemoveAll(dir)
file := filepath.Join(dir, "aaa")
- if err := cp.CopyFile(file, cachetestAccounts[0].File); err != nil {
+ if err := cp.CopyFile(file, cachetestAccounts[0].URL.Path); err != nil {
t.Fatal(err)
}
- // am should see the account.
- wantAccounts := []Account{cachetestAccounts[0]}
- wantAccounts[0].File = file
+ // ks should see the account.
+ wantAccounts := []accounts.Account{cachetestAccounts[0]}
+ wantAccounts[0].URL = accounts.URL{Scheme: KeyStoreScheme, Path: file}
for d := 200 * time.Millisecond; d < 8*time.Second; d *= 2 {
- list = am.Accounts()
+ list = ks.Accounts()
if reflect.DeepEqual(list, wantAccounts) {
+ // ks should have also received change notifications
+ select {
+ case <-ks.changes:
+ default:
+ t.Fatalf("wasn't notified of new accounts")
+ }
return
}
time.Sleep(d)
@@ -118,7 +132,7 @@ func TestWatchNoDir(t *testing.T) {
}
func TestCacheInitialReload(t *testing.T) {
- cache := newAddrCache(cachetestDir)
+ cache, _ := newAccountCache(cachetestDir)
accounts := cache.accounts()
if !reflect.DeepEqual(accounts, cachetestAccounts) {
t.Fatalf("got initial accounts: %swant %s", spew.Sdump(accounts), spew.Sdump(cachetestAccounts))
@@ -126,55 +140,55 @@ func TestCacheInitialReload(t *testing.T) {
}
func TestCacheAddDeleteOrder(t *testing.T) {
- cache := newAddrCache("testdata/no-such-dir")
+ cache, _ := newAccountCache("testdata/no-such-dir")
cache.watcher.running = true // prevent unexpected reloads
- accounts := []Account{
+ accs := []accounts.Account{
{
Address: common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"),
- File: "-309830980",
+ URL: accounts.URL{Scheme: KeyStoreScheme, Path: "-309830980"},
},
{
Address: common.HexToAddress("2cac1adea150210703ba75ed097ddfe24e14f213"),
- File: "ggg",
+ URL: accounts.URL{Scheme: KeyStoreScheme, Path: "ggg"},
},
{
Address: common.HexToAddress("8bda78331c916a08481428e4b07c96d3e916d165"),
- File: "zzzzzz-the-very-last-one.keyXXX",
+ URL: accounts.URL{Scheme: KeyStoreScheme, Path: "zzzzzz-the-very-last-one.keyXXX"},
},
{
Address: common.HexToAddress("d49ff4eeb0b2686ed89c0fc0f2b6ea533ddbbd5e"),
- File: "SOMETHING.key",
+ URL: accounts.URL{Scheme: KeyStoreScheme, Path: "SOMETHING.key"},
},
{
Address: common.HexToAddress("7ef5a6135f1fd6a02593eedc869c6d41d934aef8"),
- File: "UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8",
+ URL: accounts.URL{Scheme: KeyStoreScheme, Path: "UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8"},
},
{
Address: common.HexToAddress("f466859ead1932d743d622cb74fc058882e8648a"),
- File: "aaa",
+ URL: accounts.URL{Scheme: KeyStoreScheme, Path: "aaa"},
},
{
Address: common.HexToAddress("289d485d9771714cce91d3393d764e1311907acc"),
- File: "zzz",
+ URL: accounts.URL{Scheme: KeyStoreScheme, Path: "zzz"},
},
}
- for _, a := range accounts {
+ for _, a := range accs {
cache.add(a)
}
// Add some of them twice to check that they don't get reinserted.
- cache.add(accounts[0])
- cache.add(accounts[2])
+ cache.add(accs[0])
+ cache.add(accs[2])
// Check that the account list is sorted by filename.
- wantAccounts := make([]Account, len(accounts))
- copy(wantAccounts, accounts)
- sort.Sort(accountsByFile(wantAccounts))
+ wantAccounts := make([]accounts.Account, len(accs))
+ copy(wantAccounts, accs)
+ sort.Sort(accountsByURL(wantAccounts))
list := cache.accounts()
if !reflect.DeepEqual(list, wantAccounts) {
- t.Fatalf("got accounts: %s\nwant %s", spew.Sdump(accounts), spew.Sdump(wantAccounts))
+ t.Fatalf("got accounts: %s\nwant %s", spew.Sdump(accs), spew.Sdump(wantAccounts))
}
- for _, a := range accounts {
+ for _, a := range accs {
if !cache.hasAddress(a.Address) {
t.Errorf("expected hasAccount(%x) to return true", a.Address)
}
@@ -184,13 +198,13 @@ func TestCacheAddDeleteOrder(t *testing.T) {
}
// Delete a few keys from the cache.
- for i := 0; i < len(accounts); i += 2 {
+ for i := 0; i < len(accs); i += 2 {
cache.delete(wantAccounts[i])
}
- cache.delete(Account{Address: common.HexToAddress("fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e"), File: "something"})
+ cache.delete(accounts.Account{Address: common.HexToAddress("fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e"), URL: accounts.URL{Scheme: KeyStoreScheme, Path: "something"}})
// Check content again after deletion.
- wantAccountsAfterDelete := []Account{
+ wantAccountsAfterDelete := []accounts.Account{
wantAccounts[1],
wantAccounts[3],
wantAccounts[5],
@@ -211,63 +225,63 @@ func TestCacheAddDeleteOrder(t *testing.T) {
func TestCacheFind(t *testing.T) {
dir := filepath.Join("testdata", "dir")
- cache := newAddrCache(dir)
+ cache, _ := newAccountCache(dir)
cache.watcher.running = true // prevent unexpected reloads
- accounts := []Account{
+ accs := []accounts.Account{
{
Address: common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"),
- File: filepath.Join(dir, "a.key"),
+ URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(dir, "a.key")},
},
{
Address: common.HexToAddress("2cac1adea150210703ba75ed097ddfe24e14f213"),
- File: filepath.Join(dir, "b.key"),
+ URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(dir, "b.key")},
},
{
Address: common.HexToAddress("d49ff4eeb0b2686ed89c0fc0f2b6ea533ddbbd5e"),
- File: filepath.Join(dir, "c.key"),
+ URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(dir, "c.key")},
},
{
Address: common.HexToAddress("d49ff4eeb0b2686ed89c0fc0f2b6ea533ddbbd5e"),
- File: filepath.Join(dir, "c2.key"),
+ URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(dir, "c2.key")},
},
}
- for _, a := range accounts {
+ for _, a := range accs {
cache.add(a)
}
- nomatchAccount := Account{
+ nomatchAccount := accounts.Account{
Address: common.HexToAddress("f466859ead1932d743d622cb74fc058882e8648a"),
- File: filepath.Join(dir, "something"),
+ URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(dir, "something")},
}
tests := []struct {
- Query Account
- WantResult Account
+ Query accounts.Account
+ WantResult accounts.Account
WantError error
}{
// by address
- {Query: Account{Address: accounts[0].Address}, WantResult: accounts[0]},
+ {Query: accounts.Account{Address: accs[0].Address}, WantResult: accs[0]},
// by file
- {Query: Account{File: accounts[0].File}, WantResult: accounts[0]},
+ {Query: accounts.Account{URL: accs[0].URL}, WantResult: accs[0]},
// by basename
- {Query: Account{File: filepath.Base(accounts[0].File)}, WantResult: accounts[0]},
+ {Query: accounts.Account{URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Base(accs[0].URL.Path)}}, WantResult: accs[0]},
// by file and address
- {Query: accounts[0], WantResult: accounts[0]},
+ {Query: accs[0], WantResult: accs[0]},
// ambiguous address, tie resolved by file
- {Query: accounts[2], WantResult: accounts[2]},
+ {Query: accs[2], WantResult: accs[2]},
// ambiguous address error
{
- Query: Account{Address: accounts[2].Address},
+ Query: accounts.Account{Address: accs[2].Address},
WantError: &AmbiguousAddrError{
- Addr: accounts[2].Address,
- Matches: []Account{accounts[2], accounts[3]},
+ Addr: accs[2].Address,
+ Matches: []accounts.Account{accs[2], accs[3]},
},
},
// no match error
{Query: nomatchAccount, WantError: ErrNoMatch},
- {Query: Account{File: nomatchAccount.File}, WantError: ErrNoMatch},
- {Query: Account{File: filepath.Base(nomatchAccount.File)}, WantError: ErrNoMatch},
- {Query: Account{Address: nomatchAccount.Address}, WantError: ErrNoMatch},
+ {Query: accounts.Account{URL: nomatchAccount.URL}, WantError: ErrNoMatch},
+ {Query: accounts.Account{URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Base(nomatchAccount.URL.Path)}}, WantError: ErrNoMatch},
+ {Query: accounts.Account{Address: nomatchAccount.Address}, WantError: ErrNoMatch},
}
for i, test := range tests {
a, err := cache.find(test.Query)
diff --git a/accounts/key.go b/accounts/keystore/key.go
index dbcb49dcf..e2bdf09fe 100644
--- a/accounts/key.go
+++ b/accounts/keystore/key.go
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
-package accounts
+package keystore
import (
"bytes"
@@ -29,6 +29,7 @@ import (
"strings"
"time"
+ "github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/secp256k1"
@@ -175,13 +176,13 @@ func newKey(rand io.Reader) (*Key, error) {
return newKeyFromECDSA(privateKeyECDSA), nil
}
-func storeNewKey(ks keyStore, rand io.Reader, auth string) (*Key, Account, error) {
+func storeNewKey(ks keyStore, rand io.Reader, auth string) (*Key, accounts.Account, error) {
key, err := newKey(rand)
if err != nil {
- return nil, Account{}, err
+ return nil, accounts.Account{}, err
}
- a := Account{Address: key.Address, File: ks.JoinPath(keyFileName(key.Address))}
- if err := ks.StoreKey(a.File, key, auth); err != nil {
+ a := accounts.Account{Address: key.Address, URL: accounts.URL{Scheme: KeyStoreScheme, Path: ks.JoinPath(keyFileName(key.Address))}}
+ if err := ks.StoreKey(a.URL.Path, key, auth); err != nil {
zeroKey(key.PrivateKey)
return nil, a, err
}
diff --git a/accounts/keystore/keystore.go b/accounts/keystore/keystore.go
new file mode 100644
index 000000000..a01ff17e3
--- /dev/null
+++ b/accounts/keystore/keystore.go
@@ -0,0 +1,494 @@
+// Copyright 2015 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum 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 go-ethereum 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 go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+// Package keystore implements encrypted storage of secp256k1 private keys.
+//
+// Keys are stored as encrypted JSON files according to the Web3 Secret Storage specification.
+// See https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition for more information.
+package keystore
+
+import (
+ "crypto/ecdsa"
+ crand "crypto/rand"
+ "errors"
+ "fmt"
+ "math/big"
+ "os"
+ "path/filepath"
+ "reflect"
+ "runtime"
+ "sync"
+ "time"
+
+ "github.com/ethereum/go-ethereum/accounts"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/event"
+)
+
+var (
+ ErrLocked = accounts.NewAuthNeededError("password or unlock")
+ ErrNoMatch = errors.New("no key for given address or file")
+ ErrDecrypt = errors.New("could not decrypt key with given passphrase")
+)
+
+// KeyStoreType is the reflect type of a keystore backend.
+var KeyStoreType = reflect.TypeOf(&KeyStore{})
+
+// KeyStoreScheme is the protocol scheme prefixing account and wallet URLs.
+var KeyStoreScheme = "keystore"
+
+// Maximum time between wallet refreshes (if filesystem notifications don't work).
+const walletRefreshCycle = 3 * time.Second
+
+// KeyStore manages a key storage directory on disk.
+type KeyStore struct {
+ storage keyStore // Storage backend, might be cleartext or encrypted
+ cache *accountCache // In-memory account cache over the filesystem storage
+ changes chan struct{} // Channel receiving change notifications from the cache
+ unlocked map[common.Address]*unlocked // Currently unlocked account (decrypted private keys)
+
+ wallets []accounts.Wallet // Wallet wrappers around the individual key files
+ updateFeed event.Feed // Event feed to notify wallet additions/removals
+ updateScope event.SubscriptionScope // Subscription scope tracking current live listeners
+ updating bool // Whether the event notification loop is running
+
+ mu sync.RWMutex
+}
+
+type unlocked struct {
+ *Key
+ abort chan struct{}
+}
+
+// NewKeyStore creates a keystore for the given directory.
+func NewKeyStore(keydir string, scryptN, scryptP int) *KeyStore {
+ keydir, _ = filepath.Abs(keydir)
+ ks := &KeyStore{storage: &keyStorePassphrase{keydir, scryptN, scryptP}}
+ ks.init(keydir)
+ return ks
+}
+
+// NewPlaintextKeyStore creates a keystore for the given directory.
+// Deprecated: Use NewKeyStore.
+func NewPlaintextKeyStore(keydir string) *KeyStore {
+ keydir, _ = filepath.Abs(keydir)
+ ks := &KeyStore{storage: &keyStorePlain{keydir}}
+ ks.init(keydir)
+ return ks
+}
+
+func (ks *KeyStore) init(keydir string) {
+ // Lock the mutex since the account cache might call back with events
+ ks.mu.Lock()
+ defer ks.mu.Unlock()
+
+ // Initialize the set of unlocked keys and the account cache
+ ks.unlocked = make(map[common.Address]*unlocked)
+ ks.cache, ks.changes = newAccountCache(keydir)
+
+ // TODO: In order for this finalizer to work, there must be no references
+ // to ks. addressCache doesn't keep a reference but unlocked keys do,
+ // so the finalizer will not trigger until all timed unlocks have expired.
+ runtime.SetFinalizer(ks, func(m *KeyStore) {
+ m.cache.close()
+ })
+ // Create the initial list of wallets from the cache
+ accs := ks.cache.accounts()
+ ks.wallets = make([]accounts.Wallet, len(accs))
+ for i := 0; i < len(accs); i++ {
+ ks.wallets[i] = &keystoreWallet{account: accs[i], keystore: ks}
+ }
+}
+
+// Wallets implements accounts.Backend, returning all single-key wallets from the
+// keystore directory.
+func (ks *KeyStore) Wallets() []accounts.Wallet {
+ // Make sure the list of wallets is in sync with the account cache
+ ks.refreshWallets()
+
+ ks.mu.RLock()
+ defer ks.mu.RUnlock()
+
+ cpy := make([]accounts.Wallet, len(ks.wallets))
+ copy(cpy, ks.wallets)
+ return cpy
+}
+
+// refreshWallets retrieves the current account list and based on that does any
+// necessary wallet refreshes.
+func (ks *KeyStore) refreshWallets() {
+ // Retrieve the current list of accounts
+ ks.mu.Lock()
+ accs := ks.cache.accounts()
+
+ // Transform the current list of wallets into the new one
+ wallets := make([]accounts.Wallet, 0, len(accs))
+ events := []accounts.WalletEvent{}
+
+ for _, account := range accs {
+ // Drop wallets while they were in front of the next account
+ for len(ks.wallets) > 0 && ks.wallets[0].URL().Cmp(account.URL) < 0 {
+ events = append(events, accounts.WalletEvent{Wallet: ks.wallets[0], Arrive: false})
+ ks.wallets = ks.wallets[1:]
+ }
+ // If there are no more wallets or the account is before the next, wrap new wallet
+ if len(ks.wallets) == 0 || ks.wallets[0].URL().Cmp(account.URL) > 0 {
+ wallet := &keystoreWallet{account: account, keystore: ks}
+
+ events = append(events, accounts.WalletEvent{Wallet: wallet, Arrive: true})
+ wallets = append(wallets, wallet)
+ continue
+ }
+ // If the account is the same as the first wallet, keep it
+ if ks.wallets[0].Accounts()[0] == account {
+ wallets = append(wallets, ks.wallets[0])
+ ks.wallets = ks.wallets[1:]
+ continue
+ }
+ }
+ // Drop any leftover wallets and set the new batch
+ for _, wallet := range ks.wallets {
+ events = append(events, accounts.WalletEvent{Wallet: wallet, Arrive: false})
+ }
+ ks.wallets = wallets
+ ks.mu.Unlock()
+
+ // Fire all wallet events and return
+ for _, event := range events {
+ ks.updateFeed.Send(event)
+ }
+}
+
+// Subscribe implements accounts.Backend, creating an async subscription to
+// receive notifications on the addition or removal of keystore wallets.
+func (ks *KeyStore) Subscribe(sink chan<- accounts.WalletEvent) event.Subscription {
+ // We need the mutex to reliably start/stop the update loop
+ ks.mu.Lock()
+ defer ks.mu.Unlock()
+
+ // Subscribe the caller and track the subscriber count
+ sub := ks.updateScope.Track(ks.updateFeed.Subscribe(sink))
+
+ // Subscribers require an active notification loop, start it
+ if !ks.updating {
+ ks.updating = true
+ go ks.updater()
+ }
+ return sub
+}
+
+// updater is responsible for maintaining an up-to-date list of wallets stored in
+// the keystore, and for firing wallet addition/removal events. It listens for
+// account change events from the underlying account cache, and also periodically
+// forces a manual refresh (only triggers for systems where the filesystem notifier
+// is not running).
+func (ks *KeyStore) updater() {
+ for {
+ // Wait for an account update or a refresh timeout
+ select {
+ case <-ks.changes:
+ case <-time.After(walletRefreshCycle):
+ }
+ // Run the wallet refresher
+ ks.refreshWallets()
+
+ // If all our subscribers left, stop the updater
+ ks.mu.Lock()
+ if ks.updateScope.Count() == 0 {
+ ks.updating = false
+ ks.mu.Unlock()
+ return
+ }
+ ks.mu.Unlock()
+ }
+}
+
+// HasAddress reports whether a key with the given address is present.
+func (ks *KeyStore) HasAddress(addr common.Address) bool {
+ return ks.cache.hasAddress(addr)
+}
+
+// Accounts returns all key files present in the directory.
+func (ks *KeyStore) Accounts() []accounts.Account {
+ return ks.cache.accounts()
+}
+
+// Delete deletes the key matched by account if the passphrase is correct.
+// If the account contains no filename, the address must match a unique key.
+func (ks *KeyStore) Delete(a accounts.Account, passphrase string) error {
+ // Decrypting the key isn't really necessary, but we do
+ // it anyway to check the password and zero out the key
+ // immediately afterwards.
+ a, key, err := ks.getDecryptedKey(a, passphrase)
+ if key != nil {
+ zeroKey(key.PrivateKey)
+ }
+ if err != nil {
+ return err
+ }
+ // The order is crucial here. The key is dropped from the
+ // cache after the file is gone so that a reload happening in
+ // between won't insert it into the cache again.
+ err = os.Remove(a.URL.Path)
+ if err == nil {
+ ks.cache.delete(a)
+ ks.refreshWallets()
+ }
+ return err
+}
+
+// SignHash calculates a ECDSA signature for the given hash. The produced
+// signature is in the [R || S || V] format where V is 0 or 1.
+func (ks *KeyStore) SignHash(a accounts.Account, hash []byte) ([]byte, error) {
+ // Look up the key to sign with and abort if it cannot be found
+ ks.mu.RLock()
+ defer ks.mu.RUnlock()
+
+ unlockedKey, found := ks.unlocked[a.Address]
+ if !found {
+ return nil, ErrLocked
+ }
+ // Sign the hash using plain ECDSA operations
+ return crypto.Sign(hash, unlockedKey.PrivateKey)
+}
+
+// SignTx signs the given transaction with the requested account.
+func (ks *KeyStore) SignTx(a accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
+ // Look up the key to sign with and abort if it cannot be found
+ ks.mu.RLock()
+ defer ks.mu.RUnlock()
+
+ unlockedKey, found := ks.unlocked[a.Address]
+ if !found {
+ return nil, ErrLocked
+ }
+ // Depending on the presence of the chain ID, sign with EIP155 or homestead
+ if chainID != nil {
+ return types.SignTx(tx, types.NewEIP155Signer(chainID), unlockedKey.PrivateKey)
+ }
+ return types.SignTx(tx, types.HomesteadSigner{}, unlockedKey.PrivateKey)
+}
+
+// SignHashWithPassphrase signs hash if the private key matching the given address
+// can be decrypted with the given passphrase. The produced signature is in the
+// [R || S || V] format where V is 0 or 1.
+func (ks *KeyStore) SignHashWithPassphrase(a accounts.Account, passphrase string, hash []byte) (signature []byte, err error) {
+ _, key, err := ks.getDecryptedKey(a, passphrase)
+ if err != nil {
+ return nil, err
+ }
+ defer zeroKey(key.PrivateKey)
+ return crypto.Sign(hash, key.PrivateKey)
+}
+
+// SignTxWithPassphrase signs the transaction if the private key matching the
+// given address can be decrypted with the given passphrase.
+func (ks *KeyStore) SignTxWithPassphrase(a accounts.Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
+ _, key, err := ks.getDecryptedKey(a, passphrase)
+ if err != nil {
+ return nil, err
+ }
+ defer zeroKey(key.PrivateKey)
+
+ // Depending on the presence of the chain ID, sign with EIP155 or homestead
+ if chainID != nil {
+ return types.SignTx(tx, types.NewEIP155Signer(chainID), key.PrivateKey)
+ }
+ return types.SignTx(tx, types.HomesteadSigner{}, key.PrivateKey)
+}
+
+// Unlock unlocks the given account indefinitely.
+func (ks *KeyStore) Unlock(a accounts.Account, passphrase string) error {
+ return ks.TimedUnlock(a, passphrase, 0)
+}
+
+// Lock removes the private key with the given address from memory.
+func (ks *KeyStore) Lock(addr common.Address) error {
+ ks.mu.Lock()
+ if unl, found := ks.unlocked[addr]; found {
+ ks.mu.Unlock()
+ ks.expire(addr, unl, time.Duration(0)*time.Nanosecond)
+ } else {
+ ks.mu.Unlock()
+ }
+ return nil
+}
+
+// TimedUnlock unlocks the given account with the passphrase. The account
+// stays unlocked for the duration of timeout. A timeout of 0 unlocks the account
+// until the program exits. The account must match a unique key file.
+//
+// If the account address is already unlocked for a duration, TimedUnlock extends or
+// shortens the active unlock timeout. If the address was previously unlocked
+// indefinitely the timeout is not altered.
+func (ks *KeyStore) TimedUnlock(a accounts.Account, passphrase string, timeout time.Duration) error {
+ a, key, err := ks.getDecryptedKey(a, passphrase)
+ if err != nil {
+ return err
+ }
+
+ ks.mu.Lock()
+ defer ks.mu.Unlock()
+ u, found := ks.unlocked[a.Address]
+ if found {
+ if u.abort == nil {
+ // The address was unlocked indefinitely, so unlocking
+ // it with a timeout would be confusing.
+ zeroKey(key.PrivateKey)
+ return nil
+ }
+ // Terminate the expire goroutine and replace it below.
+ close(u.abort)
+ }
+ if timeout > 0 {
+ u = &unlocked{Key: key, abort: make(chan struct{})}
+ go ks.expire(a.Address, u, timeout)
+ } else {
+ u = &unlocked{Key: key}
+ }
+ ks.unlocked[a.Address] = u
+ return nil
+}
+
+// Find resolves the given account into a unique entry in the keystore.
+func (ks *KeyStore) Find(a accounts.Account) (accounts.Account, error) {
+ ks.cache.maybeReload()
+ ks.cache.mu.Lock()
+ a, err := ks.cache.find(a)
+ ks.cache.mu.Unlock()
+ return a, err
+}
+
+func (ks *KeyStore) getDecryptedKey(a accounts.Account, auth string) (accounts.Account, *Key, error) {
+ a, err := ks.Find(a)
+ if err != nil {
+ return a, nil, err
+ }
+ key, err := ks.storage.GetKey(a.Address, a.URL.Path, auth)
+ return a, key, err
+}
+
+func (ks *KeyStore) expire(addr common.Address, u *unlocked, timeout time.Duration) {
+ t := time.NewTimer(timeout)
+ defer t.Stop()
+ select {
+ case <-u.abort:
+ // just quit
+ case <-t.C:
+ ks.mu.Lock()
+ // only drop if it's still the same key instance that dropLater
+ // was launched with. we can check that using pointer equality
+ // because the map stores a new pointer every time the key is
+ // unlocked.
+ if ks.unlocked[addr] == u {
+ zeroKey(u.PrivateKey)
+ delete(ks.unlocked, addr)
+ }
+ ks.mu.Unlock()
+ }
+}
+
+// NewAccount generates a new key and stores it into the key directory,
+// encrypting it with the passphrase.
+func (ks *KeyStore) NewAccount(passphrase string) (accounts.Account, error) {
+ _, account, err := storeNewKey(ks.storage, crand.Reader, passphrase)
+ if err != nil {
+ return accounts.Account{}, err
+ }
+ // Add the account to the cache immediately rather
+ // than waiting for file system notifications to pick it up.
+ ks.cache.add(account)
+ ks.refreshWallets()
+ return account, nil
+}
+
+// Export exports as a JSON key, encrypted with newPassphrase.
+func (ks *KeyStore) Export(a accounts.Account, passphrase, newPassphrase string) (keyJSON []byte, err error) {
+ _, key, err := ks.getDecryptedKey(a, passphrase)
+ if err != nil {
+ return nil, err
+ }
+ var N, P int
+ if store, ok := ks.storage.(*keyStorePassphrase); ok {
+ N, P = store.scryptN, store.scryptP
+ } else {
+ N, P = StandardScryptN, StandardScryptP
+ }
+ return EncryptKey(key, newPassphrase, N, P)
+}
+
+// Import stores the given encrypted JSON key into the key directory.
+func (ks *KeyStore) Import(keyJSON []byte, passphrase, newPassphrase string) (accounts.Account, error) {
+ key, err := DecryptKey(keyJSON, passphrase)
+ if key != nil && key.PrivateKey != nil {
+ defer zeroKey(key.PrivateKey)
+ }
+ if err != nil {
+ return accounts.Account{}, err
+ }
+ return ks.importKey(key, newPassphrase)
+}
+
+// ImportECDSA stores the given key into the key directory, encrypting it with the passphrase.
+func (ks *KeyStore) ImportECDSA(priv *ecdsa.PrivateKey, passphrase string) (accounts.Account, error) {
+ key := newKeyFromECDSA(priv)
+ if ks.cache.hasAddress(key.Address) {
+ return accounts.Account{}, fmt.Errorf("account already exists")
+ }
+
+ return ks.importKey(key, passphrase)
+}
+
+func (ks *KeyStore) importKey(key *Key, passphrase string) (accounts.Account, error) {
+ a := accounts.Account{Address: key.Address, URL: accounts.URL{Scheme: KeyStoreScheme, Path: ks.storage.JoinPath(keyFileName(key.Address))}}
+ if err := ks.storage.StoreKey(a.URL.Path, key, passphrase); err != nil {
+ return accounts.Account{}, err
+ }
+ ks.cache.add(a)
+ ks.refreshWallets()
+ return a, nil
+}
+
+// Update changes the passphrase of an existing account.
+func (ks *KeyStore) Update(a accounts.Account, passphrase, newPassphrase string) error {
+ a, key, err := ks.getDecryptedKey(a, passphrase)
+ if err != nil {
+ return err
+ }
+ return ks.storage.StoreKey(a.URL.Path, key, newPassphrase)
+}
+
+// ImportPreSaleKey decrypts the given Ethereum presale wallet and stores
+// a key file in the key directory. The key file is encrypted with the same passphrase.
+func (ks *KeyStore) ImportPreSaleKey(keyJSON []byte, passphrase string) (accounts.Account, error) {
+ a, _, err := importPreSaleKey(ks.storage, keyJSON, passphrase)
+ if err != nil {
+ return a, err
+ }
+ ks.cache.add(a)
+ ks.refreshWallets()
+ return a, nil
+}
+
+// zeroKey zeroes a private key in memory.
+func zeroKey(k *ecdsa.PrivateKey) {
+ b := k.D.Bits()
+ for i := range b {
+ b[i] = 0
+ }
+}
diff --git a/accounts/key_store_passphrase.go b/accounts/keystore/keystore_passphrase.go
index 4a777956d..8ef510fcf 100644
--- a/accounts/key_store_passphrase.go
+++ b/accounts/keystore/keystore_passphrase.go
@@ -23,7 +23,7 @@ The crypto is documented at https://github.com/ethereum/wiki/wiki/Web3-Secret-St
*/
-package accounts
+package keystore
import (
"bytes"
diff --git a/accounts/key_store_passphrase_test.go b/accounts/keystore/keystore_passphrase_test.go
index 217393fa5..086addbc1 100644
--- a/accounts/key_store_passphrase_test.go
+++ b/accounts/keystore/keystore_passphrase_test.go
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
-package accounts
+package keystore
import (
"io/ioutil"
diff --git a/accounts/key_store_plain.go b/accounts/keystore/keystore_plain.go
index 2cbaa94df..b490ca72b 100644
--- a/accounts/key_store_plain.go
+++ b/accounts/keystore/keystore_plain.go
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
-package accounts
+package keystore
import (
"encoding/json"
diff --git a/accounts/key_store_test.go b/accounts/keystore/keystore_plain_test.go
index d0713caa0..8c0eb52ea 100644
--- a/accounts/key_store_test.go
+++ b/accounts/keystore/keystore_plain_test.go
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
-package accounts
+package keystore
import (
"crypto/rand"
@@ -30,7 +30,7 @@ import (
"github.com/ethereum/go-ethereum/crypto"
)
-func tmpKeyStore(t *testing.T, encrypted bool) (dir string, ks keyStore) {
+func tmpKeyStoreIface(t *testing.T, encrypted bool) (dir string, ks keyStore) {
d, err := ioutil.TempDir("", "geth-keystore-test")
if err != nil {
t.Fatal(err)
@@ -44,7 +44,7 @@ func tmpKeyStore(t *testing.T, encrypted bool) (dir string, ks keyStore) {
}
func TestKeyStorePlain(t *testing.T) {
- dir, ks := tmpKeyStore(t, false)
+ dir, ks := tmpKeyStoreIface(t, false)
defer os.RemoveAll(dir)
pass := "" // not used but required by API
@@ -52,7 +52,7 @@ func TestKeyStorePlain(t *testing.T) {
if err != nil {
t.Fatal(err)
}
- k2, err := ks.GetKey(k1.Address, account.File, pass)
+ k2, err := ks.GetKey(k1.Address, account.URL.Path, pass)
if err != nil {
t.Fatal(err)
}
@@ -65,7 +65,7 @@ func TestKeyStorePlain(t *testing.T) {
}
func TestKeyStorePassphrase(t *testing.T) {
- dir, ks := tmpKeyStore(t, true)
+ dir, ks := tmpKeyStoreIface(t, true)
defer os.RemoveAll(dir)
pass := "foo"
@@ -73,7 +73,7 @@ func TestKeyStorePassphrase(t *testing.T) {
if err != nil {
t.Fatal(err)
}
- k2, err := ks.GetKey(k1.Address, account.File, pass)
+ k2, err := ks.GetKey(k1.Address, account.URL.Path, pass)
if err != nil {
t.Fatal(err)
}
@@ -86,7 +86,7 @@ func TestKeyStorePassphrase(t *testing.T) {
}
func TestKeyStorePassphraseDecryptionFail(t *testing.T) {
- dir, ks := tmpKeyStore(t, true)
+ dir, ks := tmpKeyStoreIface(t, true)
defer os.RemoveAll(dir)
pass := "foo"
@@ -94,13 +94,13 @@ func TestKeyStorePassphraseDecryptionFail(t *testing.T) {
if err != nil {
t.Fatal(err)
}
- if _, err = ks.GetKey(k1.Address, account.File, "bar"); err != ErrDecrypt {
+ if _, err = ks.GetKey(k1.Address, account.URL.Path, "bar"); err != ErrDecrypt {
t.Fatalf("wrong error for invalid passphrase\ngot %q\nwant %q", err, ErrDecrypt)
}
}
func TestImportPreSaleKey(t *testing.T) {
- dir, ks := tmpKeyStore(t, true)
+ dir, ks := tmpKeyStoreIface(t, true)
defer os.RemoveAll(dir)
// file content of a presale key file generated with:
@@ -115,8 +115,8 @@ func TestImportPreSaleKey(t *testing.T) {
if account.Address != common.HexToAddress("d4584b5f6229b7be90727b0fc8c6b91bb427821f") {
t.Errorf("imported account has wrong address %x", account.Address)
}
- if !strings.HasPrefix(account.File, dir) {
- t.Errorf("imported account file not in keystore directory: %q", account.File)
+ if !strings.HasPrefix(account.URL.Path, dir) {
+ t.Errorf("imported account file not in keystore directory: %q", account.URL)
}
}
@@ -142,19 +142,19 @@ func TestV3_PBKDF2_1(t *testing.T) {
func TestV3_PBKDF2_2(t *testing.T) {
t.Parallel()
- tests := loadKeyStoreTestV3("../tests/files/KeyStoreTests/basic_tests.json", t)
+ tests := loadKeyStoreTestV3("../../tests/files/KeyStoreTests/basic_tests.json", t)
testDecryptV3(tests["test1"], t)
}
func TestV3_PBKDF2_3(t *testing.T) {
t.Parallel()
- tests := loadKeyStoreTestV3("../tests/files/KeyStoreTests/basic_tests.json", t)
+ tests := loadKeyStoreTestV3("../../tests/files/KeyStoreTests/basic_tests.json", t)
testDecryptV3(tests["python_generated_test_with_odd_iv"], t)
}
func TestV3_PBKDF2_4(t *testing.T) {
t.Parallel()
- tests := loadKeyStoreTestV3("../tests/files/KeyStoreTests/basic_tests.json", t)
+ tests := loadKeyStoreTestV3("../../tests/files/KeyStoreTests/basic_tests.json", t)
testDecryptV3(tests["evilnonce"], t)
}
@@ -166,7 +166,7 @@ func TestV3_Scrypt_1(t *testing.T) {
func TestV3_Scrypt_2(t *testing.T) {
t.Parallel()
- tests := loadKeyStoreTestV3("../tests/files/KeyStoreTests/basic_tests.json", t)
+ tests := loadKeyStoreTestV3("../../tests/files/KeyStoreTests/basic_tests.json", t)
testDecryptV3(tests["test2"], t)
}
diff --git a/accounts/keystore/keystore_test.go b/accounts/keystore/keystore_test.go
new file mode 100644
index 000000000..60f2606ee
--- /dev/null
+++ b/accounts/keystore/keystore_test.go
@@ -0,0 +1,365 @@
+// Copyright 2015 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum 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 go-ethereum 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 go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package keystore
+
+import (
+ "io/ioutil"
+ "math/rand"
+ "os"
+ "runtime"
+ "sort"
+ "strings"
+ "testing"
+ "time"
+
+ "github.com/ethereum/go-ethereum/accounts"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/event"
+)
+
+var testSigData = make([]byte, 32)
+
+func TestKeyStore(t *testing.T) {
+ dir, ks := tmpKeyStore(t, true)
+ defer os.RemoveAll(dir)
+
+ a, err := ks.NewAccount("foo")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !strings.HasPrefix(a.URL.Path, dir) {
+ t.Errorf("account file %s doesn't have dir prefix", a.URL)
+ }
+ stat, err := os.Stat(a.URL.Path)
+ if err != nil {
+ t.Fatalf("account file %s doesn't exist (%v)", a.URL, err)
+ }
+ if runtime.GOOS != "windows" && stat.Mode() != 0600 {
+ t.Fatalf("account file has wrong mode: got %o, want %o", stat.Mode(), 0600)
+ }
+ if !ks.HasAddress(a.Address) {
+ t.Errorf("HasAccount(%x) should've returned true", a.Address)
+ }
+ if err := ks.Update(a, "foo", "bar"); err != nil {
+ t.Errorf("Update error: %v", err)
+ }
+ if err := ks.Delete(a, "bar"); err != nil {
+ t.Errorf("Delete error: %v", err)
+ }
+ if common.FileExist(a.URL.Path) {
+ t.Errorf("account file %s should be gone after Delete", a.URL)
+ }
+ if ks.HasAddress(a.Address) {
+ t.Errorf("HasAccount(%x) should've returned true after Delete", a.Address)
+ }
+}
+
+func TestSign(t *testing.T) {
+ dir, ks := tmpKeyStore(t, true)
+ defer os.RemoveAll(dir)
+
+ pass := "" // not used but required by API
+ a1, err := ks.NewAccount(pass)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if err := ks.Unlock(a1, ""); err != nil {
+ t.Fatal(err)
+ }
+ if _, err := ks.SignHash(accounts.Account{Address: a1.Address}, testSigData); err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestSignWithPassphrase(t *testing.T) {
+ dir, ks := tmpKeyStore(t, true)
+ defer os.RemoveAll(dir)
+
+ pass := "passwd"
+ acc, err := ks.NewAccount(pass)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if _, unlocked := ks.unlocked[acc.Address]; unlocked {
+ t.Fatal("expected account to be locked")
+ }
+
+ _, err = ks.SignHashWithPassphrase(acc, pass, testSigData)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if _, unlocked := ks.unlocked[acc.Address]; unlocked {
+ t.Fatal("expected account to be locked")
+ }
+
+ if _, err = ks.SignHashWithPassphrase(acc, "invalid passwd", testSigData); err == nil {
+ t.Fatal("expected SignHashWithPassphrase to fail with invalid password")
+ }
+}
+
+func TestTimedUnlock(t *testing.T) {
+ dir, ks := tmpKeyStore(t, true)
+ defer os.RemoveAll(dir)
+
+ pass := "foo"
+ a1, err := ks.NewAccount(pass)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // Signing without passphrase fails because account is locked
+ _, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData)
+ if err != ErrLocked {
+ t.Fatal("Signing should've failed with ErrLocked before unlocking, got ", err)
+ }
+
+ // Signing with passphrase works
+ if err = ks.TimedUnlock(a1, pass, 100*time.Millisecond); err != nil {
+ t.Fatal(err)
+ }
+
+ // Signing without passphrase works because account is temp unlocked
+ _, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData)
+ if err != nil {
+ t.Fatal("Signing shouldn't return an error after unlocking, got ", err)
+ }
+
+ // Signing fails again after automatic locking
+ time.Sleep(250 * time.Millisecond)
+ _, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData)
+ if err != ErrLocked {
+ t.Fatal("Signing should've failed with ErrLocked timeout expired, got ", err)
+ }
+}
+
+func TestOverrideUnlock(t *testing.T) {
+ dir, ks := tmpKeyStore(t, false)
+ defer os.RemoveAll(dir)
+
+ pass := "foo"
+ a1, err := ks.NewAccount(pass)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // Unlock indefinitely.
+ if err = ks.TimedUnlock(a1, pass, 5*time.Minute); err != nil {
+ t.Fatal(err)
+ }
+
+ // Signing without passphrase works because account is temp unlocked
+ _, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData)
+ if err != nil {
+ t.Fatal("Signing shouldn't return an error after unlocking, got ", err)
+ }
+
+ // reset unlock to a shorter period, invalidates the previous unlock
+ if err = ks.TimedUnlock(a1, pass, 100*time.Millisecond); err != nil {
+ t.Fatal(err)
+ }
+
+ // Signing without passphrase still works because account is temp unlocked
+ _, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData)
+ if err != nil {
+ t.Fatal("Signing shouldn't return an error after unlocking, got ", err)
+ }
+
+ // Signing fails again after automatic locking
+ time.Sleep(250 * time.Millisecond)
+ _, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData)
+ if err != ErrLocked {
+ t.Fatal("Signing should've failed with ErrLocked timeout expired, got ", err)
+ }
+}
+
+// This test should fail under -race if signing races the expiration goroutine.
+func TestSignRace(t *testing.T) {
+ dir, ks := tmpKeyStore(t, false)
+ defer os.RemoveAll(dir)
+
+ // Create a test account.
+ a1, err := ks.NewAccount("")
+ if err != nil {
+ t.Fatal("could not create the test account", err)
+ }
+
+ if err := ks.TimedUnlock(a1, "", 15*time.Millisecond); err != nil {
+ t.Fatal("could not unlock the test account", err)
+ }
+ end := time.Now().Add(500 * time.Millisecond)
+ for time.Now().Before(end) {
+ if _, err := ks.SignHash(accounts.Account{Address: a1.Address}, testSigData); err == ErrLocked {
+ return
+ } else if err != nil {
+ t.Errorf("Sign error: %v", err)
+ return
+ }
+ time.Sleep(1 * time.Millisecond)
+ }
+ t.Errorf("Account did not lock within the timeout")
+}
+
+// Tests that the wallet notifier loop starts and stops correctly based on the
+// addition and removal of wallet event subscriptions.
+func TestWalletNotifierLifecycle(t *testing.T) {
+ // Create a temporary kesytore to test with
+ dir, ks := tmpKeyStore(t, false)
+ defer os.RemoveAll(dir)
+
+ // Ensure that the notification updater is not running yet
+ time.Sleep(250 * time.Millisecond)
+ ks.mu.RLock()
+ updating := ks.updating
+ ks.mu.RUnlock()
+
+ if updating {
+ t.Errorf("wallet notifier running without subscribers")
+ }
+ // Subscribe to the wallet feed and ensure the updater boots up
+ updates := make(chan accounts.WalletEvent)
+
+ subs := make([]event.Subscription, 2)
+ for i := 0; i < len(subs); i++ {
+ // Create a new subscription
+ subs[i] = ks.Subscribe(updates)
+
+ // Ensure the notifier comes online
+ time.Sleep(250 * time.Millisecond)
+ ks.mu.RLock()
+ updating = ks.updating
+ ks.mu.RUnlock()
+
+ if !updating {
+ t.Errorf("sub %d: wallet notifier not running after subscription", i)
+ }
+ }
+ // Unsubscribe and ensure the updater terminates eventually
+ for i := 0; i < len(subs); i++ {
+ // Close an existing subscription
+ subs[i].Unsubscribe()
+
+ // Ensure the notifier shuts down at and only at the last close
+ for k := 0; k < int(walletRefreshCycle/(250*time.Millisecond))+2; k++ {
+ ks.mu.RLock()
+ updating = ks.updating
+ ks.mu.RUnlock()
+
+ if i < len(subs)-1 && !updating {
+ t.Fatalf("sub %d: event notifier stopped prematurely", i)
+ }
+ if i == len(subs)-1 && !updating {
+ return
+ }
+ time.Sleep(250 * time.Millisecond)
+ }
+ }
+ t.Errorf("wallet notifier didn't terminate after unsubscribe")
+}
+
+// Tests that wallet notifications and correctly fired when accounts are added
+// or deleted from the keystore.
+func TestWalletNotifications(t *testing.T) {
+ // Create a temporary kesytore to test with
+ dir, ks := tmpKeyStore(t, false)
+ defer os.RemoveAll(dir)
+
+ // Subscribe to the wallet feed
+ updates := make(chan accounts.WalletEvent, 1)
+ sub := ks.Subscribe(updates)
+ defer sub.Unsubscribe()
+
+ // Randomly add and remove account and make sure events and wallets are in sync
+ live := make(map[common.Address]accounts.Account)
+ for i := 0; i < 1024; i++ {
+ // Execute a creation or deletion and ensure event arrival
+ if create := len(live) == 0 || rand.Int()%4 > 0; create {
+ // Add a new account and ensure wallet notifications arrives
+ account, err := ks.NewAccount("")
+ if err != nil {
+ t.Fatalf("failed to create test account: %v", err)
+ }
+ select {
+ case event := <-updates:
+ if !event.Arrive {
+ t.Errorf("departure event on account creation")
+ }
+ if event.Wallet.Accounts()[0] != account {
+ t.Errorf("account mismatch on created wallet: have %v, want %v", event.Wallet.Accounts()[0], account)
+ }
+ default:
+ t.Errorf("wallet arrival event not fired on account creation")
+ }
+ live[account.Address] = account
+ } else {
+ // Select a random account to delete (crude, but works)
+ var account accounts.Account
+ for _, a := range live {
+ account = a
+ break
+ }
+ // Remove an account and ensure wallet notifiaction arrives
+ if err := ks.Delete(account, ""); err != nil {
+ t.Fatalf("failed to delete test account: %v", err)
+ }
+ select {
+ case event := <-updates:
+ if event.Arrive {
+ t.Errorf("arrival event on account deletion")
+ }
+ if event.Wallet.Accounts()[0] != account {
+ t.Errorf("account mismatch on deleted wallet: have %v, want %v", event.Wallet.Accounts()[0], account)
+ }
+ default:
+ t.Errorf("wallet departure event not fired on account creation")
+ }
+ delete(live, account.Address)
+ }
+ // Retrieve the list of wallets and ensure it matches with our required live set
+ liveList := make([]accounts.Account, 0, len(live))
+ for _, account := range live {
+ liveList = append(liveList, account)
+ }
+ sort.Sort(accountsByURL(liveList))
+
+ wallets := ks.Wallets()
+ if len(liveList) != len(wallets) {
+ t.Errorf("wallet list doesn't match required accounts: have %v, want %v", wallets, liveList)
+ } else {
+ for j, wallet := range wallets {
+ if accs := wallet.Accounts(); len(accs) != 1 {
+ t.Errorf("wallet %d: contains invalid number of accounts: have %d, want 1", j, len(accs))
+ } else if accs[0] != liveList[j] {
+ t.Errorf("wallet %d: account mismatch: have %v, want %v", j, accs[0], liveList[j])
+ }
+ }
+ }
+ }
+}
+
+func tmpKeyStore(t *testing.T, encrypted bool) (string, *KeyStore) {
+ d, err := ioutil.TempDir("", "eth-keystore-test")
+ if err != nil {
+ t.Fatal(err)
+ }
+ new := NewPlaintextKeyStore
+ if encrypted {
+ new = func(kd string) *KeyStore { return NewKeyStore(kd, veryLightScryptN, veryLightScryptP) }
+ }
+ return d, new(d)
+}
diff --git a/accounts/keystore/keystore_wallet.go b/accounts/keystore/keystore_wallet.go
new file mode 100644
index 000000000..7165d2821
--- /dev/null
+++ b/accounts/keystore/keystore_wallet.go
@@ -0,0 +1,139 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum 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 go-ethereum 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 go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package keystore
+
+import (
+ "math/big"
+
+ ethereum "github.com/ethereum/go-ethereum"
+ "github.com/ethereum/go-ethereum/accounts"
+ "github.com/ethereum/go-ethereum/core/types"
+)
+
+// keystoreWallet implements the accounts.Wallet interface for the original
+// keystore.
+type keystoreWallet struct {
+ account accounts.Account // Single account contained in this wallet
+ keystore *KeyStore // Keystore where the account originates from
+}
+
+// URL implements accounts.Wallet, returning the URL of the account within.
+func (w *keystoreWallet) URL() accounts.URL {
+ return w.account.URL
+}
+
+// Status implements accounts.Wallet, always returning "open", since there is no
+// concept of open/close for plain keystore accounts.
+func (w *keystoreWallet) Status() string {
+ w.keystore.mu.RLock()
+ defer w.keystore.mu.RUnlock()
+
+ if _, ok := w.keystore.unlocked[w.account.Address]; ok {
+ return "Unlocked"
+ }
+ return "Locked"
+}
+
+// Open implements accounts.Wallet, but is a noop for plain wallets since there
+// is no connection or decryption step necessary to access the list of accounts.
+func (w *keystoreWallet) Open(passphrase string) error { return nil }
+
+// Close implements accounts.Wallet, but is a noop for plain wallets since is no
+// meaningful open operation.
+func (w *keystoreWallet) Close() error { return nil }
+
+// Accounts implements accounts.Wallet, returning an account list consisting of
+// a single account that the plain kestore wallet contains.
+func (w *keystoreWallet) Accounts() []accounts.Account {
+ return []accounts.Account{w.account}
+}
+
+// Contains implements accounts.Wallet, returning whether a particular account is
+// or is not wrapped by this wallet instance.
+func (w *keystoreWallet) Contains(account accounts.Account) bool {
+ return account.Address == w.account.Address && (account.URL == (accounts.URL{}) || account.URL == w.account.URL)
+}
+
+// Derive implements accounts.Wallet, but is a noop for plain wallets since there
+// is no notion of hierarchical account derivation for plain keystore accounts.
+func (w *keystoreWallet) Derive(path accounts.DerivationPath, pin bool) (accounts.Account, error) {
+ return accounts.Account{}, accounts.ErrNotSupported
+}
+
+// SelfDerive implements accounts.Wallet, but is a noop for plain wallets since
+// there is no notion of hierarchical account derivation for plain keystore accounts.
+func (w *keystoreWallet) SelfDerive(base accounts.DerivationPath, chain ethereum.ChainStateReader) {}
+
+// SignHash implements accounts.Wallet, attempting to sign the given hash with
+// the given account. If the wallet does not wrap this particular account, an
+// error is returned to avoid account leakage (even though in theory we may be
+// able to sign via our shared keystore backend).
+func (w *keystoreWallet) SignHash(account accounts.Account, hash []byte) ([]byte, error) {
+ // Make sure the requested account is contained within
+ if account.Address != w.account.Address {
+ return nil, accounts.ErrUnknownAccount
+ }
+ if account.URL != (accounts.URL{}) && account.URL != w.account.URL {
+ return nil, accounts.ErrUnknownAccount
+ }
+ // Account seems valid, request the keystore to sign
+ return w.keystore.SignHash(account, hash)
+}
+
+// SignTx implements accounts.Wallet, attempting to sign the given transaction
+// with the given account. If the wallet does not wrap this particular account,
+// an error is returned to avoid account leakage (even though in theory we may
+// be able to sign via our shared keystore backend).
+func (w *keystoreWallet) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
+ // Make sure the requested account is contained within
+ if account.Address != w.account.Address {
+ return nil, accounts.ErrUnknownAccount
+ }
+ if account.URL != (accounts.URL{}) && account.URL != w.account.URL {
+ return nil, accounts.ErrUnknownAccount
+ }
+ // Account seems valid, request the keystore to sign
+ return w.keystore.SignTx(account, tx, chainID)
+}
+
+// SignHashWithPassphrase implements accounts.Wallet, attempting to sign the
+// given hash with the given account using passphrase as extra authentication.
+func (w *keystoreWallet) SignHashWithPassphrase(account accounts.Account, passphrase string, hash []byte) ([]byte, error) {
+ // Make sure the requested account is contained within
+ if account.Address != w.account.Address {
+ return nil, accounts.ErrUnknownAccount
+ }
+ if account.URL != (accounts.URL{}) && account.URL != w.account.URL {
+ return nil, accounts.ErrUnknownAccount
+ }
+ // Account seems valid, request the keystore to sign
+ return w.keystore.SignHashWithPassphrase(account, passphrase, hash)
+}
+
+// SignTxWithPassphrase implements accounts.Wallet, attempting to sign the given
+// transaction with the given account using passphrase as extra authentication.
+func (w *keystoreWallet) SignTxWithPassphrase(account accounts.Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
+ // Make sure the requested account is contained within
+ if account.Address != w.account.Address {
+ return nil, accounts.ErrUnknownAccount
+ }
+ if account.URL != (accounts.URL{}) && account.URL != w.account.URL {
+ return nil, accounts.ErrUnknownAccount
+ }
+ // Account seems valid, request the keystore to sign
+ return w.keystore.SignTxWithPassphrase(account, passphrase, tx, chainID)
+}
diff --git a/accounts/presale.go b/accounts/keystore/presale.go
index f00b4f502..5b883c45f 100644
--- a/accounts/presale.go
+++ b/accounts/keystore/presale.go
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
-package accounts
+package keystore
import (
"crypto/aes"
@@ -25,20 +25,21 @@ import (
"errors"
"fmt"
+ "github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/crypto"
"github.com/pborman/uuid"
"golang.org/x/crypto/pbkdf2"
)
// creates a Key and stores that in the given KeyStore by decrypting a presale key JSON
-func importPreSaleKey(keyStore keyStore, keyJSON []byte, password string) (Account, *Key, error) {
+func importPreSaleKey(keyStore keyStore, keyJSON []byte, password string) (accounts.Account, *Key, error) {
key, err := decryptPreSaleKey(keyJSON, password)
if err != nil {
- return Account{}, nil, err
+ return accounts.Account{}, nil, err
}
key.Id = uuid.NewRandom()
- a := Account{Address: key.Address, File: keyStore.JoinPath(keyFileName(key.Address))}
- err = keyStore.StoreKey(a.File, key, password)
+ a := accounts.Account{Address: key.Address, URL: accounts.URL{Scheme: KeyStoreScheme, Path: keyStore.JoinPath(keyFileName(key.Address))}}
+ err = keyStore.StoreKey(a.URL.Path, key, password)
return a, key, err
}
diff --git a/accounts/testdata/dupes/1 b/accounts/keystore/testdata/dupes/1
index a3868ec6d..a3868ec6d 100644
--- a/accounts/testdata/dupes/1
+++ b/accounts/keystore/testdata/dupes/1
diff --git a/accounts/testdata/dupes/2 b/accounts/keystore/testdata/dupes/2
index a3868ec6d..a3868ec6d 100644
--- a/accounts/testdata/dupes/2
+++ b/accounts/keystore/testdata/dupes/2
diff --git a/accounts/testdata/dupes/foo b/accounts/keystore/testdata/dupes/foo
index c57060aea..c57060aea 100644
--- a/accounts/testdata/dupes/foo
+++ b/accounts/keystore/testdata/dupes/foo
diff --git a/accounts/testdata/keystore/.hiddenfile b/accounts/keystore/testdata/keystore/.hiddenfile
index d91faccde..d91faccde 100644
--- a/accounts/testdata/keystore/.hiddenfile
+++ b/accounts/keystore/testdata/keystore/.hiddenfile
diff --git a/accounts/testdata/keystore/README b/accounts/keystore/testdata/keystore/README
index a5a86f964..a5a86f964 100644
--- a/accounts/testdata/keystore/README
+++ b/accounts/keystore/testdata/keystore/README
diff --git a/accounts/testdata/keystore/UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8 b/accounts/keystore/testdata/keystore/UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8
index c57060aea..c57060aea 100644
--- a/accounts/testdata/keystore/UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8
+++ b/accounts/keystore/testdata/keystore/UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8
diff --git a/accounts/testdata/keystore/aaa b/accounts/keystore/testdata/keystore/aaa
index a3868ec6d..a3868ec6d 100644
--- a/accounts/testdata/keystore/aaa
+++ b/accounts/keystore/testdata/keystore/aaa
diff --git a/accounts/testdata/keystore/empty b/accounts/keystore/testdata/keystore/empty
index e69de29bb..e69de29bb 100644
--- a/accounts/testdata/keystore/empty
+++ b/accounts/keystore/testdata/keystore/empty
diff --git a/accounts/testdata/keystore/foo/fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e b/accounts/keystore/testdata/keystore/foo/fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e
index 309841e52..309841e52 100644
--- a/accounts/testdata/keystore/foo/fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e
+++ b/accounts/keystore/testdata/keystore/foo/fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e
diff --git a/accounts/testdata/keystore/garbage b/accounts/keystore/testdata/keystore/garbage
index ff45091e7..ff45091e7 100644
--- a/accounts/testdata/keystore/garbage
+++ b/accounts/keystore/testdata/keystore/garbage
Binary files differ
diff --git a/accounts/testdata/keystore/no-address b/accounts/keystore/testdata/keystore/no-address
index ad51269ea..ad51269ea 100644
--- a/accounts/testdata/keystore/no-address
+++ b/accounts/keystore/testdata/keystore/no-address
diff --git a/accounts/testdata/keystore/zero b/accounts/keystore/testdata/keystore/zero
index b52617f8a..b52617f8a 100644
--- a/accounts/testdata/keystore/zero
+++ b/accounts/keystore/testdata/keystore/zero
diff --git a/accounts/testdata/keystore/zzz b/accounts/keystore/testdata/keystore/zzz
index cfd8a4701..cfd8a4701 100644
--- a/accounts/testdata/keystore/zzz
+++ b/accounts/keystore/testdata/keystore/zzz
diff --git a/accounts/testdata/v1/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e b/accounts/keystore/testdata/v1/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e
index 498d8131e..498d8131e 100644
--- a/accounts/testdata/v1/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e
+++ b/accounts/keystore/testdata/v1/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e
diff --git a/accounts/testdata/v1_test_vector.json b/accounts/keystore/testdata/v1_test_vector.json
index 3d09b55b5..3d09b55b5 100644
--- a/accounts/testdata/v1_test_vector.json
+++ b/accounts/keystore/testdata/v1_test_vector.json
diff --git a/accounts/testdata/v3_test_vector.json b/accounts/keystore/testdata/v3_test_vector.json
index 1e7f790c0..1e7f790c0 100644
--- a/accounts/testdata/v3_test_vector.json
+++ b/accounts/keystore/testdata/v3_test_vector.json
diff --git a/accounts/testdata/very-light-scrypt.json b/accounts/keystore/testdata/very-light-scrypt.json
index d23b9b2b9..d23b9b2b9 100644
--- a/accounts/testdata/very-light-scrypt.json
+++ b/accounts/keystore/testdata/very-light-scrypt.json
diff --git a/accounts/watch.go b/accounts/keystore/watch.go
index 472be2df7..0b4401255 100644
--- a/accounts/watch.go
+++ b/accounts/keystore/watch.go
@@ -16,7 +16,7 @@
// +build darwin,!ios freebsd linux,!arm64 netbsd solaris
-package accounts
+package keystore
import (
"time"
@@ -27,14 +27,14 @@ import (
)
type watcher struct {
- ac *addrCache
+ ac *accountCache
starting bool
running bool
ev chan notify.EventInfo
quit chan struct{}
}
-func newWatcher(ac *addrCache) *watcher {
+func newWatcher(ac *accountCache) *watcher {
return &watcher{
ac: ac,
ev: make(chan notify.EventInfo, 10),
diff --git a/accounts/watch_fallback.go b/accounts/keystore/watch_fallback.go
index bf971cb1b..7c5e9cb2e 100644
--- a/accounts/watch_fallback.go
+++ b/accounts/keystore/watch_fallback.go
@@ -19,10 +19,10 @@
// This is the fallback implementation of directory watching.
// It is used on unsupported platforms.
-package accounts
+package keystore
type watcher struct{ running bool }
-func newWatcher(*addrCache) *watcher { return new(watcher) }
-func (*watcher) start() {}
-func (*watcher) close() {}
+func newWatcher(*accountCache) *watcher { return new(watcher) }
+func (*watcher) start() {}
+func (*watcher) close() {}
diff --git a/accounts/manager.go b/accounts/manager.go
new file mode 100644
index 000000000..12a5bfcd9
--- /dev/null
+++ b/accounts/manager.go
@@ -0,0 +1,198 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum 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 go-ethereum 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 go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package accounts
+
+import (
+ "reflect"
+ "sort"
+ "sync"
+
+ "github.com/ethereum/go-ethereum/event"
+)
+
+// Manager is an overarching account manager that can communicate with various
+// backends for signing transactions.
+type Manager struct {
+ backends map[reflect.Type][]Backend // Index of backends currently registered
+ updaters []event.Subscription // Wallet update subscriptions for all backends
+ updates chan WalletEvent // Subscription sink for backend wallet changes
+ wallets []Wallet // Cache of all wallets from all registered backends
+
+ feed event.Feed // Wallet feed notifying of arrivals/departures
+
+ quit chan chan error
+ lock sync.RWMutex
+}
+
+// NewManager creates a generic account manager to sign transaction via various
+// supported backends.
+func NewManager(backends ...Backend) *Manager {
+ // Subscribe to wallet notifications from all backends
+ updates := make(chan WalletEvent, 4*len(backends))
+
+ subs := make([]event.Subscription, len(backends))
+ for i, backend := range backends {
+ subs[i] = backend.Subscribe(updates)
+ }
+ // Retrieve the initial list of wallets from the backends and sort by URL
+ var wallets []Wallet
+ for _, backend := range backends {
+ wallets = merge(wallets, backend.Wallets()...)
+ }
+ // Assemble the account manager and return
+ am := &Manager{
+ backends: make(map[reflect.Type][]Backend),
+ updaters: subs,
+ updates: updates,
+ wallets: wallets,
+ quit: make(chan chan error),
+ }
+ for _, backend := range backends {
+ kind := reflect.TypeOf(backend)
+ am.backends[kind] = append(am.backends[kind], backend)
+ }
+ go am.update()
+
+ return am
+}
+
+// Close terminates the account manager's internal notification processes.
+func (am *Manager) Close() error {
+ errc := make(chan error)
+ am.quit <- errc
+ return <-errc
+}
+
+// update is the wallet event loop listening for notifications from the backends
+// and updating the cache of wallets.
+func (am *Manager) update() {
+ // Close all subscriptions when the manager terminates
+ defer func() {
+ am.lock.Lock()
+ for _, sub := range am.updaters {
+ sub.Unsubscribe()
+ }
+ am.updaters = nil
+ am.lock.Unlock()
+ }()
+
+ // Loop until termination
+ for {
+ select {
+ case event := <-am.updates:
+ // Wallet event arrived, update local cache
+ am.lock.Lock()
+ if event.Arrive {
+ am.wallets = merge(am.wallets, event.Wallet)
+ } else {
+ am.wallets = drop(am.wallets, event.Wallet)
+ }
+ am.lock.Unlock()
+
+ // Notify any listeners of the event
+ am.feed.Send(event)
+
+ case errc := <-am.quit:
+ // Manager terminating, return
+ errc <- nil
+ return
+ }
+ }
+}
+
+// Backends retrieves the backend(s) with the given type from the account manager.
+func (am *Manager) Backends(kind reflect.Type) []Backend {
+ return am.backends[kind]
+}
+
+// Wallets returns all signer accounts registered under this account manager.
+func (am *Manager) Wallets() []Wallet {
+ am.lock.RLock()
+ defer am.lock.RUnlock()
+
+ cpy := make([]Wallet, len(am.wallets))
+ copy(cpy, am.wallets)
+ return cpy
+}
+
+// Wallet retrieves the wallet associated with a particular URL.
+func (am *Manager) Wallet(url string) (Wallet, error) {
+ am.lock.RLock()
+ defer am.lock.RUnlock()
+
+ parsed, err := parseURL(url)
+ if err != nil {
+ return nil, err
+ }
+ for _, wallet := range am.Wallets() {
+ if wallet.URL() == parsed {
+ return wallet, nil
+ }
+ }
+ return nil, ErrUnknownWallet
+}
+
+// Find attempts to locate the wallet corresponding to a specific account. Since
+// accounts can be dynamically added to and removed from wallets, this method has
+// a linear runtime in the number of wallets.
+func (am *Manager) Find(account Account) (Wallet, error) {
+ am.lock.RLock()
+ defer am.lock.RUnlock()
+
+ for _, wallet := range am.wallets {
+ if wallet.Contains(account) {
+ return wallet, nil
+ }
+ }
+ return nil, ErrUnknownAccount
+}
+
+// Subscribe creates an async subscription to receive notifications when the
+// manager detects the arrival or departure of a wallet from any of its backends.
+func (am *Manager) Subscribe(sink chan<- WalletEvent) event.Subscription {
+ return am.feed.Subscribe(sink)
+}
+
+// merge is a sorted analogue of append for wallets, where the ordering of the
+// origin list is preserved by inserting new wallets at the correct position.
+//
+// The original slice is assumed to be already sorted by URL.
+func merge(slice []Wallet, wallets ...Wallet) []Wallet {
+ for _, wallet := range wallets {
+ n := sort.Search(len(slice), func(i int) bool { return slice[i].URL().Cmp(wallet.URL()) >= 0 })
+ if n == len(slice) {
+ slice = append(slice, wallet)
+ continue
+ }
+ slice = append(slice[:n], append([]Wallet{wallet}, slice[n:]...)...)
+ }
+ return slice
+}
+
+// drop is the couterpart of merge, which looks up wallets from within the sorted
+// cache and removes the ones specified.
+func drop(slice []Wallet, wallets ...Wallet) []Wallet {
+ for _, wallet := range wallets {
+ n := sort.Search(len(slice), func(i int) bool { return slice[i].URL().Cmp(wallet.URL()) >= 0 })
+ if n == len(slice) {
+ // Wallet not found, may happen during startup
+ continue
+ }
+ slice = append(slice[:n], slice[n+1:]...)
+ }
+ return slice
+}
diff --git a/accounts/url.go b/accounts/url.go
new file mode 100644
index 000000000..a2d00c1c6
--- /dev/null
+++ b/accounts/url.go
@@ -0,0 +1,79 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum 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 go-ethereum 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 go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package accounts
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "strings"
+)
+
+// URL represents the canonical identification URL of a wallet or account.
+//
+// It is a simplified version of url.URL, with the important limitations (which
+// are considered features here) that it contains value-copyable components only,
+// as well as that it doesn't do any URL encoding/decoding of special characters.
+//
+// The former is important to allow an account to be copied without leaving live
+// references to the original version, whereas the latter is important to ensure
+// one single canonical form opposed to many allowed ones by the RFC 3986 spec.
+//
+// As such, these URLs should not be used outside of the scope of an Ethereum
+// wallet or account.
+type URL struct {
+ Scheme string // Protocol scheme to identify a capable account backend
+ Path string // Path for the backend to identify a unique entity
+}
+
+// parseURL converts a user supplied URL into the accounts specific structure.
+func parseURL(url string) (URL, error) {
+ parts := strings.Split(url, "://")
+ if len(parts) != 2 || parts[0] == "" {
+ return URL{}, errors.New("protocol scheme missing")
+ }
+ return URL{
+ Scheme: parts[0],
+ Path: parts[1],
+ }, nil
+}
+
+// String implements the stringer interface.
+func (u URL) String() string {
+ if u.Scheme != "" {
+ return fmt.Sprintf("%s://%s", u.Scheme, u.Path)
+ }
+ return u.Path
+}
+
+// MarshalJSON implements the json.Marshaller interface.
+func (u URL) MarshalJSON() ([]byte, error) {
+ return json.Marshal(u.String())
+}
+
+// Cmp compares x and y and returns:
+//
+// -1 if x < y
+// 0 if x == y
+// +1 if x > y
+//
+func (u URL) Cmp(url URL) int {
+ if u.Scheme == url.Scheme {
+ return strings.Compare(u.Path, url.Path)
+ }
+ return strings.Compare(u.Scheme, url.Scheme)
+}
diff --git a/accounts/usbwallet/ledger_hub.go b/accounts/usbwallet/ledger_hub.go
new file mode 100644
index 000000000..ad5940cd4
--- /dev/null
+++ b/accounts/usbwallet/ledger_hub.go
@@ -0,0 +1,209 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum 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 go-ethereum 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 go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+// This file contains the implementation for interacting with the Ledger hardware
+// wallets. The wire protocol spec can be found in the Ledger Blue GitHub repo:
+// https://raw.githubusercontent.com/LedgerHQ/blue-app-eth/master/doc/ethapp.asc
+
+// +build !ios
+
+package usbwallet
+
+import (
+ "fmt"
+ "sync"
+ "time"
+
+ "github.com/ethereum/go-ethereum/accounts"
+ "github.com/ethereum/go-ethereum/event"
+ "github.com/karalabe/gousb/usb"
+)
+
+// LedgerScheme is the protocol scheme prefixing account and wallet URLs.
+var LedgerScheme = "ledger"
+
+// ledgerDeviceIDs are the known device IDs that Ledger wallets use.
+var ledgerDeviceIDs = []deviceID{
+ {Vendor: 0x2c97, Product: 0x0000}, // Ledger Blue
+ {Vendor: 0x2c97, Product: 0x0001}, // Ledger Nano S
+}
+
+// Maximum time between wallet refreshes (if USB hotplug notifications don't work).
+const ledgerRefreshCycle = time.Second
+
+// Minimum time between wallet refreshes to avoid USB trashing.
+const ledgerRefreshThrottling = 500 * time.Millisecond
+
+// LedgerHub is a accounts.Backend that can find and handle Ledger hardware wallets.
+type LedgerHub struct {
+ ctx *usb.Context // Context interfacing with a libusb instance
+
+ refreshed time.Time // Time instance when the list of wallets was last refreshed
+ wallets []accounts.Wallet // List of Ledger devices currently tracking
+ updateFeed event.Feed // Event feed to notify wallet additions/removals
+ updateScope event.SubscriptionScope // Subscription scope tracking current live listeners
+ updating bool // Whether the event notification loop is running
+
+ quit chan chan error
+ lock sync.RWMutex
+}
+
+// NewLedgerHub creates a new hardware wallet manager for Ledger devices.
+func NewLedgerHub() (*LedgerHub, error) {
+ // Initialize the USB library to access Ledgers through
+ ctx, err := usb.NewContext()
+ if err != nil {
+ return nil, err
+ }
+ // Create the USB hub, start and return it
+ hub := &LedgerHub{
+ ctx: ctx,
+ quit: make(chan chan error),
+ }
+ hub.refreshWallets()
+
+ return hub, nil
+}
+
+// Wallets implements accounts.Backend, returning all the currently tracked USB
+// devices that appear to be Ledger hardware wallets.
+func (hub *LedgerHub) Wallets() []accounts.Wallet {
+ // Make sure the list of wallets is up to date
+ hub.refreshWallets()
+
+ hub.lock.RLock()
+ defer hub.lock.RUnlock()
+
+ cpy := make([]accounts.Wallet, len(hub.wallets))
+ copy(cpy, hub.wallets)
+ return cpy
+}
+
+// refreshWallets scans the USB devices attached to the machine and updates the
+// list of wallets based on the found devices.
+func (hub *LedgerHub) refreshWallets() {
+ // Don't scan the USB like crazy it the user fetches wallets in a loop
+ hub.lock.RLock()
+ elapsed := time.Since(hub.refreshed)
+ hub.lock.RUnlock()
+
+ if elapsed < ledgerRefreshThrottling {
+ return
+ }
+ // Retrieve the current list of Ledger devices
+ var devIDs []deviceID
+ var busIDs []uint16
+
+ hub.ctx.ListDevices(func(desc *usb.Descriptor) bool {
+ // Gather Ledger devices, don't connect any just yet
+ for _, id := range ledgerDeviceIDs {
+ if desc.Vendor == id.Vendor && desc.Product == id.Product {
+ devIDs = append(devIDs, deviceID{Vendor: desc.Vendor, Product: desc.Product})
+ busIDs = append(busIDs, uint16(desc.Bus)<<8+uint16(desc.Address))
+ return false
+ }
+ }
+ // Not ledger, ignore and don't connect either
+ return false
+ })
+ // Transform the current list of wallets into the new one
+ hub.lock.Lock()
+
+ wallets := make([]accounts.Wallet, 0, len(devIDs))
+ events := []accounts.WalletEvent{}
+
+ for i := 0; i < len(devIDs); i++ {
+ devID, busID := devIDs[i], busIDs[i]
+
+ url := accounts.URL{Scheme: LedgerScheme, Path: fmt.Sprintf("%03d:%03d", busID>>8, busID&0xff)}
+
+ // Drop wallets in front of the next device or those that failed for some reason
+ for len(hub.wallets) > 0 && (hub.wallets[0].URL().Cmp(url) < 0 || hub.wallets[0].(*ledgerWallet).failed()) {
+ events = append(events, accounts.WalletEvent{Wallet: hub.wallets[0], Arrive: false})
+ hub.wallets = hub.wallets[1:]
+ }
+ // If there are no more wallets or the device is before the next, wrap new wallet
+ if len(hub.wallets) == 0 || hub.wallets[0].URL().Cmp(url) > 0 {
+ wallet := &ledgerWallet{context: hub.ctx, hardwareID: devID, locationID: busID, url: &url}
+
+ events = append(events, accounts.WalletEvent{Wallet: wallet, Arrive: true})
+ wallets = append(wallets, wallet)
+ continue
+ }
+ // If the device is the same as the first wallet, keep it
+ if hub.wallets[0].URL().Cmp(url) == 0 {
+ wallets = append(wallets, hub.wallets[0])
+ hub.wallets = hub.wallets[1:]
+ continue
+ }
+ }
+ // Drop any leftover wallets and set the new batch
+ for _, wallet := range hub.wallets {
+ events = append(events, accounts.WalletEvent{Wallet: wallet, Arrive: false})
+ }
+ hub.refreshed = time.Now()
+ hub.wallets = wallets
+ hub.lock.Unlock()
+
+ // Fire all wallet events and return
+ for _, event := range events {
+ hub.updateFeed.Send(event)
+ }
+}
+
+// Subscribe implements accounts.Backend, creating an async subscription to
+// receive notifications on the addition or removal of Ledger wallets.
+func (hub *LedgerHub) Subscribe(sink chan<- accounts.WalletEvent) event.Subscription {
+ // We need the mutex to reliably start/stop the update loop
+ hub.lock.Lock()
+ defer hub.lock.Unlock()
+
+ // Subscribe the caller and track the subscriber count
+ sub := hub.updateScope.Track(hub.updateFeed.Subscribe(sink))
+
+ // Subscribers require an active notification loop, start it
+ if !hub.updating {
+ hub.updating = true
+ go hub.updater()
+ }
+ return sub
+}
+
+// updater is responsible for maintaining an up-to-date list of wallets stored in
+// the keystore, and for firing wallet addition/removal events. It listens for
+// account change events from the underlying account cache, and also periodically
+// forces a manual refresh (only triggers for systems where the filesystem notifier
+// is not running).
+func (hub *LedgerHub) updater() {
+ for {
+ // Wait for a USB hotplug event (not supported yet) or a refresh timeout
+ select {
+ //case <-hub.changes: // reenable on hutplug implementation
+ case <-time.After(ledgerRefreshCycle):
+ }
+ // Run the wallet refresher
+ hub.refreshWallets()
+
+ // If all our subscribers left, stop the updater
+ hub.lock.Lock()
+ if hub.updateScope.Count() == 0 {
+ hub.updating = false
+ hub.lock.Unlock()
+ return
+ }
+ hub.lock.Unlock()
+ }
+}
diff --git a/accounts/usbwallet/ledger_wallet.go b/accounts/usbwallet/ledger_wallet.go
new file mode 100644
index 000000000..a667f580a
--- /dev/null
+++ b/accounts/usbwallet/ledger_wallet.go
@@ -0,0 +1,945 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum 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 go-ethereum 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 go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+// This file contains the implementation for interacting with the Ledger hardware
+// wallets. The wire protocol spec can be found in the Ledger Blue GitHub repo:
+// https://raw.githubusercontent.com/LedgerHQ/blue-app-eth/master/doc/ethapp.asc
+
+// +build !ios
+
+package usbwallet
+
+import (
+ "encoding/binary"
+ "encoding/hex"
+ "errors"
+ "fmt"
+ "io"
+ "math/big"
+ "sync"
+ "time"
+
+ ethereum "github.com/ethereum/go-ethereum"
+ "github.com/ethereum/go-ethereum/accounts"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/logger"
+ "github.com/ethereum/go-ethereum/logger/glog"
+ "github.com/ethereum/go-ethereum/rlp"
+ "github.com/karalabe/gousb/usb"
+ "golang.org/x/net/context"
+)
+
+// Maximum time between wallet health checks to detect USB unplugs.
+const ledgerHeartbeatCycle = time.Second
+
+// Minimum time to wait between self derivation attempts, even it the user is
+// requesting accounts like crazy.
+const ledgerSelfDeriveThrottling = time.Second
+
+// ledgerOpcode is an enumeration encoding the supported Ledger opcodes.
+type ledgerOpcode byte
+
+// ledgerParam1 is an enumeration encoding the supported Ledger parameters for
+// specific opcodes. The same parameter values may be reused between opcodes.
+type ledgerParam1 byte
+
+// ledgerParam2 is an enumeration encoding the supported Ledger parameters for
+// specific opcodes. The same parameter values may be reused between opcodes.
+type ledgerParam2 byte
+
+const (
+ ledgerOpRetrieveAddress ledgerOpcode = 0x02 // Returns the public key and Ethereum address for a given BIP 32 path
+ ledgerOpSignTransaction ledgerOpcode = 0x04 // Signs an Ethereum transaction after having the user validate the parameters
+ ledgerOpGetConfiguration ledgerOpcode = 0x06 // Returns specific wallet application configuration
+
+ ledgerP1DirectlyFetchAddress ledgerParam1 = 0x00 // Return address directly from the wallet
+ ledgerP1ConfirmFetchAddress ledgerParam1 = 0x01 // Require a user confirmation before returning the address
+ ledgerP1InitTransactionData ledgerParam1 = 0x00 // First transaction data block for signing
+ ledgerP1ContTransactionData ledgerParam1 = 0x80 // Subsequent transaction data block for signing
+ ledgerP2DiscardAddressChainCode ledgerParam2 = 0x00 // Do not return the chain code along with the address
+ ledgerP2ReturnAddressChainCode ledgerParam2 = 0x01 // Require a user confirmation before returning the address
+)
+
+// errReplyInvalidHeader is the error message returned by a Ledfer data exchange
+// if the device replies with a mismatching header. This usually means the device
+// is in browser mode.
+var errReplyInvalidHeader = errors.New("invalid reply header")
+
+// ledgerWallet represents a live USB Ledger hardware wallet.
+type ledgerWallet struct {
+ context *usb.Context // USB context to interface libusb through
+ hardwareID deviceID // USB identifiers to identify this device type
+ locationID uint16 // USB bus and address to identify this device instance
+ url *accounts.URL // Textual URL uniquely identifying this wallet
+
+ device *usb.Device // USB device advertising itself as a Ledger wallet
+ input usb.Endpoint // Input endpoint to send data to this device
+ output usb.Endpoint // Output endpoint to receive data from this device
+ failure error // Any failure that would make the device unusable
+
+ version [3]byte // Current version of the Ledger Ethereum app (zero if app is offline)
+ browser bool // Flag whether the Ledger is in browser mode (reply channel mismatch)
+ accounts []accounts.Account // List of derive accounts pinned on the Ledger
+ paths map[common.Address]accounts.DerivationPath // Known derivation paths for signing operations
+
+ deriveNextPath accounts.DerivationPath // Next derivation path for account auto-discovery
+ deriveNextAddr common.Address // Next derived account address for auto-discovery
+ deriveChain ethereum.ChainStateReader // Blockchain state reader to discover used account with
+ deriveReq chan chan struct{} // Channel to request a self-derivation on
+ deriveQuit chan chan error // Channel to terminate the self-deriver with
+
+ healthQuit chan chan error
+
+ // Locking a hardware wallet is a bit special. Since hardware devices are lower
+ // performing, any communication with them might take a non negligible amount of
+ // time. Worse still, waiting for user confirmation can take arbitrarily long,
+ // but exclusive communication must be upheld during. Locking the entire wallet
+ // in the mean time however would stall any parts of the system that don't want
+ // to communicate, just read some state (e.g. list the accounts).
+ //
+ // As such, a hardware wallet needs two locks to function correctly. A state
+ // lock can be used to protect the wallet's software-side internal state, which
+ // must not be held exlusively during hardware communication. A communication
+ // lock can be used to achieve exclusive access to the device itself, this one
+ // however should allow "skipping" waiting for operations that might want to
+ // use the device, but can live without too (e.g. account self-derivation).
+ //
+ // Since we have two locks, it's important to know how to properly use them:
+ // - Communication requires the `device` to not change, so obtaining the
+ // commsLock should be done after having a stateLock.
+ // - Communication must not disable read access to the wallet state, so it
+ // must only ever hold a *read* lock to stateLock.
+ commsLock chan struct{} // Mutex (buf=1) for the USB comms without keeping the state locked
+ stateLock sync.RWMutex // Protects read and write access to the wallet struct fields
+}
+
+// URL implements accounts.Wallet, returning the URL of the Ledger device.
+func (w *ledgerWallet) URL() accounts.URL {
+ return *w.url // Immutable, no need for a lock
+}
+
+// Status implements accounts.Wallet, always whether the Ledger is opened, closed
+// or whether the Ethereum app was not started on it.
+func (w *ledgerWallet) Status() string {
+ w.stateLock.RLock() // No device communication, state lock is enough
+ defer w.stateLock.RUnlock()
+
+ if w.failure != nil {
+ return fmt.Sprintf("Failed: %v", w.failure)
+ }
+ if w.device == nil {
+ return "Closed"
+ }
+ if w.browser {
+ return "Ethereum app in browser mode"
+ }
+ if w.offline() {
+ return "Ethereum app offline"
+ }
+ return fmt.Sprintf("Ethereum app v%d.%d.%d online", w.version[0], w.version[1], w.version[2])
+}
+
+// offline returns whether the wallet and the Ethereum app is offline or not.
+//
+// The method assumes that the state lock is held!
+func (w *ledgerWallet) offline() bool {
+ return w.version == [3]byte{0, 0, 0}
+}
+
+// failed returns if the USB device wrapped by the wallet failed for some reason.
+// This is used by the device scanner to report failed wallets as departed.
+//
+// The method assumes that the state lock is *not* held!
+func (w *ledgerWallet) failed() bool {
+ w.stateLock.RLock() // No device communication, state lock is enough
+ defer w.stateLock.RUnlock()
+
+ return w.failure != nil
+}
+
+// Open implements accounts.Wallet, attempting to open a USB connection to the
+// Ledger hardware wallet. The Ledger does not require a user passphrase, so that
+// parameter is silently discarded.
+func (w *ledgerWallet) Open(passphrase string) error {
+ w.stateLock.Lock() // State lock is enough since there's no connection yet at this point
+ defer w.stateLock.Unlock()
+
+ // If the wallet was already opened, don't try to open again
+ if w.device != nil {
+ return accounts.ErrWalletAlreadyOpen
+ }
+ // Otherwise iterate over all USB devices and find this again (no way to directly do this)
+ // Iterate over all attached devices and fetch those seemingly Ledger
+ devices, err := w.context.ListDevices(func(desc *usb.Descriptor) bool {
+ // Only open this single specific device
+ return desc.Vendor == w.hardwareID.Vendor && desc.Product == w.hardwareID.Product &&
+ uint16(desc.Bus)<<8+uint16(desc.Address) == w.locationID
+ })
+ if err != nil {
+ return err
+ }
+ if len(devices) == 0 {
+ return accounts.ErrUnknownWallet
+ }
+ // Device opened, attach to the input and output endpoints
+ device := devices[0]
+
+ var invalid string
+ switch {
+ case len(device.Descriptor.Configs) == 0:
+ invalid = "no endpoint config available"
+ case len(device.Descriptor.Configs[0].Interfaces) == 0:
+ invalid = "no endpoint interface available"
+ case len(device.Descriptor.Configs[0].Interfaces[0].Setups) == 0:
+ invalid = "no endpoint setup available"
+ case len(device.Descriptor.Configs[0].Interfaces[0].Setups[0].Endpoints) < 2:
+ invalid = "not enough IO endpoints available"
+ }
+ if invalid != "" {
+ device.Close()
+ return fmt.Errorf("ledger wallet [%s] invalid: %s", w.url, invalid)
+ }
+ // Open the input and output endpoints to the device
+ input, err := device.OpenEndpoint(
+ device.Descriptor.Configs[0].Config,
+ device.Descriptor.Configs[0].Interfaces[0].Number,
+ device.Descriptor.Configs[0].Interfaces[0].Setups[0].Number,
+ device.Descriptor.Configs[0].Interfaces[0].Setups[0].Endpoints[1].Address,
+ )
+ if err != nil {
+ device.Close()
+ return fmt.Errorf("ledger wallet [%s] input open failed: %v", w.url, err)
+ }
+ output, err := device.OpenEndpoint(
+ device.Descriptor.Configs[0].Config,
+ device.Descriptor.Configs[0].Interfaces[0].Number,
+ device.Descriptor.Configs[0].Interfaces[0].Setups[0].Number,
+ device.Descriptor.Configs[0].Interfaces[0].Setups[0].Endpoints[0].Address,
+ )
+ if err != nil {
+ device.Close()
+ return fmt.Errorf("ledger wallet [%s] output open failed: %v", w.url, err)
+ }
+ // Wallet seems to be successfully opened, guess if the Ethereum app is running
+ w.device, w.input, w.output = device, input, output
+ w.commsLock = make(chan struct{}, 1)
+ w.commsLock <- struct{}{} // Enable lock
+
+ w.paths = make(map[common.Address]accounts.DerivationPath)
+
+ w.deriveReq = make(chan chan struct{})
+ w.deriveQuit = make(chan chan error)
+ w.healthQuit = make(chan chan error)
+
+ defer func() {
+ go w.heartbeat()
+ go w.selfDerive()
+ }()
+
+ if _, err = w.ledgerDerive(accounts.DefaultBaseDerivationPath); err != nil {
+ // Ethereum app is not running or in browser mode, nothing more to do, return
+ if err == errReplyInvalidHeader {
+ w.browser = true
+ }
+ return nil
+ }
+ // Try to resolve the Ethereum app's version, will fail prior to v1.0.2
+ if w.version, err = w.ledgerVersion(); err != nil {
+ w.version = [3]byte{1, 0, 0} // Assume worst case, can't verify if v1.0.0 or v1.0.1
+ }
+ return nil
+}
+
+// heartbeat is a health check loop for the Ledger wallets to periodically verify
+// whether they are still present or if they malfunctioned. It is needed because:
+// - libusb on Windows doesn't support hotplug, so we can't detect USB unplugs
+// - communication timeout on the Ledger requires a device power cycle to fix
+func (w *ledgerWallet) heartbeat() {
+ glog.V(logger.Debug).Infof("%s health-check started", w.url.String())
+ defer glog.V(logger.Debug).Infof("%s health-check stopped", w.url.String())
+
+ // Execute heartbeat checks until termination or error
+ var (
+ errc chan error
+ err error
+ )
+ for errc == nil && err == nil {
+ // Wait until termination is requested or the heartbeat cycle arrives
+ select {
+ case errc = <-w.healthQuit:
+ // Termination requested
+ continue
+ case <-time.After(ledgerHeartbeatCycle):
+ // Heartbeat time
+ }
+ // Execute a tiny data exchange to see responsiveness
+ w.stateLock.RLock()
+ if w.device == nil {
+ // Terminated while waiting for the lock
+ w.stateLock.RUnlock()
+ continue
+ }
+ <-w.commsLock // Don't lock state while resolving version
+ _, err = w.ledgerVersion()
+ w.commsLock <- struct{}{}
+ w.stateLock.RUnlock()
+
+ if err == usb.ERROR_IO || err == usb.ERROR_NO_DEVICE {
+ w.stateLock.Lock() // Lock state to tear the wallet down
+ w.failure = err
+ w.close()
+ w.stateLock.Unlock()
+ }
+ // Ignore uninteresting errors
+ err = nil
+ }
+ // In case of error, wait for termination
+ if err != nil {
+ glog.V(logger.Debug).Infof("%s health-check failed: %v", w.url.String(), err)
+ errc = <-w.healthQuit
+ }
+ errc <- err
+}
+
+// Close implements accounts.Wallet, closing the USB connection to the Ledger.
+func (w *ledgerWallet) Close() error {
+ // Ensure the wallet was opened
+ w.stateLock.RLock()
+ hQuit, dQuit := w.healthQuit, w.deriveQuit
+ w.stateLock.RUnlock()
+
+ // Terminate the health checks
+ var herr error
+ if hQuit != nil {
+ errc := make(chan error)
+ hQuit <- errc
+ herr = <-errc // Save for later, we *must* close the USB
+ }
+ // Terminate the self-derivations
+ var derr error
+ if dQuit != nil {
+ errc := make(chan error)
+ dQuit <- errc
+ derr = <-errc // Save for later, we *must* close the USB
+ }
+ // Terminate the device connection
+ w.stateLock.Lock()
+ defer w.stateLock.Unlock()
+
+ w.healthQuit = nil
+ w.deriveQuit = nil
+ w.deriveReq = nil
+
+ if err := w.close(); err != nil {
+ return err
+ }
+ if herr != nil {
+ return herr
+ }
+ return derr
+}
+
+// close is the internal wallet closer that terminates the USB connection and
+// resets all the fields to their defaults.
+//
+// Note, close assumes the state lock is held!
+func (w *ledgerWallet) close() error {
+ // Allow duplicate closes, especially for health-check failures
+ if w.device == nil {
+ return nil
+ }
+ // Close the device, clear everything, then return
+ err := w.device.Close()
+
+ w.device, w.input, w.output = nil, nil, nil
+ w.browser, w.version = false, [3]byte{}
+ w.accounts, w.paths = nil, nil
+
+ return err
+}
+
+// Accounts implements accounts.Wallet, returning the list of accounts pinned to
+// the Ledger hardware wallet. If self-derivation was enabled, the account list
+// is periodically expanded based on current chain state.
+func (w *ledgerWallet) Accounts() []accounts.Account {
+ // Attempt self-derivation if it's running
+ reqc := make(chan struct{}, 1)
+ select {
+ case w.deriveReq <- reqc:
+ // Self-derivation request accepted, wait for it
+ <-reqc
+ default:
+ // Self-derivation offline, throttled or busy, skip
+ }
+ // Return whatever account list we ended up with
+ w.stateLock.RLock()
+ defer w.stateLock.RUnlock()
+
+ cpy := make([]accounts.Account, len(w.accounts))
+ copy(cpy, w.accounts)
+ return cpy
+}
+
+// selfDerive is an account derivation loop that upon request attempts to find
+// new non-zero accounts.
+func (w *ledgerWallet) selfDerive() {
+ glog.V(logger.Debug).Infof("%s self-derivation started", w.url.String())
+ defer glog.V(logger.Debug).Infof("%s self-derivation stopped", w.url.String())
+
+ // Execute self-derivations until termination or error
+ var (
+ reqc chan struct{}
+ errc chan error
+ err error
+ )
+ for errc == nil && err == nil {
+ // Wait until either derivation or termination is requested
+ select {
+ case errc = <-w.deriveQuit:
+ // Termination requested
+ continue
+ case reqc = <-w.deriveReq:
+ // Account discovery requested
+ }
+ // Derivation needs a chain and device access, skip if either unavailable
+ w.stateLock.RLock()
+ if w.device == nil || w.deriveChain == nil || w.offline() {
+ w.stateLock.RUnlock()
+ reqc <- struct{}{}
+ continue
+ }
+ select {
+ case <-w.commsLock:
+ default:
+ w.stateLock.RUnlock()
+ reqc <- struct{}{}
+ continue
+ }
+ // Device lock obtained, derive the next batch of accounts
+ var (
+ accs []accounts.Account
+ paths []accounts.DerivationPath
+
+ nextAddr = w.deriveNextAddr
+ nextPath = w.deriveNextPath
+
+ context = context.Background()
+ )
+ for empty := false; !empty; {
+ // Retrieve the next derived Ethereum account
+ if nextAddr == (common.Address{}) {
+ if nextAddr, err = w.ledgerDerive(nextPath); err != nil {
+ glog.V(logger.Warn).Infof("%s self-derivation failed: %v", w.url.String(), err)
+ break
+ }
+ }
+ // Check the account's status against the current chain state
+ var (
+ balance *big.Int
+ nonce uint64
+ )
+ balance, err = w.deriveChain.BalanceAt(context, nextAddr, nil)
+ if err != nil {
+ glog.V(logger.Warn).Infof("%s self-derivation balance retrieval failed: %v", w.url.String(), err)
+ break
+ }
+ nonce, err = w.deriveChain.NonceAt(context, nextAddr, nil)
+ if err != nil {
+ glog.V(logger.Warn).Infof("%s self-derivation nonce retrieval failed: %v", w.url.String(), err)
+ break
+ }
+ // If the next account is empty, stop self-derivation, but add it nonetheless
+ if balance.BitLen() == 0 && nonce == 0 {
+ empty = true
+ }
+ // We've just self-derived a new account, start tracking it locally
+ path := make(accounts.DerivationPath, len(nextPath))
+ copy(path[:], nextPath[:])
+ paths = append(paths, path)
+
+ account := accounts.Account{
+ Address: nextAddr,
+ URL: accounts.URL{Scheme: w.url.Scheme, Path: fmt.Sprintf("%s/%s", w.url.Path, path)},
+ }
+ accs = append(accs, account)
+
+ // Display a log message to the user for new (or previously empty accounts)
+ if _, known := w.paths[nextAddr]; !known || (!empty && nextAddr == w.deriveNextAddr) {
+ glog.V(logger.Info).Infof("%s discovered %s (balance %22v, nonce %4d) at %s", w.url.String(), nextAddr.Hex(), balance, nonce, path)
+ }
+ // Fetch the next potential account
+ if !empty {
+ nextAddr = common.Address{}
+ nextPath[len(nextPath)-1]++
+ }
+ }
+ // Self derivation complete, release device lock
+ w.commsLock <- struct{}{}
+ w.stateLock.RUnlock()
+
+ // Insert any accounts successfully derived
+ w.stateLock.Lock()
+ for i := 0; i < len(accs); i++ {
+ if _, ok := w.paths[accs[i].Address]; !ok {
+ w.accounts = append(w.accounts, accs[i])
+ w.paths[accs[i].Address] = paths[i]
+ }
+ }
+ // Shift the self-derivation forward
+ // TODO(karalabe): don't overwrite changes from wallet.SelfDerive
+ w.deriveNextAddr = nextAddr
+ w.deriveNextPath = nextPath
+ w.stateLock.Unlock()
+
+ // Notify the user of termination and loop after a bit of time (to avoid trashing)
+ reqc <- struct{}{}
+ if err == nil {
+ select {
+ case errc = <-w.deriveQuit:
+ // Termination requested, abort
+ case <-time.After(ledgerSelfDeriveThrottling):
+ // Waited enough, willing to self-derive again
+ }
+ }
+ }
+ // In case of error, wait for termination
+ if err != nil {
+ glog.V(logger.Debug).Infof("%s self-derivation failed: %s", w.url.String(), err)
+ errc = <-w.deriveQuit
+ }
+ errc <- err
+}
+
+// Contains implements accounts.Wallet, returning whether a particular account is
+// or is not pinned into this Ledger instance. Although we could attempt to resolve
+// unpinned accounts, that would be an non-negligible hardware operation.
+func (w *ledgerWallet) Contains(account accounts.Account) bool {
+ w.stateLock.RLock()
+ defer w.stateLock.RUnlock()
+
+ _, exists := w.paths[account.Address]
+ return exists
+}
+
+// Derive implements accounts.Wallet, deriving a new account at the specific
+// derivation path. If pin is set to true, the account will be added to the list
+// of tracked accounts.
+func (w *ledgerWallet) Derive(path accounts.DerivationPath, pin bool) (accounts.Account, error) {
+ // Try to derive the actual account and update its URL if successful
+ w.stateLock.RLock() // Avoid device disappearing during derivation
+
+ if w.device == nil || w.offline() {
+ w.stateLock.RUnlock()
+ return accounts.Account{}, accounts.ErrWalletClosed
+ }
+ <-w.commsLock // Avoid concurrent hardware access
+ address, err := w.ledgerDerive(path)
+ w.commsLock <- struct{}{}
+
+ w.stateLock.RUnlock()
+
+ // If an error occurred or no pinning was requested, return
+ if err != nil {
+ return accounts.Account{}, err
+ }
+ account := accounts.Account{
+ Address: address,
+ URL: accounts.URL{Scheme: w.url.Scheme, Path: fmt.Sprintf("%s/%s", w.url.Path, path)},
+ }
+ if !pin {
+ return account, nil
+ }
+ // Pinning needs to modify the state
+ w.stateLock.Lock()
+ defer w.stateLock.Unlock()
+
+ if _, ok := w.paths[address]; !ok {
+ w.accounts = append(w.accounts, account)
+ w.paths[address] = path
+ }
+ return account, nil
+}
+
+// SelfDerive implements accounts.Wallet, trying to discover accounts that the
+// user used previously (based on the chain state), but ones that he/she did not
+// explicitly pin to the wallet manually. To avoid chain head monitoring, self
+// derivation only runs during account listing (and even then throttled).
+func (w *ledgerWallet) SelfDerive(base accounts.DerivationPath, chain ethereum.ChainStateReader) {
+ w.stateLock.Lock()
+ defer w.stateLock.Unlock()
+
+ w.deriveNextPath = make(accounts.DerivationPath, len(base))
+ copy(w.deriveNextPath[:], base[:])
+
+ w.deriveNextAddr = common.Address{}
+ w.deriveChain = chain
+}
+
+// SignHash implements accounts.Wallet, however signing arbitrary data is not
+// supported for Ledger wallets, so this method will always return an error.
+func (w *ledgerWallet) SignHash(acc accounts.Account, hash []byte) ([]byte, error) {
+ return nil, accounts.ErrNotSupported
+}
+
+// SignTx implements accounts.Wallet. It sends the transaction over to the Ledger
+// wallet to request a confirmation from the user. It returns either the signed
+// transaction or a failure if the user denied the transaction.
+//
+// Note, if the version of the Ethereum application running on the Ledger wallet is
+// too old to sign EIP-155 transactions, but such is requested nonetheless, an error
+// will be returned opposed to silently signing in Homestead mode.
+func (w *ledgerWallet) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
+ w.stateLock.RLock() // Comms have own mutex, this is for the state fields
+ defer w.stateLock.RUnlock()
+
+ // If the wallet is closed, or the Ethereum app doesn't run, abort
+ if w.device == nil || w.offline() {
+ return nil, accounts.ErrWalletClosed
+ }
+ // Make sure the requested account is contained within
+ path, ok := w.paths[account.Address]
+ if !ok {
+ return nil, accounts.ErrUnknownAccount
+ }
+ // Ensure the wallet is capable of signing the given transaction
+ if chainID != nil && w.version[0] <= 1 && w.version[1] <= 0 && w.version[2] <= 2 {
+ return nil, fmt.Errorf("Ledger v%d.%d.%d doesn't support signing this transaction, please update to v1.0.3 at least", w.version[0], w.version[1], w.version[2])
+ }
+ // All infos gathered and metadata checks out, request signing
+ <-w.commsLock
+ defer func() { w.commsLock <- struct{}{} }()
+
+ return w.ledgerSign(path, account.Address, tx, chainID)
+}
+
+// SignHashWithPassphrase implements accounts.Wallet, however signing arbitrary
+// data is not supported for Ledger wallets, so this method will always return
+// an error.
+func (w *ledgerWallet) SignHashWithPassphrase(account accounts.Account, passphrase string, hash []byte) ([]byte, error) {
+ return nil, accounts.ErrNotSupported
+}
+
+// SignTxWithPassphrase implements accounts.Wallet, attempting to sign the given
+// transaction with the given account using passphrase as extra authentication.
+// Since the Ledger does not support extra passphrases, it is silently ignored.
+func (w *ledgerWallet) SignTxWithPassphrase(account accounts.Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
+ return w.SignTx(account, tx, chainID)
+}
+
+// ledgerVersion retrieves the current version of the Ethereum wallet app running
+// on the Ledger wallet.
+//
+// The version retrieval protocol is defined as follows:
+//
+// CLA | INS | P1 | P2 | Lc | Le
+// ----+-----+----+----+----+---
+// E0 | 06 | 00 | 00 | 00 | 04
+//
+// With no input data, and the output data being:
+//
+// Description | Length
+// ---------------------------------------------------+--------
+// Flags 01: arbitrary data signature enabled by user | 1 byte
+// Application major version | 1 byte
+// Application minor version | 1 byte
+// Application patch version | 1 byte
+func (w *ledgerWallet) ledgerVersion() ([3]byte, error) {
+ // Send the request and wait for the response
+ reply, err := w.ledgerExchange(ledgerOpGetConfiguration, 0, 0, nil)
+ if err != nil {
+ return [3]byte{}, err
+ }
+ if len(reply) != 4 {
+ return [3]byte{}, errors.New("reply not of correct size")
+ }
+ // Cache the version for future reference
+ var version [3]byte
+ copy(version[:], reply[1:])
+ return version, nil
+}
+
+// ledgerDerive retrieves the currently active Ethereum address from a Ledger
+// wallet at the specified derivation path.
+//
+// The address derivation protocol is defined as follows:
+//
+// CLA | INS | P1 | P2 | Lc | Le
+// ----+-----+----+----+-----+---
+// E0 | 02 | 00 return address
+// 01 display address and confirm before returning
+// | 00: do not return the chain code
+// | 01: return the chain code
+// | var | 00
+//
+// Where the input data is:
+//
+// Description | Length
+// -------------------------------------------------+--------
+// Number of BIP 32 derivations to perform (max 10) | 1 byte
+// First derivation index (big endian) | 4 bytes
+// ... | 4 bytes
+// Last derivation index (big endian) | 4 bytes
+//
+// And the output data is:
+//
+// Description | Length
+// ------------------------+-------------------
+// Public Key length | 1 byte
+// Uncompressed Public Key | arbitrary
+// Ethereum address length | 1 byte
+// Ethereum address | 40 bytes hex ascii
+// Chain code if requested | 32 bytes
+func (w *ledgerWallet) ledgerDerive(derivationPath []uint32) (common.Address, error) {
+ // Flatten the derivation path into the Ledger request
+ path := make([]byte, 1+4*len(derivationPath))
+ path[0] = byte(len(derivationPath))
+ for i, component := range derivationPath {
+ binary.BigEndian.PutUint32(path[1+4*i:], component)
+ }
+ // Send the request and wait for the response
+ reply, err := w.ledgerExchange(ledgerOpRetrieveAddress, ledgerP1DirectlyFetchAddress, ledgerP2DiscardAddressChainCode, path)
+ if err != nil {
+ return common.Address{}, err
+ }
+ // Discard the public key, we don't need that for now
+ if len(reply) < 1 || len(reply) < 1+int(reply[0]) {
+ return common.Address{}, errors.New("reply lacks public key entry")
+ }
+ reply = reply[1+int(reply[0]):]
+
+ // Extract the Ethereum hex address string
+ if len(reply) < 1 || len(reply) < 1+int(reply[0]) {
+ return common.Address{}, errors.New("reply lacks address entry")
+ }
+ hexstr := reply[1 : 1+int(reply[0])]
+
+ // Decode the hex sting into an Ethereum address and return
+ var address common.Address
+ hex.Decode(address[:], hexstr)
+ return address, nil
+}
+
+// ledgerSign sends the transaction to the Ledger wallet, and waits for the user
+// to confirm or deny the transaction.
+//
+// The transaction signing protocol is defined as follows:
+//
+// CLA | INS | P1 | P2 | Lc | Le
+// ----+-----+----+----+-----+---
+// E0 | 04 | 00: first transaction data block
+// 80: subsequent transaction data block
+// | 00 | variable | variable
+//
+// Where the input for the first transaction block (first 255 bytes) is:
+//
+// Description | Length
+// -------------------------------------------------+----------
+// Number of BIP 32 derivations to perform (max 10) | 1 byte
+// First derivation index (big endian) | 4 bytes
+// ... | 4 bytes
+// Last derivation index (big endian) | 4 bytes
+// RLP transaction chunk | arbitrary
+//
+// And the input for subsequent transaction blocks (first 255 bytes) are:
+//
+// Description | Length
+// ----------------------+----------
+// RLP transaction chunk | arbitrary
+//
+// And the output data is:
+//
+// Description | Length
+// ------------+---------
+// signature V | 1 byte
+// signature R | 32 bytes
+// signature S | 32 bytes
+func (w *ledgerWallet) ledgerSign(derivationPath []uint32, address common.Address, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
+ // We need to modify the timeouts to account for user feedback
+ defer func(old time.Duration) { w.device.ReadTimeout = old }(w.device.ReadTimeout)
+ w.device.ReadTimeout = time.Hour * 24 * 30 // Timeout requires a Ledger power cycle, only if you must
+
+ // Flatten the derivation path into the Ledger request
+ path := make([]byte, 1+4*len(derivationPath))
+ path[0] = byte(len(derivationPath))
+ for i, component := range derivationPath {
+ binary.BigEndian.PutUint32(path[1+4*i:], component)
+ }
+ // Create the transaction RLP based on whether legacy or EIP155 signing was requeste
+ var (
+ txrlp []byte
+ err error
+ )
+ if chainID == nil {
+ if txrlp, err = rlp.EncodeToBytes([]interface{}{tx.Nonce(), tx.GasPrice(), tx.Gas(), tx.To(), tx.Value(), tx.Data()}); err != nil {
+ return nil, err
+ }
+ } else {
+ if txrlp, err = rlp.EncodeToBytes([]interface{}{tx.Nonce(), tx.GasPrice(), tx.Gas(), tx.To(), tx.Value(), tx.Data(), chainID, big.NewInt(0), big.NewInt(0)}); err != nil {
+ return nil, err
+ }
+ }
+ payload := append(path, txrlp...)
+
+ // Send the request and wait for the response
+ var (
+ op = ledgerP1InitTransactionData
+ reply []byte
+ )
+ for len(payload) > 0 {
+ // Calculate the size of the next data chunk
+ chunk := 255
+ if chunk > len(payload) {
+ chunk = len(payload)
+ }
+ // Send the chunk over, ensuring it's processed correctly
+ reply, err = w.ledgerExchange(ledgerOpSignTransaction, op, 0, payload[:chunk])
+ if err != nil {
+ return nil, err
+ }
+ // Shift the payload and ensure subsequent chunks are marked as such
+ payload = payload[chunk:]
+ op = ledgerP1ContTransactionData
+ }
+ // Extract the Ethereum signature and do a sanity validation
+ if len(reply) != 65 {
+ return nil, errors.New("reply lacks signature")
+ }
+ signature := append(reply[1:], reply[0])
+
+ // Create the correct signer and signature transform based on the chain ID
+ var signer types.Signer
+ if chainID == nil {
+ signer = new(types.HomesteadSigner)
+ } else {
+ signer = types.NewEIP155Signer(chainID)
+ signature[64] = signature[64] - byte(chainID.Uint64()*2+35)
+ }
+ // Inject the final signature into the transaction and sanity check the sender
+ signed, err := tx.WithSignature(signer, signature)
+ if err != nil {
+ return nil, err
+ }
+ sender, err := types.Sender(signer, signed)
+ if err != nil {
+ return nil, err
+ }
+ if sender != address {
+ return nil, fmt.Errorf("signer mismatch: expected %s, got %s", address.Hex(), sender.Hex())
+ }
+ return signed, nil
+}
+
+// ledgerExchange performs a data exchange with the Ledger wallet, sending it a
+// message and retrieving the response.
+//
+// The common transport header is defined as follows:
+//
+// Description | Length
+// --------------------------------------+----------
+// Communication channel ID (big endian) | 2 bytes
+// Command tag | 1 byte
+// Packet sequence index (big endian) | 2 bytes
+// Payload | arbitrary
+//
+// The Communication channel ID allows commands multiplexing over the same
+// physical link. It is not used for the time being, and should be set to 0101
+// to avoid compatibility issues with implementations ignoring a leading 00 byte.
+//
+// The Command tag describes the message content. Use TAG_APDU (0x05) for standard
+// APDU payloads, or TAG_PING (0x02) for a simple link test.
+//
+// The Packet sequence index describes the current sequence for fragmented payloads.
+// The first fragment index is 0x00.
+//
+// APDU Command payloads are encoded as follows:
+//
+// Description | Length
+// -----------------------------------
+// APDU length (big endian) | 2 bytes
+// APDU CLA | 1 byte
+// APDU INS | 1 byte
+// APDU P1 | 1 byte
+// APDU P2 | 1 byte
+// APDU length | 1 byte
+// Optional APDU data | arbitrary
+func (w *ledgerWallet) ledgerExchange(opcode ledgerOpcode, p1 ledgerParam1, p2 ledgerParam2, data []byte) ([]byte, error) {
+ // Construct the message payload, possibly split into multiple chunks
+ apdu := make([]byte, 2, 7+len(data))
+
+ binary.BigEndian.PutUint16(apdu, uint16(5+len(data)))
+ apdu = append(apdu, []byte{0xe0, byte(opcode), byte(p1), byte(p2), byte(len(data))}...)
+ apdu = append(apdu, data...)
+
+ // Stream all the chunks to the device
+ header := []byte{0x01, 0x01, 0x05, 0x00, 0x00} // Channel ID and command tag appended
+ chunk := make([]byte, 64)
+ space := len(chunk) - len(header)
+
+ for i := 0; len(apdu) > 0; i++ {
+ // Construct the new message to stream
+ chunk = append(chunk[:0], header...)
+ binary.BigEndian.PutUint16(chunk[3:], uint16(i))
+
+ if len(apdu) > space {
+ chunk = append(chunk, apdu[:space]...)
+ apdu = apdu[space:]
+ } else {
+ chunk = append(chunk, apdu...)
+ apdu = nil
+ }
+ // Send over to the device
+ if glog.V(logger.Detail) {
+ glog.Infof("-> %03d.%03d: %x", w.device.Bus, w.device.Address, chunk)
+ }
+ if _, err := w.input.Write(chunk); err != nil {
+ return nil, err
+ }
+ }
+ // Stream the reply back from the wallet in 64 byte chunks
+ var reply []byte
+ chunk = chunk[:64] // Yeah, we surely have enough space
+ for {
+ // Read the next chunk from the Ledger wallet
+ if _, err := io.ReadFull(w.output, chunk); err != nil {
+ return nil, err
+ }
+ if glog.V(logger.Detail) {
+ glog.Infof("<- %03d.%03d: %x", w.device.Bus, w.device.Address, chunk)
+ }
+ // Make sure the transport header matches
+ if chunk[0] != 0x01 || chunk[1] != 0x01 || chunk[2] != 0x05 {
+ return nil, errReplyInvalidHeader
+ }
+ // If it's the first chunk, retrieve the total message length
+ var payload []byte
+
+ if chunk[3] == 0x00 && chunk[4] == 0x00 {
+ reply = make([]byte, 0, int(binary.BigEndian.Uint16(chunk[5:7])))
+ payload = chunk[7:]
+ } else {
+ payload = chunk[5:]
+ }
+ // Append to the reply and stop when filled up
+ if left := cap(reply) - len(reply); left > len(payload) {
+ reply = append(reply, payload...)
+ } else {
+ reply = append(reply, payload[:left]...)
+ break
+ }
+ }
+ return reply[:len(reply)-2], nil
+}
diff --git a/accounts/usbwallet/usbwallet.go b/accounts/usbwallet/usbwallet.go
new file mode 100644
index 000000000..3989f3d02
--- /dev/null
+++ b/accounts/usbwallet/usbwallet.go
@@ -0,0 +1,29 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum 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 go-ethereum 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 go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+// +build !ios
+
+// Package usbwallet implements support for USB hardware wallets.
+package usbwallet
+
+import "github.com/karalabe/gousb/usb"
+
+// deviceID is a combined vendor/product identifier to uniquely identify a USB
+// hardware device.
+type deviceID struct {
+ Vendor usb.ID // The Vendor identifer
+ Product usb.ID // The Product identifier
+}
diff --git a/accounts/usbwallet/usbwallet_ios.go b/accounts/usbwallet/usbwallet_ios.go
new file mode 100644
index 000000000..17d342903
--- /dev/null
+++ b/accounts/usbwallet/usbwallet_ios.go
@@ -0,0 +1,38 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum 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 go-ethereum 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 go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+// This file contains the implementation for interacting with the Ledger hardware
+// wallets. The wire protocol spec can be found in the Ledger Blue GitHub repo:
+// https://raw.githubusercontent.com/LedgerHQ/blue-app-eth/master/doc/ethapp.asc
+
+// +build ios
+
+package usbwallet
+
+import (
+ "errors"
+
+ "github.com/ethereum/go-ethereum/accounts"
+)
+
+// Here be dragons! There is no USB support on iOS.
+
+// ErrIOSNotSupported is returned for all USB hardware backends on iOS.
+var ErrIOSNotSupported = errors.New("no USB support on iOS")
+
+func NewLedgerHub() (accounts.Backend, error) {
+ return nil, ErrIOSNotSupported
+}
diff --git a/build/ci.go b/build/ci.go
index 51540f9b6..d8c76567c 100644
--- a/build/ci.go
+++ b/build/ci.go
@@ -236,6 +236,14 @@ func goToolArch(arch string, subcmd string, args ...string) *exec.Cmd {
gocmd := filepath.Join(runtime.GOROOT(), "bin", "go")
cmd := exec.Command(gocmd, subcmd)
cmd.Args = append(cmd.Args, args...)
+
+ if subcmd == "build" || subcmd == "install" || subcmd == "test" {
+ // Go CGO has a Windows linker error prior to 1.8 (https://github.com/golang/go/issues/8756).
+ // Work around issue by allowing multiple definitions for <1.8 builds.
+ if runtime.GOOS == "windows" && runtime.Version() < "go1.8" {
+ cmd.Args = append(cmd.Args, []string{"-ldflags", "-extldflags -Wl,--allow-multiple-definition"}...)
+ }
+ }
cmd.Env = []string{
"GO15VENDOREXPERIMENT=1",
"GOPATH=" + build.GOPATH(),
diff --git a/cmd/geth/accountcmd.go b/cmd/geth/accountcmd.go
index 237af99eb..cd398eadb 100644
--- a/cmd/geth/accountcmd.go
+++ b/cmd/geth/accountcmd.go
@@ -21,6 +21,7 @@ import (
"io/ioutil"
"github.com/ethereum/go-ethereum/accounts"
+ "github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/console"
"github.com/ethereum/go-ethereum/crypto"
@@ -180,31 +181,36 @@ nodes.
func accountList(ctx *cli.Context) error {
stack := utils.MakeNode(ctx, clientIdentifier, gitCommit)
- for i, acct := range stack.AccountManager().Accounts() {
- fmt.Printf("Account #%d: {%x} %s\n", i, acct.Address, acct.File)
+
+ var index int
+ for _, wallet := range stack.AccountManager().Wallets() {
+ for _, account := range wallet.Accounts() {
+ fmt.Printf("Account #%d: {%x} %s\n", index, account.Address, &account.URL)
+ index++
+ }
}
return nil
}
// tries unlocking the specified account a few times.
-func unlockAccount(ctx *cli.Context, accman *accounts.Manager, address string, i int, passwords []string) (accounts.Account, string) {
- account, err := utils.MakeAddress(accman, address)
+func unlockAccount(ctx *cli.Context, ks *keystore.KeyStore, address string, i int, passwords []string) (accounts.Account, string) {
+ account, err := utils.MakeAddress(ks, address)
if err != nil {
utils.Fatalf("Could not list accounts: %v", err)
}
for trials := 0; trials < 3; trials++ {
prompt := fmt.Sprintf("Unlocking account %s | Attempt %d/%d", address, trials+1, 3)
password := getPassPhrase(prompt, false, i, passwords)
- err = accman.Unlock(account, password)
+ err = ks.Unlock(account, password)
if err == nil {
glog.V(logger.Info).Infof("Unlocked account %x", account.Address)
return account, password
}
- if err, ok := err.(*accounts.AmbiguousAddrError); ok {
+ if err, ok := err.(*keystore.AmbiguousAddrError); ok {
glog.V(logger.Info).Infof("Unlocked account %x", account.Address)
- return ambiguousAddrRecovery(accman, err, password), password
+ return ambiguousAddrRecovery(ks, err, password), password
}
- if err != accounts.ErrDecrypt {
+ if err != keystore.ErrDecrypt {
// No need to prompt again if the error is not decryption-related.
break
}
@@ -244,15 +250,15 @@ func getPassPhrase(prompt string, confirmation bool, i int, passwords []string)
return password
}
-func ambiguousAddrRecovery(am *accounts.Manager, err *accounts.AmbiguousAddrError, auth string) accounts.Account {
+func ambiguousAddrRecovery(ks *keystore.KeyStore, err *keystore.AmbiguousAddrError, auth string) accounts.Account {
fmt.Printf("Multiple key files exist for address %x:\n", err.Addr)
for _, a := range err.Matches {
- fmt.Println(" ", a.File)
+ fmt.Println(" ", a.URL)
}
fmt.Println("Testing your passphrase against all of them...")
var match *accounts.Account
for _, a := range err.Matches {
- if err := am.Unlock(a, auth); err == nil {
+ if err := ks.Unlock(a, auth); err == nil {
match = &a
break
}
@@ -260,11 +266,11 @@ func ambiguousAddrRecovery(am *accounts.Manager, err *accounts.AmbiguousAddrErro
if match == nil {
utils.Fatalf("None of the listed files could be unlocked.")
}
- fmt.Printf("Your passphrase unlocked %s\n", match.File)
+ fmt.Printf("Your passphrase unlocked %s\n", match.URL)
fmt.Println("In order to avoid this warning, you need to remove the following duplicate key files:")
for _, a := range err.Matches {
if a != *match {
- fmt.Println(" ", a.File)
+ fmt.Println(" ", a.URL)
}
}
return *match
@@ -275,7 +281,8 @@ func accountCreate(ctx *cli.Context) error {
stack := utils.MakeNode(ctx, clientIdentifier, gitCommit)
password := getPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx))
- account, err := stack.AccountManager().NewAccount(password)
+ ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
+ account, err := ks.NewAccount(password)
if err != nil {
utils.Fatalf("Failed to create account: %v", err)
}
@@ -290,9 +297,11 @@ func accountUpdate(ctx *cli.Context) error {
utils.Fatalf("No accounts specified to update")
}
stack := utils.MakeNode(ctx, clientIdentifier, gitCommit)
- account, oldPassword := unlockAccount(ctx, stack.AccountManager(), ctx.Args().First(), 0, nil)
+ ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
+
+ account, oldPassword := unlockAccount(ctx, ks, ctx.Args().First(), 0, nil)
newPassword := getPassPhrase("Please give a new password. Do not forget this password.", true, 0, nil)
- if err := stack.AccountManager().Update(account, oldPassword, newPassword); err != nil {
+ if err := ks.Update(account, oldPassword, newPassword); err != nil {
utils.Fatalf("Could not update the account: %v", err)
}
return nil
@@ -310,7 +319,9 @@ func importWallet(ctx *cli.Context) error {
stack := utils.MakeNode(ctx, clientIdentifier, gitCommit)
passphrase := getPassPhrase("", false, 0, utils.MakePasswordList(ctx))
- acct, err := stack.AccountManager().ImportPreSaleKey(keyJson, passphrase)
+
+ ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
+ acct, err := ks.ImportPreSaleKey(keyJson, passphrase)
if err != nil {
utils.Fatalf("%v", err)
}
@@ -329,7 +340,9 @@ func accountImport(ctx *cli.Context) error {
}
stack := utils.MakeNode(ctx, clientIdentifier, gitCommit)
passphrase := getPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx))
- acct, err := stack.AccountManager().ImportECDSA(key, passphrase)
+
+ ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
+ acct, err := ks.ImportECDSA(key, passphrase)
if err != nil {
utils.Fatalf("Could not create the account: %v", err)
}
diff --git a/cmd/geth/accountcmd_test.go b/cmd/geth/accountcmd_test.go
index 113df983e..679a7ec30 100644
--- a/cmd/geth/accountcmd_test.go
+++ b/cmd/geth/accountcmd_test.go
@@ -35,7 +35,7 @@ import (
func tmpDatadirWithKeystore(t *testing.T) string {
datadir := tmpdir(t)
keystore := filepath.Join(datadir, "keystore")
- source := filepath.Join("..", "..", "accounts", "testdata", "keystore")
+ source := filepath.Join("..", "..", "accounts", "keystore", "testdata", "keystore")
if err := cp.CopyAll(keystore, source); err != nil {
t.Fatal(err)
}
@@ -53,15 +53,15 @@ func TestAccountList(t *testing.T) {
defer geth.expectExit()
if runtime.GOOS == "windows" {
geth.expect(`
-Account #0: {7ef5a6135f1fd6a02593eedc869c6d41d934aef8} {{.Datadir}}\keystore\UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8
-Account #1: {f466859ead1932d743d622cb74fc058882e8648a} {{.Datadir}}\keystore\aaa
-Account #2: {289d485d9771714cce91d3393d764e1311907acc} {{.Datadir}}\keystore\zzz
+Account #0: {7ef5a6135f1fd6a02593eedc869c6d41d934aef8} keystore://{{.Datadir}}\keystore\UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8
+Account #1: {f466859ead1932d743d622cb74fc058882e8648a} keystore://{{.Datadir}}\keystore\aaa
+Account #2: {289d485d9771714cce91d3393d764e1311907acc} keystore://{{.Datadir}}\keystore\zzz
`)
} else {
geth.expect(`
-Account #0: {7ef5a6135f1fd6a02593eedc869c6d41d934aef8} {{.Datadir}}/keystore/UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8
-Account #1: {f466859ead1932d743d622cb74fc058882e8648a} {{.Datadir}}/keystore/aaa
-Account #2: {289d485d9771714cce91d3393d764e1311907acc} {{.Datadir}}/keystore/zzz
+Account #0: {7ef5a6135f1fd6a02593eedc869c6d41d934aef8} keystore://{{.Datadir}}/keystore/UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8
+Account #1: {f466859ead1932d743d622cb74fc058882e8648a} keystore://{{.Datadir}}/keystore/aaa
+Account #2: {289d485d9771714cce91d3393d764e1311907acc} keystore://{{.Datadir}}/keystore/zzz
`)
}
}
@@ -230,7 +230,7 @@ Fatal: Failed to unlock account 0 (could not decrypt key with given passphrase)
}
func TestUnlockFlagAmbiguous(t *testing.T) {
- store := filepath.Join("..", "..", "accounts", "testdata", "dupes")
+ store := filepath.Join("..", "..", "accounts", "keystore", "testdata", "dupes")
geth := runGeth(t,
"--keystore", store, "--nat", "none", "--nodiscover", "--dev",
"--unlock", "f466859ead1932d743d622cb74fc058882e8648a",
@@ -247,12 +247,12 @@ Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3
!! Unsupported terminal, password will be echoed.
Passphrase: {{.InputLine "foobar"}}
Multiple key files exist for address f466859ead1932d743d622cb74fc058882e8648a:
- {{keypath "1"}}
- {{keypath "2"}}
+ keystore://{{keypath "1"}}
+ keystore://{{keypath "2"}}
Testing your passphrase against all of them...
-Your passphrase unlocked {{keypath "1"}}
+Your passphrase unlocked keystore://{{keypath "1"}}
In order to avoid this warning, you need to remove the following duplicate key files:
- {{keypath "2"}}
+ keystore://{{keypath "2"}}
`)
geth.expectExit()
@@ -267,7 +267,7 @@ In order to avoid this warning, you need to remove the following duplicate key f
}
func TestUnlockFlagAmbiguousWrongPassword(t *testing.T) {
- store := filepath.Join("..", "..", "accounts", "testdata", "dupes")
+ store := filepath.Join("..", "..", "accounts", "keystore", "testdata", "dupes")
geth := runGeth(t,
"--keystore", store, "--nat", "none", "--nodiscover", "--dev",
"--unlock", "f466859ead1932d743d622cb74fc058882e8648a")
@@ -283,8 +283,8 @@ Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3
!! Unsupported terminal, password will be echoed.
Passphrase: {{.InputLine "wrong"}}
Multiple key files exist for address f466859ead1932d743d622cb74fc058882e8648a:
- {{keypath "1"}}
- {{keypath "2"}}
+ keystore://{{keypath "1"}}
+ keystore://{{keypath "2"}}
Testing your passphrase against all of them...
Fatal: None of the listed files could be unlocked.
`)
diff --git a/cmd/geth/main.go b/cmd/geth/main.go
index d7e4cc7b5..c19770bfa 100644
--- a/cmd/geth/main.go
+++ b/cmd/geth/main.go
@@ -25,11 +25,14 @@ import (
"strings"
"time"
+ "github.com/ethereum/go-ethereum/accounts"
+ "github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/console"
"github.com/ethereum/go-ethereum/contracts/release"
"github.com/ethereum/go-ethereum/eth"
+ "github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/internal/debug"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
@@ -245,14 +248,50 @@ func startNode(ctx *cli.Context, stack *node.Node) {
utils.StartNode(stack)
// Unlock any account specifically requested
- accman := stack.AccountManager()
+ ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
+
passwords := utils.MakePasswordList(ctx)
- accounts := strings.Split(ctx.GlobalString(utils.UnlockedAccountFlag.Name), ",")
- for i, account := range accounts {
+ unlocks := strings.Split(ctx.GlobalString(utils.UnlockedAccountFlag.Name), ",")
+ for i, account := range unlocks {
if trimmed := strings.TrimSpace(account); trimmed != "" {
- unlockAccount(ctx, accman, trimmed, i, passwords)
+ unlockAccount(ctx, ks, trimmed, i, passwords)
}
}
+ // Register wallet event handlers to open and auto-derive wallets
+ events := make(chan accounts.WalletEvent, 16)
+ stack.AccountManager().Subscribe(events)
+
+ go func() {
+ // Create an chain state reader for self-derivation
+ rpcClient, err := stack.Attach()
+ if err != nil {
+ utils.Fatalf("Failed to attach to self: %v", err)
+ }
+ stateReader := ethclient.NewClient(rpcClient)
+
+ // Open and self derive any wallets already attached
+ for _, wallet := range stack.AccountManager().Wallets() {
+ if err := wallet.Open(""); err != nil {
+ glog.V(logger.Warn).Infof("Failed to open wallet %s: %v", wallet.URL(), err)
+ } else {
+ wallet.SelfDerive(accounts.DefaultBaseDerivationPath, stateReader)
+ }
+ }
+ // Listen for wallet event till termination
+ for event := range events {
+ if event.Arrive {
+ if err := event.Wallet.Open(""); err != nil {
+ glog.V(logger.Info).Infof("New wallet appeared: %s, failed to open: %s", event.Wallet.URL(), err)
+ } else {
+ glog.V(logger.Info).Infof("New wallet appeared: %s, %s", event.Wallet.URL(), event.Wallet.Status())
+ event.Wallet.SelfDerive(accounts.DefaultBaseDerivationPath, stateReader)
+ }
+ } else {
+ glog.V(logger.Info).Infof("Old wallet dropped: %s", event.Wallet.URL())
+ event.Wallet.Close()
+ }
+ }
+ }()
// Start auxiliary services if enabled
if ctx.GlobalBool(utils.MiningEnabledFlag.Name) {
var ethereum *eth.Ethereum
diff --git a/cmd/gethrpctest/main.go b/cmd/gethrpctest/main.go
index 850bf8eb2..9e80ad05d 100644
--- a/cmd/gethrpctest/main.go
+++ b/cmd/gethrpctest/main.go
@@ -23,6 +23,7 @@ import (
"os"
"os/signal"
+ "github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/ethdb"
@@ -99,17 +100,18 @@ func MakeSystemNode(privkey string, test *tests.BlockTest) (*node.Node, error) {
return nil, err
}
// Create the keystore and inject an unlocked account if requested
- accman := stack.AccountManager()
+ ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
+
if len(privkey) > 0 {
key, err := crypto.HexToECDSA(privkey)
if err != nil {
return nil, err
}
- a, err := accman.ImportECDSA(key, "")
+ a, err := ks.ImportECDSA(key, "")
if err != nil {
return nil, err
}
- if err := accman.Unlock(a, ""); err != nil {
+ if err := ks.Unlock(a, ""); err != nil {
return nil, err
}
}
diff --git a/cmd/swarm/main.go b/cmd/swarm/main.go
index 7d76d55c1..14adc3b10 100644
--- a/cmd/swarm/main.go
+++ b/cmd/swarm/main.go
@@ -26,6 +26,7 @@ import (
"strings"
"github.com/ethereum/go-ethereum/accounts"
+ "github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/console"
@@ -336,29 +337,36 @@ func getAccount(ctx *cli.Context, stack *node.Node) *ecdsa.PrivateKey {
return key
}
// Otherwise try getting it from the keystore.
- return decryptStoreAccount(stack.AccountManager(), keyid)
+ am := stack.AccountManager()
+ ks := am.Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
+
+ return decryptStoreAccount(ks, keyid)
}
-func decryptStoreAccount(accman *accounts.Manager, account string) *ecdsa.PrivateKey {
+func decryptStoreAccount(ks *keystore.KeyStore, account string) *ecdsa.PrivateKey {
var a accounts.Account
var err error
if common.IsHexAddress(account) {
- a, err = accman.Find(accounts.Account{Address: common.HexToAddress(account)})
- } else if ix, ixerr := strconv.Atoi(account); ixerr == nil {
- a, err = accman.AccountByIndex(ix)
+ a, err = ks.Find(accounts.Account{Address: common.HexToAddress(account)})
+ } else if ix, ixerr := strconv.Atoi(account); ixerr == nil && ix > 0 {
+ if accounts := ks.Accounts(); len(accounts) > ix {
+ a = accounts[ix]
+ } else {
+ err = fmt.Errorf("index %d higher than number of accounts %d", ix, len(accounts))
+ }
} else {
utils.Fatalf("Can't find swarm account key %s", account)
}
if err != nil {
utils.Fatalf("Can't find swarm account key: %v", err)
}
- keyjson, err := ioutil.ReadFile(a.File)
+ keyjson, err := ioutil.ReadFile(a.URL.Path)
if err != nil {
utils.Fatalf("Can't load swarm account key: %v", err)
}
for i := 1; i <= 3; i++ {
passphrase := promptPassphrase(fmt.Sprintf("Unlocking swarm account %s [%d/3]", a.Address.Hex(), i))
- key, err := accounts.DecryptKey(keyjson, passphrase)
+ key, err := keystore.DecryptKey(keyjson, passphrase)
if err == nil {
return key.PrivateKey
}
diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go
index 9ba33df80..92eb05e32 100644
--- a/cmd/utils/flags.go
+++ b/cmd/utils/flags.go
@@ -30,6 +30,7 @@ import (
"github.com/ethereum/ethash"
"github.com/ethereum/go-ethereum/accounts"
+ "github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state"
@@ -587,23 +588,27 @@ func MakeDatabaseHandles() int {
// MakeAddress converts an account specified directly as a hex encoded string or
// a key index in the key store to an internal account representation.
-func MakeAddress(accman *accounts.Manager, account string) (accounts.Account, error) {
+func MakeAddress(ks *keystore.KeyStore, account string) (accounts.Account, error) {
// If the specified account is a valid address, return it
if common.IsHexAddress(account) {
return accounts.Account{Address: common.HexToAddress(account)}, nil
}
// Otherwise try to interpret the account as a keystore index
index, err := strconv.Atoi(account)
- if err != nil {
+ if err != nil || index < 0 {
return accounts.Account{}, fmt.Errorf("invalid account address or index %q", account)
}
- return accman.AccountByIndex(index)
+ accs := ks.Accounts()
+ if len(accs) <= index {
+ return accounts.Account{}, fmt.Errorf("index %d higher than number of accounts %d", index, len(accs))
+ }
+ return accs[index], nil
}
// MakeEtherbase retrieves the etherbase either from the directly specified
// command line flags or from the keystore if CLI indexed.
-func MakeEtherbase(accman *accounts.Manager, ctx *cli.Context) common.Address {
- accounts := accman.Accounts()
+func MakeEtherbase(ks *keystore.KeyStore, ctx *cli.Context) common.Address {
+ accounts := ks.Accounts()
if !ctx.GlobalIsSet(EtherbaseFlag.Name) && len(accounts) == 0 {
glog.V(logger.Error).Infoln("WARNING: No etherbase set and no accounts found as default")
return common.Address{}
@@ -613,7 +618,7 @@ func MakeEtherbase(accman *accounts.Manager, ctx *cli.Context) common.Address {
return common.Address{}
}
// If the specified etherbase is a valid address, return it
- account, err := MakeAddress(accman, etherbase)
+ account, err := MakeAddress(ks, etherbase)
if err != nil {
Fatalf("Option %q: %v", EtherbaseFlag.Name, err)
}
@@ -721,9 +726,10 @@ func RegisterEthService(ctx *cli.Context, stack *node.Node, extra []byte) {
if networks > 1 {
Fatalf("The %v flags are mutually exclusive", netFlags)
}
+ ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
ethConf := &eth.Config{
- Etherbase: MakeEtherbase(stack.AccountManager(), ctx),
+ Etherbase: MakeEtherbase(ks, ctx),
ChainConfig: MakeChainConfig(ctx, stack),
FastSync: ctx.GlobalBool(FastSyncFlag.Name),
LightMode: ctx.GlobalBool(LightModeFlag.Name),
diff --git a/eth/backend.go b/eth/backend.go
index af120cbad..ef3ac93c8 100644
--- a/eth/backend.go
+++ b/eth/backend.go
@@ -361,15 +361,15 @@ func (s *Ethereum) ResetWithGenesisBlock(gb *types.Block) {
}
func (s *Ethereum) Etherbase() (eb common.Address, err error) {
- eb = s.etherbase
- if (eb == common.Address{}) {
- firstAccount, err := s.AccountManager().AccountByIndex(0)
- eb = firstAccount.Address
- if err != nil {
- return eb, fmt.Errorf("etherbase address must be explicitly specified")
+ if s.etherbase != (common.Address{}) {
+ return s.etherbase, nil
+ }
+ if wallets := s.AccountManager().Wallets(); len(wallets) > 0 {
+ if accounts := wallets[0].Accounts(); len(accounts) > 0 {
+ return accounts[0].Address, nil
}
}
- return eb, nil
+ return common.Address{}, fmt.Errorf("etherbase address must be explicitly specified")
}
// set in js console via admin interface or wrapper from cli flags
diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go
index 4c8e784c5..f49434e17 100644
--- a/internal/ethapi/api.go
+++ b/internal/ethapi/api.go
@@ -28,6 +28,7 @@ import (
"github.com/ethereum/ethash"
"github.com/ethereum/go-ethereum/accounts"
+ "github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core"
@@ -187,8 +188,14 @@ func NewPublicAccountAPI(am *accounts.Manager) *PublicAccountAPI {
}
// Accounts returns the collection of accounts this node manages
-func (s *PublicAccountAPI) Accounts() []accounts.Account {
- return s.am.Accounts()
+func (s *PublicAccountAPI) Accounts() []common.Address {
+ var addresses []common.Address
+ for _, wallet := range s.am.Wallets() {
+ for _, account := range wallet.Accounts() {
+ addresses = append(addresses, account.Address)
+ }
+ }
+ return addresses
}
// PrivateAccountAPI provides an API to access accounts managed by this node.
@@ -209,23 +216,67 @@ func NewPrivateAccountAPI(b Backend) *PrivateAccountAPI {
// ListAccounts will return a list of addresses for accounts this node manages.
func (s *PrivateAccountAPI) ListAccounts() []common.Address {
- accounts := s.am.Accounts()
- addresses := make([]common.Address, len(accounts))
- for i, acc := range accounts {
- addresses[i] = acc.Address
+ var addresses []common.Address
+ for _, wallet := range s.am.Wallets() {
+ for _, account := range wallet.Accounts() {
+ addresses = append(addresses, account.Address)
+ }
}
return addresses
}
+// rawWallet is a JSON representation of an accounts.Wallet interface, with its
+// data contents extracted into plain fields.
+type rawWallet struct {
+ URL string `json:"url"`
+ Status string `json:"status"`
+ Accounts []accounts.Account `json:"accounts"`
+}
+
+// ListWallets will return a list of wallets this node manages.
+func (s *PrivateAccountAPI) ListWallets() []rawWallet {
+ var wallets []rawWallet
+ for _, wallet := range s.am.Wallets() {
+ wallets = append(wallets, rawWallet{
+ URL: wallet.URL().String(),
+ Status: wallet.Status(),
+ Accounts: wallet.Accounts(),
+ })
+ }
+ return wallets
+}
+
+// DeriveAccount requests a HD wallet to derive a new account, optionally pinning
+// it for later reuse.
+func (s *PrivateAccountAPI) DeriveAccount(url string, path string, pin *bool) (accounts.Account, error) {
+ wallet, err := s.am.Wallet(url)
+ if err != nil {
+ return accounts.Account{}, err
+ }
+ derivPath, err := accounts.ParseDerivationPath(path)
+ if err != nil {
+ return accounts.Account{}, err
+ }
+ if pin == nil {
+ pin = new(bool)
+ }
+ return wallet.Derive(derivPath, *pin)
+}
+
// NewAccount will create a new account and returns the address for the new account.
func (s *PrivateAccountAPI) NewAccount(password string) (common.Address, error) {
- acc, err := s.am.NewAccount(password)
+ acc, err := fetchKeystore(s.am).NewAccount(password)
if err == nil {
return acc.Address, nil
}
return common.Address{}, err
}
+// fetchKeystore retrives the encrypted keystore from the account manager.
+func fetchKeystore(am *accounts.Manager) *keystore.KeyStore {
+ return am.Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
+}
+
// ImportRawKey stores the given hex encoded ECDSA key into the key directory,
// encrypting it with the passphrase.
func (s *PrivateAccountAPI) ImportRawKey(privkey string, password string) (common.Address, error) {
@@ -234,7 +285,7 @@ func (s *PrivateAccountAPI) ImportRawKey(privkey string, password string) (commo
return common.Address{}, err
}
- acc, err := s.am.ImportECDSA(crypto.ToECDSA(hexkey), password)
+ acc, err := fetchKeystore(s.am).ImportECDSA(crypto.ToECDSA(hexkey), password)
return acc.Address, err
}
@@ -251,30 +302,42 @@ func (s *PrivateAccountAPI) UnlockAccount(addr common.Address, password string,
} else {
d = time.Duration(*duration) * time.Second
}
- err := s.am.TimedUnlock(accounts.Account{Address: addr}, password, d)
+ err := fetchKeystore(s.am).TimedUnlock(accounts.Account{Address: addr}, password, d)
return err == nil, err
}
// LockAccount will lock the account associated with the given address when it's unlocked.
func (s *PrivateAccountAPI) LockAccount(addr common.Address) bool {
- return s.am.Lock(addr) == nil
+ return fetchKeystore(s.am).Lock(addr) == nil
}
// SendTransaction will create a transaction from the given arguments and
// tries to sign it with the key associated with args.To. If the given passwd isn't
// able to decrypt the key it fails.
func (s *PrivateAccountAPI) SendTransaction(ctx context.Context, args SendTxArgs, passwd string) (common.Hash, error) {
+ // Set some sanity defaults and terminate on failure
if err := args.setDefaults(ctx, s.b); err != nil {
return common.Hash{}, err
}
- tx := args.toTransaction()
- signer := types.MakeSigner(s.b.ChainConfig(), s.b.CurrentBlock().Number())
- signature, err := s.am.SignWithPassphrase(accounts.Account{Address: args.From}, passwd, signer.Hash(tx).Bytes())
+ // Look up the wallet containing the requested signer
+ account := accounts.Account{Address: args.From}
+
+ wallet, err := s.am.Find(account)
if err != nil {
return common.Hash{}, err
}
+ // Assemble the transaction and sign with the wallet
+ tx := args.toTransaction()
- return submitTransaction(ctx, s.b, tx, signature)
+ var chainID *big.Int
+ if config := s.b.ChainConfig(); config.IsEIP155(s.b.CurrentBlock().Number()) {
+ chainID = config.ChainId
+ }
+ signed, err := wallet.SignTxWithPassphrase(account, passwd, tx, chainID)
+ if err != nil {
+ return common.Hash{}, err
+ }
+ return submitTransaction(ctx, s.b, signed)
}
// signHash is a helper function that calculates a hash for the given message that can be
@@ -299,7 +362,15 @@ func signHash(data []byte) []byte {
//
// https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_sign
func (s *PrivateAccountAPI) Sign(ctx context.Context, data hexutil.Bytes, addr common.Address, passwd string) (hexutil.Bytes, error) {
- signature, err := s.b.AccountManager().SignWithPassphrase(accounts.Account{Address: addr}, passwd, signHash(data))
+ // Look up the wallet containing the requested signer
+ account := accounts.Account{Address: addr}
+
+ wallet, err := s.b.AccountManager().Find(account)
+ if err != nil {
+ return nil, err
+ }
+ // Assemble sign the data with the wallet
+ signature, err := wallet.SignHashWithPassphrase(account, passwd, signHash(data))
if err != nil {
return nil, err
}
@@ -512,16 +583,15 @@ func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr
if state == nil || err != nil {
return nil, common.Big0, err
}
-
// Set sender address or use a default if none specified
addr := args.From
if addr == (common.Address{}) {
- accounts := s.b.AccountManager().Accounts()
- if len(accounts) > 0 {
- addr = accounts[0].Address
+ if wallets := s.b.AccountManager().Wallets(); len(wallets) > 0 {
+ if accounts := wallets[0].Accounts(); len(accounts) > 0 {
+ addr = accounts[0].Address
+ }
}
}
-
// Set default gas & gas price if none were set
gas, gasPrice := args.Gas.ToInt(), args.GasPrice.ToInt()
if gas.BitLen() == 0 {
@@ -530,7 +600,6 @@ func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr
if gasPrice.BitLen() == 0 {
gasPrice = new(big.Int).Mul(big.NewInt(50), common.Shannon)
}
-
// Create new call message
msg := types.NewMessage(addr, args.To, 0, args.Value.ToInt(), gas, gasPrice, args.Data, false)
@@ -1023,13 +1092,19 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(txHash common.Hash) (ma
// sign is a helper function that signs a transaction with the private key of the given address.
func (s *PublicTransactionPoolAPI) sign(addr common.Address, tx *types.Transaction) (*types.Transaction, error) {
- signer := types.MakeSigner(s.b.ChainConfig(), s.b.CurrentBlock().Number())
+ // Look up the wallet containing the requested signer
+ account := accounts.Account{Address: addr}
- signature, err := s.b.AccountManager().Sign(addr, signer.Hash(tx).Bytes())
+ wallet, err := s.b.AccountManager().Find(account)
if err != nil {
return nil, err
}
- return tx.WithSignature(signer, signature)
+ // Request the wallet to sign the transaction
+ var chainID *big.Int
+ if config := s.b.ChainConfig(); config.IsEIP155(s.b.CurrentBlock().Number()) {
+ chainID = config.ChainId
+ }
+ return wallet.SignTx(account, tx, chainID)
}
// SendTxArgs represents the arguments to sumbit a new transaction into the transaction pool.
@@ -1076,42 +1151,47 @@ func (args *SendTxArgs) toTransaction() *types.Transaction {
}
// submitTransaction is a helper function that submits tx to txPool and logs a message.
-func submitTransaction(ctx context.Context, b Backend, tx *types.Transaction, signature []byte) (common.Hash, error) {
- signer := types.MakeSigner(b.ChainConfig(), b.CurrentBlock().Number())
-
- signedTx, err := tx.WithSignature(signer, signature)
- if err != nil {
+func submitTransaction(ctx context.Context, b Backend, tx *types.Transaction) (common.Hash, error) {
+ if err := b.SendTx(ctx, tx); err != nil {
return common.Hash{}, err
}
-
- if err := b.SendTx(ctx, signedTx); err != nil {
- return common.Hash{}, err
- }
-
- if signedTx.To() == nil {
- from, _ := types.Sender(signer, signedTx)
- addr := crypto.CreateAddress(from, signedTx.Nonce())
- glog.V(logger.Info).Infof("Tx(%s) created: %s\n", signedTx.Hash().Hex(), addr.Hex())
+ if tx.To() == nil {
+ signer := types.MakeSigner(b.ChainConfig(), b.CurrentBlock().Number())
+ from, _ := types.Sender(signer, tx)
+ addr := crypto.CreateAddress(from, tx.Nonce())
+ glog.V(logger.Info).Infof("Tx(%s) created: %s\n", tx.Hash().Hex(), addr.Hex())
} else {
- glog.V(logger.Info).Infof("Tx(%s) to: %s\n", signedTx.Hash().Hex(), tx.To().Hex())
+ glog.V(logger.Info).Infof("Tx(%s) to: %s\n", tx.Hash().Hex(), tx.To().Hex())
}
-
- return signedTx.Hash(), nil
+ return tx.Hash(), nil
}
// SendTransaction creates a transaction for the given argument, sign it and submit it to the
// transaction pool.
func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args SendTxArgs) (common.Hash, error) {
+ // Set some sanity defaults and terminate on failure
if err := args.setDefaults(ctx, s.b); err != nil {
return common.Hash{}, err
}
+ // Look up the wallet containing the requested signer
+ account := accounts.Account{Address: args.From}
+
+ wallet, err := s.b.AccountManager().Find(account)
+ if err != nil {
+ return common.Hash{}, err
+ }
+ // Assemble the transaction and sign with the wallet
tx := args.toTransaction()
- signer := types.MakeSigner(s.b.ChainConfig(), s.b.CurrentBlock().Number())
- signature, err := s.b.AccountManager().Sign(args.From, signer.Hash(tx).Bytes())
+
+ var chainID *big.Int
+ if config := s.b.ChainConfig(); config.IsEIP155(s.b.CurrentBlock().Number()) {
+ chainID = config.ChainId
+ }
+ signed, err := wallet.SignTx(account, tx, chainID)
if err != nil {
return common.Hash{}, err
}
- return submitTransaction(ctx, s.b, tx, signature)
+ return submitTransaction(ctx, s.b, signed)
}
// SendRawTransaction will add the signed transaction to the transaction pool.
@@ -1151,7 +1231,15 @@ func (s *PublicTransactionPoolAPI) SendRawTransaction(ctx context.Context, encod
//
// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign
func (s *PublicTransactionPoolAPI) Sign(addr common.Address, data hexutil.Bytes) (hexutil.Bytes, error) {
- signature, err := s.b.AccountManager().Sign(addr, signHash(data))
+ // Look up the wallet containing the requested signer
+ account := accounts.Account{Address: addr}
+
+ wallet, err := s.b.AccountManager().Find(account)
+ if err != nil {
+ return nil, err
+ }
+ // Sign the requested hash with the wallet
+ signature, err := wallet.SignHash(account, signHash(data))
if err == nil {
signature[64] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper
}
@@ -1197,7 +1285,7 @@ func (s *PublicTransactionPoolAPI) PendingTransactions() ([]*RPCTransaction, err
signer = types.NewEIP155Signer(tx.ChainId())
}
from, _ := types.Sender(signer, tx)
- if s.b.AccountManager().HasAddress(from) {
+ if _, err := s.b.AccountManager().Find(accounts.Account{Address: from}); err == nil {
transactions = append(transactions, newRPCPendingTransaction(tx))
}
}
diff --git a/internal/guide/guide_test.go b/internal/guide/guide_test.go
index 8f89037bd..9c7ad16d1 100644
--- a/internal/guide/guide_test.go
+++ b/internal/guide/guide_test.go
@@ -24,13 +24,14 @@ package guide
import (
"io/ioutil"
+ "math/big"
"os"
"path/filepath"
"testing"
"time"
- "github.com/ethereum/go-ethereum/accounts"
- "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/accounts/keystore"
+ "github.com/ethereum/go-ethereum/core/types"
)
// Tests that the account management snippets work correctly.
@@ -42,59 +43,59 @@ func TestAccountManagement(t *testing.T) {
}
defer os.RemoveAll(workdir)
- // Create an encrypted keystore manager with standard crypto parameters
- am := accounts.NewManager(filepath.Join(workdir, "keystore"), accounts.StandardScryptN, accounts.StandardScryptP)
+ // Create an encrypted keystore with standard crypto parameters
+ ks := keystore.NewKeyStore(filepath.Join(workdir, "keystore"), keystore.StandardScryptN, keystore.StandardScryptP)
// Create a new account with the specified encryption passphrase
- newAcc, err := am.NewAccount("Creation password")
+ newAcc, err := ks.NewAccount("Creation password")
if err != nil {
t.Fatalf("Failed to create new account: %v", err)
}
// Export the newly created account with a different passphrase. The returned
// data from this method invocation is a JSON encoded, encrypted key-file
- jsonAcc, err := am.Export(newAcc, "Creation password", "Export password")
+ jsonAcc, err := ks.Export(newAcc, "Creation password", "Export password")
if err != nil {
t.Fatalf("Failed to export account: %v", err)
}
// Update the passphrase on the account created above inside the local keystore
- if err := am.Update(newAcc, "Creation password", "Update password"); err != nil {
+ if err := ks.Update(newAcc, "Creation password", "Update password"); err != nil {
t.Fatalf("Failed to update account: %v", err)
}
// Delete the account updated above from the local keystore
- if err := am.Delete(newAcc, "Update password"); err != nil {
+ if err := ks.Delete(newAcc, "Update password"); err != nil {
t.Fatalf("Failed to delete account: %v", err)
}
// Import back the account we've exported (and then deleted) above with yet
// again a fresh passphrase
- if _, err := am.Import(jsonAcc, "Export password", "Import password"); err != nil {
+ if _, err := ks.Import(jsonAcc, "Export password", "Import password"); err != nil {
t.Fatalf("Failed to import account: %v", err)
}
// Create a new account to sign transactions with
- signer, err := am.NewAccount("Signer password")
+ signer, err := ks.NewAccount("Signer password")
if err != nil {
t.Fatalf("Failed to create signer account: %v", err)
}
- txHash := common.HexToHash("0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")
+ tx, chain := new(types.Transaction), big.NewInt(1)
// Sign a transaction with a single authorization
- if _, err := am.SignWithPassphrase(signer, "Signer password", txHash.Bytes()); err != nil {
+ if _, err := ks.SignTxWithPassphrase(signer, "Signer password", tx, chain); err != nil {
t.Fatalf("Failed to sign with passphrase: %v", err)
}
// Sign a transaction with multiple manually cancelled authorizations
- if err := am.Unlock(signer, "Signer password"); err != nil {
+ if err := ks.Unlock(signer, "Signer password"); err != nil {
t.Fatalf("Failed to unlock account: %v", err)
}
- if _, err := am.Sign(signer.Address, txHash.Bytes()); err != nil {
+ if _, err := ks.SignTx(signer, tx, chain); err != nil {
t.Fatalf("Failed to sign with unlocked account: %v", err)
}
- if err := am.Lock(signer.Address); err != nil {
+ if err := ks.Lock(signer.Address); err != nil {
t.Fatalf("Failed to lock account: %v", err)
}
// Sign a transaction with multiple automatically cancelled authorizations
- if err := am.TimedUnlock(signer, "Signer password", time.Second); err != nil {
+ if err := ks.TimedUnlock(signer, "Signer password", time.Second); err != nil {
t.Fatalf("Failed to time unlock account: %v", err)
}
- if _, err := am.Sign(signer.Address, txHash.Bytes()); err != nil {
+ if _, err := ks.SignTx(signer, tx, chain); err != nil {
t.Fatalf("Failed to sign with time unlocked account: %v", err)
}
}
diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go
index edbe45fa3..2012c2517 100644
--- a/internal/web3ext/web3ext.go
+++ b/internal/web3ext/web3ext.go
@@ -448,6 +448,18 @@ web3._extend({
name: 'ecRecover',
call: 'personal_ecRecover',
params: 2
+ }),
+ new web3._extend.Method({
+ name: 'deriveAccount',
+ call: 'personal_deriveAccount',
+ params: 3
+ })
+ ],
+ properties:
+ [
+ new web3._extend.Property({
+ name: 'listWallets',
+ getter: 'personal_listWallets'
})
]
})
diff --git a/miner/worker.go b/miner/worker.go
index 49ac60253..ef64c8fc9 100644
--- a/miner/worker.go
+++ b/miner/worker.go
@@ -386,8 +386,11 @@ func (self *worker) makeCurrent(parent *types.Block, header *types.Header) error
work.family.Add(ancestor.Hash())
work.ancestors.Add(ancestor.Hash())
}
- accounts := self.eth.AccountManager().Accounts()
-
+ wallets := self.eth.AccountManager().Wallets()
+ accounts := make([]accounts.Account, 0, len(wallets))
+ for _, wallet := range wallets {
+ accounts = append(accounts, wallet.Accounts()...)
+ }
// Keep track of transactions which return errors so they can be removed
work.tcount = 0
work.ownedAccounts = accountAddressesSet(accounts)
diff --git a/mobile/accounts.go b/mobile/accounts.go
index 621be4d7a..fbaa3bf40 100644
--- a/mobile/accounts.go
+++ b/mobile/accounts.go
@@ -24,24 +24,25 @@ import (
"time"
"github.com/ethereum/go-ethereum/accounts"
+ "github.com/ethereum/go-ethereum/accounts/keystore"
)
const (
// StandardScryptN is the N parameter of Scrypt encryption algorithm, using 256MB
// memory and taking approximately 1s CPU time on a modern processor.
- StandardScryptN = int(accounts.StandardScryptN)
+ StandardScryptN = int(keystore.StandardScryptN)
// StandardScryptP is the P parameter of Scrypt encryption algorithm, using 256MB
// memory and taking approximately 1s CPU time on a modern processor.
- StandardScryptP = int(accounts.StandardScryptP)
+ StandardScryptP = int(keystore.StandardScryptP)
// LightScryptN is the N parameter of Scrypt encryption algorithm, using 4MB
// memory and taking approximately 100ms CPU time on a modern processor.
- LightScryptN = int(accounts.LightScryptN)
+ LightScryptN = int(keystore.LightScryptN)
// LightScryptP is the P parameter of Scrypt encryption algorithm, using 4MB
// memory and taking approximately 100ms CPU time on a modern processor.
- LightScryptP = int(accounts.LightScryptP)
+ LightScryptP = int(keystore.LightScryptP)
)
// Account represents a stored key.
@@ -77,59 +78,75 @@ func (a *Account) GetAddress() *Address {
return &Address{a.account.Address}
}
-// GetFile retrieves the path of the file containing the account key.
-func (a *Account) GetFile() string {
- return a.account.File
+// GetURL retrieves the canonical URL of the account.
+func (a *Account) GetURL() string {
+ return a.account.URL.String()
}
-// AccountManager manages a key storage directory on disk.
-type AccountManager struct{ manager *accounts.Manager }
+// KeyStore manages a key storage directory on disk.
+type KeyStore struct{ keystore *keystore.KeyStore }
-// NewAccountManager creates a manager for the given directory.
-func NewAccountManager(keydir string, scryptN, scryptP int) *AccountManager {
- return &AccountManager{manager: accounts.NewManager(keydir, scryptN, scryptP)}
+// NewKeyStore creates a keystore for the given directory.
+func NewKeyStore(keydir string, scryptN, scryptP int) *KeyStore {
+ return &KeyStore{keystore: keystore.NewKeyStore(keydir, scryptN, scryptP)}
}
// HasAddress reports whether a key with the given address is present.
-func (am *AccountManager) HasAddress(address *Address) bool {
- return am.manager.HasAddress(address.address)
+func (ks *KeyStore) HasAddress(address *Address) bool {
+ return ks.keystore.HasAddress(address.address)
}
// GetAccounts returns all key files present in the directory.
-func (am *AccountManager) GetAccounts() *Accounts {
- return &Accounts{am.manager.Accounts()}
+func (ks *KeyStore) GetAccounts() *Accounts {
+ return &Accounts{ks.keystore.Accounts()}
}
// DeleteAccount deletes the key matched by account if the passphrase is correct.
// If a contains no filename, the address must match a unique key.
-func (am *AccountManager) DeleteAccount(account *Account, passphrase string) error {
- return am.manager.Delete(accounts.Account{
- Address: account.account.Address,
- File: account.account.File,
- }, passphrase)
+func (ks *KeyStore) DeleteAccount(account *Account, passphrase string) error {
+ return ks.keystore.Delete(account.account, passphrase)
}
-// Sign calculates a ECDSA signature for the given hash. The produced signature
+// SignHash calculates a ECDSA signature for the given hash. The produced signature
// is in the [R || S || V] format where V is 0 or 1.
-func (am *AccountManager) Sign(address *Address, hash []byte) (signature []byte, _ error) {
- return am.manager.Sign(address.address, hash)
+func (ks *KeyStore) SignHash(address *Address, hash []byte) (signature []byte, _ error) {
+ return ks.keystore.SignHash(accounts.Account{Address: address.address}, hash)
}
-// SignPassphrase signs hash if the private key matching the given address can
+// SignTx signs the given transaction with the requested account.
+func (ks *KeyStore) SignTx(account *Account, tx *Transaction, chainID *BigInt) (*Transaction, error) {
+ signed, err := ks.keystore.SignTx(account.account, tx.tx, chainID.bigint)
+ if err != nil {
+ return nil, err
+ }
+ return &Transaction{signed}, nil
+}
+
+// SignHashPassphrase signs hash if the private key matching the given address can
// be decrypted with the given passphrase. The produced signature is in the
// [R || S || V] format where V is 0 or 1.
-func (am *AccountManager) SignPassphrase(account *Account, passphrase string, hash []byte) (signature []byte, _ error) {
- return am.manager.SignWithPassphrase(account.account, passphrase, hash)
+func (ks *KeyStore) SignHashPassphrase(account *Account, passphrase string, hash []byte) (signature []byte, _ error) {
+ return ks.keystore.SignHashWithPassphrase(account.account, passphrase, hash)
+}
+
+// SignTxPassphrase signs the transaction if the private key matching the
+// given address can be decrypted with the given passphrase.
+func (ks *KeyStore) SignTxPassphrase(account *Account, passphrase string, tx *Transaction, chainID *BigInt) (*Transaction, error) {
+ signed, err := ks.keystore.SignTxWithPassphrase(account.account, passphrase, tx.tx, chainID.bigint)
+ if err != nil {
+ return nil, err
+ }
+ return &Transaction{signed}, nil
}
// Unlock unlocks the given account indefinitely.
-func (am *AccountManager) Unlock(account *Account, passphrase string) error {
- return am.manager.TimedUnlock(account.account, passphrase, 0)
+func (ks *KeyStore) Unlock(account *Account, passphrase string) error {
+ return ks.keystore.TimedUnlock(account.account, passphrase, 0)
}
// Lock removes the private key with the given address from memory.
-func (am *AccountManager) Lock(address *Address) error {
- return am.manager.Lock(address.address)
+func (ks *KeyStore) Lock(address *Address) error {
+ return ks.keystore.Lock(address.address)
}
// TimedUnlock unlocks the given account with the passphrase. The account stays
@@ -139,14 +156,14 @@ func (am *AccountManager) Lock(address *Address) error {
// If the account address is already unlocked for a duration, TimedUnlock extends or
// shortens the active unlock timeout. If the address was previously unlocked
// indefinitely the timeout is not altered.
-func (am *AccountManager) TimedUnlock(account *Account, passphrase string, timeout int64) error {
- return am.manager.TimedUnlock(account.account, passphrase, time.Duration(timeout))
+func (ks *KeyStore) TimedUnlock(account *Account, passphrase string, timeout int64) error {
+ return ks.keystore.TimedUnlock(account.account, passphrase, time.Duration(timeout))
}
// NewAccount generates a new key and stores it into the key directory,
// encrypting it with the passphrase.
-func (am *AccountManager) NewAccount(passphrase string) (*Account, error) {
- account, err := am.manager.NewAccount(passphrase)
+func (ks *KeyStore) NewAccount(passphrase string) (*Account, error) {
+ account, err := ks.keystore.NewAccount(passphrase)
if err != nil {
return nil, err
}
@@ -154,13 +171,13 @@ func (am *AccountManager) NewAccount(passphrase string) (*Account, error) {
}
// ExportKey exports as a JSON key, encrypted with newPassphrase.
-func (am *AccountManager) ExportKey(account *Account, passphrase, newPassphrase string) (key []byte, _ error) {
- return am.manager.Export(account.account, passphrase, newPassphrase)
+func (ks *KeyStore) ExportKey(account *Account, passphrase, newPassphrase string) (key []byte, _ error) {
+ return ks.keystore.Export(account.account, passphrase, newPassphrase)
}
// ImportKey stores the given encrypted JSON key into the key directory.
-func (am *AccountManager) ImportKey(keyJSON []byte, passphrase, newPassphrase string) (account *Account, _ error) {
- acc, err := am.manager.Import(keyJSON, passphrase, newPassphrase)
+func (ks *KeyStore) ImportKey(keyJSON []byte, passphrase, newPassphrase string) (account *Account, _ error) {
+ acc, err := ks.keystore.Import(keyJSON, passphrase, newPassphrase)
if err != nil {
return nil, err
}
@@ -168,14 +185,14 @@ func (am *AccountManager) ImportKey(keyJSON []byte, passphrase, newPassphrase st
}
// UpdateAccount changes the passphrase of an existing account.
-func (am *AccountManager) UpdateAccount(account *Account, passphrase, newPassphrase string) error {
- return am.manager.Update(account.account, passphrase, newPassphrase)
+func (ks *KeyStore) UpdateAccount(account *Account, passphrase, newPassphrase string) error {
+ return ks.keystore.Update(account.account, passphrase, newPassphrase)
}
// ImportPreSaleKey decrypts the given Ethereum presale wallet and stores
// a key file in the key directory. The key file is encrypted with the same passphrase.
-func (am *AccountManager) ImportPreSaleKey(keyJSON []byte, passphrase string) (ccount *Account, _ error) {
- account, err := am.manager.ImportPreSaleKey(keyJSON, passphrase)
+func (ks *KeyStore) ImportPreSaleKey(keyJSON []byte, passphrase string) (ccount *Account, _ error) {
+ account, err := ks.keystore.ImportPreSaleKey(keyJSON, passphrase)
if err != nil {
return nil, err
}
diff --git a/mobile/android_test.go b/mobile/android_test.go
index 3776f8291..6f5076d64 100644
--- a/mobile/android_test.go
+++ b/mobile/android_test.go
@@ -43,42 +43,46 @@ public class AndroidTest extends InstrumentationTestCase {
public AndroidTest() {}
public void testAccountManagement() {
- // Create an encrypted keystore manager with light crypto parameters.
- AccountManager am = new AccountManager(getInstrumentation().getContext().getFilesDir() + "/keystore", Geth.LightScryptN, Geth.LightScryptP);
+ // Create an encrypted keystore with light crypto parameters.
+ KeyStore ks = new KeyStore(getInstrumentation().getContext().getFilesDir() + "/keystore", Geth.LightScryptN, Geth.LightScryptP);
try {
// Create a new account with the specified encryption passphrase.
- Account newAcc = am.newAccount("Creation password");
+ Account newAcc = ks.newAccount("Creation password");
// Export the newly created account with a different passphrase. The returned
// data from this method invocation is a JSON encoded, encrypted key-file.
- byte[] jsonAcc = am.exportKey(newAcc, "Creation password", "Export password");
+ byte[] jsonAcc = ks.exportKey(newAcc, "Creation password", "Export password");
// Update the passphrase on the account created above inside the local keystore.
- am.updateAccount(newAcc, "Creation password", "Update password");
+ ks.updateAccount(newAcc, "Creation password", "Update password");
// Delete the account updated above from the local keystore.
- am.deleteAccount(newAcc, "Update password");
+ ks.deleteAccount(newAcc, "Update password");
// Import back the account we've exported (and then deleted) above with yet
// again a fresh passphrase.
- Account impAcc = am.importKey(jsonAcc, "Export password", "Import password");
+ Account impAcc = ks.importKey(jsonAcc, "Export password", "Import password");
// Create a new account to sign transactions with
- Account signer = am.newAccount("Signer password");
- Hash txHash = new Hash("0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef");
+ Account signer = ks.newAccount("Signer password");
+
+ Transaction tx = new Transaction(
+ 1, new Address("0x0000000000000000000000000000000000000000"),
+ new BigInt(0), new BigInt(0), new BigInt(1), null); // Random empty transaction
+ BigInt chain = new BigInt(1); // Chain identifier of the main net
// Sign a transaction with a single authorization
- byte[] signature = am.signPassphrase(signer, "Signer password", txHash.getBytes());
+ Transaction signed = ks.signTxPassphrase(signer, "Signer password", tx, chain);
// Sign a transaction with multiple manually cancelled authorizations
- am.unlock(signer, "Signer password");
- signature = am.sign(signer.getAddress(), txHash.getBytes());
- am.lock(signer.getAddress());
+ ks.unlock(signer, "Signer password");
+ signed = ks.signTx(signer, tx, chain);
+ ks.lock(signer.getAddress());
// Sign a transaction with multiple automatically cancelled authorizations
- am.timedUnlock(signer, "Signer password", 1000000000);
- signature = am.sign(signer.getAddress(), txHash.getBytes());
+ ks.timedUnlock(signer, "Signer password", 1000000000);
+ signed = ks.signTx(signer, tx, chain);
} catch (Exception e) {
fail(e.toString());
}
diff --git a/mobile/types.go b/mobile/types.go
index 9ea70ea9b..a9c8cf68c 100644
--- a/mobile/types.go
+++ b/mobile/types.go
@@ -132,6 +132,11 @@ type Transaction struct {
tx *types.Transaction
}
+// NewTransaction creates a new transaction with the given properties.
+func NewTransaction(nonce int64, to *Address, amount, gasLimit, gasPrice *BigInt, data []byte) *Transaction {
+ return &Transaction{types.NewTransaction(uint64(nonce), to.address, amount.bigint, gasLimit.bigint, gasPrice.bigint, data)}
+}
+
func (tx *Transaction) GetData() []byte { return tx.tx.Data() }
func (tx *Transaction) GetGas() int64 { return tx.tx.Gas().Int64() }
func (tx *Transaction) GetGasPrice() *BigInt { return &BigInt{tx.tx.GasPrice()} }
diff --git a/node/config.go b/node/config.go
index 8d75e441b..c09f51747 100644
--- a/node/config.go
+++ b/node/config.go
@@ -27,6 +27,8 @@ import (
"strings"
"github.com/ethereum/go-ethereum/accounts"
+ "github.com/ethereum/go-ethereum/accounts/keystore"
+ "github.com/ethereum/go-ethereum/accounts/usbwallet"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/logger"
@@ -400,15 +402,19 @@ func (c *Config) parsePersistentNodes(path string) []*discover.Node {
return nodes
}
-func makeAccountManager(conf *Config) (am *accounts.Manager, ephemeralKeystore string, err error) {
- scryptN := accounts.StandardScryptN
- scryptP := accounts.StandardScryptP
+func makeAccountManager(conf *Config) (*accounts.Manager, string, error) {
+ scryptN := keystore.StandardScryptN
+ scryptP := keystore.StandardScryptP
if conf.UseLightweightKDF {
- scryptN = accounts.LightScryptN
- scryptP = accounts.LightScryptP
+ scryptN = keystore.LightScryptN
+ scryptP = keystore.LightScryptP
}
- var keydir string
+ var (
+ keydir string
+ ephemeral string
+ err error
+ )
switch {
case filepath.IsAbs(conf.KeyStoreDir):
keydir = conf.KeyStoreDir
@@ -423,7 +429,7 @@ func makeAccountManager(conf *Config) (am *accounts.Manager, ephemeralKeystore s
default:
// There is no datadir.
keydir, err = ioutil.TempDir("", "go-ethereum-keystore")
- ephemeralKeystore = keydir
+ ephemeral = keydir
}
if err != nil {
return nil, "", err
@@ -431,6 +437,14 @@ func makeAccountManager(conf *Config) (am *accounts.Manager, ephemeralKeystore s
if err := os.MkdirAll(keydir, 0700); err != nil {
return nil, "", err
}
-
- return accounts.NewManager(keydir, scryptN, scryptP), ephemeralKeystore, nil
+ // Assemble the account manager and supported backends
+ backends := []accounts.Backend{
+ keystore.NewKeyStore(keydir, scryptN, scryptP),
+ }
+ if ledgerhub, err := usbwallet.NewLedgerHub(); err != nil {
+ glog.V(logger.Warn).Infof("Failed to start Ledger hub, disabling: %v", err)
+ } else {
+ backends = append(backends, ledgerhub)
+ }
+ return accounts.NewManager(backends...), ephemeral, nil
}
diff --git a/vendor.conf b/vendor.conf
index 1428a1b59..92092a2ac 100644
--- a/vendor.conf
+++ b/vendor.conf
@@ -13,6 +13,7 @@ github.com/golang/snappy d9eb7a3
github.com/hashicorp/golang-lru 0a025b7
github.com/huin/goupnp 679507a
github.com/jackpal/go-nat-pmp v1.0.1-4-g1fa385a
+github.com/karalabe/gousb ffa821b
github.com/maruel/panicparse ad66119
github.com/mattn/go-colorable v0.0.6-9-gd228849
github.com/mattn/go-isatty 30a891c
diff --git a/vendor/github.com/karalabe/gousb/.gitignore b/vendor/github.com/karalabe/gousb/.gitignore
new file mode 100644
index 000000000..dbec55fb6
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/.gitignore
@@ -0,0 +1 @@
+*.sw[op]
diff --git a/vendor/github.com/karalabe/gousb/.travis.yml b/vendor/github.com/karalabe/gousb/.travis.yml
new file mode 100644
index 000000000..2029cc478
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/.travis.yml
@@ -0,0 +1,12 @@
+language: go
+
+matrix:
+ include:
+ - os: linux
+ dist: trusty
+ go: 1.7.4
+ - os: osx
+ go: 1.7.4
+
+script:
+ - go test -v -test.run='BCD|Parse' ./...
diff --git a/vendor/github.com/karalabe/gousb/LICENSE b/vendor/github.com/karalabe/gousb/LICENSE
new file mode 100644
index 000000000..d64569567
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
diff --git a/vendor/github.com/karalabe/gousb/README.md b/vendor/github.com/karalabe/gousb/README.md
new file mode 100644
index 000000000..05f333003
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/README.md
@@ -0,0 +1,47 @@
+Introduction
+============
+
+[![Travis Build Status][travisimg]][travis]
+[![AppVeyor Build Status][appveyorimg]][appveyor]
+[![GoDoc][docimg]][doc]
+
+The gousb package is an attempt at wrapping the `libusb` library into a Go-like binding in a fully self-contained, go-gettable package. Supported platforms include Linux, macOS and Windows as well as the mobile platforms Android and iOS.
+
+This package is a fork of [`github.com/kylelemons/gousb`](https://github.com/kylelemons/gousb), which at the moment seems to be unmaintained. The current fork is different from the upstream package as it contains code to embed `libusb` directly into the Go package (thus becoming fully self-cotnained and go-gettable), as well as it features a few contributions and bugfixes that never really got addressed in the upstream package, but which address important issues nonetheless.
+
+*Note, if @kylelemons decides to pick development of the upstream project up again, consider all commits made by me to this repo as ready contributions. I cannot vouch for other commits as the upstream repo needs a signed CLA for Google.*
+
+[travisimg]: https://travis-ci.org/karalabe/gousb.svg?branch=master
+[travis]: https://travis-ci.org/karalabe/gousb
+[appveyorimg]: https://ci.appveyor.com/api/projects/status/84k9xse10rl72gn2/branch/master?svg=true
+[appveyor]: https://ci.appveyor.com/project/karalabe/gousb
+[docimg]: https://godoc.org/github.com/karalabe/gousb?status.svg
+[doc]: https://godoc.org/github.com/karalabe/gousb
+
+Installation
+============
+
+Example: lsusb
+--------------
+The gousb project provides a simple but useful example: lsusb. This binary will list the USB devices connected to your system and various interesting tidbits about them, their configurations, endpoints, etc. To install it, run the following command:
+
+ go get -v github.com/karalabe/gousb/lsusb
+
+gousb
+-----
+If you installed the lsusb example, both libraries below are already installed.
+
+Installing the primary gousb package is really easy:
+
+ go get -v github.com/karalabe/gousb/usb
+
+There is also a `usbid` package that will not be installed by default by this command, but which provides useful information including the human-readable vendor and product codes for detected hardware. It's not installed by default and not linked into the `usb` package by default because it adds ~400kb to the resulting binary. If you want both, they can be installed thus:
+
+ go get -v github.com/karalabe/gousb/usb{,id}
+
+Documentation
+=============
+The documentation can be viewed via local godoc or via the excellent [godoc.org](http://godoc.org/):
+
+- [usb](http://godoc.org/github.com/karalabe/gousb/usb)
+- [usbid](http://godoc.org/pkg/github.com/karalabe/gousb/usbid)
diff --git a/vendor/github.com/karalabe/gousb/appveyor.yml b/vendor/github.com/karalabe/gousb/appveyor.yml
new file mode 100644
index 000000000..b24986202
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/appveyor.yml
@@ -0,0 +1,34 @@
+os: Visual Studio 2015
+
+# Clone directly into GOPATH.
+clone_folder: C:\gopath\src\github.com\karalabe\gousb
+clone_depth: 5
+version: "{branch}.{build}"
+environment:
+ global:
+ GOPATH: C:\gopath
+ CC: gcc.exe
+ matrix:
+ - GOARCH: amd64
+ MSYS2_ARCH: x86_64
+ MSYS2_BITS: 64
+ MSYSTEM: MINGW64
+ PATH: C:\msys64\mingw64\bin\;%PATH%
+ - GOARCH: 386
+ MSYS2_ARCH: i686
+ MSYS2_BITS: 32
+ MSYSTEM: MINGW32
+ PATH: C:\msys64\mingw32\bin\;%PATH%
+
+install:
+ - rmdir C:\go /s /q
+ - appveyor DownloadFile https://storage.googleapis.com/golang/go1.7.4.windows-%GOARCH%.zip
+ - 7z x go1.7.4.windows-%GOARCH%.zip -y -oC:\ > NUL
+ - go version
+ - gcc --version
+
+build_script:
+ - go install ./...
+
+test_script:
+ - go test -v -test.run="BCD|Parse" ./...
diff --git a/vendor/github.com/karalabe/gousb/internal/libusb/AUTHORS b/vendor/github.com/karalabe/gousb/internal/libusb/AUTHORS
new file mode 100644
index 000000000..70d407bd1
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/internal/libusb/AUTHORS
@@ -0,0 +1,89 @@
+Copyright © 2001 Johannes Erdfelt <johannes@erdfelt.com>
+Copyright © 2007-2009 Daniel Drake <dsd@gentoo.org>
+Copyright © 2010-2012 Peter Stuge <peter@stuge.se>
+Copyright © 2008-2016 Nathan Hjelm <hjelmn@users.sourceforge.net>
+Copyright © 2009-2013 Pete Batard <pete@akeo.ie>
+Copyright © 2009-2013 Ludovic Rousseau <ludovic.rousseau@gmail.com>
+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>
+
+Other contributors:
+Akshay Jaggi
+Alan Ott
+Alan Stern
+Alex Vatchenko
+Andrew Fernandes
+Anthony Clay
+Antonio Ospite
+Artem Egorkine
+Aurelien Jarno
+Bastien Nocera
+Bei Zhang
+Benjamin Dobell
+Carl Karsten
+Colin Walters
+Dave Camarillo
+David Engraf
+David Moore
+Davidlohr Bueso
+Federico Manzan
+Felipe Balbi
+Florian Albrechtskirchinger
+Francesco Montorsi
+Francisco Facioni
+Gaurav Gupta
+Graeme Gill
+Gustavo Zacarias
+Hans Ulrich Niedermann
+Hector Martin
+Hoi-Ho Chan
+Ilya Konstantinov
+James Hanko
+John Sheu
+Joshua Blake
+Justin Bischoff
+Karsten Koenig
+Konrad Rzepecki
+Kuangye Guo
+Lars Kanis
+Lars Wirzenius
+Luca Longinotti
+Marcus Meissner
+Markus Heidelberg
+Martin Ettl
+Martin Koegler
+Matthias Bolte
+Mike Frysinger
+Mikhail Gusarov
+Moritz Fischer
+Ларионов Даниил
+Nicholas Corgan
+Omri Iluz
+Orin Eman
+Paul Fertser
+Pekka Nikander
+Rob Walker
+Sean McBride
+Sebastian Pipping
+Simon Haggett
+Simon Newton
+Thomas Röfer
+Tim Hutt
+Tim Roberts
+Tobias Klauser
+Toby Peterson
+Tormod Volden
+Trygve Laugstøl
+Uri Lublin
+Vasily Khoruzhick
+Vegard Storheil Eriksen
+Venkatesh Shukla
+Vitali Lovich
+Xiaofan Chen
+Zoltán Kovács
+Роман Донченко
+parafin
+xantares
diff --git a/vendor/github.com/karalabe/gousb/internal/libusb/COPYING b/vendor/github.com/karalabe/gousb/internal/libusb/COPYING
new file mode 100644
index 000000000..5ab7695ab
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/internal/libusb/COPYING
@@ -0,0 +1,504 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. 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 not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the 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
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/vendor/github.com/karalabe/gousb/internal/libusb/libusb/config.h b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/config.h
new file mode 100644
index 000000000..e004f03cd
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/config.h
@@ -0,0 +1,3 @@
+#ifndef CONFIG_H
+#define CONFIG_H
+#endif
diff --git a/vendor/github.com/karalabe/gousb/internal/libusb/libusb/core.c b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/core.c
new file mode 100644
index 000000000..d45bfe177
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/core.c
@@ -0,0 +1,2523 @@
+/* -*- Mode: C; indent-tabs-mode:t ; c-basic-offset:8 -*- */
+/*
+ * Core functions for libusb
+ * Copyright © 2012-2013 Nathan Hjelm <hjelmn@cs.unm.edu>
+ * Copyright © 2007-2008 Daniel Drake <dsd@gentoo.org>
+ * Copyright © 2001 Johannes Erdfelt <johannes@erdfelt.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 <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#ifdef HAVE_SYSLOG_H
+#include <syslog.h>
+#endif
+
+#ifdef __ANDROID__
+#include <android/log.h>
+#endif
+
+#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,
+ LIBUSB_RC, "http://libusb.info" };
+static int default_context_refcnt = 0;
+static usbi_mutex_static_t default_context_lock = USBI_MUTEX_INITIALIZER;
+static struct timespec timestamp_origin = { 0, 0 };
+
+usbi_mutex_static_t active_contexts_lock = USBI_MUTEX_INITIALIZER;
+struct list_head active_contexts_list;
+
+/**
+ * \mainpage libusb-1.0 API Reference
+ *
+ * \section intro Introduction
+ *
+ * libusb is an open source library that allows you to communicate with USB
+ * devices from userspace. For more info, see the
+ * <a href="http://libusb.info">libusb homepage</a>.
+ *
+ * This documentation is aimed at application developers wishing to
+ * communicate with USB peripherals from their own software. After reviewing
+ * this documentation, feedback and questions can be sent to the
+ * <a href="http://mailing-list.libusb.info">libusb-devel mailing list</a>.
+ *
+ * This documentation assumes knowledge of how to operate USB devices from
+ * a software standpoint (descriptors, configurations, interfaces, endpoints,
+ * control/bulk/interrupt/isochronous transfers, etc). Full information
+ * can be found in the <a href="http://www.usb.org/developers/docs/">USB 3.0
+ * Specification</a> which is available for free download. You can probably
+ * find less verbose introductions by searching the web.
+ *
+ * \section API Application Programming Interface (API)
+ *
+ * See the \ref libusb_api page for a complete list of the libusb functions.
+ *
+ * \section features Library features
+ *
+ * - All transfer types supported (control/bulk/interrupt/isochronous)
+ * - 2 transfer interfaces:
+ * -# Synchronous (simple)
+ * -# Asynchronous (more complicated, but more powerful)
+ * - Thread safe (although the asynchronous interface means that you
+ * usually won't need to thread)
+ * - Lightweight with lean API
+ * - Compatible with libusb-0.1 through the libusb-compat-0.1 translation layer
+ * - Hotplug support (on some platforms). See \ref libusb_hotplug.
+ *
+ * \section gettingstarted Getting Started
+ *
+ * To begin reading the API documentation, start with the Modules page which
+ * links to the different categories of libusb's functionality.
+ *
+ * One decision you will have to make is whether to use the synchronous
+ * or the asynchronous data transfer interface. The \ref libusb_io documentation
+ * provides some insight into this topic.
+ *
+ * Some example programs can be found in the libusb source distribution under
+ * the "examples" subdirectory. The libusb homepage includes a list of
+ * real-life project examples which use libusb.
+ *
+ * \section errorhandling Error handling
+ *
+ * libusb functions typically return 0 on success or a negative error code
+ * on failure. These negative error codes relate to LIBUSB_ERROR constants
+ * which are listed on the \ref libusb_misc "miscellaneous" documentation page.
+ *
+ * \section msglog Debug message logging
+ *
+ * libusb uses stderr for all logging. By default, logging is set to NONE,
+ * which means that no output will be produced. However, unless the library
+ * has been compiled with logging disabled, then any application calls to
+ * libusb_set_debug(), or the setting of the environmental variable
+ * LIBUSB_DEBUG outside of the application, can result in logging being
+ * produced. Your application should therefore not close stderr, but instead
+ * direct it to the null device if its output is undesirable.
+ *
+ * The libusb_set_debug() function can be used to enable logging of certain
+ * messages. Under standard configuration, libusb doesn't really log much
+ * so you are advised to use this function to enable all error/warning/
+ * informational messages. It will help debug problems with your software.
+ *
+ * The logged messages are unstructured. There is no one-to-one correspondence
+ * between messages being logged and success or failure return codes from
+ * libusb functions. There is no format to the messages, so you should not
+ * try to capture or parse them. They are not and will not be localized.
+ * These messages are not intended to being passed to your application user;
+ * instead, you should interpret the error codes returned from libusb functions
+ * and provide appropriate notification to the user. The messages are simply
+ * there to aid you as a programmer, and if you're confused because you're
+ * getting a strange error code from a libusb function, enabling message
+ * logging may give you a suitable explanation.
+ *
+ * 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
+ * environment variable is set, the message logging verbosity level is fixed
+ * and libusb_set_debug() 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.
+ *
+ * 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.
+ *
+ * \section remarks Other remarks
+ *
+ * libusb does have imperfections. The \ref libusb_caveats "caveats" page attempts
+ * to document these.
+ */
+
+/**
+ * \page libusb_caveats Caveats
+ *
+ * \section devresets Device resets
+ *
+ * The libusb_reset_device() function allows you to reset a device. If your
+ * program has to call such a function, it should obviously be aware that
+ * the reset will cause device state to change (e.g. register values may be
+ * reset).
+ *
+ * The problem is that any other program could reset the device your program
+ * is working with, at any time. libusb does not offer a mechanism to inform
+ * you when this has happened, so if someone else resets your device it will
+ * not be clear to your own program why the device state has changed.
+ *
+ * Ultimately, this is a limitation of writing drivers in userspace.
+ * Separation from the USB stack in the underlying kernel makes it difficult
+ * for the operating system to deliver such notifications to your program.
+ * The Linux kernel USB stack allows such reset notifications to be delivered
+ * to in-kernel USB drivers, but it is not clear how such notifications could
+ * be delivered to second-class drivers that live in userspace.
+ *
+ * \section blockonly Blocking-only functionality
+ *
+ * The functionality listed below is only available through synchronous,
+ * blocking functions. There are no asynchronous/non-blocking alternatives,
+ * and no clear ways of implementing these.
+ *
+ * - Configuration activation (libusb_set_configuration())
+ * - Interface/alternate setting activation (libusb_set_interface_alt_setting())
+ * - Releasing of interfaces (libusb_release_interface())
+ * - Clearing of halt/stall condition (libusb_clear_halt())
+ * - Device resets (libusb_reset_device())
+ *
+ * \section configsel Configuration selection and handling
+ *
+ * When libusb presents a device handle to an application, there is a chance
+ * that the corresponding device may be in unconfigured state. For devices
+ * with multiple configurations, there is also a chance that the configuration
+ * currently selected is not the one that the application wants to use.
+ *
+ * The obvious solution is to add a call to libusb_set_configuration() early
+ * on during your device initialization routines, but there are caveats to
+ * be aware of:
+ * -# If the device is already in the desired configuration, calling
+ * libusb_set_configuration() using the same configuration value will cause
+ * a lightweight device reset. This may not be desirable behaviour.
+ * -# In the case where the desired configuration is already active, libusb
+ * may not even be able to perform a lightweight device reset. For example,
+ * take my USB keyboard with fingerprint reader: I'm interested in driving
+ * the fingerprint reader interface through libusb, but the kernel's
+ * USB-HID driver will almost always have claimed the keyboard interface.
+ * Because the kernel has claimed an interface, it is not even possible to
+ * perform the lightweight device reset, so libusb_set_configuration() will
+ * fail. (Luckily the device in question only has a single configuration.)
+ * -# libusb will be unable to set a configuration if other programs or
+ * drivers have claimed interfaces. In particular, this means that kernel
+ * drivers must be detached from all the interfaces before
+ * libusb_set_configuration() may succeed.
+ *
+ * One solution to some of the above problems is to consider the currently
+ * active configuration. If the configuration we want is already active, then
+ * we don't have to select any configuration:
+\code
+cfg = -1;
+libusb_get_configuration(dev, &cfg);
+if (cfg != desired)
+ libusb_set_configuration(dev, desired);
+\endcode
+ *
+ * This is probably suitable for most scenarios, but is inherently racy:
+ * another application or driver may change the selected configuration
+ * <em>after</em> the libusb_get_configuration() call.
+ *
+ * Even in cases where libusb_set_configuration() succeeds, consider that other
+ * applications or drivers may change configuration after your application
+ * calls libusb_set_configuration().
+ *
+ * One possible way to lock your device into a specific configuration is as
+ * follows:
+ * -# Set the desired configuration (or use the logic above to realise that
+ * it is already in the desired configuration)
+ * -# Claim the interface that you wish to use
+ * -# Check that the currently active configuration is the one that you want
+ * to use.
+ *
+ * The above method works because once an interface is claimed, no application
+ * or driver is able to select another configuration.
+ *
+ * \section earlycomp Early transfer completion
+ *
+ * NOTE: This section is currently Linux-centric. I am not sure if any of these
+ * considerations apply to Darwin or other platforms.
+ *
+ * When a transfer completes early (i.e. when less data is received/sent in
+ * any one packet than the transfer buffer allows for) then libusb is designed
+ * to terminate the transfer immediately, not transferring or receiving any
+ * more data unless other transfers have been queued by the user.
+ *
+ * On legacy platforms, libusb is unable to do this in all situations. After
+ * the incomplete packet occurs, "surplus" data may be transferred. For recent
+ * versions of libusb, this information is kept (the data length of the
+ * transfer is updated) and, for device-to-host transfers, any surplus data was
+ * added to the buffer. Still, this is not a nice solution because it loses the
+ * 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
+ * submitting a transfer of zero length.
+ * - The \ref libusb_transfer_flags::LIBUSB_TRANSFER_ADD_ZERO_PACKET
+ * "LIBUSB_TRANSFER_ADD_ZERO_PACKET" flag is currently only supported on Linux.
+ */
+
+/**
+ * \page libusb_contexts Contexts
+ *
+ * It is possible that libusb may be used simultaneously from two independent
+ * libraries linked into the same executable. For example, if your application
+ * has a plugin-like system which allows the user to dynamically load a range
+ * of modules into your program, it is feasible that two independently
+ * 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
+ * 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.
+ *
+ * This is made possible through libusb's <em>context</em> concept. When you
+ * call libusb_init(), you are (optionally) given a context. You can then pass
+ * this context pointer back into future libusb functions.
+ *
+ * In order to keep things simple for more simplistic applications, it is
+ * legal to pass NULL to all functions requiring a context pointer (as long as
+ * you're sure no other code will attempt to use libusb from the same process).
+ * When you pass NULL, the default context will be used. The default context
+ * is created the first time a process calls libusb_init() when no other
+ * context is alive. Contexts are destroyed during libusb_exit().
+ *
+ * The default context is reference-counted and can be shared. That means that
+ * if libusb_init(NULL) is called twice within the same process, the two
+ * users end up sharing the same context. The deinitialization and freeing of
+ * the default context will only happen when the last user calls libusb_exit().
+ * In other words, the default context is created and initialized when its
+ * reference count goes from 0 to 1, and is deinitialized and destroyed when
+ * its reference count goes from 1 to 0.
+ *
+ * You may be wondering why only a subset of libusb functions require a
+ * context pointer in their function definition. Internally, libusb stores
+ * context pointers in other objects (e.g. libusb_device instances) and hence
+ * can infer the context from those objects.
+ */
+
+ /**
+ * \page libusb_api Application Programming Interface
+ *
+ * This is the complete list of libusb functions, structures and
+ * enumerations in alphabetical order.
+ *
+ * \section Functions
+ * - libusb_alloc_streams()
+ * - libusb_alloc_transfer()
+ * - libusb_attach_kernel_driver()
+ * - libusb_bulk_transfer()
+ * - libusb_cancel_transfer()
+ * - libusb_claim_interface()
+ * - libusb_clear_halt()
+ * - libusb_close()
+ * - libusb_control_transfer()
+ * - libusb_control_transfer_get_data()
+ * - libusb_control_transfer_get_setup()
+ * - libusb_cpu_to_le16()
+ * - libusb_detach_kernel_driver()
+ * - libusb_dev_mem_alloc()
+ * - libusb_dev_mem_free()
+ * - libusb_error_name()
+ * - libusb_event_handler_active()
+ * - libusb_event_handling_ok()
+ * - libusb_exit()
+ * - libusb_fill_bulk_stream_transfer()
+ * - libusb_fill_bulk_transfer()
+ * - libusb_fill_control_setup()
+ * - libusb_fill_control_transfer()
+ * - libusb_fill_interrupt_transfer()
+ * - libusb_fill_iso_transfer()
+ * - libusb_free_bos_descriptor()
+ * - libusb_free_config_descriptor()
+ * - libusb_free_container_id_descriptor()
+ * - libusb_free_device_list()
+ * - libusb_free_pollfds()
+ * - libusb_free_ss_endpoint_companion_descriptor()
+ * - libusb_free_ss_usb_device_capability_descriptor()
+ * - libusb_free_streams()
+ * - libusb_free_transfer()
+ * - libusb_free_usb_2_0_extension_descriptor()
+ * - libusb_get_active_config_descriptor()
+ * - libusb_get_bos_descriptor()
+ * - libusb_get_bus_number()
+ * - libusb_get_config_descriptor()
+ * - libusb_get_config_descriptor_by_value()
+ * - libusb_get_configuration()
+ * - libusb_get_container_id_descriptor()
+ * - libusb_get_descriptor()
+ * - libusb_get_device()
+ * - libusb_get_device_address()
+ * - libusb_get_device_descriptor()
+ * - libusb_get_device_list()
+ * - libusb_get_device_speed()
+ * - libusb_get_iso_packet_buffer()
+ * - libusb_get_iso_packet_buffer_simple()
+ * - libusb_get_max_iso_packet_size()
+ * - libusb_get_max_packet_size()
+ * - libusb_get_next_timeout()
+ * - libusb_get_parent()
+ * - libusb_get_pollfds()
+ * - libusb_get_port_number()
+ * - libusb_get_port_numbers()
+ * - libusb_get_port_path()
+ * - libusb_get_ss_endpoint_companion_descriptor()
+ * - libusb_get_ss_usb_device_capability_descriptor()
+ * - libusb_get_string_descriptor()
+ * - libusb_get_string_descriptor_ascii()
+ * - libusb_get_usb_2_0_extension_descriptor()
+ * - libusb_get_version()
+ * - libusb_handle_events()
+ * - libusb_handle_events_completed()
+ * - libusb_handle_events_locked()
+ * - libusb_handle_events_timeout()
+ * - libusb_handle_events_timeout_completed()
+ * - libusb_has_capability()
+ * - libusb_hotplug_deregister_callback()
+ * - libusb_hotplug_register_callback()
+ * - libusb_init()
+ * - libusb_interrupt_event_handler()
+ * - libusb_interrupt_transfer()
+ * - libusb_kernel_driver_active()
+ * - libusb_lock_events()
+ * - libusb_lock_event_waiters()
+ * - libusb_open()
+ * - libusb_open_device_with_vid_pid()
+ * - libusb_pollfds_handle_timeouts()
+ * - libusb_ref_device()
+ * - libusb_release_interface()
+ * - libusb_reset_device()
+ * - libusb_set_auto_detach_kernel_driver()
+ * - libusb_set_configuration()
+ * - libusb_set_debug()
+ * - libusb_set_interface_alt_setting()
+ * - libusb_set_iso_packet_lengths()
+ * - libusb_setlocale()
+ * - libusb_set_pollfd_notifiers()
+ * - libusb_strerror()
+ * - libusb_submit_transfer()
+ * - libusb_transfer_get_stream_id()
+ * - libusb_transfer_set_stream_id()
+ * - libusb_try_lock_events()
+ * - libusb_unlock_events()
+ * - libusb_unlock_event_waiters()
+ * - libusb_unref_device()
+ * - libusb_wait_for_event()
+ *
+ * \section Structures
+ * - libusb_bos_descriptor
+ * - libusb_bos_dev_capability_descriptor
+ * - libusb_config_descriptor
+ * - libusb_container_id_descriptor
+ * - \ref libusb_context
+ * - libusb_control_setup
+ * - \ref libusb_device
+ * - libusb_device_descriptor
+ * - \ref libusb_device_handle
+ * - libusb_endpoint_descriptor
+ * - libusb_interface
+ * - libusb_interface_descriptor
+ * - libusb_iso_packet_descriptor
+ * - libusb_pollfd
+ * - libusb_ss_endpoint_companion_descriptor
+ * - libusb_ss_usb_device_capability_descriptor
+ * - libusb_transfer
+ * - libusb_usb_2_0_extension_descriptor
+ * - libusb_version
+ *
+ * \section Enums
+ * - \ref libusb_bos_type
+ * - \ref libusb_capability
+ * - \ref libusb_class_code
+ * - \ref libusb_descriptor_type
+ * - \ref libusb_endpoint_direction
+ * - \ref libusb_error
+ * - \ref libusb_iso_sync_type
+ * - \ref libusb_iso_usage_type
+ * - \ref libusb_log_level
+ * - \ref libusb_request_recipient
+ * - \ref libusb_request_type
+ * - \ref libusb_speed
+ * - \ref libusb_ss_usb_device_capability_attributes
+ * - \ref libusb_standard_request
+ * - \ref libusb_supported_speed
+ * - \ref libusb_transfer_flags
+ * - \ref libusb_transfer_status
+ * - \ref libusb_transfer_type
+ * - \ref libusb_usb_2_0_extension_attributes
+ */
+
+/**
+ * @defgroup libusb_lib Library initialization/deinitialization
+ * This page details how to initialize and deinitialize libusb. Initialization
+ * must be performed before using any libusb functionality, and similarly you
+ * must not call any libusb functions after deinitialization.
+ */
+
+/**
+ * @defgroup libusb_dev Device handling and enumeration
+ * The functionality documented below is designed to help with the following
+ * operations:
+ * - Enumerating the USB devices currently attached to the system
+ * - Choosing a device to operate from your software
+ * - Opening and closing the chosen device
+ *
+ * \section nutshell In a nutshell...
+ *
+ * The description below really makes things sound more complicated than they
+ * actually are. The following sequence of function calls will be suitable
+ * for almost all scenarios and does not require you to have such a deep
+ * understanding of the resource management issues:
+ * \code
+// discover devices
+libusb_device **list;
+libusb_device *found = NULL;
+ssize_t cnt = libusb_get_device_list(NULL, &list);
+ssize_t i = 0;
+int err = 0;
+if (cnt < 0)
+ error();
+
+for (i = 0; i < cnt; i++) {
+ libusb_device *device = list[i];
+ if (is_interesting(device)) {
+ found = device;
+ break;
+ }
+}
+
+if (found) {
+ libusb_device_handle *handle;
+
+ err = libusb_open(found, &handle);
+ if (err)
+ error();
+ // etc
+}
+
+libusb_free_device_list(list, 1);
+\endcode
+ *
+ * The two important points:
+ * - You asked libusb_free_device_list() to unreference the devices (2nd
+ * parameter)
+ * - You opened the device before freeing the list and unreferencing the
+ * devices
+ *
+ * If you ended up with a handle, you can now proceed to perform I/O on the
+ * device.
+ *
+ * \section devshandles Devices and device handles
+ * libusb has a concept of a USB device, represented by the
+ * \ref libusb_device opaque type. A device represents a USB device that
+ * is currently or was previously connected to the system. Using a reference
+ * to a device, you can determine certain information about the device (e.g.
+ * you can read the descriptor data).
+ *
+ * The libusb_get_device_list() function can be used to obtain a list of
+ * devices currently connected to the system. This is known as device
+ * discovery.
+ *
+ * Just because you have a reference to a device does not mean it is
+ * necessarily usable. The device may have been unplugged, you may not have
+ * permission to operate such device, or another program or driver may be
+ * using the device.
+ *
+ * When you've found a device that you'd like to operate, you must ask
+ * libusb to open the device using the libusb_open() function. Assuming
+ * success, libusb then returns you a <em>device handle</em>
+ * (a \ref libusb_device_handle pointer). All "real" I/O operations then
+ * operate on the handle rather than the original device pointer.
+ *
+ * \section devref Device discovery and reference counting
+ *
+ * Device discovery (i.e. calling libusb_get_device_list()) returns a
+ * freshly-allocated list of devices. The list itself must be freed when
+ * you are done with it. libusb also needs to know when it is OK to free
+ * the contents of the list - the devices themselves.
+ *
+ * To handle these issues, libusb provides you with two separate items:
+ * - A function to free the list itself
+ * - A reference counting system for the devices inside
+ *
+ * New devices presented by the libusb_get_device_list() function all have a
+ * reference count of 1. You can increase and decrease reference count using
+ * libusb_ref_device() and libusb_unref_device(). A device is destroyed when
+ * its reference count reaches 0.
+ *
+ * With the above information in mind, the process of opening a device can
+ * be viewed as follows:
+ * -# Discover devices using libusb_get_device_list().
+ * -# Choose the device that you want to operate, and call libusb_open().
+ * -# Unref all devices in the discovered device list.
+ * -# Free the discovered device list.
+ *
+ * The order is important - you must not unreference the device before
+ * attempting to open it, because unreferencing it may destroy the device.
+ *
+ * For convenience, the libusb_free_device_list() function includes a
+ * parameter to optionally unreference all the devices in the list before
+ * freeing the list itself. This combines steps 3 and 4 above.
+ *
+ * As an implementation detail, libusb_open() actually adds a reference to
+ * the device in question. This is because the device remains available
+ * through the handle via libusb_get_device(). The reference is deleted during
+ * libusb_close().
+ */
+
+/** @defgroup libusb_misc Miscellaneous */
+
+/* we traverse usbfs without knowing how many devices we are going to find.
+ * so we create this discovered_devs model which is similar to a linked-list
+ * which grows when required. it can be freed once discovery has completed,
+ * eliminating the need for a list node in the libusb_device structure
+ * itself. */
+#define DISCOVERED_DEVICES_SIZE_STEP 8
+
+static struct discovered_devs *discovered_devs_alloc(void)
+{
+ struct discovered_devs *ret =
+ malloc(sizeof(*ret) + (sizeof(void *) * DISCOVERED_DEVICES_SIZE_STEP));
+
+ if (ret) {
+ ret->len = 0;
+ ret->capacity = DISCOVERED_DEVICES_SIZE_STEP;
+ }
+ return ret;
+}
+
+static void discovered_devs_free(struct discovered_devs *discdevs)
+{
+ size_t i;
+
+ for (i = 0; i < discdevs->len; i++)
+ libusb_unref_device(discdevs->devices[i]);
+
+ free(discdevs);
+}
+
+/* append a device to the discovered devices collection. may realloc itself,
+ * returning new discdevs. returns NULL on realloc failure. */
+struct discovered_devs *discovered_devs_append(
+ struct discovered_devs *discdevs, struct libusb_device *dev)
+{
+ size_t len = discdevs->len;
+ size_t capacity;
+ struct discovered_devs *new_discdevs;
+
+ /* if there is space, just append the device */
+ if (len < discdevs->capacity) {
+ discdevs->devices[len] = libusb_ref_device(dev);
+ discdevs->len++;
+ return discdevs;
+ }
+
+ /* exceeded capacity, need to grow */
+ usbi_dbg("need to increase capacity");
+ capacity = discdevs->capacity + DISCOVERED_DEVICES_SIZE_STEP;
+ /* can't use usbi_reallocf here because in failure cases it would
+ * free the existing discdevs without unreferencing its devices. */
+ new_discdevs = realloc(discdevs,
+ sizeof(*discdevs) + (sizeof(void *) * capacity));
+ if (!new_discdevs) {
+ discovered_devs_free(discdevs);
+ return NULL;
+ }
+
+ discdevs = new_discdevs;
+ discdevs->capacity = capacity;
+ discdevs->devices[len] = libusb_ref_device(dev);
+ discdevs->len++;
+
+ return discdevs;
+}
+
+/* Allocate a new device with a specific session ID. The returned device has
+ * a reference count of 1. */
+struct libusb_device *usbi_alloc_device(struct libusb_context *ctx,
+ unsigned long session_id)
+{
+ size_t priv_size = usbi_backend->device_priv_size;
+ struct libusb_device *dev = calloc(1, sizeof(*dev) + priv_size);
+ int r;
+
+ if (!dev)
+ return NULL;
+
+ r = usbi_mutex_init(&dev->lock);
+ if (r) {
+ free(dev);
+ return NULL;
+ }
+
+ dev->ctx = ctx;
+ dev->refcnt = 1;
+ dev->session_data = session_id;
+ dev->speed = LIBUSB_SPEED_UNKNOWN;
+
+ if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
+ usbi_connect_device (dev);
+ }
+
+ return dev;
+}
+
+void usbi_connect_device(struct libusb_device *dev)
+{
+ struct libusb_context *ctx = DEVICE_CTX(dev);
+
+ dev->attached = 1;
+
+ usbi_mutex_lock(&dev->ctx->usb_devs_lock);
+ list_add(&dev->list, &dev->ctx->usb_devs);
+ usbi_mutex_unlock(&dev->ctx->usb_devs_lock);
+
+ /* Signal that an event has occurred for this device if we support hotplug AND
+ * the hotplug message list is ready. This prevents an event from getting raised
+ * during initial enumeration. */
+ if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG) && dev->ctx->hotplug_msgs.next) {
+ usbi_hotplug_notification(ctx, dev, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED);
+ }
+}
+
+void usbi_disconnect_device(struct libusb_device *dev)
+{
+ struct libusb_context *ctx = DEVICE_CTX(dev);
+
+ usbi_mutex_lock(&dev->lock);
+ dev->attached = 0;
+ usbi_mutex_unlock(&dev->lock);
+
+ usbi_mutex_lock(&ctx->usb_devs_lock);
+ list_del(&dev->list);
+ usbi_mutex_unlock(&ctx->usb_devs_lock);
+
+ /* Signal that an event has occurred for this device if we support hotplug AND
+ * the hotplug message list is ready. This prevents an event from getting raised
+ * during initial enumeration. libusb_handle_events will take care of dereferencing
+ * the device. */
+ if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG) && dev->ctx->hotplug_msgs.next) {
+ usbi_hotplug_notification(ctx, dev, LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT);
+ }
+}
+
+/* Perform some final sanity checks on a newly discovered device. If this
+ * function fails (negative return code), the device should not be added
+ * to the discovered device list. */
+int usbi_sanitize_device(struct libusb_device *dev)
+{
+ int r;
+ uint8_t num_configurations;
+
+ r = usbi_device_cache_descriptor(dev);
+ if (r < 0)
+ return r;
+
+ num_configurations = dev->device_descriptor.bNumConfigurations;
+ if (num_configurations > USB_MAXCONFIG) {
+ usbi_err(DEVICE_CTX(dev), "too many configurations");
+ return LIBUSB_ERROR_IO;
+ } else if (0 == num_configurations)
+ usbi_dbg("zero configurations, maybe an unauthorized device");
+
+ dev->num_configurations = num_configurations;
+ return 0;
+}
+
+/* Examine libusb's internal list of known devices, looking for one with
+ * a specific session ID. Returns the matching device if it was found, and
+ * NULL otherwise. */
+struct libusb_device *usbi_get_device_by_session_id(struct libusb_context *ctx,
+ unsigned long session_id)
+{
+ struct libusb_device *dev;
+ struct libusb_device *ret = NULL;
+
+ usbi_mutex_lock(&ctx->usb_devs_lock);
+ list_for_each_entry(dev, &ctx->usb_devs, list, struct libusb_device)
+ if (dev->session_data == session_id) {
+ ret = libusb_ref_device(dev);
+ break;
+ }
+ usbi_mutex_unlock(&ctx->usb_devs_lock);
+
+ return ret;
+}
+
+/** @ingroup libusb_dev
+ * Returns a list of USB devices currently attached to the system. This is
+ * your entry point into finding a USB device to operate.
+ *
+ * You are expected to unreference all the devices when you are done with
+ * them, and then free the list with libusb_free_device_list(). Note that
+ * libusb_free_device_list() can unref all the devices for you. Be careful
+ * not to unreference a device you are about to open until after you have
+ * opened it.
+ *
+ * This return value of this function indicates the number of devices in
+ * the resultant list. The list is actually one element larger, as it is
+ * NULL-terminated.
+ *
+ * \param ctx the context to operate on, or NULL for the default context
+ * \param list output location for a list of devices. Must be later freed with
+ * libusb_free_device_list().
+ * \returns the number of devices in the outputted list, or any
+ * \ref libusb_error according to errors encountered by the backend.
+ */
+ssize_t API_EXPORTED libusb_get_device_list(libusb_context *ctx,
+ libusb_device ***list)
+{
+ struct discovered_devs *discdevs = discovered_devs_alloc();
+ struct libusb_device **ret;
+ int r = 0;
+ ssize_t i, len;
+ USBI_GET_CONTEXT(ctx);
+ usbi_dbg("");
+
+ if (!discdevs)
+ return LIBUSB_ERROR_NO_MEM;
+
+ if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
+ /* backend provides hotplug support */
+ struct libusb_device *dev;
+
+ 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) {
+ discdevs = discovered_devs_append(discdevs, dev);
+
+ if (!discdevs) {
+ r = LIBUSB_ERROR_NO_MEM;
+ break;
+ }
+ }
+ usbi_mutex_unlock(&ctx->usb_devs_lock);
+ } else {
+ /* backend does not provide hotplug support */
+ r = usbi_backend->get_device_list(ctx, &discdevs);
+ }
+
+ if (r < 0) {
+ len = r;
+ goto out;
+ }
+
+ /* convert discovered_devs into a list */
+ len = discdevs->len;
+ ret = calloc(len + 1, sizeof(struct libusb_device *));
+ if (!ret) {
+ len = LIBUSB_ERROR_NO_MEM;
+ goto out;
+ }
+
+ ret[len] = NULL;
+ for (i = 0; i < len; i++) {
+ struct libusb_device *dev = discdevs->devices[i];
+ ret[i] = libusb_ref_device(dev);
+ }
+ *list = ret;
+
+out:
+ if (discdevs)
+ discovered_devs_free(discdevs);
+ return len;
+}
+
+/** \ingroup libusb_dev
+ * Frees a list of devices previously discovered using
+ * libusb_get_device_list(). If the unref_devices parameter is set, the
+ * reference count of each device in the list is decremented by 1.
+ * \param list the list to free
+ * \param unref_devices whether to unref the devices in the list
+ */
+void API_EXPORTED libusb_free_device_list(libusb_device **list,
+ int unref_devices)
+{
+ if (!list)
+ return;
+
+ if (unref_devices) {
+ int i = 0;
+ struct libusb_device *dev;
+
+ while ((dev = list[i++]) != NULL)
+ libusb_unref_device(dev);
+ }
+ free(list);
+}
+
+/** \ingroup libusb_dev
+ * Get the number of the bus that a device is connected to.
+ * \param dev a device
+ * \returns the bus number
+ */
+uint8_t API_EXPORTED libusb_get_bus_number(libusb_device *dev)
+{
+ return dev->bus_number;
+}
+
+/** \ingroup libusb_dev
+ * Get the number of the port that a device is connected to.
+ * Unless the OS does something funky, or you are hot-plugging USB extension cards,
+ * the port number returned by this call is usually guaranteed to be uniquely tied
+ * to a physical port, meaning that different devices plugged on the same physical
+ * port should return the same port number.
+ *
+ * But outside of this, there is no guarantee that the port number returned by this
+ * call will remain the same, or even match the order in which ports have been
+ * numbered by the HUB/HCD manufacturer.
+ *
+ * \param dev a device
+ * \returns the port number (0 if not available)
+ */
+uint8_t API_EXPORTED libusb_get_port_number(libusb_device *dev)
+{
+ return dev->port_number;
+}
+
+/** \ingroup libusb_dev
+ * Get the list of all port numbers from root for the specified device
+ *
+ * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102
+ * \param dev a device
+ * \param port_numbers the array that should contain the port numbers
+ * \param port_numbers_len the maximum length of the array. As per the USB 3.0
+ * specs, the current maximum limit for the depth is 7.
+ * \returns the number of elements filled
+ * \returns LIBUSB_ERROR_OVERFLOW if the array is too small
+ */
+int API_EXPORTED libusb_get_port_numbers(libusb_device *dev,
+ uint8_t* port_numbers, int port_numbers_len)
+{
+ int i = port_numbers_len;
+ struct libusb_context *ctx = DEVICE_CTX(dev);
+
+ if (port_numbers_len <= 0)
+ return LIBUSB_ERROR_INVALID_PARAM;
+
+ // HCDs can be listed as devices with port #0
+ while((dev) && (dev->port_number != 0)) {
+ if (--i < 0) {
+ usbi_warn(ctx, "port numbers array is too small");
+ return LIBUSB_ERROR_OVERFLOW;
+ }
+ port_numbers[i] = dev->port_number;
+ dev = dev->parent_dev;
+ }
+ if (i < port_numbers_len)
+ memmove(port_numbers, &port_numbers[i], port_numbers_len - i);
+ return port_numbers_len - i;
+}
+
+/** \ingroup libusb_dev
+ * Deprecated please use libusb_get_port_numbers instead.
+ */
+int API_EXPORTED libusb_get_port_path(libusb_context *ctx, libusb_device *dev,
+ uint8_t* port_numbers, uint8_t port_numbers_len)
+{
+ UNUSED(ctx);
+
+ return libusb_get_port_numbers(dev, port_numbers, port_numbers_len);
+}
+
+/** \ingroup libusb_dev
+ * Get the the parent from the specified device.
+ * \param dev a device
+ * \returns the device parent or NULL if not available
+ * You should issue a \ref libusb_get_device_list() before calling this
+ * function and make sure that you only access the parent before issuing
+ * \ref libusb_free_device_list(). The reason is that libusb currently does
+ * not maintain a permanent list of device instances, and therefore can
+ * only guarantee that parents are fully instantiated within a
+ * libusb_get_device_list() - libusb_free_device_list() block.
+ */
+DEFAULT_VISIBILITY
+libusb_device * LIBUSB_CALL libusb_get_parent(libusb_device *dev)
+{
+ return dev->parent_dev;
+}
+
+/** \ingroup libusb_dev
+ * Get the address of the device on the bus it is connected to.
+ * \param dev a device
+ * \returns the device address
+ */
+uint8_t API_EXPORTED libusb_get_device_address(libusb_device *dev)
+{
+ return dev->device_address;
+}
+
+/** \ingroup libusb_dev
+ * Get the negotiated connection speed for a device.
+ * \param dev a device
+ * \returns a \ref libusb_speed code, where LIBUSB_SPEED_UNKNOWN means that
+ * the OS doesn't know or doesn't support returning the negotiated speed.
+ */
+int API_EXPORTED libusb_get_device_speed(libusb_device *dev)
+{
+ return dev->speed;
+}
+
+static const struct libusb_endpoint_descriptor *find_endpoint(
+ struct libusb_config_descriptor *config, unsigned char endpoint)
+{
+ int iface_idx;
+ for (iface_idx = 0; iface_idx < config->bNumInterfaces; iface_idx++) {
+ const struct libusb_interface *iface = &config->interface[iface_idx];
+ int altsetting_idx;
+
+ for (altsetting_idx = 0; altsetting_idx < iface->num_altsetting;
+ altsetting_idx++) {
+ const struct libusb_interface_descriptor *altsetting
+ = &iface->altsetting[altsetting_idx];
+ int ep_idx;
+
+ for (ep_idx = 0; ep_idx < altsetting->bNumEndpoints; ep_idx++) {
+ const struct libusb_endpoint_descriptor *ep =
+ &altsetting->endpoint[ep_idx];
+ if (ep->bEndpointAddress == endpoint)
+ return ep;
+ }
+ }
+ }
+ return NULL;
+}
+
+/** \ingroup libusb_dev
+ * Convenience function to retrieve the wMaxPacketSize value for a particular
+ * endpoint in the active device configuration.
+ *
+ * This function was originally intended to be of assistance when setting up
+ * isochronous transfers, but a design mistake resulted in this function
+ * instead. It simply returns the wMaxPacketSize value without considering
+ * its contents. If you're dealing with isochronous transfers, you probably
+ * want libusb_get_max_iso_packet_size() instead.
+ *
+ * \param dev a device
+ * \param endpoint address of the endpoint in question
+ * \returns the wMaxPacketSize value
+ * \returns LIBUSB_ERROR_NOT_FOUND if the endpoint does not exist
+ * \returns LIBUSB_ERROR_OTHER on other failure
+ */
+int API_EXPORTED libusb_get_max_packet_size(libusb_device *dev,
+ unsigned char endpoint)
+{
+ struct libusb_config_descriptor *config;
+ const struct libusb_endpoint_descriptor *ep;
+ int r;
+
+ r = libusb_get_active_config_descriptor(dev, &config);
+ if (r < 0) {
+ usbi_err(DEVICE_CTX(dev),
+ "could not retrieve active config descriptor");
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ ep = find_endpoint(config, endpoint);
+ if (!ep) {
+ r = LIBUSB_ERROR_NOT_FOUND;
+ goto out;
+ }
+
+ r = ep->wMaxPacketSize;
+
+out:
+ libusb_free_config_descriptor(config);
+ return r;
+}
+
+/** \ingroup libusb_dev
+ * Calculate the maximum packet size which a specific endpoint is capable is
+ * sending or receiving in the duration of 1 microframe
+ *
+ * Only the active configuration is examined. The calculation is based on the
+ * wMaxPacketSize field in the endpoint descriptor as described in section
+ * 9.6.6 in the USB 2.0 specifications.
+ *
+ * If acting on an isochronous or interrupt endpoint, this function will
+ * multiply the value found in bits 0:10 by the number of transactions per
+ * microframe (determined by bits 11:12). Otherwise, this function just
+ * returns the numeric value found in bits 0:10.
+ *
+ * This function is useful for setting up isochronous transfers, for example
+ * you might pass the return value from this function to
+ * libusb_set_iso_packet_lengths() in order to set the length field of every
+ * isochronous packet in a transfer.
+ *
+ * Since v1.0.3.
+ *
+ * \param dev a device
+ * \param endpoint address of the endpoint in question
+ * \returns the maximum packet size which can be sent/received on this endpoint
+ * \returns LIBUSB_ERROR_NOT_FOUND if the endpoint does not exist
+ * \returns LIBUSB_ERROR_OTHER on other failure
+ */
+int API_EXPORTED libusb_get_max_iso_packet_size(libusb_device *dev,
+ unsigned char endpoint)
+{
+ struct libusb_config_descriptor *config;
+ const struct libusb_endpoint_descriptor *ep;
+ enum libusb_transfer_type ep_type;
+ uint16_t val;
+ int r;
+
+ r = libusb_get_active_config_descriptor(dev, &config);
+ if (r < 0) {
+ usbi_err(DEVICE_CTX(dev),
+ "could not retrieve active config descriptor");
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ ep = find_endpoint(config, endpoint);
+ if (!ep) {
+ r = LIBUSB_ERROR_NOT_FOUND;
+ goto out;
+ }
+
+ val = ep->wMaxPacketSize;
+ ep_type = (enum libusb_transfer_type) (ep->bmAttributes & 0x3);
+
+ r = val & 0x07ff;
+ if (ep_type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS
+ || ep_type == LIBUSB_TRANSFER_TYPE_INTERRUPT)
+ r *= (1 + ((val >> 11) & 3));
+
+out:
+ libusb_free_config_descriptor(config);
+ return r;
+}
+
+/** \ingroup libusb_dev
+ * Increment the reference count of a device.
+ * \param dev the device to reference
+ * \returns the same device
+ */
+DEFAULT_VISIBILITY
+libusb_device * LIBUSB_CALL libusb_ref_device(libusb_device *dev)
+{
+ usbi_mutex_lock(&dev->lock);
+ dev->refcnt++;
+ usbi_mutex_unlock(&dev->lock);
+ return dev;
+}
+
+/** \ingroup libusb_dev
+ * Decrement the reference count of a device. If the decrement operation
+ * causes the reference count to reach zero, the device shall be destroyed.
+ * \param dev the device to unreference
+ */
+void API_EXPORTED libusb_unref_device(libusb_device *dev)
+{
+ int refcnt;
+
+ if (!dev)
+ return;
+
+ usbi_mutex_lock(&dev->lock);
+ refcnt = --dev->refcnt;
+ usbi_mutex_unlock(&dev->lock);
+
+ if (refcnt == 0) {
+ usbi_dbg("destroy device %d.%d", dev->bus_number, dev->device_address);
+
+ libusb_unref_device(dev->parent_dev);
+
+ if (usbi_backend->destroy_device)
+ usbi_backend->destroy_device(dev);
+
+ if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
+ /* backend does not support hotplug */
+ usbi_disconnect_device(dev);
+ }
+
+ usbi_mutex_destroy(&dev->lock);
+ free(dev);
+ }
+}
+
+/*
+ * Signal the event pipe so that the event handling thread will be
+ * interrupted to process an internal event.
+ */
+int usbi_signal_event(struct libusb_context *ctx)
+{
+ unsigned char dummy = 1;
+ ssize_t r;
+
+ /* write some data on event pipe to interrupt event handlers */
+ r = usbi_write(ctx->event_pipe[1], &dummy, sizeof(dummy));
+ if (r != sizeof(dummy)) {
+ usbi_warn(ctx, "internal signalling write failed");
+ return LIBUSB_ERROR_IO;
+ }
+
+ return 0;
+}
+
+/*
+ * Clear the event pipe so that the event handling will no longer be
+ * interrupted.
+ */
+int usbi_clear_event(struct libusb_context *ctx)
+{
+ unsigned char dummy;
+ ssize_t r;
+
+ /* read some data on event pipe to clear it */
+ r = usbi_read(ctx->event_pipe[0], &dummy, sizeof(dummy));
+ if (r != sizeof(dummy)) {
+ usbi_warn(ctx, "internal signalling read failed");
+ return LIBUSB_ERROR_IO;
+ }
+
+ return 0;
+}
+
+/** \ingroup libusb_dev
+ * Open a device and obtain a device handle. A handle allows you to perform
+ * I/O on the device in question.
+ *
+ * Internally, this function adds a reference to the device and makes it
+ * available to you through libusb_get_device(). This reference is removed
+ * during libusb_close().
+ *
+ * This is a non-blocking function; no requests are sent over the bus.
+ *
+ * \param dev the device to open
+ * \param dev_handle output location for the returned device handle pointer. Only
+ * populated when the return code is 0.
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_NO_MEM on memory allocation failure
+ * \returns LIBUSB_ERROR_ACCESS if the user has insufficient permissions
+ * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * \returns another LIBUSB_ERROR code on other failure
+ */
+int API_EXPORTED libusb_open(libusb_device *dev,
+ libusb_device_handle **dev_handle)
+{
+ struct libusb_context *ctx = DEVICE_CTX(dev);
+ struct libusb_device_handle *_dev_handle;
+ size_t priv_size = usbi_backend->device_handle_priv_size;
+ int r;
+ usbi_dbg("open %d.%d", dev->bus_number, dev->device_address);
+
+ if (!dev->attached) {
+ return LIBUSB_ERROR_NO_DEVICE;
+ }
+
+ _dev_handle = malloc(sizeof(*_dev_handle) + priv_size);
+ if (!_dev_handle)
+ return LIBUSB_ERROR_NO_MEM;
+
+ r = usbi_mutex_init(&_dev_handle->lock);
+ if (r) {
+ free(_dev_handle);
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ _dev_handle->dev = libusb_ref_device(dev);
+ _dev_handle->auto_detach_kernel_driver = 0;
+ _dev_handle->claimed_interfaces = 0;
+ memset(&_dev_handle->os_priv, 0, priv_size);
+
+ 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);
+ usbi_mutex_destroy(&_dev_handle->lock);
+ free(_dev_handle);
+ return r;
+ }
+
+ usbi_mutex_lock(&ctx->open_devs_lock);
+ list_add(&_dev_handle->list, &ctx->open_devs);
+ usbi_mutex_unlock(&ctx->open_devs_lock);
+ *dev_handle = _dev_handle;
+
+ return 0;
+}
+
+/** \ingroup libusb_dev
+ * Convenience function for finding a device with a particular
+ * <tt>idVendor</tt>/<tt>idProduct</tt> combination. This function is intended
+ * for those scenarios where you are using libusb to knock up a quick test
+ * application - it allows you to avoid calling libusb_get_device_list() and
+ * worrying about traversing/freeing the list.
+ *
+ * This function has limitations and is hence not intended for use in real
+ * applications: if multiple devices have the same IDs it will only
+ * give you the first one, etc.
+ *
+ * \param ctx the context to operate on, or NULL for the default context
+ * \param vendor_id the idVendor value to search for
+ * \param product_id the idProduct value to search for
+ * \returns a device handle for the first found device, or NULL on error
+ * or if the device could not be found. */
+DEFAULT_VISIBILITY
+libusb_device_handle * LIBUSB_CALL libusb_open_device_with_vid_pid(
+ libusb_context *ctx, uint16_t vendor_id, uint16_t product_id)
+{
+ struct libusb_device **devs;
+ struct libusb_device *found = NULL;
+ struct libusb_device *dev;
+ struct libusb_device_handle *dev_handle = NULL;
+ size_t i = 0;
+ int r;
+
+ if (libusb_get_device_list(ctx, &devs) < 0)
+ return NULL;
+
+ while ((dev = devs[i++]) != NULL) {
+ struct libusb_device_descriptor desc;
+ r = libusb_get_device_descriptor(dev, &desc);
+ if (r < 0)
+ goto out;
+ if (desc.idVendor == vendor_id && desc.idProduct == product_id) {
+ found = dev;
+ break;
+ }
+ }
+
+ if (found) {
+ r = libusb_open(found, &dev_handle);
+ if (r < 0)
+ dev_handle = NULL;
+ }
+
+out:
+ libusb_free_device_list(devs, 1);
+ return dev_handle;
+}
+
+static void do_close(struct libusb_context *ctx,
+ struct libusb_device_handle *dev_handle)
+{
+ struct usbi_transfer *itransfer;
+ struct usbi_transfer *tmp;
+
+ /* remove any transfers in flight that are for this device */
+ usbi_mutex_lock(&ctx->flying_transfers_lock);
+
+ /* safe iteration because transfers may be being deleted */
+ list_for_each_entry_safe(itransfer, tmp, &ctx->flying_transfers, list, struct usbi_transfer) {
+ struct libusb_transfer *transfer =
+ USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+
+ if (transfer->dev_handle != dev_handle)
+ continue;
+
+ usbi_mutex_lock(&itransfer->lock);
+ if (!(itransfer->state_flags & USBI_TRANSFER_DEVICE_DISAPPEARED)) {
+ usbi_err(ctx, "Device handle closed while transfer was still being processed, but the device is still connected as far as we know");
+
+ if (itransfer->state_flags & USBI_TRANSFER_CANCELLING)
+ usbi_warn(ctx, "A cancellation for an in-flight transfer hasn't completed but closing the device handle");
+ else
+ usbi_err(ctx, "A cancellation hasn't even been scheduled on the transfer for which the device is closing");
+ }
+ usbi_mutex_unlock(&itransfer->lock);
+
+ /* remove from the list of in-flight transfers and make sure
+ * we don't accidentally use the device handle in the future
+ * (or that such accesses will be easily caught and identified as a crash)
+ */
+ list_del(&itransfer->list);
+ transfer->dev_handle = NULL;
+
+ /* it is up to the user to free up the actual transfer struct. this is
+ * just making sure that we don't attempt to process the transfer after
+ * the device handle is invalid
+ */
+ usbi_dbg("Removed transfer %p from the in-flight list because device handle %p closed",
+ transfer, dev_handle);
+ }
+ usbi_mutex_unlock(&ctx->flying_transfers_lock);
+
+ usbi_mutex_lock(&ctx->open_devs_lock);
+ list_del(&dev_handle->list);
+ usbi_mutex_unlock(&ctx->open_devs_lock);
+
+ usbi_backend->close(dev_handle);
+ libusb_unref_device(dev_handle->dev);
+ usbi_mutex_destroy(&dev_handle->lock);
+ free(dev_handle);
+}
+
+/** \ingroup libusb_dev
+ * Close a device handle. Should be called on all open handles before your
+ * application exits.
+ *
+ * Internally, this function destroys the reference that was added by
+ * libusb_open() on the given device.
+ *
+ * This is a non-blocking function; no requests are sent over the bus.
+ *
+ * \param dev_handle the device handle to close
+ */
+void API_EXPORTED libusb_close(libusb_device_handle *dev_handle)
+{
+ struct libusb_context *ctx;
+ int handling_events;
+ int pending_events;
+
+ if (!dev_handle)
+ return;
+ usbi_dbg("");
+
+ ctx = HANDLE_CTX(dev_handle);
+ handling_events = usbi_handling_events(ctx);
+
+ /* Similarly to libusb_open(), we want to interrupt all event handlers
+ * at this point. More importantly, we want to perform the actual close of
+ * the device while holding the event handling lock (preventing any other
+ * thread from doing event handling) because we will be removing a file
+ * descriptor from the polling loop. If this is being called by the current
+ * event handler, we can bypass the interruption code because we already
+ * hold the event handling lock. */
+
+ if (!handling_events) {
+ /* Record that we are closing a device.
+ * Only signal an event if there are no prior pending events. */
+ usbi_mutex_lock(&ctx->event_data_lock);
+ pending_events = usbi_pending_events(ctx);
+ ctx->device_close++;
+ if (!pending_events)
+ usbi_signal_event(ctx);
+ usbi_mutex_unlock(&ctx->event_data_lock);
+
+ /* take event handling lock */
+ libusb_lock_events(ctx);
+ }
+
+ /* Close the device */
+ do_close(ctx, dev_handle);
+
+ if (!handling_events) {
+ /* We're done with closing this device.
+ * Clear the event pipe if there are no further pending events. */
+ usbi_mutex_lock(&ctx->event_data_lock);
+ ctx->device_close--;
+ pending_events = usbi_pending_events(ctx);
+ if (!pending_events)
+ usbi_clear_event(ctx);
+ usbi_mutex_unlock(&ctx->event_data_lock);
+
+ /* Release event handling lock and wake up event waiters */
+ libusb_unlock_events(ctx);
+ }
+}
+
+/** \ingroup libusb_dev
+ * Get the underlying device for a device handle. This function does not modify
+ * the reference count of the returned device, so do not feel compelled to
+ * unreference it when you are done.
+ * \param dev_handle a device handle
+ * \returns the underlying device
+ */
+DEFAULT_VISIBILITY
+libusb_device * LIBUSB_CALL libusb_get_device(libusb_device_handle *dev_handle)
+{
+ return dev_handle->dev;
+}
+
+/** \ingroup libusb_dev
+ * Determine the bConfigurationValue of the currently active configuration.
+ *
+ * You could formulate your own control request to obtain this information,
+ * but this function has the advantage that it may be able to retrieve the
+ * information from operating system caches (no I/O involved).
+ *
+ * If the OS does not cache this information, then this function will block
+ * while a control transfer is submitted to retrieve the information.
+ *
+ * This function will return a value of 0 in the <tt>config</tt> output
+ * parameter if the device is in unconfigured state.
+ *
+ * \param dev_handle a device handle
+ * \param config output location for the bConfigurationValue of the active
+ * configuration (only valid for return code 0)
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * \returns another LIBUSB_ERROR code on other failure
+ */
+int API_EXPORTED libusb_get_configuration(libusb_device_handle *dev_handle,
+ int *config)
+{
+ int r = LIBUSB_ERROR_NOT_SUPPORTED;
+
+ usbi_dbg("");
+ if (usbi_backend->get_configuration)
+ r = usbi_backend->get_configuration(dev_handle, config);
+
+ if (r == LIBUSB_ERROR_NOT_SUPPORTED) {
+ uint8_t tmp = 0;
+ usbi_dbg("falling back to control message");
+ r = libusb_control_transfer(dev_handle, LIBUSB_ENDPOINT_IN,
+ LIBUSB_REQUEST_GET_CONFIGURATION, 0, 0, &tmp, 1, 1000);
+ if (r == 0) {
+ usbi_err(HANDLE_CTX(dev_handle), "zero bytes returned in ctrl transfer?");
+ r = LIBUSB_ERROR_IO;
+ } else if (r == 1) {
+ r = 0;
+ *config = tmp;
+ } else {
+ usbi_dbg("control failed, error %d", r);
+ }
+ }
+
+ if (r == 0)
+ usbi_dbg("active config %d", *config);
+
+ return r;
+}
+
+/** \ingroup libusb_dev
+ * Set the active configuration for a device.
+ *
+ * The operating system may or may not have already set an active
+ * configuration on the device. It is up to your application to ensure the
+ * correct configuration is selected before you attempt to claim interfaces
+ * and perform other operations.
+ *
+ * If you call this function on a device already configured with the selected
+ * configuration, then this function will act as a lightweight device reset:
+ * it will issue a SET_CONFIGURATION request using the current configuration,
+ * causing most USB-related device state to be reset (altsetting reset to zero,
+ * endpoint halts cleared, toggles reset).
+ *
+ * You cannot change/reset configuration if your application has claimed
+ * interfaces. It is advised to set the desired configuration before claiming
+ * interfaces.
+ *
+ * Alternatively you can call libusb_release_interface() first. Note if you
+ * do things this way you must ensure that auto_detach_kernel_driver for
+ * <tt>dev</tt> is 0, otherwise the kernel driver will be re-attached when you
+ * release the interface(s).
+ *
+ * You cannot change/reset configuration if other applications or drivers have
+ * claimed interfaces.
+ *
+ * A configuration value of -1 will put the device in unconfigured state.
+ * The USB specifications state that a configuration value of 0 does this,
+ * however buggy devices exist which actually have a configuration 0.
+ *
+ * You should always use this function rather than formulating your own
+ * SET_CONFIGURATION control request. This is because the underlying operating
+ * system needs to know when such changes happen.
+ *
+ * This is a blocking function.
+ *
+ * \param dev_handle a device handle
+ * \param configuration the bConfigurationValue of the configuration you
+ * wish to activate, or -1 if you wish to put the device in an unconfigured
+ * state
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_NOT_FOUND if the requested configuration does not exist
+ * \returns LIBUSB_ERROR_BUSY if interfaces are currently claimed
+ * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * \returns another LIBUSB_ERROR code on other failure
+ * \see libusb_set_auto_detach_kernel_driver()
+ */
+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);
+}
+
+/** \ingroup libusb_dev
+ * Claim an interface on a given device handle. You must claim the interface
+ * you wish to use before you can perform I/O on any of its endpoints.
+ *
+ * It is legal to attempt to claim an already-claimed interface, in which
+ * case libusb just returns 0 without doing anything.
+ *
+ * If auto_detach_kernel_driver is set to 1 for <tt>dev</tt>, the kernel driver
+ * will be detached if necessary, on failure the detach error is returned.
+ *
+ * Claiming of interfaces is a purely logical operation; it does not cause
+ * any requests to be sent over the bus. Interface claiming is used to
+ * instruct the underlying operating system that your application wishes
+ * to take ownership of the interface.
+ *
+ * This is a non-blocking function.
+ *
+ * \param dev_handle a device handle
+ * \param interface_number the <tt>bInterfaceNumber</tt> of the interface you
+ * wish to claim
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_NOT_FOUND if the requested interface does not exist
+ * \returns LIBUSB_ERROR_BUSY if another program or driver has claimed the
+ * interface
+ * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * \returns a LIBUSB_ERROR code on other failure
+ * \see libusb_set_auto_detach_kernel_driver()
+ */
+int API_EXPORTED libusb_claim_interface(libusb_device_handle *dev_handle,
+ int interface_number)
+{
+ int r = 0;
+
+ usbi_dbg("interface %d", interface_number);
+ if (interface_number >= USB_MAXINTERFACES)
+ return LIBUSB_ERROR_INVALID_PARAM;
+
+ if (!dev_handle->dev->attached)
+ return LIBUSB_ERROR_NO_DEVICE;
+
+ usbi_mutex_lock(&dev_handle->lock);
+ if (dev_handle->claimed_interfaces & (1 << interface_number))
+ goto out;
+
+ r = usbi_backend->claim_interface(dev_handle, interface_number);
+ if (r == 0)
+ dev_handle->claimed_interfaces |= 1 << interface_number;
+
+out:
+ usbi_mutex_unlock(&dev_handle->lock);
+ return r;
+}
+
+/** \ingroup libusb_dev
+ * Release an interface previously claimed with libusb_claim_interface(). You
+ * should release all claimed interfaces before closing a device handle.
+ *
+ * This is a blocking function. A SET_INTERFACE control request will be sent
+ * to the device, resetting interface state to the first alternate setting.
+ *
+ * If auto_detach_kernel_driver is set to 1 for <tt>dev</tt>, the kernel
+ * driver will be re-attached after releasing the interface.
+ *
+ * \param dev_handle a device handle
+ * \param interface_number the <tt>bInterfaceNumber</tt> of the
+ * previously-claimed interface
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_NOT_FOUND if the interface was not claimed
+ * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * \returns another LIBUSB_ERROR code on other failure
+ * \see libusb_set_auto_detach_kernel_driver()
+ */
+int API_EXPORTED libusb_release_interface(libusb_device_handle *dev_handle,
+ int interface_number)
+{
+ int r;
+
+ usbi_dbg("interface %d", interface_number);
+ if (interface_number >= USB_MAXINTERFACES)
+ return LIBUSB_ERROR_INVALID_PARAM;
+
+ usbi_mutex_lock(&dev_handle->lock);
+ if (!(dev_handle->claimed_interfaces & (1 << interface_number))) {
+ r = LIBUSB_ERROR_NOT_FOUND;
+ goto out;
+ }
+
+ r = usbi_backend->release_interface(dev_handle, interface_number);
+ if (r == 0)
+ dev_handle->claimed_interfaces &= ~(1 << interface_number);
+
+out:
+ usbi_mutex_unlock(&dev_handle->lock);
+ return r;
+}
+
+/** \ingroup libusb_dev
+ * Activate an alternate setting for an interface. The interface must have
+ * been previously claimed with libusb_claim_interface().
+ *
+ * You should always use this function rather than formulating your own
+ * SET_INTERFACE control request. This is because the underlying operating
+ * system needs to know when such changes happen.
+ *
+ * This is a blocking function.
+ *
+ * \param dev_handle a device handle
+ * \param interface_number the <tt>bInterfaceNumber</tt> of the
+ * previously-claimed interface
+ * \param alternate_setting the <tt>bAlternateSetting</tt> of the alternate
+ * setting to activate
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_NOT_FOUND if the interface was not claimed, or the
+ * requested alternate setting does not exist
+ * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * \returns another LIBUSB_ERROR code on other failure
+ */
+int API_EXPORTED libusb_set_interface_alt_setting(libusb_device_handle *dev_handle,
+ int interface_number, int alternate_setting)
+{
+ usbi_dbg("interface %d altsetting %d",
+ interface_number, alternate_setting);
+ if (interface_number >= USB_MAXINTERFACES)
+ return LIBUSB_ERROR_INVALID_PARAM;
+
+ usbi_mutex_lock(&dev_handle->lock);
+ if (!dev_handle->dev->attached) {
+ usbi_mutex_unlock(&dev_handle->lock);
+ return LIBUSB_ERROR_NO_DEVICE;
+ }
+
+ if (!(dev_handle->claimed_interfaces & (1 << interface_number))) {
+ usbi_mutex_unlock(&dev_handle->lock);
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
+ usbi_mutex_unlock(&dev_handle->lock);
+
+ return usbi_backend->set_interface_altsetting(dev_handle, interface_number,
+ alternate_setting);
+}
+
+/** \ingroup libusb_dev
+ * Clear the halt/stall condition for an endpoint. Endpoints with halt status
+ * are unable to receive or transmit data until the halt condition is stalled.
+ *
+ * You should cancel all pending transfers before attempting to clear the halt
+ * condition.
+ *
+ * This is a blocking function.
+ *
+ * \param dev_handle a device handle
+ * \param endpoint the endpoint to clear halt status
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_NOT_FOUND if the endpoint does not exist
+ * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * \returns another LIBUSB_ERROR code on other failure
+ */
+int API_EXPORTED libusb_clear_halt(libusb_device_handle *dev_handle,
+ unsigned char endpoint)
+{
+ usbi_dbg("endpoint %x", endpoint);
+ if (!dev_handle->dev->attached)
+ return LIBUSB_ERROR_NO_DEVICE;
+
+ return usbi_backend->clear_halt(dev_handle, endpoint);
+}
+
+/** \ingroup libusb_dev
+ * Perform a USB port reset to reinitialize a device. The system will attempt
+ * to restore the previous configuration and alternate settings after the
+ * reset has completed.
+ *
+ * If the reset fails, the descriptors change, or the previous state cannot be
+ * restored, the device will appear to be disconnected and reconnected. This
+ * means that the device handle is no longer valid (you should close it) and
+ * rediscover the device. A return code of LIBUSB_ERROR_NOT_FOUND indicates
+ * when this is the case.
+ *
+ * This is a blocking function which usually incurs a noticeable delay.
+ *
+ * \param dev_handle a handle of the device to reset
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_NOT_FOUND if re-enumeration is required, or if the
+ * device has been disconnected
+ * \returns another LIBUSB_ERROR code on other failure
+ */
+int API_EXPORTED libusb_reset_device(libusb_device_handle *dev_handle)
+{
+ usbi_dbg("");
+ if (!dev_handle->dev->attached)
+ return LIBUSB_ERROR_NO_DEVICE;
+
+ return usbi_backend->reset_device(dev_handle);
+}
+
+/** \ingroup libusb_asyncio
+ * Allocate up to num_streams usb bulk streams on the specified endpoints. This
+ * function takes an array of endpoints rather then a single endpoint because
+ * some protocols require that endpoints are setup with similar stream ids.
+ * All endpoints passed in must belong to the same interface.
+ *
+ * Note this function may return less streams then requested. Also note that the
+ * same number of streams are allocated for each endpoint in the endpoint array.
+ *
+ * Stream id 0 is reserved, and should not be used to communicate with devices.
+ * If libusb_alloc_streams() returns with a value of N, you may use stream ids
+ * 1 to N.
+ *
+ * Since version 1.0.19, \ref LIBUSB_API_VERSION >= 0x01000103
+ *
+ * \param dev_handle a device handle
+ * \param num_streams number of streams to try to allocate
+ * \param endpoints array of endpoints to allocate streams on
+ * \param num_endpoints length of the endpoints array
+ * \returns number of streams allocated, or a LIBUSB_ERROR code on failure
+ */
+int API_EXPORTED libusb_alloc_streams(libusb_device_handle *dev_handle,
+ uint32_t num_streams, unsigned char *endpoints, int num_endpoints)
+{
+ usbi_dbg("streams %u eps %d", (unsigned) num_streams, num_endpoints);
+
+ 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,
+ num_endpoints);
+ else
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+}
+
+/** \ingroup libusb_asyncio
+ * Free usb bulk streams allocated with libusb_alloc_streams().
+ *
+ * Note streams are automatically free-ed when releasing an interface.
+ *
+ * Since version 1.0.19, \ref LIBUSB_API_VERSION >= 0x01000103
+ *
+ * \param dev_handle a device handle
+ * \param endpoints array of endpoints to free streams on
+ * \param num_endpoints length of the endpoints array
+ * \returns LIBUSB_SUCCESS, or a LIBUSB_ERROR code on failure
+ */
+int API_EXPORTED libusb_free_streams(libusb_device_handle *dev_handle,
+ unsigned char *endpoints, int num_endpoints)
+{
+ usbi_dbg("eps %d", num_endpoints);
+
+ if (!dev_handle->dev->attached)
+ return LIBUSB_ERROR_NO_DEVICE;
+
+ if (usbi_backend->free_streams)
+ return usbi_backend->free_streams(dev_handle, endpoints,
+ num_endpoints);
+ else
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+}
+
+/** \ingroup libusb_asyncio
+ * Attempts to allocate a block of persistent DMA memory suitable for transfers
+ * against the given device. If successful, will return a block of memory
+ * that is suitable for use as "buffer" in \ref libusb_transfer against this
+ * device. Using this memory instead of regular memory means that the host
+ * controller can use DMA directly into the buffer to increase performance, and
+ * also that transfers can no longer fail due to kernel memory fragmentation.
+ *
+ * Note that this means you should not modify this memory (or even data on
+ * the same cache lines) when a transfer is in progress, although it is legal
+ * to have several transfers going on within the same memory block.
+ *
+ * Will return NULL on failure. Many systems do not support such zerocopy
+ * and will always return NULL. Memory allocated with this function must be
+ * freed with \ref libusb_dev_mem_free. Specifically, this means that the
+ * flag \ref LIBUSB_TRANSFER_FREE_BUFFER cannot be used to free memory allocated
+ * with this function.
+ *
+ * Since version 1.0.21, \ref LIBUSB_API_VERSION >= 0x01000105
+ *
+ * \param dev_handle a device handle
+ * \param length size of desired data buffer
+ * \returns a pointer to the newly allocated memory, or NULL on failure
+ */
+DEFAULT_VISIBILITY
+unsigned char * LIBUSB_CALL libusb_dev_mem_alloc(libusb_device_handle *dev_handle,
+ size_t length)
+{
+ if (!dev_handle->dev->attached)
+ return NULL;
+
+ if (usbi_backend->dev_mem_alloc)
+ return usbi_backend->dev_mem_alloc(dev_handle, length);
+ else
+ return NULL;
+}
+
+/** \ingroup libusb_asyncio
+ * Free device memory allocated with libusb_dev_mem_alloc().
+ *
+ * \param dev_handle a device handle
+ * \param buffer pointer to the previously allocated memory
+ * \param length size of previously allocated memory
+ * \returns LIBUSB_SUCCESS, or a LIBUSB_ERROR code on failure
+ */
+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);
+ else
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+}
+
+/** \ingroup libusb_dev
+ * Determine if a kernel driver is active on an interface. If a kernel driver
+ * is active, you cannot claim the interface, and libusb will be unable to
+ * perform I/O.
+ *
+ * This functionality is not available on Windows.
+ *
+ * \param dev_handle a device handle
+ * \param interface_number the interface to check
+ * \returns 0 if no kernel driver is active
+ * \returns 1 if a kernel driver is active
+ * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * \returns LIBUSB_ERROR_NOT_SUPPORTED on platforms where the functionality
+ * is not available
+ * \returns another LIBUSB_ERROR code on other failure
+ * \see libusb_detach_kernel_driver()
+ */
+int API_EXPORTED libusb_kernel_driver_active(libusb_device_handle *dev_handle,
+ int interface_number)
+{
+ usbi_dbg("interface %d", interface_number);
+
+ 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);
+ else
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+}
+
+/** \ingroup libusb_dev
+ * Detach a kernel driver from an interface. If successful, you will then be
+ * able to claim the interface and perform I/O.
+ *
+ * This functionality is not available on Darwin or Windows.
+ *
+ * Note that libusb itself also talks to the device through a special kernel
+ * driver, if this driver is already attached to the device, this call will
+ * not detach it and return LIBUSB_ERROR_NOT_FOUND.
+ *
+ * \param dev_handle a device handle
+ * \param interface_number the interface to detach the driver from
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_NOT_FOUND if no kernel driver was active
+ * \returns LIBUSB_ERROR_INVALID_PARAM if the interface does not exist
+ * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * \returns LIBUSB_ERROR_NOT_SUPPORTED on platforms where the functionality
+ * is not available
+ * \returns another LIBUSB_ERROR code on other failure
+ * \see libusb_kernel_driver_active()
+ */
+int API_EXPORTED libusb_detach_kernel_driver(libusb_device_handle *dev_handle,
+ int interface_number)
+{
+ usbi_dbg("interface %d", interface_number);
+
+ 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);
+ else
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+}
+
+/** \ingroup libusb_dev
+ * Re-attach an interface's kernel driver, which was previously detached
+ * using libusb_detach_kernel_driver(). This call is only effective on
+ * Linux and returns LIBUSB_ERROR_NOT_SUPPORTED on all other platforms.
+ *
+ * This functionality is not available on Darwin or Windows.
+ *
+ * \param dev_handle a device handle
+ * \param interface_number the interface to attach the driver from
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_NOT_FOUND if no kernel driver was active
+ * \returns LIBUSB_ERROR_INVALID_PARAM if the interface does not exist
+ * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * \returns LIBUSB_ERROR_NOT_SUPPORTED on platforms where the functionality
+ * is not available
+ * \returns LIBUSB_ERROR_BUSY if the driver cannot be attached because the
+ * interface is claimed by a program or driver
+ * \returns another LIBUSB_ERROR code on other failure
+ * \see libusb_kernel_driver_active()
+ */
+int API_EXPORTED libusb_attach_kernel_driver(libusb_device_handle *dev_handle,
+ int interface_number)
+{
+ usbi_dbg("interface %d", interface_number);
+
+ 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);
+ else
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+}
+
+/** \ingroup libusb_dev
+ * Enable/disable libusb's automatic kernel driver detachment. When this is
+ * enabled libusb will automatically detach the kernel driver on an interface
+ * when claiming the interface, and attach it when releasing the interface.
+ *
+ * Automatic kernel driver detachment is disabled on newly opened device
+ * handles by default.
+ *
+ * On platforms which do not have LIBUSB_CAP_SUPPORTS_DETACH_KERNEL_DRIVER
+ * this function will return LIBUSB_ERROR_NOT_SUPPORTED, and libusb will
+ * continue as if this function was never called.
+ *
+ * \param dev_handle a device handle
+ * \param enable whether to enable or disable auto kernel driver detachment
+ *
+ * \returns LIBUSB_SUCCESS on success
+ * \returns LIBUSB_ERROR_NOT_SUPPORTED on platforms where the functionality
+ * is not available
+ * \see libusb_claim_interface()
+ * \see libusb_release_interface()
+ * \see libusb_set_configuration()
+ */
+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))
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+
+ dev_handle->auto_detach_kernel_driver = enable;
+ return LIBUSB_SUCCESS;
+}
+
+/** \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
+ */
+void API_EXPORTED libusb_set_debug(libusb_context *ctx, int level)
+{
+ USBI_GET_CONTEXT(ctx);
+ if (!ctx->debug_fixed)
+ ctx->debug = level;
+}
+
+/** \ingroup libusb_lib
+ * Initialize libusb. This function must be called before calling any other
+ * libusb function.
+ *
+ * If you do not provide an output location for a context pointer, a default
+ * context will be created. If there was already a default context, it will
+ * be reused (and nothing will be initialized/reinitialized).
+ *
+ * \param context Optional output location for context pointer.
+ * Only valid on return code 0.
+ * \returns 0 on success, or a LIBUSB_ERROR code on failure
+ * \see libusb_contexts
+ */
+int API_EXPORTED libusb_init(libusb_context **context)
+{
+ struct libusb_device *dev, *next;
+ char *dbg = getenv("LIBUSB_DEBUG");
+ struct libusb_context *ctx;
+ static int first_init = 1;
+ int r = 0;
+
+ usbi_mutex_static_lock(&default_context_lock);
+
+ if (!timestamp_origin.tv_sec) {
+ usbi_backend->clock_gettime(USBI_CLOCK_REALTIME, &timestamp_origin);
+ }
+
+ if (!context && usbi_default_context) {
+ usbi_dbg("reusing default context");
+ default_context_refcnt++;
+ usbi_mutex_static_unlock(&default_context_lock);
+ return 0;
+ }
+
+ ctx = calloc(1, sizeof(*ctx));
+ 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)
+ ctx->debug_fixed = 1;
+ }
+
+ /* default context should be initialized before calling usbi_dbg */
+ if (!usbi_default_context) {
+ usbi_default_context = ctx;
+ default_context_refcnt++;
+ usbi_dbg("created default context");
+ }
+
+ usbi_dbg("libusb v%u.%u.%u.%u%s", libusb_version_internal.major, libusb_version_internal.minor,
+ libusb_version_internal.micro, libusb_version_internal.nano, libusb_version_internal.rc);
+
+ usbi_mutex_init(&ctx->usb_devs_lock);
+ usbi_mutex_init(&ctx->open_devs_lock);
+ usbi_mutex_init(&ctx->hotplug_cbs_lock);
+ list_init(&ctx->usb_devs);
+ list_init(&ctx->open_devs);
+ list_init(&ctx->hotplug_cbs);
+
+ usbi_mutex_static_lock(&active_contexts_lock);
+ if (first_init) {
+ first_init = 0;
+ list_init (&active_contexts_list);
+ }
+ list_add (&ctx->list, &active_contexts_list);
+ usbi_mutex_static_unlock(&active_contexts_lock);
+
+ if (usbi_backend->init) {
+ r = usbi_backend->init(ctx);
+ if (r)
+ goto err_free_ctx;
+ }
+
+ r = usbi_io_init(ctx);
+ if (r < 0)
+ goto err_backend_exit;
+
+ usbi_mutex_static_unlock(&default_context_lock);
+
+ if (context)
+ *context = ctx;
+
+ return 0;
+
+err_backend_exit:
+ if (usbi_backend->exit)
+ usbi_backend->exit();
+err_free_ctx:
+ if (ctx == usbi_default_context) {
+ usbi_default_context = NULL;
+ default_context_refcnt--;
+ }
+
+ usbi_mutex_static_lock(&active_contexts_lock);
+ list_del (&ctx->list);
+ usbi_mutex_static_unlock(&active_contexts_lock);
+
+ usbi_mutex_lock(&ctx->usb_devs_lock);
+ list_for_each_entry_safe(dev, next, &ctx->usb_devs, list, struct libusb_device) {
+ list_del(&dev->list);
+ libusb_unref_device(dev);
+ }
+ usbi_mutex_unlock(&ctx->usb_devs_lock);
+
+ usbi_mutex_destroy(&ctx->open_devs_lock);
+ usbi_mutex_destroy(&ctx->usb_devs_lock);
+ usbi_mutex_destroy(&ctx->hotplug_cbs_lock);
+
+ free(ctx);
+err_unlock:
+ usbi_mutex_static_unlock(&default_context_lock);
+ return r;
+}
+
+/** \ingroup libusb_lib
+ * Deinitialize libusb. Should be called after closing all open devices and
+ * before your application terminates.
+ * \param ctx the context to deinitialize, or NULL for the default context
+ */
+void API_EXPORTED libusb_exit(struct libusb_context *ctx)
+{
+ struct libusb_device *dev, *next;
+ struct timeval tv = { 0, 0 };
+
+ usbi_dbg("");
+ USBI_GET_CONTEXT(ctx);
+
+ /* if working with default context, only actually do the deinitialization
+ * if we're the last user */
+ usbi_mutex_static_lock(&default_context_lock);
+ if (ctx == usbi_default_context) {
+ if (--default_context_refcnt > 0) {
+ usbi_dbg("not destroying default context");
+ usbi_mutex_static_unlock(&default_context_lock);
+ return;
+ }
+ usbi_dbg("destroying default context");
+ usbi_default_context = NULL;
+ }
+ usbi_mutex_static_unlock(&default_context_lock);
+
+ usbi_mutex_static_lock(&active_contexts_lock);
+ list_del (&ctx->list);
+ usbi_mutex_static_unlock(&active_contexts_lock);
+
+ if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
+ usbi_hotplug_deregister_all(ctx);
+
+ /*
+ * Ensure any pending unplug events are read from the hotplug
+ * pipe. The usb_device-s hold in the events are no longer part
+ * of usb_devs, but the events still hold a reference!
+ *
+ * Note we don't do this if the application has left devices
+ * open (which implies a buggy app) to avoid packet completion
+ * handlers running when the app does not expect them to run.
+ */
+ if (list_empty(&ctx->open_devs))
+ libusb_handle_events_timeout(ctx, &tv);
+
+ usbi_mutex_lock(&ctx->usb_devs_lock);
+ list_for_each_entry_safe(dev, next, &ctx->usb_devs, list, struct libusb_device) {
+ list_del(&dev->list);
+ libusb_unref_device(dev);
+ }
+ usbi_mutex_unlock(&ctx->usb_devs_lock);
+ }
+
+ /* a few sanity checks. don't bother with locking because unless
+ * there is an application bug, nobody will be accessing these. */
+ if (!list_empty(&ctx->usb_devs))
+ usbi_warn(ctx, "some libusb_devices were leaked");
+ if (!list_empty(&ctx->open_devs))
+ usbi_warn(ctx, "application left some devices open");
+
+ usbi_io_exit(ctx);
+ if (usbi_backend->exit)
+ usbi_backend->exit();
+
+ usbi_mutex_destroy(&ctx->open_devs_lock);
+ usbi_mutex_destroy(&ctx->usb_devs_lock);
+ usbi_mutex_destroy(&ctx->hotplug_cbs_lock);
+ free(ctx);
+}
+
+/** \ingroup libusb_misc
+ * Check at runtime if the loaded library has a given capability.
+ * This call should be performed after \ref libusb_init(), to ensure the
+ * backend has updated its capability set.
+ *
+ * \param capability the \ref libusb_capability to check for
+ * \returns nonzero if the running library has the capability, 0 otherwise
+ */
+int API_EXPORTED libusb_has_capability(uint32_t capability)
+{
+ switch (capability) {
+ case LIBUSB_CAP_HAS_CAPABILITY:
+ return 1;
+ case LIBUSB_CAP_HAS_HOTPLUG:
+ return !(usbi_backend->get_device_list);
+ case LIBUSB_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 0;
+}
+
+/* this is defined in libusbi.h if needed */
+#ifdef LIBUSB_PRINTF_WIN32
+/*
+ * Prior to VS2015, Microsoft did not provide the snprintf() function and
+ * provided a vsnprintf() that did not guarantee NULL-terminated output.
+ * Microsoft did provide a _snprintf() function, but again it did not
+ * guarantee NULL-terminated output.
+ *
+ * The below implementations guarantee NULL-terminated output and are
+ * C99 compliant.
+ */
+
+int usbi_snprintf(char *str, size_t size, const char *format, ...)
+{
+ va_list ap;
+ int ret;
+
+ va_start(ap, format);
+ ret = usbi_vsnprintf(str, size, format, ap);
+ va_end(ap);
+
+ return ret;
+}
+
+int usbi_vsnprintf(char *str, size_t size, const char *format, va_list ap)
+{
+ int ret;
+
+ ret = _vsnprintf(str, size, format, ap);
+ if (ret < 0 || ret == (int)size) {
+ /* Output is truncated, ensure buffer is NULL-terminated and
+ * determine how many characters would have been written. */
+ str[size - 1] = '\0';
+ if (ret < 0)
+ ret = _vsnprintf(NULL, 0, format, ap);
+ }
+
+ return ret;
+}
+#endif
+
+static void usbi_log_str(struct libusb_context *ctx,
+ enum libusb_log_level level, const char * str)
+{
+#if defined(USE_SYSTEM_LOGGING_FACILITY)
+#if defined(OS_WINDOWS)
+ OutputDebugString(str);
+#elif defined(OS_WINCE)
+ /* Windows CE only supports the Unicode version of OutputDebugString. */
+ WCHAR wbuf[USBI_MAX_LOG_LEN];
+ MultiByteToWideChar(CP_UTF8, 0, str, -1, wbuf, sizeof(wbuf));
+ OutputDebugStringW(wbuf);
+#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_ERROR: priority = ANDROID_LOG_ERROR; 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_ERROR: syslog_level = LOG_ERR; break;
+ case LIBUSB_LOG_LEVEL_DEBUG: syslog_level = LOG_DEBUG; break;
+ }
+ syslog(syslog_level, "%s", str);
+#else /* All of gcc, Clang, XCode seem to use #warning */
+#warning System logging is not supported on this platform. Logging to stderr will be used instead.
+ fputs(str, stderr);
+#endif
+#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 = "";
+ char buf[USBI_MAX_LOG_LEN];
+ struct timespec now;
+ int global_debug, header_len, text_len;
+ static int has_debug_header_been_displayed = 0;
+
+#ifdef ENABLE_DEBUG_LOGGING
+ global_debug = 1;
+ UNUSED(ctx);
+#else
+ int ctx_level = 0;
+
+ USBI_GET_CONTEXT(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)
+ return;
+ if (level == LIBUSB_LOG_LEVEL_WARNING && ctx_level < LIBUSB_LOG_LEVEL_WARNING)
+ return;
+ if (level == LIBUSB_LOG_LEVEL_INFO && ctx_level < LIBUSB_LOG_LEVEL_INFO)
+ return;
+ if (level == LIBUSB_LOG_LEVEL_DEBUG && ctx_level < LIBUSB_LOG_LEVEL_DEBUG)
+ return;
+#endif
+
+ 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);
+ }
+ if (now.tv_nsec < timestamp_origin.tv_nsec) {
+ now.tv_sec--;
+ now.tv_nsec += 1000000000L;
+ }
+ now.tv_sec -= timestamp_origin.tv_sec;
+ now.tv_nsec -= timestamp_origin.tv_nsec;
+
+ switch (level) {
+ case LIBUSB_LOG_LEVEL_INFO:
+ prefix = "info";
+ break;
+ case LIBUSB_LOG_LEVEL_WARNING:
+ prefix = "warning";
+ break;
+ case LIBUSB_LOG_LEVEL_ERROR:
+ prefix = "error";
+ break;
+ case LIBUSB_LOG_LEVEL_DEBUG:
+ prefix = "debug";
+ break;
+ case LIBUSB_LOG_LEVEL_NONE:
+ return;
+ default:
+ prefix = "unknown";
+ break;
+ }
+
+ if (global_debug) {
+ header_len = snprintf(buf, sizeof(buf),
+ "[%2d.%06d] [%08x] libusb: %s [%s] ",
+ (int)now.tv_sec, (int)(now.tv_nsec / 1000L), usbi_get_tid(), prefix, function);
+ } else {
+ header_len = snprintf(buf, sizeof(buf),
+ "libusb: %s [%s] ", prefix, function);
+ }
+
+ if (header_len < 0 || header_len >= (int)sizeof(buf)) {
+ /* Somehow snprintf failed to write to the buffer,
+ * remove the header so something useful is output. */
+ header_len = 0;
+ }
+ /* Make sure buffer is NUL terminated */
+ buf[header_len] = '\0';
+ text_len = vsnprintf(buf + header_len, sizeof(buf) - header_len,
+ format, args);
+ if (text_len < 0 || text_len + header_len >= (int)sizeof(buf)) {
+ /* Truncated log output. On some platforms a -1 return value means
+ * that the output was truncated. */
+ text_len = sizeof(buf) - header_len;
+ }
+ if (header_len + text_len + sizeof(USBI_LOG_LINE_END) >= sizeof(buf)) {
+ /* Need to truncate the text slightly to fit on the terminator. */
+ text_len -= (header_len + text_len + sizeof(USBI_LOG_LINE_END)) - sizeof(buf);
+ }
+ strcpy(buf + header_len + text_len, USBI_LOG_LINE_END);
+
+ usbi_log_str(ctx, level, buf);
+}
+
+void usbi_log(struct libusb_context *ctx, enum libusb_log_level level,
+ const char *function, const char *format, ...)
+{
+ va_list args;
+
+ va_start (args, format);
+ usbi_log_v(ctx, level, function, format, args);
+ va_end (args);
+}
+
+/** \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
+ * string.
+ *
+ * \param error_code The \ref libusb_error or libusb_transfer_status code to
+ * return the name of.
+ * \returns The error name, or the string **UNKNOWN** if the value of
+ * error_code is not a known error / status code.
+ */
+DEFAULT_VISIBILITY const char * LIBUSB_CALL libusb_error_name(int error_code)
+{
+ switch (error_code) {
+ case LIBUSB_ERROR_IO:
+ return "LIBUSB_ERROR_IO";
+ case LIBUSB_ERROR_INVALID_PARAM:
+ return "LIBUSB_ERROR_INVALID_PARAM";
+ case LIBUSB_ERROR_ACCESS:
+ return "LIBUSB_ERROR_ACCESS";
+ case LIBUSB_ERROR_NO_DEVICE:
+ return "LIBUSB_ERROR_NO_DEVICE";
+ case LIBUSB_ERROR_NOT_FOUND:
+ return "LIBUSB_ERROR_NOT_FOUND";
+ case LIBUSB_ERROR_BUSY:
+ return "LIBUSB_ERROR_BUSY";
+ case LIBUSB_ERROR_TIMEOUT:
+ return "LIBUSB_ERROR_TIMEOUT";
+ case LIBUSB_ERROR_OVERFLOW:
+ return "LIBUSB_ERROR_OVERFLOW";
+ case LIBUSB_ERROR_PIPE:
+ return "LIBUSB_ERROR_PIPE";
+ case LIBUSB_ERROR_INTERRUPTED:
+ return "LIBUSB_ERROR_INTERRUPTED";
+ case LIBUSB_ERROR_NO_MEM:
+ return "LIBUSB_ERROR_NO_MEM";
+ case LIBUSB_ERROR_NOT_SUPPORTED:
+ return "LIBUSB_ERROR_NOT_SUPPORTED";
+ case LIBUSB_ERROR_OTHER:
+ return "LIBUSB_ERROR_OTHER";
+
+ case LIBUSB_TRANSFER_ERROR:
+ return "LIBUSB_TRANSFER_ERROR";
+ case LIBUSB_TRANSFER_TIMED_OUT:
+ return "LIBUSB_TRANSFER_TIMED_OUT";
+ case LIBUSB_TRANSFER_CANCELLED:
+ return "LIBUSB_TRANSFER_CANCELLED";
+ case LIBUSB_TRANSFER_STALL:
+ return "LIBUSB_TRANSFER_STALL";
+ case LIBUSB_TRANSFER_NO_DEVICE:
+ return "LIBUSB_TRANSFER_NO_DEVICE";
+ case LIBUSB_TRANSFER_OVERFLOW:
+ return "LIBUSB_TRANSFER_OVERFLOW";
+
+ case 0:
+ return "LIBUSB_SUCCESS / LIBUSB_TRANSFER_COMPLETED";
+ default:
+ return "**UNKNOWN**";
+ }
+}
+
+/** \ingroup libusb_misc
+ * Returns a pointer to const struct libusb_version with the version
+ * (major, minor, micro, nano and rc) of the running library.
+ */
+DEFAULT_VISIBILITY
+const struct libusb_version * LIBUSB_CALL libusb_get_version(void)
+{
+ return &libusb_version_internal;
+}
diff --git a/vendor/github.com/karalabe/gousb/internal/libusb/libusb/descriptor.c b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/descriptor.c
new file mode 100644
index 000000000..4c9435fff
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/descriptor.c
@@ -0,0 +1,1191 @@
+/* -*- Mode: C; indent-tabs-mode:t ; c-basic-offset:8 -*- */
+/*
+ * USB descriptor handling functions for libusb
+ * Copyright © 2007 Daniel Drake <dsd@gentoo.org>
+ * Copyright © 2001 Johannes Erdfelt <johannes@erdfelt.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 <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libusbi.h"
+
+#define DESC_HEADER_LENGTH 2
+#define DEVICE_DESC_LENGTH 18
+#define CONFIG_DESC_LENGTH 9
+#define INTERFACE_DESC_LENGTH 9
+#define ENDPOINT_DESC_LENGTH 7
+#define ENDPOINT_AUDIO_DESC_LENGTH 9
+
+/** @defgroup libusb_desc USB descriptors
+ * This page details how to examine the various standard USB descriptors
+ * for detected devices
+ */
+
+/* set host_endian if the w values are already in host endian format,
+ * as opposed to bus endian. */
+int usbi_parse_descriptor(const unsigned char *source, const char *descriptor,
+ void *dest, int host_endian)
+{
+ const unsigned char *sp = source;
+ unsigned char *dp = dest;
+ uint16_t w;
+ const char *cp;
+ uint32_t d;
+
+ for (cp = descriptor; *cp; cp++) {
+ switch (*cp) {
+ case 'b': /* 8-bit byte */
+ *dp++ = *sp++;
+ break;
+ case 'w': /* 16-bit word, convert from little endian to CPU */
+ dp += ((uintptr_t)dp & 1); /* Align to word boundary */
+
+ if (host_endian) {
+ memcpy(dp, sp, 2);
+ } else {
+ w = (sp[1] << 8) | sp[0];
+ *((uint16_t *)dp) = w;
+ }
+ sp += 2;
+ dp += 2;
+ break;
+ case 'd': /* 32-bit word, convert from little endian to CPU */
+ dp += ((uintptr_t)dp & 1); /* Align to word boundary */
+
+ if (host_endian) {
+ memcpy(dp, sp, 4);
+ } else {
+ d = (sp[3] << 24) | (sp[2] << 16) |
+ (sp[1] << 8) | sp[0];
+ *((uint32_t *)dp) = d;
+ }
+ sp += 4;
+ dp += 4;
+ break;
+ case 'u': /* 16 byte UUID */
+ memcpy(dp, sp, 16);
+ sp += 16;
+ dp += 16;
+ break;
+ }
+ }
+
+ return (int) (sp - source);
+}
+
+static void clear_endpoint(struct libusb_endpoint_descriptor *endpoint)
+{
+ free((void *) endpoint->extra);
+}
+
+static int parse_endpoint(struct libusb_context *ctx,
+ struct libusb_endpoint_descriptor *endpoint, unsigned char *buffer,
+ int size, int host_endian)
+{
+ struct usb_descriptor_header header;
+ unsigned char *extra;
+ unsigned char *begin;
+ int parsed = 0;
+ int len;
+
+ if (size < DESC_HEADER_LENGTH) {
+ usbi_err(ctx, "short endpoint descriptor read %d/%d",
+ size, DESC_HEADER_LENGTH);
+ return LIBUSB_ERROR_IO;
+ }
+
+ usbi_parse_descriptor(buffer, "bb", &header, 0);
+ if (header.bDescriptorType != LIBUSB_DT_ENDPOINT) {
+ usbi_err(ctx, "unexpected descriptor %x (expected %x)",
+ header.bDescriptorType, LIBUSB_DT_ENDPOINT);
+ return parsed;
+ }
+ if (header.bLength > size) {
+ usbi_warn(ctx, "short endpoint descriptor read %d/%d",
+ size, header.bLength);
+ return parsed;
+ }
+ if (header.bLength >= ENDPOINT_AUDIO_DESC_LENGTH)
+ usbi_parse_descriptor(buffer, "bbbbwbbb", endpoint, host_endian);
+ else if (header.bLength >= ENDPOINT_DESC_LENGTH)
+ usbi_parse_descriptor(buffer, "bbbbwb", endpoint, host_endian);
+ else {
+ usbi_err(ctx, "invalid endpoint bLength (%d)", header.bLength);
+ return LIBUSB_ERROR_IO;
+ }
+
+ buffer += header.bLength;
+ size -= header.bLength;
+ parsed += header.bLength;
+
+ /* Skip over the rest of the Class Specific or Vendor Specific */
+ /* descriptors */
+ begin = buffer;
+ while (size >= DESC_HEADER_LENGTH) {
+ usbi_parse_descriptor(buffer, "bb", &header, 0);
+ if (header.bLength < DESC_HEADER_LENGTH) {
+ usbi_err(ctx, "invalid extra ep desc len (%d)",
+ header.bLength);
+ return LIBUSB_ERROR_IO;
+ } else if (header.bLength > size) {
+ usbi_warn(ctx, "short extra ep desc read %d/%d",
+ size, header.bLength);
+ return parsed;
+ }
+
+ /* If we find another "proper" descriptor then we're done */
+ if ((header.bDescriptorType == LIBUSB_DT_ENDPOINT) ||
+ (header.bDescriptorType == LIBUSB_DT_INTERFACE) ||
+ (header.bDescriptorType == LIBUSB_DT_CONFIG) ||
+ (header.bDescriptorType == LIBUSB_DT_DEVICE))
+ break;
+
+ usbi_dbg("skipping descriptor %x", header.bDescriptorType);
+ buffer += header.bLength;
+ size -= header.bLength;
+ parsed += header.bLength;
+ }
+
+ /* Copy any unknown descriptors into a storage area for drivers */
+ /* to later parse */
+ len = (int)(buffer - begin);
+ if (!len) {
+ endpoint->extra = NULL;
+ endpoint->extra_length = 0;
+ return parsed;
+ }
+
+ extra = malloc(len);
+ endpoint->extra = extra;
+ if (!extra) {
+ endpoint->extra_length = 0;
+ return LIBUSB_ERROR_NO_MEM;
+ }
+
+ memcpy(extra, begin, len);
+ endpoint->extra_length = len;
+
+ return parsed;
+}
+
+static void clear_interface(struct libusb_interface *usb_interface)
+{
+ int i;
+ int j;
+
+ if (usb_interface->altsetting) {
+ for (i = 0; i < usb_interface->num_altsetting; i++) {
+ struct libusb_interface_descriptor *ifp =
+ (struct libusb_interface_descriptor *)
+ usb_interface->altsetting + i;
+ free((void *) ifp->extra);
+ if (ifp->endpoint) {
+ for (j = 0; j < ifp->bNumEndpoints; j++)
+ clear_endpoint((struct libusb_endpoint_descriptor *)
+ ifp->endpoint + j);
+ }
+ free((void *) ifp->endpoint);
+ }
+ }
+ free((void *) usb_interface->altsetting);
+ usb_interface->altsetting = NULL;
+}
+
+static int parse_interface(libusb_context *ctx,
+ struct libusb_interface *usb_interface, unsigned char *buffer, int size,
+ int host_endian)
+{
+ int i;
+ int len;
+ int r;
+ int parsed = 0;
+ int interface_number = -1;
+ struct usb_descriptor_header header;
+ struct libusb_interface_descriptor *ifp;
+ unsigned char *begin;
+
+ usb_interface->num_altsetting = 0;
+
+ while (size >= INTERFACE_DESC_LENGTH) {
+ struct libusb_interface_descriptor *altsetting =
+ (struct libusb_interface_descriptor *) usb_interface->altsetting;
+ altsetting = usbi_reallocf(altsetting,
+ sizeof(struct libusb_interface_descriptor) *
+ (usb_interface->num_altsetting + 1));
+ if (!altsetting) {
+ r = LIBUSB_ERROR_NO_MEM;
+ goto err;
+ }
+ usb_interface->altsetting = altsetting;
+
+ ifp = altsetting + usb_interface->num_altsetting;
+ usbi_parse_descriptor(buffer, "bbbbbbbbb", ifp, 0);
+ if (ifp->bDescriptorType != LIBUSB_DT_INTERFACE) {
+ usbi_err(ctx, "unexpected descriptor %x (expected %x)",
+ ifp->bDescriptorType, LIBUSB_DT_INTERFACE);
+ return parsed;
+ }
+ if (ifp->bLength < INTERFACE_DESC_LENGTH) {
+ usbi_err(ctx, "invalid interface bLength (%d)",
+ ifp->bLength);
+ r = LIBUSB_ERROR_IO;
+ goto err;
+ }
+ if (ifp->bLength > size) {
+ usbi_warn(ctx, "short intf descriptor read %d/%d",
+ size, ifp->bLength);
+ return parsed;
+ }
+ if (ifp->bNumEndpoints > USB_MAXENDPOINTS) {
+ usbi_err(ctx, "too many endpoints (%d)", ifp->bNumEndpoints);
+ r = LIBUSB_ERROR_IO;
+ goto err;
+ }
+
+ usb_interface->num_altsetting++;
+ ifp->extra = NULL;
+ ifp->extra_length = 0;
+ ifp->endpoint = NULL;
+
+ if (interface_number == -1)
+ interface_number = ifp->bInterfaceNumber;
+
+ /* Skip over the interface */
+ buffer += ifp->bLength;
+ parsed += ifp->bLength;
+ size -= ifp->bLength;
+
+ begin = buffer;
+
+ /* Skip over any interface, class or vendor descriptors */
+ while (size >= DESC_HEADER_LENGTH) {
+ usbi_parse_descriptor(buffer, "bb", &header, 0);
+ if (header.bLength < DESC_HEADER_LENGTH) {
+ usbi_err(ctx,
+ "invalid extra intf desc len (%d)",
+ header.bLength);
+ r = LIBUSB_ERROR_IO;
+ goto err;
+ } else if (header.bLength > size) {
+ usbi_warn(ctx,
+ "short extra intf desc read %d/%d",
+ size, header.bLength);
+ return parsed;
+ }
+
+ /* If we find another "proper" descriptor then we're done */
+ if ((header.bDescriptorType == LIBUSB_DT_INTERFACE) ||
+ (header.bDescriptorType == LIBUSB_DT_ENDPOINT) ||
+ (header.bDescriptorType == LIBUSB_DT_CONFIG) ||
+ (header.bDescriptorType == LIBUSB_DT_DEVICE))
+ break;
+
+ buffer += header.bLength;
+ parsed += header.bLength;
+ size -= header.bLength;
+ }
+
+ /* Copy any unknown descriptors into a storage area for */
+ /* drivers to later parse */
+ len = (int)(buffer - begin);
+ if (len) {
+ ifp->extra = malloc(len);
+ if (!ifp->extra) {
+ r = LIBUSB_ERROR_NO_MEM;
+ goto err;
+ }
+ memcpy((unsigned char *) ifp->extra, begin, len);
+ ifp->extra_length = len;
+ }
+
+ if (ifp->bNumEndpoints > 0) {
+ struct libusb_endpoint_descriptor *endpoint;
+ endpoint = calloc(ifp->bNumEndpoints, sizeof(struct libusb_endpoint_descriptor));
+ ifp->endpoint = endpoint;
+ if (!endpoint) {
+ r = LIBUSB_ERROR_NO_MEM;
+ goto err;
+ }
+
+ for (i = 0; i < ifp->bNumEndpoints; i++) {
+ r = parse_endpoint(ctx, endpoint + i, buffer, size,
+ host_endian);
+ if (r < 0)
+ goto err;
+ if (r == 0) {
+ ifp->bNumEndpoints = (uint8_t)i;
+ break;;
+ }
+
+ buffer += r;
+ parsed += r;
+ size -= r;
+ }
+ }
+
+ /* We check to see if it's an alternate to this one */
+ ifp = (struct libusb_interface_descriptor *) buffer;
+ if (size < LIBUSB_DT_INTERFACE_SIZE ||
+ ifp->bDescriptorType != LIBUSB_DT_INTERFACE ||
+ ifp->bInterfaceNumber != interface_number)
+ return parsed;
+ }
+
+ return parsed;
+err:
+ clear_interface(usb_interface);
+ return r;
+}
+
+static void clear_configuration(struct libusb_config_descriptor *config)
+{
+ int i;
+ if (config->interface) {
+ for (i = 0; i < config->bNumInterfaces; i++)
+ clear_interface((struct libusb_interface *)
+ config->interface + i);
+ }
+ free((void *) config->interface);
+ free((void *) config->extra);
+}
+
+static int parse_configuration(struct libusb_context *ctx,
+ struct libusb_config_descriptor *config, unsigned char *buffer,
+ int size, int host_endian)
+{
+ int i;
+ int r;
+ struct usb_descriptor_header header;
+ struct libusb_interface *usb_interface;
+
+ if (size < LIBUSB_DT_CONFIG_SIZE) {
+ usbi_err(ctx, "short config descriptor read %d/%d",
+ size, LIBUSB_DT_CONFIG_SIZE);
+ return LIBUSB_ERROR_IO;
+ }
+
+ usbi_parse_descriptor(buffer, "bbwbbbbb", config, host_endian);
+ if (config->bDescriptorType != LIBUSB_DT_CONFIG) {
+ usbi_err(ctx, "unexpected descriptor %x (expected %x)",
+ config->bDescriptorType, LIBUSB_DT_CONFIG);
+ return LIBUSB_ERROR_IO;
+ }
+ if (config->bLength < LIBUSB_DT_CONFIG_SIZE) {
+ usbi_err(ctx, "invalid config bLength (%d)", config->bLength);
+ return LIBUSB_ERROR_IO;
+ }
+ if (config->bLength > size) {
+ usbi_err(ctx, "short config descriptor read %d/%d",
+ size, config->bLength);
+ return LIBUSB_ERROR_IO;
+ }
+ if (config->bNumInterfaces > USB_MAXINTERFACES) {
+ usbi_err(ctx, "too many interfaces (%d)", config->bNumInterfaces);
+ return LIBUSB_ERROR_IO;
+ }
+
+ usb_interface = calloc(config->bNumInterfaces, sizeof(struct libusb_interface));
+ config->interface = usb_interface;
+ if (!usb_interface)
+ return LIBUSB_ERROR_NO_MEM;
+
+ buffer += config->bLength;
+ size -= config->bLength;
+
+ config->extra = NULL;
+ config->extra_length = 0;
+
+ for (i = 0; i < config->bNumInterfaces; i++) {
+ int len;
+ unsigned char *begin;
+
+ /* Skip over the rest of the Class Specific or Vendor */
+ /* Specific descriptors */
+ begin = buffer;
+ while (size >= DESC_HEADER_LENGTH) {
+ usbi_parse_descriptor(buffer, "bb", &header, 0);
+
+ if (header.bLength < DESC_HEADER_LENGTH) {
+ usbi_err(ctx,
+ "invalid extra config desc len (%d)",
+ header.bLength);
+ r = LIBUSB_ERROR_IO;
+ goto err;
+ } else if (header.bLength > size) {
+ usbi_warn(ctx,
+ "short extra config desc read %d/%d",
+ size, header.bLength);
+ config->bNumInterfaces = (uint8_t)i;
+ return size;
+ }
+
+ /* If we find another "proper" descriptor then we're done */
+ if ((header.bDescriptorType == LIBUSB_DT_ENDPOINT) ||
+ (header.bDescriptorType == LIBUSB_DT_INTERFACE) ||
+ (header.bDescriptorType == LIBUSB_DT_CONFIG) ||
+ (header.bDescriptorType == LIBUSB_DT_DEVICE))
+ break;
+
+ usbi_dbg("skipping descriptor 0x%x", header.bDescriptorType);
+ buffer += header.bLength;
+ size -= header.bLength;
+ }
+
+ /* Copy any unknown descriptors into a storage area for */
+ /* drivers to later parse */
+ len = (int)(buffer - begin);
+ if (len) {
+ /* FIXME: We should realloc and append here */
+ if (!config->extra_length) {
+ config->extra = malloc(len);
+ if (!config->extra) {
+ r = LIBUSB_ERROR_NO_MEM;
+ goto err;
+ }
+
+ memcpy((unsigned char *) config->extra, begin, len);
+ config->extra_length = len;
+ }
+ }
+
+ r = parse_interface(ctx, usb_interface + i, buffer, size, host_endian);
+ if (r < 0)
+ goto err;
+ if (r == 0) {
+ config->bNumInterfaces = (uint8_t)i;
+ break;
+ }
+
+ buffer += r;
+ size -= r;
+ }
+
+ return size;
+
+err:
+ clear_configuration(config);
+ return r;
+}
+
+static int raw_desc_to_config(struct libusb_context *ctx,
+ unsigned char *buf, int size, int host_endian,
+ struct libusb_config_descriptor **config)
+{
+ struct libusb_config_descriptor *_config = malloc(sizeof(*_config));
+ int r;
+
+ if (!_config)
+ return LIBUSB_ERROR_NO_MEM;
+
+ r = parse_configuration(ctx, _config, buf, size, host_endian);
+ if (r < 0) {
+ usbi_err(ctx, "parse_configuration failed with error %d", r);
+ free(_config);
+ return r;
+ } else if (r > 0) {
+ usbi_warn(ctx, "still %d bytes of descriptor data left", r);
+ }
+
+ *config = _config;
+ return LIBUSB_SUCCESS;
+}
+
+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,
+ &host_endian);
+ if (r < 0)
+ return r;
+
+ if (!host_endian) {
+ dev->device_descriptor.bcdUSB = libusb_le16_to_cpu(dev->device_descriptor.bcdUSB);
+ dev->device_descriptor.idVendor = libusb_le16_to_cpu(dev->device_descriptor.idVendor);
+ dev->device_descriptor.idProduct = libusb_le16_to_cpu(dev->device_descriptor.idProduct);
+ dev->device_descriptor.bcdDevice = libusb_le16_to_cpu(dev->device_descriptor.bcdDevice);
+ }
+
+ return LIBUSB_SUCCESS;
+}
+
+/** \ingroup libusb_desc
+ * Get the USB device descriptor for a given device.
+ *
+ * This is a non-blocking function; the device descriptor is cached in memory.
+ *
+ * Note since libusb-1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102, this
+ * function always succeeds.
+ *
+ * \param dev the device
+ * \param desc output location for the descriptor data
+ * \returns 0 on success or a LIBUSB_ERROR code on failure
+ */
+int API_EXPORTED libusb_get_device_descriptor(libusb_device *dev,
+ struct libusb_device_descriptor *desc)
+{
+ usbi_dbg("");
+ memcpy((unsigned char *) desc, (unsigned char *) &dev->device_descriptor,
+ sizeof (dev->device_descriptor));
+ return 0;
+}
+
+/** \ingroup libusb_desc
+ * Get the USB configuration descriptor for the currently active configuration.
+ * This is a non-blocking function which does not involve any requests being
+ * sent to the device.
+ *
+ * \param dev a device
+ * \param config output location for the USB configuration descriptor. Only
+ * valid if 0 was returned. Must be freed with libusb_free_config_descriptor()
+ * after use.
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_NOT_FOUND if the device is in unconfigured state
+ * \returns another LIBUSB_ERROR code on error
+ * \see libusb_get_config_descriptor
+ */
+int API_EXPORTED libusb_get_active_config_descriptor(libusb_device *dev,
+ struct libusb_config_descriptor **config)
+{
+ struct libusb_config_descriptor _config;
+ unsigned char tmp[LIBUSB_DT_CONFIG_SIZE];
+ unsigned char *buf = NULL;
+ int host_endian = 0;
+ int r;
+
+ r = usbi_backend->get_active_config_descriptor(dev, tmp,
+ LIBUSB_DT_CONFIG_SIZE, &host_endian);
+ if (r < 0)
+ return r;
+ if (r < LIBUSB_DT_CONFIG_SIZE) {
+ usbi_err(dev->ctx, "short config descriptor read %d/%d",
+ r, LIBUSB_DT_CONFIG_SIZE);
+ return LIBUSB_ERROR_IO;
+ }
+
+ usbi_parse_descriptor(tmp, "bbw", &_config, host_endian);
+ buf = malloc(_config.wTotalLength);
+ if (!buf)
+ return LIBUSB_ERROR_NO_MEM;
+
+ 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);
+
+ free(buf);
+ return r;
+}
+
+/** \ingroup libusb_desc
+ * Get a USB configuration descriptor based on its index.
+ * This is a non-blocking function which does not involve any requests being
+ * sent to the device.
+ *
+ * \param dev a device
+ * \param config_index the index of the configuration you wish to retrieve
+ * \param config output location for the USB configuration descriptor. Only
+ * valid if 0 was returned. Must be freed with libusb_free_config_descriptor()
+ * after use.
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_NOT_FOUND if the configuration does not exist
+ * \returns another LIBUSB_ERROR code on error
+ * \see libusb_get_active_config_descriptor()
+ * \see libusb_get_config_descriptor_by_value()
+ */
+int API_EXPORTED libusb_get_config_descriptor(libusb_device *dev,
+ uint8_t config_index, struct libusb_config_descriptor **config)
+{
+ struct libusb_config_descriptor _config;
+ unsigned char tmp[LIBUSB_DT_CONFIG_SIZE];
+ unsigned char *buf = NULL;
+ int host_endian = 0;
+ int r;
+
+ usbi_dbg("index %d", config_index);
+ if (config_index >= dev->num_configurations)
+ return LIBUSB_ERROR_NOT_FOUND;
+
+ r = usbi_backend->get_config_descriptor(dev, config_index, tmp,
+ LIBUSB_DT_CONFIG_SIZE, &host_endian);
+ if (r < 0)
+ return r;
+ if (r < LIBUSB_DT_CONFIG_SIZE) {
+ usbi_err(dev->ctx, "short config descriptor read %d/%d",
+ r, LIBUSB_DT_CONFIG_SIZE);
+ return LIBUSB_ERROR_IO;
+ }
+
+ usbi_parse_descriptor(tmp, "bbw", &_config, host_endian);
+ buf = malloc(_config.wTotalLength);
+ if (!buf)
+ return LIBUSB_ERROR_NO_MEM;
+
+ 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);
+
+ free(buf);
+ return r;
+}
+
+/* iterate through all configurations, returning the index of the configuration
+ * matching a specific bConfigurationValue in the idx output parameter, or -1
+ * if the config was not found.
+ * returns 0 on success or a LIBUSB_ERROR code
+ */
+int usbi_get_config_index_by_value(struct libusb_device *dev,
+ uint8_t bConfigurationValue, int *idx)
+{
+ uint8_t i;
+
+ usbi_dbg("value %d", bConfigurationValue);
+ 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),
+ &host_endian);
+ if (r < 0) {
+ *idx = -1;
+ return r;
+ }
+ if (tmp[5] == bConfigurationValue) {
+ *idx = i;
+ return 0;
+ }
+ }
+
+ *idx = -1;
+ return 0;
+}
+
+/** \ingroup libusb_desc
+ * Get a USB configuration descriptor with a specific bConfigurationValue.
+ * This is a non-blocking function which does not involve any requests being
+ * sent to the device.
+ *
+ * \param dev a device
+ * \param bConfigurationValue the bConfigurationValue of the configuration you
+ * wish to retrieve
+ * \param config output location for the USB configuration descriptor. Only
+ * valid if 0 was returned. Must be freed with libusb_free_config_descriptor()
+ * after use.
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_NOT_FOUND if the configuration does not exist
+ * \returns another LIBUSB_ERROR code on error
+ * \see libusb_get_active_config_descriptor()
+ * \see libusb_get_config_descriptor()
+ */
+int API_EXPORTED libusb_get_config_descriptor_by_value(libusb_device *dev,
+ uint8_t bConfigurationValue, struct libusb_config_descriptor **config)
+{
+ 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,
+ bConfigurationValue, &buf, &host_endian);
+ if (r < 0)
+ return r;
+ return raw_desc_to_config(dev->ctx, buf, r, host_endian, config);
+ }
+
+ r = usbi_get_config_index_by_value(dev, bConfigurationValue, &idx);
+ if (r < 0)
+ return r;
+ else if (idx == -1)
+ return LIBUSB_ERROR_NOT_FOUND;
+ else
+ return libusb_get_config_descriptor(dev, (uint8_t) idx, config);
+}
+
+/** \ingroup libusb_desc
+ * Free a configuration descriptor obtained from
+ * libusb_get_active_config_descriptor() or libusb_get_config_descriptor().
+ * It is safe to call this function with a NULL config parameter, in which
+ * case the function simply returns.
+ *
+ * \param config the configuration descriptor to free
+ */
+void API_EXPORTED libusb_free_config_descriptor(
+ struct libusb_config_descriptor *config)
+{
+ if (!config)
+ return;
+
+ clear_configuration(config);
+ free(config);
+}
+
+/** \ingroup libusb_desc
+ * Get an endpoints superspeed endpoint companion descriptor (if any)
+ *
+ * \param ctx the context to operate on, or NULL for the default context
+ * \param endpoint endpoint descriptor from which to get the superspeed
+ * endpoint companion descriptor
+ * \param ep_comp output location for the superspeed endpoint companion
+ * descriptor. Only valid if 0 was returned. Must be freed with
+ * libusb_free_ss_endpoint_companion_descriptor() after use.
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_NOT_FOUND if the configuration does not exist
+ * \returns another LIBUSB_ERROR code on error
+ */
+int API_EXPORTED libusb_get_ss_endpoint_companion_descriptor(
+ struct libusb_context *ctx,
+ const struct libusb_endpoint_descriptor *endpoint,
+ struct libusb_ss_endpoint_companion_descriptor **ep_comp)
+{
+ struct usb_descriptor_header header;
+ int size = endpoint->extra_length;
+ const unsigned char *buffer = endpoint->extra;
+
+ *ep_comp = NULL;
+
+ while (size >= DESC_HEADER_LENGTH) {
+ usbi_parse_descriptor(buffer, "bb", &header, 0);
+ if (header.bLength < 2 || header.bLength > size) {
+ usbi_err(ctx, "invalid descriptor length %d",
+ header.bLength);
+ return LIBUSB_ERROR_IO;
+ }
+ if (header.bDescriptorType != LIBUSB_DT_SS_ENDPOINT_COMPANION) {
+ buffer += header.bLength;
+ size -= header.bLength;
+ continue;
+ }
+ if (header.bLength < LIBUSB_DT_SS_ENDPOINT_COMPANION_SIZE) {
+ usbi_err(ctx, "invalid ss-ep-comp-desc length %d",
+ header.bLength);
+ return LIBUSB_ERROR_IO;
+ }
+ *ep_comp = malloc(sizeof(**ep_comp));
+ if (*ep_comp == NULL)
+ return LIBUSB_ERROR_NO_MEM;
+ usbi_parse_descriptor(buffer, "bbbbw", *ep_comp, 0);
+ return LIBUSB_SUCCESS;
+ }
+ return LIBUSB_ERROR_NOT_FOUND;
+}
+
+/** \ingroup libusb_desc
+ * Free a superspeed endpoint companion descriptor obtained from
+ * libusb_get_ss_endpoint_companion_descriptor().
+ * It is safe to call this function with a NULL ep_comp parameter, in which
+ * case the function simply returns.
+ *
+ * \param ep_comp the superspeed endpoint companion descriptor to free
+ */
+void API_EXPORTED libusb_free_ss_endpoint_companion_descriptor(
+ struct libusb_ss_endpoint_companion_descriptor *ep_comp)
+{
+ free(ep_comp);
+}
+
+static int parse_bos(struct libusb_context *ctx,
+ struct libusb_bos_descriptor **bos,
+ unsigned char *buffer, int size, int host_endian)
+{
+ struct libusb_bos_descriptor bos_header, *_bos;
+ struct libusb_bos_dev_capability_descriptor dev_cap;
+ int i;
+
+ if (size < LIBUSB_DT_BOS_SIZE) {
+ usbi_err(ctx, "short bos descriptor read %d/%d",
+ size, LIBUSB_DT_BOS_SIZE);
+ return LIBUSB_ERROR_IO;
+ }
+
+ usbi_parse_descriptor(buffer, "bbwb", &bos_header, host_endian);
+ if (bos_header.bDescriptorType != LIBUSB_DT_BOS) {
+ usbi_err(ctx, "unexpected descriptor %x (expected %x)",
+ bos_header.bDescriptorType, LIBUSB_DT_BOS);
+ return LIBUSB_ERROR_IO;
+ }
+ if (bos_header.bLength < LIBUSB_DT_BOS_SIZE) {
+ usbi_err(ctx, "invalid bos bLength (%d)", bos_header.bLength);
+ return LIBUSB_ERROR_IO;
+ }
+ if (bos_header.bLength > size) {
+ usbi_err(ctx, "short bos descriptor read %d/%d",
+ size, bos_header.bLength);
+ return LIBUSB_ERROR_IO;
+ }
+
+ _bos = calloc (1,
+ sizeof(*_bos) + bos_header.bNumDeviceCaps * sizeof(void *));
+ if (!_bos)
+ return LIBUSB_ERROR_NO_MEM;
+
+ usbi_parse_descriptor(buffer, "bbwb", _bos, host_endian);
+ buffer += bos_header.bLength;
+ size -= bos_header.bLength;
+
+ /* Get the device capability descriptors */
+ for (i = 0; i < bos_header.bNumDeviceCaps; i++) {
+ if (size < LIBUSB_DT_DEVICE_CAPABILITY_SIZE) {
+ usbi_warn(ctx, "short dev-cap descriptor read %d/%d",
+ size, LIBUSB_DT_DEVICE_CAPABILITY_SIZE);
+ break;
+ }
+ usbi_parse_descriptor(buffer, "bbb", &dev_cap, host_endian);
+ if (dev_cap.bDescriptorType != LIBUSB_DT_DEVICE_CAPABILITY) {
+ usbi_warn(ctx, "unexpected descriptor %x (expected %x)",
+ dev_cap.bDescriptorType, LIBUSB_DT_DEVICE_CAPABILITY);
+ break;
+ }
+ if (dev_cap.bLength < LIBUSB_DT_DEVICE_CAPABILITY_SIZE) {
+ usbi_err(ctx, "invalid dev-cap bLength (%d)",
+ dev_cap.bLength);
+ libusb_free_bos_descriptor(_bos);
+ return LIBUSB_ERROR_IO;
+ }
+ if (dev_cap.bLength > size) {
+ usbi_warn(ctx, "short dev-cap descriptor read %d/%d",
+ size, dev_cap.bLength);
+ break;
+ }
+
+ _bos->dev_capability[i] = malloc(dev_cap.bLength);
+ if (!_bos->dev_capability[i]) {
+ libusb_free_bos_descriptor(_bos);
+ return LIBUSB_ERROR_NO_MEM;
+ }
+ memcpy(_bos->dev_capability[i], buffer, dev_cap.bLength);
+ buffer += dev_cap.bLength;
+ size -= dev_cap.bLength;
+ }
+ _bos->bNumDeviceCaps = (uint8_t)i;
+ *bos = _bos;
+
+ return LIBUSB_SUCCESS;
+}
+
+/** \ingroup libusb_desc
+ * Get a Binary Object Store (BOS) descriptor
+ * This is a BLOCKING function, which will send requests to the device.
+ *
+ * \param dev_handle the handle of an open libusb device
+ * \param bos output location for the BOS descriptor. Only valid if 0 was returned.
+ * Must be freed with \ref libusb_free_bos_descriptor() after use.
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_NOT_FOUND if the device doesn't have a BOS descriptor
+ * \returns another LIBUSB_ERROR code on error
+ */
+int API_EXPORTED libusb_get_bos_descriptor(libusb_device_handle *dev_handle,
+ struct libusb_bos_descriptor **bos)
+{
+ struct libusb_bos_descriptor _bos;
+ uint8_t bos_header[LIBUSB_DT_BOS_SIZE] = {0};
+ unsigned char *bos_data = NULL;
+ const int host_endian = 0;
+ int r;
+
+ /* Read the BOS. This generates 2 requests on the bus,
+ * one for the header, and one for the full BOS */
+ r = libusb_get_descriptor(dev_handle, LIBUSB_DT_BOS, 0, bos_header,
+ LIBUSB_DT_BOS_SIZE);
+ if (r < 0) {
+ if (r != LIBUSB_ERROR_PIPE)
+ usbi_err(HANDLE_CTX(dev_handle), "failed to read BOS (%d)", r);
+ return r;
+ }
+ if (r < LIBUSB_DT_BOS_SIZE) {
+ usbi_err(HANDLE_CTX(dev_handle), "short BOS read %d/%d",
+ r, LIBUSB_DT_BOS_SIZE);
+ return LIBUSB_ERROR_IO;
+ }
+
+ usbi_parse_descriptor(bos_header, "bbwb", &_bos, host_endian);
+ usbi_dbg("found BOS descriptor: size %d bytes, %d capabilities",
+ _bos.wTotalLength, _bos.bNumDeviceCaps);
+ bos_data = calloc(_bos.wTotalLength, 1);
+ if (bos_data == NULL)
+ return LIBUSB_ERROR_NO_MEM;
+
+ r = libusb_get_descriptor(dev_handle, LIBUSB_DT_BOS, 0, bos_data,
+ _bos.wTotalLength);
+ if (r >= 0)
+ r = parse_bos(HANDLE_CTX(dev_handle), bos, bos_data, r, host_endian);
+ else
+ usbi_err(HANDLE_CTX(dev_handle), "failed to read BOS (%d)", r);
+
+ free(bos_data);
+ return r;
+}
+
+/** \ingroup libusb_desc
+ * Free a BOS descriptor obtained from libusb_get_bos_descriptor().
+ * It is safe to call this function with a NULL bos parameter, in which
+ * case the function simply returns.
+ *
+ * \param bos the BOS descriptor to free
+ */
+void API_EXPORTED libusb_free_bos_descriptor(struct libusb_bos_descriptor *bos)
+{
+ int i;
+
+ if (!bos)
+ return;
+
+ for (i = 0; i < bos->bNumDeviceCaps; i++)
+ free(bos->dev_capability[i]);
+ free(bos);
+}
+
+/** \ingroup libusb_desc
+ * Get an USB 2.0 Extension descriptor
+ *
+ * \param ctx the context to operate on, or NULL for the default context
+ * \param dev_cap Device Capability descriptor with a bDevCapabilityType of
+ * \ref libusb_capability_type::LIBUSB_BT_USB_2_0_EXTENSION
+ * LIBUSB_BT_USB_2_0_EXTENSION
+ * \param usb_2_0_extension output location for the USB 2.0 Extension
+ * descriptor. Only valid if 0 was returned. Must be freed with
+ * libusb_free_usb_2_0_extension_descriptor() after use.
+ * \returns 0 on success
+ * \returns a LIBUSB_ERROR code on error
+ */
+int API_EXPORTED libusb_get_usb_2_0_extension_descriptor(
+ struct libusb_context *ctx,
+ struct libusb_bos_dev_capability_descriptor *dev_cap,
+ struct libusb_usb_2_0_extension_descriptor **usb_2_0_extension)
+{
+ struct libusb_usb_2_0_extension_descriptor *_usb_2_0_extension;
+ const int host_endian = 0;
+
+ if (dev_cap->bDevCapabilityType != LIBUSB_BT_USB_2_0_EXTENSION) {
+ usbi_err(ctx, "unexpected bDevCapabilityType %x (expected %x)",
+ dev_cap->bDevCapabilityType,
+ LIBUSB_BT_USB_2_0_EXTENSION);
+ return LIBUSB_ERROR_INVALID_PARAM;
+ }
+ if (dev_cap->bLength < LIBUSB_BT_USB_2_0_EXTENSION_SIZE) {
+ usbi_err(ctx, "short dev-cap descriptor read %d/%d",
+ dev_cap->bLength, LIBUSB_BT_USB_2_0_EXTENSION_SIZE);
+ return LIBUSB_ERROR_IO;
+ }
+
+ _usb_2_0_extension = malloc(sizeof(*_usb_2_0_extension));
+ if (!_usb_2_0_extension)
+ return LIBUSB_ERROR_NO_MEM;
+
+ usbi_parse_descriptor((unsigned char *)dev_cap, "bbbd",
+ _usb_2_0_extension, host_endian);
+
+ *usb_2_0_extension = _usb_2_0_extension;
+ return LIBUSB_SUCCESS;
+}
+
+/** \ingroup libusb_desc
+ * Free a USB 2.0 Extension descriptor obtained from
+ * libusb_get_usb_2_0_extension_descriptor().
+ * It is safe to call this function with a NULL usb_2_0_extension parameter,
+ * in which case the function simply returns.
+ *
+ * \param usb_2_0_extension the USB 2.0 Extension descriptor to free
+ */
+void API_EXPORTED libusb_free_usb_2_0_extension_descriptor(
+ struct libusb_usb_2_0_extension_descriptor *usb_2_0_extension)
+{
+ free(usb_2_0_extension);
+}
+
+/** \ingroup libusb_desc
+ * Get a SuperSpeed USB Device Capability descriptor
+ *
+ * \param ctx the context to operate on, or NULL for the default context
+ * \param dev_cap Device Capability descriptor with a bDevCapabilityType of
+ * \ref libusb_capability_type::LIBUSB_BT_SS_USB_DEVICE_CAPABILITY
+ * LIBUSB_BT_SS_USB_DEVICE_CAPABILITY
+ * \param ss_usb_device_cap output location for the SuperSpeed USB Device
+ * Capability descriptor. Only valid if 0 was returned. Must be freed with
+ * libusb_free_ss_usb_device_capability_descriptor() after use.
+ * \returns 0 on success
+ * \returns a LIBUSB_ERROR code on error
+ */
+int API_EXPORTED libusb_get_ss_usb_device_capability_descriptor(
+ struct libusb_context *ctx,
+ struct libusb_bos_dev_capability_descriptor *dev_cap,
+ struct libusb_ss_usb_device_capability_descriptor **ss_usb_device_cap)
+{
+ struct libusb_ss_usb_device_capability_descriptor *_ss_usb_device_cap;
+ const int host_endian = 0;
+
+ if (dev_cap->bDevCapabilityType != LIBUSB_BT_SS_USB_DEVICE_CAPABILITY) {
+ usbi_err(ctx, "unexpected bDevCapabilityType %x (expected %x)",
+ dev_cap->bDevCapabilityType,
+ LIBUSB_BT_SS_USB_DEVICE_CAPABILITY);
+ return LIBUSB_ERROR_INVALID_PARAM;
+ }
+ if (dev_cap->bLength < LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE) {
+ usbi_err(ctx, "short dev-cap descriptor read %d/%d",
+ dev_cap->bLength, LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE);
+ return LIBUSB_ERROR_IO;
+ }
+
+ _ss_usb_device_cap = malloc(sizeof(*_ss_usb_device_cap));
+ if (!_ss_usb_device_cap)
+ return LIBUSB_ERROR_NO_MEM;
+
+ usbi_parse_descriptor((unsigned char *)dev_cap, "bbbbwbbw",
+ _ss_usb_device_cap, host_endian);
+
+ *ss_usb_device_cap = _ss_usb_device_cap;
+ return LIBUSB_SUCCESS;
+}
+
+/** \ingroup libusb_desc
+ * Free a SuperSpeed USB Device Capability descriptor obtained from
+ * libusb_get_ss_usb_device_capability_descriptor().
+ * It is safe to call this function with a NULL ss_usb_device_cap
+ * parameter, in which case the function simply returns.
+ *
+ * \param ss_usb_device_cap the USB 2.0 Extension descriptor to free
+ */
+void API_EXPORTED libusb_free_ss_usb_device_capability_descriptor(
+ struct libusb_ss_usb_device_capability_descriptor *ss_usb_device_cap)
+{
+ free(ss_usb_device_cap);
+}
+
+/** \ingroup libusb_desc
+ * Get a Container ID descriptor
+ *
+ * \param ctx the context to operate on, or NULL for the default context
+ * \param dev_cap Device Capability descriptor with a bDevCapabilityType of
+ * \ref libusb_capability_type::LIBUSB_BT_CONTAINER_ID
+ * LIBUSB_BT_CONTAINER_ID
+ * \param container_id output location for the Container ID descriptor.
+ * Only valid if 0 was returned. Must be freed with
+ * libusb_free_container_id_descriptor() after use.
+ * \returns 0 on success
+ * \returns a LIBUSB_ERROR code on error
+ */
+int API_EXPORTED libusb_get_container_id_descriptor(struct libusb_context *ctx,
+ struct libusb_bos_dev_capability_descriptor *dev_cap,
+ struct libusb_container_id_descriptor **container_id)
+{
+ struct libusb_container_id_descriptor *_container_id;
+ const int host_endian = 0;
+
+ if (dev_cap->bDevCapabilityType != LIBUSB_BT_CONTAINER_ID) {
+ usbi_err(ctx, "unexpected bDevCapabilityType %x (expected %x)",
+ dev_cap->bDevCapabilityType,
+ LIBUSB_BT_CONTAINER_ID);
+ return LIBUSB_ERROR_INVALID_PARAM;
+ }
+ if (dev_cap->bLength < LIBUSB_BT_CONTAINER_ID_SIZE) {
+ usbi_err(ctx, "short dev-cap descriptor read %d/%d",
+ dev_cap->bLength, LIBUSB_BT_CONTAINER_ID_SIZE);
+ return LIBUSB_ERROR_IO;
+ }
+
+ _container_id = malloc(sizeof(*_container_id));
+ if (!_container_id)
+ return LIBUSB_ERROR_NO_MEM;
+
+ usbi_parse_descriptor((unsigned char *)dev_cap, "bbbbu",
+ _container_id, host_endian);
+
+ *container_id = _container_id;
+ return LIBUSB_SUCCESS;
+}
+
+/** \ingroup libusb_desc
+ * Free a Container ID descriptor obtained from
+ * libusb_get_container_id_descriptor().
+ * It is safe to call this function with a NULL container_id parameter,
+ * in which case the function simply returns.
+ *
+ * \param container_id the USB 2.0 Extension descriptor to free
+ */
+void API_EXPORTED libusb_free_container_id_descriptor(
+ struct libusb_container_id_descriptor *container_id)
+{
+ free(container_id);
+}
+
+/** \ingroup libusb_desc
+ * Retrieve a string descriptor in C style ASCII.
+ *
+ * Wrapper around libusb_get_string_descriptor(). Uses the first language
+ * supported by the device.
+ *
+ * \param dev_handle a device handle
+ * \param desc_index the index of the descriptor to retrieve
+ * \param data output buffer for ASCII string descriptor
+ * \param length size of data buffer
+ * \returns number of bytes returned in data, or LIBUSB_ERROR code on failure
+ */
+int API_EXPORTED libusb_get_string_descriptor_ascii(libusb_device_handle *dev_handle,
+ uint8_t desc_index, unsigned char *data, int length)
+{
+ unsigned char tbuf[255]; /* Some devices choke on size > 255 */
+ int r, si, di;
+ uint16_t langid;
+
+ /* Asking for the zero'th index is special - it returns a string
+ * descriptor that contains all the language IDs supported by the
+ * device. Typically there aren't many - often only one. Language
+ * IDs are 16 bit numbers, and they start at the third byte in the
+ * descriptor. There's also no point in trying to read descriptor 0
+ * with this function. See USB 2.0 specification section 9.6.7 for
+ * more information.
+ */
+
+ if (desc_index == 0)
+ return LIBUSB_ERROR_INVALID_PARAM;
+
+ r = libusb_get_string_descriptor(dev_handle, 0, 0, tbuf, sizeof(tbuf));
+ if (r < 0)
+ return r;
+
+ if (r < 4)
+ return LIBUSB_ERROR_IO;
+
+ langid = tbuf[2] | (tbuf[3] << 8);
+
+ r = libusb_get_string_descriptor(dev_handle, desc_index, langid, tbuf,
+ sizeof(tbuf));
+ if (r < 0)
+ return r;
+
+ if (tbuf[1] != LIBUSB_DT_STRING)
+ return LIBUSB_ERROR_IO;
+
+ if (tbuf[0] > r)
+ return LIBUSB_ERROR_IO;
+
+ for (di = 0, si = 2; si < tbuf[0]; si += 2) {
+ if (di >= (length - 1))
+ break;
+
+ if ((tbuf[si] & 0x80) || (tbuf[si + 1])) /* non-ASCII */
+ data[di++] = '?';
+ else
+ data[di++] = tbuf[si];
+ }
+
+ data[di] = 0;
+ return di;
+}
diff --git a/vendor/github.com/karalabe/gousb/internal/libusb/libusb/hotplug.c b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/hotplug.c
new file mode 100644
index 000000000..bbfd6e79a
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/hotplug.c
@@ -0,0 +1,350 @@
+/* -*- Mode: C; indent-tabs-mode:t ; c-basic-offset:8 -*- */
+/*
+ * Hotplug functions for libusb
+ * Copyright © 2012-2013 Nathan Hjelm <hjelmn@mac.com>
+ * Copyright © 2012-2013 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
+ */
+
+#include <config.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#include <assert.h>
+
+#include "libusbi.h"
+#include "hotplug.h"
+
+/**
+ * @defgroup libusb_hotplug Device hotplug event notification
+ * This page details how to use the libusb hotplug interface, where available.
+ *
+ * Be mindful that not all platforms currently implement hotplug notification and
+ * that you should first call on \ref libusb_has_capability() with parameter
+ * \ref LIBUSB_CAP_HAS_HOTPLUG to confirm that hotplug support is available.
+ *
+ * \page libusb_hotplug Device hotplug event notification
+ *
+ * \section hotplug_intro Introduction
+ *
+ * Version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102, has added support
+ * for hotplug events on <b>some</b> platforms (you should test if your platform
+ * supports hotplug notification by calling \ref libusb_has_capability() with
+ * parameter \ref LIBUSB_CAP_HAS_HOTPLUG).
+ *
+ * This interface allows you to request notification for the arrival and departure
+ * of matching USB devices.
+ *
+ * To receive hotplug notification you register a callback by calling
+ * \ref libusb_hotplug_register_callback(). This function will optionally return
+ * a callback handle that can be passed to \ref libusb_hotplug_deregister_callback().
+ *
+ * A callback function must return an int (0 or 1) indicating whether the callback is
+ * expecting additional events. Returning 0 will rearm the callback and 1 will cause
+ * the callback to be deregistered. Note that when callbacks are called from
+ * libusb_hotplug_register_callback() because of the \ref LIBUSB_HOTPLUG_ENUMERATE
+ * flag, the callback return value is ignored, iow you cannot cause a callback
+ * to be deregistered by returning 1 when it is called from
+ * libusb_hotplug_register_callback().
+ *
+ * Callbacks for a particular context are automatically deregistered by libusb_exit().
+ *
+ * As of 1.0.16 there are two supported hotplug events:
+ * - LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED: A device has arrived and is ready to use
+ * - LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT: A device has left and is no longer available
+ *
+ * A hotplug event can listen for either or both of these events.
+ *
+ * Note: If you receive notification that a device has left and you have any
+ * a libusb_device_handles for the device it is up to you to call libusb_close()
+ * on each device handle to free up any remaining resources associated with the device.
+ * Once a device has left any libusb_device_handle associated with the device
+ * are invalid and will remain so even if the device comes back.
+ *
+ * When handling a LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED event it is considered
+ * safe to call any libusb function that takes a libusb_device. It also safe to
+ * open a device and submit asynchronous transfers. However, most other functions
+ * that take a libusb_device_handle are <b>not</b> safe to call. Examples of such
+ * functions are any of the \ref libusb_syncio "synchronous API" functions or the blocking
+ * functions that retrieve various \ref libusb_desc "USB descriptors". These functions must
+ * be used outside of the context of the hotplug callback.
+ *
+ * When handling a LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT event the only safe function
+ * is libusb_get_device_descriptor().
+ *
+ * The following code provides an example of the usage of the hotplug interface:
+\code
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <libusb.h>
+
+static int count = 0;
+
+int hotplug_callback(struct libusb_context *ctx, struct libusb_device *dev,
+ libusb_hotplug_event event, void *user_data) {
+ static libusb_device_handle *dev_handle = NULL;
+ struct libusb_device_descriptor desc;
+ int rc;
+
+ (void)libusb_get_device_descriptor(dev, &desc);
+
+ if (LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED == event) {
+ rc = libusb_open(dev, &dev_handle);
+ if (LIBUSB_SUCCESS != rc) {
+ printf("Could not open USB device\n");
+ }
+ } else if (LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT == event) {
+ if (dev_handle) {
+ libusb_close(dev_handle);
+ dev_handle = NULL;
+ }
+ } else {
+ printf("Unhandled event %d\n", event);
+ }
+ count++;
+
+ return 0;
+}
+
+int main (void) {
+ libusb_hotplug_callback_handle callback_handle;
+ int rc;
+
+ libusb_init(NULL);
+
+ rc = libusb_hotplug_register_callback(NULL, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED |
+ LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT, 0, 0x045a, 0x5005,
+ LIBUSB_HOTPLUG_MATCH_ANY, hotplug_callback, NULL,
+ &callback_handle);
+ if (LIBUSB_SUCCESS != rc) {
+ printf("Error creating a hotplug callback\n");
+ libusb_exit(NULL);
+ return EXIT_FAILURE;
+ }
+
+ while (count < 2) {
+ libusb_handle_events_completed(NULL, NULL);
+ nanosleep(&(struct timespec){0, 10000000UL}, NULL);
+ }
+
+ libusb_hotplug_deregister_callback(NULL, callback_handle);
+ libusb_exit(NULL);
+
+ return 0;
+}
+\endcode
+ */
+
+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)) {
+ return 0;
+ }
+
+ if (LIBUSB_HOTPLUG_MATCH_ANY != hotplug_cb->vendor_id &&
+ hotplug_cb->vendor_id != dev->device_descriptor.idVendor) {
+ return 0;
+ }
+
+ if (LIBUSB_HOTPLUG_MATCH_ANY != hotplug_cb->product_id &&
+ hotplug_cb->product_id != dev->device_descriptor.idProduct) {
+ return 0;
+ }
+
+ if (LIBUSB_HOTPLUG_MATCH_ANY != hotplug_cb->dev_class &&
+ hotplug_cb->dev_class != dev->device_descriptor.bDeviceClass) {
+ return 0;
+ }
+
+ return hotplug_cb->cb (ctx, dev, event, hotplug_cb->user_data);
+}
+
+void usbi_hotplug_match(struct libusb_context *ctx, struct libusb_device *dev,
+ libusb_hotplug_event event)
+{
+ struct libusb_hotplug_callback *hotplug_cb, *next;
+ int ret;
+
+ usbi_mutex_lock(&ctx->hotplug_cbs_lock);
+
+ list_for_each_entry_safe(hotplug_cb, next, &ctx->hotplug_cbs, list, struct libusb_hotplug_callback) {
+ usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
+ ret = usbi_hotplug_match_cb (ctx, dev, event, hotplug_cb);
+ usbi_mutex_lock(&ctx->hotplug_cbs_lock);
+
+ if (ret) {
+ list_del(&hotplug_cb->list);
+ free(hotplug_cb);
+ }
+ }
+
+ 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));
+
+ if (!message) {
+ usbi_err(ctx, "error allocating hotplug message");
+ return;
+ }
+
+ message->event = event;
+ message->device = dev;
+
+ /* Take the event data lock and add this message to the list.
+ * Only signal an event if there are no prior pending events. */
+ usbi_mutex_lock(&ctx->event_data_lock);
+ pending_events = usbi_pending_events(ctx);
+ list_add_tail(&message->list, &ctx->hotplug_msgs);
+ if (!pending_events)
+ usbi_signal_event(ctx);
+ usbi_mutex_unlock(&ctx->event_data_lock);
+}
+
+int API_EXPORTED libusb_hotplug_register_callback(libusb_context *ctx,
+ libusb_hotplug_event events, libusb_hotplug_flag flags,
+ int vendor_id, int product_id, int dev_class,
+ 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;
+ }
+
+ /* check for sane values */
+ if ((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;
+ }
+
+ USBI_GET_CONTEXT(ctx);
+
+ new_callback = (libusb_hotplug_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->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++;
+
+ list_add(&new_callback->list, &ctx->hotplug_cbs);
+
+ usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
+
+
+ if (flags & LIBUSB_HOTPLUG_ENUMERATE) {
+ int i, len;
+ struct libusb_device **devs;
+
+ len = (int) libusb_get_device_list(ctx, &devs);
+ if (len < 0) {
+ libusb_hotplug_deregister_callback(ctx,
+ new_callback->handle);
+ return len;
+ }
+
+ for (i = 0; i < len; i++) {
+ usbi_hotplug_match_cb(ctx, devs[i],
+ LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED,
+ new_callback);
+ }
+
+ libusb_free_device_list(devs, 1);
+ }
+
+
+ if (callback_handle)
+ *callback_handle = new_callback->handle;
+
+ return LIBUSB_SUCCESS;
+}
+
+void API_EXPORTED libusb_hotplug_deregister_callback (struct libusb_context *ctx,
+ libusb_hotplug_callback_handle callback_handle)
+{
+ struct libusb_hotplug_callback *hotplug_cb;
+
+ /* check for hotplug support */
+ if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
+ return;
+ }
+
+ USBI_GET_CONTEXT(ctx);
+
+ usbi_mutex_lock(&ctx->hotplug_cbs_lock);
+ 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;
+ }
+ }
+ usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
+
+ usbi_hotplug_notification(ctx, NULL, 0);
+}
+
+void usbi_hotplug_deregister_all(struct libusb_context *ctx) {
+ struct libusb_hotplug_callback *hotplug_cb, *next;
+
+ usbi_mutex_lock(&ctx->hotplug_cbs_lock);
+ list_for_each_entry_safe(hotplug_cb, next, &ctx->hotplug_cbs, list,
+ struct libusb_hotplug_callback) {
+ list_del(&hotplug_cb->list);
+ free(hotplug_cb);
+ }
+
+ usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
+}
diff --git a/vendor/github.com/karalabe/gousb/internal/libusb/libusb/hotplug.h b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/hotplug.h
new file mode 100644
index 000000000..2bec81b06
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/hotplug.h
@@ -0,0 +1,90 @@
+/* -*- Mode: C; indent-tabs-mode:t ; c-basic-offset:8 -*- */
+/*
+ * Hotplug support for libusb
+ * Copyright © 2012-2013 Nathan Hjelm <hjelmn@mac.com>
+ * Copyright © 2012-2013 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
+ */
+
+#if !defined(USBI_HOTPLUG_H)
+#define USBI_HOTPLUG_H
+
+#ifndef LIBUSBI_H
+#include "libusbi.h"
+#endif
+
+/** \ingroup hotplug
+ * The hotplug callback structure. The user populates this structure with
+ * libusb_hotplug_prepare_callback() and then calls libusb_hotplug_register_callback()
+ * to receive notification of hotplug events.
+ */
+struct libusb_hotplug_callback {
+ /** Context this callback is associated with */
+ struct libusb_context *ctx;
+
+ /** Vendor ID to match or LIBUSB_HOTPLUG_MATCH_ANY */
+ int vendor_id;
+
+ /** Product ID to match or LIBUSB_HOTPLUG_MATCH_ANY */
+ int product_id;
+
+ /** 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;
+
+ /** Callback function to invoke for matching event/device */
+ libusb_hotplug_callback_fn cb;
+
+ /** Handle for this callback (used to match on deregister) */
+ libusb_hotplug_callback_handle handle;
+
+ /** 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;
+
+ /** The device for which this hotplug event occurred */
+ struct libusb_device *device;
+
+ /** List this message is contained in (ctx->hotplug_msgs) */
+ struct list_head list;
+};
+
+typedef struct libusb_hotplug_message libusb_hotplug_message;
+
+void usbi_hotplug_deregister_all(struct libusb_context *ctx);
+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,
+ libusb_hotplug_event event);
+
+#endif
diff --git a/vendor/github.com/karalabe/gousb/internal/libusb/libusb/io.c b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/io.c
new file mode 100644
index 000000000..eb1eabf1c
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/io.c
@@ -0,0 +1,2819 @@
+/* -*- Mode: C; indent-tabs-mode:t ; c-basic-offset:8 -*- */
+/*
+ * I/O functions for libusb
+ * Copyright © 2007-2009 Daniel Drake <dsd@gentoo.org>
+ * Copyright © 2001 Johannes Erdfelt <johannes@erdfelt.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 <assert.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#ifdef USBI_TIMERFD_AVAILABLE
+#include <sys/timerfd.h>
+#endif
+
+#include "libusbi.h"
+#include "hotplug.h"
+
+/**
+ * \page libusb_io Synchronous and asynchronous device I/O
+ *
+ * \section io_intro Introduction
+ *
+ * If you're using libusb in your application, you're probably wanting to
+ * perform I/O with devices - you want to perform USB data transfers.
+ *
+ * libusb offers two separate interfaces for device I/O. This page aims to
+ * introduce the two in order to help you decide which one is more suitable
+ * for your application. You can also choose to use both interfaces in your
+ * application by considering each transfer on a case-by-case basis.
+ *
+ * Once you have read through the following discussion, you should consult the
+ * detailed API documentation pages for the details:
+ * - \ref libusb_syncio
+ * - \ref libusb_asyncio
+ *
+ * \section theory Transfers at a logical level
+ *
+ * At a logical level, USB transfers typically happen in two parts. For
+ * example, when reading data from a endpoint:
+ * -# A request for data is sent to the device
+ * -# Some time later, the incoming data is received by the host
+ *
+ * or when writing data to an endpoint:
+ *
+ * -# The data is sent to the device
+ * -# Some time later, the host receives acknowledgement from the device that
+ * the data has been transferred.
+ *
+ * There may be an indefinite delay between the two steps. Consider a
+ * fictional USB input device with a button that the user can press. In order
+ * to determine when the button is pressed, you would likely submit a request
+ * to read data on a bulk or interrupt endpoint and wait for data to arrive.
+ * Data will arrive when the button is pressed by the user, which is
+ * potentially hours later.
+ *
+ * libusb offers both a synchronous and an asynchronous interface to performing
+ * USB transfers. The main difference is that the synchronous interface
+ * combines both steps indicated above into a single function call, whereas
+ * the asynchronous interface separates them.
+ *
+ * \section sync The synchronous interface
+ *
+ * The synchronous I/O interface allows you to perform a USB transfer with
+ * a single function call. When the function call returns, the transfer has
+ * completed and you can parse the results.
+ *
+ * If you have used the libusb-0.1 before, this I/O style will seem familar to
+ * you. libusb-0.1 only offered a synchronous interface.
+ *
+ * In our input device example, to read button presses you might write code
+ * in the following style:
+\code
+unsigned char data[4];
+int actual_length;
+int r = libusb_bulk_transfer(dev_handle, LIBUSB_ENDPOINT_IN, data, sizeof(data), &actual_length, 0);
+if (r == 0 && actual_length == sizeof(data)) {
+ // results of the transaction can now be found in the data buffer
+ // parse them here and report button press
+} else {
+ error();
+}
+\endcode
+ *
+ * The main advantage of this model is simplicity: you did everything with
+ * a single simple function call.
+ *
+ * However, this interface has its limitations. Your application will sleep
+ * inside libusb_bulk_transfer() until the transaction has completed. If it
+ * takes the user 3 hours to press the button, your application will be
+ * sleeping for that long. Execution will be tied up inside the library -
+ * the entire thread will be useless for that duration.
+ *
+ * Another issue is that by tieing up the thread with that single transaction
+ * there is no possibility of performing I/O with multiple endpoints and/or
+ * multiple devices simultaneously, unless you resort to creating one thread
+ * per transaction.
+ *
+ * Additionally, there is no opportunity to cancel the transfer after the
+ * request has been submitted.
+ *
+ * For details on how to use the synchronous API, see the
+ * \ref libusb_syncio "synchronous I/O API documentation" pages.
+ *
+ * \section async The asynchronous interface
+ *
+ * Asynchronous I/O is the most significant new feature in libusb-1.0.
+ * Although it is a more complex interface, it solves all the issues detailed
+ * above.
+ *
+ * Instead of providing which functions that block until the I/O has complete,
+ * libusb's asynchronous interface presents non-blocking functions which
+ * begin a transfer and then return immediately. Your application passes a
+ * callback function pointer to this non-blocking function, which libusb will
+ * call with the results of the transaction when it has completed.
+ *
+ * Transfers which have been submitted through the non-blocking functions
+ * can be cancelled with a separate function call.
+ *
+ * The non-blocking nature of this interface allows you to be simultaneously
+ * performing I/O to multiple endpoints on multiple devices, without having
+ * to use threads.
+ *
+ * This added flexibility does come with some complications though:
+ * - In the interest of being a lightweight library, libusb does not create
+ * threads and can only operate when your application is calling into it. Your
+ * application must call into libusb from it's main loop when events are ready
+ * to be handled, or you must use some other scheme to allow libusb to
+ * undertake whatever work needs to be done.
+ * - libusb also needs to be called into at certain fixed points in time in
+ * order to accurately handle transfer timeouts.
+ * - Memory handling becomes more complex. You cannot use stack memory unless
+ * the function with that stack is guaranteed not to return until the transfer
+ * callback has finished executing.
+ * - You generally lose some linearity from your code flow because submitting
+ * the transfer request is done in a separate function from where the transfer
+ * results are handled. This becomes particularly obvious when you want to
+ * submit a second transfer based on the results of an earlier transfer.
+ *
+ * Internally, libusb's synchronous interface is expressed in terms of function
+ * calls to the asynchronous interface.
+ *
+ * For details on how to use the asynchronous API, see the
+ * \ref libusb_asyncio "asynchronous I/O API" documentation pages.
+ */
+
+
+/**
+ * \page libusb_packetoverflow Packets and overflows
+ *
+ * \section packets Packet abstraction
+ *
+ * The USB specifications describe how data is transmitted in packets, with
+ * constraints on packet size defined by endpoint descriptors. The host must
+ * not send data payloads larger than the endpoint's maximum packet size.
+ *
+ * libusb and the underlying OS abstract out the packet concept, allowing you
+ * to request transfers of any size. Internally, the request will be divided
+ * up into correctly-sized packets. You do not have to be concerned with
+ * packet sizes, but there is one exception when considering overflows.
+ *
+ * \section overflow Bulk/interrupt transfer overflows
+ *
+ * When requesting data on a bulk endpoint, libusb requires you to supply a
+ * buffer and the maximum number of bytes of data that libusb can put in that
+ * buffer. However, the size of the buffer is not communicated to the device -
+ * the device is just asked to send any amount of data.
+ *
+ * There is no problem if the device sends an amount of data that is less than
+ * or equal to the buffer size. libusb reports this condition to you through
+ * the \ref libusb_transfer::actual_length "libusb_transfer.actual_length"
+ * field.
+ *
+ * Problems may occur if the device attempts to send more data than can fit in
+ * the buffer. libusb reports LIBUSB_TRANSFER_OVERFLOW for this condition but
+ * other behaviour is largely undefined: actual_length may or may not be
+ * accurate, the chunk of data that can fit in the buffer (before overflow)
+ * may or may not have been transferred.
+ *
+ * Overflows are nasty, but can be avoided. Even though you were told to
+ * ignore packets above, think about the lower level details: each transfer is
+ * split into packets (typically small, with a maximum size of 512 bytes).
+ * Overflows can only happen if the final packet in an incoming data transfer
+ * is smaller than the actual packet that the device wants to transfer.
+ * Therefore, you will never see an overflow if your transfer buffer size is a
+ * multiple of the endpoint's packet size: the final packet will either
+ * fill up completely or will be only partially filled.
+ */
+
+/**
+ * @defgroup libusb_asyncio Asynchronous device I/O
+ *
+ * This page details libusb's asynchronous (non-blocking) API for USB device
+ * I/O. This interface is very powerful but is also quite complex - you will
+ * need to read this page carefully to understand the necessary considerations
+ * and issues surrounding use of this interface. Simplistic applications
+ * may wish to consider the \ref libusb_syncio "synchronous I/O API" instead.
+ *
+ * The asynchronous interface is built around the idea of separating transfer
+ * submission and handling of transfer completion (the synchronous model
+ * combines both of these into one). There may be a long delay between
+ * submission and completion, however the asynchronous submission function
+ * is non-blocking so will return control to your application during that
+ * potentially long delay.
+ *
+ * \section asyncabstraction Transfer abstraction
+ *
+ * For the asynchronous I/O, libusb implements the concept of a generic
+ * transfer entity for all types of I/O (control, bulk, interrupt,
+ * isochronous). The generic transfer object must be treated slightly
+ * differently depending on which type of I/O you are performing with it.
+ *
+ * This is represented by the public libusb_transfer structure type.
+ *
+ * \section asynctrf Asynchronous transfers
+ *
+ * We can view asynchronous I/O as a 5 step process:
+ * -# <b>Allocation</b>: allocate a libusb_transfer
+ * -# <b>Filling</b>: populate the libusb_transfer instance with information
+ * about the transfer you wish to perform
+ * -# <b>Submission</b>: ask libusb to submit the transfer
+ * -# <b>Completion handling</b>: examine transfer results in the
+ * libusb_transfer structure
+ * -# <b>Deallocation</b>: clean up resources
+ *
+ *
+ * \subsection asyncalloc Allocation
+ *
+ * This step involves allocating memory for a USB transfer. This is the
+ * generic transfer object mentioned above. At this stage, the transfer
+ * is "blank" with no details about what type of I/O it will be used for.
+ *
+ * Allocation is done with the libusb_alloc_transfer() function. You must use
+ * this function rather than allocating your own transfers.
+ *
+ * \subsection asyncfill Filling
+ *
+ * This step is where you take a previously allocated transfer and fill it
+ * with information to determine the message type and direction, data buffer,
+ * callback function, etc.
+ *
+ * You can either fill the required fields yourself or you can use the
+ * helper functions: libusb_fill_control_transfer(), libusb_fill_bulk_transfer()
+ * and libusb_fill_interrupt_transfer().
+ *
+ * \subsection asyncsubmit Submission
+ *
+ * When you have allocated a transfer and filled it, you can submit it using
+ * libusb_submit_transfer(). This function returns immediately but can be
+ * regarded as firing off the I/O request in the background.
+ *
+ * \subsection asynccomplete Completion handling
+ *
+ * After a transfer has been submitted, one of four things can happen to it:
+ *
+ * - The transfer completes (i.e. some data was transferred)
+ * - The transfer has a timeout and the timeout expires before all data is
+ * transferred
+ * - The transfer fails due to an error
+ * - The transfer is cancelled
+ *
+ * Each of these will cause the user-specified transfer callback function to
+ * be invoked. It is up to the callback function to determine which of the
+ * above actually happened and to act accordingly.
+ *
+ * The user-specified callback is passed a pointer to the libusb_transfer
+ * structure which was used to setup and submit the transfer. At completion
+ * time, libusb has populated this structure with results of the transfer:
+ * success or failure reason, number of bytes of data transferred, etc. See
+ * the libusb_transfer structure documentation for more information.
+ *
+ * <b>Important Note</b>: The user-specified callback is called from an event
+ * handling context. It is therefore important that no calls are made into
+ * libusb that will attempt to perform any event handling. Examples of such
+ * functions are any listed in the \ref libusb_syncio "synchronous API" and any of
+ * the blocking functions that retrieve \ref libusb_desc "USB descriptors".
+ *
+ * \subsection Deallocation
+ *
+ * When a transfer has completed (i.e. the callback function has been invoked),
+ * you are advised to free the transfer (unless you wish to resubmit it, see
+ * below). Transfers are deallocated with libusb_free_transfer().
+ *
+ * It is undefined behaviour to free a transfer which has not completed.
+ *
+ * \section asyncresubmit Resubmission
+ *
+ * You may be wondering why allocation, filling, and submission are all
+ * separated above where they could reasonably be combined into a single
+ * operation.
+ *
+ * The reason for separation is to allow you to resubmit transfers without
+ * having to allocate new ones every time. This is especially useful for
+ * common situations dealing with interrupt endpoints - you allocate one
+ * transfer, fill and submit it, and when it returns with results you just
+ * resubmit it for the next interrupt.
+ *
+ * \section asynccancel Cancellation
+ *
+ * Another advantage of using the asynchronous interface is that you have
+ * the ability to cancel transfers which have not yet completed. This is
+ * done by calling the libusb_cancel_transfer() function.
+ *
+ * libusb_cancel_transfer() is asynchronous/non-blocking in itself. When the
+ * cancellation actually completes, the transfer's callback function will
+ * be invoked, and the callback function should check the transfer status to
+ * determine that it was cancelled.
+ *
+ * Freeing the transfer after it has been cancelled but before cancellation
+ * has completed will result in undefined behaviour.
+ *
+ * When a transfer is cancelled, some of the data may have been transferred.
+ * libusb will communicate this to you in the transfer callback. Do not assume
+ * that no data was transferred.
+ *
+ * \section bulk_overflows Overflows on device-to-host bulk/interrupt endpoints
+ *
+ * If your device does not have predictable transfer sizes (or it misbehaves),
+ * your application may submit a request for data on an IN endpoint which is
+ * smaller than the data that the device wishes to send. In some circumstances
+ * this will cause an overflow, which is a nasty condition to deal with. See
+ * the \ref libusb_packetoverflow page for discussion.
+ *
+ * \section asyncctrl Considerations for control transfers
+ *
+ * The <tt>libusb_transfer</tt> structure is generic and hence does not
+ * include specific fields for the control-specific setup packet structure.
+ *
+ * In order to perform a control transfer, you must place the 8-byte setup
+ * packet at the start of the data buffer. To simplify this, you could
+ * cast the buffer pointer to type struct libusb_control_setup, or you can
+ * use the helper function libusb_fill_control_setup().
+ *
+ * The wLength field placed in the setup packet must be the length you would
+ * expect to be sent in the setup packet: the length of the payload that
+ * follows (or the expected maximum number of bytes to receive). However,
+ * the length field of the libusb_transfer object must be the length of
+ * the data buffer - i.e. it should be wLength <em>plus</em> the size of
+ * the setup packet (LIBUSB_CONTROL_SETUP_SIZE).
+ *
+ * If you use the helper functions, this is simplified for you:
+ * -# Allocate a buffer of size LIBUSB_CONTROL_SETUP_SIZE plus the size of the
+ * data you are sending/requesting.
+ * -# Call libusb_fill_control_setup() on the data buffer, using the transfer
+ * request size as the wLength value (i.e. do not include the extra space you
+ * allocated for the control setup).
+ * -# If this is a host-to-device transfer, place the data to be transferred
+ * in the data buffer, starting at offset LIBUSB_CONTROL_SETUP_SIZE.
+ * -# Call libusb_fill_control_transfer() to associate the data buffer with
+ * the transfer (and to set the remaining details such as callback and timeout).
+ * - Note that there is no parameter to set the length field of the transfer.
+ * The length is automatically inferred from the wLength field of the setup
+ * packet.
+ * -# Submit the transfer.
+ *
+ * The multi-byte control setup fields (wValue, wIndex and wLength) must
+ * be given in little-endian byte order (the endianness of the USB bus).
+ * Endianness conversion is transparently handled by
+ * libusb_fill_control_setup() which is documented to accept host-endian
+ * values.
+ *
+ * Further considerations are needed when handling transfer completion in
+ * your callback function:
+ * - As you might expect, the setup packet will still be sitting at the start
+ * of the data buffer.
+ * - If this was a device-to-host transfer, the received data will be sitting
+ * at offset LIBUSB_CONTROL_SETUP_SIZE into the buffer.
+ * - The actual_length field of the transfer structure is relative to the
+ * wLength of the setup packet, rather than the size of the data buffer. So,
+ * if your wLength was 4, your transfer's <tt>length</tt> was 12, then you
+ * should expect an <tt>actual_length</tt> of 4 to indicate that the data was
+ * transferred in entirity.
+ *
+ * To simplify parsing of setup packets and obtaining the data from the
+ * correct offset, you may wish to use the libusb_control_transfer_get_data()
+ * and libusb_control_transfer_get_setup() functions within your transfer
+ * callback.
+ *
+ * Even though control endpoints do not halt, a completed control transfer
+ * may have a LIBUSB_TRANSFER_STALL status code. This indicates the control
+ * request was not supported.
+ *
+ * \section asyncintr Considerations for interrupt transfers
+ *
+ * All interrupt transfers are performed using the polling interval presented
+ * by the bInterval value of the endpoint descriptor.
+ *
+ * \section asynciso Considerations for isochronous transfers
+ *
+ * Isochronous transfers are more complicated than transfers to
+ * non-isochronous endpoints.
+ *
+ * To perform I/O to an isochronous endpoint, allocate the transfer by calling
+ * libusb_alloc_transfer() with an appropriate number of isochronous packets.
+ *
+ * During filling, set \ref libusb_transfer::type "type" to
+ * \ref libusb_transfer_type::LIBUSB_TRANSFER_TYPE_ISOCHRONOUS
+ * "LIBUSB_TRANSFER_TYPE_ISOCHRONOUS", and set
+ * \ref libusb_transfer::num_iso_packets "num_iso_packets" to a value less than
+ * or equal to the number of packets you requested during allocation.
+ * libusb_alloc_transfer() does not set either of these fields for you, given
+ * that you might not even use the transfer on an isochronous endpoint.
+ *
+ * Next, populate the length field for the first num_iso_packets entries in
+ * the \ref libusb_transfer::iso_packet_desc "iso_packet_desc" array. Section
+ * 5.6.3 of the USB2 specifications describe how the maximum isochronous
+ * packet length is determined by the wMaxPacketSize field in the endpoint
+ * descriptor.
+ * Two functions can help you here:
+ *
+ * - libusb_get_max_iso_packet_size() is an easy way to determine the max
+ * packet size for an isochronous endpoint. Note that the maximum packet
+ * size is actually the maximum number of bytes that can be transmitted in
+ * a single microframe, therefore this function multiplies the maximum number
+ * of bytes per transaction by the number of transaction opportunities per
+ * microframe.
+ * - libusb_set_iso_packet_lengths() assigns the same length to all packets
+ * within a transfer, which is usually what you want.
+ *
+ * For outgoing transfers, you'll obviously fill the buffer and populate the
+ * packet descriptors in hope that all the data gets transferred. For incoming
+ * transfers, you must ensure the buffer has sufficient capacity for
+ * the situation where all packets transfer the full amount of requested data.
+ *
+ * Completion handling requires some extra consideration. The
+ * \ref libusb_transfer::actual_length "actual_length" field of the transfer
+ * is meaningless and should not be examined; instead you must refer to the
+ * \ref libusb_iso_packet_descriptor::actual_length "actual_length" field of
+ * each individual packet.
+ *
+ * The \ref libusb_transfer::status "status" field of the transfer is also a
+ * little misleading:
+ * - If the packets were submitted and the isochronous data microframes
+ * completed normally, status will have value
+ * \ref libusb_transfer_status::LIBUSB_TRANSFER_COMPLETED
+ * "LIBUSB_TRANSFER_COMPLETED". Note that bus errors and software-incurred
+ * delays are not counted as transfer errors; the transfer.status field may
+ * indicate COMPLETED even if some or all of the packets failed. Refer to
+ * the \ref libusb_iso_packet_descriptor::status "status" field of each
+ * individual packet to determine packet failures.
+ * - The status field will have value
+ * \ref libusb_transfer_status::LIBUSB_TRANSFER_ERROR
+ * "LIBUSB_TRANSFER_ERROR" only when serious errors were encountered.
+ * - Other transfer status codes occur with normal behaviour.
+ *
+ * The data for each packet will be found at an offset into the buffer that
+ * can be calculated as if each prior packet completed in full. The
+ * libusb_get_iso_packet_buffer() and libusb_get_iso_packet_buffer_simple()
+ * functions may help you here.
+ *
+ * <b>Note</b>: Some operating systems (e.g. Linux) may impose limits on the
+ * length of individual isochronous packets and/or the total length of the
+ * isochronous transfer. Such limits can be difficult for libusb to detect,
+ * so the library will simply try and submit the transfer as set up by you.
+ * If the transfer fails to submit because it is too large,
+ * libusb_submit_transfer() will return
+ * \ref libusb_error::LIBUSB_ERROR_INVALID_PARAM "LIBUSB_ERROR_INVALID_PARAM".
+ *
+ * \section asyncmem Memory caveats
+ *
+ * In most circumstances, it is not safe to use stack memory for transfer
+ * buffers. This is because the function that fired off the asynchronous
+ * transfer may return before libusb has finished using the buffer, and when
+ * the function returns it's stack gets destroyed. This is true for both
+ * host-to-device and device-to-host transfers.
+ *
+ * The only case in which it is safe to use stack memory is where you can
+ * guarantee that the function owning the stack space for the buffer does not
+ * return until after the transfer's callback function has completed. In every
+ * other case, you need to use heap memory instead.
+ *
+ * \section asyncflags Fine control
+ *
+ * Through using this asynchronous interface, you may find yourself repeating
+ * a few simple operations many times. You can apply a bitwise OR of certain
+ * flags to a transfer to simplify certain things:
+ * - \ref libusb_transfer_flags::LIBUSB_TRANSFER_SHORT_NOT_OK
+ * "LIBUSB_TRANSFER_SHORT_NOT_OK" results in transfers which transferred
+ * less than the requested amount of data being marked with status
+ * \ref libusb_transfer_status::LIBUSB_TRANSFER_ERROR "LIBUSB_TRANSFER_ERROR"
+ * (they would normally be regarded as COMPLETED)
+ * - \ref libusb_transfer_flags::LIBUSB_TRANSFER_FREE_BUFFER
+ * "LIBUSB_TRANSFER_FREE_BUFFER" allows you to ask libusb to free the transfer
+ * buffer when freeing the transfer.
+ * - \ref libusb_transfer_flags::LIBUSB_TRANSFER_FREE_TRANSFER
+ * "LIBUSB_TRANSFER_FREE_TRANSFER" causes libusb to automatically free the
+ * transfer after the transfer callback returns.
+ *
+ * \section asyncevent Event handling
+ *
+ * An asynchronous model requires that libusb perform work at various
+ * points in time - namely processing the results of previously-submitted
+ * transfers and invoking the user-supplied callback function.
+ *
+ * This gives rise to the libusb_handle_events() function which your
+ * application must call into when libusb has work do to. This gives libusb
+ * the opportunity to reap pending transfers, invoke callbacks, etc.
+ *
+ * There are 2 different approaches to dealing with libusb_handle_events:
+ *
+ * -# Repeatedly call libusb_handle_events() in blocking mode from a dedicated
+ * thread.
+ * -# Integrate libusb with your application's main event loop. libusb
+ * exposes a set of file descriptors which allow you to do this.
+ *
+ * The first approach has the big advantage that it will also work on Windows
+ * were libusb' poll API for select / poll integration is not available. So
+ * if you want to support Windows and use the async API, you must use this
+ * approach, see the \ref eventthread "Using an event handling thread" section
+ * below for details.
+ *
+ * If you prefer a single threaded approach with a single central event loop,
+ * see the \ref libusb_poll "polling and timing" section for how to integrate libusb
+ * into your application's main event loop.
+ *
+ * \section eventthread Using an event handling thread
+ *
+ * Lets begin with stating the obvious: If you're going to use a separate
+ * thread for libusb event handling, your callback functions MUST be
+ * threadsafe.
+ *
+ * Other then that doing event handling from a separate thread, is mostly
+ * simple. You can use an event thread function as follows:
+\code
+void *event_thread_func(void *ctx)
+{
+ while (event_thread_run)
+ libusb_handle_events(ctx);
+
+ return NULL;
+}
+\endcode
+ *
+ * There is one caveat though, stopping this thread requires setting the
+ * event_thread_run variable to 0, and after that libusb_handle_events() needs
+ * to return control to event_thread_func. But unless some event happens,
+ * libusb_handle_events() will not return.
+ *
+ * There are 2 different ways of dealing with this, depending on if your
+ * application uses libusb' \ref libusb_hotplug "hotplug" support or not.
+ *
+ * Applications which do not use hotplug support, should not start the event
+ * thread until after their first call to libusb_open(), and should stop the
+ * thread when closing the last open device as follows:
+\code
+void my_close_handle(libusb_device_handle *dev_handle)
+{
+ if (open_devs == 1)
+ event_thread_run = 0;
+
+ libusb_close(dev_handle); // This wakes up libusb_handle_events()
+
+ if (open_devs == 1)
+ pthread_join(event_thread);
+
+ open_devs--;
+}
+\endcode
+ *
+ * Applications using hotplug support should start the thread at program init,
+ * after having successfully called libusb_hotplug_register_callback(), and
+ * should stop the thread at program exit as follows:
+\code
+void my_libusb_exit(void)
+{
+ event_thread_run = 0;
+ libusb_hotplug_deregister_callback(ctx, hotplug_cb_handle); // This wakes up libusb_handle_events()
+ pthread_join(event_thread);
+ libusb_exit(ctx);
+}
+\endcode
+ */
+
+/**
+ * @defgroup libusb_poll Polling and timing
+ *
+ * This page documents libusb's functions for polling events and timing.
+ * These functions are only necessary for users of the
+ * \ref libusb_asyncio "asynchronous API". If you are only using the simpler
+ * \ref libusb_syncio "synchronous API" then you do not need to ever call these
+ * functions.
+ *
+ * The justification for the functionality described here has already been
+ * discussed in the \ref asyncevent "event handling" section of the
+ * asynchronous API documentation. In summary, libusb does not create internal
+ * threads for event processing and hence relies on your application calling
+ * into libusb at certain points in time so that pending events can be handled.
+ *
+ * Your main loop is probably already calling poll() or select() or a
+ * variant on a set of file descriptors for other event sources (e.g. keyboard
+ * button presses, mouse movements, network sockets, etc). You then add
+ * libusb's file descriptors to your poll()/select() calls, and when activity
+ * is detected on such descriptors you know it is time to call
+ * libusb_handle_events().
+ *
+ * There is one final event handling complication. libusb supports
+ * asynchronous transfers which time out after a specified time period.
+ *
+ * On some platforms a timerfd is used, so the timeout handling is just another
+ * fd, on other platforms this requires that libusb is called into at or after
+ * the timeout to handle it. So, in addition to considering libusb's file
+ * descriptors in your main event loop, you must also consider that libusb
+ * sometimes needs to be called into at fixed points in time even when there
+ * is no file descriptor activity, see \ref polltime details.
+ *
+ * In order to know precisely when libusb needs to be called into, libusb
+ * offers you a set of pollable file descriptors and information about when
+ * the next timeout expires.
+ *
+ * If you are using the asynchronous I/O API, you must take one of the two
+ * following options, otherwise your I/O will not complete.
+ *
+ * \section pollsimple The simple option
+ *
+ * If your application revolves solely around libusb and does not need to
+ * handle other event sources, you can have a program structure as follows:
+\code
+// initialize libusb
+// find and open device
+// maybe fire off some initial async I/O
+
+while (user_has_not_requested_exit)
+ libusb_handle_events(ctx);
+
+// clean up and exit
+\endcode
+ *
+ * With such a simple main loop, you do not have to worry about managing
+ * sets of file descriptors or handling timeouts. libusb_handle_events() will
+ * handle those details internally.
+ *
+ * \section libusb_pollmain The more advanced option
+ *
+ * \note This functionality is currently only available on Unix-like platforms.
+ * On Windows, libusb_get_pollfds() simply returns NULL. Applications which
+ * want to support Windows are advised to use an \ref eventthread
+ * "event handling thread" instead.
+ *
+ * In more advanced applications, you will already have a main loop which
+ * is monitoring other event sources: network sockets, X11 events, mouse
+ * movements, etc. Through exposing a set of file descriptors, libusb is
+ * designed to cleanly integrate into such main loops.
+ *
+ * In addition to polling file descriptors for the other event sources, you
+ * take a set of file descriptors from libusb and monitor those too. When you
+ * detect activity on libusb's file descriptors, you call
+ * libusb_handle_events_timeout() in non-blocking mode.
+ *
+ * What's more, libusb may also need to handle events at specific moments in
+ * time. No file descriptor activity is generated at these times, so your
+ * own application needs to be continually aware of when the next one of these
+ * moments occurs (through calling libusb_get_next_timeout()), and then it
+ * needs to call libusb_handle_events_timeout() in non-blocking mode when
+ * these moments occur. This means that you need to adjust your
+ * poll()/select() timeout accordingly.
+ *
+ * libusb provides you with a set of file descriptors to poll and expects you
+ * to poll all of them, treating them as a single entity. The meaning of each
+ * file descriptor in the set is an internal implementation detail,
+ * platform-dependent and may vary from release to release. Don't try and
+ * interpret the meaning of the file descriptors, just do as libusb indicates,
+ * polling all of them at once.
+ *
+ * In pseudo-code, you want something that looks like:
+\code
+// initialise libusb
+
+libusb_get_pollfds(ctx)
+while (user has not requested application exit) {
+ libusb_get_next_timeout(ctx);
+ poll(on libusb file descriptors plus any other event sources of interest,
+ using a timeout no larger than the value libusb just suggested)
+ if (poll() indicated activity on libusb file descriptors)
+ libusb_handle_events_timeout(ctx, &zero_tv);
+ if (time has elapsed to or beyond the libusb timeout)
+ libusb_handle_events_timeout(ctx, &zero_tv);
+ // handle events from other sources here
+}
+
+// clean up and exit
+\endcode
+ *
+ * \subsection polltime Notes on time-based events
+ *
+ * The above complication with having to track time and call into libusb at
+ * specific moments is a bit of a headache. For maximum compatibility, you do
+ * need to write your main loop as above, but you may decide that you can
+ * restrict the supported platforms of your application and get away with
+ * a more simplistic scheme.
+ *
+ * These time-based event complications are \b not required on the following
+ * platforms:
+ * - Darwin
+ * - Linux, provided that the following version requirements are satisfied:
+ * - Linux v2.6.27 or newer, compiled with timerfd support
+ * - glibc v2.9 or newer
+ * - libusb v1.0.5 or newer
+ *
+ * Under these configurations, libusb_get_next_timeout() will \em always return
+ * 0, so your main loop can be simplified to:
+\code
+// initialise libusb
+
+libusb_get_pollfds(ctx)
+while (user has not requested application exit) {
+ poll(on libusb file descriptors plus any other event sources of interest,
+ using any timeout that you like)
+ if (poll() indicated activity on libusb file descriptors)
+ libusb_handle_events_timeout(ctx, &zero_tv);
+ // handle events from other sources here
+}
+
+// clean up and exit
+\endcode
+ *
+ * Do remember that if you simplify your main loop to the above, you will
+ * lose compatibility with some platforms (including legacy Linux platforms,
+ * and <em>any future platforms supported by libusb which may have time-based
+ * event requirements</em>). The resultant problems will likely appear as
+ * strange bugs in your application.
+ *
+ * You can use the libusb_pollfds_handle_timeouts() function to do a runtime
+ * check to see if it is safe to ignore the time-based event complications.
+ * If your application has taken the shortcut of ignoring libusb's next timeout
+ * in your main loop, then you are advised to check the return value of
+ * libusb_pollfds_handle_timeouts() during application startup, and to abort
+ * if the platform does suffer from these timing complications.
+ *
+ * \subsection fdsetchange Changes in the file descriptor set
+ *
+ * The set of file descriptors that libusb uses as event sources may change
+ * during the life of your application. Rather than having to repeatedly
+ * call libusb_get_pollfds(), you can set up notification functions for when
+ * the file descriptor set changes using libusb_set_pollfd_notifiers().
+ *
+ * \subsection mtissues Multi-threaded considerations
+ *
+ * Unfortunately, the situation is complicated further when multiple threads
+ * come into play. If two threads are monitoring the same file descriptors,
+ * the fact that only one thread will be woken up when an event occurs causes
+ * some headaches.
+ *
+ * The events lock, event waiters lock, and libusb_handle_events_locked()
+ * entities are added to solve these problems. You do not need to be concerned
+ * with these entities otherwise.
+ *
+ * See the extra documentation: \ref libusb_mtasync
+ */
+
+/** \page libusb_mtasync Multi-threaded applications and asynchronous I/O
+ *
+ * libusb is a thread-safe library, but extra considerations must be applied
+ * to applications which interact with libusb from multiple threads.
+ *
+ * The underlying issue that must be addressed is that all libusb I/O
+ * revolves around monitoring file descriptors through the poll()/select()
+ * system calls. This is directly exposed at the
+ * \ref libusb_asyncio "asynchronous interface" but it is important to note that the
+ * \ref libusb_syncio "synchronous interface" is implemented on top of the
+ * asynchonrous interface, therefore the same considerations apply.
+ *
+ * The issue is that if two or more threads are concurrently calling poll()
+ * or select() on libusb's file descriptors then only one of those threads
+ * will be woken up when an event arrives. The others will be completely
+ * oblivious that anything has happened.
+ *
+ * Consider the following pseudo-code, which submits an asynchronous transfer
+ * then waits for its completion. This style is one way you could implement a
+ * synchronous interface on top of the asynchronous interface (and libusb
+ * does something similar, albeit more advanced due to the complications
+ * explained on this page).
+ *
+\code
+void cb(struct libusb_transfer *transfer)
+{
+ int *completed = transfer->user_data;
+ *completed = 1;
+}
+
+void myfunc() {
+ struct libusb_transfer *transfer;
+ unsigned char buffer[LIBUSB_CONTROL_SETUP_SIZE] __attribute__ ((aligned (2)));
+ int completed = 0;
+
+ transfer = libusb_alloc_transfer(0);
+ libusb_fill_control_setup(buffer,
+ LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_OUT, 0x04, 0x01, 0, 0);
+ libusb_fill_control_transfer(transfer, dev, buffer, cb, &completed, 1000);
+ libusb_submit_transfer(transfer);
+
+ while (!completed) {
+ poll(libusb file descriptors, 120*1000);
+ if (poll indicates activity)
+ libusb_handle_events_timeout(ctx, &zero_tv);
+ }
+ printf("completed!");
+ // other code here
+}
+\endcode
+ *
+ * Here we are <em>serializing</em> completion of an asynchronous event
+ * against a condition - the condition being completion of a specific transfer.
+ * The poll() loop has a long timeout to minimize CPU usage during situations
+ * when nothing is happening (it could reasonably be unlimited).
+ *
+ * If this is the only thread that is polling libusb's file descriptors, there
+ * is no problem: there is no danger that another thread will swallow up the
+ * event that we are interested in. On the other hand, if there is another
+ * thread polling the same descriptors, there is a chance that it will receive
+ * the event that we were interested in. In this situation, <tt>myfunc()</tt>
+ * will only realise that the transfer has completed on the next iteration of
+ * the loop, <em>up to 120 seconds later.</em> Clearly a two-minute delay is
+ * undesirable, and don't even think about using short timeouts to circumvent
+ * this issue!
+ *
+ * The solution here is to ensure that no two threads are ever polling the
+ * file descriptors at the same time. A naive implementation of this would
+ * impact the capabilities of the library, so libusb offers the scheme
+ * documented below to ensure no loss of functionality.
+ *
+ * Before we go any further, it is worth mentioning that all libusb-wrapped
+ * event handling procedures fully adhere to the scheme documented below.
+ * This includes libusb_handle_events() and its variants, and all the
+ * synchronous I/O functions - libusb hides this headache from you.
+ *
+ * \section Using libusb_handle_events() from multiple threads
+ *
+ * Even when only using libusb_handle_events() and synchronous I/O functions,
+ * you can still have a race condition. You might be tempted to solve the
+ * above with libusb_handle_events() like so:
+ *
+\code
+ libusb_submit_transfer(transfer);
+
+ while (!completed) {
+ libusb_handle_events(ctx);
+ }
+ printf("completed!");
+\endcode
+ *
+ * This however has a race between the checking of completed and
+ * libusb_handle_events() acquiring the events lock, so another thread
+ * could have completed the transfer, resulting in this thread hanging
+ * until either a timeout or another event occurs. See also commit
+ * 6696512aade99bb15d6792af90ae329af270eba6 which fixes this in the
+ * synchronous API implementation of libusb.
+ *
+ * Fixing this race requires checking the variable completed only after
+ * taking the event lock, which defeats the concept of just calling
+ * libusb_handle_events() without worrying about locking. This is why
+ * libusb-1.0.9 introduces the new libusb_handle_events_timeout_completed()
+ * and libusb_handle_events_completed() functions, which handles doing the
+ * completion check for you after they have acquired the lock:
+ *
+\code
+ libusb_submit_transfer(transfer);
+
+ while (!completed) {
+ libusb_handle_events_completed(ctx, &completed);
+ }
+ printf("completed!");
+\endcode
+ *
+ * This nicely fixes the race in our example. Note that if all you want to
+ * do is submit a single transfer and wait for its completion, then using
+ * one of the synchronous I/O functions is much easier.
+ *
+ * \section eventlock The events lock
+ *
+ * The problem is when we consider the fact that libusb exposes file
+ * descriptors to allow for you to integrate asynchronous USB I/O into
+ * existing main loops, effectively allowing you to do some work behind
+ * libusb's back. If you do take libusb's file descriptors and pass them to
+ * poll()/select() yourself, you need to be aware of the associated issues.
+ *
+ * The first concept to be introduced is the events lock. The events lock
+ * is used to serialize threads that want to handle events, such that only
+ * one thread is handling events at any one time.
+ *
+ * You must take the events lock before polling libusb file descriptors,
+ * using libusb_lock_events(). You must release the lock as soon as you have
+ * aborted your poll()/select() loop, using libusb_unlock_events().
+ *
+ * \section threadwait Letting other threads do the work for you
+ *
+ * Although the events lock is a critical part of the solution, it is not
+ * enough on it's own. You might wonder if the following is sufficient...
+\code
+ libusb_lock_events(ctx);
+ while (!completed) {
+ poll(libusb file descriptors, 120*1000);
+ if (poll indicates activity)
+ libusb_handle_events_timeout(ctx, &zero_tv);
+ }
+ libusb_unlock_events(ctx);
+\endcode
+ * ...and the answer is that it is not. This is because the transfer in the
+ * code shown above may take a long time (say 30 seconds) to complete, and
+ * the lock is not released until the transfer is completed.
+ *
+ * Another thread with similar code that wants to do event handling may be
+ * working with a transfer that completes after a few milliseconds. Despite
+ * having such a quick completion time, the other thread cannot check that
+ * status of its transfer until the code above has finished (30 seconds later)
+ * due to contention on the lock.
+ *
+ * To solve this, libusb offers you a mechanism to determine when another
+ * thread is handling events. It also offers a mechanism to block your thread
+ * until the event handling thread has completed an event (and this mechanism
+ * does not involve polling of file descriptors).
+ *
+ * After determining that another thread is currently handling events, you
+ * obtain the <em>event waiters</em> lock using libusb_lock_event_waiters().
+ * You then re-check that some other thread is still handling events, and if
+ * so, you call libusb_wait_for_event().
+ *
+ * libusb_wait_for_event() puts your application to sleep until an event
+ * occurs, or until a thread releases the events lock. When either of these
+ * things happen, your thread is woken up, and should re-check the condition
+ * it was waiting on. It should also re-check that another thread is handling
+ * events, and if not, it should start handling events itself.
+ *
+ * This looks like the following, as pseudo-code:
+\code
+retry:
+if (libusb_try_lock_events(ctx) == 0) {
+ // we obtained the event lock: do our own event handling
+ while (!completed) {
+ if (!libusb_event_handling_ok(ctx)) {
+ libusb_unlock_events(ctx);
+ goto retry;
+ }
+ poll(libusb file descriptors, 120*1000);
+ if (poll indicates activity)
+ libusb_handle_events_locked(ctx, 0);
+ }
+ libusb_unlock_events(ctx);
+} else {
+ // another thread is doing event handling. wait for it to signal us that
+ // an event has completed
+ libusb_lock_event_waiters(ctx);
+
+ while (!completed) {
+ // now that we have the event waiters lock, double check that another
+ // thread is still handling events for us. (it may have ceased handling
+ // events in the time it took us to reach this point)
+ if (!libusb_event_handler_active(ctx)) {
+ // whoever was handling events is no longer doing so, try again
+ libusb_unlock_event_waiters(ctx);
+ goto retry;
+ }
+
+ libusb_wait_for_event(ctx, NULL);
+ }
+ libusb_unlock_event_waiters(ctx);
+}
+printf("completed!\n");
+\endcode
+ *
+ * A naive look at the above code may suggest that this can only support
+ * one event waiter (hence a total of 2 competing threads, the other doing
+ * event handling), because the event waiter seems to have taken the event
+ * waiters lock while waiting for an event. However, the system does support
+ * multiple event waiters, because libusb_wait_for_event() actually drops
+ * the lock while waiting, and reaquires it before continuing.
+ *
+ * We have now implemented code which can dynamically handle situations where
+ * nobody is handling events (so we should do it ourselves), and it can also
+ * handle situations where another thread is doing event handling (so we can
+ * piggyback onto them). It is also equipped to handle a combination of
+ * the two, for example, another thread is doing event handling, but for
+ * whatever reason it stops doing so before our condition is met, so we take
+ * over the event handling.
+ *
+ * Four functions were introduced in the above pseudo-code. Their importance
+ * should be apparent from the code shown above.
+ * -# libusb_try_lock_events() is a non-blocking function which attempts
+ * to acquire the events lock but returns a failure code if it is contended.
+ * -# libusb_event_handling_ok() checks that libusb is still happy for your
+ * thread to be performing event handling. Sometimes, libusb needs to
+ * interrupt the event handler, and this is how you can check if you have
+ * been interrupted. If this function returns 0, the correct behaviour is
+ * for you to give up the event handling lock, and then to repeat the cycle.
+ * The following libusb_try_lock_events() will fail, so you will become an
+ * events waiter. For more information on this, read \ref fullstory below.
+ * -# libusb_handle_events_locked() is a variant of
+ * libusb_handle_events_timeout() that you can call while holding the
+ * events lock. libusb_handle_events_timeout() itself implements similar
+ * logic to the above, so be sure not to call it when you are
+ * "working behind libusb's back", as is the case here.
+ * -# libusb_event_handler_active() determines if someone is currently
+ * holding the events lock
+ *
+ * You might be wondering why there is no function to wake up all threads
+ * blocked on libusb_wait_for_event(). This is because libusb can do this
+ * internally: it will wake up all such threads when someone calls
+ * libusb_unlock_events() or when a transfer completes (at the point after its
+ * callback has returned).
+ *
+ * \subsection fullstory The full story
+ *
+ * The above explanation should be enough to get you going, but if you're
+ * really thinking through the issues then you may be left with some more
+ * questions regarding libusb's internals. If you're curious, read on, and if
+ * not, skip to the next section to avoid confusing yourself!
+ *
+ * The immediate question that may spring to mind is: what if one thread
+ * modifies the set of file descriptors that need to be polled while another
+ * thread is doing event handling?
+ *
+ * There are 2 situations in which this may happen.
+ * -# libusb_open() will add another file descriptor to the poll set,
+ * therefore it is desirable to interrupt the event handler so that it
+ * restarts, picking up the new descriptor.
+ * -# libusb_close() will remove a file descriptor from the poll set. There
+ * are all kinds of race conditions that could arise here, so it is
+ * important that nobody is doing event handling at this time.
+ *
+ * libusb handles these issues internally, so application developers do not
+ * have to stop their event handlers while opening/closing devices. Here's how
+ * it works, focusing on the libusb_close() situation first:
+ *
+ * -# During initialization, libusb opens an internal pipe, and it adds the read
+ * end of this pipe to the set of file descriptors to be polled.
+ * -# During libusb_close(), libusb writes some dummy data on this event pipe.
+ * This immediately interrupts the event handler. libusb also records
+ * internally that it is trying to interrupt event handlers for this
+ * high-priority event.
+ * -# At this point, some of the functions described above start behaving
+ * differently:
+ * - libusb_event_handling_ok() starts returning 1, indicating that it is NOT
+ * OK for event handling to continue.
+ * - libusb_try_lock_events() starts returning 1, indicating that another
+ * thread holds the event handling lock, even if the lock is uncontended.
+ * - libusb_event_handler_active() starts returning 1, indicating that
+ * another thread is doing event handling, even if that is not true.
+ * -# The above changes in behaviour result in the event handler stopping and
+ * giving up the events lock very quickly, giving the high-priority
+ * libusb_close() operation a "free ride" to acquire the events lock. All
+ * threads that are competing to do event handling become event waiters.
+ * -# With the events lock held inside libusb_close(), libusb can safely remove
+ * a file descriptor from the poll set, in the safety of knowledge that
+ * nobody is polling those descriptors or trying to access the poll set.
+ * -# After obtaining the events lock, the close operation completes very
+ * quickly (usually a matter of milliseconds) and then immediately releases
+ * the events lock.
+ * -# At the same time, the behaviour of libusb_event_handling_ok() and friends
+ * reverts to the original, documented behaviour.
+ * -# The release of the events lock causes the threads that are waiting for
+ * events to be woken up and to start competing to become event handlers
+ * again. One of them will succeed; it will then re-obtain the list of poll
+ * descriptors, and USB I/O will then continue as normal.
+ *
+ * libusb_open() is similar, and is actually a more simplistic case. Upon a
+ * call to libusb_open():
+ *
+ * -# The device is opened and a file descriptor is added to the poll set.
+ * -# libusb sends some dummy data on the event pipe, and records that it
+ * is trying to modify the poll descriptor set.
+ * -# The event handler is interrupted, and the same behaviour change as for
+ * libusb_close() takes effect, causing all event handling threads to become
+ * event waiters.
+ * -# The libusb_open() implementation takes its free ride to the events lock.
+ * -# Happy that it has successfully paused the events handler, libusb_open()
+ * releases the events lock.
+ * -# The event waiter threads are all woken up and compete to become event
+ * handlers again. The one that succeeds will obtain the list of poll
+ * descriptors again, which will include the addition of the new device.
+ *
+ * \subsection concl Closing remarks
+ *
+ * The above may seem a little complicated, but hopefully I have made it clear
+ * why such complications are necessary. Also, do not forget that this only
+ * applies to applications that take libusb's file descriptors and integrate
+ * them into their own polling loops.
+ *
+ * You may decide that it is OK for your multi-threaded application to ignore
+ * some of the rules and locks detailed above, because you don't think that
+ * two threads can ever be polling the descriptors at the same time. If that
+ * is the case, then that's good news for you because you don't have to worry.
+ * But be careful here; remember that the synchronous I/O functions do event
+ * handling internally. If you have one thread doing event handling in a loop
+ * (without implementing the rules and locking semantics documented above)
+ * and another trying to send a synchronous USB transfer, you will end up with
+ * two threads monitoring the same descriptors, and the above-described
+ * undesirable behaviour occurring. The solution is for your polling thread to
+ * play by the rules; the synchronous I/O functions do so, and this will result
+ * in them getting along in perfect harmony.
+ *
+ * If you do have a dedicated thread doing event handling, it is perfectly
+ * legal for it to take the event handling lock for long periods of time. Any
+ * synchronous I/O functions you call from other threads will transparently
+ * fall back to the "event waiters" mechanism detailed above. The only
+ * consideration that your event handling thread must apply is the one related
+ * to libusb_event_handling_ok(): you must call this before every poll(), and
+ * give up the events lock if instructed.
+ */
+
+int usbi_io_init(struct libusb_context *ctx)
+{
+ int r;
+
+ usbi_mutex_init(&ctx->flying_transfers_lock);
+ usbi_mutex_init(&ctx->events_lock);
+ usbi_mutex_init(&ctx->event_waiters_lock);
+ usbi_cond_init(&ctx->event_waiters_cond);
+ usbi_mutex_init(&ctx->event_data_lock);
+ usbi_tls_key_create(&ctx->event_handling_key);
+ list_init(&ctx->flying_transfers);
+ list_init(&ctx->ipollfds);
+ list_init(&ctx->hotplug_msgs);
+ list_init(&ctx->completed_transfers);
+
+ /* FIXME should use an eventfd on kernels that support it */
+ r = usbi_pipe(ctx->event_pipe);
+ if (r < 0) {
+ r = LIBUSB_ERROR_OTHER;
+ goto err;
+ }
+
+ r = usbi_add_pollfd(ctx, ctx->event_pipe[0], POLLIN);
+ if (r < 0)
+ goto err_close_pipe;
+
+#ifdef USBI_TIMERFD_AVAILABLE
+ ctx->timerfd = timerfd_create(usbi_backend->get_timerfd_clockid(),
+ TFD_NONBLOCK);
+ if (ctx->timerfd >= 0) {
+ usbi_dbg("using timerfd for timeouts");
+ r = usbi_add_pollfd(ctx, ctx->timerfd, POLLIN);
+ if (r < 0)
+ goto err_close_timerfd;
+ } else {
+ usbi_dbg("timerfd not available (code %d error %d)", ctx->timerfd, errno);
+ ctx->timerfd = -1;
+ }
+#endif
+
+ return 0;
+
+#ifdef USBI_TIMERFD_AVAILABLE
+err_close_timerfd:
+ close(ctx->timerfd);
+ usbi_remove_pollfd(ctx, ctx->event_pipe[0]);
+#endif
+err_close_pipe:
+ usbi_close(ctx->event_pipe[0]);
+ usbi_close(ctx->event_pipe[1]);
+err:
+ usbi_mutex_destroy(&ctx->flying_transfers_lock);
+ usbi_mutex_destroy(&ctx->events_lock);
+ usbi_mutex_destroy(&ctx->event_waiters_lock);
+ usbi_cond_destroy(&ctx->event_waiters_cond);
+ usbi_mutex_destroy(&ctx->event_data_lock);
+ usbi_tls_key_delete(ctx->event_handling_key);
+ return r;
+}
+
+void usbi_io_exit(struct libusb_context *ctx)
+{
+ usbi_remove_pollfd(ctx, ctx->event_pipe[0]);
+ usbi_close(ctx->event_pipe[0]);
+ usbi_close(ctx->event_pipe[1]);
+#ifdef USBI_TIMERFD_AVAILABLE
+ if (usbi_using_timerfd(ctx)) {
+ usbi_remove_pollfd(ctx, ctx->timerfd);
+ close(ctx->timerfd);
+ }
+#endif
+ usbi_mutex_destroy(&ctx->flying_transfers_lock);
+ usbi_mutex_destroy(&ctx->events_lock);
+ usbi_mutex_destroy(&ctx->event_waiters_lock);
+ usbi_cond_destroy(&ctx->event_waiters_cond);
+ usbi_mutex_destroy(&ctx->event_data_lock);
+ usbi_tls_key_delete(ctx->event_handling_key);
+ if (ctx->pollfds)
+ free(ctx->pollfds);
+}
+
+static int calculate_timeout(struct usbi_transfer *transfer)
+{
+ int r;
+ struct timespec current_time;
+ unsigned int timeout =
+ USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer)->timeout;
+
+ if (!timeout)
+ return 0;
+
+ r = usbi_backend->clock_gettime(USBI_CLOCK_MONOTONIC, &current_time);
+ if (r < 0) {
+ usbi_err(ITRANSFER_CTX(transfer),
+ "failed to read monotonic clock, errno=%d", errno);
+ return r;
+ }
+
+ current_time.tv_sec += timeout / 1000;
+ current_time.tv_nsec += (timeout % 1000) * 1000000;
+
+ while (current_time.tv_nsec >= 1000000000) {
+ current_time.tv_nsec -= 1000000000;
+ current_time.tv_sec++;
+ }
+
+ TIMESPEC_TO_TIMEVAL(&transfer->timeout, &current_time);
+ return 0;
+}
+
+/** \ingroup libusb_asyncio
+ * Allocate a libusb transfer with a specified number of isochronous packet
+ * descriptors. The returned transfer is pre-initialized for you. When the new
+ * transfer is no longer needed, it should be freed with
+ * libusb_free_transfer().
+ *
+ * Transfers intended for non-isochronous endpoints (e.g. control, bulk,
+ * interrupt) should specify an iso_packets count of zero.
+ *
+ * For transfers intended for isochronous endpoints, specify an appropriate
+ * number of packet descriptors to be allocated as part of the transfer.
+ * The returned transfer is not specially initialized for isochronous I/O;
+ * you are still required to set the
+ * \ref libusb_transfer::num_iso_packets "num_iso_packets" and
+ * \ref libusb_transfer::type "type" fields accordingly.
+ *
+ * It is safe to allocate a transfer with some isochronous packets and then
+ * use it on a non-isochronous endpoint. If you do this, ensure that at time
+ * of submission, num_iso_packets is 0 and that type is set appropriately.
+ *
+ * \param iso_packets number of isochronous packet descriptors to allocate
+ * \returns a newly allocated transfer, or NULL on error
+ */
+DEFAULT_VISIBILITY
+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 alloc_size = sizeof(struct usbi_transfer)
+ + sizeof(struct libusb_transfer)
+ + (sizeof(struct libusb_iso_packet_descriptor) * iso_packets)
+ + os_alloc_size;
+ struct usbi_transfer *itransfer = calloc(1, alloc_size);
+ if (!itransfer)
+ return NULL;
+
+ itransfer->num_iso_packets = iso_packets;
+ usbi_mutex_init(&itransfer->lock);
+ transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ usbi_dbg("transfer %p", transfer);
+ return transfer;
+}
+
+/** \ingroup libusb_asyncio
+ * Free a transfer structure. This should be called for all transfers
+ * allocated with libusb_alloc_transfer().
+ *
+ * If the \ref libusb_transfer_flags::LIBUSB_TRANSFER_FREE_BUFFER
+ * "LIBUSB_TRANSFER_FREE_BUFFER" flag is set and the transfer buffer is
+ * non-NULL, this function will also free the transfer buffer using the
+ * standard system memory allocator (e.g. free()).
+ *
+ * It is legal to call this function with a NULL transfer. In this case,
+ * the function will simply return safely.
+ *
+ * It is not legal to free an active transfer (one which has been submitted
+ * and has not yet completed).
+ *
+ * \param transfer the transfer to free
+ */
+void API_EXPORTED libusb_free_transfer(struct libusb_transfer *transfer)
+{
+ struct usbi_transfer *itransfer;
+ if (!transfer)
+ return;
+
+ usbi_dbg("transfer %p", transfer);
+ if (transfer->flags & LIBUSB_TRANSFER_FREE_BUFFER && transfer->buffer)
+ free(transfer->buffer);
+
+ itransfer = LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer);
+ usbi_mutex_destroy(&itransfer->lock);
+ free(itransfer);
+}
+
+#ifdef USBI_TIMERFD_AVAILABLE
+static int disarm_timerfd(struct libusb_context *ctx)
+{
+ const struct itimerspec disarm_timer = { { 0, 0 }, { 0, 0 } };
+ int r;
+
+ usbi_dbg("");
+ r = timerfd_settime(ctx->timerfd, 0, &disarm_timer, NULL);
+ if (r < 0)
+ return LIBUSB_ERROR_OTHER;
+ else
+ return 0;
+}
+
+/* iterates through the flying transfers, and rearms the timerfd based on the
+ * next upcoming timeout.
+ * must be called with flying_list locked.
+ * returns 0 on success or a LIBUSB_ERROR code on failure.
+ */
+static int arm_timerfd_for_next_timeout(struct libusb_context *ctx)
+{
+ struct usbi_transfer *transfer;
+
+ list_for_each_entry(transfer, &ctx->flying_transfers, list, struct usbi_transfer) {
+ struct timeval *cur_tv = &transfer->timeout;
+
+ /* if we've reached transfers of infinite timeout, then we have no
+ * arming to do */
+ if (!timerisset(cur_tv))
+ goto disarm;
+
+ /* act on first transfer that has not already been handled */
+ if (!(transfer->timeout_flags & (USBI_TRANSFER_TIMEOUT_HANDLED | USBI_TRANSFER_OS_HANDLES_TIMEOUT))) {
+ int r;
+ const struct itimerspec it = { {0, 0},
+ { cur_tv->tv_sec, cur_tv->tv_usec * 1000 } };
+ usbi_dbg("next timeout originally %dms", USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer)->timeout);
+ r = timerfd_settime(ctx->timerfd, TFD_TIMER_ABSTIME, &it, NULL);
+ if (r < 0)
+ return LIBUSB_ERROR_OTHER;
+ return 0;
+ }
+ }
+
+disarm:
+ return disarm_timerfd(ctx);
+}
+#else
+static int arm_timerfd_for_next_timeout(struct libusb_context *ctx)
+{
+ UNUSED(ctx);
+ return 0;
+}
+#endif
+
+/* add a transfer to the (timeout-sorted) active transfers list.
+ * This function will return non 0 if fails to update the timer,
+ * in which case the transfer is *not* on the flying_transfers list. */
+static int add_to_flying_list(struct usbi_transfer *transfer)
+{
+ struct usbi_transfer *cur;
+ struct timeval *timeout = &transfer->timeout;
+ struct libusb_context *ctx = ITRANSFER_CTX(transfer);
+ int r;
+ int first = 1;
+
+ r = calculate_timeout(transfer);
+ if (r)
+ return r;
+
+ /* if we have no other flying transfers, start the list with this one */
+ if (list_empty(&ctx->flying_transfers)) {
+ list_add(&transfer->list, &ctx->flying_transfers);
+ goto out;
+ }
+
+ /* if we have infinite timeout, append to end of list */
+ if (!timerisset(timeout)) {
+ list_add_tail(&transfer->list, &ctx->flying_transfers);
+ /* first is irrelevant in this case */
+ goto out;
+ }
+
+ /* otherwise, find appropriate place in list */
+ list_for_each_entry(cur, &ctx->flying_transfers, list, struct usbi_transfer) {
+ /* find first timeout that occurs after the transfer in question */
+ struct timeval *cur_tv = &cur->timeout;
+
+ if (!timerisset(cur_tv) || (cur_tv->tv_sec > timeout->tv_sec) ||
+ (cur_tv->tv_sec == timeout->tv_sec &&
+ cur_tv->tv_usec > timeout->tv_usec)) {
+ list_add_tail(&transfer->list, &cur->list);
+ goto out;
+ }
+ first = 0;
+ }
+ /* first is 0 at this stage (list not empty) */
+
+ /* otherwise we need to be inserted at the end */
+ list_add_tail(&transfer->list, &ctx->flying_transfers);
+out:
+#ifdef USBI_TIMERFD_AVAILABLE
+ if (first && usbi_using_timerfd(ctx) && timerisset(timeout)) {
+ /* if this transfer has the lowest timeout of all active transfers,
+ * rearm the timerfd with this transfer's timeout */
+ const struct itimerspec it = { {0, 0},
+ { timeout->tv_sec, timeout->tv_usec * 1000 } };
+ usbi_dbg("arm timerfd for timeout in %dms (first in line)",
+ USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer)->timeout);
+ r = timerfd_settime(ctx->timerfd, TFD_TIMER_ABSTIME, &it, NULL);
+ if (r < 0) {
+ usbi_warn(ctx, "failed to arm first timerfd (errno %d)", errno);
+ r = LIBUSB_ERROR_OTHER;
+ }
+ }
+#else
+ UNUSED(first);
+#endif
+
+ if (r)
+ list_del(&transfer->list);
+
+ return r;
+}
+
+/* remove a transfer from the active transfers list.
+ * This function will *always* remove the transfer from the
+ * flying_transfers list. It will return a LIBUSB_ERROR code
+ * if it fails to update the timer for the next timeout. */
+static int remove_from_flying_list(struct usbi_transfer *transfer)
+{
+ struct libusb_context *ctx = ITRANSFER_CTX(transfer);
+ int rearm_timerfd;
+ int r = 0;
+
+ usbi_mutex_lock(&ctx->flying_transfers_lock);
+ rearm_timerfd = (timerisset(&transfer->timeout) &&
+ list_first_entry(&ctx->flying_transfers, struct usbi_transfer, list) == transfer);
+ list_del(&transfer->list);
+ if (usbi_using_timerfd(ctx) && rearm_timerfd)
+ r = arm_timerfd_for_next_timeout(ctx);
+ usbi_mutex_unlock(&ctx->flying_transfers_lock);
+
+ return r;
+}
+
+/** \ingroup libusb_asyncio
+ * Submit a transfer. This function will fire off the USB transfer and then
+ * return immediately.
+ *
+ * \param transfer the transfer to submit
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * \returns LIBUSB_ERROR_BUSY if the transfer has already been submitted.
+ * \returns LIBUSB_ERROR_NOT_SUPPORTED if the transfer flags are not supported
+ * by the operating system.
+ * \returns LIBUSB_ERROR_INVALID_PARAM if the transfer size is larger than
+ * the operating system and/or hardware can support
+ * \returns another LIBUSB_ERROR code on other failure
+ */
+int API_EXPORTED libusb_submit_transfer(struct libusb_transfer *transfer)
+{
+ struct usbi_transfer *itransfer =
+ LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer);
+ struct libusb_context *ctx = TRANSFER_CTX(transfer);
+ int r;
+
+ usbi_dbg("transfer %p", transfer);
+
+ /*
+ * Important note on locking, this function takes / releases locks
+ * in the following order:
+ * take flying_transfers_lock
+ * take itransfer->lock
+ * clear transfer
+ * add to flying_transfers list
+ * release flying_transfers_lock
+ * submit transfer
+ * release itransfer->lock
+ * if submit failed:
+ * take flying_transfers_lock
+ * remove from flying_transfers list
+ * release flying_transfers_lock
+ *
+ * Note that it takes locks in the order a-b and then releases them
+ * in the same order a-b. This is somewhat unusual but not wrong,
+ * release order is not important as long as *all* locks are released
+ * before re-acquiring any locks.
+ *
+ * This means that the ordering of first releasing itransfer->lock
+ * and then re-acquiring the flying_transfers_list on error is
+ * important and must not be changed!
+ *
+ * This is done this way because when we take both locks we must always
+ * take flying_transfers_lock first to avoid ab-ba style deadlocks with
+ * the timeout handling and usbi_handle_disconnect paths.
+ *
+ * And we cannot release itransfer->lock before the submission is
+ * complete otherwise timeout handling for transfers with short
+ * timeouts may run before submission.
+ */
+ usbi_mutex_lock(&ctx->flying_transfers_lock);
+ usbi_mutex_lock(&itransfer->lock);
+ if (itransfer->state_flags & USBI_TRANSFER_IN_FLIGHT) {
+ usbi_mutex_unlock(&ctx->flying_transfers_lock);
+ usbi_mutex_unlock(&itransfer->lock);
+ return LIBUSB_ERROR_BUSY;
+ }
+ itransfer->transferred = 0;
+ itransfer->state_flags = 0;
+ itransfer->timeout_flags = 0;
+ r = add_to_flying_list(itransfer);
+ if (r) {
+ usbi_mutex_unlock(&ctx->flying_transfers_lock);
+ usbi_mutex_unlock(&itransfer->lock);
+ return r;
+ }
+ /*
+ * We must release the flying transfers lock here, because with
+ * some backends the submit_transfer method is synchroneous.
+ */
+ usbi_mutex_unlock(&ctx->flying_transfers_lock);
+
+ r = usbi_backend->submit_transfer(itransfer);
+ if (r == LIBUSB_SUCCESS) {
+ itransfer->state_flags |= USBI_TRANSFER_IN_FLIGHT;
+ /* keep a reference to this device */
+ libusb_ref_device(transfer->dev_handle->dev);
+ }
+ usbi_mutex_unlock(&itransfer->lock);
+
+ if (r != LIBUSB_SUCCESS)
+ remove_from_flying_list(itransfer);
+
+ return r;
+}
+
+/** \ingroup libusb_asyncio
+ * Asynchronously cancel a previously submitted transfer.
+ * This function returns immediately, but this does not indicate cancellation
+ * is complete. Your callback function will be invoked at some later time
+ * with a transfer status of
+ * \ref libusb_transfer_status::LIBUSB_TRANSFER_CANCELLED
+ * "LIBUSB_TRANSFER_CANCELLED."
+ *
+ * \param transfer the transfer to cancel
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_NOT_FOUND if the transfer is not in progress,
+ * already complete, or already cancelled.
+ * \returns a LIBUSB_ERROR code on failure
+ */
+int API_EXPORTED libusb_cancel_transfer(struct libusb_transfer *transfer)
+{
+ struct usbi_transfer *itransfer =
+ LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer);
+ int r;
+
+ usbi_dbg("transfer %p", transfer );
+ usbi_mutex_lock(&itransfer->lock);
+ if (!(itransfer->state_flags & USBI_TRANSFER_IN_FLIGHT)
+ || (itransfer->state_flags & USBI_TRANSFER_CANCELLING)) {
+ r = LIBUSB_ERROR_NOT_FOUND;
+ goto out;
+ }
+ r = usbi_backend->cancel_transfer(itransfer);
+ if (r < 0) {
+ if (r != LIBUSB_ERROR_NOT_FOUND &&
+ r != LIBUSB_ERROR_NO_DEVICE)
+ usbi_err(TRANSFER_CTX(transfer),
+ "cancel transfer failed error %d", r);
+ else
+ usbi_dbg("cancel transfer failed error %d", r);
+
+ if (r == LIBUSB_ERROR_NO_DEVICE)
+ itransfer->state_flags |= USBI_TRANSFER_DEVICE_DISAPPEARED;
+ }
+
+ itransfer->state_flags |= USBI_TRANSFER_CANCELLING;
+
+out:
+ usbi_mutex_unlock(&itransfer->lock);
+ return r;
+}
+
+/** \ingroup libusb_asyncio
+ * Set a transfers bulk stream id. Note users are advised to use
+ * libusb_fill_bulk_stream_transfer() instead of calling this function
+ * directly.
+ *
+ * Since version 1.0.19, \ref LIBUSB_API_VERSION >= 0x01000103
+ *
+ * \param transfer the transfer to set the stream id for
+ * \param stream_id the stream id to set
+ * \see libusb_alloc_streams()
+ */
+void API_EXPORTED libusb_transfer_set_stream_id(
+ struct libusb_transfer *transfer, uint32_t stream_id)
+{
+ struct usbi_transfer *itransfer =
+ LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer);
+
+ itransfer->stream_id = stream_id;
+}
+
+/** \ingroup libusb_asyncio
+ * Get a transfers bulk stream id.
+ *
+ * Since version 1.0.19, \ref LIBUSB_API_VERSION >= 0x01000103
+ *
+ * \param transfer the transfer to get the stream id for
+ * \returns the stream id for the transfer
+ */
+uint32_t API_EXPORTED libusb_transfer_get_stream_id(
+ struct libusb_transfer *transfer)
+{
+ struct usbi_transfer *itransfer =
+ LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer);
+
+ return itransfer->stream_id;
+}
+
+/* Handle completion of a transfer (completion might be an error condition).
+ * This will invoke the user-supplied callback function, which may end up
+ * freeing the transfer. Therefore you cannot use the transfer structure
+ * after calling this function, and you should free all backend-specific
+ * data before calling it.
+ * Do not call this function with the usbi_transfer lock held. User-specified
+ * callback functions may attempt to directly resubmit the transfer, which
+ * will attempt to take the lock. */
+int usbi_handle_transfer_completion(struct usbi_transfer *itransfer,
+ enum libusb_transfer_status status)
+{
+ struct libusb_transfer *transfer =
+ USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct libusb_device_handle *dev_handle = transfer->dev_handle;
+ uint8_t flags;
+ int r;
+
+ r = remove_from_flying_list(itransfer);
+ if (r < 0)
+ usbi_err(ITRANSFER_CTX(itransfer), "failed to set timer for next timeout, errno=%d", errno);
+
+ usbi_mutex_lock(&itransfer->lock);
+ itransfer->state_flags &= ~USBI_TRANSFER_IN_FLIGHT;
+ usbi_mutex_unlock(&itransfer->lock);
+
+ if (status == LIBUSB_TRANSFER_COMPLETED
+ && transfer->flags & LIBUSB_TRANSFER_SHORT_NOT_OK) {
+ int rqlen = transfer->length;
+ if (transfer->type == LIBUSB_TRANSFER_TYPE_CONTROL)
+ rqlen -= LIBUSB_CONTROL_SETUP_SIZE;
+ if (rqlen != itransfer->transferred) {
+ usbi_dbg("interpreting short transfer as error");
+ status = LIBUSB_TRANSFER_ERROR;
+ }
+ }
+
+ flags = transfer->flags;
+ transfer->status = status;
+ transfer->actual_length = itransfer->transferred;
+ usbi_dbg("transfer %p has callback %p", transfer, transfer->callback);
+ if (transfer->callback)
+ transfer->callback(transfer);
+ /* transfer might have been freed by the above call, do not use from
+ * this point. */
+ if (flags & LIBUSB_TRANSFER_FREE_TRANSFER)
+ libusb_free_transfer(transfer);
+ libusb_unref_device(dev_handle->dev);
+ return r;
+}
+
+/* Similar to usbi_handle_transfer_completion() but exclusively for transfers
+ * that were asynchronously cancelled. The same concerns w.r.t. freeing of
+ * transfers exist here.
+ * Do not call this function with the usbi_transfer lock held. User-specified
+ * callback functions may attempt to directly resubmit the transfer, which
+ * will attempt to take the lock. */
+int usbi_handle_transfer_cancellation(struct usbi_transfer *transfer)
+{
+ struct libusb_context *ctx = ITRANSFER_CTX(transfer);
+ uint8_t timed_out;
+
+ usbi_mutex_lock(&ctx->flying_transfers_lock);
+ timed_out = transfer->timeout_flags & USBI_TRANSFER_TIMED_OUT;
+ usbi_mutex_unlock(&ctx->flying_transfers_lock);
+
+ /* if the URB was cancelled due to timeout, report timeout to the user */
+ if (timed_out) {
+ usbi_dbg("detected timeout cancellation");
+ return usbi_handle_transfer_completion(transfer, LIBUSB_TRANSFER_TIMED_OUT);
+ }
+
+ /* otherwise its a normal async cancel */
+ return usbi_handle_transfer_completion(transfer, LIBUSB_TRANSFER_CANCELLED);
+}
+
+/* Add a completed transfer to the completed_transfers list of the
+ * context and signal the event. The backend's handle_transfer_completion()
+ * function will be called the next time an event handler runs. */
+void usbi_signal_transfer_completion(struct usbi_transfer *transfer)
+{
+ struct libusb_context *ctx = ITRANSFER_CTX(transfer);
+ int pending_events;
+
+ usbi_mutex_lock(&ctx->event_data_lock);
+ pending_events = usbi_pending_events(ctx);
+ list_add_tail(&transfer->completed_list, &ctx->completed_transfers);
+ if (!pending_events)
+ usbi_signal_event(ctx);
+ usbi_mutex_unlock(&ctx->event_data_lock);
+}
+
+/** \ingroup libusb_poll
+ * Attempt to acquire the event handling lock. This lock is used to ensure that
+ * only one thread is monitoring libusb event sources at any one time.
+ *
+ * You only need to use this lock if you are developing an application
+ * which calls poll() or select() on libusb's file descriptors directly.
+ * If you stick to libusb's event handling loop functions (e.g.
+ * libusb_handle_events()) then you do not need to be concerned with this
+ * locking.
+ *
+ * While holding this lock, you are trusted to actually be handling events.
+ * If you are no longer handling events, you must call libusb_unlock_events()
+ * as soon as possible.
+ *
+ * \param ctx the context to operate on, or NULL for the default context
+ * \returns 0 if the lock was obtained successfully
+ * \returns 1 if the lock was not obtained (i.e. another thread holds the lock)
+ * \ref libusb_mtasync
+ */
+int API_EXPORTED libusb_try_lock_events(libusb_context *ctx)
+{
+ int r;
+ unsigned int ru;
+ USBI_GET_CONTEXT(ctx);
+
+ /* is someone else waiting to close a device? if so, don't let this thread
+ * start event handling */
+ usbi_mutex_lock(&ctx->event_data_lock);
+ ru = ctx->device_close;
+ usbi_mutex_unlock(&ctx->event_data_lock);
+ if (ru) {
+ usbi_dbg("someone else is closing a device");
+ return 1;
+ }
+
+ r = usbi_mutex_trylock(&ctx->events_lock);
+ if (r)
+ return 1;
+
+ ctx->event_handler_active = 1;
+ return 0;
+}
+
+/** \ingroup libusb_poll
+ * Acquire the event handling lock, blocking until successful acquisition if
+ * it is contended. This lock is used to ensure that only one thread is
+ * monitoring libusb event sources at any one time.
+ *
+ * You only need to use this lock if you are developing an application
+ * which calls poll() or select() on libusb's file descriptors directly.
+ * If you stick to libusb's event handling loop functions (e.g.
+ * libusb_handle_events()) then you do not need to be concerned with this
+ * locking.
+ *
+ * While holding this lock, you are trusted to actually be handling events.
+ * If you are no longer handling events, you must call libusb_unlock_events()
+ * as soon as possible.
+ *
+ * \param ctx the context to operate on, or NULL for the default context
+ * \ref libusb_mtasync
+ */
+void API_EXPORTED libusb_lock_events(libusb_context *ctx)
+{
+ USBI_GET_CONTEXT(ctx);
+ usbi_mutex_lock(&ctx->events_lock);
+ ctx->event_handler_active = 1;
+}
+
+/** \ingroup libusb_poll
+ * Release the lock previously acquired with libusb_try_lock_events() or
+ * libusb_lock_events(). Releasing this lock will wake up any threads blocked
+ * on libusb_wait_for_event().
+ *
+ * \param ctx the context to operate on, or NULL for the default context
+ * \ref libusb_mtasync
+ */
+void API_EXPORTED libusb_unlock_events(libusb_context *ctx)
+{
+ USBI_GET_CONTEXT(ctx);
+ ctx->event_handler_active = 0;
+ usbi_mutex_unlock(&ctx->events_lock);
+
+ /* FIXME: perhaps we should be a bit more efficient by not broadcasting
+ * the availability of the events lock when we are modifying pollfds
+ * (check ctx->device_close)? */
+ usbi_mutex_lock(&ctx->event_waiters_lock);
+ usbi_cond_broadcast(&ctx->event_waiters_cond);
+ usbi_mutex_unlock(&ctx->event_waiters_lock);
+}
+
+/** \ingroup libusb_poll
+ * Determine if it is still OK for this thread to be doing event handling.
+ *
+ * Sometimes, libusb needs to temporarily pause all event handlers, and this
+ * is the function you should use before polling file descriptors to see if
+ * this is the case.
+ *
+ * If this function instructs your thread to give up the events lock, you
+ * should just continue the usual logic that is documented in \ref libusb_mtasync.
+ * On the next iteration, your thread will fail to obtain the events lock,
+ * and will hence become an event waiter.
+ *
+ * This function should be called while the events lock is held: you don't
+ * need to worry about the results of this function if your thread is not
+ * the current event handler.
+ *
+ * \param ctx the context to operate on, or NULL for the default context
+ * \returns 1 if event handling can start or continue
+ * \returns 0 if this thread must give up the events lock
+ * \ref fullstory "Multi-threaded I/O: the full story"
+ */
+int API_EXPORTED libusb_event_handling_ok(libusb_context *ctx)
+{
+ unsigned int r;
+ USBI_GET_CONTEXT(ctx);
+
+ /* is someone else waiting to close a device? if so, don't let this thread
+ * continue event handling */
+ usbi_mutex_lock(&ctx->event_data_lock);
+ r = ctx->device_close;
+ usbi_mutex_unlock(&ctx->event_data_lock);
+ if (r) {
+ usbi_dbg("someone else is closing a device");
+ return 0;
+ }
+
+ return 1;
+}
+
+
+/** \ingroup libusb_poll
+ * Determine if an active thread is handling events (i.e. if anyone is holding
+ * the event handling lock).
+ *
+ * \param ctx the context to operate on, or NULL for the default context
+ * \returns 1 if a thread is handling events
+ * \returns 0 if there are no threads currently handling events
+ * \ref libusb_mtasync
+ */
+int API_EXPORTED libusb_event_handler_active(libusb_context *ctx)
+{
+ unsigned int r;
+ USBI_GET_CONTEXT(ctx);
+
+ /* is someone else waiting to close a device? if so, don't let this thread
+ * start event handling -- indicate that event handling is happening */
+ usbi_mutex_lock(&ctx->event_data_lock);
+ r = ctx->device_close;
+ usbi_mutex_unlock(&ctx->event_data_lock);
+ if (r) {
+ usbi_dbg("someone else is closing a device");
+ return 1;
+ }
+
+ return ctx->event_handler_active;
+}
+
+/** \ingroup libusb_poll
+ * Interrupt any active thread that is handling events. This is mainly useful
+ * for interrupting a dedicated event handling thread when an application
+ * wishes to call libusb_exit().
+ *
+ * Since version 1.0.21, \ref LIBUSB_API_VERSION >= 0x01000105
+ *
+ * \param ctx the context to operate on, or NULL for the default context
+ * \ref libusb_mtasync
+ */
+void API_EXPORTED libusb_interrupt_event_handler(libusb_context *ctx)
+{
+ int pending_events;
+ USBI_GET_CONTEXT(ctx);
+
+ usbi_dbg("");
+ usbi_mutex_lock(&ctx->event_data_lock);
+
+ pending_events = usbi_pending_events(ctx);
+ ctx->event_flags |= USBI_EVENT_USER_INTERRUPT;
+ if (!pending_events)
+ usbi_signal_event(ctx);
+
+ usbi_mutex_unlock(&ctx->event_data_lock);
+}
+
+/** \ingroup libusb_poll
+ * Acquire the event waiters lock. This lock is designed to be obtained under
+ * the situation where you want to be aware when events are completed, but
+ * some other thread is event handling so calling libusb_handle_events() is not
+ * allowed.
+ *
+ * You then obtain this lock, re-check that another thread is still handling
+ * events, then call libusb_wait_for_event().
+ *
+ * You only need to use this lock if you are developing an application
+ * which calls poll() or select() on libusb's file descriptors directly,
+ * <b>and</b> may potentially be handling events from 2 threads simultaenously.
+ * If you stick to libusb's event handling loop functions (e.g.
+ * libusb_handle_events()) then you do not need to be concerned with this
+ * locking.
+ *
+ * \param ctx the context to operate on, or NULL for the default context
+ * \ref libusb_mtasync
+ */
+void API_EXPORTED libusb_lock_event_waiters(libusb_context *ctx)
+{
+ USBI_GET_CONTEXT(ctx);
+ usbi_mutex_lock(&ctx->event_waiters_lock);
+}
+
+/** \ingroup libusb_poll
+ * Release the event waiters lock.
+ * \param ctx the context to operate on, or NULL for the default context
+ * \ref libusb_mtasync
+ */
+void API_EXPORTED libusb_unlock_event_waiters(libusb_context *ctx)
+{
+ USBI_GET_CONTEXT(ctx);
+ usbi_mutex_unlock(&ctx->event_waiters_lock);
+}
+
+/** \ingroup libusb_poll
+ * Wait for another thread to signal completion of an event. Must be called
+ * with the event waiters lock held, see libusb_lock_event_waiters().
+ *
+ * This function will block until any of the following conditions are met:
+ * -# The timeout expires
+ * -# A transfer completes
+ * -# A thread releases the event handling lock through libusb_unlock_events()
+ *
+ * Condition 1 is obvious. Condition 2 unblocks your thread <em>after</em>
+ * the callback for the transfer has completed. Condition 3 is important
+ * because it means that the thread that was previously handling events is no
+ * longer doing so, so if any events are to complete, another thread needs to
+ * step up and start event handling.
+ *
+ * This function releases the event waiters lock before putting your thread
+ * to sleep, and reacquires the lock as it is being woken up.
+ *
+ * \param ctx the context to operate on, or NULL for the default context
+ * \param tv maximum timeout for this blocking function. A NULL value
+ * indicates unlimited timeout.
+ * \returns 0 after a transfer completes or another thread stops event handling
+ * \returns 1 if the timeout expired
+ * \ref libusb_mtasync
+ */
+int API_EXPORTED libusb_wait_for_event(libusb_context *ctx, struct timeval *tv)
+{
+ int r;
+
+ USBI_GET_CONTEXT(ctx);
+ if (tv == NULL) {
+ usbi_cond_wait(&ctx->event_waiters_cond, &ctx->event_waiters_lock);
+ return 0;
+ }
+
+ r = usbi_cond_timedwait(&ctx->event_waiters_cond,
+ &ctx->event_waiters_lock, tv);
+
+ if (r < 0)
+ return r;
+ else
+ return (r == ETIMEDOUT);
+}
+
+static void handle_timeout(struct usbi_transfer *itransfer)
+{
+ struct libusb_transfer *transfer =
+ USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ int r;
+
+ itransfer->timeout_flags |= USBI_TRANSFER_TIMEOUT_HANDLED;
+ r = libusb_cancel_transfer(transfer);
+ if (r == LIBUSB_SUCCESS)
+ itransfer->timeout_flags |= USBI_TRANSFER_TIMED_OUT;
+ else
+ usbi_warn(TRANSFER_CTX(transfer),
+ "async cancel failed %d errno=%d", r, errno);
+}
+
+static int handle_timeouts_locked(struct libusb_context *ctx)
+{
+ int r;
+ struct timespec systime_ts;
+ struct timeval systime;
+ struct usbi_transfer *transfer;
+
+ if (list_empty(&ctx->flying_transfers))
+ return 0;
+
+ /* get current time */
+ r = usbi_backend->clock_gettime(USBI_CLOCK_MONOTONIC, &systime_ts);
+ if (r < 0)
+ return r;
+
+ TIMESPEC_TO_TIMEVAL(&systime, &systime_ts);
+
+ /* iterate through flying transfers list, finding all transfers that
+ * have expired timeouts */
+ list_for_each_entry(transfer, &ctx->flying_transfers, list, struct usbi_transfer) {
+ struct timeval *cur_tv = &transfer->timeout;
+
+ /* if we've reached transfers of infinite timeout, we're all done */
+ if (!timerisset(cur_tv))
+ return 0;
+
+ /* ignore timeouts we've already handled */
+ if (transfer->timeout_flags & (USBI_TRANSFER_TIMEOUT_HANDLED | USBI_TRANSFER_OS_HANDLES_TIMEOUT))
+ continue;
+
+ /* if transfer has non-expired timeout, nothing more to do */
+ if ((cur_tv->tv_sec > systime.tv_sec) ||
+ (cur_tv->tv_sec == systime.tv_sec &&
+ cur_tv->tv_usec > systime.tv_usec))
+ return 0;
+
+ /* otherwise, we've got an expired timeout to handle */
+ handle_timeout(transfer);
+ }
+ return 0;
+}
+
+static int handle_timeouts(struct libusb_context *ctx)
+{
+ int r;
+ USBI_GET_CONTEXT(ctx);
+ usbi_mutex_lock(&ctx->flying_transfers_lock);
+ r = handle_timeouts_locked(ctx);
+ usbi_mutex_unlock(&ctx->flying_transfers_lock);
+ return r;
+}
+
+#ifdef USBI_TIMERFD_AVAILABLE
+static int handle_timerfd_trigger(struct libusb_context *ctx)
+{
+ int r;
+
+ usbi_mutex_lock(&ctx->flying_transfers_lock);
+
+ /* process the timeout that just happened */
+ r = handle_timeouts_locked(ctx);
+ if (r < 0)
+ goto out;
+
+ /* arm for next timeout*/
+ r = arm_timerfd_for_next_timeout(ctx);
+
+out:
+ usbi_mutex_unlock(&ctx->flying_transfers_lock);
+ return r;
+}
+#endif
+
+/* do the actual event handling. assumes that no other thread is concurrently
+ * doing the same thing. */
+static int handle_events(struct libusb_context *ctx, struct timeval *tv)
+{
+ int r;
+ struct usbi_pollfd *ipollfd;
+ POLL_NFDS_TYPE nfds = 0;
+ POLL_NFDS_TYPE internal_nfds;
+ 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) */
+ if (usbi_handling_events(ctx))
+ return LIBUSB_ERROR_BUSY;
+ usbi_start_event_handling(ctx);
+
+ /* there are certain fds that libusb uses internally, currently:
+ *
+ * 1) event pipe
+ * 2) timerfd
+ *
+ * the backend will never need to attempt to handle events on these fds, so
+ * we determine how many fds are in use internally for this context and when
+ * handle_events() is called in the backend, the pollfd list and count will
+ * be adjusted to skip over these internal fds */
+ if (usbi_using_timerfd(ctx))
+ internal_nfds = 2;
+ else
+ internal_nfds = 1;
+
+ /* only reallocate the poll fds when the list of poll fds has been modified
+ * since the last poll, otherwise reuse them to save the additional overhead */
+ usbi_mutex_lock(&ctx->event_data_lock);
+ if (ctx->event_flags & USBI_EVENT_POLLFDS_MODIFIED) {
+ usbi_dbg("poll fds modified, reallocating");
+
+ if (ctx->pollfds) {
+ free(ctx->pollfds);
+ ctx->pollfds = NULL;
+ }
+
+ /* sanity check - it is invalid for a context to have fewer than the
+ * required internal fds (memory corruption?) */
+ assert(ctx->pollfds_cnt >= internal_nfds);
+
+ ctx->pollfds = calloc(ctx->pollfds_cnt, sizeof(*ctx->pollfds));
+ if (!ctx->pollfds) {
+ usbi_mutex_unlock(&ctx->event_data_lock);
+ r = LIBUSB_ERROR_NO_MEM;
+ goto done;
+ }
+
+ list_for_each_entry(ipollfd, &ctx->ipollfds, list, struct usbi_pollfd) {
+ struct libusb_pollfd *pollfd = &ipollfd->pollfd;
+ i++;
+ ctx->pollfds[i].fd = pollfd->fd;
+ ctx->pollfds[i].events = pollfd->events;
+ }
+
+ /* reset the flag now that we have the updated list */
+ ctx->event_flags &= ~USBI_EVENT_POLLFDS_MODIFIED;
+
+ /* if no further pending events, clear the event pipe so that we do
+ * not immediately return from poll */
+ if (!usbi_pending_events(ctx))
+ usbi_clear_event(ctx);
+ }
+ fds = ctx->pollfds;
+ nfds = ctx->pollfds_cnt;
+ usbi_mutex_unlock(&ctx->event_data_lock);
+
+ timeout_ms = (int)(tv->tv_sec * 1000) + (tv->tv_usec / 1000);
+
+ /* round up to next millisecond */
+ 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) {
+ r = LIBUSB_ERROR_INTERRUPTED;
+ goto done;
+ }
+ 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 usbi_transfer *itransfer;
+ int ret = 0;
+
+ usbi_dbg("caught a fish on the event pipe");
+
+ /* take the the event data lock while processing events */
+ usbi_mutex_lock(&ctx->event_data_lock);
+
+ /* check if someone added a new poll fd */
+ if (ctx->event_flags & USBI_EVENT_POLLFDS_MODIFIED)
+ usbi_dbg("someone updated the poll fds");
+
+ if (ctx->event_flags & USBI_EVENT_USER_INTERRUPT) {
+ usbi_dbg("someone purposely interrupted");
+ ctx->event_flags &= ~USBI_EVENT_USER_INTERRUPT;
+ }
+
+ /* check if someone is closing a device */
+ if (ctx->device_close)
+ usbi_dbg("someone is closing a device");
+
+ /* 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);
+ }
+
+ /* complete any pending transfers */
+ while (ret == 0 && !list_empty(&ctx->completed_transfers)) {
+ 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);
+ if (ret)
+ usbi_err(ctx, "backend handle_transfer_completion failed with error %d", ret);
+ usbi_mutex_lock(&ctx->event_data_lock);
+ }
+
+ /* if no further pending events, clear the event pipe */
+ if (!usbi_pending_events(ctx))
+ usbi_clear_event(ctx);
+
+ usbi_mutex_unlock(&ctx->event_data_lock);
+
+ /* process the hotplug message, if any */
+ if (message) {
+ 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);
+
+ free(message);
+ }
+
+ if (ret) {
+ /* return error code */
+ r = ret;
+ goto done;
+ }
+
+ if (0 == --r)
+ goto handled;
+ }
+
+#ifdef USBI_TIMERFD_AVAILABLE
+ /* on timerfd configurations, fds[1] is the timerfd */
+ if (usbi_using_timerfd(ctx) && fds[1].revents) {
+ /* timerfd indicates that a timeout has expired */
+ int ret;
+ usbi_dbg("timerfd triggered");
+ special_event = 1;
+
+ ret = handle_timerfd_trigger(ctx);
+ if (ret < 0) {
+ /* return error code */
+ r = ret;
+ goto done;
+ }
+
+ if (0 == --r)
+ goto handled;
+ }
+#endif
+
+ 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;
+}
+
+/* returns the smallest of:
+ * 1. timeout of next URB
+ * 2. user-supplied timeout
+ * returns 1 if there is an already-expired timeout, otherwise returns 0
+ * and populates out
+ */
+static int get_next_timeout(libusb_context *ctx, struct timeval *tv,
+ struct timeval *out)
+{
+ struct timeval timeout;
+ int r = libusb_get_next_timeout(ctx, &timeout);
+ if (r) {
+ /* timeout already expired? */
+ if (!timerisset(&timeout))
+ return 1;
+
+ /* choose the smallest of next URB timeout or user specified timeout */
+ if (timercmp(&timeout, tv, <))
+ *out = timeout;
+ else
+ *out = *tv;
+ } else {
+ *out = *tv;
+ }
+ return 0;
+}
+
+/** \ingroup libusb_poll
+ * Handle any pending events.
+ *
+ * libusb determines "pending events" by checking if any timeouts have expired
+ * and by checking the set of file descriptors for activity.
+ *
+ * If a zero timeval is passed, this function will handle any already-pending
+ * events and then immediately return in non-blocking style.
+ *
+ * If a non-zero timeval is passed and no events are currently pending, this
+ * function will block waiting for events to handle up until the specified
+ * timeout. If an event arrives or a signal is raised, this function will
+ * return early.
+ *
+ * If the parameter completed is not NULL then <em>after obtaining the event
+ * handling lock</em> this function will return immediately if the integer
+ * pointed to is not 0. This allows for race free waiting for the completion
+ * of a specific transfer.
+ *
+ * \param ctx the context to operate on, or NULL for the default context
+ * \param tv the maximum time to block waiting for events, or an all zero
+ * timeval struct for non-blocking mode
+ * \param completed pointer to completion integer to check, or NULL
+ * \returns 0 on success, or a LIBUSB_ERROR code on failure
+ * \ref libusb_mtasync
+ */
+int API_EXPORTED libusb_handle_events_timeout_completed(libusb_context *ctx,
+ struct timeval *tv, int *completed)
+{
+ int r;
+ struct timeval poll_timeout;
+
+ USBI_GET_CONTEXT(ctx);
+ r = get_next_timeout(ctx, tv, &poll_timeout);
+ if (r) {
+ /* timeout already expired */
+ return handle_timeouts(ctx);
+ }
+
+retry:
+ if (libusb_try_lock_events(ctx) == 0) {
+ if (completed == NULL || !*completed) {
+ /* we obtained the event lock: do our own event handling */
+ usbi_dbg("doing our own event handling");
+ r = handle_events(ctx, &poll_timeout);
+ }
+ libusb_unlock_events(ctx);
+ return r;
+ }
+
+ /* another thread is doing event handling. wait for thread events that
+ * notify event completion. */
+ libusb_lock_event_waiters(ctx);
+
+ if (completed && *completed)
+ goto already_done;
+
+ if (!libusb_event_handler_active(ctx)) {
+ /* we hit a race: whoever was event handling earlier finished in the
+ * time it took us to reach this point. try the cycle again. */
+ libusb_unlock_event_waiters(ctx);
+ usbi_dbg("event handler was active but went away, retrying");
+ goto retry;
+ }
+
+ usbi_dbg("another thread is doing event handling");
+ r = libusb_wait_for_event(ctx, &poll_timeout);
+
+already_done:
+ libusb_unlock_event_waiters(ctx);
+
+ if (r < 0)
+ return r;
+ else if (r == 1)
+ return handle_timeouts(ctx);
+ else
+ return 0;
+}
+
+/** \ingroup libusb_poll
+ * Handle any pending events
+ *
+ * Like libusb_handle_events_timeout_completed(), but without the completed
+ * parameter, calling this function is equivalent to calling
+ * libusb_handle_events_timeout_completed() with a NULL completed parameter.
+ *
+ * This function is kept primarily for backwards compatibility.
+ * All new code should call libusb_handle_events_completed() or
+ * libusb_handle_events_timeout_completed() to avoid race conditions.
+ *
+ * \param ctx the context to operate on, or NULL for the default context
+ * \param tv the maximum time to block waiting for events, or an all zero
+ * timeval struct for non-blocking mode
+ * \returns 0 on success, or a LIBUSB_ERROR code on failure
+ */
+int API_EXPORTED libusb_handle_events_timeout(libusb_context *ctx,
+ struct timeval *tv)
+{
+ return libusb_handle_events_timeout_completed(ctx, tv, NULL);
+}
+
+/** \ingroup libusb_poll
+ * Handle any pending events in blocking mode. There is currently a timeout
+ * hardcoded at 60 seconds but we plan to make it unlimited in future. For
+ * finer control over whether this function is blocking or non-blocking, or
+ * for control over the timeout, use libusb_handle_events_timeout_completed()
+ * instead.
+ *
+ * This function is kept primarily for backwards compatibility.
+ * All new code should call libusb_handle_events_completed() or
+ * libusb_handle_events_timeout_completed() to avoid race conditions.
+ *
+ * \param ctx the context to operate on, or NULL for the default context
+ * \returns 0 on success, or a LIBUSB_ERROR code on failure
+ */
+int API_EXPORTED libusb_handle_events(libusb_context *ctx)
+{
+ struct timeval tv;
+ tv.tv_sec = 60;
+ tv.tv_usec = 0;
+ return libusb_handle_events_timeout_completed(ctx, &tv, NULL);
+}
+
+/** \ingroup libusb_poll
+ * Handle any pending events in blocking mode.
+ *
+ * Like libusb_handle_events(), with the addition of a completed parameter
+ * to allow for race free waiting for the completion of a specific transfer.
+ *
+ * See libusb_handle_events_timeout_completed() for details on the completed
+ * parameter.
+ *
+ * \param ctx the context to operate on, or NULL for the default context
+ * \param completed pointer to completion integer to check, or NULL
+ * \returns 0 on success, or a LIBUSB_ERROR code on failure
+ * \ref libusb_mtasync
+ */
+int API_EXPORTED libusb_handle_events_completed(libusb_context *ctx,
+ int *completed)
+{
+ struct timeval tv;
+ tv.tv_sec = 60;
+ tv.tv_usec = 0;
+ return libusb_handle_events_timeout_completed(ctx, &tv, completed);
+}
+
+/** \ingroup libusb_poll
+ * Handle any pending events by polling file descriptors, without checking if
+ * any other threads are already doing so. Must be called with the event lock
+ * held, see libusb_lock_events().
+ *
+ * This function is designed to be called under the situation where you have
+ * taken the event lock and are calling poll()/select() directly on libusb's
+ * file descriptors (as opposed to using libusb_handle_events() or similar).
+ * You detect events on libusb's descriptors, so you then call this function
+ * with a zero timeout value (while still holding the event lock).
+ *
+ * \param ctx the context to operate on, or NULL for the default context
+ * \param tv the maximum time to block waiting for events, or zero for
+ * non-blocking mode
+ * \returns 0 on success, or a LIBUSB_ERROR code on failure
+ * \ref libusb_mtasync
+ */
+int API_EXPORTED libusb_handle_events_locked(libusb_context *ctx,
+ struct timeval *tv)
+{
+ int r;
+ struct timeval poll_timeout;
+
+ USBI_GET_CONTEXT(ctx);
+ r = get_next_timeout(ctx, tv, &poll_timeout);
+ if (r) {
+ /* timeout already expired */
+ return handle_timeouts(ctx);
+ }
+
+ return handle_events(ctx, &poll_timeout);
+}
+
+/** \ingroup libusb_poll
+ * Determines whether your application must apply special timing considerations
+ * when monitoring libusb's file descriptors.
+ *
+ * This function is only useful for applications which retrieve and poll
+ * libusb's file descriptors in their own main loop (\ref libusb_pollmain).
+ *
+ * Ordinarily, libusb's event handler needs to be called into at specific
+ * moments in time (in addition to times when there is activity on the file
+ * descriptor set). The usual approach is to use libusb_get_next_timeout()
+ * to learn about when the next timeout occurs, and to adjust your
+ * poll()/select() timeout accordingly so that you can make a call into the
+ * library at that time.
+ *
+ * Some platforms supported by libusb do not come with this baggage - any
+ * events relevant to timing will be represented by activity on the file
+ * descriptor set, and libusb_get_next_timeout() will always return 0.
+ * This function allows you to detect whether you are running on such a
+ * platform.
+ *
+ * Since v1.0.5.
+ *
+ * \param ctx the context to operate on, or NULL for the default context
+ * \returns 0 if you must call into libusb at times determined by
+ * libusb_get_next_timeout(), or 1 if all timeout events are handled internally
+ * or through regular activity on the file descriptors.
+ * \ref libusb_pollmain "Polling libusb file descriptors for event handling"
+ */
+int API_EXPORTED libusb_pollfds_handle_timeouts(libusb_context *ctx)
+{
+#if defined(USBI_TIMERFD_AVAILABLE)
+ USBI_GET_CONTEXT(ctx);
+ return usbi_using_timerfd(ctx);
+#else
+ UNUSED(ctx);
+ return 0;
+#endif
+}
+
+/** \ingroup libusb_poll
+ * Determine the next internal timeout that libusb needs to handle. You only
+ * need to use this function if you are calling poll() or select() or similar
+ * on libusb's file descriptors yourself - you do not need to use it if you
+ * are calling libusb_handle_events() or a variant directly.
+ *
+ * You should call this function in your main loop in order to determine how
+ * long to wait for select() or poll() to return results. libusb needs to be
+ * called into at this timeout, so you should use it as an upper bound on
+ * your select() or poll() call.
+ *
+ * When the timeout has expired, call into libusb_handle_events_timeout()
+ * (perhaps in non-blocking mode) so that libusb can handle the timeout.
+ *
+ * This function may return 1 (success) and an all-zero timeval. If this is
+ * the case, it indicates that libusb has a timeout that has already expired
+ * so you should call libusb_handle_events_timeout() or similar immediately.
+ * A return code of 0 indicates that there are no pending timeouts.
+ *
+ * On some platforms, this function will always returns 0 (no pending
+ * timeouts). See \ref polltime.
+ *
+ * \param ctx the context to operate on, or NULL for the default context
+ * \param tv output location for a relative time against the current
+ * clock in which libusb must be called into in order to process timeout events
+ * \returns 0 if there are no pending timeouts, 1 if a timeout was returned,
+ * or LIBUSB_ERROR_OTHER on failure
+ */
+int API_EXPORTED libusb_get_next_timeout(libusb_context *ctx,
+ struct timeval *tv)
+{
+ struct usbi_transfer *transfer;
+ struct timespec cur_ts;
+ struct timeval cur_tv;
+ struct timeval next_timeout = { 0, 0 };
+ int r;
+
+ USBI_GET_CONTEXT(ctx);
+ if (usbi_using_timerfd(ctx))
+ return 0;
+
+ usbi_mutex_lock(&ctx->flying_transfers_lock);
+ if (list_empty(&ctx->flying_transfers)) {
+ usbi_mutex_unlock(&ctx->flying_transfers_lock);
+ usbi_dbg("no URBs, no timeout!");
+ return 0;
+ }
+
+ /* find next transfer which hasn't already been processed as timed out */
+ list_for_each_entry(transfer, &ctx->flying_transfers, list, struct usbi_transfer) {
+ if (transfer->timeout_flags & (USBI_TRANSFER_TIMEOUT_HANDLED | USBI_TRANSFER_OS_HANDLES_TIMEOUT))
+ continue;
+
+ /* if we've reached transfers of infinte timeout, we're done looking */
+ if (!timerisset(&transfer->timeout))
+ break;
+
+ next_timeout = transfer->timeout;
+ break;
+ }
+ usbi_mutex_unlock(&ctx->flying_transfers_lock);
+
+ if (!timerisset(&next_timeout)) {
+ usbi_dbg("no URB with timeout or all handled by OS; no timeout!");
+ return 0;
+ }
+
+ 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;
+ }
+ TIMESPEC_TO_TIMEVAL(&cur_tv, &cur_ts);
+
+ if (!timercmp(&cur_tv, &next_timeout, <)) {
+ usbi_dbg("first timeout already expired");
+ timerclear(tv);
+ } else {
+ timersub(&next_timeout, &cur_tv, tv);
+ usbi_dbg("next timeout in %d.%06ds", tv->tv_sec, tv->tv_usec);
+ }
+
+ return 1;
+}
+
+/** \ingroup libusb_poll
+ * Register notification functions for file descriptor additions/removals.
+ * These functions will be invoked for every new or removed file descriptor
+ * that libusb uses as an event source.
+ *
+ * To remove notifiers, pass NULL values for the function pointers.
+ *
+ * Note that file descriptors may have been added even before you register
+ * these notifiers (e.g. at libusb_init() time).
+ *
+ * Additionally, note that the removal notifier may be called during
+ * libusb_exit() (e.g. when it is closing file descriptors that were opened
+ * and added to the poll set at libusb_init() time). If you don't want this,
+ * remove the notifiers immediately before calling libusb_exit().
+ *
+ * \param ctx the context to operate on, or NULL for the default context
+ * \param added_cb pointer to function for addition notifications
+ * \param removed_cb pointer to function for removal notifications
+ * \param user_data User data to be passed back to callbacks (useful for
+ * passing context information)
+ */
+void API_EXPORTED libusb_set_pollfd_notifiers(libusb_context *ctx,
+ libusb_pollfd_added_cb added_cb, libusb_pollfd_removed_cb removed_cb,
+ void *user_data)
+{
+ USBI_GET_CONTEXT(ctx);
+ ctx->fd_added_cb = added_cb;
+ ctx->fd_removed_cb = removed_cb;
+ ctx->fd_cb_user_data = user_data;
+}
+
+/*
+ * Interrupt the iteration of the event handling thread, so that it picks
+ * up the fd change. Callers of this function must hold the event_data_lock.
+ */
+static void usbi_fd_notification(struct libusb_context *ctx)
+{
+ int pending_events;
+
+ /* Record that there is a new poll fd.
+ * Only signal an event if there are no prior pending events. */
+ pending_events = usbi_pending_events(ctx);
+ ctx->event_flags |= USBI_EVENT_POLLFDS_MODIFIED;
+ if (!pending_events)
+ usbi_signal_event(ctx);
+}
+
+/* Add a file descriptor to the list of file descriptors to be monitored.
+ * events should be specified as a bitmask of events passed to poll(), e.g.
+ * POLLIN and/or POLLOUT. */
+int usbi_add_pollfd(struct libusb_context *ctx, int fd, short events)
+{
+ struct usbi_pollfd *ipollfd = malloc(sizeof(*ipollfd));
+ if (!ipollfd)
+ return LIBUSB_ERROR_NO_MEM;
+
+ usbi_dbg("add fd %d events %d", fd, events);
+ ipollfd->pollfd.fd = fd;
+ ipollfd->pollfd.events = events;
+ usbi_mutex_lock(&ctx->event_data_lock);
+ list_add_tail(&ipollfd->list, &ctx->ipollfds);
+ ctx->pollfds_cnt++;
+ usbi_fd_notification(ctx);
+ usbi_mutex_unlock(&ctx->event_data_lock);
+
+ if (ctx->fd_added_cb)
+ ctx->fd_added_cb(fd, events, ctx->fd_cb_user_data);
+ return 0;
+}
+
+/* Remove a file descriptor from the list of file descriptors to be polled. */
+void usbi_remove_pollfd(struct libusb_context *ctx, int fd)
+{
+ struct usbi_pollfd *ipollfd;
+ int found = 0;
+
+ usbi_dbg("remove fd %d", fd);
+ usbi_mutex_lock(&ctx->event_data_lock);
+ list_for_each_entry(ipollfd, &ctx->ipollfds, list, struct usbi_pollfd)
+ if (ipollfd->pollfd.fd == fd) {
+ found = 1;
+ break;
+ }
+
+ if (!found) {
+ usbi_dbg("couldn't find fd %d to remove", fd);
+ usbi_mutex_unlock(&ctx->event_data_lock);
+ return;
+ }
+
+ list_del(&ipollfd->list);
+ ctx->pollfds_cnt--;
+ usbi_fd_notification(ctx);
+ usbi_mutex_unlock(&ctx->event_data_lock);
+ free(ipollfd);
+ if (ctx->fd_removed_cb)
+ ctx->fd_removed_cb(fd, ctx->fd_cb_user_data);
+}
+
+/** \ingroup libusb_poll
+ * Retrieve a list of file descriptors that should be polled by your main loop
+ * as libusb event sources.
+ *
+ * The returned list is NULL-terminated and should be freed with libusb_free_pollfds()
+ * when done. The actual list contents must not be touched.
+ *
+ * As file descriptors are a Unix-specific concept, this function is not
+ * available on Windows and will always return NULL.
+ *
+ * \param ctx the context to operate on, or NULL for the default context
+ * \returns a NULL-terminated list of libusb_pollfd structures
+ * \returns NULL on error
+ * \returns NULL on platforms where the functionality is not available
+ */
+DEFAULT_VISIBILITY
+const struct libusb_pollfd ** LIBUSB_CALL libusb_get_pollfds(
+ libusb_context *ctx)
+{
+#ifndef OS_WINDOWS
+ struct libusb_pollfd **ret = NULL;
+ struct usbi_pollfd *ipollfd;
+ size_t i = 0;
+ USBI_GET_CONTEXT(ctx);
+
+ usbi_mutex_lock(&ctx->event_data_lock);
+
+ ret = calloc(ctx->pollfds_cnt + 1, sizeof(struct libusb_pollfd *));
+ if (!ret)
+ goto out;
+
+ list_for_each_entry(ipollfd, &ctx->ipollfds, list, struct usbi_pollfd)
+ ret[i++] = (struct libusb_pollfd *) ipollfd;
+ ret[ctx->pollfds_cnt] = NULL;
+
+out:
+ usbi_mutex_unlock(&ctx->event_data_lock);
+ return (const struct libusb_pollfd **) ret;
+#else
+ usbi_err(ctx, "external polling of libusb's internal descriptors "\
+ "is not yet supported on Windows platforms");
+ return NULL;
+#endif
+}
+
+/** \ingroup libusb_poll
+ * Free a list of libusb_pollfd structures. This should be called for all
+ * pollfd lists allocated with libusb_get_pollfds().
+ *
+ * Since version 1.0.20, \ref LIBUSB_API_VERSION >= 0x01000104
+ *
+ * It is legal to call this function with a NULL pollfd list. In this case,
+ * the function will simply return safely.
+ *
+ * \param pollfds the list of libusb_pollfd structures to free
+ */
+void API_EXPORTED libusb_free_pollfds(const struct libusb_pollfd **pollfds)
+{
+ if (!pollfds)
+ return;
+
+ free((void *)pollfds);
+}
+
+/* Backends may call this from handle_events to report disconnection of a
+ * device. This function ensures transfers get cancelled appropriately.
+ * Callers of this function must hold the events_lock.
+ */
+void usbi_handle_disconnect(struct libusb_device_handle *dev_handle)
+{
+ struct usbi_transfer *cur;
+ struct usbi_transfer *to_cancel;
+
+ usbi_dbg("device %d.%d",
+ dev_handle->dev->bus_number, dev_handle->dev->device_address);
+
+ /* terminate all pending transfers with the LIBUSB_TRANSFER_NO_DEVICE
+ * status code.
+ *
+ * when we find a transfer for this device on the list, there are two
+ * possible scenarios:
+ * 1. the transfer is currently in-flight, in which case we terminate the
+ * transfer here
+ * 2. the transfer has been added to the flying transfer list by
+ * libusb_submit_transfer, has failed to submit and
+ * libusb_submit_transfer is waiting for us to release the
+ * flying_transfers_lock to remove it, so we ignore it
+ */
+
+ while (1) {
+ to_cancel = NULL;
+ usbi_mutex_lock(&HANDLE_CTX(dev_handle)->flying_transfers_lock);
+ list_for_each_entry(cur, &HANDLE_CTX(dev_handle)->flying_transfers, list, struct usbi_transfer)
+ if (USBI_TRANSFER_TO_LIBUSB_TRANSFER(cur)->dev_handle == dev_handle) {
+ usbi_mutex_lock(&cur->lock);
+ if (cur->state_flags & USBI_TRANSFER_IN_FLIGHT)
+ to_cancel = cur;
+ usbi_mutex_unlock(&cur->lock);
+
+ if (to_cancel)
+ break;
+ }
+ usbi_mutex_unlock(&HANDLE_CTX(dev_handle)->flying_transfers_lock);
+
+ if (!to_cancel)
+ break;
+
+ usbi_dbg("cancelling transfer %p from disconnect",
+ USBI_TRANSFER_TO_LIBUSB_TRANSFER(to_cancel));
+
+ usbi_mutex_lock(&to_cancel->lock);
+ usbi_backend->clear_transfer_priv(to_cancel);
+ usbi_mutex_unlock(&to_cancel->lock);
+ usbi_handle_transfer_completion(to_cancel, LIBUSB_TRANSFER_NO_DEVICE);
+ }
+
+}
diff --git a/vendor/github.com/karalabe/gousb/internal/libusb/libusb/libusb.h b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/libusb.h
new file mode 100644
index 000000000..c562690f9
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/libusb.h
@@ -0,0 +1,2008 @@
+/*
+ * Public libusb header file
+ * Copyright © 2001 Johannes Erdfelt <johannes@erdfelt.com>
+ * Copyright © 2007-2008 Daniel Drake <dsd@gentoo.org>
+ * Copyright © 2012 Pete Batard <pete@akeo.ie>
+ * Copyright © 2012 Nathan Hjelm <hjelmn@cs.unm.edu>
+ * For more information, please visit: http://libusb.info
+ *
+ * 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_H
+#define LIBUSB_H
+
+#ifdef _MSC_VER
+/* on MS environments, the inline keyword is available in C++ only */
+#if !defined(__cplusplus)
+#define inline __inline
+#endif
+/* ssize_t is also not available (copy/paste from MinGW) */
+#ifndef _SSIZE_T_DEFINED
+#define _SSIZE_T_DEFINED
+#undef ssize_t
+#ifdef _WIN64
+ typedef __int64 ssize_t;
+#else
+ typedef int ssize_t;
+#endif /* _WIN64 */
+#endif /* _SSIZE_T_DEFINED */
+#endif /* _MSC_VER */
+
+/* stdint.h is not available on older MSVC */
+#if defined(_MSC_VER) && (_MSC_VER < 1600) && (!defined(_STDINT)) && (!defined(_STDINT_H))
+typedef unsigned __int8 uint8_t;
+typedef unsigned __int16 uint16_t;
+typedef unsigned __int32 uint32_t;
+#else
+#include <stdint.h>
+#endif
+
+#if !defined(_WIN32_WCE)
+#include <sys/types.h>
+#endif
+
+#if defined(__linux) || defined(__APPLE__) || defined(__CYGWIN__) || defined(__HAIKU__)
+#include <sys/time.h>
+#endif
+
+#include <time.h>
+#include <limits.h>
+
+/* 'interface' might be defined as a macro on Windows, so we need to
+ * undefine it so as not to break the current libusb API, because
+ * libusb_config_descriptor has an 'interface' member
+ * As this can be problematic if you include windows.h after libusb.h
+ * in your sources, we force windows.h to be included first. */
+#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE)
+#include <windows.h>
+#if defined(interface)
+#undef interface
+#endif
+#if !defined(__CYGWIN__)
+#include <winsock.h>
+#endif
+#endif
+
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
+#define LIBUSB_DEPRECATED_FOR(f) \
+ __attribute__((deprecated("Use " #f " instead")))
+#else
+#define LIBUSB_DEPRECATED_FOR(f)
+#endif /* __GNUC__ */
+
+/** \def LIBUSB_CALL
+ * \ingroup libusb_misc
+ * libusb's Windows calling convention.
+ *
+ * Under Windows, the selection of available compilers and configurations
+ * means that, unlike other platforms, there is not <em>one true calling
+ * convention</em> (calling convention: the manner in which parameters are
+ * passed to functions in the generated assembly code).
+ *
+ * Matching the Windows API itself, libusb uses the WINAPI convention (which
+ * translates to the <tt>stdcall</tt> convention) and guarantees that the
+ * library is compiled in this way. The public header file also includes
+ * appropriate annotations so that your own software will use the right
+ * convention, even if another convention is being used by default within
+ * your codebase.
+ *
+ * The one consideration that you must apply in your software is to mark
+ * all functions which you use as libusb callbacks with this LIBUSB_CALL
+ * annotation, so that they too get compiled for the correct calling
+ * convention.
+ *
+ * On non-Windows operating systems, this macro is defined as nothing. This
+ * means that you can apply it to your code without worrying about
+ * cross-platform compatibility.
+ */
+/* LIBUSB_CALL must be defined on both definition and declaration of libusb
+ * functions. You'd think that declaration would be enough, but cygwin will
+ * complain about conflicting types unless both are marked this way.
+ * The placement of this macro is important too; it must appear after the
+ * return type, before the function name. See internal documentation for
+ * API_EXPORTED.
+ */
+#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE)
+#define LIBUSB_CALL WINAPI
+#else
+#define LIBUSB_CALL
+#endif
+
+/** \def LIBUSB_API_VERSION
+ * \ingroup libusb_misc
+ * libusb's API version.
+ *
+ * Since version 1.0.13, to help with feature detection, libusb defines
+ * a LIBUSB_API_VERSION macro that gets increased every time there is a
+ * significant change to the API, such as the introduction of a new call,
+ * the definition of a new macro/enum member, or any other element that
+ * libusb applications may want to detect at compilation time.
+ *
+ * The macro is typically used in an application as follows:
+ * \code
+ * #if defined(LIBUSB_API_VERSION) && (LIBUSB_API_VERSION >= 0x01001234)
+ * // Use one of the newer features from the libusb API
+ * #endif
+ * \endcode
+ *
+ * Internally, LIBUSB_API_VERSION is defined as follows:
+ * (libusb major << 24) | (libusb minor << 16) | (16 bit incremental)
+ */
+#define LIBUSB_API_VERSION 0x01000105
+
+/* The following is kept for compatibility, but will be deprecated in the future */
+#define LIBUSBX_API_VERSION LIBUSB_API_VERSION
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \ingroup libusb_misc
+ * Convert a 16-bit value from host-endian to little-endian format. On
+ * little endian systems, this function does nothing. On big endian systems,
+ * the bytes are swapped.
+ * \param x the host-endian value to convert
+ * \returns the value in little-endian byte order
+ */
+static inline uint16_t libusb_cpu_to_le16(const uint16_t x)
+{
+ union {
+ uint8_t b8[2];
+ uint16_t b16;
+ } _tmp;
+ _tmp.b8[1] = (uint8_t) (x >> 8);
+ _tmp.b8[0] = (uint8_t) (x & 0xff);
+ return _tmp.b16;
+}
+
+/** \def libusb_le16_to_cpu
+ * \ingroup libusb_misc
+ * Convert a 16-bit value from little-endian to host-endian format. On
+ * little endian systems, this function does nothing. On big endian systems,
+ * the bytes are swapped.
+ * \param x the little-endian value to convert
+ * \returns the value in host-endian byte order
+ */
+#define libusb_le16_to_cpu libusb_cpu_to_le16
+
+/* standard USB stuff */
+
+/** \ingroup libusb_desc
+ * Device and/or Interface Class codes */
+enum libusb_class_code {
+ /** In the context of a \ref libusb_device_descriptor "device descriptor",
+ * this bDeviceClass value indicates that each interface specifies its
+ * own class information and all interfaces operate independently.
+ */
+ LIBUSB_CLASS_PER_INTERFACE = 0,
+
+ /** Audio class */
+ LIBUSB_CLASS_AUDIO = 1,
+
+ /** Communications class */
+ LIBUSB_CLASS_COMM = 2,
+
+ /** Human Interface Device class */
+ LIBUSB_CLASS_HID = 3,
+
+ /** Physical */
+ LIBUSB_CLASS_PHYSICAL = 5,
+
+ /** Printer class */
+ LIBUSB_CLASS_PRINTER = 7,
+
+ /** Image class */
+ LIBUSB_CLASS_PTP = 6, /* legacy name from libusb-0.1 usb.h */
+ LIBUSB_CLASS_IMAGE = 6,
+
+ /** Mass storage class */
+ LIBUSB_CLASS_MASS_STORAGE = 8,
+
+ /** Hub class */
+ LIBUSB_CLASS_HUB = 9,
+
+ /** Data class */
+ LIBUSB_CLASS_DATA = 10,
+
+ /** Smart Card */
+ LIBUSB_CLASS_SMART_CARD = 0x0b,
+
+ /** Content Security */
+ LIBUSB_CLASS_CONTENT_SECURITY = 0x0d,
+
+ /** Video */
+ LIBUSB_CLASS_VIDEO = 0x0e,
+
+ /** Personal Healthcare */
+ LIBUSB_CLASS_PERSONAL_HEALTHCARE = 0x0f,
+
+ /** Diagnostic Device */
+ LIBUSB_CLASS_DIAGNOSTIC_DEVICE = 0xdc,
+
+ /** Wireless class */
+ LIBUSB_CLASS_WIRELESS = 0xe0,
+
+ /** Application class */
+ LIBUSB_CLASS_APPLICATION = 0xfe,
+
+ /** Class is vendor-specific */
+ LIBUSB_CLASS_VENDOR_SPEC = 0xff
+};
+
+/** \ingroup libusb_desc
+ * Descriptor types as defined by the USB specification. */
+enum libusb_descriptor_type {
+ /** Device descriptor. See libusb_device_descriptor. */
+ LIBUSB_DT_DEVICE = 0x01,
+
+ /** Configuration descriptor. See libusb_config_descriptor. */
+ LIBUSB_DT_CONFIG = 0x02,
+
+ /** String descriptor */
+ LIBUSB_DT_STRING = 0x03,
+
+ /** Interface descriptor. See libusb_interface_descriptor. */
+ LIBUSB_DT_INTERFACE = 0x04,
+
+ /** Endpoint descriptor. See libusb_endpoint_descriptor. */
+ LIBUSB_DT_ENDPOINT = 0x05,
+
+ /** BOS descriptor */
+ LIBUSB_DT_BOS = 0x0f,
+
+ /** Device Capability descriptor */
+ LIBUSB_DT_DEVICE_CAPABILITY = 0x10,
+
+ /** HID descriptor */
+ LIBUSB_DT_HID = 0x21,
+
+ /** HID report descriptor */
+ LIBUSB_DT_REPORT = 0x22,
+
+ /** Physical descriptor */
+ LIBUSB_DT_PHYSICAL = 0x23,
+
+ /** Hub descriptor */
+ LIBUSB_DT_HUB = 0x29,
+
+ /** SuperSpeed Hub descriptor */
+ LIBUSB_DT_SUPERSPEED_HUB = 0x2a,
+
+ /** SuperSpeed Endpoint Companion descriptor */
+ LIBUSB_DT_SS_ENDPOINT_COMPANION = 0x30
+};
+
+/* Descriptor sizes per descriptor type */
+#define LIBUSB_DT_DEVICE_SIZE 18
+#define LIBUSB_DT_CONFIG_SIZE 9
+#define LIBUSB_DT_INTERFACE_SIZE 9
+#define LIBUSB_DT_ENDPOINT_SIZE 7
+#define LIBUSB_DT_ENDPOINT_AUDIO_SIZE 9 /* Audio extension */
+#define LIBUSB_DT_HUB_NONVAR_SIZE 7
+#define LIBUSB_DT_SS_ENDPOINT_COMPANION_SIZE 6
+#define LIBUSB_DT_BOS_SIZE 5
+#define LIBUSB_DT_DEVICE_CAPABILITY_SIZE 3
+
+/* BOS descriptor sizes */
+#define LIBUSB_BT_USB_2_0_EXTENSION_SIZE 7
+#define LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE 10
+#define LIBUSB_BT_CONTAINER_ID_SIZE 20
+
+/* We unwrap the BOS => define its max size */
+#define LIBUSB_DT_BOS_MAX_SIZE ((LIBUSB_DT_BOS_SIZE) +\
+ (LIBUSB_BT_USB_2_0_EXTENSION_SIZE) +\
+ (LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE) +\
+ (LIBUSB_BT_CONTAINER_ID_SIZE))
+
+#define LIBUSB_ENDPOINT_ADDRESS_MASK 0x0f /* in bEndpointAddress */
+#define LIBUSB_ENDPOINT_DIR_MASK 0x80
+
+/** \ingroup libusb_desc
+ * Endpoint direction. Values for bit 7 of the
+ * \ref libusb_endpoint_descriptor::bEndpointAddress "endpoint address" scheme.
+ */
+enum libusb_endpoint_direction {
+ /** In: device-to-host */
+ LIBUSB_ENDPOINT_IN = 0x80,
+
+ /** Out: host-to-device */
+ LIBUSB_ENDPOINT_OUT = 0x00
+};
+
+#define LIBUSB_TRANSFER_TYPE_MASK 0x03 /* in bmAttributes */
+
+/** \ingroup libusb_desc
+ * Endpoint transfer type. Values for bits 0:1 of the
+ * \ref libusb_endpoint_descriptor::bmAttributes "endpoint attributes" field.
+ */
+enum libusb_transfer_type {
+ /** Control endpoint */
+ LIBUSB_TRANSFER_TYPE_CONTROL = 0,
+
+ /** Isochronous endpoint */
+ LIBUSB_TRANSFER_TYPE_ISOCHRONOUS = 1,
+
+ /** Bulk endpoint */
+ LIBUSB_TRANSFER_TYPE_BULK = 2,
+
+ /** Interrupt endpoint */
+ LIBUSB_TRANSFER_TYPE_INTERRUPT = 3,
+
+ /** Stream endpoint */
+ LIBUSB_TRANSFER_TYPE_BULK_STREAM = 4,
+};
+
+/** \ingroup libusb_misc
+ * Standard requests, as defined in table 9-5 of the USB 3.0 specifications */
+enum libusb_standard_request {
+ /** Request status of the specific recipient */
+ LIBUSB_REQUEST_GET_STATUS = 0x00,
+
+ /** Clear or disable a specific feature */
+ LIBUSB_REQUEST_CLEAR_FEATURE = 0x01,
+
+ /* 0x02 is reserved */
+
+ /** Set or enable a specific feature */
+ LIBUSB_REQUEST_SET_FEATURE = 0x03,
+
+ /* 0x04 is reserved */
+
+ /** Set device address for all future accesses */
+ LIBUSB_REQUEST_SET_ADDRESS = 0x05,
+
+ /** Get the specified descriptor */
+ LIBUSB_REQUEST_GET_DESCRIPTOR = 0x06,
+
+ /** Used to update existing descriptors or add new descriptors */
+ LIBUSB_REQUEST_SET_DESCRIPTOR = 0x07,
+
+ /** Get the current device configuration value */
+ LIBUSB_REQUEST_GET_CONFIGURATION = 0x08,
+
+ /** Set device configuration */
+ LIBUSB_REQUEST_SET_CONFIGURATION = 0x09,
+
+ /** Return the selected alternate setting for the specified interface */
+ LIBUSB_REQUEST_GET_INTERFACE = 0x0A,
+
+ /** Select an alternate interface for the specified interface */
+ LIBUSB_REQUEST_SET_INTERFACE = 0x0B,
+
+ /** Set then report an endpoint's synchronization frame */
+ LIBUSB_REQUEST_SYNCH_FRAME = 0x0C,
+
+ /** Sets both the U1 and U2 Exit Latency */
+ LIBUSB_REQUEST_SET_SEL = 0x30,
+
+ /** Delay from the time a host transmits a packet to the time it is
+ * received by the device. */
+ LIBUSB_SET_ISOCH_DELAY = 0x31,
+};
+
+/** \ingroup libusb_misc
+ * Request type bits of the
+ * \ref libusb_control_setup::bmRequestType "bmRequestType" field in control
+ * transfers. */
+enum libusb_request_type {
+ /** Standard */
+ LIBUSB_REQUEST_TYPE_STANDARD = (0x00 << 5),
+
+ /** Class */
+ LIBUSB_REQUEST_TYPE_CLASS = (0x01 << 5),
+
+ /** Vendor */
+ LIBUSB_REQUEST_TYPE_VENDOR = (0x02 << 5),
+
+ /** Reserved */
+ LIBUSB_REQUEST_TYPE_RESERVED = (0x03 << 5)
+};
+
+/** \ingroup libusb_misc
+ * Recipient bits of the
+ * \ref libusb_control_setup::bmRequestType "bmRequestType" field in control
+ * transfers. Values 4 through 31 are reserved. */
+enum libusb_request_recipient {
+ /** Device */
+ LIBUSB_RECIPIENT_DEVICE = 0x00,
+
+ /** Interface */
+ LIBUSB_RECIPIENT_INTERFACE = 0x01,
+
+ /** Endpoint */
+ LIBUSB_RECIPIENT_ENDPOINT = 0x02,
+
+ /** Other */
+ LIBUSB_RECIPIENT_OTHER = 0x03,
+};
+
+#define LIBUSB_ISO_SYNC_TYPE_MASK 0x0C
+
+/** \ingroup libusb_desc
+ * Synchronization type for isochronous endpoints. Values for bits 2:3 of the
+ * \ref libusb_endpoint_descriptor::bmAttributes "bmAttributes" field in
+ * libusb_endpoint_descriptor.
+ */
+enum libusb_iso_sync_type {
+ /** No synchronization */
+ LIBUSB_ISO_SYNC_TYPE_NONE = 0,
+
+ /** Asynchronous */
+ LIBUSB_ISO_SYNC_TYPE_ASYNC = 1,
+
+ /** Adaptive */
+ LIBUSB_ISO_SYNC_TYPE_ADAPTIVE = 2,
+
+ /** Synchronous */
+ LIBUSB_ISO_SYNC_TYPE_SYNC = 3
+};
+
+#define LIBUSB_ISO_USAGE_TYPE_MASK 0x30
+
+/** \ingroup libusb_desc
+ * Usage type for isochronous endpoints. Values for bits 4:5 of the
+ * \ref libusb_endpoint_descriptor::bmAttributes "bmAttributes" field in
+ * libusb_endpoint_descriptor.
+ */
+enum libusb_iso_usage_type {
+ /** Data endpoint */
+ LIBUSB_ISO_USAGE_TYPE_DATA = 0,
+
+ /** Feedback endpoint */
+ LIBUSB_ISO_USAGE_TYPE_FEEDBACK = 1,
+
+ /** Implicit feedback Data endpoint */
+ LIBUSB_ISO_USAGE_TYPE_IMPLICIT = 2,
+};
+
+/** \ingroup libusb_desc
+ * A structure representing the standard USB device descriptor. This
+ * descriptor is documented in section 9.6.1 of the USB 3.0 specification.
+ * All multiple-byte fields are represented in host-endian format.
+ */
+struct libusb_device_descriptor {
+ /** Size of this descriptor (in bytes) */
+ uint8_t bLength;
+
+ /** Descriptor type. Will have value
+ * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE LIBUSB_DT_DEVICE in this
+ * context. */
+ uint8_t bDescriptorType;
+
+ /** USB specification release number in binary-coded decimal. A value of
+ * 0x0200 indicates USB 2.0, 0x0110 indicates USB 1.1, etc. */
+ uint16_t bcdUSB;
+
+ /** USB-IF class code for the device. See \ref libusb_class_code. */
+ uint8_t bDeviceClass;
+
+ /** USB-IF subclass code for the device, qualified by the bDeviceClass
+ * value */
+ uint8_t bDeviceSubClass;
+
+ /** USB-IF protocol code for the device, qualified by the bDeviceClass and
+ * bDeviceSubClass values */
+ uint8_t bDeviceProtocol;
+
+ /** Maximum packet size for endpoint 0 */
+ uint8_t bMaxPacketSize0;
+
+ /** USB-IF vendor ID */
+ uint16_t idVendor;
+
+ /** USB-IF product ID */
+ uint16_t idProduct;
+
+ /** Device release number in binary-coded decimal */
+ uint16_t bcdDevice;
+
+ /** Index of string descriptor describing manufacturer */
+ uint8_t iManufacturer;
+
+ /** Index of string descriptor describing product */
+ uint8_t iProduct;
+
+ /** Index of string descriptor containing device serial number */
+ uint8_t iSerialNumber;
+
+ /** Number of possible configurations */
+ uint8_t bNumConfigurations;
+};
+
+/** \ingroup libusb_desc
+ * A structure representing the standard USB endpoint descriptor. This
+ * descriptor is documented in section 9.6.6 of the USB 3.0 specification.
+ * All multiple-byte fields are represented in host-endian format.
+ */
+struct libusb_endpoint_descriptor {
+ /** Size of this descriptor (in bytes) */
+ uint8_t bLength;
+
+ /** Descriptor type. Will have value
+ * \ref libusb_descriptor_type::LIBUSB_DT_ENDPOINT LIBUSB_DT_ENDPOINT in
+ * this context. */
+ uint8_t bDescriptorType;
+
+ /** The address of the endpoint described by this descriptor. Bits 0:3 are
+ * the endpoint number. Bits 4:6 are reserved. Bit 7 indicates direction,
+ * see \ref libusb_endpoint_direction.
+ */
+ uint8_t bEndpointAddress;
+
+ /** Attributes which apply to the endpoint when it is configured using
+ * the bConfigurationValue. Bits 0:1 determine the transfer type and
+ * correspond to \ref libusb_transfer_type. Bits 2:3 are only used for
+ * isochronous endpoints and correspond to \ref libusb_iso_sync_type.
+ * Bits 4:5 are also only used for isochronous endpoints and correspond to
+ * \ref libusb_iso_usage_type. Bits 6:7 are reserved.
+ */
+ uint8_t bmAttributes;
+
+ /** Maximum packet size this endpoint is capable of sending/receiving. */
+ uint16_t wMaxPacketSize;
+
+ /** Interval for polling endpoint for data transfers. */
+ uint8_t bInterval;
+
+ /** For audio devices only: the rate at which synchronization feedback
+ * is provided. */
+ uint8_t bRefresh;
+
+ /** For audio devices only: the address if the synch endpoint */
+ uint8_t bSynchAddress;
+
+ /** Extra descriptors. If libusb encounters unknown endpoint descriptors,
+ * it will store them here, should you wish to parse them. */
+ const unsigned char *extra;
+
+ /** Length of the extra descriptors, in bytes. */
+ int extra_length;
+};
+
+/** \ingroup libusb_desc
+ * A structure representing the standard USB interface descriptor. This
+ * descriptor is documented in section 9.6.5 of the USB 3.0 specification.
+ * All multiple-byte fields are represented in host-endian format.
+ */
+struct libusb_interface_descriptor {
+ /** Size of this descriptor (in bytes) */
+ uint8_t bLength;
+
+ /** Descriptor type. Will have value
+ * \ref libusb_descriptor_type::LIBUSB_DT_INTERFACE LIBUSB_DT_INTERFACE
+ * in this context. */
+ uint8_t bDescriptorType;
+
+ /** Number of this interface */
+ uint8_t bInterfaceNumber;
+
+ /** Value used to select this alternate setting for this interface */
+ uint8_t bAlternateSetting;
+
+ /** Number of endpoints used by this interface (excluding the control
+ * endpoint). */
+ uint8_t bNumEndpoints;
+
+ /** USB-IF class code for this interface. See \ref libusb_class_code. */
+ uint8_t bInterfaceClass;
+
+ /** USB-IF subclass code for this interface, qualified by the
+ * bInterfaceClass value */
+ uint8_t bInterfaceSubClass;
+
+ /** USB-IF protocol code for this interface, qualified by the
+ * bInterfaceClass and bInterfaceSubClass values */
+ uint8_t bInterfaceProtocol;
+
+ /** Index of string descriptor describing this interface */
+ uint8_t iInterface;
+
+ /** Array of endpoint descriptors. This length of this array is determined
+ * by the bNumEndpoints field. */
+ const struct libusb_endpoint_descriptor *endpoint;
+
+ /** Extra descriptors. If libusb encounters unknown interface descriptors,
+ * it will store them here, should you wish to parse them. */
+ const unsigned char *extra;
+
+ /** Length of the extra descriptors, in bytes. */
+ int extra_length;
+};
+
+/** \ingroup libusb_desc
+ * A collection of alternate settings for a particular USB interface.
+ */
+struct libusb_interface {
+ /** Array of interface descriptors. The length of this array is determined
+ * by the num_altsetting field. */
+ const struct libusb_interface_descriptor *altsetting;
+
+ /** The number of alternate settings that belong to this interface */
+ int num_altsetting;
+};
+
+/** \ingroup libusb_desc
+ * A structure representing the standard USB configuration descriptor. This
+ * descriptor is documented in section 9.6.3 of the USB 3.0 specification.
+ * All multiple-byte fields are represented in host-endian format.
+ */
+struct libusb_config_descriptor {
+ /** Size of this descriptor (in bytes) */
+ uint8_t bLength;
+
+ /** Descriptor type. Will have value
+ * \ref libusb_descriptor_type::LIBUSB_DT_CONFIG LIBUSB_DT_CONFIG
+ * in this context. */
+ uint8_t bDescriptorType;
+
+ /** Total length of data returned for this configuration */
+ uint16_t wTotalLength;
+
+ /** Number of interfaces supported by this configuration */
+ uint8_t bNumInterfaces;
+
+ /** Identifier value for this configuration */
+ uint8_t bConfigurationValue;
+
+ /** Index of string descriptor describing this configuration */
+ uint8_t iConfiguration;
+
+ /** Configuration characteristics */
+ uint8_t bmAttributes;
+
+ /** Maximum power consumption of the USB device from this bus in this
+ * configuration when the device is fully operation. Expressed in units
+ * of 2 mA when the device is operating in high-speed mode and in units
+ * of 8 mA when the device is operating in super-speed mode. */
+ uint8_t MaxPower;
+
+ /** Array of interfaces supported by this configuration. The length of
+ * this array is determined by the bNumInterfaces field. */
+ const struct libusb_interface *interface;
+
+ /** Extra descriptors. If libusb encounters unknown configuration
+ * descriptors, it will store them here, should you wish to parse them. */
+ const unsigned char *extra;
+
+ /** Length of the extra descriptors, in bytes. */
+ int extra_length;
+};
+
+/** \ingroup libusb_desc
+ * A structure representing the superspeed endpoint companion
+ * descriptor. This descriptor is documented in section 9.6.7 of
+ * the USB 3.0 specification. All multiple-byte fields are represented in
+ * host-endian format.
+ */
+struct libusb_ss_endpoint_companion_descriptor {
+
+ /** Size of this descriptor (in bytes) */
+ uint8_t bLength;
+
+ /** Descriptor type. Will have value
+ * \ref libusb_descriptor_type::LIBUSB_DT_SS_ENDPOINT_COMPANION in
+ * this context. */
+ uint8_t bDescriptorType;
+
+
+ /** The maximum number of packets the endpoint can send or
+ * receive as part of a burst. */
+ uint8_t bMaxBurst;
+
+ /** In bulk EP: bits 4:0 represents the maximum number of
+ * streams the EP supports. In isochronous EP: bits 1:0
+ * represents the Mult - a zero based value that determines
+ * the maximum number of packets within a service interval */
+ uint8_t bmAttributes;
+
+ /** The total number of bytes this EP will transfer every
+ * service interval. valid only for periodic EPs. */
+ uint16_t wBytesPerInterval;
+};
+
+/** \ingroup libusb_desc
+ * A generic representation of a BOS Device Capability descriptor. It is
+ * advised to check bDevCapabilityType and call the matching
+ * libusb_get_*_descriptor function to get a structure fully matching the type.
+ */
+struct libusb_bos_dev_capability_descriptor {
+ /** Size of this descriptor (in bytes) */
+ uint8_t bLength;
+ /** Descriptor type. Will have value
+ * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE_CAPABILITY
+ * LIBUSB_DT_DEVICE_CAPABILITY in this context. */
+ uint8_t bDescriptorType;
+ /** 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
+ ;
+};
+
+/** \ingroup libusb_desc
+ * A structure representing the Binary Device Object Store (BOS) descriptor.
+ * This descriptor is documented in section 9.6.2 of the USB 3.0 specification.
+ * All multiple-byte fields are represented in host-endian format.
+ */
+struct libusb_bos_descriptor {
+ /** Size of this descriptor (in bytes) */
+ uint8_t bLength;
+
+ /** Descriptor type. Will have value
+ * \ref libusb_descriptor_type::LIBUSB_DT_BOS LIBUSB_DT_BOS
+ * in this context. */
+ uint8_t bDescriptorType;
+
+ /** Length of this descriptor and all of its sub descriptors */
+ uint16_t wTotalLength;
+
+ /** The number of separate device capability descriptors in
+ * the BOS */
+ 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
+ ;
+};
+
+/** \ingroup libusb_desc
+ * A structure representing the USB 2.0 Extension descriptor
+ * This descriptor is documented in section 9.6.2.1 of the USB 3.0 specification.
+ * All multiple-byte fields are represented in host-endian format.
+ */
+struct libusb_usb_2_0_extension_descriptor {
+ /** Size of this descriptor (in bytes) */
+ uint8_t bLength;
+
+ /** Descriptor type. Will have value
+ * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE_CAPABILITY
+ * LIBUSB_DT_DEVICE_CAPABILITY in this context. */
+ uint8_t bDescriptorType;
+
+ /** Capability type. Will have value
+ * \ref libusb_capability_type::LIBUSB_BT_USB_2_0_EXTENSION
+ * LIBUSB_BT_USB_2_0_EXTENSION in this context. */
+ uint8_t bDevCapabilityType;
+
+ /** Bitmap encoding of supported device level features.
+ * A value of one in a bit location indicates a feature is
+ * supported; a value of zero indicates it is not supported.
+ * See \ref libusb_usb_2_0_extension_attributes. */
+ uint32_t bmAttributes;
+};
+
+/** \ingroup libusb_desc
+ * A structure representing the SuperSpeed USB Device Capability descriptor
+ * This descriptor is documented in section 9.6.2.2 of the USB 3.0 specification.
+ * All multiple-byte fields are represented in host-endian format.
+ */
+struct libusb_ss_usb_device_capability_descriptor {
+ /** Size of this descriptor (in bytes) */
+ uint8_t bLength;
+
+ /** Descriptor type. Will have value
+ * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE_CAPABILITY
+ * LIBUSB_DT_DEVICE_CAPABILITY in this context. */
+ uint8_t bDescriptorType;
+
+ /** Capability type. Will have value
+ * \ref libusb_capability_type::LIBUSB_BT_SS_USB_DEVICE_CAPABILITY
+ * LIBUSB_BT_SS_USB_DEVICE_CAPABILITY in this context. */
+ uint8_t bDevCapabilityType;
+
+ /** Bitmap encoding of supported device level features.
+ * A value of one in a bit location indicates a feature is
+ * supported; a value of zero indicates it is not supported.
+ * See \ref libusb_ss_usb_device_capability_attributes. */
+ uint8_t bmAttributes;
+
+ /** Bitmap encoding of the speed supported by this device when
+ * operating in SuperSpeed mode. See \ref libusb_supported_speed. */
+ uint16_t wSpeedSupported;
+
+ /** The lowest speed at which all the functionality supported
+ * by the device is available to the user. For example if the
+ * device supports all its functionality when connected at
+ * full speed and above then it sets this value to 1. */
+ uint8_t bFunctionalitySupport;
+
+ /** U1 Device Exit Latency. */
+ uint8_t bU1DevExitLat;
+
+ /** U2 Device Exit Latency. */
+ uint16_t bU2DevExitLat;
+};
+
+/** \ingroup libusb_desc
+ * A structure representing the Container ID descriptor.
+ * This descriptor is documented in section 9.6.2.3 of the USB 3.0 specification.
+ * All multiple-byte fields, except UUIDs, are represented in host-endian format.
+ */
+struct libusb_container_id_descriptor {
+ /** Size of this descriptor (in bytes) */
+ uint8_t bLength;
+
+ /** Descriptor type. Will have value
+ * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE_CAPABILITY
+ * LIBUSB_DT_DEVICE_CAPABILITY in this context. */
+ uint8_t bDescriptorType;
+
+ /** Capability type. Will have value
+ * \ref libusb_capability_type::LIBUSB_BT_CONTAINER_ID
+ * LIBUSB_BT_CONTAINER_ID in this context. */
+ uint8_t bDevCapabilityType;
+
+ /** Reserved field */
+ uint8_t bReserved;
+
+ /** 128 bit UUID */
+ uint8_t ContainerID[16];
+};
+
+/** \ingroup libusb_asyncio
+ * Setup packet for control transfers. */
+struct libusb_control_setup {
+ /** Request type. Bits 0:4 determine recipient, see
+ * \ref libusb_request_recipient. Bits 5:6 determine type, see
+ * \ref libusb_request_type. Bit 7 determines data transfer direction, see
+ * \ref libusb_endpoint_direction.
+ */
+ uint8_t bmRequestType;
+
+ /** Request. If the type bits of bmRequestType are equal to
+ * \ref libusb_request_type::LIBUSB_REQUEST_TYPE_STANDARD
+ * "LIBUSB_REQUEST_TYPE_STANDARD" then this field refers to
+ * \ref libusb_standard_request. For other cases, use of this field is
+ * application-specific. */
+ uint8_t bRequest;
+
+ /** Value. Varies according to request */
+ uint16_t wValue;
+
+ /** Index. Varies according to request, typically used to pass an index
+ * or offset */
+ uint16_t wIndex;
+
+ /** Number of bytes to transfer */
+ uint16_t wLength;
+};
+
+#define LIBUSB_CONTROL_SETUP_SIZE (sizeof(struct libusb_control_setup))
+
+/* libusb */
+
+struct libusb_context;
+struct libusb_device;
+struct libusb_device_handle;
+
+/** \ingroup libusb_lib
+ * Structure providing the version of the libusb runtime
+ */
+struct libusb_version {
+ /** Library major version. */
+ const uint16_t major;
+
+ /** Library minor version. */
+ const uint16_t minor;
+
+ /** Library micro version. */
+ const uint16_t micro;
+
+ /** Library nano version. */
+ const uint16_t nano;
+
+ /** Library release candidate suffix string, e.g. "-rc4". */
+ const char *rc;
+
+ /** For ABI compatibility only. */
+ const char* describe;
+};
+
+/** \ingroup libusb_lib
+ * Structure representing a libusb session. The concept of individual libusb
+ * 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_exit() will not destroy resources that the other user is still
+ * using.
+ *
+ * Sessions are created by libusb_init() and destroyed through libusb_exit().
+ * If your application is guaranteed to only ever include a single libusb
+ * user (i.e. you), you do not have to worry about contexts: pass NULL in
+ * every function call where a context is required. The default context
+ * will be used.
+ *
+ * For more information, see \ref libusb_contexts.
+ */
+typedef struct libusb_context libusb_context;
+
+/** \ingroup libusb_dev
+ * Structure representing a USB device detected on the system. This is an
+ * opaque type for which you are only ever provided with a pointer, usually
+ * originating from libusb_get_device_list().
+ *
+ * Certain operations can be performed on a device, but in order to do any
+ * I/O you will have to first obtain a device handle using libusb_open().
+ *
+ * Devices are reference counted with libusb_ref_device() and
+ * libusb_unref_device(), and are freed when the reference count reaches 0.
+ * New devices presented by libusb_get_device_list() have a reference count of
+ * 1, and libusb_free_device_list() can optionally decrease the reference count
+ * on all devices in the list. libusb_open() adds another reference which is
+ * later destroyed by libusb_close().
+ */
+typedef struct libusb_device libusb_device;
+
+
+/** \ingroup libusb_dev
+ * Structure representing a handle on a USB device. This is an opaque type for
+ * which you are only ever provided with a pointer, usually originating from
+ * libusb_open().
+ *
+ * A device handle is used to perform I/O and other operations. When finished
+ * with a device handle, you should call libusb_close().
+ */
+typedef struct libusb_device_handle libusb_device_handle;
+
+/** \ingroup libusb_dev
+ * Speed codes. Indicates the speed at which the device is operating.
+ */
+enum libusb_speed {
+ /** The OS doesn't report or know the device speed. */
+ LIBUSB_SPEED_UNKNOWN = 0,
+
+ /** The device is operating at low speed (1.5MBit/s). */
+ LIBUSB_SPEED_LOW = 1,
+
+ /** The device is operating at full speed (12MBit/s). */
+ LIBUSB_SPEED_FULL = 2,
+
+ /** The device is operating at high speed (480MBit/s). */
+ LIBUSB_SPEED_HIGH = 3,
+
+ /** The device is operating at super speed (5000MBit/s). */
+ LIBUSB_SPEED_SUPER = 4,
+};
+
+/** \ingroup libusb_dev
+ * Supported speeds (wSpeedSupported) bitfield. Indicates what
+ * speeds the device supports.
+ */
+enum libusb_supported_speed {
+ /** Low speed operation supported (1.5MBit/s). */
+ LIBUSB_LOW_SPEED_OPERATION = 1,
+
+ /** Full speed operation supported (12MBit/s). */
+ LIBUSB_FULL_SPEED_OPERATION = 2,
+
+ /** High speed operation supported (480MBit/s). */
+ LIBUSB_HIGH_SPEED_OPERATION = 4,
+
+ /** Superspeed operation supported (5000MBit/s). */
+ LIBUSB_SUPER_SPEED_OPERATION = 8,
+};
+
+/** \ingroup libusb_dev
+ * Masks for the bits of the
+ * \ref libusb_usb_2_0_extension_descriptor::bmAttributes "bmAttributes" field
+ * of the USB 2.0 Extension descriptor.
+ */
+enum libusb_usb_2_0_extension_attributes {
+ /** Supports Link Power Management (LPM) */
+ LIBUSB_BM_LPM_SUPPORT = 2,
+};
+
+/** \ingroup libusb_dev
+ * Masks for the bits of the
+ * \ref libusb_ss_usb_device_capability_descriptor::bmAttributes "bmAttributes" field
+ * field of the SuperSpeed USB Device Capability descriptor.
+ */
+enum libusb_ss_usb_device_capability_attributes {
+ /** Supports Latency Tolerance Messages (LTM) */
+ LIBUSB_BM_LTM_SUPPORT = 2,
+};
+
+/** \ingroup libusb_dev
+ * USB capability types
+ */
+enum libusb_bos_type {
+ /** Wireless USB device capability */
+ LIBUSB_BT_WIRELESS_USB_DEVICE_CAPABILITY = 1,
+
+ /** USB 2.0 extensions */
+ LIBUSB_BT_USB_2_0_EXTENSION = 2,
+
+ /** SuperSpeed USB device capability */
+ LIBUSB_BT_SS_USB_DEVICE_CAPABILITY = 3,
+
+ /** Container ID type */
+ LIBUSB_BT_CONTAINER_ID = 4,
+};
+
+/** \ingroup libusb_misc
+ * Error codes. Most libusb functions return 0 on success or one of these
+ * codes on failure.
+ * You can call libusb_error_name() to retrieve a string representation of an
+ * error code or libusb_strerror() to get an end-user suitable description of
+ * an error code.
+ */
+enum libusb_error {
+ /** Success (no error) */
+ LIBUSB_SUCCESS = 0,
+
+ /** Input/output error */
+ LIBUSB_ERROR_IO = -1,
+
+ /** Invalid parameter */
+ LIBUSB_ERROR_INVALID_PARAM = -2,
+
+ /** Access denied (insufficient permissions) */
+ LIBUSB_ERROR_ACCESS = -3,
+
+ /** No such device (it may have been disconnected) */
+ LIBUSB_ERROR_NO_DEVICE = -4,
+
+ /** Entity not found */
+ LIBUSB_ERROR_NOT_FOUND = -5,
+
+ /** Resource busy */
+ LIBUSB_ERROR_BUSY = -6,
+
+ /** Operation timed out */
+ LIBUSB_ERROR_TIMEOUT = -7,
+
+ /** Overflow */
+ LIBUSB_ERROR_OVERFLOW = -8,
+
+ /** Pipe error */
+ LIBUSB_ERROR_PIPE = -9,
+
+ /** System call interrupted (perhaps due to signal) */
+ LIBUSB_ERROR_INTERRUPTED = -10,
+
+ /** Insufficient memory */
+ LIBUSB_ERROR_NO_MEM = -11,
+
+ /** Operation not supported or unimplemented on this platform */
+ LIBUSB_ERROR_NOT_SUPPORTED = -12,
+
+ /* NB: Remember to update LIBUSB_ERROR_COUNT below as well as the
+ message strings in strerror.c when adding new error codes here. */
+
+ /** Other error */
+ LIBUSB_ERROR_OTHER = -99,
+};
+
+/* Total number of error codes in enum libusb_error */
+#define LIBUSB_ERROR_COUNT 14
+
+/** \ingroup libusb_asyncio
+ * Transfer status codes */
+enum libusb_transfer_status {
+ /** Transfer completed without error. Note that this does not indicate
+ * that the entire amount of requested data was transferred. */
+ LIBUSB_TRANSFER_COMPLETED,
+
+ /** Transfer failed */
+ LIBUSB_TRANSFER_ERROR,
+
+ /** Transfer timed out */
+ LIBUSB_TRANSFER_TIMED_OUT,
+
+ /** Transfer was cancelled */
+ LIBUSB_TRANSFER_CANCELLED,
+
+ /** For bulk/interrupt endpoints: halt condition detected (endpoint
+ * stalled). For control endpoints: control request not supported. */
+ LIBUSB_TRANSFER_STALL,
+
+ /** Device was disconnected */
+ LIBUSB_TRANSFER_NO_DEVICE,
+
+ /** Device sent more data than requested */
+ LIBUSB_TRANSFER_OVERFLOW,
+
+ /* NB! Remember to update libusb_error_name()
+ when adding new status codes here. */
+};
+
+/** \ingroup libusb_asyncio
+ * libusb_transfer.flags values */
+enum libusb_transfer_flags {
+ /** Report short frames as errors */
+ LIBUSB_TRANSFER_SHORT_NOT_OK = 1<<0,
+
+ /** Automatically free() transfer buffer during libusb_free_transfer().
+ * Note that buffers allocated with libusb_dev_mem_alloc() should not
+ * be attempted freed in this way, since free() is not an appropriate
+ * way to release such memory. */
+ LIBUSB_TRANSFER_FREE_BUFFER = 1<<1,
+
+ /** Automatically call libusb_free_transfer() after callback returns.
+ * If this flag is set, it is illegal to call libusb_free_transfer()
+ * from your transfer callback, as this will result in a double-free
+ * when this flag is acted upon. */
+ LIBUSB_TRANSFER_FREE_TRANSFER = 1<<2,
+
+ /** Terminate transfers that are a multiple of the endpoint's
+ * wMaxPacketSize with an extra zero length packet. This is useful
+ * when a device protocol mandates that each logical request is
+ * terminated by an incomplete packet (i.e. the logical requests are
+ * not separated by other means).
+ *
+ * This flag only affects host-to-device transfers to bulk and interrupt
+ * endpoints. In other situations, it is ignored.
+ *
+ * This flag only affects transfers with a length that is a multiple of
+ * the endpoint's wMaxPacketSize. On transfers of other lengths, this
+ * flag has no effect. Therefore, if you are working with a device that
+ * needs a ZLP whenever the end of the logical request falls on a packet
+ * boundary, then it is sensible to set this flag on <em>every</em>
+ * transfer (you do not have to worry about only setting it on transfers
+ * that end on the boundary).
+ *
+ * This flag is currently only supported on Linux.
+ * On other systems, libusb_submit_transfer() will return
+ * LIBUSB_ERROR_NOT_SUPPORTED for every transfer where this flag is set.
+ *
+ * Available since libusb-1.0.9.
+ */
+ LIBUSB_TRANSFER_ADD_ZERO_PACKET = 1 << 3,
+};
+
+/** \ingroup libusb_asyncio
+ * Isochronous packet descriptor. */
+struct libusb_iso_packet_descriptor {
+ /** Length of data to request in this packet */
+ unsigned int length;
+
+ /** Amount of data that was actually transferred */
+ unsigned int actual_length;
+
+ /** Status code for this packet */
+ enum libusb_transfer_status status;
+};
+
+struct libusb_transfer;
+
+/** \ingroup libusb_asyncio
+ * Asynchronous transfer callback function type. When submitting asynchronous
+ * transfers, you pass a pointer to a callback function of this type via the
+ * \ref libusb_transfer::callback "callback" member of the libusb_transfer
+ * structure. libusb will call this function later, when the transfer has
+ * completed or failed. See \ref libusb_asyncio for more information.
+ * \param transfer The libusb_transfer struct the callback function is being
+ * notified about.
+ */
+typedef void (LIBUSB_CALL *libusb_transfer_cb_fn)(struct libusb_transfer *transfer);
+
+/** \ingroup libusb_asyncio
+ * The generic USB transfer structure. The user populates this structure and
+ * then submits it in order to request a transfer. After the transfer has
+ * completed, the library populates the transfer with the results and passes
+ * it back to the user.
+ */
+struct libusb_transfer {
+ /** Handle of the device that this transfer will be submitted to */
+ libusb_device_handle *dev_handle;
+
+ /** A bitwise OR combination of \ref libusb_transfer_flags. */
+ uint8_t flags;
+
+ /** Address of the endpoint where this transfer will be sent. */
+ unsigned char endpoint;
+
+ /** Type of the endpoint from \ref libusb_transfer_type */
+ unsigned char type;
+
+ /** Timeout for this transfer in milliseconds. A value of 0 indicates no
+ * timeout. */
+ unsigned int timeout;
+
+ /** The status of the transfer. Read-only, and only for use within
+ * transfer callback function.
+ *
+ * If this is an isochronous transfer, this field may read COMPLETED even
+ * if there were errors in the frames. Use the
+ * \ref libusb_iso_packet_descriptor::status "status" field in each packet
+ * to determine if errors occurred. */
+ enum libusb_transfer_status status;
+
+ /** Length of the data buffer */
+ int length;
+
+ /** Actual length of data that was transferred. Read-only, and only for
+ * use within transfer callback function. Not valid for isochronous
+ * endpoint transfers. */
+ int actual_length;
+
+ /** Callback function. This will be invoked when the transfer completes,
+ * fails, or is cancelled. */
+ libusb_transfer_cb_fn callback;
+
+ /** User context data to pass to the callback function. */
+ void *user_data;
+
+ /** Data buffer */
+ unsigned char *buffer;
+
+ /** Number of isochronous packets. Only used for I/O with isochronous
+ * endpoints. */
+ 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
+ ;
+};
+
+/** \ingroup libusb_misc
+ * Capabilities supported by an instance of libusb on the current running
+ * platform. Test if the loaded library supports a given capability by calling
+ * \ref libusb_has_capability().
+ */
+enum libusb_capability {
+ /** The libusb_has_capability() API is available. */
+ LIBUSB_CAP_HAS_CAPABILITY = 0x0000,
+ /** Hotplug support is available on this platform. */
+ LIBUSB_CAP_HAS_HOTPLUG = 0x0001,
+ /** The library can access HID devices without requiring user intervention.
+ * Note that before being able to actually access an HID device, you may
+ * still have to call additional libusb functions such as
+ * \ref libusb_detach_kernel_driver(). */
+ LIBUSB_CAP_HAS_HID_ACCESS = 0x0100,
+ /** The library supports detaching of the default USB driver, using
+ * \ref libusb_detach_kernel_driver(), if one is set by the OS kernel */
+ LIBUSB_CAP_SUPPORTS_DETACH_KERNEL_DRIVER = 0x0101
+};
+
+/** \ingroup libusb_lib
+ * Log message levels.
+ * - 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
+ */
+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,
+};
+
+int LIBUSB_CALL libusb_init(libusb_context **ctx);
+void LIBUSB_CALL libusb_exit(libusb_context *ctx);
+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);
+const char * LIBUSB_CALL libusb_error_name(int errcode);
+int LIBUSB_CALL libusb_setlocale(const char *locale);
+const char * LIBUSB_CALL libusb_strerror(enum libusb_error errcode);
+
+ssize_t LIBUSB_CALL libusb_get_device_list(libusb_context *ctx,
+ libusb_device ***list);
+void LIBUSB_CALL libusb_free_device_list(libusb_device **list,
+ int unref_devices);
+libusb_device * LIBUSB_CALL libusb_ref_device(libusb_device *dev);
+void LIBUSB_CALL libusb_unref_device(libusb_device *dev);
+
+int LIBUSB_CALL libusb_get_configuration(libusb_device_handle *dev,
+ int *config);
+int LIBUSB_CALL libusb_get_device_descriptor(libusb_device *dev,
+ struct libusb_device_descriptor *desc);
+int LIBUSB_CALL libusb_get_active_config_descriptor(libusb_device *dev,
+ struct libusb_config_descriptor **config);
+int LIBUSB_CALL libusb_get_config_descriptor(libusb_device *dev,
+ uint8_t config_index, struct libusb_config_descriptor **config);
+int LIBUSB_CALL libusb_get_config_descriptor_by_value(libusb_device *dev,
+ uint8_t bConfigurationValue, struct libusb_config_descriptor **config);
+void LIBUSB_CALL libusb_free_config_descriptor(
+ struct libusb_config_descriptor *config);
+int LIBUSB_CALL libusb_get_ss_endpoint_companion_descriptor(
+ struct libusb_context *ctx,
+ const struct libusb_endpoint_descriptor *endpoint,
+ struct libusb_ss_endpoint_companion_descriptor **ep_comp);
+void LIBUSB_CALL libusb_free_ss_endpoint_companion_descriptor(
+ struct libusb_ss_endpoint_companion_descriptor *ep_comp);
+int LIBUSB_CALL libusb_get_bos_descriptor(libusb_device_handle *dev_handle,
+ struct libusb_bos_descriptor **bos);
+void LIBUSB_CALL libusb_free_bos_descriptor(struct libusb_bos_descriptor *bos);
+int LIBUSB_CALL libusb_get_usb_2_0_extension_descriptor(
+ struct libusb_context *ctx,
+ struct libusb_bos_dev_capability_descriptor *dev_cap,
+ struct libusb_usb_2_0_extension_descriptor **usb_2_0_extension);
+void LIBUSB_CALL libusb_free_usb_2_0_extension_descriptor(
+ struct libusb_usb_2_0_extension_descriptor *usb_2_0_extension);
+int LIBUSB_CALL libusb_get_ss_usb_device_capability_descriptor(
+ struct libusb_context *ctx,
+ struct libusb_bos_dev_capability_descriptor *dev_cap,
+ struct libusb_ss_usb_device_capability_descriptor **ss_usb_device_cap);
+void LIBUSB_CALL libusb_free_ss_usb_device_capability_descriptor(
+ struct libusb_ss_usb_device_capability_descriptor *ss_usb_device_cap);
+int LIBUSB_CALL libusb_get_container_id_descriptor(struct libusb_context *ctx,
+ struct libusb_bos_dev_capability_descriptor *dev_cap,
+ struct libusb_container_id_descriptor **container_id);
+void LIBUSB_CALL libusb_free_container_id_descriptor(
+ struct libusb_container_id_descriptor *container_id);
+uint8_t LIBUSB_CALL libusb_get_bus_number(libusb_device *dev);
+uint8_t LIBUSB_CALL libusb_get_port_number(libusb_device *dev);
+int LIBUSB_CALL libusb_get_port_numbers(libusb_device *dev, uint8_t* port_numbers, int port_numbers_len);
+LIBUSB_DEPRECATED_FOR(libusb_get_port_numbers)
+int LIBUSB_CALL libusb_get_port_path(libusb_context *ctx, libusb_device *dev, uint8_t* path, uint8_t path_length);
+libusb_device * LIBUSB_CALL libusb_get_parent(libusb_device *dev);
+uint8_t LIBUSB_CALL libusb_get_device_address(libusb_device *dev);
+int LIBUSB_CALL libusb_get_device_speed(libusb_device *dev);
+int LIBUSB_CALL libusb_get_max_packet_size(libusb_device *dev,
+ unsigned char endpoint);
+int LIBUSB_CALL libusb_get_max_iso_packet_size(libusb_device *dev,
+ unsigned char endpoint);
+
+int LIBUSB_CALL libusb_open(libusb_device *dev, libusb_device_handle **dev_handle);
+void LIBUSB_CALL libusb_close(libusb_device_handle *dev_handle);
+libusb_device * LIBUSB_CALL libusb_get_device(libusb_device_handle *dev_handle);
+
+int LIBUSB_CALL libusb_set_configuration(libusb_device_handle *dev_handle,
+ int configuration);
+int LIBUSB_CALL libusb_claim_interface(libusb_device_handle *dev_handle,
+ int interface_number);
+int LIBUSB_CALL libusb_release_interface(libusb_device_handle *dev_handle,
+ int interface_number);
+
+libusb_device_handle * LIBUSB_CALL libusb_open_device_with_vid_pid(
+ libusb_context *ctx, uint16_t vendor_id, uint16_t product_id);
+
+int LIBUSB_CALL libusb_set_interface_alt_setting(libusb_device_handle *dev_handle,
+ int interface_number, int alternate_setting);
+int LIBUSB_CALL libusb_clear_halt(libusb_device_handle *dev_handle,
+ unsigned char endpoint);
+int LIBUSB_CALL libusb_reset_device(libusb_device_handle *dev_handle);
+
+int LIBUSB_CALL libusb_alloc_streams(libusb_device_handle *dev_handle,
+ uint32_t num_streams, unsigned char *endpoints, int num_endpoints);
+int LIBUSB_CALL libusb_free_streams(libusb_device_handle *dev_handle,
+ unsigned char *endpoints, int num_endpoints);
+
+unsigned char * LIBUSB_CALL libusb_dev_mem_alloc(libusb_device_handle *dev_handle,
+ size_t length);
+int LIBUSB_CALL libusb_dev_mem_free(libusb_device_handle *dev_handle,
+ unsigned char *buffer, size_t length);
+
+int LIBUSB_CALL libusb_kernel_driver_active(libusb_device_handle *dev_handle,
+ int interface_number);
+int LIBUSB_CALL libusb_detach_kernel_driver(libusb_device_handle *dev_handle,
+ int interface_number);
+int LIBUSB_CALL libusb_attach_kernel_driver(libusb_device_handle *dev_handle,
+ int interface_number);
+int LIBUSB_CALL libusb_set_auto_detach_kernel_driver(
+ libusb_device_handle *dev_handle, int enable);
+
+/* async I/O */
+
+/** \ingroup libusb_asyncio
+ * Get the data section of a control transfer. This convenience function is here
+ * to remind you that the data does not start until 8 bytes into the actual
+ * buffer, as the setup packet comes first.
+ *
+ * Calling this function only makes sense from a transfer callback function,
+ * or situations where you have already allocated a suitably sized buffer at
+ * transfer->buffer.
+ *
+ * \param transfer a transfer
+ * \returns pointer to the first byte of the data section
+ */
+static inline unsigned char *libusb_control_transfer_get_data(
+ struct libusb_transfer *transfer)
+{
+ return transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE;
+}
+
+/** \ingroup libusb_asyncio
+ * Get the control setup packet of a control transfer. This convenience
+ * function is here to remind you that the control setup occupies the first
+ * 8 bytes of the transfer data buffer.
+ *
+ * Calling this function only makes sense from a transfer callback function,
+ * or situations where you have already allocated a suitably sized buffer at
+ * transfer->buffer.
+ *
+ * \param transfer a transfer
+ * \returns a casted pointer to the start of the transfer data buffer
+ */
+static inline struct libusb_control_setup *libusb_control_transfer_get_setup(
+ struct libusb_transfer *transfer)
+{
+ return (struct libusb_control_setup *)(void *) transfer->buffer;
+}
+
+/** \ingroup libusb_asyncio
+ * Helper function to populate the setup packet (first 8 bytes of the data
+ * buffer) for a control transfer. The wIndex, wValue and wLength values should
+ * be given in host-endian byte order.
+ *
+ * \param buffer buffer to output the setup packet into
+ * This pointer must be aligned to at least 2 bytes boundary.
+ * \param bmRequestType see the
+ * \ref libusb_control_setup::bmRequestType "bmRequestType" field of
+ * \ref libusb_control_setup
+ * \param bRequest see the
+ * \ref libusb_control_setup::bRequest "bRequest" field of
+ * \ref libusb_control_setup
+ * \param wValue see the
+ * \ref libusb_control_setup::wValue "wValue" field of
+ * \ref libusb_control_setup
+ * \param wIndex see the
+ * \ref libusb_control_setup::wIndex "wIndex" field of
+ * \ref libusb_control_setup
+ * \param wLength see the
+ * \ref libusb_control_setup::wLength "wLength" field of
+ * \ref libusb_control_setup
+ */
+static inline void libusb_fill_control_setup(unsigned char *buffer,
+ uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex,
+ uint16_t wLength)
+{
+ struct libusb_control_setup *setup = (struct libusb_control_setup *)(void *) buffer;
+ setup->bmRequestType = bmRequestType;
+ setup->bRequest = bRequest;
+ setup->wValue = libusb_cpu_to_le16(wValue);
+ setup->wIndex = libusb_cpu_to_le16(wIndex);
+ setup->wLength = libusb_cpu_to_le16(wLength);
+}
+
+struct libusb_transfer * LIBUSB_CALL libusb_alloc_transfer(int iso_packets);
+int LIBUSB_CALL libusb_submit_transfer(struct libusb_transfer *transfer);
+int LIBUSB_CALL libusb_cancel_transfer(struct libusb_transfer *transfer);
+void LIBUSB_CALL libusb_free_transfer(struct libusb_transfer *transfer);
+void LIBUSB_CALL libusb_transfer_set_stream_id(
+ struct libusb_transfer *transfer, uint32_t stream_id);
+uint32_t LIBUSB_CALL libusb_transfer_get_stream_id(
+ struct libusb_transfer *transfer);
+
+/** \ingroup libusb_asyncio
+ * Helper function to populate the required \ref libusb_transfer fields
+ * for a control transfer.
+ *
+ * If you pass a transfer buffer to this function, the first 8 bytes will
+ * be interpreted as a control setup packet, and the wLength field will be
+ * used to automatically populate the \ref libusb_transfer::length "length"
+ * field of the transfer. Therefore the recommended approach is:
+ * -# Allocate a suitably sized data buffer (including space for control setup)
+ * -# Call libusb_fill_control_setup()
+ * -# If this is a host-to-device transfer with a data stage, put the data
+ * in place after the setup packet
+ * -# Call this function
+ * -# Call libusb_submit_transfer()
+ *
+ * It is also legal to pass a NULL buffer to this function, in which case this
+ * function will not attempt to populate the length field. Remember that you
+ * must then populate the buffer and length fields later.
+ *
+ * \param transfer the transfer to populate
+ * \param dev_handle handle of the device that will handle the transfer
+ * \param buffer data buffer. If provided, this function will interpret the
+ * first 8 bytes as a setup packet and infer the transfer length from that.
+ * This pointer must be aligned to at least 2 bytes boundary.
+ * \param callback callback function to be invoked on transfer completion
+ * \param user_data user data to pass to callback function
+ * \param timeout timeout for the transfer in milliseconds
+ */
+static inline void libusb_fill_control_transfer(
+ struct libusb_transfer *transfer, libusb_device_handle *dev_handle,
+ unsigned char *buffer, libusb_transfer_cb_fn callback, void *user_data,
+ unsigned int timeout)
+{
+ struct libusb_control_setup *setup = (struct libusb_control_setup *)(void *) buffer;
+ transfer->dev_handle = dev_handle;
+ transfer->endpoint = 0;
+ transfer->type = LIBUSB_TRANSFER_TYPE_CONTROL;
+ transfer->timeout = timeout;
+ transfer->buffer = buffer;
+ if (setup)
+ transfer->length = (int) (LIBUSB_CONTROL_SETUP_SIZE
+ + libusb_le16_to_cpu(setup->wLength));
+ transfer->user_data = user_data;
+ transfer->callback = callback;
+}
+
+/** \ingroup libusb_asyncio
+ * Helper function to populate the required \ref libusb_transfer fields
+ * for a bulk transfer.
+ *
+ * \param transfer the transfer to populate
+ * \param dev_handle handle of the device that will handle the transfer
+ * \param endpoint address of the endpoint where this transfer will be sent
+ * \param buffer data buffer
+ * \param length length of data buffer
+ * \param callback callback function to be invoked on transfer completion
+ * \param user_data user data to pass to callback function
+ * \param timeout timeout for the transfer in milliseconds
+ */
+static inline void libusb_fill_bulk_transfer(struct libusb_transfer *transfer,
+ libusb_device_handle *dev_handle, unsigned char endpoint,
+ unsigned char *buffer, int length, libusb_transfer_cb_fn callback,
+ void *user_data, unsigned int timeout)
+{
+ transfer->dev_handle = dev_handle;
+ transfer->endpoint = endpoint;
+ transfer->type = LIBUSB_TRANSFER_TYPE_BULK;
+ transfer->timeout = timeout;
+ transfer->buffer = buffer;
+ transfer->length = length;
+ transfer->user_data = user_data;
+ transfer->callback = callback;
+}
+
+/** \ingroup libusb_asyncio
+ * Helper function to populate the required \ref libusb_transfer fields
+ * for a bulk transfer using bulk streams.
+ *
+ * Since version 1.0.19, \ref LIBUSB_API_VERSION >= 0x01000103
+ *
+ * \param transfer the transfer to populate
+ * \param dev_handle handle of the device that will handle the transfer
+ * \param endpoint address of the endpoint where this transfer will be sent
+ * \param stream_id bulk stream id for this transfer
+ * \param buffer data buffer
+ * \param length length of data buffer
+ * \param callback callback function to be invoked on transfer completion
+ * \param user_data user data to pass to callback function
+ * \param timeout timeout for the transfer in milliseconds
+ */
+static inline void libusb_fill_bulk_stream_transfer(
+ struct libusb_transfer *transfer, libusb_device_handle *dev_handle,
+ unsigned char endpoint, uint32_t stream_id,
+ unsigned char *buffer, int length, libusb_transfer_cb_fn callback,
+ void *user_data, unsigned int timeout)
+{
+ libusb_fill_bulk_transfer(transfer, dev_handle, endpoint, buffer,
+ length, callback, user_data, timeout);
+ transfer->type = LIBUSB_TRANSFER_TYPE_BULK_STREAM;
+ libusb_transfer_set_stream_id(transfer, stream_id);
+}
+
+/** \ingroup libusb_asyncio
+ * Helper function to populate the required \ref libusb_transfer fields
+ * for an interrupt transfer.
+ *
+ * \param transfer the transfer to populate
+ * \param dev_handle handle of the device that will handle the transfer
+ * \param endpoint address of the endpoint where this transfer will be sent
+ * \param buffer data buffer
+ * \param length length of data buffer
+ * \param callback callback function to be invoked on transfer completion
+ * \param user_data user data to pass to callback function
+ * \param timeout timeout for the transfer in milliseconds
+ */
+static inline void libusb_fill_interrupt_transfer(
+ struct libusb_transfer *transfer, libusb_device_handle *dev_handle,
+ unsigned char endpoint, unsigned char *buffer, int length,
+ libusb_transfer_cb_fn callback, void *user_data, unsigned int timeout)
+{
+ transfer->dev_handle = dev_handle;
+ transfer->endpoint = endpoint;
+ transfer->type = LIBUSB_TRANSFER_TYPE_INTERRUPT;
+ transfer->timeout = timeout;
+ transfer->buffer = buffer;
+ transfer->length = length;
+ transfer->user_data = user_data;
+ transfer->callback = callback;
+}
+
+/** \ingroup libusb_asyncio
+ * Helper function to populate the required \ref libusb_transfer fields
+ * for an isochronous transfer.
+ *
+ * \param transfer the transfer to populate
+ * \param dev_handle handle of the device that will handle the transfer
+ * \param endpoint address of the endpoint where this transfer will be sent
+ * \param buffer data buffer
+ * \param length length of data buffer
+ * \param num_iso_packets the number of isochronous packets
+ * \param callback callback function to be invoked on transfer completion
+ * \param user_data user data to pass to callback function
+ * \param timeout timeout for the transfer in milliseconds
+ */
+static inline void libusb_fill_iso_transfer(struct libusb_transfer *transfer,
+ libusb_device_handle *dev_handle, unsigned char endpoint,
+ unsigned char *buffer, int length, int num_iso_packets,
+ libusb_transfer_cb_fn callback, void *user_data, unsigned int timeout)
+{
+ transfer->dev_handle = dev_handle;
+ transfer->endpoint = endpoint;
+ transfer->type = LIBUSB_TRANSFER_TYPE_ISOCHRONOUS;
+ transfer->timeout = timeout;
+ transfer->buffer = buffer;
+ transfer->length = length;
+ transfer->num_iso_packets = num_iso_packets;
+ transfer->user_data = user_data;
+ transfer->callback = callback;
+}
+
+/** \ingroup libusb_asyncio
+ * Convenience function to set the length of all packets in an isochronous
+ * transfer, based on the num_iso_packets field in the transfer structure.
+ *
+ * \param transfer a transfer
+ * \param length the length to set in each isochronous packet descriptor
+ * \see libusb_get_max_packet_size()
+ */
+static inline void libusb_set_iso_packet_lengths(
+ struct libusb_transfer *transfer, unsigned int length)
+{
+ int i;
+ for (i = 0; i < transfer->num_iso_packets; i++)
+ transfer->iso_packet_desc[i].length = length;
+}
+
+/** \ingroup libusb_asyncio
+ * Convenience function to locate the position of an isochronous packet
+ * within the buffer of an isochronous transfer.
+ *
+ * This is a thorough function which loops through all preceding packets,
+ * accumulating their lengths to find the position of the specified packet.
+ * Typically you will assign equal lengths to each packet in the transfer,
+ * and hence the above method is sub-optimal. You may wish to use
+ * libusb_get_iso_packet_buffer_simple() instead.
+ *
+ * \param transfer a transfer
+ * \param packet the packet to return the address of
+ * \returns the base address of the packet buffer inside the transfer buffer,
+ * or NULL if the packet does not exist.
+ * \see libusb_get_iso_packet_buffer_simple()
+ */
+static inline unsigned char *libusb_get_iso_packet_buffer(
+ struct libusb_transfer *transfer, unsigned int packet)
+{
+ int i;
+ size_t offset = 0;
+ int _packet;
+
+ /* oops..slight bug in the API. packet is an unsigned int, but we use
+ * signed integers almost everywhere else. range-check and convert to
+ * signed to avoid compiler warnings. FIXME for libusb-2. */
+ if (packet > INT_MAX)
+ return NULL;
+ _packet = (int) packet;
+
+ if (_packet >= transfer->num_iso_packets)
+ return NULL;
+
+ for (i = 0; i < _packet; i++)
+ offset += transfer->iso_packet_desc[i].length;
+
+ return transfer->buffer + offset;
+}
+
+/** \ingroup libusb_asyncio
+ * Convenience function to locate the position of an isochronous packet
+ * within the buffer of an isochronous transfer, for transfers where each
+ * packet is of identical size.
+ *
+ * This function relies on the assumption that every packet within the transfer
+ * is of identical size to the first packet. Calculating the location of
+ * the packet buffer is then just a simple calculation:
+ * <tt>buffer + (packet_size * packet)</tt>
+ *
+ * Do not use this function on transfers other than those that have identical
+ * packet lengths for each packet.
+ *
+ * \param transfer a transfer
+ * \param packet the packet to return the address of
+ * \returns the base address of the packet buffer inside the transfer buffer,
+ * or NULL if the packet does not exist.
+ * \see libusb_get_iso_packet_buffer()
+ */
+static inline unsigned char *libusb_get_iso_packet_buffer_simple(
+ struct libusb_transfer *transfer, unsigned int packet)
+{
+ int _packet;
+
+ /* oops..slight bug in the API. packet is an unsigned int, but we use
+ * signed integers almost everywhere else. range-check and convert to
+ * signed to avoid compiler warnings. FIXME for libusb-2. */
+ if (packet > INT_MAX)
+ return NULL;
+ _packet = (int) packet;
+
+ if (_packet >= transfer->num_iso_packets)
+ return NULL;
+
+ return transfer->buffer + ((int) transfer->iso_packet_desc[0].length * _packet);
+}
+
+/* sync I/O */
+
+int LIBUSB_CALL libusb_control_transfer(libusb_device_handle *dev_handle,
+ uint8_t request_type, uint8_t bRequest, uint16_t wValue, uint16_t wIndex,
+ unsigned char *data, uint16_t wLength, unsigned int timeout);
+
+int LIBUSB_CALL libusb_bulk_transfer(libusb_device_handle *dev_handle,
+ unsigned char endpoint, unsigned char *data, int length,
+ int *actual_length, unsigned int timeout);
+
+int LIBUSB_CALL libusb_interrupt_transfer(libusb_device_handle *dev_handle,
+ unsigned char endpoint, unsigned char *data, int length,
+ int *actual_length, unsigned int timeout);
+
+/** \ingroup libusb_desc
+ * Retrieve a descriptor from the default control pipe.
+ * This is a convenience function which formulates the appropriate control
+ * message to retrieve the descriptor.
+ *
+ * \param dev_handle a device handle
+ * \param desc_type the descriptor type, see \ref libusb_descriptor_type
+ * \param desc_index the index of the descriptor to retrieve
+ * \param data output buffer for descriptor
+ * \param length size of data buffer
+ * \returns number of bytes returned in data, or LIBUSB_ERROR code on failure
+ */
+static inline int libusb_get_descriptor(libusb_device_handle *dev_handle,
+ uint8_t desc_type, uint8_t desc_index, unsigned char *data, int length)
+{
+ return libusb_control_transfer(dev_handle, LIBUSB_ENDPOINT_IN,
+ LIBUSB_REQUEST_GET_DESCRIPTOR, (uint16_t) ((desc_type << 8) | desc_index),
+ 0, data, (uint16_t) length, 1000);
+}
+
+/** \ingroup libusb_desc
+ * Retrieve a descriptor from a device.
+ * This is a convenience function which formulates the appropriate control
+ * message to retrieve the descriptor. The string returned is Unicode, as
+ * detailed in the USB specifications.
+ *
+ * \param dev_handle a device handle
+ * \param desc_index the index of the descriptor to retrieve
+ * \param langid the language ID for the string descriptor
+ * \param data output buffer for descriptor
+ * \param length size of data buffer
+ * \returns number of bytes returned in data, or LIBUSB_ERROR code on failure
+ * \see libusb_get_string_descriptor_ascii()
+ */
+static inline int libusb_get_string_descriptor(libusb_device_handle *dev_handle,
+ uint8_t desc_index, uint16_t langid, unsigned char *data, int length)
+{
+ return libusb_control_transfer(dev_handle, LIBUSB_ENDPOINT_IN,
+ LIBUSB_REQUEST_GET_DESCRIPTOR, (uint16_t)((LIBUSB_DT_STRING << 8) | desc_index),
+ langid, data, (uint16_t) length, 1000);
+}
+
+int LIBUSB_CALL libusb_get_string_descriptor_ascii(libusb_device_handle *dev_handle,
+ uint8_t desc_index, unsigned char *data, int length);
+
+/* polling and timeouts */
+
+int LIBUSB_CALL libusb_try_lock_events(libusb_context *ctx);
+void LIBUSB_CALL libusb_lock_events(libusb_context *ctx);
+void LIBUSB_CALL libusb_unlock_events(libusb_context *ctx);
+int LIBUSB_CALL libusb_event_handling_ok(libusb_context *ctx);
+int LIBUSB_CALL libusb_event_handler_active(libusb_context *ctx);
+void LIBUSB_CALL libusb_interrupt_event_handler(libusb_context *ctx);
+void LIBUSB_CALL libusb_lock_event_waiters(libusb_context *ctx);
+void LIBUSB_CALL libusb_unlock_event_waiters(libusb_context *ctx);
+int LIBUSB_CALL libusb_wait_for_event(libusb_context *ctx, struct timeval *tv);
+
+int LIBUSB_CALL libusb_handle_events_timeout(libusb_context *ctx,
+ struct timeval *tv);
+int LIBUSB_CALL libusb_handle_events_timeout_completed(libusb_context *ctx,
+ struct timeval *tv, int *completed);
+int LIBUSB_CALL libusb_handle_events(libusb_context *ctx);
+int LIBUSB_CALL libusb_handle_events_completed(libusb_context *ctx, int *completed);
+int LIBUSB_CALL libusb_handle_events_locked(libusb_context *ctx,
+ struct timeval *tv);
+int LIBUSB_CALL libusb_pollfds_handle_timeouts(libusb_context *ctx);
+int LIBUSB_CALL libusb_get_next_timeout(libusb_context *ctx,
+ struct timeval *tv);
+
+/** \ingroup libusb_poll
+ * File descriptor for polling
+ */
+struct libusb_pollfd {
+ /** Numeric file descriptor */
+ int fd;
+
+ /** Event flags to poll for from <poll.h>. POLLIN indicates that you
+ * should monitor this file descriptor for becoming ready to read from,
+ * and POLLOUT indicates that you should monitor this file descriptor for
+ * nonblocking write readiness. */
+ short events;
+};
+
+/** \ingroup libusb_poll
+ * Callback function, invoked when a new file descriptor should be added
+ * to the set of file descriptors monitored for events.
+ * \param fd the new file descriptor
+ * \param events events to monitor for, see \ref libusb_pollfd for a
+ * description
+ * \param user_data User data pointer specified in
+ * libusb_set_pollfd_notifiers() call
+ * \see libusb_set_pollfd_notifiers()
+ */
+typedef void (LIBUSB_CALL *libusb_pollfd_added_cb)(int fd, short events,
+ void *user_data);
+
+/** \ingroup libusb_poll
+ * Callback function, invoked when a file descriptor should be removed from
+ * the set of file descriptors being monitored for events. After returning
+ * from this callback, do not use that file descriptor again.
+ * \param fd the file descriptor to stop monitoring
+ * \param user_data User data pointer specified in
+ * libusb_set_pollfd_notifiers() call
+ * \see libusb_set_pollfd_notifiers()
+ */
+typedef void (LIBUSB_CALL *libusb_pollfd_removed_cb)(int fd, void *user_data);
+
+const struct libusb_pollfd ** LIBUSB_CALL libusb_get_pollfds(
+ libusb_context *ctx);
+void LIBUSB_CALL libusb_free_pollfds(const struct libusb_pollfd **pollfds);
+void LIBUSB_CALL libusb_set_pollfd_notifiers(libusb_context *ctx,
+ libusb_pollfd_added_cb added_cb, libusb_pollfd_removed_cb removed_cb,
+ void *user_data);
+
+/** \ingroup libusb_hotplug
+ * Callback handle.
+ *
+ * Callbacks handles are generated by libusb_hotplug_register_callback()
+ * and can be used to deregister callbacks. Callback handles are unique
+ * per libusb_context and it is safe to call libusb_hotplug_deregister_callback()
+ * on an already deregisted callback.
+ *
+ * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102
+ *
+ * For more information, see \ref libusb_hotplug.
+ */
+typedef int libusb_hotplug_callback_handle;
+
+/** \ingroup libusb_hotplug
+ *
+ * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102
+ *
+ * Flags for hotplug events */
+typedef enum {
+ /** Default value when not using any flags. */
+ LIBUSB_HOTPLUG_NO_FLAGS = 0,
+
+ /** Arm the callback and fire it for all matching currently attached devices. */
+ LIBUSB_HOTPLUG_ENUMERATE = 1<<0,
+} libusb_hotplug_flag;
+
+/** \ingroup libusb_hotplug
+ *
+ * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102
+ *
+ * Hotplug events */
+typedef enum {
+ /** A device has been plugged in and is ready to use */
+ LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED = 0x01,
+
+ /** A device has left and is no longer available.
+ * It is the user's responsibility to call libusb_close on any handle associated with a disconnected device.
+ * It is safe to call libusb_get_device_descriptor on a device that has left */
+ LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT = 0x02,
+} libusb_hotplug_event;
+
+/** \ingroup libusb_hotplug
+ * Wildcard matching for hotplug events */
+#define LIBUSB_HOTPLUG_MATCH_ANY -1
+
+/** \ingroup libusb_hotplug
+ * Hotplug callback function type. When requesting hotplug event notifications,
+ * you pass a pointer to a callback function of this type.
+ *
+ * This callback may be called by an internal event thread and as such it is
+ * recommended the callback do minimal processing before returning.
+ *
+ * libusb will call this function later, when a matching event had happened on
+ * a matching device. See \ref libusb_hotplug for more information.
+ *
+ * It is safe to call either libusb_hotplug_register_callback() or
+ * libusb_hotplug_deregister_callback() from within a callback function.
+ *
+ * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102
+ *
+ * \param ctx context of this notification
+ * \param device libusb_device this event occurred on
+ * \param event event that occurred
+ * \param user_data user data provided when this callback was registered
+ * \returns bool whether this callback is finished processing events.
+ * returning 1 will cause this callback to be deregistered
+ */
+typedef int (LIBUSB_CALL *libusb_hotplug_callback_fn)(libusb_context *ctx,
+ libusb_device *device,
+ libusb_hotplug_event event,
+ void *user_data);
+
+/** \ingroup libusb_hotplug
+ * Register a hotplug callback function
+ *
+ * Register a callback with the libusb_context. The callback will fire
+ * when a matching event occurs on a matching device. The callback is
+ * armed until either it is deregistered with libusb_hotplug_deregister_callback()
+ * or the supplied callback returns 1 to indicate it is finished processing events.
+ *
+ * If the \ref LIBUSB_HOTPLUG_ENUMERATE is passed the callback will be
+ * called with a \ref LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED for all devices
+ * already plugged into the machine. Note that libusb modifies its internal
+ * device list from a separate thread, while calling hotplug callbacks from
+ * libusb_handle_events(), so it is possible for a device to already be present
+ * on, or removed from, its internal device list, while the hotplug callbacks
+ * still need to be dispatched. This means that when using \ref
+ * LIBUSB_HOTPLUG_ENUMERATE, your callback may be called twice for the arrival
+ * of the same device, once from libusb_hotplug_register_callback() and once
+ * from libusb_handle_events(); and/or your callback may be called for the
+ * removal of a device for which an arrived call was never made.
+ *
+ * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102
+ *
+ * \param[in] ctx context to register this callback with
+ * \param[in] events bitwise or of events that will trigger this callback. See \ref
+ * libusb_hotplug_event
+ * \param[in] flags hotplug callback flags. See \ref libusb_hotplug_flag
+ * \param[in] vendor_id the vendor id to match or \ref LIBUSB_HOTPLUG_MATCH_ANY
+ * \param[in] product_id the product id to match or \ref LIBUSB_HOTPLUG_MATCH_ANY
+ * \param[in] dev_class the device class to match or \ref LIBUSB_HOTPLUG_MATCH_ANY
+ * \param[in] cb_fn the function to be invoked on a matching event/device
+ * \param[in] user_data user data to pass to the callback function
+ * \param[out] callback_handle pointer to store the handle of the allocated callback (can be NULL)
+ * \returns LIBUSB_SUCCESS on success LIBUSB_ERROR code on failure
+ */
+int LIBUSB_CALL libusb_hotplug_register_callback(libusb_context *ctx,
+ libusb_hotplug_event events,
+ libusb_hotplug_flag flags,
+ int vendor_id, int product_id,
+ int dev_class,
+ libusb_hotplug_callback_fn cb_fn,
+ void *user_data,
+ libusb_hotplug_callback_handle *callback_handle);
+
+/** \ingroup libusb_hotplug
+ * Deregisters a hotplug callback.
+ *
+ * Deregister a callback from a libusb_context. This function is safe to call from within
+ * a hotplug callback.
+ *
+ * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102
+ *
+ * \param[in] ctx context this callback is registered with
+ * \param[in] callback_handle the handle of the callback to deregister
+ */
+void LIBUSB_CALL libusb_hotplug_deregister_callback(libusb_context *ctx,
+ libusb_hotplug_callback_handle callback_handle);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/vendor/github.com/karalabe/gousb/internal/libusb/libusb/libusbi.h b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/libusbi.h
new file mode 100644
index 000000000..752e39887
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/libusbi.h
@@ -0,0 +1,1149 @@
+/*
+ * Internal header for libusb
+ * Copyright © 2007-2009 Daniel Drake <dsd@gentoo.org>
+ * Copyright © 2001 Johannes Erdfelt <johannes@erdfelt.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef LIBUSBI_H
+#define LIBUSBI_H
+
+#include <config.h>
+
+#include <stdlib.h>
+
+#include <stddef.h>
+#include <stdint.h>
+#include <time.h>
+#include <stdarg.h>
+#ifdef HAVE_POLL_H
+#include <poll.h>
+#endif
+#ifdef HAVE_MISSING_H
+#include <missing.h>
+#endif
+
+#include "libusb.h"
+#include "version.h"
+
+/* 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:
+ * DEFAULT_VISIBILITY return_type * LIBUSB_CALL function_name(params) { ... }
+ * In the libusb public header, mark all declarations as:
+ * return_type LIBUSB_CALL function_name(params);
+ */
+#define API_EXPORTED LIBUSB_CALL DEFAULT_VISIBILITY
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define DEVICE_DESC_LENGTH 18
+
+#define USB_MAXENDPOINTS 32
+#define USB_MAXINTERFACES 32
+#define USB_MAXCONFIG 8
+
+/* Backend specific capabilities */
+#define USBI_CAP_HAS_HID_ACCESS 0x00010000
+#define USBI_CAP_SUPPORTS_DETACH_KERNEL_DRIVER 0x00020000
+
+/* Maximum number of bytes in a log line */
+#define USBI_MAX_LOG_LEN 1024
+/* Terminator for log lines */
+#define USBI_LOG_LINE_END "\n"
+
+/* The following is used to silence warnings for unused variables */
+#define UNUSED(var) do { (void)(var); } while(0)
+
+#if !defined(ARRAYSIZE)
+#define ARRAYSIZE(array) (sizeof(array) / sizeof(array[0]))
+#endif
+
+struct list_head {
+ struct list_head *prev, *next;
+};
+
+/* Get an entry from the list
+ * ptr - the address of this list_head element in "type"
+ * type - the data type that contains "member"
+ * member - the list_head element in "type"
+ */
+#define list_entry(ptr, type, member) \
+ ((type *)((uintptr_t)(ptr) - (uintptr_t)offsetof(type, member)))
+
+#define list_first_entry(ptr, type, member) \
+ list_entry((ptr)->next, type, member)
+
+/* Get each entry from a list
+ * pos - A structure pointer has a "member" element
+ * head - list head
+ * member - the list_head element in "pos"
+ * type - the type of the first parameter
+ */
+#define list_for_each_entry(pos, head, member, type) \
+ for (pos = list_entry((head)->next, type, member); \
+ &pos->member != (head); \
+ pos = list_entry(pos->member.next, type, member))
+
+#define list_for_each_entry_safe(pos, n, head, member, type) \
+ for (pos = list_entry((head)->next, type, member), \
+ n = list_entry(pos->member.next, type, member); \
+ &pos->member != (head); \
+ pos = n, n = list_entry(n->member.next, type, member))
+
+#define list_empty(entry) ((entry)->next == (entry))
+
+static inline void list_init(struct list_head *entry)
+{
+ entry->prev = entry->next = entry;
+}
+
+static inline void list_add(struct list_head *entry, struct list_head *head)
+{
+ entry->next = head->next;
+ entry->prev = head;
+
+ head->next->prev = entry;
+ head->next = entry;
+}
+
+static inline void list_add_tail(struct list_head *entry,
+ struct list_head *head)
+{
+ entry->next = head;
+ entry->prev = head->prev;
+
+ head->prev->next = entry;
+ head->prev = entry;
+}
+
+static inline void list_del(struct list_head *entry)
+{
+ entry->next->prev = entry->prev;
+ entry->prev->next = entry->next;
+ entry->next = entry->prev = NULL;
+}
+
+static inline void *usbi_reallocf(void *ptr, size_t size)
+{
+ void *ret = realloc(ptr, size);
+ if (!ret)
+ free(ptr);
+ return ret;
+}
+
+#define container_of(ptr, type, member) ({ \
+ const typeof( ((type *)0)->member ) *mptr = (ptr); \
+ (type *)( (char *)mptr - offsetof(type,member) );})
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+#ifndef MAX
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif
+
+#define TIMESPEC_IS_SET(ts) ((ts)->tv_sec != 0 || (ts)->tv_nsec != 0)
+
+#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE)
+#define TIMEVAL_TV_SEC_TYPE long
+#else
+#define TIMEVAL_TV_SEC_TYPE time_t
+#endif
+
+/* Some platforms don't have this define */
+#ifndef TIMESPEC_TO_TIMEVAL
+#define TIMESPEC_TO_TIMEVAL(tv, ts) \
+ do { \
+ (tv)->tv_sec = (TIMEVAL_TV_SEC_TYPE) (ts)->tv_sec; \
+ (tv)->tv_usec = (ts)->tv_nsec / 1000; \
+ } while (0)
+#endif
+
+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
+
+#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__)
+
+#else /* !defined(_MSC_VER) || _MSC_VER >= 1400 */
+
+#ifdef ENABLE_LOGGING
+#define LOG_BODY(ctxt, level) \
+{ \
+ va_list args; \
+ va_start(args, format); \
+ 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_dbg(const char *format, ...)
+ LOG_BODY(NULL, LIBUSB_LOG_LEVEL_DEBUG)
+
+#endif /* !defined(_MSC_VER) || _MSC_VER >= 1400 */
+
+#define USBI_GET_CONTEXT(ctx) \
+ do { \
+ if (!(ctx)) \
+ (ctx) = usbi_default_context; \
+ } while(0)
+
+#define DEVICE_CTX(dev) ((dev)->ctx)
+#define HANDLE_CTX(handle) (DEVICE_CTX((handle)->dev))
+#define TRANSFER_CTX(transfer) (HANDLE_CTX((transfer)->dev_handle))
+#define ITRANSFER_CTX(transfer) \
+ (TRANSFER_CTX(USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer)))
+
+#define IS_EPIN(ep) (0 != ((ep) & LIBUSB_ENDPOINT_IN))
+#define IS_EPOUT(ep) (!IS_EPIN(ep))
+#define IS_XFERIN(xfer) (0 != ((xfer)->endpoint & LIBUSB_ENDPOINT_IN))
+#define IS_XFEROUT(xfer) (!IS_XFERIN(xfer))
+
+/* Internal abstraction for thread synchronization */
+#if defined(THREADS_POSIX)
+#include "os/threads_posix.h"
+#elif defined(OS_WINDOWS) || defined(OS_WINCE)
+#include "os/threads_windows.h"
+#endif
+
+extern struct libusb_context *usbi_default_context;
+
+/* Forward declaration for use in context (fully defined inside poll abstraction) */
+struct pollfd;
+
+struct libusb_context {
+ int debug;
+ int debug_fixed;
+
+ /* internal event pipe, used for signalling occurrence of an internal event. */
+ int event_pipe[2];
+
+ struct list_head usb_devs;
+ usbi_mutex_t usb_devs_lock;
+
+ /* A list of open handles. Backends are free to traverse this if required.
+ */
+ struct list_head open_devs;
+ usbi_mutex_t open_devs_lock;
+
+ /* A list of registered hotplug callbacks */
+ struct list_head hotplug_cbs;
+ usbi_mutex_t hotplug_cbs_lock;
+
+ /* this is a list of in-flight transfer handles, sorted by timeout
+ * expiration. URBs to timeout the soonest are placed at the beginning of
+ * the list, URBs that will time out later are placed after, and urbs with
+ * infinite timeout are always placed at the very end. */
+ struct list_head flying_transfers;
+ /* Note paths taking both this and usbi_transfer->lock must always
+ * take this lock first */
+ usbi_mutex_t flying_transfers_lock;
+
+ /* user callbacks for pollfd changes */
+ libusb_pollfd_added_cb fd_added_cb;
+ libusb_pollfd_removed_cb fd_removed_cb;
+ void *fd_cb_user_data;
+
+ /* ensures that only one thread is handling events at any one time */
+ usbi_mutex_t events_lock;
+
+ /* used to see if there is an active thread doing event handling */
+ int event_handler_active;
+
+ /* A thread-local storage key to track which thread is performing event
+ * handling */
+ usbi_tls_key_t event_handling_key;
+
+ /* used to wait for event completion in threads other than the one that is
+ * event handling */
+ usbi_mutex_t event_waiters_lock;
+ usbi_cond_t event_waiters_cond;
+
+ /* A lock to protect internal context event data. */
+ usbi_mutex_t event_data_lock;
+
+ /* A bitmask of flags that are set to indicate specific events that need to
+ * be handled. Protected by event_data_lock. */
+ unsigned int event_flags;
+
+ /* A counter that is set when we want to interrupt and prevent event handling,
+ * in order to safely close a device. Protected by event_data_lock. */
+ unsigned int device_close;
+
+ /* list and count of poll fds and an array of poll fd structures that is
+ * (re)allocated as necessary prior to polling. Protected by event_data_lock. */
+ struct list_head ipollfds;
+ struct pollfd *pollfds;
+ POLL_NFDS_TYPE pollfds_cnt;
+
+ /* A list of pending hotplug messages. Protected by event_data_lock. */
+ struct list_head hotplug_msgs;
+
+ /* A list of pending completed transfers. Protected by event_data_lock. */
+ struct list_head completed_transfers;
+
+#ifdef USBI_TIMERFD_AVAILABLE
+ /* used for timeout handling, if supported by OS.
+ * this timerfd is maintained to trigger on the next pending timeout */
+ int timerfd;
+#endif
+
+ struct list_head list;
+};
+
+enum usbi_event_flags {
+ /* The list of pollfds has been modified */
+ USBI_EVENT_POLLFDS_MODIFIED = 1 << 0,
+
+ /* The user has interrupted the event handler */
+ USBI_EVENT_USER_INTERRUPT = 1 << 1,
+};
+
+/* Macros for managing event handling state */
+#define usbi_handling_events(ctx) \
+ (usbi_tls_key_get((ctx)->event_handling_key) != NULL)
+
+#define usbi_start_event_handling(ctx) \
+ usbi_tls_key_set((ctx)->event_handling_key, ctx)
+
+#define usbi_end_event_handling(ctx) \
+ usbi_tls_key_set((ctx)->event_handling_key, NULL)
+
+/* Update the following macro if new event sources are added */
+#define usbi_pending_events(ctx) \
+ ((ctx)->event_flags || (ctx)->device_close \
+ || !list_empty(&(ctx)->hotplug_msgs) || !list_empty(&(ctx)->completed_transfers))
+
+#ifdef USBI_TIMERFD_AVAILABLE
+#define usbi_using_timerfd(ctx) ((ctx)->timerfd >= 0)
+#else
+#define usbi_using_timerfd(ctx) (0)
+#endif
+
+struct libusb_device {
+ /* lock protects refcnt, everything else is finalized at initialization
+ * time */
+ usbi_mutex_t lock;
+ int refcnt;
+
+ struct libusb_context *ctx;
+
+ uint8_t bus_number;
+ uint8_t port_number;
+ struct libusb_device* parent_dev;
+ uint8_t device_address;
+ uint8_t num_configurations;
+ enum libusb_speed speed;
+
+ struct list_head list;
+ unsigned long session_data;
+
+ 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
+};
+
+struct libusb_device_handle {
+ /* lock protects claimed_interfaces */
+ usbi_mutex_t lock;
+ unsigned long claimed_interfaces;
+
+ 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
+};
+
+enum {
+ USBI_CLOCK_MONOTONIC,
+ USBI_CLOCK_REALTIME
+};
+
+/* in-memory transfer layout:
+ *
+ * 1. struct usbi_transfer
+ * 2. struct libusb_transfer (which includes iso packets) [variable size]
+ * 3. os private data [variable size]
+ *
+ * from a libusb_transfer, you can get the usbi_transfer by rewinding the
+ * appropriate number of bytes.
+ * the usbi_transfer includes the number of allocated packets, so you can
+ * determine the size of the transfer and hence the start and length of the
+ * OS-private data.
+ */
+
+struct usbi_transfer {
+ int num_iso_packets;
+ struct list_head list;
+ struct list_head completed_list;
+ struct timeval timeout;
+ int transferred;
+ uint32_t stream_id;
+ uint8_t state_flags; /* Protected by usbi_transfer->lock */
+ uint8_t timeout_flags; /* Protected by the flying_stransfers_lock */
+
+ /* this lock is held during libusb_submit_transfer() and
+ * libusb_cancel_transfer() (allowing the OS backend to prevent duplicate
+ * cancellation, submission-during-cancellation, etc). the OS backend
+ * should also take this lock in the handle_events path, to prevent the user
+ * cancelling the transfer from another thread while you are processing
+ * its completion (presumably there would be races within your OS backend
+ * if this were possible).
+ * Note paths taking both this and the flying_transfers_lock must
+ * always take the flying_transfers_lock first */
+ usbi_mutex_t lock;
+};
+
+enum usbi_transfer_state_flags {
+ /* Transfer successfully submitted by backend */
+ USBI_TRANSFER_IN_FLIGHT = 1 << 0,
+
+ /* Cancellation was requested via libusb_cancel_transfer() */
+ USBI_TRANSFER_CANCELLING = 1 << 1,
+
+ /* Operation on the transfer failed because the device disappeared */
+ USBI_TRANSFER_DEVICE_DISAPPEARED = 1 << 2,
+};
+
+enum usbi_transfer_timeout_flags {
+ /* Set by backend submit_transfer() if the OS handles timeout */
+ USBI_TRANSFER_OS_HANDLES_TIMEOUT = 1 << 0,
+
+ /* The transfer timeout has been handled */
+ USBI_TRANSFER_TIMEOUT_HANDLED = 1 << 1,
+
+ /* The transfer timeout was successfully processed */
+ USBI_TRANSFER_TIMED_OUT = 1 << 2,
+};
+
+#define USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer) \
+ ((struct libusb_transfer *)(((unsigned char *)(transfer)) \
+ + sizeof(struct usbi_transfer)))
+#define LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer) \
+ ((struct usbi_transfer *)(((unsigned char *)(transfer)) \
+ - sizeof(struct usbi_transfer)))
+
+static inline void *usbi_transfer_get_os_priv(struct usbi_transfer *transfer)
+{
+ return ((unsigned char *)transfer) + sizeof(struct usbi_transfer)
+ + sizeof(struct libusb_transfer)
+ + (transfer->num_iso_packets
+ * sizeof(struct libusb_iso_packet_descriptor));
+}
+
+/* bus structures */
+
+/* All standard descriptors have these 2 fields in common */
+struct usb_descriptor_header {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+};
+
+/* shared data and functions */
+
+int usbi_io_init(struct libusb_context *ctx);
+void usbi_io_exit(struct libusb_context *ctx);
+
+struct libusb_device *usbi_alloc_device(struct libusb_context *ctx,
+ unsigned long session_id);
+struct libusb_device *usbi_get_device_by_session_id(struct libusb_context *ctx,
+ unsigned long session_id);
+int usbi_sanitize_device(struct libusb_device *dev);
+void usbi_handle_disconnect(struct libusb_device_handle *dev_handle);
+
+int usbi_handle_transfer_completion(struct usbi_transfer *itransfer,
+ enum libusb_transfer_status status);
+int usbi_handle_transfer_cancellation(struct usbi_transfer *transfer);
+void usbi_signal_transfer_completion(struct usbi_transfer *transfer);
+
+int usbi_parse_descriptor(const unsigned char *source, const char *descriptor,
+ void *dest, int host_endian);
+int usbi_device_cache_descriptor(libusb_device *dev);
+int usbi_get_config_index_by_value(struct libusb_device *dev,
+ uint8_t bConfigurationValue, int *idx);
+
+void usbi_connect_device (struct libusb_device *dev);
+void usbi_disconnect_device (struct libusb_device *dev);
+
+int usbi_signal_event(struct libusb_context *ctx);
+int usbi_clear_event(struct libusb_context *ctx);
+
+/* Internal abstraction for poll (needs struct usbi_transfer on Windows) */
+#if defined(OS_LINUX) || defined(OS_DARWIN) || defined(OS_OPENBSD) || defined(OS_NETBSD) ||\
+ defined(OS_HAIKU) || defined(OS_SUNOS)
+#include <unistd.h>
+#include "os/poll_posix.h"
+#elif defined(OS_WINDOWS) || defined(OS_WINCE)
+#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;
+
+ struct list_head list;
+};
+
+int usbi_add_pollfd(struct libusb_context *ctx, int fd, short events);
+void usbi_remove_pollfd(struct libusb_context *ctx, int fd);
+
+/* device discovery */
+
+/* we traverse usbfs without knowing how many devices we are going to find.
+ * so we create this discovered_devs model which is similar to a linked-list
+ * which grows when required. it can be freed once discovery has completed,
+ * eliminating the need for a list node in the libusb_device structure
+ * itself. */
+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 discovered_devs *discovered_devs_append(
+ struct discovered_devs *discdevs, struct libusb_device *dev);
+
+/* OS abstraction */
+
+/* This is the interface that OS backends need to implement.
+ * All fields are mandatory, except ones explicitly noted as optional. */
+struct usbi_os_backend {
+ /* A human-readable name for your backend, e.g. "Linux usbfs" */
+ const char *name;
+
+ /* Binary mask for backend specific capabilities */
+ uint32_t caps;
+
+ /* Perform initialization of your backend. You might use this function
+ * to determine specific capabilities of the system, allocate required
+ * data structures for later, etc.
+ *
+ * This function is called when a libusb user initializes the library
+ * prior to use.
+ *
+ * Return 0 on success, or a LIBUSB_ERROR code on failure.
+ */
+ int (*init)(struct libusb_context *ctx);
+
+ /* Deinitialization. Optional. This function should destroy anything
+ * that was set up by init.
+ *
+ * This function is called when the user deinitializes the library.
+ */
+ void (*exit)(void);
+
+ /* Enumerate all the USB devices on the system, returning them in a list
+ * of discovered devices.
+ *
+ * Your implementation should enumerate all devices on the system,
+ * regardless of whether they have been seen before or not.
+ *
+ * When you have found a device, compute a session ID for it. The session
+ * ID should uniquely represent that particular device for that particular
+ * connection session since boot (i.e. if you disconnect and reconnect a
+ * device immediately after, it should be assigned a different session ID).
+ * If your OS cannot provide a unique session ID as described above,
+ * presenting a session ID of (bus_number << 8 | device_address) should
+ * be sufficient. Bus numbers and device addresses wrap and get reused,
+ * but that is an unlikely case.
+ *
+ * After computing a session ID for a device, call
+ * usbi_get_device_by_session_id(). This function checks if libusb already
+ * knows about the device, and if so, it provides you with a reference
+ * to a libusb_device structure for it.
+ *
+ * If usbi_get_device_by_session_id() returns NULL, it is time to allocate
+ * a new device structure for the device. Call usbi_alloc_device() to
+ * obtain a new libusb_device structure with reference count 1. Populate
+ * the bus_number and device_address attributes of the new device, and
+ * perform any other internal backend initialization you need to do. At
+ * this point, you should be ready to provide device descriptors and so
+ * on through the get_*_descriptor functions. Finally, call
+ * usbi_sanitize_device() to perform some final sanity checks on the
+ * device. Assuming all of the above succeeded, we can now continue.
+ * If any of the above failed, remember to unreference the device that
+ * was returned by usbi_alloc_device().
+ *
+ * At this stage we have a populated libusb_device structure (either one
+ * that was found earlier, or one that we have just allocated and
+ * populated). This can now be added to the discovered devices list
+ * using discovered_devs_append(). Note that discovered_devs_append()
+ * may reallocate the list, returning a new location for it, and also
+ * note that reallocation can fail. Your backend should handle these
+ * error conditions appropriately.
+ *
+ * This function should not generate any bus I/O and should not block.
+ * If I/O is required (e.g. reading the active configuration value), it is
+ * OK to ignore these suggestions :)
+ *
+ * This function is executed when the user wishes to retrieve a list
+ * of USB devices connected to the system.
+ *
+ * If the backend has hotplug support, this function is not used!
+ *
+ * Return 0 on success, or a LIBUSB_ERROR code on failure.
+ */
+ int (*get_device_list)(struct libusb_context *ctx,
+ struct discovered_devs **discdevs);
+
+ /* Apps which were written before hotplug support, may listen for
+ * hotplug events on their own and call libusb_get_device_list on
+ * device addition. In this case libusb_get_device_list will likely
+ * return a list without the new device in there, as the hotplug
+ * event thread will still be busy enumerating the device, which may
+ * take a while, or may not even have seen the event yet.
+ *
+ * To avoid this libusb_get_device_list will call this optional
+ * function for backends with hotplug support before copying
+ * ctx->usb_devs to the user. In this function the backend should
+ * ensure any pending hotplug events are fully processed before
+ * returning.
+ *
+ * Optional, should be implemented by backends with hotplug support.
+ */
+ void (*hotplug_poll)(void);
+
+ /* Open a device for I/O and other USB operations. The device handle
+ * is preallocated for you, you can retrieve the device in question
+ * through handle->dev.
+ *
+ * Your backend should allocate any internal resources required for I/O
+ * and other operations so that those operations can happen (hopefully)
+ * without hiccup. This is also a good place to inform libusb that it
+ * should monitor certain file descriptors related to this device -
+ * see the usbi_add_pollfd() function.
+ *
+ * This function should not generate any bus I/O and should not block.
+ *
+ * This function is called when the user attempts to obtain a device
+ * handle for a device.
+ *
+ * Return:
+ * - 0 on success
+ * - LIBUSB_ERROR_ACCESS if the user has insufficient permissions
+ * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since
+ * discovery
+ * - another LIBUSB_ERROR code on other failure
+ *
+ * Do not worry about freeing the handle on failed open, the upper layers
+ * do this for you.
+ */
+ int (*open)(struct libusb_device_handle *dev_handle);
+
+ /* Close a device such that the handle cannot be used again. Your backend
+ * should destroy any resources that were allocated in the open path.
+ * This may also be a good place to call usbi_remove_pollfd() to inform
+ * libusb of any file descriptors associated with this device that should
+ * no longer be monitored.
+ *
+ * This function is called when the user closes a device handle.
+ */
+ void (*close)(struct libusb_device_handle *dev_handle);
+
+ /* Retrieve the device descriptor from a device.
+ *
+ * The descriptor should be retrieved from memory, NOT via bus I/O to the
+ * device. This means that you may have to cache it in a private structure
+ * during get_device_list enumeration. Alternatively, you may be able
+ * to retrieve it from a kernel interface (some Linux setups can do this)
+ * still without generating bus I/O.
+ *
+ * This function is expected to write DEVICE_DESC_LENGTH (18) bytes into
+ * buffer, which is guaranteed to be big enough.
+ *
+ * This function is called when sanity-checking a device before adding
+ * it to the list of discovered devices, and also when the user requests
+ * to read the device descriptor.
+ *
+ * This function is expected to return the descriptor in bus-endian format
+ * (LE). If it returns the multi-byte values in host-endian format,
+ * set the host_endian output parameter to "1".
+ *
+ * Return 0 on success or a LIBUSB_ERROR code on failure.
+ */
+ int (*get_device_descriptor)(struct libusb_device *device,
+ unsigned char *buffer, int *host_endian);
+
+ /* Get the ACTIVE configuration descriptor for a device.
+ *
+ * The descriptor should be retrieved from memory, NOT via bus I/O to the
+ * device. This means that you may have to cache it in a private structure
+ * during get_device_list enumeration. You may also have to keep track
+ * of which configuration is active when the user changes it.
+ *
+ * This function is expected to write len bytes of data into buffer, which
+ * is guaranteed to be big enough. If you can only do a partial write,
+ * return an error code.
+ *
+ * This function is expected to return the descriptor in bus-endian format
+ * (LE). If it returns the multi-byte values in host-endian format,
+ * set the host_endian output parameter to "1".
+ *
+ * Return:
+ * - 0 on success
+ * - LIBUSB_ERROR_NOT_FOUND if the device is in unconfigured state
+ * - another LIBUSB_ERROR code on other failure
+ */
+ int (*get_active_config_descriptor)(struct libusb_device *device,
+ unsigned char *buffer, size_t len, int *host_endian);
+
+ /* Get a specific configuration descriptor for a device.
+ *
+ * The descriptor should be retrieved from memory, NOT via bus I/O to the
+ * device. This means that you may have to cache it in a private structure
+ * during get_device_list enumeration.
+ *
+ * The requested descriptor is expressed as a zero-based index (i.e. 0
+ * indicates that we are requesting the first descriptor). The index does
+ * not (necessarily) equal the bConfigurationValue of the configuration
+ * being requested.
+ *
+ * This function is expected to write len bytes of data into buffer, which
+ * is guaranteed to be big enough. If you can only do a partial write,
+ * return an error code.
+ *
+ * This function is expected to return the descriptor in bus-endian format
+ * (LE). If it returns the multi-byte values in host-endian format,
+ * set the host_endian output parameter to "1".
+ *
+ * Return the length read on success or a LIBUSB_ERROR code on failure.
+ */
+ int (*get_config_descriptor)(struct libusb_device *device,
+ uint8_t config_index, unsigned char *buffer, size_t len,
+ int *host_endian);
+
+ /* Like get_config_descriptor but then by bConfigurationValue instead
+ * of by index.
+ *
+ * Optional, if not present the core will call get_config_descriptor
+ * for all configs until it finds the desired bConfigurationValue.
+ *
+ * Returns a pointer to the raw-descriptor in *buffer, this memory
+ * is valid as long as device is valid.
+ *
+ * Returns the length of the returned raw-descriptor on success,
+ * or a LIBUSB_ERROR code on failure.
+ */
+ int (*get_config_descriptor_by_value)(struct libusb_device *device,
+ uint8_t bConfigurationValue, unsigned char **buffer,
+ int *host_endian);
+
+ /* Get the bConfigurationValue for the active configuration for a device.
+ * Optional. This should only be implemented if you can retrieve it from
+ * cache (don't generate I/O).
+ *
+ * If you cannot retrieve this from cache, either do not implement this
+ * function, or return LIBUSB_ERROR_NOT_SUPPORTED. This will cause
+ * libusb to retrieve the information through a standard control transfer.
+ *
+ * This function must be non-blocking.
+ * Return:
+ * - 0 on success
+ * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it
+ * was opened
+ * - LIBUSB_ERROR_NOT_SUPPORTED if the value cannot be retrieved without
+ * blocking
+ * - another LIBUSB_ERROR code on other failure.
+ */
+ int (*get_configuration)(struct libusb_device_handle *dev_handle, int *config);
+
+ /* Set the active configuration for a device.
+ *
+ * A configuration value of -1 should put the device in unconfigured state.
+ *
+ * This function can block.
+ *
+ * Return:
+ * - 0 on success
+ * - LIBUSB_ERROR_NOT_FOUND if the configuration does not exist
+ * - LIBUSB_ERROR_BUSY if interfaces are currently claimed (and hence
+ * configuration cannot be changed)
+ * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it
+ * was opened
+ * - another LIBUSB_ERROR code on other failure.
+ */
+ int (*set_configuration)(struct libusb_device_handle *dev_handle, int config);
+
+ /* Claim an interface. When claimed, the application can then perform
+ * I/O to an interface's endpoints.
+ *
+ * This function should not generate any bus I/O and should not block.
+ * Interface claiming is a logical operation that simply ensures that
+ * no other drivers/applications are using the interface, and after
+ * claiming, no other drivers/applications can use the interface because
+ * we now "own" it.
+ *
+ * Return:
+ * - 0 on success
+ * - LIBUSB_ERROR_NOT_FOUND if the interface does not exist
+ * - LIBUSB_ERROR_BUSY if the interface is in use by another driver/app
+ * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it
+ * was opened
+ * - another LIBUSB_ERROR code on other failure
+ */
+ int (*claim_interface)(struct libusb_device_handle *dev_handle, int interface_number);
+
+ /* Release a previously claimed interface.
+ *
+ * This function should also generate a SET_INTERFACE control request,
+ * resetting the alternate setting of that interface to 0. It's OK for
+ * this function to block as a result.
+ *
+ * You will only ever be asked to release an interface which was
+ * successfully claimed earlier.
+ *
+ * Return:
+ * - 0 on success
+ * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it
+ * was opened
+ * - another LIBUSB_ERROR code on other failure
+ */
+ int (*release_interface)(struct libusb_device_handle *dev_handle, int interface_number);
+
+ /* Set the alternate setting for an interface.
+ *
+ * You will only ever be asked to set the alternate setting for an
+ * interface which was successfully claimed earlier.
+ *
+ * It's OK for this function to block.
+ *
+ * Return:
+ * - 0 on success
+ * - LIBUSB_ERROR_NOT_FOUND if the alternate setting does not exist
+ * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it
+ * was opened
+ * - another LIBUSB_ERROR code on other failure
+ */
+ int (*set_interface_altsetting)(struct libusb_device_handle *dev_handle,
+ int interface_number, int altsetting);
+
+ /* Clear a halt/stall condition on an endpoint.
+ *
+ * It's OK for this function to block.
+ *
+ * Return:
+ * - 0 on success
+ * - LIBUSB_ERROR_NOT_FOUND if the endpoint does not exist
+ * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it
+ * was opened
+ * - another LIBUSB_ERROR code on other failure
+ */
+ int (*clear_halt)(struct libusb_device_handle *dev_handle,
+ unsigned char endpoint);
+
+ /* Perform a USB port reset to reinitialize a device.
+ *
+ * If possible, the device handle should still be usable after the reset
+ * completes, assuming that the device descriptors did not change during
+ * reset and all previous interface state can be restored.
+ *
+ * If something changes, or you cannot easily locate/verify the resetted
+ * device, return LIBUSB_ERROR_NOT_FOUND. This prompts the application
+ * to close the old handle and re-enumerate the device.
+ *
+ * Return:
+ * - 0 on success
+ * - LIBUSB_ERROR_NOT_FOUND if re-enumeration is required, or if the device
+ * has been disconnected since it was opened
+ * - another LIBUSB_ERROR code on other failure
+ */
+ int (*reset_device)(struct libusb_device_handle *dev_handle);
+
+ /* Alloc num_streams usb3 bulk streams on the passed in endpoints */
+ int (*alloc_streams)(struct libusb_device_handle *dev_handle,
+ uint32_t num_streams, unsigned char *endpoints, int num_endpoints);
+
+ /* Free usb3 bulk streams allocated with alloc_streams */
+ int (*free_streams)(struct libusb_device_handle *dev_handle,
+ unsigned char *endpoints, int num_endpoints);
+
+ /* Allocate persistent DMA memory for the given device, suitable for
+ * zerocopy. May return NULL on failure. Optional to implement.
+ */
+ unsigned char *(*dev_mem_alloc)(struct libusb_device_handle *handle,
+ size_t len);
+
+ /* Free memory allocated by dev_mem_alloc. */
+ int (*dev_mem_free)(struct libusb_device_handle *handle,
+ unsigned char *buffer, size_t len);
+
+ /* Determine if a kernel driver is active on an interface. Optional.
+ *
+ * The presence of a kernel driver on an interface indicates that any
+ * calls to claim_interface would fail with the LIBUSB_ERROR_BUSY code.
+ *
+ * Return:
+ * - 0 if no driver is active
+ * - 1 if a driver is active
+ * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it
+ * was opened
+ * - another LIBUSB_ERROR code on other failure
+ */
+ int (*kernel_driver_active)(struct libusb_device_handle *dev_handle,
+ int interface_number);
+
+ /* Detach a kernel driver from an interface. Optional.
+ *
+ * After detaching a kernel driver, the interface should be available
+ * for claim.
+ *
+ * Return:
+ * - 0 on success
+ * - LIBUSB_ERROR_NOT_FOUND if no kernel driver was active
+ * - LIBUSB_ERROR_INVALID_PARAM if the interface does not exist
+ * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it
+ * was opened
+ * - another LIBUSB_ERROR code on other failure
+ */
+ int (*detach_kernel_driver)(struct libusb_device_handle *dev_handle,
+ int interface_number);
+
+ /* Attach a kernel driver to an interface. Optional.
+ *
+ * Reattach a kernel driver to the device.
+ *
+ * Return:
+ * - 0 on success
+ * - LIBUSB_ERROR_NOT_FOUND if no kernel driver was active
+ * - LIBUSB_ERROR_INVALID_PARAM if the interface does not exist
+ * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it
+ * was opened
+ * - LIBUSB_ERROR_BUSY if a program or driver has claimed the interface,
+ * preventing reattachment
+ * - another LIBUSB_ERROR code on other failure
+ */
+ int (*attach_kernel_driver)(struct libusb_device_handle *dev_handle,
+ int interface_number);
+
+ /* Destroy a device. Optional.
+ *
+ * This function is called when the last reference to a device is
+ * destroyed. It should free any resources allocated in the get_device_list
+ * path.
+ */
+ void (*destroy_device)(struct libusb_device *dev);
+
+ /* Submit a transfer. Your implementation should take the transfer,
+ * morph it into whatever form your platform requires, and submit it
+ * asynchronously.
+ *
+ * This function must not block.
+ *
+ * This function gets called with the flying_transfers_lock locked!
+ *
+ * Return:
+ * - 0 on success
+ * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * - another LIBUSB_ERROR code on other failure
+ */
+ int (*submit_transfer)(struct usbi_transfer *itransfer);
+
+ /* Cancel a previously submitted transfer.
+ *
+ * This function must not block. The transfer cancellation must complete
+ * later, resulting in a call to usbi_handle_transfer_cancellation()
+ * from the context of handle_events.
+ */
+ int (*cancel_transfer)(struct usbi_transfer *itransfer);
+
+ /* Clear a transfer as if it has completed or cancelled, but do not
+ * report any completion/cancellation to the library. You should free
+ * all private data from the transfer as if you were just about to report
+ * completion or cancellation.
+ *
+ * This function might seem a bit out of place. It is used when libusb
+ * detects a disconnected device - it calls this function for all pending
+ * transfers before reporting completion (with the disconnect code) to
+ * the user. Maybe we can improve upon this internal interface in future.
+ */
+ void (*clear_transfer_priv)(struct usbi_transfer *itransfer);
+
+ /* Handle any pending events on file descriptors. Optional.
+ *
+ * Provide this function when file descriptors directly indicate device
+ * or transfer activity. If your backend does not have such file descriptors,
+ * implement the handle_transfer_completion function below.
+ *
+ * This involves monitoring any active transfers and processing their
+ * completion or cancellation.
+ *
+ * The function is passed an array of pollfd structures (size nfds)
+ * as a result of the poll() system call. The num_ready parameter
+ * indicates the number of file descriptors that have reported events
+ * (i.e. the poll() return value). This should be enough information
+ * for you to determine which actions need to be taken on the currently
+ * active transfers.
+ *
+ * For any cancelled transfers, call usbi_handle_transfer_cancellation().
+ * For completed transfers, call usbi_handle_transfer_completion().
+ * For control/bulk/interrupt transfers, populate the "transferred"
+ * element of the appropriate usbi_transfer structure before calling the
+ * above functions. For isochronous transfers, populate the status and
+ * transferred fields of the iso packet descriptors of the transfer.
+ *
+ * This function should also be able to detect disconnection of the
+ * device, reporting that situation with usbi_handle_disconnect().
+ *
+ * When processing an event related to a transfer, you probably want to
+ * take usbi_transfer.lock to prevent races. See the documentation for
+ * the usbi_transfer structure.
+ *
+ * Return 0 on success, or a LIBUSB_ERROR code on failure.
+ */
+ int (*handle_events)(struct libusb_context *ctx,
+ struct pollfd *fds, POLL_NFDS_TYPE nfds, int num_ready);
+
+ /* Handle transfer completion. Optional.
+ *
+ * Provide this function when there are no file descriptors available
+ * that directly indicate device or transfer activity. If your backend does
+ * have such file descriptors, implement the handle_events function above.
+ *
+ * Your backend must tell the library when a transfer has completed by
+ * calling usbi_signal_transfer_completion(). You should store any private
+ * information about the transfer and its completion status in the transfer's
+ * private backend data.
+ *
+ * During event handling, this function will be called on each transfer for
+ * which usbi_signal_transfer_completion() was called.
+ *
+ * For any cancelled transfers, call usbi_handle_transfer_cancellation().
+ * For completed transfers, call usbi_handle_transfer_completion().
+ * For control/bulk/interrupt transfers, populate the "transferred"
+ * element of the appropriate usbi_transfer structure before calling the
+ * above functions. For isochronous transfers, populate the status and
+ * transferred fields of the iso packet descriptors of the transfer.
+ *
+ * Return 0 on success, or a LIBUSB_ERROR code on failure.
+ */
+ int (*handle_transfer_completion)(struct usbi_transfer *itransfer);
+
+ /* Get time from specified clock. At least two clocks must be implemented
+ by the backend: USBI_CLOCK_REALTIME, and USBI_CLOCK_MONOTONIC.
+
+ Description of clocks:
+ USBI_CLOCK_REALTIME : clock returns time since system epoch.
+ USBI_CLOCK_MONOTONIC: clock returns time since unspecified start
+ time (usually boot).
+ */
+ int (*clock_gettime)(int clkid, struct timespec *tp);
+
+#ifdef USBI_TIMERFD_AVAILABLE
+ /* clock ID of the clock that should be used for timerfd */
+ clockid_t (*get_timerfd_clockid)(void);
+#endif
+
+ /* 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. */
+ size_t device_priv_size;
+
+ /* Number of bytes to reserve for per-handle private backend data.
+ * This private data area is accessible through the "os_priv" field of
+ * struct libusb_device. */
+ size_t device_handle_priv_size;
+
+ /* Number of bytes to reserve for per-transfer private backend data.
+ * This private data area is accessible by calling
+ * usbi_transfer_get_os_priv() on the appropriate usbi_transfer instance.
+ */
+ 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 struct list_head active_contexts_list;
+extern usbi_mutex_static_t active_contexts_lock;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/darwin_usb.c b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/darwin_usb.c
new file mode 100644
index 000000000..b0219d1b0
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/darwin_usb.c
@@ -0,0 +1,2094 @@
+/* -*- Mode: C; indent-tabs-mode:nil -*- */
+/*
+ * darwin backend for libusb 1.0
+ * Copyright © 2008-2016 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
+ * 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 <time.h>
+#include <ctype.h>
+#include <errno.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/sysctl.h>
+
+#include <mach/clock.h>
+#include <mach/clock_types.h>
+#include <mach/mach_host.h>
+#include <mach/mach_port.h>
+
+#include <AvailabilityMacros.h>
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 && MAC_OS_X_VERSION_MIN_REQUIRED < 101200
+ #include <objc/objc-auto.h>
+#endif
+
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= 101200
+/* Apple deprecated the darwin atomics in 10.12 in favor of C11 atomics */
+#include <stdatomic.h>
+#define libusb_darwin_atomic_fetch_add(x, y) atomic_fetch_add(x, y)
+
+_Atomic int32_t initCount = ATOMIC_VAR_INIT(0);
+#else
+/* use darwin atomics if the target is older than 10.12 */
+#include <libkern/OSAtomic.h>
+
+/* OSAtomicAdd32Barrier returns the new value */
+#define libusb_darwin_atomic_fetch_add(x, y) (OSAtomicAdd32Barrier(y, x) - y)
+
+static volatile int32_t initCount = 0;
+#endif
+
+#include "darwin_usb.h"
+
+/* async event thread */
+static pthread_mutex_t libusb_darwin_at_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t libusb_darwin_at_cond = PTHREAD_COND_INITIALIZER;
+
+static pthread_once_t darwin_init_once = PTHREAD_ONCE_INIT;
+
+static clock_serv_t clock_realtime;
+static clock_serv_t clock_monotonic;
+
+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;
+
+#define DARWIN_CACHED_DEVICE(a) ((struct darwin_cached_device *) (((struct darwin_device_priv *)((a)->os_priv))->dev))
+
+/* async event thread */
+static pthread_t libusb_darwin_at;
+
+static int darwin_get_config_descriptor(struct libusb_device *dev, uint8_t config_index, unsigned char *buffer, size_t len, int *host_endian);
+static int darwin_claim_interface(struct libusb_device_handle *dev_handle, int iface);
+static int darwin_release_interface(struct libusb_device_handle *dev_handle, int iface);
+static int darwin_reset_device(struct libusb_device_handle *dev_handle);
+static void darwin_async_io_callback (void *refcon, IOReturn result, void *arg0);
+
+static int darwin_scan_devices(struct libusb_context *ctx);
+static int process_new_device (struct libusb_context *ctx, io_service_t service);
+
+#if defined(ENABLE_LOGGING)
+static const char *darwin_error_str (int result) {
+ static char string_buffer[50];
+ switch (result) {
+ case kIOReturnSuccess:
+ return "no error";
+ case kIOReturnNotOpen:
+ return "device not opened for exclusive access";
+ case kIOReturnNoDevice:
+ return "no connection to an IOService";
+ case kIOUSBNoAsyncPortErr:
+ return "no async port has been opened for interface";
+ case kIOReturnExclusiveAccess:
+ return "another process has device opened for exclusive access";
+ case kIOUSBPipeStalled:
+ return "pipe is stalled";
+ case kIOReturnError:
+ return "could not establish a connection to the Darwin kernel";
+ case kIOUSBTransactionTimeout:
+ return "transaction timed out";
+ case kIOReturnBadArgument:
+ return "invalid argument";
+ case kIOReturnAborted:
+ return "transaction aborted";
+ case kIOReturnNotResponding:
+ return "device not responding";
+ case kIOReturnOverrun:
+ return "data overrun";
+ case kIOReturnCannotWire:
+ return "physical memory can not be wired down";
+ case kIOReturnNoResources:
+ return "out of resources";
+ case kIOUSBHighSpeedSplitError:
+ return "high speed split error";
+ default:
+ snprintf(string_buffer, sizeof(string_buffer), "unknown error (0x%x)", result);
+ return string_buffer;
+ }
+}
+#endif
+
+static int darwin_to_libusb (int result) {
+ switch (result) {
+ case kIOReturnUnderrun:
+ case kIOReturnSuccess:
+ return LIBUSB_SUCCESS;
+ case kIOReturnNotOpen:
+ case kIOReturnNoDevice:
+ return LIBUSB_ERROR_NO_DEVICE;
+ case kIOReturnExclusiveAccess:
+ return LIBUSB_ERROR_ACCESS;
+ case kIOUSBPipeStalled:
+ return LIBUSB_ERROR_PIPE;
+ case kIOReturnBadArgument:
+ return LIBUSB_ERROR_INVALID_PARAM;
+ case kIOUSBTransactionTimeout:
+ return LIBUSB_ERROR_TIMEOUT;
+ case kIOReturnNotResponding:
+ case kIOReturnAborted:
+ case kIOReturnError:
+ case kIOUSBNoAsyncPortErr:
+ default:
+ return LIBUSB_ERROR_OTHER;
+ }
+}
+
+/* this function must be called with the darwin_cached_devices_lock held */
+static void darwin_deref_cached_device(struct darwin_cached_device *cached_dev) {
+ cached_dev->refcount--;
+ /* free the device and remove it from the cache */
+ if (0 == cached_dev->refcount) {
+ list_del(&cached_dev->list);
+
+ (*(cached_dev->device))->Release(cached_dev->device);
+ free (cached_dev);
+ }
+}
+
+static void darwin_ref_cached_device(struct darwin_cached_device *cached_dev) {
+ cached_dev->refcount++;
+}
+
+static int ep_to_pipeRef(struct libusb_device_handle *dev_handle, uint8_t ep, uint8_t *pipep, uint8_t *ifcp, struct darwin_interface **interface_out) {
+ struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv;
+
+ /* current interface */
+ struct darwin_interface *cInterface;
+
+ int8_t i, iface;
+
+ usbi_dbg ("converting ep address 0x%02x to pipeRef and interface", ep);
+
+ for (iface = 0 ; iface < USB_MAXINTERFACES ; iface++) {
+ cInterface = &priv->interfaces[iface];
+
+ if (dev_handle->claimed_interfaces & (1 << iface)) {
+ for (i = 0 ; i < cInterface->num_endpoints ; i++) {
+ if (cInterface->endpoint_addrs[i] == ep) {
+ *pipep = i + 1;
+
+ if (ifcp)
+ *ifcp = iface;
+
+ if (interface_out)
+ *interface_out = cInterface;
+
+ usbi_dbg ("pipe %d on interface %d matches", *pipep, iface);
+ return 0;
+ }
+ }
+ }
+ }
+
+ /* No pipe found with the correct endpoint address */
+ usbi_warn (HANDLE_CTX(dev_handle), "no pipeRef found with endpoint address 0x%02x.", ep);
+
+ return LIBUSB_ERROR_NOT_FOUND;
+}
+
+static int usb_setup_device_iterator (io_iterator_t *deviceIterator, UInt32 location) {
+ CFMutableDictionaryRef matchingDict = IOServiceMatching(darwin_device_class);
+
+ if (!matchingDict)
+ return kIOReturnError;
+
+ if (location) {
+ CFMutableDictionaryRef propertyMatchDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+
+ if (propertyMatchDict) {
+ /* there are no unsigned CFNumber types so treat the value as signed. the os seems to do this
+ internally (CFNumberType of locationID is 3) */
+ CFTypeRef locationCF = CFNumberCreate (NULL, kCFNumberSInt32Type, &location);
+
+ 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 */
+ }
+
+ return IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, deviceIterator);
+}
+
+/* Returns 1 on success, 0 on failure. */
+static int get_ioregistry_value_number (io_service_t service, CFStringRef property, CFNumberType type, void *p) {
+ CFTypeRef cfNumber = IORegistryEntryCreateCFProperty (service, property, kCFAllocatorDefault, 0);
+ int ret = 0;
+
+ if (cfNumber) {
+ if (CFGetTypeID(cfNumber) == CFNumberGetTypeID()) {
+ ret = CFNumberGetValue(cfNumber, type, p);
+ }
+
+ CFRelease (cfNumber);
+ }
+
+ return ret;
+}
+
+static int get_ioregistry_value_data (io_service_t service, CFStringRef property, ssize_t size, void *p) {
+ CFTypeRef cfData = IORegistryEntryCreateCFProperty (service, property, kCFAllocatorDefault, 0);
+ int ret = 0;
+
+ if (cfData) {
+ if (CFGetTypeID (cfData) == CFDataGetTypeID ()) {
+ CFIndex length = CFDataGetLength (cfData);
+ if (length < size) {
+ size = length;
+ }
+
+ CFDataGetBytes (cfData, CFRangeMake(0, size), p);
+ ret = 1;
+ }
+
+ CFRelease (cfData);
+ }
+
+ return ret;
+}
+
+static usb_device_t **darwin_device_from_service (io_service_t service)
+{
+ io_cf_plugin_ref_t *plugInInterface = NULL;
+ usb_device_t **device;
+ kern_return_t result;
+ SInt32 score;
+
+ result = IOCreatePlugInInterfaceForService(service, kIOUSBDeviceUserClientTypeID,
+ kIOCFPlugInInterfaceID, &plugInInterface,
+ &score);
+
+ if (kIOReturnSuccess != result || !plugInInterface) {
+ usbi_dbg ("could not set up plugin for service: %s", darwin_error_str (result));
+ return NULL;
+ }
+
+ (void)(*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(DeviceInterfaceID),
+ (LPVOID)&device);
+ /* Use release instead of IODestroyPlugInInterface to avoid stopping IOServices associated with this device */
+ (*plugInInterface)->Release (plugInInterface);
+
+ return device;
+}
+
+static void darwin_devices_attached (void *ptr, io_iterator_t add_devices) {
+ struct libusb_context *ctx;
+ io_service_t service;
+
+ usbi_mutex_lock(&active_contexts_lock);
+
+ 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);;
+ }
+
+ IOObjectRelease(service);
+ }
+
+ usbi_mutex_unlock(&active_contexts_lock);
+}
+
+static void darwin_devices_detached (void *ptr, io_iterator_t rem_devices) {
+ struct libusb_device *dev = NULL;
+ struct libusb_context *ctx;
+ struct darwin_cached_device *old_device;
+
+ io_service_t device;
+ UInt64 session;
+ int ret;
+
+ usbi_mutex_lock(&active_contexts_lock);
+
+ while ((device = IOIteratorNext (rem_devices)) != 0) {
+ /* get the location from the i/o registry */
+ ret = get_ioregistry_value_number (device, CFSTR("sessionID"), kCFNumberSInt64Type, &session);
+ IOObjectRelease (device);
+ if (!ret)
+ continue;
+
+ /* we need to match darwin_ref_cached_device call made in darwin_get_cached_device function
+ otherwise no cached device will ever get freed */
+ usbi_mutex_lock(&darwin_cached_devices_lock);
+ list_for_each_entry(old_device, &darwin_cached_devices, list, struct darwin_cached_device) {
+ if (old_device->session == session) {
+ darwin_deref_cached_device (old_device);
+ break;
+ }
+ }
+ usbi_mutex_unlock(&darwin_cached_devices_lock);
+
+ list_for_each_entry(ctx, &active_contexts_list, list, struct libusb_context) {
+ usbi_dbg ("notifying context %p of device disconnect", ctx);
+
+ dev = usbi_get_device_by_session_id(ctx, (unsigned long) session);
+ if (dev) {
+ /* signal the core that this device has been disconnected. the core will tear down this device
+ when the reference count reaches 0 */
+ usbi_disconnect_device(dev);
+ libusb_unref_device(dev);
+ }
+ }
+ }
+
+ usbi_mutex_unlock(&active_contexts_lock);
+}
+
+static void darwin_hotplug_poll (void)
+{
+ /* not sure if 5 seconds will be too long/short but it should work ok */
+ mach_timespec_t timeout = {.tv_sec = 5, .tv_nsec = 0};
+
+ /* since a kernel thread may nodify the IOInterators used for
+ * hotplug notidication we can't just clear the iterators.
+ * instead just wait until all IOService providers are quiet */
+ (void) IOKitWaitQuiet (kIOMasterPortDefault, &timeout);
+}
+
+static void darwin_clear_iterator (io_iterator_t iter) {
+ io_service_t device;
+
+ while ((device = IOIteratorNext (iter)) != 0)
+ IOObjectRelease (device);
+}
+
+static void *darwin_event_thread_main (void *arg0) {
+ IOReturn kresult;
+ struct libusb_context *ctx = (struct libusb_context *)arg0;
+ CFRunLoopRef runloop;
+
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
+ /* Set this thread's name, so it can be seen in the debugger
+ and crash reports. */
+ pthread_setname_np ("org.libusb.device-hotplug");
+#endif
+
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 && MAC_OS_X_VERSION_MIN_REQUIRED < 101200
+ /* Tell the Objective-C garbage collector about this thread.
+ This is required because, unlike NSThreads, pthreads are
+ not automatically registered. Although we don't use
+ Objective-C, we use CoreFoundation, which does.
+ Garbage collection support was entirely removed in 10.12,
+ so don't bother there. */
+ objc_registerThreadWithCollector();
+#endif
+
+ /* hotplug (device arrival/removal) sources */
+ CFRunLoopSourceContext libusb_shutdown_cfsourcectx;
+ CFRunLoopSourceRef libusb_notification_cfsource;
+ io_notification_port_t libusb_notification_port;
+ io_iterator_t libusb_rem_device_iterator;
+ io_iterator_t libusb_add_device_iterator;
+
+ usbi_dbg ("creating hotplug event source");
+
+ runloop = CFRunLoopGetCurrent ();
+ CFRetain (runloop);
+
+ /* add the shutdown cfsource to the run loop */
+ memset(&libusb_shutdown_cfsourcectx, 0, sizeof(libusb_shutdown_cfsourcectx));
+ libusb_shutdown_cfsourcectx.info = runloop;
+ libusb_shutdown_cfsourcectx.perform = (void (*)(void *))CFRunLoopStop;
+ libusb_darwin_acfls = CFRunLoopSourceCreate(NULL, 0, &libusb_shutdown_cfsourcectx);
+ CFRunLoopAddSource(runloop, libusb_darwin_acfls, kCFRunLoopDefaultMode);
+
+ /* add the notification port to the run loop */
+ libusb_notification_port = IONotificationPortCreate (kIOMasterPortDefault);
+ libusb_notification_cfsource = IONotificationPortGetRunLoopSource (libusb_notification_port);
+ CFRunLoopAddSource(runloop, libusb_notification_cfsource, kCFRunLoopDefaultMode);
+
+ /* create notifications for removed devices */
+ kresult = IOServiceAddMatchingNotification (libusb_notification_port, kIOTerminatedNotification,
+ IOServiceMatching(darwin_device_class),
+ darwin_devices_detached,
+ ctx, &libusb_rem_device_iterator);
+
+ if (kresult != kIOReturnSuccess) {
+ usbi_err (ctx, "could not add hotplug event source: %s", darwin_error_str (kresult));
+
+ pthread_exit (NULL);
+ }
+
+ /* create notifications for attached devices */
+ kresult = IOServiceAddMatchingNotification(libusb_notification_port, kIOFirstMatchNotification,
+ IOServiceMatching(darwin_device_class),
+ darwin_devices_attached,
+ ctx, &libusb_add_device_iterator);
+
+ if (kresult != kIOReturnSuccess) {
+ usbi_err (ctx, "could not add hotplug event source: %s", darwin_error_str (kresult));
+
+ pthread_exit (NULL);
+ }
+
+ /* arm notifiers */
+ darwin_clear_iterator (libusb_rem_device_iterator);
+ darwin_clear_iterator (libusb_add_device_iterator);
+
+ usbi_dbg ("darwin event thread ready to receive events");
+
+ /* signal the main thread that the hotplug runloop has been created. */
+ pthread_mutex_lock (&libusb_darwin_at_mutex);
+ libusb_darwin_acfl = runloop;
+ pthread_cond_signal (&libusb_darwin_at_cond);
+ pthread_mutex_unlock (&libusb_darwin_at_mutex);
+
+ /* run the runloop */
+ CFRunLoopRun();
+
+ usbi_dbg ("darwin event thread exiting");
+
+ /* remove the notification cfsource */
+ CFRunLoopRemoveSource(runloop, libusb_notification_cfsource, kCFRunLoopDefaultMode);
+
+ /* remove the shutdown cfsource */
+ CFRunLoopRemoveSource(runloop, libusb_darwin_acfls, kCFRunLoopDefaultMode);
+
+ /* delete notification port */
+ IONotificationPortDestroy (libusb_notification_port);
+
+ /* delete iterators */
+ IOObjectRelease (libusb_rem_device_iterator);
+ IOObjectRelease (libusb_add_device_iterator);
+
+ CFRelease (libusb_darwin_acfls);
+ CFRelease (runloop);
+
+ libusb_darwin_acfls = NULL;
+ libusb_darwin_acfl = NULL;
+
+ pthread_exit (NULL);
+}
+
+/* cleanup function to destroy cached devices */
+static void __attribute__((destructor)) _darwin_finalize(void) {
+ struct darwin_cached_device *dev, *next;
+
+ usbi_mutex_lock(&darwin_cached_devices_lock);
+ list_for_each_entry_safe(dev, next, &darwin_cached_devices, list, struct darwin_cached_device) {
+ darwin_deref_cached_device(dev);
+ }
+ usbi_mutex_unlock(&darwin_cached_devices_lock);
+}
+
+static void darwin_check_version (void) {
+ /* adjust for changes in the USB stack in xnu 15 */
+ int sysctl_args[] = {CTL_KERN, KERN_OSRELEASE};
+ long version;
+ char version_string[256] = {'\0',};
+ size_t length = 256;
+
+ sysctl(sysctl_args, 2, version_string, &length, NULL, 0);
+
+ errno = 0;
+ version = strtol (version_string, NULL, 10);
+ if (0 == errno && version >= 15) {
+ darwin_device_class = "IOUSBHostDevice";
+ }
+}
+
+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);
+ if (rc) {
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ rc = darwin_scan_devices (ctx);
+ if (LIBUSB_SUCCESS != rc) {
+ return rc;
+ }
+
+ if (libusb_darwin_atomic_fetch_add (&initCount, 1) == 0) {
+ /* create the clocks that will be used */
+
+ 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);
+
+ pthread_create (&libusb_darwin_at, NULL, darwin_event_thread_main, ctx);
+
+ pthread_mutex_lock (&libusb_darwin_at_mutex);
+ while (!libusb_darwin_acfl)
+ pthread_cond_wait (&libusb_darwin_at_cond, &libusb_darwin_at_mutex);
+ pthread_mutex_unlock (&libusb_darwin_at_mutex);
+ }
+
+ return rc;
+}
+
+static void darwin_exit (void) {
+ if (libusb_darwin_atomic_fetch_add (&initCount, -1) == 1) {
+ mach_port_deallocate(mach_task_self(), clock_realtime);
+ mach_port_deallocate(mach_task_self(), clock_monotonic);
+
+ /* stop the event runloop and wait for the thread to terminate. */
+ CFRunLoopSourceSignal(libusb_darwin_acfls);
+ CFRunLoopWakeUp (libusb_darwin_acfl);
+ pthread_join (libusb_darwin_at, NULL);
+ }
+}
+
+static int darwin_get_device_descriptor(struct libusb_device *dev, unsigned char *buffer, int *host_endian) {
+ struct darwin_cached_device *priv = DARWIN_CACHED_DEVICE(dev);
+
+ /* return cached copy */
+ memmove (buffer, &(priv->dev_descriptor), DEVICE_DESC_LENGTH);
+
+ *host_endian = 0;
+
+ return 0;
+}
+
+static int get_configuration_index (struct libusb_device *dev, int config_value) {
+ struct darwin_cached_device *priv = DARWIN_CACHED_DEVICE(dev);
+ UInt8 i, numConfig;
+ IOUSBConfigurationDescriptorPtr desc;
+ IOReturn kresult;
+
+ /* is there a simpler way to determine the index? */
+ kresult = (*(priv->device))->GetNumberOfConfigurations (priv->device, &numConfig);
+ if (kresult != kIOReturnSuccess)
+ return darwin_to_libusb (kresult);
+
+ for (i = 0 ; i < numConfig ; i++) {
+ (*(priv->device))->GetConfigurationDescriptorPtr (priv->device, i, &desc);
+
+ if (desc->bConfigurationValue == config_value)
+ return i;
+ }
+
+ /* configuration not found */
+ return LIBUSB_ERROR_NOT_FOUND;
+}
+
+static int darwin_get_active_config_descriptor(struct libusb_device *dev, unsigned char *buffer, size_t len, int *host_endian) {
+ struct darwin_cached_device *priv = DARWIN_CACHED_DEVICE(dev);
+ int config_index;
+
+ if (0 == priv->active_config)
+ return LIBUSB_ERROR_NOT_FOUND;
+
+ config_index = get_configuration_index (dev, priv->active_config);
+ if (config_index < 0)
+ return config_index;
+
+ return darwin_get_config_descriptor (dev, config_index, buffer, len, host_endian);
+}
+
+static int darwin_get_config_descriptor(struct libusb_device *dev, uint8_t config_index, unsigned char *buffer, size_t len, int *host_endian) {
+ struct darwin_cached_device *priv = DARWIN_CACHED_DEVICE(dev);
+ IOUSBConfigurationDescriptorPtr desc;
+ IOReturn kresult;
+ int ret;
+
+ if (!priv || !priv->device)
+ return LIBUSB_ERROR_OTHER;
+
+ kresult = (*priv->device)->GetConfigurationDescriptorPtr (priv->device, config_index, &desc);
+ if (kresult == kIOReturnSuccess) {
+ /* copy descriptor */
+ if (libusb_le16_to_cpu(desc->wTotalLength) < len)
+ len = libusb_le16_to_cpu(desc->wTotalLength);
+
+ memmove (buffer, desc, len);
+
+ /* GetConfigurationDescriptorPtr returns the descriptor in USB bus order */
+ *host_endian = 0;
+ }
+
+ ret = darwin_to_libusb (kresult);
+ if (ret != LIBUSB_SUCCESS)
+ return ret;
+
+ return (int) len;
+}
+
+/* check whether the os has configured the device */
+static int darwin_check_configuration (struct libusb_context *ctx, struct darwin_cached_device *dev) {
+ usb_device_t **darwin_device = dev->device;
+
+ IOUSBConfigurationDescriptorPtr configDesc;
+ IOUSBFindInterfaceRequest request;
+ kern_return_t kresult;
+ io_iterator_t interface_iterator;
+ io_service_t firstInterface;
+
+ if (dev->dev_descriptor.bNumConfigurations < 1) {
+ usbi_err (ctx, "device has no configurations");
+ return LIBUSB_ERROR_OTHER; /* no configurations at this speed so we can't use it */
+ }
+
+ /* checking the configuration of a root hub simulation takes ~1 s in 10.11. the device is
+ not usable anyway */
+ if (0x05ac == dev->dev_descriptor.idVendor && 0x8005 == dev->dev_descriptor.idProduct) {
+ usbi_dbg ("ignoring configuration on root hub simulation");
+ dev->active_config = 0;
+ return 0;
+ }
+
+ /* find the first configuration */
+ kresult = (*darwin_device)->GetConfigurationDescriptorPtr (darwin_device, 0, &configDesc);
+ dev->first_config = (kIOReturnSuccess == kresult) ? configDesc->bConfigurationValue : 1;
+
+ /* check if the device is already configured. there is probably a better way than iterating over the
+ to accomplish this (the trick is we need to avoid a call to GetConfigurations since buggy devices
+ might lock up on the device request) */
+
+ /* Setup the Interface Request */
+ request.bInterfaceClass = kIOUSBFindInterfaceDontCare;
+ request.bInterfaceSubClass = kIOUSBFindInterfaceDontCare;
+ request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;
+ request.bAlternateSetting = kIOUSBFindInterfaceDontCare;
+
+ kresult = (*(darwin_device))->CreateInterfaceIterator(darwin_device, &request, &interface_iterator);
+ if (kresult)
+ return darwin_to_libusb (kresult);
+
+ /* iterate once */
+ firstInterface = IOIteratorNext(interface_iterator);
+
+ /* done with the interface iterator */
+ IOObjectRelease(interface_iterator);
+
+ if (firstInterface) {
+ IOObjectRelease (firstInterface);
+
+ /* device is configured */
+ if (dev->dev_descriptor.bNumConfigurations == 1)
+ /* to avoid problems with some devices get the configurations value from the configuration descriptor */
+ dev->active_config = dev->first_config;
+ else
+ /* devices with more than one configuration should work with GetConfiguration */
+ (*darwin_device)->GetConfiguration (darwin_device, &dev->active_config);
+ } else
+ /* not configured */
+ dev->active_config = 0;
+
+ usbi_dbg ("active config: %u, first config: %u", dev->active_config, dev->first_config);
+
+ return 0;
+}
+
+static int darwin_request_descriptor (usb_device_t **device, UInt8 desc, UInt8 desc_index, void *buffer, size_t buffer_size) {
+ IOUSBDevRequestTO req;
+
+ memset (buffer, 0, buffer_size);
+
+ /* Set up request for descriptor/ */
+ req.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
+ req.bRequest = kUSBRqGetDescriptor;
+ req.wValue = desc << 8;
+ req.wIndex = desc_index;
+ req.wLength = buffer_size;
+ req.pData = buffer;
+ req.noDataTimeout = 20;
+ req.completionTimeout = 100;
+
+ return (*device)->DeviceRequestTO (device, &req);
+}
+
+static int darwin_cache_device_descriptor (struct libusb_context *ctx, struct darwin_cached_device *dev) {
+ usb_device_t **device = dev->device;
+ int retries = 1, delay = 30000;
+ int unsuspended = 0, try_unsuspend = 1, try_reconfigure = 1;
+ int is_open = 0;
+ int ret = 0, ret2;
+ UInt8 bDeviceClass;
+ UInt16 idProduct, idVendor;
+
+ dev->can_enumerate = 0;
+
+ (*device)->GetDeviceClass (device, &bDeviceClass);
+ (*device)->GetDeviceProduct (device, &idProduct);
+ (*device)->GetDeviceVendor (device, &idVendor);
+
+ /* According to Apple's documentation the device must be open for DeviceRequest but we may not be able to open some
+ * devices and Apple's USB Prober doesn't bother to open the device before issuing a descriptor request. Still,
+ * to follow the spec as closely as possible, try opening the device */
+ is_open = ((*device)->USBDeviceOpenSeize(device) == kIOReturnSuccess);
+
+ do {
+ /**** retrieve device descriptor ****/
+ ret = darwin_request_descriptor (device, kUSBDeviceDesc, 0, &dev->dev_descriptor, sizeof(dev->dev_descriptor));
+
+ if (kIOReturnOverrun == ret && kUSBDeviceDesc == dev->dev_descriptor.bDescriptorType)
+ /* received an overrun error but we still received a device descriptor */
+ ret = kIOReturnSuccess;
+
+ if (kIOUSBVendorIDAppleComputer == idVendor) {
+ /* NTH: don't bother retrying or unsuspending Apple devices */
+ break;
+ }
+
+ if (kIOReturnSuccess == ret && (0 == dev->dev_descriptor.bNumConfigurations ||
+ 0 == dev->dev_descriptor.bcdUSB)) {
+ /* work around for incorrectly configured devices */
+ if (try_reconfigure && is_open) {
+ usbi_dbg("descriptor appears to be invalid. resetting configuration before trying again...");
+
+ /* set the first configuration */
+ (*device)->SetConfiguration(device, 1);
+
+ /* don't try to reconfigure again */
+ try_reconfigure = 0;
+ }
+
+ ret = kIOUSBPipeStalled;
+ }
+
+ if (kIOReturnSuccess != ret && is_open && try_unsuspend) {
+ /* device may be suspended. unsuspend it and try again */
+#if DeviceVersion >= 320
+ UInt32 info = 0;
+
+ /* IOUSBFamily 320+ provides a way to detect device suspension but earlier versions do not */
+ (void)(*device)->GetUSBDeviceInformation (device, &info);
+
+ /* note that the device was suspended */
+ if (info & (1 << kUSBInformationDeviceIsSuspendedBit) || 0 == info)
+ try_unsuspend = 1;
+#endif
+
+ if (try_unsuspend) {
+ /* try to unsuspend the device */
+ ret2 = (*device)->USBDeviceSuspend (device, 0);
+ if (kIOReturnSuccess != ret2) {
+ /* prevent log spew from poorly behaving devices. this indicates the
+ os actually had trouble communicating with the device */
+ usbi_dbg("could not retrieve device descriptor. failed to unsuspend: %s",darwin_error_str(ret2));
+ } else
+ unsuspended = 1;
+
+ try_unsuspend = 0;
+ }
+ }
+
+ if (kIOReturnSuccess != ret) {
+ usbi_dbg("kernel responded with code: 0x%08x. sleeping for %d ms before trying again", ret, delay/1000);
+ /* sleep for a little while before trying again */
+ nanosleep(&(struct timespec){delay / 1000000, (delay * 1000) % 1000000000UL}, NULL);
+ }
+ } while (kIOReturnSuccess != ret && retries--);
+
+ if (unsuspended)
+ /* resuspend the device */
+ (void)(*device)->USBDeviceSuspend (device, 1);
+
+ if (is_open)
+ (void) (*device)->USBDeviceClose (device);
+
+ if (ret != kIOReturnSuccess) {
+ /* a debug message was already printed out for this error */
+ if (LIBUSB_CLASS_HUB == bDeviceClass)
+ usbi_dbg ("could not retrieve device descriptor %.4x:%.4x: %s (%x). skipping device",
+ idVendor, idProduct, darwin_error_str (ret), ret);
+ else
+ usbi_warn (ctx, "could not retrieve device descriptor %.4x:%.4x: %s (%x). skipping device",
+ idVendor, idProduct, darwin_error_str (ret), ret);
+ return darwin_to_libusb (ret);
+ }
+
+ /* catch buggy hubs (which appear to be virtual). Apple's own USB prober has problems with these devices. */
+ if (libusb_le16_to_cpu (dev->dev_descriptor.idProduct) != idProduct) {
+ /* not a valid device */
+ usbi_warn (ctx, "idProduct from iokit (%04x) does not match idProduct in descriptor (%04x). skipping device",
+ idProduct, libusb_le16_to_cpu (dev->dev_descriptor.idProduct));
+ return LIBUSB_ERROR_NO_DEVICE;
+ }
+
+ usbi_dbg ("cached device descriptor:");
+ usbi_dbg (" bDescriptorType: 0x%02x", dev->dev_descriptor.bDescriptorType);
+ usbi_dbg (" bcdUSB: 0x%04x", dev->dev_descriptor.bcdUSB);
+ usbi_dbg (" bDeviceClass: 0x%02x", dev->dev_descriptor.bDeviceClass);
+ usbi_dbg (" bDeviceSubClass: 0x%02x", dev->dev_descriptor.bDeviceSubClass);
+ usbi_dbg (" bDeviceProtocol: 0x%02x", dev->dev_descriptor.bDeviceProtocol);
+ usbi_dbg (" bMaxPacketSize0: 0x%02x", dev->dev_descriptor.bMaxPacketSize0);
+ usbi_dbg (" idVendor: 0x%04x", dev->dev_descriptor.idVendor);
+ usbi_dbg (" idProduct: 0x%04x", dev->dev_descriptor.idProduct);
+ usbi_dbg (" bcdDevice: 0x%04x", dev->dev_descriptor.bcdDevice);
+ usbi_dbg (" iManufacturer: 0x%02x", dev->dev_descriptor.iManufacturer);
+ usbi_dbg (" iProduct: 0x%02x", dev->dev_descriptor.iProduct);
+ usbi_dbg (" iSerialNumber: 0x%02x", dev->dev_descriptor.iSerialNumber);
+ usbi_dbg (" bNumConfigurations: 0x%02x", dev->dev_descriptor.bNumConfigurations);
+
+ dev->can_enumerate = 1;
+
+ return LIBUSB_SUCCESS;
+}
+
+static int get_device_port (io_service_t service, UInt8 *port) {
+ kern_return_t result;
+ io_service_t parent;
+ int ret = 0;
+
+ if (get_ioregistry_value_number (service, CFSTR("PortNum"), kCFNumberSInt8Type, port)) {
+ return 1;
+ }
+
+ result = IORegistryEntryGetParentEntry (service, kIOServicePlane, &parent);
+ if (kIOReturnSuccess == result) {
+ ret = get_ioregistry_value_data (parent, CFSTR("port"), 1, port);
+ IOObjectRelease (parent);
+ }
+
+ return ret;
+}
+
+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 */
+ (void) get_ioregistry_value_number (service, CFSTR("sessionID"), kCFNumberSInt64Type, &sessionID);
+ if (!get_device_port (service, &port)) {
+ usbi_dbg("could not get connected port number");
+ }
+
+ 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);
+ }
+
+ usbi_mutex_lock(&darwin_cached_devices_lock);
+ do {
+ *cached_out = NULL;
+
+ list_for_each_entry(new_device, &darwin_cached_devices, list, struct darwin_cached_device) {
+ usbi_dbg("matching sessionID 0x%" PRIx64 " against cached device with sessionID 0x%" PRIx64, sessionID, new_device->session);
+ if (new_device->session == sessionID) {
+ usbi_dbg("using cached device for device");
+ *cached_out = new_device;
+ break;
+ }
+ }
+
+ if (*cached_out)
+ break;
+
+ usbi_dbg("caching new device with sessionID 0x%" PRIx64, sessionID);
+
+ device = darwin_device_from_service (service);
+ if (!device) {
+ ret = LIBUSB_ERROR_NO_DEVICE;
+ break;
+ }
+
+ new_device = calloc (1, sizeof (*new_device));
+ if (!new_device) {
+ ret = LIBUSB_ERROR_NO_MEM;
+ break;
+ }
+
+ /* add this device to the cached device list */
+ list_add(&new_device->list, &darwin_cached_devices);
+
+ (*device)->GetDeviceAddress (device, (USBDeviceAddress *)&new_device->address);
+
+ /* keep a reference to this device */
+ darwin_ref_cached_device(new_device);
+
+ new_device->device = device;
+ new_device->session = sessionID;
+ (*device)->GetLocationID (device, &new_device->location);
+ new_device->port = port;
+ new_device->parent_session = parent_sessionID;
+
+ /* cache the device descriptor */
+ ret = darwin_cache_device_descriptor(ctx, new_device);
+ if (ret)
+ break;
+
+ if (new_device->can_enumerate) {
+ snprintf(new_device->sys_path, 20, "%03i-%04x-%04x-%02x-%02x", new_device->address,
+ new_device->dev_descriptor.idVendor, new_device->dev_descriptor.idProduct,
+ new_device->dev_descriptor.bDeviceClass, new_device->dev_descriptor.bDeviceSubClass);
+ }
+ } while (0);
+
+ usbi_mutex_unlock(&darwin_cached_devices_lock);
+
+ /* keep track of devices regardless of if we successfully enumerate them to
+ prevent them from being enumerated multiple times */
+
+ *cached_out = new_device;
+
+ return ret;
+}
+
+static int process_new_device (struct libusb_context *ctx, io_service_t service) {
+ struct darwin_device_priv *priv;
+ struct libusb_device *dev = NULL;
+ struct darwin_cached_device *cached_device;
+ UInt8 devSpeed;
+ int ret = 0;
+
+ do {
+ ret = darwin_get_cached_device (ctx, service, &cached_device);
+
+ if (ret < 0 || !cached_device->can_enumerate) {
+ return ret;
+ }
+
+ /* check current active configuration (and cache the first configuration value--
+ which may be used by claim_interface) */
+ ret = darwin_check_configuration (ctx, cached_device);
+ if (ret)
+ break;
+
+ usbi_dbg ("allocating new device in context %p for with session 0x%" PRIx64,
+ ctx, cached_device->session);
+
+ dev = usbi_alloc_device(ctx, (unsigned long) cached_device->session);
+ if (!dev) {
+ return LIBUSB_ERROR_NO_MEM;
+ }
+
+ priv = (struct darwin_device_priv *)dev->os_priv;
+
+ priv->dev = cached_device;
+ darwin_ref_cached_device (priv->dev);
+
+ if (cached_device->parent_session > 0) {
+ dev->parent_dev = usbi_get_device_by_session_id (ctx, (unsigned long) cached_device->parent_session);
+ } else {
+ dev->parent_dev = NULL;
+ }
+ dev->port_number = cached_device->port;
+ dev->bus_number = cached_device->location >> 24;
+ dev->device_address = cached_device->address;
+
+ (*(priv->dev->device))->GetDeviceSpeed (priv->dev->device, &devSpeed);
+
+ switch (devSpeed) {
+ 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
+ case kUSBDeviceSpeedSuper: dev->speed = LIBUSB_SPEED_SUPER; break;
+#endif
+ default:
+ usbi_warn (ctx, "Got unknown device speed %d", devSpeed);
+ }
+
+ ret = usbi_sanitize_device (dev);
+ if (ret < 0)
+ break;
+
+ usbi_dbg ("found device with address %d port = %d parent = %p at %p", dev->device_address,
+ dev->port_number, (void *) dev->parent_dev, priv->dev->sys_path);
+ } while (0);
+
+ if (0 == ret) {
+ usbi_connect_device (dev);
+ } else {
+ libusb_unref_device (dev);
+ }
+
+ return ret;
+}
+
+static int darwin_scan_devices(struct libusb_context *ctx) {
+ io_iterator_t deviceIterator;
+ io_service_t service;
+ kern_return_t kresult;
+
+ kresult = usb_setup_device_iterator (&deviceIterator, 0);
+ if (kresult != kIOReturnSuccess)
+ return darwin_to_libusb (kresult);
+
+ while ((service = IOIteratorNext (deviceIterator))) {
+ (void) process_new_device (ctx, service);
+
+ IOObjectRelease(service);
+ }
+
+ IOObjectRelease(deviceIterator);
+
+ return 0;
+}
+
+static int darwin_open (struct libusb_device_handle *dev_handle) {
+ struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv;
+ struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
+ IOReturn kresult;
+
+ if (0 == dpriv->open_count) {
+ /* try to open the device */
+ kresult = (*(dpriv->device))->USBDeviceOpenSeize (dpriv->device);
+ if (kresult != kIOReturnSuccess) {
+ usbi_warn (HANDLE_CTX (dev_handle), "USBDeviceOpen: %s", darwin_error_str(kresult));
+
+ if (kIOReturnExclusiveAccess != kresult) {
+ return darwin_to_libusb (kresult);
+ }
+
+ /* it is possible to perform some actions on a device that is not open so do not return an error */
+ priv->is_open = 0;
+ } else {
+ priv->is_open = 1;
+ }
+
+ /* create async event source */
+ kresult = (*(dpriv->device))->CreateDeviceAsyncEventSource (dpriv->device, &priv->cfSource);
+ if (kresult != kIOReturnSuccess) {
+ usbi_err (HANDLE_CTX (dev_handle), "CreateDeviceAsyncEventSource: %s", darwin_error_str(kresult));
+
+ if (priv->is_open) {
+ (*(dpriv->device))->USBDeviceClose (dpriv->device);
+ }
+
+ priv->is_open = 0;
+
+ return darwin_to_libusb (kresult);
+ }
+
+ CFRetain (libusb_darwin_acfl);
+
+ /* add the cfSource to the aync run loop */
+ CFRunLoopAddSource(libusb_darwin_acfl, priv->cfSource, kCFRunLoopCommonModes);
+ }
+
+ /* device opened successfully */
+ dpriv->open_count++;
+
+ usbi_dbg ("device open for access");
+
+ return 0;
+}
+
+static void darwin_close (struct libusb_device_handle *dev_handle) {
+ struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv;
+ struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
+ IOReturn kresult;
+ int i;
+
+ if (dpriv->open_count == 0) {
+ /* something is probably very wrong if this is the case */
+ usbi_err (HANDLE_CTX (dev_handle), "Close called on a device that was not open!");
+ return;
+ }
+
+ dpriv->open_count--;
+
+ /* make sure all interfaces are released */
+ for (i = 0 ; i < USB_MAXINTERFACES ; i++)
+ if (dev_handle->claimed_interfaces & (1 << i))
+ libusb_release_interface (dev_handle, i);
+
+ if (0 == dpriv->open_count) {
+ /* delete the device's async event source */
+ if (priv->cfSource) {
+ CFRunLoopRemoveSource (libusb_darwin_acfl, priv->cfSource, kCFRunLoopDefaultMode);
+ CFRelease (priv->cfSource);
+ priv->cfSource = NULL;
+ CFRelease (libusb_darwin_acfl);
+ }
+
+ if (priv->is_open) {
+ /* close the device */
+ kresult = (*(dpriv->device))->USBDeviceClose(dpriv->device);
+ if (kresult) {
+ /* Log the fact that we had a problem closing the file, however failing a
+ * close isn't really an error, so return success anyway */
+ usbi_warn (HANDLE_CTX (dev_handle), "USBDeviceClose: %s", darwin_error_str(kresult));
+ }
+ }
+ }
+}
+
+static int darwin_get_configuration(struct libusb_device_handle *dev_handle, int *config) {
+ struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
+
+ *config = (int) dpriv->active_config;
+
+ return 0;
+}
+
+static int darwin_set_configuration(struct libusb_device_handle *dev_handle, int config) {
+ struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
+ IOReturn kresult;
+ int i;
+
+ /* Setting configuration will invalidate the interface, so we need
+ to reclaim it. First, dispose of existing interfaces, if any. */
+ for (i = 0 ; i < USB_MAXINTERFACES ; i++)
+ if (dev_handle->claimed_interfaces & (1 << i))
+ darwin_release_interface (dev_handle, i);
+
+ kresult = (*(dpriv->device))->SetConfiguration (dpriv->device, config);
+ if (kresult != kIOReturnSuccess)
+ return darwin_to_libusb (kresult);
+
+ /* Reclaim any interfaces. */
+ for (i = 0 ; i < USB_MAXINTERFACES ; i++)
+ if (dev_handle->claimed_interfaces & (1 << i))
+ darwin_claim_interface (dev_handle, i);
+
+ dpriv->active_config = config;
+
+ return 0;
+}
+
+static int darwin_get_interface (usb_device_t **darwin_device, uint8_t ifc, io_service_t *usbInterfacep) {
+ IOUSBFindInterfaceRequest request;
+ kern_return_t kresult;
+ io_iterator_t interface_iterator;
+ UInt8 bInterfaceNumber;
+ int ret;
+
+ *usbInterfacep = IO_OBJECT_NULL;
+
+ /* Setup the Interface Request */
+ request.bInterfaceClass = kIOUSBFindInterfaceDontCare;
+ request.bInterfaceSubClass = kIOUSBFindInterfaceDontCare;
+ request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;
+ request.bAlternateSetting = kIOUSBFindInterfaceDontCare;
+
+ kresult = (*(darwin_device))->CreateInterfaceIterator(darwin_device, &request, &interface_iterator);
+ if (kresult)
+ return kresult;
+
+ while ((*usbInterfacep = IOIteratorNext(interface_iterator))) {
+ /* find the interface number */
+ ret = get_ioregistry_value_number (*usbInterfacep, CFSTR("bInterfaceNumber"), kCFNumberSInt8Type,
+ &bInterfaceNumber);
+
+ if (ret && bInterfaceNumber == ifc) {
+ break;
+ }
+
+ (void) IOObjectRelease (*usbInterfacep);
+ }
+
+ /* done with the interface iterator */
+ IOObjectRelease(interface_iterator);
+
+ return 0;
+}
+
+static int get_endpoints (struct libusb_device_handle *dev_handle, int iface) {
+ struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv;
+
+ /* current interface */
+ struct darwin_interface *cInterface = &priv->interfaces[iface];
+
+ kern_return_t kresult;
+
+ u_int8_t numep, direction, number;
+ u_int8_t dont_care1, dont_care3;
+ u_int16_t dont_care2;
+ int rc;
+
+ usbi_dbg ("building table of endpoints.");
+
+ /* retrieve the total number of endpoints on this interface */
+ kresult = (*(cInterface->interface))->GetNumEndpoints(cInterface->interface, &numep);
+ if (kresult) {
+ usbi_err (HANDLE_CTX (dev_handle), "can't get number of endpoints for interface: %s", darwin_error_str(kresult));
+ return darwin_to_libusb (kresult);
+ }
+
+ /* iterate through pipe references */
+ for (int i = 1 ; i <= numep ; i++) {
+ kresult = (*(cInterface->interface))->GetPipeProperties(cInterface->interface, i, &direction, &number, &dont_care1,
+ &dont_care2, &dont_care3);
+
+ if (kresult != kIOReturnSuccess) {
+ /* probably a buggy device. try to get the endpoint address from the descriptors */
+ struct libusb_config_descriptor *config;
+ const struct libusb_endpoint_descriptor *endpoint_desc;
+ UInt8 alt_setting;
+
+ kresult = (*(cInterface->interface))->GetAlternateSetting (cInterface->interface, &alt_setting);
+ if (kresult) {
+ usbi_err (HANDLE_CTX (dev_handle), "can't get alternate setting for interface");
+ return darwin_to_libusb (kresult);
+ }
+
+ rc = libusb_get_active_config_descriptor (dev_handle->dev, &config);
+ if (LIBUSB_SUCCESS != rc) {
+ return rc;
+ }
+
+ endpoint_desc = config->interface[iface].altsetting[alt_setting].endpoint + i - 1;
+
+ cInterface->endpoint_addrs[i - 1] = endpoint_desc->bEndpointAddress;
+ } else {
+ cInterface->endpoint_addrs[i - 1] = (((kUSBIn == direction) << kUSBRqDirnShift) | (number & LIBUSB_ENDPOINT_ADDRESS_MASK));
+ }
+
+ usbi_dbg ("interface: %i pipe %i: dir: %i number: %i", iface, i, cInterface->endpoint_addrs[i - 1] >> kUSBRqDirnShift,
+ cInterface->endpoint_addrs[i - 1] & LIBUSB_ENDPOINT_ADDRESS_MASK);
+ }
+
+ cInterface->num_endpoints = numep;
+
+ return 0;
+}
+
+static int darwin_claim_interface(struct libusb_device_handle *dev_handle, int iface) {
+ struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
+ struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv;
+ io_service_t usbInterface = IO_OBJECT_NULL;
+ IOReturn kresult;
+ IOCFPlugInInterface **plugInInterface = NULL;
+ SInt32 score;
+
+ /* current interface */
+ struct darwin_interface *cInterface = &priv->interfaces[iface];
+
+ kresult = darwin_get_interface (dpriv->device, iface, &usbInterface);
+ if (kresult != kIOReturnSuccess)
+ return darwin_to_libusb (kresult);
+
+ /* make sure we have an interface */
+ if (!usbInterface && dpriv->first_config != 0) {
+ usbi_info (HANDLE_CTX (dev_handle), "no interface found; setting configuration: %d", dpriv->first_config);
+
+ /* set the configuration */
+ kresult = darwin_set_configuration (dev_handle, dpriv->first_config);
+ if (kresult != LIBUSB_SUCCESS) {
+ usbi_err (HANDLE_CTX (dev_handle), "could not set configuration");
+ return kresult;
+ }
+
+ kresult = darwin_get_interface (dpriv->device, iface, &usbInterface);
+ if (kresult) {
+ usbi_err (HANDLE_CTX (dev_handle), "darwin_get_interface: %s", darwin_error_str(kresult));
+ return darwin_to_libusb (kresult);
+ }
+ }
+
+ if (!usbInterface) {
+ usbi_err (HANDLE_CTX (dev_handle), "interface not found");
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
+
+ /* get an interface to the device's interface */
+ kresult = IOCreatePlugInInterfaceForService (usbInterface, kIOUSBInterfaceUserClientTypeID,
+ kIOCFPlugInInterfaceID, &plugInInterface, &score);
+
+ /* ignore release error */
+ (void)IOObjectRelease (usbInterface);
+
+ if (kresult) {
+ usbi_err (HANDLE_CTX (dev_handle), "IOCreatePlugInInterfaceForService: %s", darwin_error_str(kresult));
+ return darwin_to_libusb (kresult);
+ }
+
+ if (!plugInInterface) {
+ usbi_err (HANDLE_CTX (dev_handle), "plugin interface not found");
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
+
+ /* Do the actual claim */
+ kresult = (*plugInInterface)->QueryInterface(plugInInterface,
+ CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID),
+ (LPVOID)&cInterface->interface);
+ /* We no longer need the intermediate plug-in */
+ /* Use release instead of IODestroyPlugInInterface to avoid stopping IOServices associated with this device */
+ (*plugInInterface)->Release (plugInInterface);
+ if (kresult || !cInterface->interface) {
+ usbi_err (HANDLE_CTX (dev_handle), "QueryInterface: %s", darwin_error_str(kresult));
+ return darwin_to_libusb (kresult);
+ }
+
+ /* claim the interface */
+ kresult = (*(cInterface->interface))->USBInterfaceOpen(cInterface->interface);
+ if (kresult) {
+ usbi_err (HANDLE_CTX (dev_handle), "USBInterfaceOpen: %s", darwin_error_str(kresult));
+ return darwin_to_libusb (kresult);
+ }
+
+ /* update list of endpoints */
+ kresult = get_endpoints (dev_handle, iface);
+ if (kresult) {
+ /* this should not happen */
+ darwin_release_interface (dev_handle, iface);
+ usbi_err (HANDLE_CTX (dev_handle), "could not build endpoint table");
+ return kresult;
+ }
+
+ cInterface->cfSource = NULL;
+
+ /* create async event source */
+ kresult = (*(cInterface->interface))->CreateInterfaceAsyncEventSource (cInterface->interface, &cInterface->cfSource);
+ if (kresult != kIOReturnSuccess) {
+ usbi_err (HANDLE_CTX (dev_handle), "could not create async event source");
+
+ /* can't continue without an async event source */
+ (void)darwin_release_interface (dev_handle, iface);
+
+ return darwin_to_libusb (kresult);
+ }
+
+ /* add the cfSource to the async thread's run loop */
+ CFRunLoopAddSource(libusb_darwin_acfl, cInterface->cfSource, kCFRunLoopDefaultMode);
+
+ usbi_dbg ("interface opened");
+
+ return 0;
+}
+
+static int darwin_release_interface(struct libusb_device_handle *dev_handle, int iface) {
+ struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv;
+ IOReturn kresult;
+
+ /* current interface */
+ struct darwin_interface *cInterface = &priv->interfaces[iface];
+
+ /* Check to see if an interface is open */
+ if (!cInterface->interface)
+ return LIBUSB_SUCCESS;
+
+ /* clean up endpoint data */
+ cInterface->num_endpoints = 0;
+
+ /* delete the interface's async event source */
+ if (cInterface->cfSource) {
+ CFRunLoopRemoveSource (libusb_darwin_acfl, cInterface->cfSource, kCFRunLoopDefaultMode);
+ CFRelease (cInterface->cfSource);
+ }
+
+ kresult = (*(cInterface->interface))->USBInterfaceClose(cInterface->interface);
+ if (kresult)
+ usbi_warn (HANDLE_CTX (dev_handle), "USBInterfaceClose: %s", darwin_error_str(kresult));
+
+ kresult = (*(cInterface->interface))->Release(cInterface->interface);
+ if (kresult != kIOReturnSuccess)
+ usbi_warn (HANDLE_CTX (dev_handle), "Release: %s", darwin_error_str(kresult));
+
+ cInterface->interface = (usb_interface_t **) IO_OBJECT_NULL;
+
+ return darwin_to_libusb (kresult);
+}
+
+static int darwin_set_interface_altsetting(struct libusb_device_handle *dev_handle, int iface, int altsetting) {
+ struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv;
+ IOReturn kresult;
+
+ /* current interface */
+ struct darwin_interface *cInterface = &priv->interfaces[iface];
+
+ if (!cInterface->interface)
+ return LIBUSB_ERROR_NO_DEVICE;
+
+ kresult = (*(cInterface->interface))->SetAlternateInterface (cInterface->interface, altsetting);
+ if (kresult != kIOReturnSuccess)
+ darwin_reset_device (dev_handle);
+
+ /* update list of endpoints */
+ kresult = get_endpoints (dev_handle, iface);
+ if (kresult) {
+ /* this should not happen */
+ darwin_release_interface (dev_handle, iface);
+ usbi_err (HANDLE_CTX (dev_handle), "could not build endpoint table");
+ return kresult;
+ }
+
+ return darwin_to_libusb (kresult);
+}
+
+static int darwin_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint) {
+ /* current interface */
+ struct darwin_interface *cInterface;
+ IOReturn kresult;
+ uint8_t pipeRef;
+
+ /* determine the interface/endpoint to use */
+ if (ep_to_pipeRef (dev_handle, endpoint, &pipeRef, NULL, &cInterface) != 0) {
+ usbi_err (HANDLE_CTX (dev_handle), "endpoint not found on any open interface");
+
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
+
+ /* newer versions of darwin support clearing additional bits on the device's endpoint */
+ kresult = (*(cInterface->interface))->ClearPipeStallBothEnds(cInterface->interface, pipeRef);
+ if (kresult)
+ usbi_warn (HANDLE_CTX (dev_handle), "ClearPipeStall: %s", darwin_error_str (kresult));
+
+ return darwin_to_libusb (kresult);
+}
+
+static int darwin_reset_device(struct libusb_device_handle *dev_handle) {
+ struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
+ IOUSBDeviceDescriptor descriptor;
+ IOUSBConfigurationDescriptorPtr cached_configuration;
+ IOUSBConfigurationDescriptor configuration;
+ bool reenumerate = false;
+ IOReturn kresult;
+ int i;
+
+ kresult = (*(dpriv->device))->ResetDevice (dpriv->device);
+ if (kresult) {
+ usbi_err (HANDLE_CTX (dev_handle), "ResetDevice: %s", darwin_error_str (kresult));
+ return darwin_to_libusb (kresult);
+ }
+
+ do {
+ usbi_dbg ("darwin/reset_device: checking if device descriptor changed");
+
+ /* ignore return code. if we can't get a descriptor it might be worthwhile re-enumerating anway */
+ (void) darwin_request_descriptor (dpriv->device, kUSBDeviceDesc, 0, &descriptor, sizeof (descriptor));
+
+ /* check if the device descriptor has changed */
+ if (0 != memcmp (&dpriv->dev_descriptor, &descriptor, sizeof (descriptor))) {
+ reenumerate = true;
+ break;
+ }
+
+ /* check if any configuration descriptor has changed */
+ for (i = 0 ; i < descriptor.bNumConfigurations ; ++i) {
+ usbi_dbg ("darwin/reset_device: checking if configuration descriptor %d changed", i);
+
+ (void) darwin_request_descriptor (dpriv->device, kUSBConfDesc, i, &configuration, sizeof (configuration));
+ (*(dpriv->device))->GetConfigurationDescriptorPtr (dpriv->device, i, &cached_configuration);
+
+ if (!cached_configuration || 0 != memcmp (cached_configuration, &configuration, sizeof (configuration))) {
+ reenumerate = true;
+ break;
+ }
+ }
+ } while (0);
+
+ if (reenumerate) {
+ usbi_dbg ("darwin/reset_device: device requires reenumeration");
+ (void) (*(dpriv->device))->USBDeviceReEnumerate (dpriv->device, 0);
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
+
+ usbi_dbg ("darwin/reset_device: device reset complete");
+
+ return LIBUSB_SUCCESS;
+}
+
+static int darwin_kernel_driver_active(struct libusb_device_handle *dev_handle, int interface) {
+ struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
+ io_service_t usbInterface;
+ CFTypeRef driver;
+ IOReturn kresult;
+
+ kresult = darwin_get_interface (dpriv->device, interface, &usbInterface);
+ if (kresult) {
+ usbi_err (HANDLE_CTX (dev_handle), "darwin_get_interface: %s", darwin_error_str(kresult));
+
+ return darwin_to_libusb (kresult);
+ }
+
+ driver = IORegistryEntryCreateCFProperty (usbInterface, kIOBundleIdentifierKey, kCFAllocatorDefault, 0);
+ IOObjectRelease (usbInterface);
+
+ if (driver) {
+ CFRelease (driver);
+
+ return 1;
+ }
+
+ /* no driver */
+ return 0;
+}
+
+/* attaching/detaching kernel drivers is not currently supported (maybe in the future?) */
+static int darwin_attach_kernel_driver (struct libusb_device_handle *dev_handle, int interface) {
+ UNUSED(dev_handle);
+ UNUSED(interface);
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+}
+
+static int darwin_detach_kernel_driver (struct libusb_device_handle *dev_handle, int interface) {
+ UNUSED(dev_handle);
+ UNUSED(interface);
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+}
+
+static void darwin_destroy_device(struct libusb_device *dev) {
+ struct darwin_device_priv *dpriv = (struct darwin_device_priv *) dev->os_priv;
+
+ if (dpriv->dev) {
+ /* need to hold the lock in case this is the last reference to the device */
+ usbi_mutex_lock(&darwin_cached_devices_lock);
+ darwin_deref_cached_device (dpriv->dev);
+ dpriv->dev = NULL;
+ usbi_mutex_unlock(&darwin_cached_devices_lock);
+ }
+}
+
+static int submit_bulk_transfer(struct usbi_transfer *itransfer) {
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+
+ IOReturn ret;
+ uint8_t transferType;
+ /* None of the values below are used in libusbx for bulk transfers */
+ uint8_t direction, number, interval, pipeRef;
+ uint16_t maxPacketSize;
+
+ struct darwin_interface *cInterface;
+
+ if (ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, NULL, &cInterface) != 0) {
+ usbi_err (TRANSFER_CTX (transfer), "endpoint not found on any open interface");
+
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
+
+ ret = (*(cInterface->interface))->GetPipeProperties (cInterface->interface, pipeRef, &direction, &number,
+ &transferType, &maxPacketSize, &interval);
+
+ if (ret) {
+ usbi_err (TRANSFER_CTX (transfer), "bulk transfer failed (dir = %s): %s (code = 0x%08x)", IS_XFERIN(transfer) ? "In" : "Out",
+ darwin_error_str(ret), ret);
+ return darwin_to_libusb (ret);
+ }
+
+ if (0 != (transfer->length % maxPacketSize)) {
+ /* do not need a zero packet */
+ transfer->flags &= ~LIBUSB_TRANSFER_ADD_ZERO_PACKET;
+ }
+
+ /* submit the request */
+ /* timeouts are unavailable on interrupt endpoints */
+ if (transferType == kUSBInterrupt) {
+ if (IS_XFERIN(transfer))
+ ret = (*(cInterface->interface))->ReadPipeAsync(cInterface->interface, pipeRef, transfer->buffer,
+ transfer->length, darwin_async_io_callback, itransfer);
+ else
+ ret = (*(cInterface->interface))->WritePipeAsync(cInterface->interface, pipeRef, transfer->buffer,
+ transfer->length, darwin_async_io_callback, itransfer);
+ } else {
+ itransfer->timeout_flags |= USBI_TRANSFER_OS_HANDLES_TIMEOUT;
+
+ if (IS_XFERIN(transfer))
+ ret = (*(cInterface->interface))->ReadPipeAsyncTO(cInterface->interface, pipeRef, transfer->buffer,
+ transfer->length, transfer->timeout, transfer->timeout,
+ darwin_async_io_callback, (void *)itransfer);
+ else
+ ret = (*(cInterface->interface))->WritePipeAsyncTO(cInterface->interface, pipeRef, transfer->buffer,
+ transfer->length, transfer->timeout, transfer->timeout,
+ darwin_async_io_callback, (void *)itransfer);
+ }
+
+ if (ret)
+ usbi_err (TRANSFER_CTX (transfer), "bulk transfer failed (dir = %s): %s (code = 0x%08x)", IS_XFERIN(transfer) ? "In" : "Out",
+ darwin_error_str(ret), ret);
+
+ return darwin_to_libusb (ret);
+}
+
+#if InterfaceVersion >= 550
+static int submit_stream_transfer(struct usbi_transfer *itransfer) {
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct darwin_interface *cInterface;
+ uint8_t pipeRef;
+ IOReturn ret;
+
+ if (ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, NULL, &cInterface) != 0) {
+ usbi_err (TRANSFER_CTX (transfer), "endpoint not found on any open interface");
+
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
+
+ itransfer->timeout_flags |= USBI_TRANSFER_OS_HANDLES_TIMEOUT;
+
+ if (IS_XFERIN(transfer))
+ ret = (*(cInterface->interface))->ReadStreamsPipeAsyncTO(cInterface->interface, pipeRef, itransfer->stream_id,
+ transfer->buffer, transfer->length, transfer->timeout,
+ transfer->timeout, darwin_async_io_callback, (void *)itransfer);
+ else
+ ret = (*(cInterface->interface))->WriteStreamsPipeAsyncTO(cInterface->interface, pipeRef, itransfer->stream_id,
+ transfer->buffer, transfer->length, transfer->timeout,
+ transfer->timeout, darwin_async_io_callback, (void *)itransfer);
+
+ if (ret)
+ usbi_err (TRANSFER_CTX (transfer), "bulk stream transfer failed (dir = %s): %s (code = 0x%08x)", IS_XFERIN(transfer) ? "In" : "Out",
+ darwin_error_str(ret), ret);
+
+ return darwin_to_libusb (ret);
+}
+#endif
+
+static int submit_iso_transfer(struct usbi_transfer *itransfer) {
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct darwin_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer);
+
+ IOReturn kresult;
+ uint8_t direction, number, interval, pipeRef, transferType;
+ uint16_t maxPacketSize;
+ UInt64 frame;
+ AbsoluteTime atTime;
+ int i;
+
+ struct darwin_interface *cInterface;
+
+ /* construct an array of IOUSBIsocFrames, reuse the old one if possible */
+ if (tpriv->isoc_framelist && tpriv->num_iso_packets != transfer->num_iso_packets) {
+ free(tpriv->isoc_framelist);
+ tpriv->isoc_framelist = NULL;
+ }
+
+ if (!tpriv->isoc_framelist) {
+ tpriv->num_iso_packets = transfer->num_iso_packets;
+ tpriv->isoc_framelist = (IOUSBIsocFrame*) calloc (transfer->num_iso_packets, sizeof(IOUSBIsocFrame));
+ if (!tpriv->isoc_framelist)
+ return LIBUSB_ERROR_NO_MEM;
+ }
+
+ /* copy the frame list from the libusb descriptor (the structures differ only is member order) */
+ for (i = 0 ; i < transfer->num_iso_packets ; i++)
+ tpriv->isoc_framelist[i].frReqCount = transfer->iso_packet_desc[i].length;
+
+ /* determine the interface/endpoint to use */
+ if (ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, NULL, &cInterface) != 0) {
+ usbi_err (TRANSFER_CTX (transfer), "endpoint not found on any open interface");
+
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
+
+ /* determine the properties of this endpoint and the speed of the device */
+ (*(cInterface->interface))->GetPipeProperties (cInterface->interface, pipeRef, &direction, &number,
+ &transferType, &maxPacketSize, &interval);
+
+ /* Last but not least we need the bus frame number */
+ kresult = (*(cInterface->interface))->GetBusFrameNumber(cInterface->interface, &frame, &atTime);
+ if (kresult) {
+ usbi_err (TRANSFER_CTX (transfer), "failed to get bus frame number: %d", kresult);
+ free(tpriv->isoc_framelist);
+ tpriv->isoc_framelist = NULL;
+
+ return darwin_to_libusb (kresult);
+ }
+
+ (*(cInterface->interface))->GetPipeProperties (cInterface->interface, pipeRef, &direction, &number,
+ &transferType, &maxPacketSize, &interval);
+
+ /* schedule for a frame a little in the future */
+ frame += 4;
+
+ if (cInterface->frames[transfer->endpoint] && frame < cInterface->frames[transfer->endpoint])
+ frame = cInterface->frames[transfer->endpoint];
+
+ /* submit the request */
+ if (IS_XFERIN(transfer))
+ kresult = (*(cInterface->interface))->ReadIsochPipeAsync(cInterface->interface, pipeRef, transfer->buffer, frame,
+ transfer->num_iso_packets, tpriv->isoc_framelist, darwin_async_io_callback,
+ itransfer);
+ else
+ kresult = (*(cInterface->interface))->WriteIsochPipeAsync(cInterface->interface, pipeRef, transfer->buffer, frame,
+ transfer->num_iso_packets, tpriv->isoc_framelist, darwin_async_io_callback,
+ itransfer);
+
+ if (LIBUSB_SPEED_FULL == transfer->dev_handle->dev->speed)
+ /* Full speed */
+ cInterface->frames[transfer->endpoint] = frame + transfer->num_iso_packets * (1 << (interval - 1));
+ else
+ /* High/super speed */
+ cInterface->frames[transfer->endpoint] = frame + transfer->num_iso_packets * (1 << (interval - 1)) / 8;
+
+ if (kresult != kIOReturnSuccess) {
+ usbi_err (TRANSFER_CTX (transfer), "isochronous transfer failed (dir: %s): %s", IS_XFERIN(transfer) ? "In" : "Out",
+ darwin_error_str(kresult));
+ free (tpriv->isoc_framelist);
+ tpriv->isoc_framelist = NULL;
+ }
+
+ return darwin_to_libusb (kresult);
+}
+
+static int submit_control_transfer(struct usbi_transfer *itransfer) {
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct libusb_control_setup *setup = (struct libusb_control_setup *) transfer->buffer;
+ struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(transfer->dev_handle->dev);
+ struct darwin_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer);
+
+ IOReturn kresult;
+
+ memset(&tpriv->req, 0, sizeof(tpriv->req));
+
+ /* IOUSBDeviceInterface expects the request in cpu endianness */
+ tpriv->req.bmRequestType = setup->bmRequestType;
+ tpriv->req.bRequest = setup->bRequest;
+ /* these values should be in bus order from libusb_fill_control_setup */
+ tpriv->req.wValue = OSSwapLittleToHostInt16 (setup->wValue);
+ tpriv->req.wIndex = OSSwapLittleToHostInt16 (setup->wIndex);
+ tpriv->req.wLength = OSSwapLittleToHostInt16 (setup->wLength);
+ /* data is stored after the libusb control block */
+ tpriv->req.pData = transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE;
+ tpriv->req.completionTimeout = transfer->timeout;
+ tpriv->req.noDataTimeout = transfer->timeout;
+
+ itransfer->timeout_flags |= USBI_TRANSFER_OS_HANDLES_TIMEOUT;
+
+ /* all transfers in libusb-1.0 are async */
+
+ if (transfer->endpoint) {
+ struct darwin_interface *cInterface;
+ uint8_t pipeRef;
+
+ if (ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, NULL, &cInterface) != 0) {
+ usbi_err (TRANSFER_CTX (transfer), "endpoint not found on any open interface");
+
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
+
+ kresult = (*(cInterface->interface))->ControlRequestAsyncTO (cInterface->interface, pipeRef, &(tpriv->req), darwin_async_io_callback, itransfer);
+ } else
+ /* control request on endpoint 0 */
+ kresult = (*(dpriv->device))->DeviceRequestAsyncTO(dpriv->device, &(tpriv->req), darwin_async_io_callback, itransfer);
+
+ if (kresult != kIOReturnSuccess)
+ usbi_err (TRANSFER_CTX (transfer), "control request failed: %s", darwin_error_str(kresult));
+
+ return darwin_to_libusb (kresult);
+}
+
+static int darwin_submit_transfer(struct usbi_transfer *itransfer) {
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+
+ switch (transfer->type) {
+ case LIBUSB_TRANSFER_TYPE_CONTROL:
+ return submit_control_transfer(itransfer);
+ case LIBUSB_TRANSFER_TYPE_BULK:
+ case LIBUSB_TRANSFER_TYPE_INTERRUPT:
+ return submit_bulk_transfer(itransfer);
+ case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
+ return submit_iso_transfer(itransfer);
+ case LIBUSB_TRANSFER_TYPE_BULK_STREAM:
+#if InterfaceVersion >= 550
+ return submit_stream_transfer(itransfer);
+#else
+ usbi_err (TRANSFER_CTX(transfer), "IOUSBFamily version does not support bulk stream transfers");
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+#endif
+ default:
+ usbi_err (TRANSFER_CTX(transfer), "unknown endpoint type %d", transfer->type);
+ return LIBUSB_ERROR_INVALID_PARAM;
+ }
+}
+
+static int cancel_control_transfer(struct usbi_transfer *itransfer) {
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(transfer->dev_handle->dev);
+ IOReturn kresult;
+
+ usbi_warn (ITRANSFER_CTX (itransfer), "aborting all transactions control pipe");
+
+ if (!dpriv->device)
+ return LIBUSB_ERROR_NO_DEVICE;
+
+ kresult = (*(dpriv->device))->USBDeviceAbortPipeZero (dpriv->device);
+
+ return darwin_to_libusb (kresult);
+}
+
+static int darwin_abort_transfers (struct usbi_transfer *itransfer) {
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(transfer->dev_handle->dev);
+ struct darwin_interface *cInterface;
+ uint8_t pipeRef, iface;
+ IOReturn kresult;
+
+ if (ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, &iface, &cInterface) != 0) {
+ usbi_err (TRANSFER_CTX (transfer), "endpoint not found on any open interface");
+
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
+
+ if (!dpriv->device)
+ return LIBUSB_ERROR_NO_DEVICE;
+
+ usbi_warn (ITRANSFER_CTX (itransfer), "aborting all transactions on interface %d pipe %d", iface, pipeRef);
+
+ /* abort transactions */
+#if InterfaceVersion >= 550
+ if (LIBUSB_TRANSFER_TYPE_BULK_STREAM == transfer->type)
+ (*(cInterface->interface))->AbortStreamsPipe (cInterface->interface, pipeRef, itransfer->stream_id);
+ else
+#endif
+ (*(cInterface->interface))->AbortPipe (cInterface->interface, pipeRef);
+
+ usbi_dbg ("calling clear pipe stall to clear the data toggle bit");
+
+ /* newer versions of darwin support clearing additional bits on the device's endpoint */
+ kresult = (*(cInterface->interface))->ClearPipeStallBothEnds(cInterface->interface, pipeRef);
+
+ return darwin_to_libusb (kresult);
+}
+
+static int darwin_cancel_transfer(struct usbi_transfer *itransfer) {
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+
+ switch (transfer->type) {
+ case LIBUSB_TRANSFER_TYPE_CONTROL:
+ return cancel_control_transfer(itransfer);
+ case LIBUSB_TRANSFER_TYPE_BULK:
+ case LIBUSB_TRANSFER_TYPE_INTERRUPT:
+ case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
+ return darwin_abort_transfers (itransfer);
+ default:
+ usbi_err (TRANSFER_CTX(transfer), "unknown endpoint type %d", transfer->type);
+ return LIBUSB_ERROR_INVALID_PARAM;
+ }
+}
+
+static void darwin_clear_transfer_priv (struct usbi_transfer *itransfer) {
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct darwin_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer);
+
+ if (transfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS && tpriv->isoc_framelist) {
+ free (tpriv->isoc_framelist);
+ tpriv->isoc_framelist = NULL;
+ }
+}
+
+static void darwin_async_io_callback (void *refcon, IOReturn result, void *arg0) {
+ struct usbi_transfer *itransfer = (struct usbi_transfer *)refcon;
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct darwin_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer);
+
+ usbi_dbg ("an async io operation has completed");
+
+ /* if requested write a zero packet */
+ if (kIOReturnSuccess == result && IS_XFEROUT(transfer) && transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET) {
+ struct darwin_interface *cInterface;
+ uint8_t pipeRef;
+
+ (void) ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, NULL, &cInterface);
+
+ (*(cInterface->interface))->WritePipe (cInterface->interface, pipeRef, transfer->buffer, 0);
+ }
+
+ tpriv->result = result;
+ tpriv->size = (UInt32) (uintptr_t) arg0;
+
+ /* signal the core that this transfer is complete */
+ usbi_signal_transfer_completion(itransfer);
+}
+
+static int darwin_transfer_status (struct usbi_transfer *itransfer, kern_return_t result) {
+ if (itransfer->timeout_flags & USBI_TRANSFER_TIMED_OUT)
+ result = kIOUSBTransactionTimeout;
+
+ switch (result) {
+ case kIOReturnUnderrun:
+ case kIOReturnSuccess:
+ return LIBUSB_TRANSFER_COMPLETED;
+ case kIOReturnAborted:
+ return LIBUSB_TRANSFER_CANCELLED;
+ case kIOUSBPipeStalled:
+ usbi_dbg ("transfer error: pipe is stalled");
+ return LIBUSB_TRANSFER_STALL;
+ case kIOReturnOverrun:
+ usbi_warn (ITRANSFER_CTX (itransfer), "transfer error: data overrun");
+ return LIBUSB_TRANSFER_OVERFLOW;
+ case kIOUSBTransactionTimeout:
+ usbi_warn (ITRANSFER_CTX (itransfer), "transfer error: timed out");
+ itransfer->timeout_flags |= USBI_TRANSFER_TIMED_OUT;
+ return LIBUSB_TRANSFER_TIMED_OUT;
+ default:
+ usbi_warn (ITRANSFER_CTX (itransfer), "transfer error: %s (value = 0x%08x)", darwin_error_str (result), result);
+ return LIBUSB_TRANSFER_ERROR;
+ }
+}
+
+static int darwin_handle_transfer_completion (struct usbi_transfer *itransfer) {
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct darwin_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer);
+ int isIsoc = LIBUSB_TRANSFER_TYPE_ISOCHRONOUS == transfer->type;
+ int isBulk = LIBUSB_TRANSFER_TYPE_BULK == transfer->type;
+ int isControl = LIBUSB_TRANSFER_TYPE_CONTROL == transfer->type;
+ int isInterrupt = LIBUSB_TRANSFER_TYPE_INTERRUPT == transfer->type;
+ int i;
+
+ if (!isIsoc && !isBulk && !isControl && !isInterrupt) {
+ usbi_err (TRANSFER_CTX(transfer), "unknown endpoint type %d", transfer->type);
+ return LIBUSB_ERROR_INVALID_PARAM;
+ }
+
+ usbi_dbg ("handling %s completion with kernel status %d",
+ isControl ? "control" : isBulk ? "bulk" : isIsoc ? "isoc" : "interrupt", tpriv->result);
+
+ if (kIOReturnSuccess == tpriv->result || kIOReturnUnderrun == tpriv->result) {
+ if (isIsoc && tpriv->isoc_framelist) {
+ /* copy isochronous results back */
+
+ for (i = 0; i < transfer->num_iso_packets ; i++) {
+ struct libusb_iso_packet_descriptor *lib_desc = &transfer->iso_packet_desc[i];
+ lib_desc->status = darwin_to_libusb (tpriv->isoc_framelist[i].frStatus);
+ lib_desc->actual_length = tpriv->isoc_framelist[i].frActCount;
+ }
+ } else if (!isIsoc)
+ itransfer->transferred += tpriv->size;
+ }
+
+ /* it is ok to handle cancelled transfers without calling usbi_handle_transfer_cancellation (we catch timeout transfers) */
+ return usbi_handle_transfer_completion (itransfer, darwin_transfer_status (itransfer, tpriv->result));
+}
+
+static int darwin_clock_gettime(int clk_id, struct timespec *tp) {
+ mach_timespec_t sys_time;
+ clock_serv_t clock_ref;
+
+ switch (clk_id) {
+ case USBI_CLOCK_REALTIME:
+ /* CLOCK_REALTIME represents time since the epoch */
+ clock_ref = clock_realtime;
+ break;
+ case USBI_CLOCK_MONOTONIC:
+ /* use system boot time as reference for the monotonic clock */
+ clock_ref = clock_monotonic;
+ break;
+ default:
+ return LIBUSB_ERROR_INVALID_PARAM;
+ }
+
+ clock_get_time (clock_ref, &sys_time);
+
+ tp->tv_sec = sys_time.tv_sec;
+ tp->tv_nsec = sys_time.tv_nsec;
+
+ return 0;
+}
+
+#if InterfaceVersion >= 550
+static int darwin_alloc_streams (struct libusb_device_handle *dev_handle, uint32_t num_streams, unsigned char *endpoints,
+ int num_endpoints) {
+ struct darwin_interface *cInterface;
+ UInt32 supportsStreams;
+ uint8_t pipeRef;
+ int rc, i;
+
+ /* find the mimimum number of supported streams on the endpoint list */
+ for (i = 0 ; i < num_endpoints ; ++i) {
+ if (0 != (rc = ep_to_pipeRef (dev_handle, endpoints[i], &pipeRef, NULL, &cInterface))) {
+ return rc;
+ }
+
+ (*(cInterface->interface))->SupportsStreams (cInterface->interface, pipeRef, &supportsStreams);
+ if (num_streams > supportsStreams)
+ num_streams = supportsStreams;
+ }
+
+ /* it is an error if any endpoint in endpoints does not support streams */
+ if (0 == num_streams)
+ return LIBUSB_ERROR_INVALID_PARAM;
+
+ /* create the streams */
+ for (i = 0 ; i < num_endpoints ; ++i) {
+ (void) ep_to_pipeRef (dev_handle, endpoints[i], &pipeRef, NULL, &cInterface);
+
+ rc = (*(cInterface->interface))->CreateStreams (cInterface->interface, pipeRef, num_streams);
+ if (kIOReturnSuccess != rc)
+ return darwin_to_libusb(rc);
+ }
+
+ return num_streams;
+}
+
+static int darwin_free_streams (struct libusb_device_handle *dev_handle, unsigned char *endpoints, int num_endpoints) {
+ struct darwin_interface *cInterface;
+ UInt32 supportsStreams;
+ uint8_t pipeRef;
+ int rc;
+
+ for (int i = 0 ; i < num_endpoints ; ++i) {
+ if (0 != (rc = ep_to_pipeRef (dev_handle, endpoints[i], &pipeRef, NULL, &cInterface)))
+ return rc;
+
+ (*(cInterface->interface))->SupportsStreams (cInterface->interface, pipeRef, &supportsStreams);
+ if (0 == supportsStreams)
+ return LIBUSB_ERROR_INVALID_PARAM;
+
+ rc = (*(cInterface->interface))->CreateStreams (cInterface->interface, pipeRef, 0);
+ if (kIOReturnSuccess != rc)
+ return darwin_to_libusb(rc);
+ }
+
+ return LIBUSB_SUCCESS;
+}
+#endif
+
+const struct usbi_os_backend darwin_backend = {
+ .name = "Darwin",
+ .caps = 0,
+ .init = darwin_init,
+ .exit = darwin_exit,
+ .get_device_list = NULL, /* not needed */
+ .get_device_descriptor = darwin_get_device_descriptor,
+ .get_active_config_descriptor = darwin_get_active_config_descriptor,
+ .get_config_descriptor = darwin_get_config_descriptor,
+ .hotplug_poll = darwin_hotplug_poll,
+
+ .open = darwin_open,
+ .close = darwin_close,
+ .get_configuration = darwin_get_configuration,
+ .set_configuration = darwin_set_configuration,
+ .claim_interface = darwin_claim_interface,
+ .release_interface = darwin_release_interface,
+
+ .set_interface_altsetting = darwin_set_interface_altsetting,
+ .clear_halt = darwin_clear_halt,
+ .reset_device = darwin_reset_device,
+
+#if InterfaceVersion >= 550
+ .alloc_streams = darwin_alloc_streams,
+ .free_streams = darwin_free_streams,
+#endif
+
+ .kernel_driver_active = darwin_kernel_driver_active,
+ .detach_kernel_driver = darwin_detach_kernel_driver,
+ .attach_kernel_driver = darwin_attach_kernel_driver,
+
+ .destroy_device = darwin_destroy_device,
+
+ .submit_transfer = darwin_submit_transfer,
+ .cancel_transfer = darwin_cancel_transfer,
+ .clear_transfer_priv = darwin_clear_transfer_priv,
+
+ .handle_transfer_completion = darwin_handle_transfer_completion,
+
+ .clock_gettime = darwin_clock_gettime,
+
+ .device_priv_size = sizeof(struct darwin_device_priv),
+ .device_handle_priv_size = sizeof(struct darwin_device_handle_priv),
+ .transfer_priv_size = sizeof(struct darwin_transfer_priv),
+};
diff --git a/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/darwin_usb.h b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/darwin_usb.h
new file mode 100644
index 000000000..118043421
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/darwin_usb.h
@@ -0,0 +1,164 @@
+/*
+ * darwin backend for libusb 1.0
+ * Copyright © 2008-2015 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
+ * 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
+ */
+
+#if !defined(LIBUSB_DARWIN_H)
+#define LIBUSB_DARWIN_H
+
+#include "libusbi.h"
+
+#include <IOKit/IOTypes.h>
+#include <IOKit/IOCFBundle.h>
+#include <IOKit/usb/IOUSBLib.h>
+#include <IOKit/IOCFPlugIn.h>
+
+/* IOUSBInterfaceInferface */
+#if defined (kIOUSBInterfaceInterfaceID700) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_9
+
+#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
+
+#define usb_interface_t IOUSBInterfaceInterface550
+#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID550
+#define InterfaceVersion 550
+
+#elif defined (kIOUSBInterfaceInterfaceID500)
+
+#define usb_interface_t IOUSBInterfaceInterface500
+#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID500
+#define InterfaceVersion 500
+
+#elif defined (kIOUSBInterfaceInterfaceID300)
+
+#define usb_interface_t IOUSBInterfaceInterface300
+#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID300
+#define InterfaceVersion 300
+
+#elif defined (kIOUSBInterfaceInterfaceID245)
+
+#define usb_interface_t IOUSBInterfaceInterface245
+#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID245
+#define InterfaceVersion 245
+
+#elif defined (kIOUSBInterfaceInterfaceID220)
+
+#define usb_interface_t IOUSBInterfaceInterface220
+#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID220
+#define InterfaceVersion 220
+
+#else
+
+#error "IOUSBFamily is too old. Please upgrade your OS"
+
+#endif
+
+/* IOUSBDeviceInterface */
+#if defined (kIOUSBDeviceInterfaceID500) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_9
+
+#define usb_device_t IOUSBDeviceInterface500
+#define DeviceInterfaceID kIOUSBDeviceInterfaceID500
+#define DeviceVersion 500
+
+#elif defined (kIOUSBDeviceInterfaceID320)
+
+#define usb_device_t IOUSBDeviceInterface320
+#define DeviceInterfaceID kIOUSBDeviceInterfaceID320
+#define DeviceVersion 320
+
+#elif defined (kIOUSBDeviceInterfaceID300)
+
+#define usb_device_t IOUSBDeviceInterface300
+#define DeviceInterfaceID kIOUSBDeviceInterfaceID300
+#define DeviceVersion 300
+
+#elif defined (kIOUSBDeviceInterfaceID245)
+
+#define usb_device_t IOUSBDeviceInterface245
+#define DeviceInterfaceID kIOUSBDeviceInterfaceID245
+#define DeviceVersion 245
+
+#elif defined (kIOUSBDeviceInterfaceID220)
+#define usb_device_t IOUSBDeviceInterface197
+#define DeviceInterfaceID kIOUSBDeviceInterfaceID197
+#define DeviceVersion 197
+
+#else
+
+#error "IOUSBFamily is too old. Please upgrade your OS"
+
+#endif
+
+#if !defined(IO_OBJECT_NULL)
+#define IO_OBJECT_NULL ((io_object_t) 0)
+#endif
+
+typedef IOCFPlugInInterface *io_cf_plugin_ref_t;
+typedef IONotificationPortRef io_notification_port_t;
+
+/* private structures */
+struct darwin_cached_device {
+ struct list_head list;
+ IOUSBDeviceDescriptor dev_descriptor;
+ UInt32 location;
+ UInt64 parent_session;
+ UInt64 session;
+ UInt16 address;
+ char sys_path[21];
+ usb_device_t **device;
+ int open_count;
+ UInt8 first_config, active_config, port;
+ int can_enumerate;
+ int refcount;
+};
+
+struct darwin_device_priv {
+ struct darwin_cached_device *dev;
+};
+
+struct darwin_device_handle_priv {
+ int is_open;
+ CFRunLoopSourceRef cfSource;
+
+ struct darwin_interface {
+ usb_interface_t **interface;
+ uint8_t num_endpoints;
+ CFRunLoopSourceRef cfSource;
+ uint64_t frames[256];
+ uint8_t endpoint_addrs[USB_MAXENDPOINTS];
+ } interfaces[USB_MAXINTERFACES];
+};
+
+struct darwin_transfer_priv {
+ /* Isoc */
+ IOUSBIsocFrame *isoc_framelist;
+ int num_iso_packets;
+
+ /* Control */
+ IOUSBDevRequestTO req;
+
+ /* Bulk */
+
+ /* Completion status */
+ IOReturn result;
+ UInt32 size;
+};
+
+#endif
diff --git a/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/haiku_pollfs.cpp b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/haiku_pollfs.cpp
new file mode 100644
index 000000000..e0c771320
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/haiku_pollfs.cpp
@@ -0,0 +1,367 @@
+/*
+ * Copyright 2007-2008, Haiku Inc. All rights reserved.
+ * Distributed under the terms of the MIT License.
+ *
+ * Authors:
+ * Michael Lotz <mmlr@mlotz.ch>
+ */
+
+#include "haiku_usb.h"
+#include <cstdio>
+#include <Directory.h>
+#include <Entry.h>
+#include <Looper.h>
+#include <Messenger.h>
+#include <Node.h>
+#include <NodeMonitor.h>
+#include <Path.h>
+#include <cstring>
+
+class WatchedEntry {
+public:
+ WatchedEntry(BMessenger *, entry_ref *);
+ ~WatchedEntry();
+ bool EntryCreated(entry_ref *ref);
+ bool EntryRemoved(ino_t node);
+ bool InitCheck();
+
+private:
+ BMessenger* fMessenger;
+ node_ref fNode;
+ bool fIsDirectory;
+ USBDevice* fDevice;
+ WatchedEntry* fEntries;
+ WatchedEntry* fLink;
+ bool fInitCheck;
+};
+
+
+class RosterLooper : public BLooper {
+public:
+ RosterLooper(USBRoster *);
+ void Stop();
+ virtual void MessageReceived(BMessage *);
+ bool InitCheck();
+
+private:
+ USBRoster* fRoster;
+ WatchedEntry* fRoot;
+ BMessenger* fMessenger;
+ bool fInitCheck;
+};
+
+
+WatchedEntry::WatchedEntry(BMessenger *messenger, entry_ref *ref)
+ : fMessenger(messenger),
+ fIsDirectory(false),
+ fDevice(NULL),
+ fEntries(NULL),
+ fLink(NULL),
+ fInitCheck(false)
+{
+ BEntry entry(ref);
+ entry.GetNodeRef(&fNode);
+
+ BDirectory directory;
+ if (entry.IsDirectory() && directory.SetTo(ref) >= B_OK) {
+ fIsDirectory = true;
+
+ while (directory.GetNextEntry(&entry) >= B_OK) {
+ if (entry.GetRef(ref) < B_OK)
+ continue;
+
+ WatchedEntry *child = new(std::nothrow) WatchedEntry(fMessenger, ref);
+ if (child == NULL)
+ continue;
+ if (child->InitCheck() == false) {
+ delete child;
+ continue;
+ }
+
+ child->fLink = fEntries;
+ fEntries = child;
+ }
+
+ watch_node(&fNode, B_WATCH_DIRECTORY, *fMessenger);
+ }
+ else {
+ if (strncmp(ref->name, "raw", 3) == 0)
+ return;
+
+ BPath path, parent_path;
+ entry.GetPath(&path);
+ fDevice = new(std::nothrow) USBDevice(path.Path());
+ if (fDevice != NULL && fDevice->InitCheck() == true) {
+ // Add this new device to each active context's device list
+ struct libusb_context *ctx;
+ unsigned long session_id = (unsigned long)&fDevice;
+
+ usbi_mutex_lock(&active_contexts_lock);
+ list_for_each_entry(ctx, &active_contexts_list, list, struct libusb_context) {
+ struct libusb_device *dev = usbi_get_device_by_session_id(ctx, session_id);
+ if (dev) {
+ usbi_dbg("using previously allocated device with location %lu", session_id);
+ libusb_unref_device(dev);
+ continue;
+ }
+ usbi_dbg("allocating new device with location %lu", session_id);
+ dev = usbi_alloc_device(ctx, session_id);
+ if (!dev) {
+ usbi_dbg("device allocation failed");
+ continue;
+ }
+ *((USBDevice **)dev->os_priv) = fDevice;
+
+ // Calculate pseudo-device-address
+ int addr, tmp;
+ if (strcmp(path.Leaf(), "hub") == 0)
+ tmp = 100; //Random Number
+ else
+ sscanf(path.Leaf(), "%d", &tmp);
+ addr = tmp + 1;
+ path.GetParent(&parent_path);
+ while (strcmp(parent_path.Leaf(), "usb") != 0) {
+ sscanf(parent_path.Leaf(), "%d", &tmp);
+ addr += tmp + 1;
+ parent_path.GetParent(&parent_path);
+ }
+ sscanf(path.Path(), "/dev/bus/usb/%d", &dev->bus_number);
+ dev->device_address = addr - (dev->bus_number + 1);
+
+ if (usbi_sanitize_device(dev) < 0) {
+ usbi_dbg("device sanitization failed");
+ libusb_unref_device(dev);
+ continue;
+ }
+ usbi_connect_device(dev);
+ }
+ usbi_mutex_unlock(&active_contexts_lock);
+ }
+ else if (fDevice) {
+ delete fDevice;
+ fDevice = NULL;
+ return;
+ }
+ }
+ fInitCheck = true;
+}
+
+
+WatchedEntry::~WatchedEntry()
+{
+ if (fIsDirectory) {
+ watch_node(&fNode, B_STOP_WATCHING, *fMessenger);
+
+ WatchedEntry *child = fEntries;
+ while (child) {
+ WatchedEntry *next = child->fLink;
+ delete child;
+ child = next;
+ }
+ }
+
+ if (fDevice) {
+ // Remove this device from each active context's device list
+ struct libusb_context *ctx;
+ struct libusb_device *dev;
+ unsigned long session_id = (unsigned long)&fDevice;
+
+ usbi_mutex_lock(&active_contexts_lock);
+ list_for_each_entry(ctx, &active_contexts_list, list, struct libusb_context) {
+ dev = usbi_get_device_by_session_id(ctx, session_id);
+ if (dev != NULL) {
+ usbi_disconnect_device(dev);
+ libusb_unref_device(dev);
+ } else {
+ usbi_dbg("device with location %lu not found", session_id);
+ }
+ }
+ usbi_mutex_static_unlock(&active_contexts_lock);
+ delete fDevice;
+ }
+}
+
+
+bool
+WatchedEntry::EntryCreated(entry_ref *ref)
+{
+ if (!fIsDirectory)
+ return false;
+
+ if (ref->directory != fNode.node) {
+ WatchedEntry *child = fEntries;
+ while (child) {
+ if (child->EntryCreated(ref))
+ return true;
+ child = child->fLink;
+ }
+ return false;
+ }
+
+ WatchedEntry *child = new(std::nothrow) WatchedEntry(fMessenger, ref);
+ if (child == NULL)
+ return false;
+ child->fLink = fEntries;
+ fEntries = child;
+ return true;
+}
+
+
+bool
+WatchedEntry::EntryRemoved(ino_t node)
+{
+ if (!fIsDirectory)
+ return false;
+
+ WatchedEntry *child = fEntries;
+ WatchedEntry *lastChild = NULL;
+ while (child) {
+ if (child->fNode.node == node) {
+ if (lastChild)
+ lastChild->fLink = child->fLink;
+ else
+ fEntries = child->fLink;
+ delete child;
+ return true;
+ }
+
+ if (child->EntryRemoved(node))
+ return true;
+
+ lastChild = child;
+ child = child->fLink;
+ }
+ return false;
+}
+
+
+bool
+WatchedEntry::InitCheck()
+{
+ return fInitCheck;
+}
+
+
+RosterLooper::RosterLooper(USBRoster *roster)
+ : BLooper("LibusbRoster Looper"),
+ fRoster(roster),
+ fRoot(NULL),
+ fMessenger(NULL),
+ fInitCheck(false)
+{
+ BEntry entry("/dev/bus/usb");
+ if (!entry.Exists()) {
+ usbi_err(NULL, "usb_raw not published");
+ return;
+ }
+
+ Run();
+ fMessenger = new(std::nothrow) BMessenger(this);
+ if (fMessenger == NULL) {
+ usbi_err(NULL, "error creating BMessenger object");
+ return;
+ }
+
+ if (Lock()) {
+ entry_ref ref;
+ entry.GetRef(&ref);
+ fRoot = new(std::nothrow) WatchedEntry(fMessenger, &ref);
+ Unlock();
+ if (fRoot == NULL)
+ return;
+ if (fRoot->InitCheck() == false) {
+ delete fRoot;
+ fRoot = NULL;
+ return;
+ }
+ }
+ fInitCheck = true;
+}
+
+
+void
+RosterLooper::Stop()
+{
+ Lock();
+ delete fRoot;
+ delete fMessenger;
+ Quit();
+}
+
+
+void
+RosterLooper::MessageReceived(BMessage *message)
+{
+ int32 opcode;
+ if (message->FindInt32("opcode", &opcode) < B_OK)
+ return;
+
+ switch (opcode) {
+ case B_ENTRY_CREATED:
+ {
+ dev_t device;
+ ino_t directory;
+ const char *name;
+ if (message->FindInt32("device", &device) < B_OK ||
+ message->FindInt64("directory", &directory) < B_OK ||
+ message->FindString("name", &name) < B_OK)
+ break;
+
+ entry_ref ref(device, directory, name);
+ fRoot->EntryCreated(&ref);
+ break;
+ }
+ case B_ENTRY_REMOVED:
+ {
+ ino_t node;
+ if (message->FindInt64("node", &node) < B_OK)
+ break;
+ fRoot->EntryRemoved(node);
+ break;
+ }
+ }
+}
+
+
+bool
+RosterLooper::InitCheck()
+{
+ return fInitCheck;
+}
+
+
+USBRoster::USBRoster()
+ : fLooper(NULL)
+{
+}
+
+
+USBRoster::~USBRoster()
+{
+ Stop();
+}
+
+
+int
+USBRoster::Start()
+{
+ if (fLooper == NULL) {
+ fLooper = new(std::nothrow) RosterLooper(this);
+ if (fLooper == NULL || ((RosterLooper *)fLooper)->InitCheck() == false) {
+ if (fLooper)
+ fLooper = NULL;
+ return LIBUSB_ERROR_OTHER;
+ }
+ }
+ return LIBUSB_SUCCESS;
+}
+
+
+void
+USBRoster::Stop()
+{
+ if (fLooper) {
+ ((RosterLooper *)fLooper)->Stop();
+ fLooper = NULL;
+ }
+}
diff --git a/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/haiku_usb.h b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/haiku_usb.h
new file mode 100644
index 000000000..d51ae9eae
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/haiku_usb.h
@@ -0,0 +1,112 @@
+/*
+ * Haiku Backend for libusb
+ * Copyright © 2014 Akshay Jaggi <akshay1994.leo@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 <List.h>
+#include <Locker.h>
+#include <Autolock.h>
+#include <USBKit.h>
+#include <map>
+#include "libusbi.h"
+#include "haiku_usb_raw.h"
+
+using namespace std;
+
+class USBDevice;
+class USBDeviceHandle;
+class USBTransfer;
+
+class USBDevice {
+public:
+ USBDevice(const char *);
+ virtual ~USBDevice();
+ const char* Location() const;
+ uint8 CountConfigurations() const;
+ const usb_device_descriptor* Descriptor() const;
+ const usb_configuration_descriptor* ConfigurationDescriptor(uint32) const;
+ const usb_configuration_descriptor* ActiveConfiguration() const;
+ uint8 EndpointToIndex(uint8) const;
+ uint8 EndpointToInterface(uint8) const;
+ int ClaimInterface(int);
+ int ReleaseInterface(int);
+ int CheckInterfacesFree(int);
+ int SetActiveConfiguration(int);
+ int ActiveConfigurationIndex() const;
+ bool InitCheck();
+private:
+ int Initialise();
+ unsigned int fClaimedInterfaces; // Max Interfaces can be 32. Using a bitmask
+ usb_device_descriptor fDeviceDescriptor;
+ unsigned char** fConfigurationDescriptors;
+ int fActiveConfiguration;
+ char* fPath;
+ map<uint8,uint8> fConfigToIndex;
+ map<uint8,uint8>* fEndpointToIndex;
+ map<uint8,uint8>* fEndpointToInterface;
+ bool fInitCheck;
+};
+
+class USBDeviceHandle {
+public:
+ USBDeviceHandle(USBDevice *dev);
+ virtual ~USBDeviceHandle();
+ int ClaimInterface(int);
+ int ReleaseInterface(int);
+ int SetConfiguration(int);
+ int SetAltSetting(int, int);
+ status_t SubmitTransfer(struct usbi_transfer *);
+ status_t CancelTransfer(USBTransfer *);
+ bool InitCheck();
+private:
+ int fRawFD;
+ static status_t TransfersThread(void *);
+ void TransfersWorker();
+ USBDevice* fUSBDevice;
+ unsigned int fClaimedInterfaces;
+ BList fTransfers;
+ BLocker fTransfersLock;
+ sem_id fTransfersSem;
+ thread_id fTransfersThread;
+ bool fInitCheck;
+};
+
+class USBTransfer {
+public:
+ USBTransfer(struct usbi_transfer *, USBDevice *);
+ virtual ~USBTransfer();
+ void Do(int);
+ struct usbi_transfer* UsbiTransfer();
+ void SetCancelled();
+ bool IsCancelled();
+private:
+ struct usbi_transfer* fUsbiTransfer;
+ struct libusb_transfer* fLibusbTransfer;
+ USBDevice* fUSBDevice;
+ BLocker fStatusLock;
+ bool fCancelled;
+};
+
+class USBRoster {
+public:
+ USBRoster();
+ virtual ~USBRoster();
+ int Start();
+ void Stop();
+private:
+ void* fLooper;
+};
diff --git a/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/haiku_usb_backend.cpp b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/haiku_usb_backend.cpp
new file mode 100644
index 000000000..d3de8cc08
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/haiku_usb_backend.cpp
@@ -0,0 +1,517 @@
+/*
+ * Haiku Backend for libusb
+ * Copyright © 2014 Akshay Jaggi <akshay1994.leo@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 <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <new>
+#include <vector>
+
+#include "haiku_usb.h"
+
+int _errno_to_libusb(int status)
+{
+ return status;
+}
+
+USBTransfer::USBTransfer(struct usbi_transfer *itransfer, USBDevice *device)
+{
+ fUsbiTransfer = itransfer;
+ fLibusbTransfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ fUSBDevice = device;
+ fCancelled = false;
+}
+
+USBTransfer::~USBTransfer()
+{
+}
+
+struct usbi_transfer *
+USBTransfer::UsbiTransfer()
+{
+ return fUsbiTransfer;
+}
+
+void
+USBTransfer::SetCancelled()
+{
+ fCancelled = true;
+}
+
+bool
+USBTransfer::IsCancelled()
+{
+ return fCancelled;
+}
+
+void
+USBTransfer::Do(int fRawFD)
+{
+ switch (fLibusbTransfer->type) {
+ case LIBUSB_TRANSFER_TYPE_CONTROL:
+ {
+ struct libusb_control_setup *setup = (struct libusb_control_setup *)fLibusbTransfer->buffer;
+ usb_raw_command command;
+ command.control.request_type = setup->bmRequestType;
+ command.control.request = setup->bRequest;
+ command.control.value = setup->wValue;
+ command.control.index = setup->wIndex;
+ command.control.length = setup->wLength;
+ command.control.data = fLibusbTransfer->buffer + LIBUSB_CONTROL_SETUP_SIZE;
+ if (fCancelled)
+ break;
+ if (ioctl(fRawFD, B_USB_RAW_COMMAND_CONTROL_TRANSFER, &command, sizeof(command)) ||
+ command.control.status != B_USB_RAW_STATUS_SUCCESS) {
+ fUsbiTransfer->transferred = -1;
+ usbi_err(TRANSFER_CTX(fLibusbTransfer), "failed control transfer");
+ break;
+ }
+ fUsbiTransfer->transferred = command.control.length;
+ }
+ break;
+ case LIBUSB_TRANSFER_TYPE_BULK:
+ case LIBUSB_TRANSFER_TYPE_INTERRUPT:
+ {
+ usb_raw_command command;
+ command.transfer.interface = fUSBDevice->EndpointToInterface(fLibusbTransfer->endpoint);
+ command.transfer.endpoint = fUSBDevice->EndpointToIndex(fLibusbTransfer->endpoint);
+ command.transfer.data = fLibusbTransfer->buffer;
+ command.transfer.length = fLibusbTransfer->length;
+ if (fCancelled)
+ break;
+ if (fLibusbTransfer->type == LIBUSB_TRANSFER_TYPE_BULK) {
+ if (ioctl(fRawFD, B_USB_RAW_COMMAND_BULK_TRANSFER, &command, sizeof(command)) ||
+ command.transfer.status != B_USB_RAW_STATUS_SUCCESS) {
+ fUsbiTransfer->transferred = -1;
+ usbi_err(TRANSFER_CTX(fLibusbTransfer), "failed bulk transfer");
+ break;
+ }
+ }
+ else {
+ if (ioctl(fRawFD, B_USB_RAW_COMMAND_INTERRUPT_TRANSFER, &command, sizeof(command)) ||
+ command.transfer.status != B_USB_RAW_STATUS_SUCCESS) {
+ fUsbiTransfer->transferred = -1;
+ usbi_err(TRANSFER_CTX(fLibusbTransfer), "failed interrupt transfer");
+ break;
+ }
+ }
+ fUsbiTransfer->transferred = command.transfer.length;
+ }
+ break;
+ // IsochronousTransfers not tested
+ case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
+ {
+ usb_raw_command command;
+ command.isochronous.interface = fUSBDevice->EndpointToInterface(fLibusbTransfer->endpoint);
+ command.isochronous.endpoint = fUSBDevice->EndpointToIndex(fLibusbTransfer->endpoint);
+ command.isochronous.data = fLibusbTransfer->buffer;
+ command.isochronous.length = fLibusbTransfer->length;
+ command.isochronous.packet_count = fLibusbTransfer->num_iso_packets;
+ int i;
+ usb_iso_packet_descriptor *packetDescriptors = new usb_iso_packet_descriptor[fLibusbTransfer->num_iso_packets];
+ for (i = 0; i < fLibusbTransfer->num_iso_packets; i++) {
+ if ((int16)(fLibusbTransfer->iso_packet_desc[i]).length != (fLibusbTransfer->iso_packet_desc[i]).length) {
+ fUsbiTransfer->transferred = -1;
+ usbi_err(TRANSFER_CTX(fLibusbTransfer), "failed isochronous transfer");
+ break;
+ }
+ packetDescriptors[i].request_length = (int16)(fLibusbTransfer->iso_packet_desc[i]).length;
+ }
+ if (i < fLibusbTransfer->num_iso_packets)
+ break; // TODO Handle this error
+ command.isochronous.packet_descriptors = packetDescriptors;
+ if (fCancelled)
+ break;
+ if (ioctl(fRawFD, B_USB_RAW_COMMAND_ISOCHRONOUS_TRANSFER, &command, sizeof(command)) ||
+ command.isochronous.status != B_USB_RAW_STATUS_SUCCESS) {
+ fUsbiTransfer->transferred = -1;
+ usbi_err(TRANSFER_CTX(fLibusbTransfer), "failed isochronous transfer");
+ break;
+ }
+ for (i = 0; i < fLibusbTransfer->num_iso_packets; i++) {
+ (fLibusbTransfer->iso_packet_desc[i]).actual_length = packetDescriptors[i].actual_length;
+ switch (packetDescriptors[i].status) {
+ case B_OK:
+ (fLibusbTransfer->iso_packet_desc[i]).status = LIBUSB_TRANSFER_COMPLETED;
+ break;
+ default:
+ (fLibusbTransfer->iso_packet_desc[i]).status = LIBUSB_TRANSFER_ERROR;
+ break;
+ }
+ }
+ delete[] packetDescriptors;
+ // Do we put the length of transfer here, for isochronous transfers?
+ fUsbiTransfer->transferred = command.transfer.length;
+ }
+ break;
+ default:
+ usbi_err(TRANSFER_CTX(fLibusbTransfer), "Unknown type of transfer");
+ }
+}
+
+bool
+USBDeviceHandle::InitCheck()
+{
+ return fInitCheck;
+}
+
+status_t
+USBDeviceHandle::TransfersThread(void *self)
+{
+ USBDeviceHandle *handle = (USBDeviceHandle *)self;
+ handle->TransfersWorker();
+ return B_OK;
+}
+
+void
+USBDeviceHandle::TransfersWorker()
+{
+ while (true) {
+ status_t status = acquire_sem(fTransfersSem);
+ if (status == B_BAD_SEM_ID)
+ break;
+ if (status == B_INTERRUPTED)
+ continue;
+ fTransfersLock.Lock();
+ USBTransfer *fPendingTransfer = (USBTransfer *) fTransfers.RemoveItem((int32)0);
+ fTransfersLock.Unlock();
+ fPendingTransfer->Do(fRawFD);
+ usbi_signal_transfer_completion(fPendingTransfer->UsbiTransfer());
+ }
+}
+
+status_t
+USBDeviceHandle::SubmitTransfer(struct usbi_transfer *itransfer)
+{
+ USBTransfer *transfer = new USBTransfer(itransfer, fUSBDevice);
+ *((USBTransfer **)usbi_transfer_get_os_priv(itransfer)) = transfer;
+ BAutolock locker(fTransfersLock);
+ fTransfers.AddItem(transfer);
+ release_sem(fTransfersSem);
+ return LIBUSB_SUCCESS;
+}
+
+status_t
+USBDeviceHandle::CancelTransfer(USBTransfer *transfer)
+{
+ transfer->SetCancelled();
+ fTransfersLock.Lock();
+ bool removed = fTransfers.RemoveItem(transfer);
+ fTransfersLock.Unlock();
+ if(removed)
+ usbi_signal_transfer_completion(transfer->UsbiTransfer());
+ return LIBUSB_SUCCESS;
+}
+
+USBDeviceHandle::USBDeviceHandle(USBDevice *dev)
+ :
+ fTransfersThread(-1),
+ fUSBDevice(dev),
+ fClaimedInterfaces(0),
+ fInitCheck(false)
+{
+ fRawFD = open(dev->Location(), O_RDWR | O_CLOEXEC);
+ if (fRawFD < 0) {
+ usbi_err(NULL,"failed to open device");
+ return;
+ }
+ fTransfersSem = create_sem(0, "Transfers Queue Sem");
+ fTransfersThread = spawn_thread(TransfersThread, "Transfer Worker", B_NORMAL_PRIORITY, this);
+ resume_thread(fTransfersThread);
+ fInitCheck = true;
+}
+
+USBDeviceHandle::~USBDeviceHandle()
+{
+ if (fRawFD > 0)
+ close(fRawFD);
+ for(int i = 0; i < 32; i++) {
+ if (fClaimedInterfaces & (1 << i))
+ ReleaseInterface(i);
+ }
+ delete_sem(fTransfersSem);
+ if (fTransfersThread > 0)
+ wait_for_thread(fTransfersThread, NULL);
+}
+
+int
+USBDeviceHandle::ClaimInterface(int inumber)
+{
+ int status = fUSBDevice->ClaimInterface(inumber);
+ if (status == LIBUSB_SUCCESS)
+ fClaimedInterfaces |= (1 << inumber);
+ return status;
+}
+
+int
+USBDeviceHandle::ReleaseInterface(int inumber)
+{
+ fUSBDevice->ReleaseInterface(inumber);
+ fClaimedInterfaces &= ~(1 << inumber);
+ return LIBUSB_SUCCESS;
+}
+
+int
+USBDeviceHandle::SetConfiguration(int config)
+{
+ int config_index = fUSBDevice->CheckInterfacesFree(config);
+ if(config_index == LIBUSB_ERROR_BUSY || config_index == LIBUSB_ERROR_NOT_FOUND)
+ return config_index;
+ usb_raw_command command;
+ command.config.config_index = config_index;
+ if (ioctl(fRawFD, B_USB_RAW_COMMAND_SET_CONFIGURATION, &command, sizeof(command)) ||
+ command.config.status != B_USB_RAW_STATUS_SUCCESS) {
+ return _errno_to_libusb(command.config.status);
+ }
+ fUSBDevice->SetActiveConfiguration(config_index);
+ return LIBUSB_SUCCESS;
+}
+
+int
+USBDeviceHandle::SetAltSetting(int inumber, int alt)
+{
+ usb_raw_command command;
+ command.alternate.config_index = fUSBDevice->ActiveConfigurationIndex();
+ command.alternate.interface_index = inumber;
+ if (ioctl(fRawFD, B_USB_RAW_COMMAND_GET_ACTIVE_ALT_INTERFACE_INDEX, &command, sizeof(command)) ||
+ command.alternate.status != B_USB_RAW_STATUS_SUCCESS) {
+ usbi_err(NULL, "Error retrieving active alternate interface");
+ return _errno_to_libusb(command.alternate.status);
+ }
+ if (command.alternate.alternate_info == alt) {
+ usbi_dbg("Setting alternate interface successful");
+ return LIBUSB_SUCCESS;
+ }
+ command.alternate.alternate_info = alt;
+ if (ioctl(fRawFD, B_USB_RAW_COMMAND_SET_ALT_INTERFACE, &command, sizeof(command)) ||
+ command.alternate.status != B_USB_RAW_STATUS_SUCCESS) { //IF IOCTL FAILS DEVICE DISONNECTED PROBABLY
+ usbi_err(NULL, "Error setting alternate interface");
+ return _errno_to_libusb(command.alternate.status);
+ }
+ usbi_dbg("Setting alternate interface successful");
+ return LIBUSB_SUCCESS;
+}
+
+
+USBDevice::USBDevice(const char *path)
+ :
+ fPath(NULL),
+ fActiveConfiguration(0), //0?
+ fConfigurationDescriptors(NULL),
+ fClaimedInterfaces(0),
+ fEndpointToIndex(NULL),
+ fEndpointToInterface(NULL),
+ fInitCheck(false)
+{
+ fPath=strdup(path);
+ Initialise();
+}
+
+USBDevice::~USBDevice()
+{
+ free(fPath);
+ if (fConfigurationDescriptors) {
+ for(int i = 0; i < fDeviceDescriptor.num_configurations; i++) {
+ if (fConfigurationDescriptors[i])
+ delete fConfigurationDescriptors[i];
+ }
+ delete[] fConfigurationDescriptors;
+ }
+ if (fEndpointToIndex)
+ delete[] fEndpointToIndex;
+ if (fEndpointToInterface)
+ delete[] fEndpointToInterface;
+}
+
+bool
+USBDevice::InitCheck()
+{
+ return fInitCheck;
+}
+
+const char *
+USBDevice::Location() const
+{
+ return fPath;
+}
+
+uint8
+USBDevice::CountConfigurations() const
+{
+ return fDeviceDescriptor.num_configurations;
+}
+
+const usb_device_descriptor *
+USBDevice::Descriptor() const
+{
+ return &fDeviceDescriptor;
+}
+
+const usb_configuration_descriptor *
+USBDevice::ConfigurationDescriptor(uint32 index) const
+{
+ if (index > CountConfigurations())
+ return NULL;
+ return (usb_configuration_descriptor *) fConfigurationDescriptors[index];
+}
+
+const usb_configuration_descriptor *
+USBDevice::ActiveConfiguration() const
+{
+ return (usb_configuration_descriptor *) fConfigurationDescriptors[fActiveConfiguration];
+}
+
+int
+USBDevice::ActiveConfigurationIndex() const
+{
+ return fActiveConfiguration;
+}
+
+int USBDevice::ClaimInterface(int interface)
+{
+ if (interface > ActiveConfiguration()->number_interfaces)
+ return LIBUSB_ERROR_NOT_FOUND;
+ if (fClaimedInterfaces & (1 << interface))
+ return LIBUSB_ERROR_BUSY;
+ fClaimedInterfaces |= (1 << interface);
+ return LIBUSB_SUCCESS;
+}
+
+int USBDevice::ReleaseInterface(int interface)
+{
+ fClaimedInterfaces &= ~(1 << interface);
+ return LIBUSB_SUCCESS;
+}
+
+int
+USBDevice::CheckInterfacesFree(int config)
+{
+ if (fConfigToIndex.count(config) == 0)
+ return LIBUSB_ERROR_NOT_FOUND;
+ if (fClaimedInterfaces == 0)
+ return fConfigToIndex[(uint8)config];
+ return LIBUSB_ERROR_BUSY;
+}
+
+int
+USBDevice::SetActiveConfiguration(int config_index)
+{
+ fActiveConfiguration = config_index;
+ return LIBUSB_SUCCESS;
+}
+
+uint8
+USBDevice::EndpointToIndex(uint8 address) const
+{
+ return fEndpointToIndex[fActiveConfiguration][address];
+}
+
+uint8
+USBDevice::EndpointToInterface(uint8 address) const
+{
+ return fEndpointToInterface[fActiveConfiguration][address];
+}
+
+int
+USBDevice::Initialise() //Do we need more error checking, etc? How to report?
+{
+ int fRawFD = open(fPath, O_RDWR | O_CLOEXEC);
+ if (fRawFD < 0)
+ return B_ERROR;
+ usb_raw_command command;
+ command.device.descriptor = &fDeviceDescriptor;
+ if (ioctl(fRawFD, B_USB_RAW_COMMAND_GET_DEVICE_DESCRIPTOR, &command, sizeof(command)) ||
+ command.device.status != B_USB_RAW_STATUS_SUCCESS) {
+ close(fRawFD);
+ return B_ERROR;
+ }
+
+ fConfigurationDescriptors = new(std::nothrow) unsigned char *[fDeviceDescriptor.num_configurations];
+ fEndpointToIndex = new(std::nothrow) map<uint8,uint8> [fDeviceDescriptor.num_configurations];
+ fEndpointToInterface = new(std::nothrow) map<uint8,uint8> [fDeviceDescriptor.num_configurations];
+ for (int i = 0; i < fDeviceDescriptor.num_configurations; i++) {
+ usb_configuration_descriptor tmp_config;
+ command.config.descriptor = &tmp_config;
+ command.config.config_index = i;
+ if (ioctl(fRawFD, B_USB_RAW_COMMAND_GET_CONFIGURATION_DESCRIPTOR, &command, sizeof(command)) ||
+ command.config.status != B_USB_RAW_STATUS_SUCCESS) {
+ usbi_err(NULL, "failed retrieving configuration descriptor");
+ close(fRawFD);
+ return B_ERROR;
+ }
+ fConfigToIndex[tmp_config.configuration_value] = i;
+ fConfigurationDescriptors[i] = new(std::nothrow) unsigned char[tmp_config.total_length];
+ command.control.request_type = 128;
+ command.control.request = 6;
+ command.control.value = (2 << 8) | i;
+ command.control.index = 0;
+ command.control.length = tmp_config.total_length;
+ command.control.data = fConfigurationDescriptors[i];
+ if (ioctl(fRawFD, B_USB_RAW_COMMAND_CONTROL_TRANSFER, &command, sizeof(command)) ||
+ command.control.status!=B_USB_RAW_STATUS_SUCCESS) {
+ usbi_err(NULL, "failed retrieving full configuration descriptor");
+ close(fRawFD);
+ return B_ERROR;
+ }
+ for (int j = 0; j < tmp_config.number_interfaces; j++) {
+ command.alternate.config_index = i;
+ command.alternate.interface_index = j;
+ if (ioctl(fRawFD, B_USB_RAW_COMMAND_GET_ALT_INTERFACE_COUNT, &command, sizeof(command)) ||
+ command.config.status != B_USB_RAW_STATUS_SUCCESS) {
+ usbi_err(NULL, "failed retrieving number of alternate interfaces");
+ close(fRawFD);
+ return B_ERROR;
+ }
+ int num_alternate = command.alternate.alternate_info;
+ for (int k = 0; k < num_alternate; k++) {
+ usb_interface_descriptor tmp_interface;
+ command.interface_etc.config_index = i;
+ command.interface_etc.interface_index = j;
+ command.interface_etc.alternate_index = k;
+ command.interface_etc.descriptor = &tmp_interface;
+ if (ioctl(fRawFD, B_USB_RAW_COMMAND_GET_INTERFACE_DESCRIPTOR_ETC, &command, sizeof(command)) ||
+ command.config.status != B_USB_RAW_STATUS_SUCCESS) {
+ usbi_err(NULL, "failed retrieving interface descriptor");
+ close(fRawFD);
+ return B_ERROR;
+ }
+ for (int l = 0; l < tmp_interface.num_endpoints; l++) {
+ usb_endpoint_descriptor tmp_endpoint;
+ command.endpoint_etc.config_index = i;
+ command.endpoint_etc.interface_index = j;
+ command.endpoint_etc.alternate_index = k;
+ command.endpoint_etc.endpoint_index = l;
+ command.endpoint_etc.descriptor = &tmp_endpoint;
+ if (ioctl(fRawFD, B_USB_RAW_COMMAND_GET_ENDPOINT_DESCRIPTOR_ETC, &command, sizeof(command)) ||
+ command.config.status != B_USB_RAW_STATUS_SUCCESS) {
+ usbi_err(NULL, "failed retrieving endpoint descriptor");
+ close(fRawFD);
+ return B_ERROR;
+ }
+ fEndpointToIndex[i][tmp_endpoint.endpoint_address] = l;
+ fEndpointToInterface[i][tmp_endpoint.endpoint_address] = j;
+ }
+ }
+ }
+ }
+ close(fRawFD);
+ fInitCheck = true;
+ return B_OK;
+}
diff --git a/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/haiku_usb_raw.cpp b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/haiku_usb_raw.cpp
new file mode 100644
index 000000000..77adbd1e6
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/haiku_usb_raw.cpp
@@ -0,0 +1,250 @@
+/*
+ * Haiku Backend for libusb
+ * Copyright © 2014 Akshay Jaggi <akshay1994.leo@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 <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <new>
+#include <vector>
+
+#include "haiku_usb.h"
+
+USBRoster gUsbRoster;
+int32 gInitCount = 0;
+
+static int
+haiku_init(struct libusb_context *ctx)
+{
+ if (atomic_add(&gInitCount, 1) == 0)
+ return gUsbRoster.Start();
+ return LIBUSB_SUCCESS;
+}
+
+static void
+haiku_exit(void)
+{
+ if (atomic_add(&gInitCount, -1) == 1)
+ gUsbRoster.Stop();
+}
+
+static int
+haiku_open(struct libusb_device_handle *dev_handle)
+{
+ USBDevice *dev = *((USBDevice **)dev_handle->dev->os_priv);
+ USBDeviceHandle *handle = new(std::nothrow) USBDeviceHandle(dev);
+ if (handle == NULL)
+ return LIBUSB_ERROR_NO_MEM;
+ if (handle->InitCheck() == false) {
+ delete handle;
+ return LIBUSB_ERROR_NO_DEVICE;
+ }
+ *((USBDeviceHandle **)dev_handle->os_priv) = handle;
+ return LIBUSB_SUCCESS;
+}
+
+static void
+haiku_close(struct libusb_device_handle *dev_handle)
+{
+ USBDeviceHandle *handle = *((USBDeviceHandle **)dev_handle->os_priv);
+ if (handle == NULL)
+ return;
+ delete handle;
+ *((USBDeviceHandle **)dev_handle->os_priv) = NULL;
+}
+
+static int
+haiku_get_device_descriptor(struct libusb_device *device, unsigned char *buffer, int *host_endian)
+{
+ USBDevice *dev = *((USBDevice **)device->os_priv);
+ memcpy(buffer, dev->Descriptor(), DEVICE_DESC_LENGTH);
+ *host_endian = 0;
+ return LIBUSB_SUCCESS;
+}
+
+static int
+haiku_get_active_config_descriptor(struct libusb_device *device, unsigned char *buffer, size_t len, int *host_endian)
+{
+ USBDevice *dev = *((USBDevice **)device->os_priv);
+ const usb_configuration_descriptor *act_config = dev->ActiveConfiguration();
+ if (len > act_config->total_length)
+ return LIBUSB_ERROR_OVERFLOW;
+ memcpy(buffer, act_config, len);
+ *host_endian = 0;
+ return LIBUSB_SUCCESS;
+}
+
+static int
+haiku_get_config_descriptor(struct libusb_device *device, uint8_t config_index, unsigned char *buffer, size_t len, int *host_endian)
+{
+ USBDevice *dev = *((USBDevice **)device->os_priv);
+ const usb_configuration_descriptor *config = dev->ConfigurationDescriptor(config_index);
+ if (config == NULL) {
+ usbi_err(DEVICE_CTX(device), "failed getting configuration descriptor");
+ return LIBUSB_ERROR_INVALID_PARAM;
+ }
+ if (len > config->total_length)
+ len = config->total_length;
+ memcpy(buffer, config, len);
+ *host_endian = 0;
+ return len;
+}
+
+static int
+haiku_set_configuration(struct libusb_device_handle *dev_handle, int config)
+{
+ USBDeviceHandle *handle= *((USBDeviceHandle **)dev_handle->os_priv);
+ return handle->SetConfiguration(config);
+}
+
+static int
+haiku_claim_interface(struct libusb_device_handle *dev_handle, int interface_number)
+{
+ USBDeviceHandle *handle = *((USBDeviceHandle **)dev_handle->os_priv);
+ return handle->ClaimInterface(interface_number);
+}
+
+static int
+haiku_set_altsetting(struct libusb_device_handle *dev_handle, int interface_number, int altsetting)
+{
+ USBDeviceHandle *handle = *((USBDeviceHandle **)dev_handle->os_priv);
+ return handle->SetAltSetting(interface_number, altsetting);
+}
+
+static int
+haiku_release_interface(struct libusb_device_handle *dev_handle, int interface_number)
+{
+ USBDeviceHandle *handle = *((USBDeviceHandle **)dev_handle->os_priv);
+ haiku_set_altsetting(dev_handle,interface_number, 0);
+ return handle->ReleaseInterface(interface_number);
+}
+
+static int
+haiku_submit_transfer(struct usbi_transfer *itransfer)
+{
+ struct libusb_transfer *fLibusbTransfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ USBDeviceHandle *fDeviceHandle = *((USBDeviceHandle **)fLibusbTransfer->dev_handle->os_priv);
+ return fDeviceHandle->SubmitTransfer(itransfer);
+}
+
+static int
+haiku_cancel_transfer(struct usbi_transfer *itransfer)
+{
+ struct libusb_transfer *fLibusbTransfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ USBDeviceHandle *fDeviceHandle = *((USBDeviceHandle **)fLibusbTransfer->dev_handle->os_priv);
+ return fDeviceHandle->CancelTransfer(*((USBTransfer **)usbi_transfer_get_os_priv(itransfer)));
+}
+
+static void
+haiku_clear_transfer_priv(struct usbi_transfer *itransfer)
+{
+ USBTransfer *transfer = *((USBTransfer **)usbi_transfer_get_os_priv(itransfer));
+ delete transfer;
+ *((USBTransfer **)usbi_transfer_get_os_priv(itransfer)) = NULL;
+}
+
+static int
+haiku_handle_transfer_completion(struct usbi_transfer *itransfer)
+{
+ USBTransfer *transfer = *((USBTransfer **)usbi_transfer_get_os_priv(itransfer));
+
+ usbi_mutex_lock(&itransfer->lock);
+ if (transfer->IsCancelled()) {
+ delete transfer;
+ *((USBTransfer **)usbi_transfer_get_os_priv(itransfer)) = NULL;
+ usbi_mutex_unlock(&itransfer->lock);
+ if (itransfer->transferred < 0)
+ itransfer->transferred = 0;
+ return usbi_handle_transfer_cancellation(itransfer);
+ }
+ libusb_transfer_status status = LIBUSB_TRANSFER_COMPLETED;
+ if (itransfer->transferred < 0) {
+ usbi_err(ITRANSFER_CTX(itransfer), "error in transfer");
+ status = LIBUSB_TRANSFER_ERROR;
+ itransfer->transferred = 0;
+ }
+ delete transfer;
+ *((USBTransfer **)usbi_transfer_get_os_priv(itransfer)) = NULL;
+ usbi_mutex_unlock(&itransfer->lock);
+ return usbi_handle_transfer_completion(itransfer, status);
+}
+
+static int
+haiku_clock_gettime(int clkid, struct timespec *tp)
+{
+ if (clkid == USBI_CLOCK_REALTIME)
+ return clock_gettime(CLOCK_REALTIME, tp);
+ if (clkid == USBI_CLOCK_MONOTONIC)
+ return clock_gettime(CLOCK_MONOTONIC, tp);
+ return LIBUSB_ERROR_INVALID_PARAM;
+}
+
+const struct usbi_os_backend haiku_usb_raw_backend = {
+ /*.name =*/ "Haiku usbfs",
+ /*.caps =*/ 0,
+ /*.init =*/ haiku_init,
+ /*.exit =*/ haiku_exit,
+ /*.get_device_list =*/ NULL,
+ /*.hotplug_poll =*/ NULL,
+ /*.open =*/ haiku_open,
+ /*.close =*/ haiku_close,
+ /*.get_device_descriptor =*/ haiku_get_device_descriptor,
+ /*.get_active_config_descriptor =*/ haiku_get_active_config_descriptor,
+ /*.get_config_descriptor =*/ haiku_get_config_descriptor,
+ /*.get_config_descriptor_by_value =*/ NULL,
+
+
+ /*.get_configuration =*/ NULL,
+ /*.set_configuration =*/ haiku_set_configuration,
+ /*.claim_interface =*/ haiku_claim_interface,
+ /*.release_interface =*/ haiku_release_interface,
+
+ /*.set_interface_altsetting =*/ haiku_set_altsetting,
+ /*.clear_halt =*/ NULL,
+ /*.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 =*/ NULL,
+
+ /*.destroy_device =*/ NULL,
+
+ /*.submit_transfer =*/ haiku_submit_transfer,
+ /*.cancel_transfer =*/ haiku_cancel_transfer,
+ /*.clear_transfer_priv =*/ haiku_clear_transfer_priv,
+
+ /*.handle_events =*/ NULL,
+ /*.handle_transfer_completion =*/ haiku_handle_transfer_completion,
+
+ /*.clock_gettime =*/ haiku_clock_gettime,
+
+#ifdef USBI_TIMERFD_AVAILABLE
+ /*.get_timerfd_clockid =*/ NULL,
+#endif
+
+ /*.device_priv_size =*/ sizeof(USBDevice *),
+ /*.device_handle_priv_size =*/ sizeof(USBDeviceHandle *),
+ /*.transfer_priv_size =*/ sizeof(USBTransfer *),
+};
diff --git a/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/haiku_usb_raw.h b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/haiku_usb_raw.h
new file mode 100644
index 000000000..5baf53d7c
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/haiku_usb_raw.h
@@ -0,0 +1,180 @@
+/*
+ * Copyright 2006-2008, Haiku Inc. All rights reserved.
+ * Distributed under the terms of the MIT License.
+ */
+
+#ifndef _USB_RAW_H_
+#define _USB_RAW_H_
+
+#include <USB3.h>
+
+#define B_USB_RAW_PROTOCOL_VERSION 0x0015
+#define B_USB_RAW_ACTIVE_ALTERNATE 0xffffffff
+
+typedef enum {
+ B_USB_RAW_COMMAND_GET_VERSION = 0x1000,
+
+ B_USB_RAW_COMMAND_GET_DEVICE_DESCRIPTOR = 0x2000,
+ B_USB_RAW_COMMAND_GET_CONFIGURATION_DESCRIPTOR,
+ B_USB_RAW_COMMAND_GET_INTERFACE_DESCRIPTOR,
+ B_USB_RAW_COMMAND_GET_ENDPOINT_DESCRIPTOR,
+ B_USB_RAW_COMMAND_GET_STRING_DESCRIPTOR,
+ B_USB_RAW_COMMAND_GET_GENERIC_DESCRIPTOR,
+ B_USB_RAW_COMMAND_GET_ALT_INTERFACE_COUNT,
+ B_USB_RAW_COMMAND_GET_ACTIVE_ALT_INTERFACE_INDEX,
+ B_USB_RAW_COMMAND_GET_INTERFACE_DESCRIPTOR_ETC,
+ B_USB_RAW_COMMAND_GET_ENDPOINT_DESCRIPTOR_ETC,
+ B_USB_RAW_COMMAND_GET_GENERIC_DESCRIPTOR_ETC,
+
+ B_USB_RAW_COMMAND_SET_CONFIGURATION = 0x3000,
+ B_USB_RAW_COMMAND_SET_FEATURE,
+ B_USB_RAW_COMMAND_CLEAR_FEATURE,
+ B_USB_RAW_COMMAND_GET_STATUS,
+ B_USB_RAW_COMMAND_GET_DESCRIPTOR,
+ B_USB_RAW_COMMAND_SET_ALT_INTERFACE,
+
+ B_USB_RAW_COMMAND_CONTROL_TRANSFER = 0x4000,
+ B_USB_RAW_COMMAND_INTERRUPT_TRANSFER,
+ B_USB_RAW_COMMAND_BULK_TRANSFER,
+ B_USB_RAW_COMMAND_ISOCHRONOUS_TRANSFER
+} usb_raw_command_id;
+
+
+typedef enum {
+ B_USB_RAW_STATUS_SUCCESS = 0,
+
+ B_USB_RAW_STATUS_FAILED,
+ B_USB_RAW_STATUS_ABORTED,
+ B_USB_RAW_STATUS_STALLED,
+ B_USB_RAW_STATUS_CRC_ERROR,
+ B_USB_RAW_STATUS_TIMEOUT,
+
+ B_USB_RAW_STATUS_INVALID_CONFIGURATION,
+ B_USB_RAW_STATUS_INVALID_INTERFACE,
+ B_USB_RAW_STATUS_INVALID_ENDPOINT,
+ B_USB_RAW_STATUS_INVALID_STRING,
+
+ B_USB_RAW_STATUS_NO_MEMORY
+} usb_raw_command_status;
+
+
+typedef union {
+ struct {
+ status_t status;
+ } version;
+
+ struct {
+ status_t status;
+ usb_device_descriptor *descriptor;
+ } device;
+
+ struct {
+ status_t status;
+ usb_configuration_descriptor *descriptor;
+ uint32 config_index;
+ } config;
+
+ struct {
+ status_t status;
+ uint32 alternate_info;
+ uint32 config_index;
+ uint32 interface_index;
+ } alternate;
+
+ struct {
+ status_t status;
+ usb_interface_descriptor *descriptor;
+ uint32 config_index;
+ uint32 interface_index;
+ } interface;
+
+ struct {
+ status_t status;
+ usb_interface_descriptor *descriptor;
+ uint32 config_index;
+ uint32 interface_index;
+ uint32 alternate_index;
+ } interface_etc;
+
+ struct {
+ status_t status;
+ usb_endpoint_descriptor *descriptor;
+ uint32 config_index;
+ uint32 interface_index;
+ uint32 endpoint_index;
+ } endpoint;
+
+ struct {
+ status_t status;
+ usb_endpoint_descriptor *descriptor;
+ uint32 config_index;
+ uint32 interface_index;
+ uint32 alternate_index;
+ uint32 endpoint_index;
+ } endpoint_etc;
+
+ struct {
+ status_t status;
+ usb_descriptor *descriptor;
+ uint32 config_index;
+ uint32 interface_index;
+ uint32 generic_index;
+ size_t length;
+ } generic;
+
+ struct {
+ status_t status;
+ usb_descriptor *descriptor;
+ uint32 config_index;
+ uint32 interface_index;
+ uint32 alternate_index;
+ uint32 generic_index;
+ size_t length;
+ } generic_etc;
+
+ struct {
+ status_t status;
+ usb_string_descriptor *descriptor;
+ uint32 string_index;
+ size_t length;
+ } string;
+
+ struct {
+ status_t status;
+ uint8 type;
+ uint8 index;
+ uint16 language_id;
+ void *data;
+ size_t length;
+ } descriptor;
+
+ struct {
+ status_t status;
+ uint8 request_type;
+ uint8 request;
+ uint16 value;
+ uint16 index;
+ uint16 length;
+ void *data;
+ } control;
+
+ struct {
+ status_t status;
+ uint32 interface;
+ uint32 endpoint;
+ void *data;
+ size_t length;
+ } transfer;
+
+ struct {
+ status_t status;
+ uint32 interface;
+ uint32 endpoint;
+ void *data;
+ size_t length;
+ usb_iso_packet_descriptor *packet_descriptors;
+ uint32 packet_count;
+ } isochronous;
+} usb_raw_command;
+
+#endif // _USB_RAW_H_
diff --git a/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/linux_netlink.c b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/linux_netlink.c
new file mode 100644
index 000000000..60cf3ad1b
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/linux_netlink.c
@@ -0,0 +1,400 @@
+/* -*- Mode: C; c-basic-offset:8 ; indent-tabs-mode:t -*- */
+/*
+ * Linux usbfs backend for libusb
+ * Copyright (C) 2007-2009 Daniel Drake <dsd@gentoo.org>
+ * Copyright (c) 2001 Johannes Erdfelt <johannes@erdfelt.com>
+ * Copyright (c) 2013 Nathan Hjelm <hjelmn@mac.com>
+ * Copyright (c) 2016 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
+ */
+
+#include <config.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#ifdef HAVE_ASM_TYPES_H
+#include <asm/types.h>
+#endif
+
+#include <sys/socket.h>
+#include <linux/netlink.h>
+
+#include "libusbi.h"
+#include "linux_usbfs.h"
+
+#define NL_GROUP_KERNEL 1
+
+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)
+{
+ int flags;
+
+#if defined(FD_CLOEXEC)
+ flags = fcntl(fd, F_GETFD);
+ if (flags == -1) {
+ usbi_err(NULL, "failed to get netlink fd flags (%d)", errno);
+ return -1;
+ }
+
+ 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;
+ }
+ }
+#endif
+
+ 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;
+ }
+ }
+
+ return 0;
+}
+
+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 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);
+ }
+
+ if (linux_netlink_socket == -1) {
+ usbi_err(NULL, "failed to create netlink socket (%d)", errno);
+ goto err;
+ }
+
+ ret = set_fd_cloexec_nb(linux_netlink_socket);
+ if (ret == -1)
+ goto err_close_socket;
+
+ ret = bind(linux_netlink_socket, (struct sockaddr *)&sa_nl, sizeof(sa_nl));
+ if (ret == -1) {
+ usbi_err(NULL, "failed to bind netlink socket (%d)", errno);
+ goto err_close_socket;
+ }
+
+ ret = setsockopt(linux_netlink_socket, SOL_SOCKET, SO_PASSCRED, &opt, sizeof(opt));
+ if (ret == -1) {
+ usbi_err(NULL, "failed to set netlink socket SO_PASSCRED option (%d)", errno);
+ goto err_close_socket;
+ }
+
+ ret = usbi_pipe(netlink_control_pipe);
+ if (ret) {
+ usbi_err(NULL, "failed to create netlink control pipe");
+ goto err_close_socket;
+ }
+
+ ret = pthread_create(&libusb_linux_event_thread, NULL, linux_netlink_event_thread_main, NULL);
+ if (ret != 0) {
+ usbi_err(NULL, "failed to create netlink event thread (%d)", ret);
+ goto err_close_pipe;
+ }
+
+ return LIBUSB_SUCCESS;
+
+err_close_pipe:
+ close(netlink_control_pipe[0]);
+ close(netlink_control_pipe[1]);
+ netlink_control_pipe[0] = -1;
+ netlink_control_pipe[1] = -1;
+err_close_socket:
+ close(linux_netlink_socket);
+ linux_netlink_socket = -1;
+err:
+ return LIBUSB_ERROR_OTHER;
+}
+
+int linux_netlink_stop_event_monitor(void)
+{
+ char dummy = 1;
+ ssize_t r;
+
+ assert(linux_netlink_socket != -1);
+
+ /* 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));
+ if (r <= 0)
+ usbi_warn(NULL, "netlink control pipe signal failed");
+
+ pthread_join(libusb_linux_event_thread, NULL);
+
+ close(linux_netlink_socket);
+ linux_netlink_socket = -1;
+
+ /* close and reset control pipe */
+ close(netlink_control_pipe[0]);
+ close(netlink_control_pipe[1]);
+ netlink_control_pipe[0] = -1;
+ netlink_control_pipe[1] = -1;
+
+ return LIBUSB_SUCCESS;
+}
+
+static const char *netlink_message_parse(const char *buffer, size_t len, const char *key)
+{
+ const char *end = buffer + len;
+ size_t keylen = strlen(key);
+
+ while (buffer < end && *buffer) {
+ if (strncmp(buffer, key, keylen) == 0 && buffer[keylen] == '=')
+ return buffer + keylen + 1;
+ buffer += strlen(buffer) + 1;
+ }
+
+ return NULL;
+}
+
+/* parse parts of netlink message common to both libudev and the kernel */
+static int linux_netlink_parse(const char *buffer, size_t len, int *detached,
+ const char **sys_name, uint8_t *busnum, uint8_t *devaddr)
+{
+ const char *tmp, *slash;
+
+ errno = 0;
+
+ *sys_name = NULL;
+ *detached = 0;
+ *busnum = 0;
+ *devaddr = 0;
+
+ tmp = netlink_message_parse(buffer, len, "ACTION");
+ if (!tmp) {
+ return -1;
+ } else if (strcmp(tmp, "remove") == 0) {
+ *detached = 1;
+ } else if (strcmp(tmp, "add") != 0) {
+ usbi_dbg("unknown device action %s", tmp);
+ return -1;
+ }
+
+ /* check that this is a usb message */
+ tmp = netlink_message_parse(buffer, len, "SUBSYSTEM");
+ if (!tmp || strcmp(tmp, "usb") != 0) {
+ /* not usb. ignore */
+ return -1;
+ }
+
+ /* check that this is an actual usb device */
+ tmp = netlink_message_parse(buffer, len, "DEVTYPE");
+ if (!tmp || strcmp(tmp, "usb_device") != 0) {
+ /* not usb. ignore */
+ return -1;
+ }
+
+ tmp = netlink_message_parse(buffer, len, "BUSNUM");
+ if (tmp) {
+ *busnum = (uint8_t)(strtoul(tmp, NULL, 10) & 0xff);
+ if (errno) {
+ errno = 0;
+ return -1;
+ }
+
+ tmp = netlink_message_parse(buffer, len, "DEVNUM");
+ if (NULL == tmp)
+ return -1;
+
+ *devaddr = (uint8_t)(strtoul(tmp, NULL, 10) & 0xff);
+ if (errno) {
+ errno = 0;
+ return -1;
+ }
+ } else {
+ /* no bus number. try "DEVICE" */
+ tmp = netlink_message_parse(buffer, len, "DEVICE");
+ if (!tmp) {
+ /* not usb. ignore */
+ return -1;
+ }
+
+ /* Parse a device path such as /dev/bus/usb/003/004 */
+ slash = strrchr(tmp, '/');
+ if (!slash)
+ return -1;
+
+ *busnum = (uint8_t)(strtoul(slash - 3, NULL, 10) & 0xff);
+ if (errno) {
+ errno = 0;
+ return -1;
+ }
+
+ *devaddr = (uint8_t)(strtoul(slash + 1, NULL, 10) & 0xff);
+ if (errno) {
+ errno = 0;
+ return -1;
+ }
+
+ return 0;
+ }
+
+ tmp = netlink_message_parse(buffer, len, "DEVPATH");
+ if (!tmp)
+ return -1;
+
+ slash = strrchr(tmp, '/');
+ if (slash)
+ *sys_name = slash + 1;
+
+ /* found a usb device */
+ return 0;
+}
+
+static int linux_netlink_read_message(void)
+{
+ char cred_buffer[CMSG_SPACE(sizeof(struct ucred))];
+ char msg_buffer[2048];
+ const char *sys_name = NULL;
+ uint8_t busnum, devaddr;
+ int detached, r;
+ ssize_t len;
+ struct cmsghdr *cmsg;
+ struct ucred *cred;
+ struct sockaddr_nl sa_nl;
+ struct iovec iov = { .iov_base = msg_buffer, .iov_len = sizeof(msg_buffer) };
+ struct msghdr msg = {
+ .msg_iov = &iov, .msg_iovlen = 1,
+ .msg_control = cred_buffer, .msg_controllen = sizeof(cred_buffer),
+ .msg_name = &sa_nl, .msg_namelen = sizeof(sa_nl)
+ };
+
+ /* read netlink message */
+ len = recvmsg(linux_netlink_socket, &msg, 0);
+ if (len == -1) {
+ if (errno != EAGAIN && errno != EINTR)
+ usbi_err(NULL, "error receiving message from netlink (%d)", errno);
+ return -1;
+ }
+
+ if (len < 32 || (msg.msg_flags & MSG_TRUNC)) {
+ usbi_err(NULL, "invalid netlink message length");
+ return -1;
+ }
+
+ if (sa_nl.nl_groups != NL_GROUP_KERNEL || sa_nl.nl_pid != 0) {
+ usbi_dbg("ignoring netlink message from unknown group/PID (%u/%u)",
+ (unsigned int)sa_nl.nl_groups, (unsigned int)sa_nl.nl_pid);
+ return -1;
+ }
+
+ cmsg = CMSG_FIRSTHDR(&msg);
+ if (!cmsg || cmsg->cmsg_type != SCM_CREDENTIALS) {
+ usbi_dbg("ignoring netlink message with no sender credentials");
+ return -1;
+ }
+
+ cred = (struct ucred *)CMSG_DATA(cmsg);
+ if (cred->uid != 0) {
+ usbi_dbg("ignoring netlink message with non-zero sender UID %u", (unsigned int)cred->uid);
+ return -1;
+ }
+
+ r = linux_netlink_parse(msg_buffer, (size_t)len, &detached, &sys_name, &busnum, &devaddr);
+ if (r)
+ return r;
+
+ usbi_dbg("netlink hotplug found device busnum: %hhu, devaddr: %hhu, sys_name: %s, removed: %s",
+ busnum, devaddr, sys_name, detached ? "yes" : "no");
+
+ /* signal device is available (or not) to all contexts */
+ if (detached)
+ linux_device_disconnected(busnum, devaddr);
+ else
+ linux_hotplug_enumerate(busnum, devaddr, sys_name);
+
+ return 0;
+}
+
+static void *linux_netlink_event_thread_main(void *arg)
+{
+ char dummy;
+ ssize_t r;
+ struct pollfd fds[] = {
+ { .fd = netlink_control_pipe[0],
+ .events = POLLIN },
+ { .fd = linux_netlink_socket,
+ .events = POLLIN },
+ };
+
+ UNUSED(arg);
+
+ usbi_dbg("netlink event thread entering");
+
+ while (poll(fds, 2, -1) >= 0) {
+ 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)
+ usbi_warn(NULL, "netlink control pipe read failed");
+ break;
+ }
+ if (fds[1].revents & POLLIN) {
+ usbi_mutex_static_lock(&linux_hotplug_lock);
+ linux_netlink_read_message();
+ usbi_mutex_static_unlock(&linux_hotplug_lock);
+ }
+ }
+
+ usbi_dbg("netlink event thread exiting");
+
+ return NULL;
+}
+
+void linux_netlink_hotplug_poll(void)
+{
+ int r;
+
+ usbi_mutex_static_lock(&linux_hotplug_lock);
+ do {
+ r = linux_netlink_read_message();
+ } while (r == 0);
+ usbi_mutex_static_unlock(&linux_hotplug_lock);
+}
diff --git a/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/linux_udev.c b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/linux_udev.c
new file mode 100644
index 000000000..61d953d8c
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/linux_udev.c
@@ -0,0 +1,311 @@
+/* -*- Mode: C; c-basic-offset:8 ; indent-tabs-mode:t -*- */
+/*
+ * Linux usbfs backend for libusb
+ * Copyright (C) 2007-2009 Daniel Drake <dsd@gentoo.org>
+ * Copyright (c) 2001 Johannes Erdfelt <johannes@erdfelt.com>
+ * Copyright (c) 2012-2013 Nathan Hjelm <hjelmn@mac.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 <assert.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <libudev.h>
+
+#include "libusbi.h"
+#include "linux_usbfs.h"
+
+/* udev context */
+static struct udev *udev_ctx = NULL;
+static int udev_monitor_fd = -1;
+static int udev_control_pipe[2] = {-1, -1};
+static struct udev_monitor *udev_monitor = NULL;
+static pthread_t linux_event_thread;
+
+static void udev_hotplug_event(struct udev_device* udev_dev);
+static void *linux_udev_event_thread_main(void *arg);
+
+int linux_udev_start_event_monitor(void)
+{
+ int r;
+
+ assert(udev_ctx == NULL);
+ udev_ctx = udev_new();
+ if (!udev_ctx) {
+ usbi_err(NULL, "could not create udev context");
+ goto err;
+ }
+
+ udev_monitor = udev_monitor_new_from_netlink(udev_ctx, "udev");
+ if (!udev_monitor) {
+ usbi_err(NULL, "could not initialize udev monitor");
+ goto err_free_ctx;
+ }
+
+ r = udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, "usb", "usb_device");
+ if (r) {
+ usbi_err(NULL, "could not initialize udev monitor filter for \"usb\" subsystem");
+ goto err_free_monitor;
+ }
+
+ if (udev_monitor_enable_receiving(udev_monitor)) {
+ usbi_err(NULL, "failed to enable the udev monitor");
+ goto err_free_monitor;
+ }
+
+ udev_monitor_fd = udev_monitor_get_fd(udev_monitor);
+
+ /* 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);
+ goto err_free_monitor;
+ }
+ r = fcntl(udev_monitor_fd, F_SETFL, r | O_NONBLOCK);
+ if (r) {
+ usbi_err(NULL, "setting udev monitor fd flags (%d)", errno);
+ goto err_free_monitor;
+ }
+
+ r = usbi_pipe(udev_control_pipe);
+ if (r) {
+ usbi_err(NULL, "could not create udev control pipe");
+ goto err_free_monitor;
+ }
+
+ r = pthread_create(&linux_event_thread, NULL, linux_udev_event_thread_main, NULL);
+ if (r) {
+ usbi_err(NULL, "creating hotplug event thread (%d)", r);
+ goto err_close_pipe;
+ }
+
+ return LIBUSB_SUCCESS;
+
+err_close_pipe:
+ close(udev_control_pipe[0]);
+ close(udev_control_pipe[1]);
+err_free_monitor:
+ udev_monitor_unref(udev_monitor);
+ udev_monitor = NULL;
+ udev_monitor_fd = -1;
+err_free_ctx:
+ udev_unref(udev_ctx);
+err:
+ udev_ctx = NULL;
+ return LIBUSB_ERROR_OTHER;
+}
+
+int linux_udev_stop_event_monitor(void)
+{
+ char dummy = 1;
+ int r;
+
+ assert(udev_ctx != NULL);
+ assert(udev_monitor != NULL);
+ assert(udev_monitor_fd != -1);
+
+ /* 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));
+ if (r <= 0) {
+ usbi_warn(NULL, "udev control pipe signal failed");
+ }
+ pthread_join(linux_event_thread, NULL);
+
+ /* Release the udev monitor */
+ udev_monitor_unref(udev_monitor);
+ udev_monitor = NULL;
+ udev_monitor_fd = -1;
+
+ /* Clean up the udev context */
+ udev_unref(udev_ctx);
+ udev_ctx = NULL;
+
+ /* close and reset control pipe */
+ close(udev_control_pipe[0]);
+ close(udev_control_pipe[1]);
+ udev_control_pipe[0] = -1;
+ udev_control_pipe[1] = -1;
+
+ return LIBUSB_SUCCESS;
+}
+
+static void *linux_udev_event_thread_main(void *arg)
+{
+ char dummy;
+ int r;
+ struct udev_device* udev_dev;
+ struct pollfd fds[] = {
+ {.fd = udev_control_pipe[0],
+ .events = POLLIN},
+ {.fd = udev_monitor_fd,
+ .events = POLLIN},
+ };
+
+ usbi_dbg("udev event thread entering.");
+
+ 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(udev_control_pipe[0], &dummy, sizeof(dummy));
+ if (r <= 0) {
+ usbi_warn(NULL, "udev control pipe read failed");
+ }
+ break;
+ }
+ if (fds[1].revents & POLLIN) {
+ usbi_mutex_static_lock(&linux_hotplug_lock);
+ udev_dev = udev_monitor_receive_device(udev_monitor);
+ if (udev_dev)
+ udev_hotplug_event(udev_dev);
+ usbi_mutex_static_unlock(&linux_hotplug_lock);
+ }
+ }
+
+ usbi_dbg("udev event thread exiting");
+
+ return NULL;
+}
+
+static int udev_device_info(struct libusb_context *ctx, int detached,
+ struct udev_device *udev_dev, uint8_t *busnum,
+ uint8_t *devaddr, const char **sys_name) {
+ const char *dev_node;
+
+ dev_node = udev_device_get_devnode(udev_dev);
+ if (!dev_node) {
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ *sys_name = udev_device_get_sysname(udev_dev);
+ if (!*sys_name) {
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ return linux_get_device_address(ctx, detached, busnum, devaddr,
+ dev_node, *sys_name);
+}
+
+static void udev_hotplug_event(struct udev_device* udev_dev)
+{
+ const char* udev_action;
+ const char* sys_name = NULL;
+ uint8_t busnum = 0, devaddr = 0;
+ int detached;
+ int r;
+
+ do {
+ udev_action = udev_device_get_action(udev_dev);
+ if (!udev_action) {
+ break;
+ }
+
+ detached = !strncmp(udev_action, "remove", 6);
+
+ r = udev_device_info(NULL, detached, udev_dev, &busnum, &devaddr, &sys_name);
+ if (LIBUSB_SUCCESS != r) {
+ break;
+ }
+
+ usbi_dbg("udev hotplug event. action: %s.", udev_action);
+
+ if (strncmp(udev_action, "add", 3) == 0) {
+ linux_hotplug_enumerate(busnum, devaddr, sys_name);
+ } else if (detached) {
+ linux_device_disconnected(busnum, devaddr);
+ } else {
+ usbi_err(NULL, "ignoring udev action %s", udev_action);
+ }
+ } while (0);
+
+ udev_device_unref(udev_dev);
+}
+
+int linux_udev_scan_devices(struct libusb_context *ctx)
+{
+ struct udev_enumerate *enumerator;
+ struct udev_list_entry *devices, *entry;
+ struct udev_device *udev_dev;
+ const char *sys_name;
+ int r;
+
+ assert(udev_ctx != NULL);
+
+ enumerator = udev_enumerate_new(udev_ctx);
+ if (NULL == enumerator) {
+ usbi_err(ctx, "error creating udev enumerator");
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ udev_enumerate_add_match_subsystem(enumerator, "usb");
+ udev_enumerate_add_match_property(enumerator, "DEVTYPE", "usb_device");
+ udev_enumerate_scan_devices(enumerator);
+ devices = udev_enumerate_get_list_entry(enumerator);
+
+ udev_list_entry_foreach(entry, devices) {
+ const char *path = udev_list_entry_get_name(entry);
+ uint8_t busnum = 0, devaddr = 0;
+
+ udev_dev = udev_device_new_from_syspath(udev_ctx, path);
+
+ r = udev_device_info(ctx, 0, udev_dev, &busnum, &devaddr, &sys_name);
+ if (r) {
+ udev_device_unref(udev_dev);
+ continue;
+ }
+
+ linux_enumerate_device(ctx, busnum, devaddr, sys_name);
+ udev_device_unref(udev_dev);
+ }
+
+ udev_enumerate_unref(enumerator);
+
+ return LIBUSB_SUCCESS;
+}
+
+void linux_udev_hotplug_poll(void)
+{
+ struct udev_device* udev_dev;
+
+ usbi_mutex_static_lock(&linux_hotplug_lock);
+ do {
+ udev_dev = udev_monitor_receive_device(udev_monitor);
+ if (udev_dev) {
+ usbi_dbg("Handling hotplug event from hotplug_poll");
+ udev_hotplug_event(udev_dev);
+ }
+ } while (udev_dev);
+ usbi_mutex_static_unlock(&linux_hotplug_lock);
+}
diff --git a/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/linux_usbfs.c b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/linux_usbfs.c
new file mode 100644
index 000000000..6b89ba288
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/linux_usbfs.c
@@ -0,0 +1,2738 @@
+/* -*- Mode: C; c-basic-offset:8 ; indent-tabs-mode:t -*- */
+/*
+ * Linux usbfs backend for libusb
+ * Copyright © 2007-2009 Daniel Drake <dsd@gentoo.org>
+ * Copyright © 2001 Johannes Erdfelt <johannes@erdfelt.com>
+ * Copyright © 2013 Nathan Hjelm <hjelmn@mac.com>
+ * Copyright © 2012-2013 Hans de Goede <hdegoede@redhat.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 <assert.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+#include <time.h>
+
+#include "libusbi.h"
+#include "linux_usbfs.h"
+
+/* sysfs vs usbfs:
+ * opening a usbfs node causes the device to be resumed, so we attempt to
+ * avoid this during enumeration.
+ *
+ * sysfs allows us to read the kernel's in-memory copies of device descriptors
+ * and so forth, avoiding the need to open the device:
+ * - The binary "descriptors" file contains all config descriptors since
+ * 2.6.26, commit 217a9081d8e69026186067711131b77f0ce219ed
+ * - The binary "descriptors" file was added in 2.6.23, commit
+ * 69d42a78f935d19384d1f6e4f94b65bb162b36df, but it only contains the
+ * active config descriptors
+ * - The "busnum" file was added in 2.6.22, commit
+ * 83f7d958eab2fbc6b159ee92bf1493924e1d0f72
+ * - The "devnum" file has been present since pre-2.6.18
+ * - the "bConfigurationValue" file has been present since pre-2.6.18
+ *
+ * If we have bConfigurationValue, busnum, and devnum, then we can determine
+ * the active configuration without having to open the usbfs node in RDWR mode.
+ * The busnum file is important as that is the only way we can relate sysfs
+ * devices to usbfs nodes.
+ *
+ * If we also have all descriptors, we can obtain the device descriptor and
+ * configuration without touching usbfs at all.
+ */
+
+/* endianness for multi-byte fields:
+ *
+ * Descriptors exposed by usbfs have the multi-byte fields in the device
+ * descriptor as host endian. Multi-byte fields in the other descriptors are
+ * bus-endian. The kernel documentation says otherwise, but it is wrong.
+ *
+ * In sysfs all descriptors are bus-endian.
+ */
+
+static const char *usbfs_path = NULL;
+
+/* use usbdev*.* device names in /dev instead of the usbfs bus directories */
+static int usbdev_names = 0;
+
+/* 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
+ * URBs within that transfer will be cancelled and no more URBs will be
+ * accepted for the transfer, meaning that no more data can creep in.
+ *
+ * The BULK_CONTINUATION flag must be set on all URBs within a bulk transfer
+ * (in either direction) except the first.
+ * For IN transfers, we must also set SHORT_NOT_OK on all URBs except the
+ * last; it means that the kernel should treat a short reply as an error.
+ * For OUT transfers, SHORT_NOT_OK must not be set. it isn't needed (OUT
+ * transfers can't be short unless there's already some sort of error), and
+ * setting this flag is disallowed (a kernel with USB debugging enabled will
+ * reject such URBs).
+ */
+static int supports_flag_bulk_continuation = -1;
+
+/* Linux 2.6.31 fixes support for the zero length packet URB flag. This
+ * allows us to mark URBs that should be followed by a zero length data
+ * packet, which can be required by device- or class-specific protocols.
+ */
+static int supports_flag_zero_packet = -1;
+
+/* clock ID for monotonic clock, as not all clock sources are available on all
+ * systems. appropriate choice made at initialization time. */
+static clockid_t monotonic_clkid = -1;
+
+/* Linux 2.6.22 (commit 83f7d958eab2fbc6b159ee92bf1493924e1d0f72) adds a busnum
+ * to sysfs, so we can relate devices. This also implies that we can read
+ * the active configuration through bConfigurationValue */
+static int sysfs_can_relate_devices = -1;
+
+/* Linux 2.6.26 (commit 217a9081d8e69026186067711131b77f0ce219ed) adds all
+ * config descriptors (rather then just the active config) to the sysfs
+ * descriptors file, so from then on we can use them. */
+static int sysfs_has_descriptors = -1;
+
+/* how many times have we initted (and not exited) ? */
+static int init_count = 0;
+
+/* Serialize hotplug start/stop */
+static usbi_mutex_static_t linux_hotplug_startstop_lock = USBI_MUTEX_INITIALIZER;
+/* Serialize scan-devices, event-thread, and poll */
+usbi_mutex_static_t linux_hotplug_lock = USBI_MUTEX_INITIALIZER;
+
+static int linux_start_event_monitor(void);
+static int linux_stop_event_monitor(void);
+static int linux_scan_devices(struct libusb_context *ctx);
+static int sysfs_scan_device(struct libusb_context *ctx, const char *devname);
+static int detach_kernel_driver_and_claim(struct libusb_device_handle *, int);
+
+#if !defined(USE_UDEV)
+static int linux_default_scan_devices (struct libusb_context *ctx);
+#endif
+
+struct linux_device_priv {
+ char *sysfs_dir;
+ unsigned char *descriptors;
+ int descriptors_len;
+ int active_config; /* cache val for !sysfs_can_relate_devices */
+};
+
+struct linux_device_handle_priv {
+ int fd;
+ int fd_removed;
+ uint32_t caps;
+};
+
+enum reap_action {
+ NORMAL = 0,
+ /* submission failed after the first URB, so await cancellation/completion
+ * of all the others */
+ SUBMIT_FAILED,
+
+ /* cancelled by user or timeout */
+ CANCELLED,
+
+ /* completed multi-URB transfer in non-final URB */
+ COMPLETED_EARLY,
+
+ /* one or more urbs encountered a low-level error */
+ ERROR,
+};
+
+struct linux_transfer_priv {
+ union {
+ struct usbfs_urb *urbs;
+ struct usbfs_urb **iso_urbs;
+ };
+
+ enum reap_action reap_action;
+ int num_urbs;
+ int num_retired;
+ enum libusb_transfer_status reap_status;
+
+ /* next iso packet in user-supplied transfer to be populated */
+ int iso_packet_offset;
+};
+
+static int _get_usbfs_fd(struct libusb_device *dev, mode_t mode, int silent)
+{
+ struct libusb_context *ctx = DEVICE_CTX(dev);
+ char path[PATH_MAX];
+ int fd;
+ int delay = 10000;
+
+ if (usbdev_names)
+ snprintf(path, PATH_MAX, "%s/usbdev%d.%d",
+ usbfs_path, dev->bus_number, dev->device_address);
+ else
+ snprintf(path, PATH_MAX, "%s/%03d/%03d",
+ usbfs_path, dev->bus_number, dev->device_address);
+
+ fd = open(path, mode);
+ if (fd != -1)
+ return fd; /* Success */
+
+ if (errno == ENOENT) {
+ if (!silent)
+ usbi_err(ctx, "File doesn't exist, wait %d ms and try again", delay/1000);
+
+ /* Wait 10ms for USB device path creation.*/
+ nanosleep(&(struct timespec){delay / 1000000, (delay * 1000) % 1000000000UL}, NULL);
+
+ fd = open(path, mode);
+ if (fd != -1)
+ return fd; /* Success */
+ }
+
+ if (!silent) {
+ usbi_err(ctx, "libusb couldn't open USB device %s: %s",
+ path, strerror(errno));
+ if (errno == EACCES && mode == O_RDWR)
+ usbi_err(ctx, "libusb requires write access to USB "
+ "device nodes.");
+ }
+
+ if (errno == EACCES)
+ return LIBUSB_ERROR_ACCESS;
+ if (errno == ENOENT)
+ return LIBUSB_ERROR_NO_DEVICE;
+ return LIBUSB_ERROR_IO;
+}
+
+static struct linux_device_priv *_device_priv(struct libusb_device *dev)
+{
+ return (struct linux_device_priv *) dev->os_priv;
+}
+
+static struct linux_device_handle_priv *_device_handle_priv(
+ struct libusb_device_handle *handle)
+{
+ return (struct linux_device_handle_priv *) handle->os_priv;
+}
+
+/* check dirent for a /dev/usbdev%d.%d name
+ * optionally return bus/device on success */
+static int _is_usbdev_entry(struct dirent *entry, int *bus_p, int *dev_p)
+{
+ int busnum, devnum;
+
+ if (sscanf(entry->d_name, "usbdev%d.%d", &busnum, &devnum) != 2)
+ return 0;
+
+ usbi_dbg("found: %s", entry->d_name);
+ if (bus_p != NULL)
+ *bus_p = busnum;
+ if (dev_p != NULL)
+ *dev_p = devnum;
+ return 1;
+}
+
+static int check_usb_vfs(const char *dirname)
+{
+ DIR *dir;
+ struct dirent *entry;
+ int found = 0;
+
+ dir = opendir(dirname);
+ if (!dir)
+ return 0;
+
+ while ((entry = readdir(dir)) != NULL) {
+ if (entry->d_name[0] == '.')
+ continue;
+
+ /* We assume if we find any files that it must be the right place */
+ found = 1;
+ break;
+ }
+
+ closedir(dir);
+ return found;
+}
+
+static const char *find_usbfs_path(void)
+{
+ const char *path = "/dev/bus/usb";
+ const char *ret = NULL;
+
+ if (check_usb_vfs(path)) {
+ ret = path;
+ } else {
+ path = "/proc/bus/usb";
+ if (check_usb_vfs(path))
+ ret = path;
+ }
+
+ /* look for /dev/usbdev*.* if the normal places fail */
+ if (ret == NULL) {
+ struct dirent *entry;
+ DIR *dir;
+
+ path = "/dev";
+ dir = opendir(path);
+ if (dir != NULL) {
+ while ((entry = readdir(dir)) != NULL) {
+ if (_is_usbdev_entry(entry, NULL, NULL)) {
+ /* found one; that's enough */
+ ret = path;
+ usbdev_names = 1;
+ break;
+ }
+ }
+ closedir(dir);
+ }
+ }
+
+/* On udev based systems without any usb-devices /dev/bus/usb will not
+ * exist. So if we've not found anything and we're using udev for hotplug
+ * simply assume /dev/bus/usb rather then making libusb_init fail. */
+#if defined(USE_UDEV)
+ if (ret == NULL)
+ ret = "/dev/bus/usb";
+#endif
+
+ if (ret != NULL)
+ usbi_dbg("found usbfs at %s", ret);
+
+ return ret;
+}
+
+/* the monotonic clock is not usable on all systems (e.g. embedded ones often
+ * seem to lack it). fall back to REALTIME if we have to. */
+static clockid_t find_monotonic_clock(void)
+{
+#ifdef CLOCK_MONOTONIC
+ struct timespec ts;
+ int r;
+
+ /* Linux 2.6.28 adds CLOCK_MONOTONIC_RAW but we don't use it
+ * because it's not available through timerfd */
+ r = clock_gettime(CLOCK_MONOTONIC, &ts);
+ if (r == 0)
+ return CLOCK_MONOTONIC;
+ usbi_dbg("monotonic clock doesn't work, errno %d", errno);
+#endif
+
+ return CLOCK_REALTIME;
+}
+
+static int kernel_version_ge(int major, int minor, int sublevel)
+{
+ struct utsname uts;
+ int atoms, kmajor, kminor, ksublevel;
+
+ if (uname(&uts) < 0)
+ return -1;
+ atoms = sscanf(uts.release, "%d.%d.%d", &kmajor, &kminor, &ksublevel);
+ if (atoms < 1)
+ return -1;
+
+ if (kmajor > major)
+ return 1;
+ if (kmajor < major)
+ return 0;
+
+ /* kmajor == major */
+ if (atoms < 2)
+ return 0 == minor && 0 == sublevel;
+ if (kminor > minor)
+ return 1;
+ if (kminor < minor)
+ return 0;
+
+ /* kminor == minor */
+ if (atoms < 3)
+ return 0 == sublevel;
+
+ return ksublevel >= sublevel;
+}
+
+static int op_init(struct libusb_context *ctx)
+{
+ struct stat statbuf;
+ int r;
+
+ usbfs_path = find_usbfs_path();
+ if (!usbfs_path) {
+ usbi_err(ctx, "could not find usbfs");
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ if (monotonic_clkid == -1)
+ monotonic_clkid = find_monotonic_clock();
+
+ 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;
+ }
+ }
+
+ if (supports_flag_bulk_continuation)
+ usbi_dbg("bulk continuation flag supported");
+
+ 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;
+ }
+ }
+
+ if (supports_flag_zero_packet)
+ usbi_dbg("zero length packet flag supported");
+
+ 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;
+ }
+ }
+
+ 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;
+ }
+ }
+
+ if (sysfs_can_relate_devices || sysfs_has_descriptors) {
+ r = stat(SYSFS_DEVICE_PATH, &statbuf);
+ if (r != 0 || !S_ISDIR(statbuf.st_mode)) {
+ usbi_warn(ctx, "sysfs not mounted");
+ sysfs_can_relate_devices = 0;
+ sysfs_has_descriptors = 0;
+ }
+ }
+
+ if (sysfs_can_relate_devices)
+ usbi_dbg("sysfs can relate devices");
+
+ if (sysfs_has_descriptors)
+ usbi_dbg("sysfs has complete descriptors");
+
+ usbi_mutex_static_lock(&linux_hotplug_startstop_lock);
+ r = LIBUSB_SUCCESS;
+ if (init_count == 0) {
+ /* start up hotplug event handler */
+ r = linux_start_event_monitor();
+ }
+ if (r == LIBUSB_SUCCESS) {
+ r = linux_scan_devices(ctx);
+ if (r == LIBUSB_SUCCESS)
+ init_count++;
+ else if (init_count == 0)
+ linux_stop_event_monitor();
+ } else
+ usbi_err(ctx, "error starting hotplug event monitor");
+ usbi_mutex_static_unlock(&linux_hotplug_startstop_lock);
+
+ return r;
+}
+
+static void op_exit(void)
+{
+ usbi_mutex_static_lock(&linux_hotplug_startstop_lock);
+ assert(init_count != 0);
+ if (!--init_count) {
+ /* tear down event handler */
+ (void)linux_stop_event_monitor();
+ }
+ usbi_mutex_static_unlock(&linux_hotplug_startstop_lock);
+}
+
+static int linux_start_event_monitor(void)
+{
+#if defined(USE_UDEV)
+ return linux_udev_start_event_monitor();
+#else
+ return linux_netlink_start_event_monitor();
+#endif
+}
+
+static int linux_stop_event_monitor(void)
+{
+#if defined(USE_UDEV)
+ return linux_udev_stop_event_monitor();
+#else
+ return linux_netlink_stop_event_monitor();
+#endif
+}
+
+static int linux_scan_devices(struct libusb_context *ctx)
+{
+ int ret;
+
+ usbi_mutex_static_lock(&linux_hotplug_lock);
+
+#if defined(USE_UDEV)
+ ret = linux_udev_scan_devices(ctx);
+#else
+ ret = linux_default_scan_devices(ctx);
+#endif
+
+ usbi_mutex_static_unlock(&linux_hotplug_lock);
+
+ return ret;
+}
+
+static void op_hotplug_poll(void)
+{
+#if defined(USE_UDEV)
+ linux_udev_hotplug_poll();
+#else
+ linux_netlink_hotplug_poll();
+#endif
+}
+
+static int _open_sysfs_attr(struct libusb_device *dev, const char *attr)
+{
+ struct linux_device_priv *priv = _device_priv(dev);
+ char filename[PATH_MAX];
+ int fd;
+
+ snprintf(filename, PATH_MAX, "%s/%s/%s",
+ SYSFS_DEVICE_PATH, priv->sysfs_dir, attr);
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ usbi_err(DEVICE_CTX(dev),
+ "open %s failed ret=%d errno=%d", filename, fd, errno);
+ return LIBUSB_ERROR_IO;
+ }
+
+ return fd;
+}
+
+/* Note only suitable for attributes which always read >= 0, < 0 is error */
+static int __read_sysfs_attr(struct libusb_context *ctx,
+ const char *devname, const char *attr)
+{
+ char filename[PATH_MAX];
+ FILE *f;
+ int r, value;
+
+ snprintf(filename, PATH_MAX, "%s/%s/%s", SYSFS_DEVICE_PATH,
+ devname, attr);
+ f = fopen(filename, "r");
+ if (f == NULL) {
+ if (errno == ENOENT) {
+ /* File doesn't exist. Assume the device has been
+ disconnected (see trac ticket #70). */
+ return LIBUSB_ERROR_NO_DEVICE;
+ }
+ usbi_err(ctx, "open %s failed errno=%d", filename, errno);
+ return LIBUSB_ERROR_IO;
+ }
+
+ r = fscanf(f, "%d", &value);
+ fclose(f);
+ if (r != 1) {
+ usbi_err(ctx, "fscanf %s returned %d, errno=%d", attr, r, errno);
+ return LIBUSB_ERROR_NO_DEVICE; /* For unplug race (trac #70) */
+ }
+ if (value < 0) {
+ usbi_err(ctx, "%s contains a negative value", filename);
+ return LIBUSB_ERROR_IO;
+ }
+
+ return value;
+}
+
+static int op_get_device_descriptor(struct libusb_device *dev,
+ unsigned char *buffer, int *host_endian)
+{
+ struct linux_device_priv *priv = _device_priv(dev);
+
+ *host_endian = sysfs_has_descriptors ? 0 : 1;
+ memcpy(buffer, priv->descriptors, DEVICE_DESC_LENGTH);
+
+ return 0;
+}
+
+/* read the bConfigurationValue for a device */
+static int sysfs_get_active_config(struct libusb_device *dev, int *config)
+{
+ char *endptr;
+ char tmp[5] = {0, 0, 0, 0, 0};
+ long num;
+ int fd;
+ ssize_t r;
+
+ fd = _open_sysfs_attr(dev, "bConfigurationValue");
+ if (fd < 0)
+ return fd;
+
+ r = read(fd, tmp, sizeof(tmp));
+ close(fd);
+ if (r < 0) {
+ usbi_err(DEVICE_CTX(dev),
+ "read bConfigurationValue failed ret=%d errno=%d", r, errno);
+ return LIBUSB_ERROR_IO;
+ } else if (r == 0) {
+ usbi_dbg("device unconfigured");
+ *config = -1;
+ return 0;
+ }
+
+ if (tmp[sizeof(tmp) - 1] != 0) {
+ usbi_err(DEVICE_CTX(dev), "not null-terminated?");
+ return LIBUSB_ERROR_IO;
+ } else if (tmp[0] == 0) {
+ usbi_err(DEVICE_CTX(dev), "no configuration value?");
+ return LIBUSB_ERROR_IO;
+ }
+
+ num = strtol(tmp, &endptr, 10);
+ if (endptr == tmp) {
+ usbi_err(DEVICE_CTX(dev), "error converting '%s' to integer", tmp);
+ return LIBUSB_ERROR_IO;
+ }
+
+ *config = (int) num;
+ return 0;
+}
+
+int linux_get_device_address (struct libusb_context *ctx, int detached,
+ uint8_t *busnum, uint8_t *devaddr,const char *dev_node,
+ const char *sys_name)
+{
+ int sysfs_attr;
+
+ usbi_dbg("getting address for device: %s detached: %d", sys_name, detached);
+ /* can't use sysfs to read the bus and device number if the
+ * device has been detached */
+ if (!sysfs_can_relate_devices || detached || NULL == sys_name) {
+ if (NULL == dev_node) {
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ /* will this work with all supported kernel versions? */
+ if (!strncmp(dev_node, "/dev/bus/usb", 12)) {
+ sscanf (dev_node, "/dev/bus/usb/%hhu/%hhu", busnum, devaddr);
+ } else if (!strncmp(dev_node, "/proc/bus/usb", 13)) {
+ sscanf (dev_node, "/proc/bus/usb/%hhu/%hhu", busnum, devaddr);
+ }
+
+ return LIBUSB_SUCCESS;
+ }
+
+ usbi_dbg("scan %s", sys_name);
+
+ sysfs_attr = __read_sysfs_attr(ctx, sys_name, "busnum");
+ if (0 > sysfs_attr)
+ return sysfs_attr;
+ if (sysfs_attr > 255)
+ return LIBUSB_ERROR_INVALID_PARAM;
+ *busnum = (uint8_t) sysfs_attr;
+
+ sysfs_attr = __read_sysfs_attr(ctx, sys_name, "devnum");
+ if (0 > sysfs_attr)
+ return sysfs_attr;
+ if (sysfs_attr > 255)
+ return LIBUSB_ERROR_INVALID_PARAM;
+
+ *devaddr = (uint8_t) sysfs_attr;
+
+ usbi_dbg("bus=%d dev=%d", *busnum, *devaddr);
+
+ return LIBUSB_SUCCESS;
+}
+
+/* Return offset of the next descriptor with the given type */
+static int seek_to_next_descriptor(struct libusb_context *ctx,
+ uint8_t descriptor_type, unsigned char *buffer, int size)
+{
+ struct usb_descriptor_header header;
+ int i;
+
+ for (i = 0; size >= 0; i += header.bLength, size -= header.bLength) {
+ if (size == 0)
+ return LIBUSB_ERROR_NOT_FOUND;
+
+ if (size < 2) {
+ usbi_err(ctx, "short descriptor read %d/2", size);
+ return LIBUSB_ERROR_IO;
+ }
+ usbi_parse_descriptor(buffer + i, "bb", &header, 0);
+
+ if (i && header.bDescriptorType == descriptor_type)
+ return i;
+ }
+ usbi_err(ctx, "bLength overflow by %d bytes", -size);
+ return LIBUSB_ERROR_IO;
+}
+
+/* Return offset to next config */
+static int seek_to_next_config(struct libusb_context *ctx,
+ unsigned char *buffer, int size)
+{
+ struct libusb_config_descriptor config;
+
+ if (size == 0)
+ return LIBUSB_ERROR_NOT_FOUND;
+
+ if (size < LIBUSB_DT_CONFIG_SIZE) {
+ usbi_err(ctx, "short descriptor read %d/%d",
+ size, LIBUSB_DT_CONFIG_SIZE);
+ return LIBUSB_ERROR_IO;
+ }
+
+ usbi_parse_descriptor(buffer, "bbwbbbbb", &config, 0);
+ if (config.bDescriptorType != LIBUSB_DT_CONFIG) {
+ usbi_err(ctx, "descriptor is not a config desc (type 0x%02x)",
+ config.bDescriptorType);
+ return LIBUSB_ERROR_IO;
+ }
+
+ /*
+ * In usbfs the config descriptors are config.wTotalLength bytes apart,
+ * with any short reads from the device appearing as holes in the file.
+ *
+ * In sysfs wTotalLength is ignored, instead the kernel returns a
+ * config descriptor with verified bLength fields, with descriptors
+ * with an invalid bLength removed.
+ */
+ if (sysfs_has_descriptors) {
+ int next = seek_to_next_descriptor(ctx, LIBUSB_DT_CONFIG,
+ buffer, size);
+ if (next == LIBUSB_ERROR_NOT_FOUND)
+ next = size;
+ if (next < 0)
+ return next;
+
+ if (next != config.wTotalLength)
+ usbi_warn(ctx, "config length mismatch wTotalLength "
+ "%d real %d", config.wTotalLength, next);
+ return next;
+ } else {
+ if (config.wTotalLength < LIBUSB_DT_CONFIG_SIZE) {
+ usbi_err(ctx, "invalid wTotalLength %d",
+ config.wTotalLength);
+ return LIBUSB_ERROR_IO;
+ } else if (config.wTotalLength > size) {
+ usbi_warn(ctx, "short descriptor read %d/%d",
+ size, config.wTotalLength);
+ return size;
+ } else
+ return config.wTotalLength;
+ }
+}
+
+static int op_get_config_descriptor_by_value(struct libusb_device *dev,
+ uint8_t value, unsigned char **buffer, int *host_endian)
+{
+ struct libusb_context *ctx = DEVICE_CTX(dev);
+ struct linux_device_priv *priv = _device_priv(dev);
+ unsigned char *descriptors = priv->descriptors;
+ int size = priv->descriptors_len;
+ struct libusb_config_descriptor *config;
+
+ *buffer = NULL;
+ /* Unlike the device desc. config descs. are always in raw format */
+ *host_endian = 0;
+
+ /* Skip device header */
+ descriptors += DEVICE_DESC_LENGTH;
+ size -= DEVICE_DESC_LENGTH;
+
+ /* Seek till the config is found, or till "EOF" */
+ while (1) {
+ int next = seek_to_next_config(ctx, descriptors, size);
+ if (next < 0)
+ return next;
+ config = (struct libusb_config_descriptor *)descriptors;
+ if (config->bConfigurationValue == value) {
+ *buffer = descriptors;
+ return next;
+ }
+ size -= next;
+ descriptors += next;
+ }
+}
+
+static int op_get_active_config_descriptor(struct libusb_device *dev,
+ unsigned char *buffer, size_t len, int *host_endian)
+{
+ int r, config;
+ unsigned char *config_desc;
+
+ if (sysfs_can_relate_devices) {
+ r = sysfs_get_active_config(dev, &config);
+ if (r < 0)
+ return r;
+ } else {
+ /* Use cached bConfigurationValue */
+ struct linux_device_priv *priv = _device_priv(dev);
+ config = priv->active_config;
+ }
+ if (config == -1)
+ return LIBUSB_ERROR_NOT_FOUND;
+
+ r = op_get_config_descriptor_by_value(dev, config, &config_desc,
+ host_endian);
+ if (r < 0)
+ return r;
+
+ len = MIN(len, r);
+ memcpy(buffer, config_desc, len);
+ return len;
+}
+
+static int op_get_config_descriptor(struct libusb_device *dev,
+ uint8_t config_index, unsigned char *buffer, size_t len, int *host_endian)
+{
+ struct linux_device_priv *priv = _device_priv(dev);
+ unsigned char *descriptors = priv->descriptors;
+ int i, r, size = priv->descriptors_len;
+
+ /* Unlike the device desc. config descs. are always in raw format */
+ *host_endian = 0;
+
+ /* Skip device header */
+ descriptors += DEVICE_DESC_LENGTH;
+ size -= DEVICE_DESC_LENGTH;
+
+ /* Seek till the config is found, or till "EOF" */
+ for (i = 0; ; i++) {
+ r = seek_to_next_config(DEVICE_CTX(dev), descriptors, size);
+ if (r < 0)
+ return r;
+ if (i == config_index)
+ break;
+ size -= r;
+ descriptors += r;
+ }
+
+ len = MIN(len, r);
+ memcpy(buffer, descriptors, len);
+ return len;
+}
+
+/* send a control message to retrieve active configuration */
+static int usbfs_get_active_config(struct libusb_device *dev, int fd)
+{
+ struct linux_device_priv *priv = _device_priv(dev);
+ unsigned char active_config = 0;
+ int r;
+
+ struct usbfs_ctrltransfer ctrl = {
+ .bmRequestType = LIBUSB_ENDPOINT_IN,
+ .bRequest = LIBUSB_REQUEST_GET_CONFIGURATION,
+ .wValue = 0,
+ .wIndex = 0,
+ .wLength = 1,
+ .timeout = 1000,
+ .data = &active_config
+ };
+
+ r = ioctl(fd, IOCTL_USBFS_CONTROL, &ctrl);
+ if (r < 0) {
+ if (errno == ENODEV)
+ return LIBUSB_ERROR_NO_DEVICE;
+
+ /* we hit this error path frequently with buggy devices :( */
+ usbi_warn(DEVICE_CTX(dev),
+ "get_configuration failed ret=%d errno=%d", r, errno);
+ priv->active_config = -1;
+ } else {
+ if (active_config > 0) {
+ priv->active_config = active_config;
+ } else {
+ /* some buggy devices have a configuration 0, but we're
+ * reaching into the corner of a corner case here, so let's
+ * not support buggy devices in these circumstances.
+ * stick to the specs: a configuration value of 0 means
+ * unconfigured. */
+ usbi_warn(DEVICE_CTX(dev),
+ "active cfg 0? assuming unconfigured device");
+ priv->active_config = -1;
+ }
+ }
+
+ return LIBUSB_SUCCESS;
+}
+
+static int initialize_device(struct libusb_device *dev, uint8_t busnum,
+ uint8_t devaddr, const char *sysfs_dir)
+{
+ struct linux_device_priv *priv = _device_priv(dev);
+ struct libusb_context *ctx = DEVICE_CTX(dev);
+ int descriptors_size = 512; /* Begin with a 1024 byte alloc */
+ int fd, speed;
+ ssize_t r;
+
+ dev->bus_number = busnum;
+ dev->device_address = devaddr;
+
+ if (sysfs_dir) {
+ priv->sysfs_dir = strdup(sysfs_dir);
+ if (!priv->sysfs_dir)
+ return LIBUSB_ERROR_NO_MEM;
+
+ /* Note speed can contain 1.5, in this case __read_sysfs_attr
+ will stop parsing at the '.' and return 1 */
+ speed = __read_sysfs_attr(DEVICE_CTX(dev), sysfs_dir, "speed");
+ if (speed >= 0) {
+ switch (speed) {
+ case 1: dev->speed = LIBUSB_SPEED_LOW; break;
+ case 12: dev->speed = LIBUSB_SPEED_FULL; break;
+ case 480: dev->speed = LIBUSB_SPEED_HIGH; break;
+ case 5000: dev->speed = LIBUSB_SPEED_SUPER; break;
+ default:
+ usbi_warn(DEVICE_CTX(dev), "Unknown device speed: %d Mbps", speed);
+ }
+ }
+ }
+
+ /* cache descriptors in memory */
+ if (sysfs_has_descriptors)
+ fd = _open_sysfs_attr(dev, "descriptors");
+ else
+ fd = _get_usbfs_fd(dev, O_RDONLY, 0);
+ if (fd < 0)
+ return fd;
+
+ do {
+ descriptors_size *= 2;
+ priv->descriptors = usbi_reallocf(priv->descriptors,
+ descriptors_size);
+ if (!priv->descriptors) {
+ close(fd);
+ return LIBUSB_ERROR_NO_MEM;
+ }
+ /* usbfs has holes in the file */
+ if (!sysfs_has_descriptors) {
+ memset(priv->descriptors + priv->descriptors_len,
+ 0, descriptors_size - priv->descriptors_len);
+ }
+ r = read(fd, priv->descriptors + priv->descriptors_len,
+ descriptors_size - priv->descriptors_len);
+ if (r < 0) {
+ usbi_err(ctx, "read descriptor failed ret=%d errno=%d",
+ fd, errno);
+ close(fd);
+ return LIBUSB_ERROR_IO;
+ }
+ priv->descriptors_len += r;
+ } while (priv->descriptors_len == descriptors_size);
+
+ close(fd);
+
+ if (priv->descriptors_len < DEVICE_DESC_LENGTH) {
+ usbi_err(ctx, "short descriptor read (%d)",
+ priv->descriptors_len);
+ return LIBUSB_ERROR_IO;
+ }
+
+ if (sysfs_can_relate_devices)
+ return LIBUSB_SUCCESS;
+
+ /* cache active config */
+ fd = _get_usbfs_fd(dev, O_RDWR, 1);
+ if (fd < 0) {
+ /* cannot send a control message to determine the active
+ * config. just assume the first one is active. */
+ usbi_warn(ctx, "Missing rw usbfs access; cannot determine "
+ "active configuration descriptor");
+ if (priv->descriptors_len >=
+ (DEVICE_DESC_LENGTH + LIBUSB_DT_CONFIG_SIZE)) {
+ struct libusb_config_descriptor config;
+ usbi_parse_descriptor(
+ priv->descriptors + DEVICE_DESC_LENGTH,
+ "bbwbbbbb", &config, 0);
+ priv->active_config = config.bConfigurationValue;
+ } else
+ priv->active_config = -1; /* No config dt */
+
+ return LIBUSB_SUCCESS;
+ }
+
+ r = usbfs_get_active_config(dev, fd);
+ close(fd);
+
+ return r;
+}
+
+static int linux_get_parent_info(struct libusb_device *dev, const char *sysfs_dir)
+{
+ struct libusb_context *ctx = DEVICE_CTX(dev);
+ struct libusb_device *it;
+ char *parent_sysfs_dir, *tmp;
+ int ret, add_parent = 1;
+
+ /* XXX -- can we figure out the topology when using usbfs? */
+ if (NULL == sysfs_dir || 0 == strncmp(sysfs_dir, "usb", 3)) {
+ /* either using usbfs or finding the parent of a root hub */
+ return LIBUSB_SUCCESS;
+ }
+
+ parent_sysfs_dir = strdup(sysfs_dir);
+ if (NULL == parent_sysfs_dir) {
+ return LIBUSB_ERROR_NO_MEM;
+ }
+ if (NULL != (tmp = strrchr(parent_sysfs_dir, '.')) ||
+ NULL != (tmp = strrchr(parent_sysfs_dir, '-'))) {
+ dev->port_number = atoi(tmp + 1);
+ *tmp = '\0';
+ } else {
+ usbi_warn(ctx, "Can not parse sysfs_dir: %s, no parent info",
+ parent_sysfs_dir);
+ free (parent_sysfs_dir);
+ return LIBUSB_SUCCESS;
+ }
+
+ /* is the parent a root hub? */
+ if (NULL == strchr(parent_sysfs_dir, '-')) {
+ tmp = parent_sysfs_dir;
+ ret = asprintf (&parent_sysfs_dir, "usb%s", tmp);
+ free (tmp);
+ if (0 > ret) {
+ return LIBUSB_ERROR_NO_MEM;
+ }
+ }
+
+retry:
+ /* find the parent in the context */
+ usbi_mutex_lock(&ctx->usb_devs_lock);
+ list_for_each_entry(it, &ctx->usb_devs, list, struct libusb_device) {
+ struct linux_device_priv *priv = _device_priv(it);
+ if (priv->sysfs_dir) {
+ if (0 == strcmp (priv->sysfs_dir, parent_sysfs_dir)) {
+ dev->parent_dev = libusb_ref_device(it);
+ break;
+ }
+ }
+ }
+ usbi_mutex_unlock(&ctx->usb_devs_lock);
+
+ if (!dev->parent_dev && add_parent) {
+ usbi_dbg("parent_dev %s not enumerated yet, enumerating now",
+ parent_sysfs_dir);
+ sysfs_scan_device(ctx, parent_sysfs_dir);
+ add_parent = 0;
+ goto retry;
+ }
+
+ usbi_dbg("Dev %p (%s) has parent %p (%s) port %d", dev, sysfs_dir,
+ dev->parent_dev, parent_sysfs_dir, dev->port_number);
+
+ free (parent_sysfs_dir);
+
+ return LIBUSB_SUCCESS;
+}
+
+int linux_enumerate_device(struct libusb_context *ctx,
+ uint8_t busnum, uint8_t devaddr, const char *sysfs_dir)
+{
+ unsigned long session_id;
+ struct libusb_device *dev;
+ int r = 0;
+
+ /* FIXME: session ID is not guaranteed unique as addresses can wrap and
+ * will be reused. instead we should add a simple sysfs attribute with
+ * a session ID. */
+ session_id = busnum << 8 | devaddr;
+ usbi_dbg("busnum %d devaddr %d session_id %ld", busnum, devaddr,
+ session_id);
+
+ dev = usbi_get_device_by_session_id(ctx, session_id);
+ if (dev) {
+ /* device already exists in the context */
+ usbi_dbg("session_id %ld already exists", session_id);
+ libusb_unref_device(dev);
+ return LIBUSB_SUCCESS;
+ }
+
+ usbi_dbg("allocating new device for %d/%d (session %ld)",
+ busnum, devaddr, session_id);
+ dev = usbi_alloc_device(ctx, session_id);
+ if (!dev)
+ return LIBUSB_ERROR_NO_MEM;
+
+ r = initialize_device(dev, busnum, devaddr, sysfs_dir);
+ if (r < 0)
+ goto out;
+ r = usbi_sanitize_device(dev);
+ if (r < 0)
+ goto out;
+
+ r = linux_get_parent_info(dev, sysfs_dir);
+ if (r < 0)
+ goto out;
+out:
+ if (r < 0)
+ libusb_unref_device(dev);
+ else
+ usbi_connect_device(dev);
+
+ return r;
+}
+
+void linux_hotplug_enumerate(uint8_t busnum, uint8_t devaddr, const char *sys_name)
+{
+ struct libusb_context *ctx;
+
+ usbi_mutex_static_lock(&active_contexts_lock);
+ list_for_each_entry(ctx, &active_contexts_list, list, struct libusb_context) {
+ linux_enumerate_device(ctx, busnum, devaddr, sys_name);
+ }
+ usbi_mutex_static_unlock(&active_contexts_lock);
+}
+
+void linux_device_disconnected(uint8_t busnum, uint8_t devaddr)
+{
+ struct libusb_context *ctx;
+ struct libusb_device *dev;
+ unsigned long session_id = busnum << 8 | devaddr;
+
+ usbi_mutex_static_lock(&active_contexts_lock);
+ list_for_each_entry(ctx, &active_contexts_list, list, struct libusb_context) {
+ dev = usbi_get_device_by_session_id (ctx, session_id);
+ if (NULL != dev) {
+ usbi_disconnect_device (dev);
+ libusb_unref_device(dev);
+ } else {
+ usbi_dbg("device not found for session %x", session_id);
+ }
+ }
+ usbi_mutex_static_unlock(&active_contexts_lock);
+}
+
+#if !defined(USE_UDEV)
+/* open a bus directory and adds all discovered devices to the context */
+static int usbfs_scan_busdir(struct libusb_context *ctx, uint8_t busnum)
+{
+ DIR *dir;
+ char dirpath[PATH_MAX];
+ struct dirent *entry;
+ int r = LIBUSB_ERROR_IO;
+
+ snprintf(dirpath, PATH_MAX, "%s/%03d", usbfs_path, busnum);
+ usbi_dbg("%s", dirpath);
+ dir = opendir(dirpath);
+ if (!dir) {
+ usbi_err(ctx, "opendir '%s' failed, errno=%d", dirpath, errno);
+ /* FIXME: should handle valid race conditions like hub unplugged
+ * during directory iteration - this is not an error */
+ return r;
+ }
+
+ while ((entry = readdir(dir))) {
+ int devaddr;
+
+ if (entry->d_name[0] == '.')
+ continue;
+
+ devaddr = atoi(entry->d_name);
+ if (devaddr == 0) {
+ usbi_dbg("unknown dir entry %s", entry->d_name);
+ continue;
+ }
+
+ if (linux_enumerate_device(ctx, busnum, (uint8_t) devaddr, NULL)) {
+ usbi_dbg("failed to enumerate dir entry %s", entry->d_name);
+ continue;
+ }
+
+ r = 0;
+ }
+
+ closedir(dir);
+ return r;
+}
+
+static int usbfs_get_device_list(struct libusb_context *ctx)
+{
+ struct dirent *entry;
+ DIR *buses = opendir(usbfs_path);
+ int r = 0;
+
+ if (!buses) {
+ usbi_err(ctx, "opendir buses failed errno=%d", errno);
+ return LIBUSB_ERROR_IO;
+ }
+
+ while ((entry = readdir(buses))) {
+ int busnum;
+
+ if (entry->d_name[0] == '.')
+ continue;
+
+ if (usbdev_names) {
+ int devaddr;
+ if (!_is_usbdev_entry(entry, &busnum, &devaddr))
+ continue;
+
+ r = linux_enumerate_device(ctx, busnum, (uint8_t) devaddr, NULL);
+ if (r < 0) {
+ usbi_dbg("failed to enumerate dir entry %s", entry->d_name);
+ continue;
+ }
+ } else {
+ busnum = atoi(entry->d_name);
+ if (busnum == 0) {
+ usbi_dbg("unknown dir entry %s", entry->d_name);
+ continue;
+ }
+
+ r = usbfs_scan_busdir(ctx, busnum);
+ if (r < 0)
+ break;
+ }
+ }
+
+ closedir(buses);
+ return r;
+
+}
+#endif
+
+static int sysfs_scan_device(struct libusb_context *ctx, const char *devname)
+{
+ uint8_t busnum, devaddr;
+ int ret;
+
+ ret = linux_get_device_address (ctx, 0, &busnum, &devaddr, NULL, devname);
+ if (LIBUSB_SUCCESS != ret) {
+ return ret;
+ }
+
+ return linux_enumerate_device(ctx, busnum & 0xff, devaddr & 0xff,
+ devname);
+}
+
+#if !defined(USE_UDEV)
+static int sysfs_get_device_list(struct libusb_context *ctx)
+{
+ DIR *devices = opendir(SYSFS_DEVICE_PATH);
+ struct dirent *entry;
+ int r = LIBUSB_ERROR_IO;
+
+ if (!devices) {
+ usbi_err(ctx, "opendir devices failed errno=%d", errno);
+ return r;
+ }
+
+ while ((entry = readdir(devices))) {
+ if ((!isdigit(entry->d_name[0]) && strncmp(entry->d_name, "usb", 3))
+ || strchr(entry->d_name, ':'))
+ continue;
+
+ if (sysfs_scan_device(ctx, entry->d_name)) {
+ usbi_dbg("failed to enumerate dir entry %s", entry->d_name);
+ continue;
+ }
+
+ r = 0;
+ }
+
+ closedir(devices);
+ return r;
+}
+
+static int linux_default_scan_devices (struct libusb_context *ctx)
+{
+ /* we can retrieve device list and descriptors from sysfs or usbfs.
+ * sysfs is preferable, because if we use usbfs we end up resuming
+ * any autosuspended USB devices. however, sysfs is not available
+ * everywhere, so we need a usbfs fallback too.
+ *
+ * as described in the "sysfs vs usbfs" comment at the top of this
+ * file, sometimes we have sysfs but not enough information to
+ * relate sysfs devices to usbfs nodes. op_init() determines the
+ * adequacy of sysfs and sets sysfs_can_relate_devices.
+ */
+ if (sysfs_can_relate_devices != 0)
+ return sysfs_get_device_list(ctx);
+ else
+ return usbfs_get_device_list(ctx);
+}
+#endif
+
+static int op_open(struct libusb_device_handle *handle)
+{
+ struct linux_device_handle_priv *hpriv = _device_handle_priv(handle);
+ int r;
+
+ hpriv->fd = _get_usbfs_fd(handle->dev, O_RDWR, 0);
+ if (hpriv->fd < 0) {
+ if (hpriv->fd == LIBUSB_ERROR_NO_DEVICE) {
+ /* device will still be marked as attached if hotplug monitor thread
+ * hasn't processed remove event yet */
+ usbi_mutex_static_lock(&linux_hotplug_lock);
+ if (handle->dev->attached) {
+ usbi_dbg("open failed with no device, but device still attached");
+ linux_device_disconnected(handle->dev->bus_number,
+ handle->dev->device_address);
+ }
+ usbi_mutex_static_unlock(&linux_hotplug_lock);
+ }
+ return hpriv->fd;
+ }
+
+ r = ioctl(hpriv->fd, IOCTL_USBFS_GET_CAPABILITIES, &hpriv->caps);
+ if (r < 0) {
+ if (errno == ENOTTY)
+ usbi_dbg("getcap not available");
+ else
+ usbi_err(HANDLE_CTX(handle), "getcap failed (%d)", errno);
+ hpriv->caps = 0;
+ if (supports_flag_zero_packet)
+ hpriv->caps |= USBFS_CAP_ZERO_PACKET;
+ if (supports_flag_bulk_continuation)
+ hpriv->caps |= USBFS_CAP_BULK_CONTINUATION;
+ }
+
+ r = usbi_add_pollfd(HANDLE_CTX(handle), hpriv->fd, POLLOUT);
+ if (r < 0)
+ close(hpriv->fd);
+
+ return r;
+}
+
+static void op_close(struct libusb_device_handle *dev_handle)
+{
+ struct linux_device_handle_priv *hpriv = _device_handle_priv(dev_handle);
+ /* fd may have already been removed by POLLERR condition in op_handle_events() */
+ if (!hpriv->fd_removed)
+ usbi_remove_pollfd(HANDLE_CTX(dev_handle), hpriv->fd);
+ close(hpriv->fd);
+}
+
+static int op_get_configuration(struct libusb_device_handle *handle,
+ int *config)
+{
+ int r;
+
+ if (sysfs_can_relate_devices) {
+ r = sysfs_get_active_config(handle->dev, config);
+ } else {
+ r = usbfs_get_active_config(handle->dev,
+ _device_handle_priv(handle)->fd);
+ if (r == LIBUSB_SUCCESS)
+ *config = _device_priv(handle->dev)->active_config;
+ }
+ if (r < 0)
+ return r;
+
+ if (*config == -1) {
+ usbi_err(HANDLE_CTX(handle), "device unconfigured");
+ *config = 0;
+ }
+
+ return 0;
+}
+
+static int op_set_configuration(struct libusb_device_handle *handle, int config)
+{
+ struct linux_device_priv *priv = _device_priv(handle->dev);
+ int fd = _device_handle_priv(handle)->fd;
+ int r = ioctl(fd, IOCTL_USBFS_SETCONFIG, &config);
+ if (r) {
+ if (errno == EINVAL)
+ return LIBUSB_ERROR_NOT_FOUND;
+ else if (errno == EBUSY)
+ return LIBUSB_ERROR_BUSY;
+ else if (errno == ENODEV)
+ return LIBUSB_ERROR_NO_DEVICE;
+
+ usbi_err(HANDLE_CTX(handle), "failed, error %d errno %d", r, errno);
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ /* update our cached active config descriptor */
+ priv->active_config = config;
+
+ return LIBUSB_SUCCESS;
+}
+
+static int claim_interface(struct libusb_device_handle *handle, int iface)
+{
+ int fd = _device_handle_priv(handle)->fd;
+ int r = ioctl(fd, IOCTL_USBFS_CLAIMINTF, &iface);
+ if (r) {
+ if (errno == ENOENT)
+ return LIBUSB_ERROR_NOT_FOUND;
+ else if (errno == EBUSY)
+ return LIBUSB_ERROR_BUSY;
+ else if (errno == ENODEV)
+ return LIBUSB_ERROR_NO_DEVICE;
+
+ usbi_err(HANDLE_CTX(handle),
+ "claim interface failed, error %d errno %d", r, errno);
+ return LIBUSB_ERROR_OTHER;
+ }
+ return 0;
+}
+
+static int release_interface(struct libusb_device_handle *handle, int iface)
+{
+ int fd = _device_handle_priv(handle)->fd;
+ int r = ioctl(fd, IOCTL_USBFS_RELEASEINTF, &iface);
+ if (r) {
+ if (errno == ENODEV)
+ return LIBUSB_ERROR_NO_DEVICE;
+
+ usbi_err(HANDLE_CTX(handle),
+ "release interface failed, error %d errno %d", r, errno);
+ return LIBUSB_ERROR_OTHER;
+ }
+ return 0;
+}
+
+static int op_set_interface(struct libusb_device_handle *handle, int iface,
+ int altsetting)
+{
+ int fd = _device_handle_priv(handle)->fd;
+ struct usbfs_setinterface setintf;
+ int r;
+
+ setintf.interface = iface;
+ setintf.altsetting = altsetting;
+ r = ioctl(fd, IOCTL_USBFS_SETINTF, &setintf);
+ if (r) {
+ if (errno == EINVAL)
+ return LIBUSB_ERROR_NOT_FOUND;
+ else if (errno == ENODEV)
+ return LIBUSB_ERROR_NO_DEVICE;
+
+ usbi_err(HANDLE_CTX(handle),
+ "setintf failed error %d errno %d", r, errno);
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ return 0;
+}
+
+static int op_clear_halt(struct libusb_device_handle *handle,
+ unsigned char endpoint)
+{
+ int fd = _device_handle_priv(handle)->fd;
+ unsigned int _endpoint = endpoint;
+ int r = ioctl(fd, IOCTL_USBFS_CLEAR_HALT, &_endpoint);
+ if (r) {
+ if (errno == ENOENT)
+ return LIBUSB_ERROR_NOT_FOUND;
+ else if (errno == ENODEV)
+ return LIBUSB_ERROR_NO_DEVICE;
+
+ usbi_err(HANDLE_CTX(handle),
+ "clear_halt failed error %d errno %d", r, errno);
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ return 0;
+}
+
+static int op_reset_device(struct libusb_device_handle *handle)
+{
+ int fd = _device_handle_priv(handle)->fd;
+ int i, r, ret = 0;
+
+ /* Doing a device reset will cause the usbfs driver to get unbound
+ from any interfaces it is bound to. By voluntarily unbinding
+ the usbfs driver ourself, we stop the kernel from rebinding
+ the interface after reset (which would end up with the interface
+ getting bound to the in kernel driver if any). */
+ for (i = 0; i < USB_MAXINTERFACES; i++) {
+ if (handle->claimed_interfaces & (1L << i)) {
+ release_interface(handle, i);
+ }
+ }
+
+ usbi_mutex_lock(&handle->lock);
+ r = ioctl(fd, IOCTL_USBFS_RESET, NULL);
+ if (r) {
+ if (errno == ENODEV) {
+ ret = LIBUSB_ERROR_NOT_FOUND;
+ goto out;
+ }
+
+ usbi_err(HANDLE_CTX(handle),
+ "reset failed error %d errno %d", r, errno);
+ ret = LIBUSB_ERROR_OTHER;
+ goto out;
+ }
+
+ /* And re-claim any interfaces which were claimed before the reset */
+ for (i = 0; i < USB_MAXINTERFACES; i++) {
+ if (handle->claimed_interfaces & (1L << i)) {
+ /*
+ * A driver may have completed modprobing during
+ * IOCTL_USBFS_RESET, and bound itself as soon as
+ * IOCTL_USBFS_RESET released the device lock
+ */
+ r = detach_kernel_driver_and_claim(handle, i);
+ if (r) {
+ usbi_warn(HANDLE_CTX(handle),
+ "failed to re-claim interface %d after reset: %s",
+ i, libusb_error_name(r));
+ handle->claimed_interfaces &= ~(1L << i);
+ ret = LIBUSB_ERROR_NOT_FOUND;
+ }
+ }
+ }
+out:
+ usbi_mutex_unlock(&handle->lock);
+ return ret;
+}
+
+static int do_streams_ioctl(struct libusb_device_handle *handle, long req,
+ uint32_t num_streams, unsigned char *endpoints, int num_endpoints)
+{
+ int r, fd = _device_handle_priv(handle)->fd;
+ struct usbfs_streams *streams;
+
+ if (num_endpoints > 30) /* Max 15 in + 15 out eps */
+ return LIBUSB_ERROR_INVALID_PARAM;
+
+ streams = malloc(sizeof(struct usbfs_streams) + num_endpoints);
+ if (!streams)
+ return LIBUSB_ERROR_NO_MEM;
+
+ streams->num_streams = num_streams;
+ streams->num_eps = num_endpoints;
+ memcpy(streams->eps, endpoints, num_endpoints);
+
+ r = ioctl(fd, req, streams);
+
+ free(streams);
+
+ if (r < 0) {
+ if (errno == ENOTTY)
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+ else if (errno == EINVAL)
+ return LIBUSB_ERROR_INVALID_PARAM;
+ else if (errno == ENODEV)
+ return LIBUSB_ERROR_NO_DEVICE;
+
+ usbi_err(HANDLE_CTX(handle),
+ "streams-ioctl failed error %d errno %d", r, errno);
+ return LIBUSB_ERROR_OTHER;
+ }
+ return r;
+}
+
+static int op_alloc_streams(struct libusb_device_handle *handle,
+ uint32_t num_streams, unsigned char *endpoints, int num_endpoints)
+{
+ return do_streams_ioctl(handle, IOCTL_USBFS_ALLOC_STREAMS,
+ num_streams, endpoints, num_endpoints);
+}
+
+static int op_free_streams(struct libusb_device_handle *handle,
+ unsigned char *endpoints, int num_endpoints)
+{
+ return do_streams_ioctl(handle, IOCTL_USBFS_FREE_STREAMS, 0,
+ endpoints, num_endpoints);
+}
+
+static unsigned char *op_dev_mem_alloc(struct libusb_device_handle *handle,
+ size_t len)
+{
+ struct linux_device_handle_priv *hpriv = _device_handle_priv(handle);
+ unsigned char *buffer = (unsigned char *)mmap(NULL, len,
+ PROT_READ | PROT_WRITE, MAP_SHARED, hpriv->fd, 0);
+ if (buffer == MAP_FAILED) {
+ usbi_err(HANDLE_CTX(handle), "alloc dev mem failed errno %d",
+ errno);
+ return NULL;
+ }
+ return buffer;
+}
+
+static int op_dev_mem_free(struct libusb_device_handle *handle,
+ unsigned char *buffer, size_t len)
+{
+ if (munmap(buffer, len) != 0) {
+ usbi_err(HANDLE_CTX(handle), "free dev mem failed errno %d",
+ errno);
+ return LIBUSB_ERROR_OTHER;
+ } else {
+ return LIBUSB_SUCCESS;
+ }
+}
+
+static int op_kernel_driver_active(struct libusb_device_handle *handle,
+ int interface)
+{
+ int fd = _device_handle_priv(handle)->fd;
+ struct usbfs_getdriver getdrv;
+ int r;
+
+ getdrv.interface = interface;
+ r = ioctl(fd, IOCTL_USBFS_GETDRIVER, &getdrv);
+ if (r) {
+ if (errno == ENODATA)
+ return 0;
+ else if (errno == ENODEV)
+ return LIBUSB_ERROR_NO_DEVICE;
+
+ usbi_err(HANDLE_CTX(handle),
+ "get driver failed error %d errno %d", r, errno);
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ return (strcmp(getdrv.driver, "usbfs") == 0) ? 0 : 1;
+}
+
+static int op_detach_kernel_driver(struct libusb_device_handle *handle,
+ int interface)
+{
+ int fd = _device_handle_priv(handle)->fd;
+ struct usbfs_ioctl command;
+ struct usbfs_getdriver getdrv;
+ int r;
+
+ command.ifno = interface;
+ command.ioctl_code = IOCTL_USBFS_DISCONNECT;
+ command.data = NULL;
+
+ getdrv.interface = interface;
+ r = ioctl(fd, IOCTL_USBFS_GETDRIVER, &getdrv);
+ if (r == 0 && strcmp(getdrv.driver, "usbfs") == 0)
+ return LIBUSB_ERROR_NOT_FOUND;
+
+ r = ioctl(fd, IOCTL_USBFS_IOCTL, &command);
+ if (r) {
+ if (errno == ENODATA)
+ return LIBUSB_ERROR_NOT_FOUND;
+ else if (errno == EINVAL)
+ return LIBUSB_ERROR_INVALID_PARAM;
+ else if (errno == ENODEV)
+ return LIBUSB_ERROR_NO_DEVICE;
+
+ usbi_err(HANDLE_CTX(handle),
+ "detach failed error %d errno %d", r, errno);
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ return 0;
+}
+
+static int op_attach_kernel_driver(struct libusb_device_handle *handle,
+ int interface)
+{
+ int fd = _device_handle_priv(handle)->fd;
+ struct usbfs_ioctl command;
+ int r;
+
+ command.ifno = interface;
+ command.ioctl_code = IOCTL_USBFS_CONNECT;
+ command.data = NULL;
+
+ r = ioctl(fd, IOCTL_USBFS_IOCTL, &command);
+ if (r < 0) {
+ if (errno == ENODATA)
+ return LIBUSB_ERROR_NOT_FOUND;
+ else if (errno == EINVAL)
+ return LIBUSB_ERROR_INVALID_PARAM;
+ else if (errno == ENODEV)
+ return LIBUSB_ERROR_NO_DEVICE;
+ else if (errno == EBUSY)
+ return LIBUSB_ERROR_BUSY;
+
+ usbi_err(HANDLE_CTX(handle),
+ "attach failed error %d errno %d", r, errno);
+ return LIBUSB_ERROR_OTHER;
+ } else if (r == 0) {
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
+
+ return 0;
+}
+
+static int detach_kernel_driver_and_claim(struct libusb_device_handle *handle,
+ int interface)
+{
+ struct usbfs_disconnect_claim dc;
+ int r, fd = _device_handle_priv(handle)->fd;
+
+ dc.interface = interface;
+ 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;
+
+ switch (errno) {
+ case EBUSY:
+ return LIBUSB_ERROR_BUSY;
+ case EINVAL:
+ return LIBUSB_ERROR_INVALID_PARAM;
+ case ENODEV:
+ return LIBUSB_ERROR_NO_DEVICE;
+ }
+ usbi_err(HANDLE_CTX(handle),
+ "disconnect-and-claim failed errno %d", errno);
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ /* Fallback code for kernels which don't support the
+ disconnect-and-claim ioctl */
+ r = op_detach_kernel_driver(handle, interface);
+ if (r != 0 && r != LIBUSB_ERROR_NOT_FOUND)
+ return r;
+
+ return claim_interface(handle, interface);
+}
+
+static int op_claim_interface(struct libusb_device_handle *handle, int iface)
+{
+ if (handle->auto_detach_kernel_driver)
+ return detach_kernel_driver_and_claim(handle, iface);
+ else
+ return claim_interface(handle, iface);
+}
+
+static int op_release_interface(struct libusb_device_handle *handle, int iface)
+{
+ int r;
+
+ r = release_interface(handle, iface);
+ if (r)
+ return r;
+
+ if (handle->auto_detach_kernel_driver)
+ op_attach_kernel_driver(handle, iface);
+
+ return 0;
+}
+
+static void op_destroy_device(struct libusb_device *dev)
+{
+ struct linux_device_priv *priv = _device_priv(dev);
+ if (priv->descriptors)
+ free(priv->descriptors);
+ if (priv->sysfs_dir)
+ free(priv->sysfs_dir);
+}
+
+/* URBs are discarded in reverse order of submission to avoid races. */
+static int discard_urbs(struct usbi_transfer *itransfer, int first, int last_plus_one)
+{
+ struct libusb_transfer *transfer =
+ USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct linux_transfer_priv *tpriv =
+ usbi_transfer_get_os_priv(itransfer);
+ struct linux_device_handle_priv *dpriv =
+ _device_handle_priv(transfer->dev_handle);
+ int i, ret = 0;
+ struct usbfs_urb *urb;
+
+ for (i = last_plus_one - 1; i >= first; i--) {
+ if (LIBUSB_TRANSFER_TYPE_ISOCHRONOUS == transfer->type)
+ urb = tpriv->iso_urbs[i];
+ else
+ urb = &tpriv->urbs[i];
+
+ if (0 == ioctl(dpriv->fd, IOCTL_USBFS_DISCARDURB, urb))
+ continue;
+
+ if (EINVAL == errno) {
+ usbi_dbg("URB not found --> assuming ready to be reaped");
+ if (i == (last_plus_one - 1))
+ ret = LIBUSB_ERROR_NOT_FOUND;
+ } else if (ENODEV == errno) {
+ usbi_dbg("Device not found for URB --> assuming ready to be reaped");
+ ret = LIBUSB_ERROR_NO_DEVICE;
+ } else {
+ usbi_warn(TRANSFER_CTX(transfer),
+ "unrecognised discard errno %d", errno);
+ ret = LIBUSB_ERROR_OTHER;
+ }
+ }
+ return ret;
+}
+
+static void free_iso_urbs(struct linux_transfer_priv *tpriv)
+{
+ int i;
+ for (i = 0; i < tpriv->num_urbs; i++) {
+ struct usbfs_urb *urb = tpriv->iso_urbs[i];
+ if (!urb)
+ break;
+ free(urb);
+ }
+
+ free(tpriv->iso_urbs);
+ tpriv->iso_urbs = NULL;
+}
+
+static int submit_bulk_transfer(struct usbi_transfer *itransfer)
+{
+ struct libusb_transfer *transfer =
+ USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct linux_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer);
+ struct linux_device_handle_priv *dpriv =
+ _device_handle_priv(transfer->dev_handle);
+ struct usbfs_urb *urbs;
+ int is_out = (transfer->endpoint & LIBUSB_ENDPOINT_DIR_MASK)
+ == LIBUSB_ENDPOINT_OUT;
+ int bulk_buffer_len, use_bulk_continuation;
+ int r;
+ int i;
+
+ if (is_out && (transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET) &&
+ !(dpriv->caps & USBFS_CAP_ZERO_PACKET))
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+
+ /*
+ * Older versions of usbfs place a 16kb limit on bulk URBs. We work
+ * around this by splitting large transfers into 16k blocks, and then
+ * submit all urbs at once. it would be simpler to submit one urb at
+ * a time, but there is a big performance gain doing it this way.
+ *
+ * Newer versions lift the 16k limit (USBFS_CAP_NO_PACKET_SIZE_LIM),
+ * using arbritary large transfers can still be a bad idea though, as
+ * the kernel needs to allocate physical contiguous memory for this,
+ * which may fail for large buffers.
+ *
+ * The kernel solves this problem by splitting the transfer into
+ * blocks itself when the host-controller is scatter-gather capable
+ * (USBFS_CAP_BULK_SCATTER_GATHER), which most controllers are.
+ *
+ * Last, there is the issue of short-transfers when splitting, for
+ * short split-transfers to work reliable USBFS_CAP_BULK_CONTINUATION
+ * is needed, but this is not always available.
+ */
+ if (dpriv->caps & USBFS_CAP_BULK_SCATTER_GATHER) {
+ /* Good! Just submit everything in one go */
+ bulk_buffer_len = transfer->length ? transfer->length : 1;
+ use_bulk_continuation = 0;
+ } else if (dpriv->caps & USBFS_CAP_BULK_CONTINUATION) {
+ /* Split the transfers and use bulk-continuation to
+ avoid issues with short-transfers */
+ bulk_buffer_len = MAX_BULK_BUFFER_LENGTH;
+ use_bulk_continuation = 1;
+ } else if (dpriv->caps & USBFS_CAP_NO_PACKET_SIZE_LIM) {
+ /* Don't split, assume the kernel can alloc the buffer
+ (otherwise the submit will fail with -ENOMEM) */
+ bulk_buffer_len = transfer->length ? transfer->length : 1;
+ use_bulk_continuation = 0;
+ } else {
+ /* Bad, splitting without bulk-continuation, short transfers
+ which end before the last urb will not work reliable! */
+ /* Note we don't warn here as this is "normal" on kernels <
+ 2.6.32 and not a problem for most applications */
+ bulk_buffer_len = MAX_BULK_BUFFER_LENGTH;
+ use_bulk_continuation = 0;
+ }
+
+ int num_urbs = transfer->length / bulk_buffer_len;
+ int last_urb_partial = 0;
+
+ if (transfer->length == 0) {
+ num_urbs = 1;
+ } else if ((transfer->length % bulk_buffer_len) > 0) {
+ last_urb_partial = 1;
+ num_urbs++;
+ }
+ usbi_dbg("need %d urbs for new transfer with length %d", num_urbs,
+ transfer->length);
+ urbs = calloc(num_urbs, sizeof(struct usbfs_urb));
+ if (!urbs)
+ return LIBUSB_ERROR_NO_MEM;
+ tpriv->urbs = urbs;
+ tpriv->num_urbs = num_urbs;
+ tpriv->num_retired = 0;
+ tpriv->reap_action = NORMAL;
+ tpriv->reap_status = LIBUSB_TRANSFER_COMPLETED;
+
+ for (i = 0; i < num_urbs; i++) {
+ struct usbfs_urb *urb = &urbs[i];
+ urb->usercontext = itransfer;
+ switch (transfer->type) {
+ case LIBUSB_TRANSFER_TYPE_BULK:
+ urb->type = USBFS_URB_TYPE_BULK;
+ urb->stream_id = 0;
+ break;
+ case LIBUSB_TRANSFER_TYPE_BULK_STREAM:
+ urb->type = USBFS_URB_TYPE_BULK;
+ urb->stream_id = itransfer->stream_id;
+ break;
+ case LIBUSB_TRANSFER_TYPE_INTERRUPT:
+ urb->type = USBFS_URB_TYPE_INTERRUPT;
+ break;
+ }
+ urb->endpoint = transfer->endpoint;
+ urb->buffer = transfer->buffer + (i * bulk_buffer_len);
+ /* don't set the short not ok flag for the last URB */
+ if (use_bulk_continuation && !is_out && (i < num_urbs - 1))
+ urb->flags = USBFS_URB_SHORT_NOT_OK;
+ if (i == num_urbs - 1 && last_urb_partial)
+ urb->buffer_length = transfer->length % bulk_buffer_len;
+ else if (transfer->length == 0)
+ urb->buffer_length = 0;
+ else
+ urb->buffer_length = bulk_buffer_len;
+
+ if (i > 0 && use_bulk_continuation)
+ urb->flags |= USBFS_URB_BULK_CONTINUATION;
+
+ /* we have already checked that the flag is supported */
+ if (is_out && i == num_urbs - 1 &&
+ transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET)
+ urb->flags |= USBFS_URB_ZERO_PACKET;
+
+ r = ioctl(dpriv->fd, IOCTL_USBFS_SUBMITURB, urb);
+ if (r < 0) {
+ if (errno == ENODEV) {
+ r = LIBUSB_ERROR_NO_DEVICE;
+ } else {
+ usbi_err(TRANSFER_CTX(transfer),
+ "submiturb failed error %d errno=%d", r, errno);
+ r = LIBUSB_ERROR_IO;
+ }
+
+ /* if the first URB submission fails, we can simply free up and
+ * return failure immediately. */
+ if (i == 0) {
+ usbi_dbg("first URB failed, easy peasy");
+ free(urbs);
+ tpriv->urbs = NULL;
+ return r;
+ }
+
+ /* if it's not the first URB that failed, the situation is a bit
+ * tricky. we may need to discard all previous URBs. there are
+ * complications:
+ * - discarding is asynchronous - discarded urbs will be reaped
+ * later. the user must not have freed the transfer when the
+ * discarded URBs are reaped, otherwise libusb will be using
+ * freed memory.
+ * - the earlier URBs may have completed successfully and we do
+ * not want to throw away any data.
+ * - this URB failing may be no error; EREMOTEIO means that
+ * this transfer simply didn't need all the URBs we submitted
+ * so, we report that the transfer was submitted successfully and
+ * in case of error we discard all previous URBs. later when
+ * the final reap completes we can report error to the user,
+ * or success if an earlier URB was completed successfully.
+ */
+ tpriv->reap_action = EREMOTEIO == errno ? COMPLETED_EARLY : SUBMIT_FAILED;
+
+ /* The URBs we haven't submitted yet we count as already
+ * retired. */
+ tpriv->num_retired += num_urbs - i;
+
+ /* If we completed short then don't try to discard. */
+ if (COMPLETED_EARLY == tpriv->reap_action)
+ return 0;
+
+ discard_urbs(itransfer, 0, i);
+
+ usbi_dbg("reporting successful submission but waiting for %d "
+ "discards before reporting error", i);
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+static int submit_iso_transfer(struct usbi_transfer *itransfer)
+{
+ struct libusb_transfer *transfer =
+ USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct linux_transfer_priv *tpriv = usbi_transfer_get_os_priv(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;
+ unsigned int packet_len;
+ 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.
+ */
+
+ /* calculate how many URBs we need */
+ for (i = 0; i < num_packets; i++) {
+ unsigned int space_remaining = MAX_ISO_BUFFER_LENGTH - this_urb_len;
+ packet_len = transfer->iso_packet_desc[i].length;
+
+ if (packet_len > space_remaining) {
+ num_urbs++;
+ this_urb_len = packet_len;
+ /* check that we can actually support this packet length */
+ if (this_urb_len > MAX_ISO_BUFFER_LENGTH)
+ return LIBUSB_ERROR_INVALID_PARAM;
+ } else {
+ this_urb_len += packet_len;
+ }
+ }
+ usbi_dbg("need %d %dk URBs for transfer", num_urbs, MAX_ISO_BUFFER_LENGTH / 1024);
+
+ urbs = calloc(num_urbs, sizeof(*urbs));
+ if (!urbs)
+ return LIBUSB_ERROR_NO_MEM;
+
+ tpriv->iso_urbs = urbs;
+ tpriv->num_urbs = num_urbs;
+ tpriv->num_retired = 0;
+ tpriv->reap_action = NORMAL;
+ tpriv->iso_packet_offset = 0;
+
+ /* allocate + initialize each URB with the correct number of packets */
+ for (i = 0; i < num_urbs; i++) {
+ 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;
+ 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));
+ urb = calloc(1, alloc_size);
+ if (!urb) {
+ free_iso_urbs(tpriv);
+ return LIBUSB_ERROR_NO_MEM;
+ }
+ 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;
+ }
+
+ urb->usercontext = itransfer;
+ urb->type = USBFS_URB_TYPE_ISO;
+ /* 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;
+ }
+
+ /* submit URBs */
+ for (i = 0; i < num_urbs; i++) {
+ int r = ioctl(dpriv->fd, IOCTL_USBFS_SUBMITURB, urbs[i]);
+ if (r < 0) {
+ if (errno == ENODEV) {
+ r = LIBUSB_ERROR_NO_DEVICE;
+ } else if (errno == EINVAL) {
+ usbi_warn(TRANSFER_CTX(transfer),
+ "submiturb failed, transfer too large");
+ r = LIBUSB_ERROR_INVALID_PARAM;
+ } else {
+ usbi_err(TRANSFER_CTX(transfer),
+ "submiturb failed error %d errno=%d", r, errno);
+ r = LIBUSB_ERROR_IO;
+ }
+
+ /* if the first URB submission fails, we can simply free up and
+ * return failure immediately. */
+ if (i == 0) {
+ usbi_dbg("first URB failed, easy peasy");
+ free_iso_urbs(tpriv);
+ return r;
+ }
+
+ /* if it's not the first URB that failed, the situation is a bit
+ * tricky. we must discard all previous URBs. there are
+ * complications:
+ * - discarding is asynchronous - discarded urbs will be reaped
+ * later. the user must not have freed the transfer when the
+ * discarded URBs are reaped, otherwise libusb will be using
+ * freed memory.
+ * - the earlier URBs may have completed successfully and we do
+ * not want to throw away any data.
+ * so, in this case we discard all the previous URBs BUT we report
+ * that the transfer was submitted successfully. then later when
+ * the final discard completes we can report error to the user.
+ */
+ tpriv->reap_action = SUBMIT_FAILED;
+
+ /* The URBs we haven't submitted yet we count as already
+ * retired. */
+ tpriv->num_retired = num_urbs - i;
+ discard_urbs(itransfer, 0, i);
+
+ usbi_dbg("reporting successful submission but waiting for %d "
+ "discards before reporting error", i);
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+static int submit_control_transfer(struct usbi_transfer *itransfer)
+{
+ struct linux_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer);
+ struct libusb_transfer *transfer =
+ USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct linux_device_handle_priv *dpriv =
+ _device_handle_priv(transfer->dev_handle);
+ struct usbfs_urb *urb;
+ int r;
+
+ if (transfer->length - LIBUSB_CONTROL_SETUP_SIZE > MAX_CTRL_BUFFER_LENGTH)
+ return LIBUSB_ERROR_INVALID_PARAM;
+
+ urb = calloc(1, sizeof(struct usbfs_urb));
+ if (!urb)
+ return LIBUSB_ERROR_NO_MEM;
+ tpriv->urbs = urb;
+ tpriv->num_urbs = 1;
+ tpriv->reap_action = NORMAL;
+
+ urb->usercontext = itransfer;
+ urb->type = USBFS_URB_TYPE_CONTROL;
+ urb->endpoint = transfer->endpoint;
+ urb->buffer = transfer->buffer;
+ urb->buffer_length = transfer->length;
+
+ r = ioctl(dpriv->fd, IOCTL_USBFS_SUBMITURB, urb);
+ if (r < 0) {
+ free(urb);
+ tpriv->urbs = NULL;
+ if (errno == ENODEV)
+ return LIBUSB_ERROR_NO_DEVICE;
+
+ usbi_err(TRANSFER_CTX(transfer),
+ "submiturb failed error %d errno=%d", r, errno);
+ return LIBUSB_ERROR_IO;
+ }
+ return 0;
+}
+
+static int op_submit_transfer(struct usbi_transfer *itransfer)
+{
+ struct libusb_transfer *transfer =
+ USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+
+ switch (transfer->type) {
+ case LIBUSB_TRANSFER_TYPE_CONTROL:
+ return submit_control_transfer(itransfer);
+ case LIBUSB_TRANSFER_TYPE_BULK:
+ case LIBUSB_TRANSFER_TYPE_BULK_STREAM:
+ return submit_bulk_transfer(itransfer);
+ case LIBUSB_TRANSFER_TYPE_INTERRUPT:
+ return submit_bulk_transfer(itransfer);
+ case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
+ return submit_iso_transfer(itransfer);
+ default:
+ usbi_err(TRANSFER_CTX(transfer),
+ "unknown endpoint type %d", transfer->type);
+ return LIBUSB_ERROR_INVALID_PARAM;
+ }
+}
+
+static int op_cancel_transfer(struct usbi_transfer *itransfer)
+{
+ struct linux_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer);
+ struct libusb_transfer *transfer =
+ USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ int r;
+
+ if (!tpriv->urbs)
+ return LIBUSB_ERROR_NOT_FOUND;
+
+ r = discard_urbs(itransfer, 0, tpriv->num_urbs);
+ if (r != 0)
+ return r;
+
+ switch (transfer->type) {
+ case LIBUSB_TRANSFER_TYPE_BULK:
+ case LIBUSB_TRANSFER_TYPE_BULK_STREAM:
+ if (tpriv->reap_action == ERROR)
+ break;
+ /* else, fall through */
+ default:
+ tpriv->reap_action = CANCELLED;
+ }
+
+ return 0;
+}
+
+static void op_clear_transfer_priv(struct usbi_transfer *itransfer)
+{
+ struct libusb_transfer *transfer =
+ 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:
+ case LIBUSB_TRANSFER_TYPE_BULK_STREAM:
+ case LIBUSB_TRANSFER_TYPE_INTERRUPT:
+ if (tpriv->urbs) {
+ free(tpriv->urbs);
+ tpriv->urbs = NULL;
+ }
+ break;
+ case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
+ if (tpriv->iso_urbs) {
+ free_iso_urbs(tpriv);
+ tpriv->iso_urbs = NULL;
+ }
+ break;
+ default:
+ usbi_err(TRANSFER_CTX(transfer),
+ "unknown endpoint type %d", transfer->type);
+ }
+}
+
+static int handle_bulk_completion(struct usbi_transfer *itransfer,
+ struct usbfs_urb *urb)
+{
+ struct linux_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer);
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ int urb_idx = urb - tpriv->urbs;
+
+ usbi_mutex_lock(&itransfer->lock);
+ usbi_dbg("handling completion status %d of bulk urb %d/%d", urb->status,
+ urb_idx + 1, tpriv->num_urbs);
+
+ tpriv->num_retired++;
+
+ if (tpriv->reap_action != NORMAL) {
+ /* cancelled, submit_fail, or completed early */
+ usbi_dbg("abnormal reap: urb status %d", urb->status);
+
+ /* even though we're in the process of cancelling, it's possible that
+ * we may receive some data in these URBs that we don't want to lose.
+ * examples:
+ * 1. while the kernel is cancelling all the packets that make up an
+ * URB, a few of them might complete. so we get back a successful
+ * cancellation *and* some data.
+ * 2. we receive a short URB which marks the early completion condition,
+ * so we start cancelling the remaining URBs. however, we're too
+ * slow and another URB completes (or at least completes partially).
+ * (this can't happen since we always use BULK_CONTINUATION.)
+ *
+ * When this happens, our objectives are not to lose any "surplus" data,
+ * and also to stick it at the end of the previously-received data
+ * (closing any holes), so that libusb reports the total amount of
+ * transferred data and presents it in a contiguous chunk.
+ */
+ if (urb->actual_length > 0) {
+ unsigned char *target = transfer->buffer + itransfer->transferred;
+ usbi_dbg("received %d bytes of surplus data", urb->actual_length);
+ if (urb->buffer != target) {
+ usbi_dbg("moving surplus data from offset %d to offset %d",
+ (unsigned char *) urb->buffer - transfer->buffer,
+ target - transfer->buffer);
+ memmove(target, urb->buffer, urb->actual_length);
+ }
+ itransfer->transferred += urb->actual_length;
+ }
+
+ if (tpriv->num_retired == tpriv->num_urbs) {
+ usbi_dbg("abnormal reap: last URB handled, reporting");
+ if (tpriv->reap_action != COMPLETED_EARLY &&
+ tpriv->reap_status == LIBUSB_TRANSFER_COMPLETED)
+ tpriv->reap_status = LIBUSB_TRANSFER_ERROR;
+ goto completed;
+ }
+ goto out_unlock;
+ }
+
+ itransfer->transferred += urb->actual_length;
+
+ /* Many of these errors can occur on *any* urb of a multi-urb
+ * transfer. When they do, we tear down the rest of the transfer.
+ */
+ switch (urb->status) {
+ case 0:
+ break;
+ case -EREMOTEIO: /* short transfer */
+ break;
+ case -ENOENT: /* cancelled */
+ case -ECONNRESET:
+ break;
+ case -ENODEV:
+ case -ESHUTDOWN:
+ usbi_dbg("device removed");
+ tpriv->reap_status = LIBUSB_TRANSFER_NO_DEVICE;
+ goto cancel_remaining;
+ case -EPIPE:
+ usbi_dbg("detected endpoint stall");
+ if (tpriv->reap_status == LIBUSB_TRANSFER_COMPLETED)
+ tpriv->reap_status = LIBUSB_TRANSFER_STALL;
+ goto cancel_remaining;
+ case -EOVERFLOW:
+ /* overflow can only ever occur in the last urb */
+ usbi_dbg("overflow, actual_length=%d", urb->actual_length);
+ if (tpriv->reap_status == LIBUSB_TRANSFER_COMPLETED)
+ tpriv->reap_status = LIBUSB_TRANSFER_OVERFLOW;
+ goto completed;
+ case -ETIME:
+ case -EPROTO:
+ case -EILSEQ:
+ case -ECOMM:
+ case -ENOSR:
+ usbi_dbg("low level error %d", urb->status);
+ tpriv->reap_action = ERROR;
+ goto cancel_remaining;
+ default:
+ usbi_warn(ITRANSFER_CTX(itransfer),
+ "unrecognised urb status %d", urb->status);
+ tpriv->reap_action = ERROR;
+ goto cancel_remaining;
+ }
+
+ /* if we're the last urb or we got less data than requested then we're
+ * done */
+ if (urb_idx == tpriv->num_urbs - 1) {
+ usbi_dbg("last URB in transfer --> complete!");
+ goto completed;
+ } else if (urb->actual_length < urb->buffer_length) {
+ usbi_dbg("short transfer %d/%d --> complete!",
+ urb->actual_length, urb->buffer_length);
+ if (tpriv->reap_action == NORMAL)
+ tpriv->reap_action = COMPLETED_EARLY;
+ } else
+ goto out_unlock;
+
+cancel_remaining:
+ if (ERROR == tpriv->reap_action && LIBUSB_TRANSFER_COMPLETED == tpriv->reap_status)
+ tpriv->reap_status = LIBUSB_TRANSFER_ERROR;
+
+ if (tpriv->num_retired == tpriv->num_urbs) /* nothing to cancel */
+ goto completed;
+
+ /* cancel remaining urbs and wait for their completion before
+ * reporting results */
+ discard_urbs(itransfer, urb_idx + 1, tpriv->num_urbs);
+
+out_unlock:
+ usbi_mutex_unlock(&itransfer->lock);
+ return 0;
+
+completed:
+ free(tpriv->urbs);
+ tpriv->urbs = NULL;
+ usbi_mutex_unlock(&itransfer->lock);
+ return CANCELLED == tpriv->reap_action ?
+ usbi_handle_transfer_cancellation(itransfer) :
+ usbi_handle_transfer_completion(itransfer, tpriv->reap_status);
+}
+
+static int handle_iso_completion(struct usbi_transfer *itransfer,
+ struct usbfs_urb *urb)
+{
+ struct libusb_transfer *transfer =
+ USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct linux_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer);
+ int num_urbs = tpriv->num_urbs;
+ int urb_idx = 0;
+ int i;
+ enum libusb_transfer_status status = LIBUSB_TRANSFER_COMPLETED;
+
+ usbi_mutex_lock(&itransfer->lock);
+ for (i = 0; i < num_urbs; i++) {
+ if (urb == tpriv->iso_urbs[i]) {
+ urb_idx = i + 1;
+ break;
+ }
+ }
+ if (urb_idx == 0) {
+ usbi_err(TRANSFER_CTX(transfer), "could not locate urb!");
+ usbi_mutex_unlock(&itransfer->lock);
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
+
+ usbi_dbg("handling completion status %d of iso urb %d/%d", urb->status,
+ urb_idx, num_urbs);
+
+ /* copy isochronous results back in */
+
+ for (i = 0; i < urb->number_of_packets; i++) {
+ struct usbfs_iso_packet_desc *urb_desc = &urb->iso_frame_desc[i];
+ struct libusb_iso_packet_descriptor *lib_desc =
+ &transfer->iso_packet_desc[tpriv->iso_packet_offset++];
+ lib_desc->status = LIBUSB_TRANSFER_COMPLETED;
+ switch (urb_desc->status) {
+ case 0:
+ break;
+ case -ENOENT: /* cancelled */
+ case -ECONNRESET:
+ break;
+ case -ENODEV:
+ case -ESHUTDOWN:
+ usbi_dbg("device removed");
+ lib_desc->status = LIBUSB_TRANSFER_NO_DEVICE;
+ break;
+ case -EPIPE:
+ usbi_dbg("detected endpoint stall");
+ lib_desc->status = LIBUSB_TRANSFER_STALL;
+ break;
+ case -EOVERFLOW:
+ usbi_dbg("overflow error");
+ lib_desc->status = LIBUSB_TRANSFER_OVERFLOW;
+ break;
+ case -ETIME:
+ case -EPROTO:
+ case -EILSEQ:
+ case -ECOMM:
+ case -ENOSR:
+ case -EXDEV:
+ usbi_dbg("low-level USB error %d", urb_desc->status);
+ lib_desc->status = LIBUSB_TRANSFER_ERROR;
+ break;
+ default:
+ usbi_warn(TRANSFER_CTX(transfer),
+ "unrecognised urb status %d", urb_desc->status);
+ lib_desc->status = LIBUSB_TRANSFER_ERROR;
+ break;
+ }
+ lib_desc->actual_length = urb_desc->actual_length;
+ }
+
+ tpriv->num_retired++;
+
+ if (tpriv->reap_action != NORMAL) { /* cancelled or submit_fail */
+ usbi_dbg("CANCEL: urb status %d", urb->status);
+
+ if (tpriv->num_retired == num_urbs) {
+ usbi_dbg("CANCEL: last URB handled, reporting");
+ free_iso_urbs(tpriv);
+ if (tpriv->reap_action == CANCELLED) {
+ usbi_mutex_unlock(&itransfer->lock);
+ return usbi_handle_transfer_cancellation(itransfer);
+ } else {
+ usbi_mutex_unlock(&itransfer->lock);
+ return usbi_handle_transfer_completion(itransfer,
+ LIBUSB_TRANSFER_ERROR);
+ }
+ }
+ goto out;
+ }
+
+ switch (urb->status) {
+ case 0:
+ break;
+ case -ENOENT: /* cancelled */
+ case -ECONNRESET:
+ break;
+ case -ESHUTDOWN:
+ usbi_dbg("device removed");
+ status = LIBUSB_TRANSFER_NO_DEVICE;
+ break;
+ default:
+ usbi_warn(TRANSFER_CTX(transfer),
+ "unrecognised urb status %d", urb->status);
+ status = LIBUSB_TRANSFER_ERROR;
+ break;
+ }
+
+ /* if we're the last urb then we're done */
+ if (urb_idx == num_urbs) {
+ usbi_dbg("last URB in transfer --> complete!");
+ free_iso_urbs(tpriv);
+ usbi_mutex_unlock(&itransfer->lock);
+ return usbi_handle_transfer_completion(itransfer, status);
+ }
+
+out:
+ usbi_mutex_unlock(&itransfer->lock);
+ return 0;
+}
+
+static int handle_control_completion(struct usbi_transfer *itransfer,
+ struct usbfs_urb *urb)
+{
+ struct linux_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer);
+ int status;
+
+ usbi_mutex_lock(&itransfer->lock);
+ usbi_dbg("handling completion status %d", urb->status);
+
+ itransfer->transferred += urb->actual_length;
+
+ if (tpriv->reap_action == CANCELLED) {
+ if (urb->status != 0 && urb->status != -ENOENT)
+ usbi_warn(ITRANSFER_CTX(itransfer),
+ "cancel: unrecognised urb status %d", urb->status);
+ free(tpriv->urbs);
+ tpriv->urbs = NULL;
+ usbi_mutex_unlock(&itransfer->lock);
+ return usbi_handle_transfer_cancellation(itransfer);
+ }
+
+ switch (urb->status) {
+ case 0:
+ status = LIBUSB_TRANSFER_COMPLETED;
+ break;
+ case -ENOENT: /* cancelled */
+ status = LIBUSB_TRANSFER_CANCELLED;
+ break;
+ case -ENODEV:
+ case -ESHUTDOWN:
+ usbi_dbg("device removed");
+ status = LIBUSB_TRANSFER_NO_DEVICE;
+ break;
+ case -EPIPE:
+ usbi_dbg("unsupported control request");
+ status = LIBUSB_TRANSFER_STALL;
+ break;
+ case -EOVERFLOW:
+ usbi_dbg("control overflow error");
+ status = LIBUSB_TRANSFER_OVERFLOW;
+ break;
+ case -ETIME:
+ case -EPROTO:
+ case -EILSEQ:
+ case -ECOMM:
+ case -ENOSR:
+ usbi_dbg("low-level bus error occurred");
+ status = LIBUSB_TRANSFER_ERROR;
+ break;
+ default:
+ usbi_warn(ITRANSFER_CTX(itransfer),
+ "unrecognised urb status %d", urb->status);
+ status = LIBUSB_TRANSFER_ERROR;
+ break;
+ }
+
+ free(tpriv->urbs);
+ tpriv->urbs = NULL;
+ usbi_mutex_unlock(&itransfer->lock);
+ return usbi_handle_transfer_completion(itransfer, status);
+}
+
+static int reap_for_handle(struct libusb_device_handle *handle)
+{
+ struct linux_device_handle_priv *hpriv = _device_handle_priv(handle);
+ int r;
+ struct usbfs_urb *urb;
+ struct usbi_transfer *itransfer;
+ struct libusb_transfer *transfer;
+
+ r = ioctl(hpriv->fd, IOCTL_USBFS_REAPURBNDELAY, &urb);
+ if (r == -1 && errno == EAGAIN)
+ return 1;
+ if (r < 0) {
+ if (errno == ENODEV)
+ return LIBUSB_ERROR_NO_DEVICE;
+
+ usbi_err(HANDLE_CTX(handle), "reap failed error %d errno=%d",
+ r, errno);
+ return LIBUSB_ERROR_IO;
+ }
+
+ itransfer = urb->usercontext;
+ transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+
+ usbi_dbg("urb type=%d status=%d transferred=%d", urb->type, urb->status,
+ urb->actual_length);
+
+ switch (transfer->type) {
+ case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
+ return handle_iso_completion(itransfer, urb);
+ case LIBUSB_TRANSFER_TYPE_BULK:
+ case LIBUSB_TRANSFER_TYPE_BULK_STREAM:
+ case LIBUSB_TRANSFER_TYPE_INTERRUPT:
+ return handle_bulk_completion(itransfer, urb);
+ case LIBUSB_TRANSFER_TYPE_CONTROL:
+ return handle_control_completion(itransfer, urb);
+ default:
+ usbi_err(HANDLE_CTX(handle), "unrecognised endpoint type %x",
+ transfer->type);
+ return LIBUSB_ERROR_OTHER;
+ }
+}
+
+static int op_handle_events(struct libusb_context *ctx,
+ struct pollfd *fds, POLL_NFDS_TYPE nfds, int num_ready)
+{
+ int r;
+ unsigned int i = 0;
+
+ usbi_mutex_lock(&ctx->open_devs_lock);
+ for (i = 0; i < nfds && num_ready > 0; i++) {
+ struct pollfd *pollfd = &fds[i];
+ struct libusb_device_handle *handle;
+ struct linux_device_handle_priv *hpriv = NULL;
+
+ if (!pollfd->revents)
+ continue;
+
+ num_ready--;
+ list_for_each_entry(handle, &ctx->open_devs, list, struct libusb_device_handle) {
+ hpriv = _device_handle_priv(handle);
+ if (hpriv->fd == pollfd->fd)
+ break;
+ }
+
+ if (!hpriv || hpriv->fd != pollfd->fd) {
+ usbi_err(ctx, "cannot find handle for fd %d",
+ pollfd->fd);
+ continue;
+ }
+
+ if (pollfd->revents & POLLERR) {
+ /* remove the fd from the pollfd set so that it doesn't continuously
+ * trigger an event, and flag that it has been removed so op_close()
+ * doesn't try to remove it a second time */
+ usbi_remove_pollfd(HANDLE_CTX(handle), hpriv->fd);
+ hpriv->fd_removed = 1;
+
+ /* device will still be marked as attached if hotplug monitor thread
+ * hasn't processed remove event yet */
+ usbi_mutex_static_lock(&linux_hotplug_lock);
+ if (handle->dev->attached)
+ linux_device_disconnected(handle->dev->bus_number,
+ handle->dev->device_address);
+ usbi_mutex_static_unlock(&linux_hotplug_lock);
+
+ if (hpriv->caps & USBFS_CAP_REAP_AFTER_DISCONNECT) {
+ do {
+ r = reap_for_handle(handle);
+ } while (r == 0);
+ }
+
+ usbi_handle_disconnect(handle);
+ continue;
+ }
+
+ do {
+ r = reap_for_handle(handle);
+ } while (r == 0);
+ if (r == 1 || r == LIBUSB_ERROR_NO_DEVICE)
+ continue;
+ else if (r < 0)
+ goto out;
+ }
+
+ r = 0;
+out:
+ usbi_mutex_unlock(&ctx->open_devs_lock);
+ return r;
+}
+
+static int op_clock_gettime(int clk_id, struct timespec *tp)
+{
+ switch (clk_id) {
+ case USBI_CLOCK_MONOTONIC:
+ return clock_gettime(monotonic_clkid, tp);
+ case USBI_CLOCK_REALTIME:
+ return clock_gettime(CLOCK_REALTIME, tp);
+ default:
+ return LIBUSB_ERROR_INVALID_PARAM;
+ }
+}
+
+#ifdef USBI_TIMERFD_AVAILABLE
+static clockid_t op_get_timerfd_clockid(void)
+{
+ return monotonic_clkid;
+
+}
+#endif
+
+const struct usbi_os_backend linux_usbfs_backend = {
+ .name = "Linux usbfs",
+ .caps = USBI_CAP_HAS_HID_ACCESS|USBI_CAP_SUPPORTS_DETACH_KERNEL_DRIVER,
+ .init = op_init,
+ .exit = op_exit,
+ .get_device_list = NULL,
+ .hotplug_poll = op_hotplug_poll,
+ .get_device_descriptor = op_get_device_descriptor,
+ .get_active_config_descriptor = op_get_active_config_descriptor,
+ .get_config_descriptor = op_get_config_descriptor,
+ .get_config_descriptor_by_value = op_get_config_descriptor_by_value,
+
+ .open = op_open,
+ .close = op_close,
+ .get_configuration = op_get_configuration,
+ .set_configuration = op_set_configuration,
+ .claim_interface = op_claim_interface,
+ .release_interface = op_release_interface,
+
+ .set_interface_altsetting = op_set_interface,
+ .clear_halt = op_clear_halt,
+ .reset_device = op_reset_device,
+
+ .alloc_streams = op_alloc_streams,
+ .free_streams = op_free_streams,
+
+ .dev_mem_alloc = op_dev_mem_alloc,
+ .dev_mem_free = op_dev_mem_free,
+
+ .kernel_driver_active = op_kernel_driver_active,
+ .detach_kernel_driver = op_detach_kernel_driver,
+ .attach_kernel_driver = op_attach_kernel_driver,
+
+ .destroy_device = op_destroy_device,
+
+ .submit_transfer = op_submit_transfer,
+ .cancel_transfer = op_cancel_transfer,
+ .clear_transfer_priv = op_clear_transfer_priv,
+
+ .handle_events = op_handle_events,
+
+ .clock_gettime = op_clock_gettime,
+
+#ifdef USBI_TIMERFD_AVAILABLE
+ .get_timerfd_clockid = op_get_timerfd_clockid,
+#endif
+
+ .device_priv_size = sizeof(struct linux_device_priv),
+ .device_handle_priv_size = sizeof(struct linux_device_handle_priv),
+ .transfer_priv_size = sizeof(struct linux_transfer_priv),
+};
diff --git a/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/linux_usbfs.h b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/linux_usbfs.h
new file mode 100644
index 000000000..8bd3ebcb1
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/linux_usbfs.h
@@ -0,0 +1,193 @@
+/*
+ * usbfs header structures
+ * Copyright © 2007 Daniel Drake <dsd@gentoo.org>
+ * Copyright © 2001 Johannes Erdfelt <johannes@erdfelt.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef LIBUSB_USBFS_H
+#define LIBUSB_USBFS_H
+
+#include <linux/types.h>
+
+#define SYSFS_DEVICE_PATH "/sys/bus/usb/devices"
+
+struct usbfs_ctrltransfer {
+ /* keep in sync with usbdevice_fs.h:usbdevfs_ctrltransfer */
+ uint8_t bmRequestType;
+ uint8_t bRequest;
+ uint16_t wValue;
+ uint16_t wIndex;
+ uint16_t wLength;
+
+ uint32_t timeout; /* in milliseconds */
+
+ /* pointer to data */
+ void *data;
+};
+
+struct usbfs_bulktransfer {
+ /* keep in sync with usbdevice_fs.h:usbdevfs_bulktransfer */
+ unsigned int ep;
+ unsigned int len;
+ unsigned int timeout; /* in milliseconds */
+
+ /* pointer to data */
+ void *data;
+};
+
+struct usbfs_setinterface {
+ /* keep in sync with usbdevice_fs.h:usbdevfs_setinterface */
+ unsigned int interface;
+ unsigned int altsetting;
+};
+
+#define USBFS_MAXDRIVERNAME 255
+
+struct usbfs_getdriver {
+ unsigned int interface;
+ char driver[USBFS_MAXDRIVERNAME + 1];
+};
+
+#define USBFS_URB_SHORT_NOT_OK 0x01
+#define USBFS_URB_ISO_ASAP 0x02
+#define USBFS_URB_BULK_CONTINUATION 0x04
+#define USBFS_URB_QUEUE_BULK 0x10
+#define USBFS_URB_ZERO_PACKET 0x40
+
+enum usbfs_urb_type {
+ USBFS_URB_TYPE_ISO = 0,
+ USBFS_URB_TYPE_INTERRUPT = 1,
+ USBFS_URB_TYPE_CONTROL = 2,
+ USBFS_URB_TYPE_BULK = 3,
+};
+
+struct usbfs_iso_packet_desc {
+ unsigned int length;
+ unsigned int actual_length;
+ unsigned int status;
+};
+
+#define MAX_ISO_BUFFER_LENGTH 49152 * 128
+#define MAX_BULK_BUFFER_LENGTH 16384
+#define MAX_CTRL_BUFFER_LENGTH 4096
+
+struct usbfs_urb {
+ unsigned char type;
+ unsigned char endpoint;
+ int status;
+ unsigned int flags;
+ void *buffer;
+ int buffer_length;
+ int actual_length;
+ int start_frame;
+ union {
+ int number_of_packets; /* Only used for isoc urbs */
+ unsigned int stream_id; /* Only used with bulk streams */
+ };
+ int error_count;
+ unsigned int signr;
+ void *usercontext;
+ struct usbfs_iso_packet_desc iso_frame_desc[0];
+};
+
+struct usbfs_connectinfo {
+ unsigned int devnum;
+ unsigned char slow;
+};
+
+struct usbfs_ioctl {
+ int ifno; /* interface 0..N ; negative numbers reserved */
+ int ioctl_code; /* MUST encode size + direction of data so the
+ * macros in <asm/ioctl.h> give correct values */
+ void *data; /* param buffer (in, or out) */
+};
+
+struct usbfs_hub_portinfo {
+ unsigned char numports;
+ unsigned char port[127]; /* port to device num mapping */
+};
+
+#define USBFS_CAP_ZERO_PACKET 0x01
+#define USBFS_CAP_BULK_CONTINUATION 0x02
+#define USBFS_CAP_NO_PACKET_SIZE_LIM 0x04
+#define USBFS_CAP_BULK_SCATTER_GATHER 0x08
+#define USBFS_CAP_REAP_AFTER_DISCONNECT 0x10
+
+#define USBFS_DISCONNECT_CLAIM_IF_DRIVER 0x01
+#define USBFS_DISCONNECT_CLAIM_EXCEPT_DRIVER 0x02
+
+struct usbfs_disconnect_claim {
+ unsigned int interface;
+ unsigned int flags;
+ char driver[USBFS_MAXDRIVERNAME + 1];
+};
+
+struct usbfs_streams {
+ unsigned int num_streams; /* Not used by USBDEVFS_FREE_STREAMS */
+ unsigned int num_eps;
+ unsigned char eps[0];
+};
+
+#define IOCTL_USBFS_CONTROL _IOWR('U', 0, struct usbfs_ctrltransfer)
+#define IOCTL_USBFS_BULK _IOWR('U', 2, struct usbfs_bulktransfer)
+#define IOCTL_USBFS_RESETEP _IOR('U', 3, unsigned int)
+#define IOCTL_USBFS_SETINTF _IOR('U', 4, struct usbfs_setinterface)
+#define IOCTL_USBFS_SETCONFIG _IOR('U', 5, unsigned int)
+#define IOCTL_USBFS_GETDRIVER _IOW('U', 8, struct usbfs_getdriver)
+#define IOCTL_USBFS_SUBMITURB _IOR('U', 10, struct usbfs_urb)
+#define IOCTL_USBFS_DISCARDURB _IO('U', 11)
+#define IOCTL_USBFS_REAPURB _IOW('U', 12, void *)
+#define IOCTL_USBFS_REAPURBNDELAY _IOW('U', 13, void *)
+#define IOCTL_USBFS_CLAIMINTF _IOR('U', 15, unsigned int)
+#define IOCTL_USBFS_RELEASEINTF _IOR('U', 16, unsigned int)
+#define IOCTL_USBFS_CONNECTINFO _IOW('U', 17, struct usbfs_connectinfo)
+#define IOCTL_USBFS_IOCTL _IOWR('U', 18, struct usbfs_ioctl)
+#define IOCTL_USBFS_HUB_PORTINFO _IOR('U', 19, struct usbfs_hub_portinfo)
+#define IOCTL_USBFS_RESET _IO('U', 20)
+#define IOCTL_USBFS_CLEAR_HALT _IOR('U', 21, unsigned int)
+#define IOCTL_USBFS_DISCONNECT _IO('U', 22)
+#define IOCTL_USBFS_CONNECT _IO('U', 23)
+#define IOCTL_USBFS_CLAIM_PORT _IOR('U', 24, unsigned int)
+#define IOCTL_USBFS_RELEASE_PORT _IOR('U', 25, unsigned int)
+#define IOCTL_USBFS_GET_CAPABILITIES _IOR('U', 26, __u32)
+#define IOCTL_USBFS_DISCONNECT_CLAIM _IOR('U', 27, struct usbfs_disconnect_claim)
+#define IOCTL_USBFS_ALLOC_STREAMS _IOR('U', 28, struct usbfs_streams)
+#define IOCTL_USBFS_FREE_STREAMS _IOR('U', 29, struct usbfs_streams)
+
+extern usbi_mutex_static_t linux_hotplug_lock;
+
+#if defined(HAVE_LIBUDEV)
+int linux_udev_start_event_monitor(void);
+int linux_udev_stop_event_monitor(void);
+int linux_udev_scan_devices(struct libusb_context *ctx);
+void linux_udev_hotplug_poll(void);
+#else
+int linux_netlink_start_event_monitor(void);
+int linux_netlink_stop_event_monitor(void);
+void linux_netlink_hotplug_poll(void);
+#endif
+
+void linux_hotplug_enumerate(uint8_t busnum, uint8_t devaddr, const char *sys_name);
+void linux_device_disconnected(uint8_t busnum, uint8_t devaddr);
+
+int linux_get_device_address (struct libusb_context *ctx, int detached,
+ uint8_t *busnum, uint8_t *devaddr, const char *dev_node,
+ const char *sys_name);
+int linux_enumerate_device(struct libusb_context *ctx,
+ uint8_t busnum, uint8_t devaddr, const char *sysfs_dir);
+
+#endif
diff --git a/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/netbsd_usb.c b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/netbsd_usb.c
new file mode 100644
index 000000000..ad1ede73e
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/netbsd_usb.c
@@ -0,0 +1,677 @@
+/*
+ * Copyright © 2011 Martin Pieuchot <mpi@openbsd.org>
+ *
+ * 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 <sys/time.h>
+#include <sys/types.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <dev/usb/usb.h>
+
+#include "libusbi.h"
+
+struct device_priv {
+ char devnode[16];
+ int fd;
+
+ unsigned char *cdesc; /* active config descriptor */
+ usb_device_descriptor_t ddesc; /* usb device descriptor */
+};
+
+struct handle_priv {
+ int endpoints[USB_MAX_ENDPOINTS];
+};
+
+/*
+ * Backend functions
+ */
+static int netbsd_get_device_list(struct libusb_context *,
+ struct discovered_devs **);
+static int netbsd_open(struct libusb_device_handle *);
+static void netbsd_close(struct libusb_device_handle *);
+
+static int netbsd_get_device_descriptor(struct libusb_device *, unsigned char *,
+ int *);
+static int netbsd_get_active_config_descriptor(struct libusb_device *,
+ unsigned char *, size_t, int *);
+static int netbsd_get_config_descriptor(struct libusb_device *, uint8_t,
+ unsigned char *, size_t, int *);
+
+static int netbsd_get_configuration(struct libusb_device_handle *, int *);
+static int netbsd_set_configuration(struct libusb_device_handle *, int);
+
+static int netbsd_claim_interface(struct libusb_device_handle *, int);
+static int netbsd_release_interface(struct libusb_device_handle *, int);
+
+static int netbsd_set_interface_altsetting(struct libusb_device_handle *, int,
+ int);
+static int netbsd_clear_halt(struct libusb_device_handle *, unsigned char);
+static int netbsd_reset_device(struct libusb_device_handle *);
+static void netbsd_destroy_device(struct libusb_device *);
+
+static int netbsd_submit_transfer(struct usbi_transfer *);
+static int netbsd_cancel_transfer(struct usbi_transfer *);
+static void netbsd_clear_transfer_priv(struct usbi_transfer *);
+static int netbsd_handle_transfer_completion(struct usbi_transfer *);
+static int netbsd_clock_gettime(int, struct timespec *);
+
+/*
+ * Private functions
+ */
+static int _errno_to_libusb(int);
+static int _cache_active_config_descriptor(struct libusb_device *, int);
+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 = {
+ "Synchronous NetBSD backend",
+ 0,
+ NULL, /* init() */
+ NULL, /* exit() */
+ netbsd_get_device_list,
+ NULL, /* hotplug_poll */
+ netbsd_open,
+ netbsd_close,
+
+ netbsd_get_device_descriptor,
+ netbsd_get_active_config_descriptor,
+ netbsd_get_config_descriptor,
+ NULL, /* get_config_descriptor_by_value() */
+
+ netbsd_get_configuration,
+ netbsd_set_configuration,
+
+ netbsd_claim_interface,
+ netbsd_release_interface,
+
+ netbsd_set_interface_altsetting,
+ netbsd_clear_halt,
+ netbsd_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() */
+
+ netbsd_destroy_device,
+
+ netbsd_submit_transfer,
+ netbsd_cancel_transfer,
+ netbsd_clear_transfer_priv,
+
+ NULL, /* handle_events() */
+ netbsd_handle_transfer_completion,
+
+ netbsd_clock_gettime,
+ sizeof(struct device_priv),
+ sizeof(struct handle_priv),
+ 0, /* transfer_priv_size */
+};
+
+int
+netbsd_get_device_list(struct libusb_context * ctx,
+ struct discovered_devs **discdevs)
+{
+ struct libusb_device *dev;
+ struct device_priv *dpriv;
+ struct usb_device_info di;
+ unsigned long session_id;
+ char devnode[16];
+ int fd, err, i;
+
+ usbi_dbg("");
+
+ /* Only ugen(4) is supported */
+ for (i = 0; i < USB_MAX_DEVICES; i++) {
+ /* Control endpoint is always .00 */
+ snprintf(devnode, sizeof(devnode), "/dev/ugen%d.00", i);
+
+ if ((fd = open(devnode, O_RDONLY)) < 0) {
+ if (errno != ENOENT && errno != ENXIO)
+ usbi_err(ctx, "could not open %s", devnode);
+ continue;
+ }
+
+ if (ioctl(fd, USB_GET_DEVICEINFO, &di) < 0)
+ continue;
+
+ session_id = (di.udi_bus << 8 | di.udi_addr);
+ dev = usbi_get_device_by_session_id(ctx, session_id);
+
+ if (dev == NULL) {
+ dev = usbi_alloc_device(ctx, session_id);
+ if (dev == NULL)
+ return (LIBUSB_ERROR_NO_MEM);
+
+ dev->bus_number = di.udi_bus;
+ dev->device_address = di.udi_addr;
+ dev->speed = di.udi_speed;
+
+ dpriv = (struct device_priv *)dev->os_priv;
+ strlcpy(dpriv->devnode, devnode, sizeof(devnode));
+ dpriv->fd = -1;
+
+ if (ioctl(fd, USB_GET_DEVICE_DESC, &dpriv->ddesc) < 0) {
+ err = errno;
+ goto error;
+ }
+
+ dpriv->cdesc = NULL;
+ if (_cache_active_config_descriptor(dev, fd)) {
+ err = errno;
+ goto error;
+ }
+
+ if ((err = usbi_sanitize_device(dev)))
+ goto error;
+ }
+ close(fd);
+
+ if (discovered_devs_append(*discdevs, dev) == NULL)
+ return (LIBUSB_ERROR_NO_MEM);
+
+ libusb_unref_device(dev);
+ }
+
+ return (LIBUSB_SUCCESS);
+
+error:
+ close(fd);
+ libusb_unref_device(dev);
+ return _errno_to_libusb(err);
+}
+
+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);
+ if (dpriv->fd < 0) {
+ dpriv->fd = open(dpriv->devnode, O_RDONLY);
+ if (dpriv->fd < 0)
+ return _errno_to_libusb(errno);
+ }
+
+ usbi_dbg("open %s: fd %d", dpriv->devnode, dpriv->fd);
+
+ return (LIBUSB_SUCCESS);
+}
+
+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);
+
+ close(dpriv->fd);
+ dpriv->fd = -1;
+}
+
+int
+netbsd_get_device_descriptor(struct libusb_device *dev, unsigned char *buf,
+ int *host_endian)
+{
+ struct device_priv *dpriv = (struct device_priv *)dev->os_priv;
+
+ usbi_dbg("");
+
+ memcpy(buf, &dpriv->ddesc, DEVICE_DESC_LENGTH);
+
+ *host_endian = 0;
+
+ return (LIBUSB_SUCCESS);
+}
+
+int
+netbsd_get_active_config_descriptor(struct libusb_device *dev,
+ unsigned char *buf, size_t len, int *host_endian)
+{
+ struct device_priv *dpriv = (struct device_priv *)dev->os_priv;
+ usb_config_descriptor_t *ucd;
+
+ ucd = (usb_config_descriptor_t *) dpriv->cdesc;
+ len = MIN(len, UGETW(ucd->wTotalLength));
+
+ usbi_dbg("len %d", len);
+
+ memcpy(buf, dpriv->cdesc, len);
+
+ *host_endian = 0;
+
+ return len;
+}
+
+int
+netbsd_get_config_descriptor(struct libusb_device *dev, uint8_t idx,
+ unsigned char *buf, size_t len, int *host_endian)
+{
+ struct device_priv *dpriv = (struct device_priv *)dev->os_priv;
+ struct usb_full_desc ufd;
+ int fd, err;
+
+ usbi_dbg("index %d, len %d", idx, len);
+
+ /* A config descriptor may be requested before opening the device */
+ if (dpriv->fd >= 0) {
+ fd = dpriv->fd;
+ } else {
+ fd = open(dpriv->devnode, O_RDONLY);
+ if (fd < 0)
+ return _errno_to_libusb(errno);
+ }
+
+ ufd.ufd_config_index = idx;
+ ufd.ufd_size = len;
+ ufd.ufd_data = buf;
+
+ if ((ioctl(fd, USB_GET_FULL_DESC, &ufd)) < 0) {
+ err = errno;
+ if (dpriv->fd < 0)
+ close(fd);
+ return _errno_to_libusb(err);
+ }
+
+ if (dpriv->fd < 0)
+ close(fd);
+
+ *host_endian = 0;
+
+ return len;
+}
+
+int
+netbsd_get_configuration(struct libusb_device_handle *handle, int *config)
+{
+ struct device_priv *dpriv = (struct device_priv *)handle->dev->os_priv;
+
+ usbi_dbg("");
+
+ if (ioctl(dpriv->fd, USB_GET_CONFIG, config) < 0)
+ return _errno_to_libusb(errno);
+
+ usbi_dbg("configuration %d", *config);
+
+ return (LIBUSB_SUCCESS);
+}
+
+int
+netbsd_set_configuration(struct libusb_device_handle *handle, int config)
+{
+ struct device_priv *dpriv = (struct device_priv *)handle->dev->os_priv;
+
+ usbi_dbg("configuration %d", config);
+
+ if (ioctl(dpriv->fd, USB_SET_CONFIG, &config) < 0)
+ return _errno_to_libusb(errno);
+
+ return _cache_active_config_descriptor(handle->dev, dpriv->fd);
+}
+
+int
+netbsd_claim_interface(struct libusb_device_handle *handle, int iface)
+{
+ struct handle_priv *hpriv = (struct handle_priv *)handle->os_priv;
+ int i;
+
+ for (i = 0; i < USB_MAX_ENDPOINTS; i++)
+ hpriv->endpoints[i] = -1;
+
+ return (LIBUSB_SUCCESS);
+}
+
+int
+netbsd_release_interface(struct libusb_device_handle *handle, int iface)
+{
+ struct handle_priv *hpriv = (struct handle_priv *)handle->os_priv;
+ int i;
+
+ for (i = 0; i < USB_MAX_ENDPOINTS; i++)
+ if (hpriv->endpoints[i] >= 0)
+ close(hpriv->endpoints[i]);
+
+ return (LIBUSB_SUCCESS);
+}
+
+int
+netbsd_set_interface_altsetting(struct libusb_device_handle *handle, int iface,
+ int altsetting)
+{
+ struct device_priv *dpriv = (struct device_priv *)handle->dev->os_priv;
+ struct usb_alt_interface intf;
+
+ usbi_dbg("iface %d, setting %d", iface, altsetting);
+
+ memset(&intf, 0, sizeof(intf));
+
+ intf.uai_interface_index = iface;
+ intf.uai_alt_no = altsetting;
+
+ if (ioctl(dpriv->fd, USB_SET_ALTINTERFACE, &intf) < 0)
+ return _errno_to_libusb(errno);
+
+ return (LIBUSB_SUCCESS);
+}
+
+int
+netbsd_clear_halt(struct libusb_device_handle *handle, unsigned char endpoint)
+{
+ struct device_priv *dpriv = (struct device_priv *)handle->dev->os_priv;
+ struct usb_ctl_request req;
+
+ usbi_dbg("");
+
+ req.ucr_request.bmRequestType = UT_WRITE_ENDPOINT;
+ req.ucr_request.bRequest = UR_CLEAR_FEATURE;
+ USETW(req.ucr_request.wValue, UF_ENDPOINT_HALT);
+ USETW(req.ucr_request.wIndex, endpoint);
+ USETW(req.ucr_request.wLength, 0);
+
+ if (ioctl(dpriv->fd, USB_DO_REQUEST, &req) < 0)
+ return _errno_to_libusb(errno);
+
+ return (LIBUSB_SUCCESS);
+}
+
+int
+netbsd_reset_device(struct libusb_device_handle *handle)
+{
+ usbi_dbg("");
+
+ return (LIBUSB_ERROR_NOT_SUPPORTED);
+}
+
+void
+netbsd_destroy_device(struct libusb_device *dev)
+{
+ struct device_priv *dpriv = (struct device_priv *)dev->os_priv;
+
+ usbi_dbg("");
+
+ free(dpriv->cdesc);
+}
+
+int
+netbsd_submit_transfer(struct usbi_transfer *itransfer)
+{
+ struct libusb_transfer *transfer;
+ struct handle_priv *hpriv;
+ int err = 0;
+
+ usbi_dbg("");
+
+ transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ hpriv = (struct handle_priv *)transfer->dev_handle->os_priv;
+
+ switch (transfer->type) {
+ case LIBUSB_TRANSFER_TYPE_CONTROL:
+ err = _sync_control_transfer(itransfer);
+ break;
+ case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
+ if (IS_XFEROUT(transfer)) {
+ /* Isochronous write is not supported */
+ err = LIBUSB_ERROR_NOT_SUPPORTED;
+ break;
+ }
+ err = _sync_gen_transfer(itransfer);
+ break;
+ case LIBUSB_TRANSFER_TYPE_BULK:
+ case LIBUSB_TRANSFER_TYPE_INTERRUPT:
+ if (IS_XFEROUT(transfer) &&
+ transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET) {
+ err = LIBUSB_ERROR_NOT_SUPPORTED;
+ break;
+ }
+ err = _sync_gen_transfer(itransfer);
+ break;
+ case LIBUSB_TRANSFER_TYPE_BULK_STREAM:
+ err = LIBUSB_ERROR_NOT_SUPPORTED;
+ break;
+ }
+
+ if (err)
+ return (err);
+
+ usbi_signal_transfer_completion(itransfer);
+
+ return (LIBUSB_SUCCESS);
+}
+
+int
+netbsd_cancel_transfer(struct usbi_transfer *itransfer)
+{
+ usbi_dbg("");
+
+ return (LIBUSB_ERROR_NOT_SUPPORTED);
+}
+
+void
+netbsd_clear_transfer_priv(struct usbi_transfer *itransfer)
+{
+ usbi_dbg("");
+
+ /* Nothing to do */
+}
+
+int
+netbsd_handle_transfer_completion(struct usbi_transfer *itransfer)
+{
+ return usbi_handle_transfer_completion(itransfer, LIBUSB_TRANSFER_COMPLETED);
+}
+
+int
+netbsd_clock_gettime(int clkid, struct timespec *tp)
+{
+ usbi_dbg("clock %d", clkid);
+
+ if (clkid == USBI_CLOCK_REALTIME)
+ return clock_gettime(CLOCK_REALTIME, tp);
+
+ if (clkid == USBI_CLOCK_MONOTONIC)
+ return clock_gettime(CLOCK_MONOTONIC, tp);
+
+ return (LIBUSB_ERROR_INVALID_PARAM);
+}
+
+int
+_errno_to_libusb(int err)
+{
+ switch (err) {
+ case EIO:
+ return (LIBUSB_ERROR_IO);
+ case EACCES:
+ return (LIBUSB_ERROR_ACCESS);
+ case ENOENT:
+ return (LIBUSB_ERROR_NO_DEVICE);
+ case ENOMEM:
+ return (LIBUSB_ERROR_NO_MEM);
+ }
+
+ usbi_dbg("error: %s", strerror(err));
+
+ return (LIBUSB_ERROR_OTHER);
+}
+
+int
+_cache_active_config_descriptor(struct libusb_device *dev, int fd)
+{
+ struct device_priv *dpriv = (struct device_priv *)dev->os_priv;
+ struct usb_config_desc ucd;
+ struct usb_full_desc ufd;
+ unsigned char* buf;
+ int len;
+
+ usbi_dbg("fd %d", fd);
+
+ ucd.ucd_config_index = USB_CURRENT_CONFIG_INDEX;
+
+ if ((ioctl(fd, USB_GET_CONFIG_DESC, &ucd)) < 0)
+ return _errno_to_libusb(errno);
+
+ usbi_dbg("active bLength %d", ucd.ucd_desc.bLength);
+
+ len = UGETW(ucd.ucd_desc.wTotalLength);
+ buf = malloc(len);
+ if (buf == NULL)
+ return (LIBUSB_ERROR_NO_MEM);
+
+ ufd.ufd_config_index = ucd.ucd_config_index;
+ ufd.ufd_size = len;
+ ufd.ufd_data = buf;
+
+ usbi_dbg("index %d, len %d", ufd.ufd_config_index, len);
+
+ if ((ioctl(fd, USB_GET_FULL_DESC, &ufd)) < 0) {
+ free(buf);
+ return _errno_to_libusb(errno);
+ }
+
+ if (dpriv->cdesc)
+ free(dpriv->cdesc);
+ dpriv->cdesc = buf;
+
+ return (0);
+}
+
+int
+_sync_control_transfer(struct usbi_transfer *itransfer)
+{
+ struct libusb_transfer *transfer;
+ struct libusb_control_setup *setup;
+ struct device_priv *dpriv;
+ struct usb_ctl_request req;
+
+ transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ dpriv = (struct device_priv *)transfer->dev_handle->dev->os_priv;
+ setup = (struct libusb_control_setup *)transfer->buffer;
+
+ usbi_dbg("type %d request %d value %d index %d length %d timeout %d",
+ setup->bmRequestType, setup->bRequest,
+ libusb_le16_to_cpu(setup->wValue),
+ libusb_le16_to_cpu(setup->wIndex),
+ libusb_le16_to_cpu(setup->wLength), transfer->timeout);
+
+ req.ucr_request.bmRequestType = setup->bmRequestType;
+ req.ucr_request.bRequest = setup->bRequest;
+ /* Don't use USETW, libusb already deals with the endianness */
+ (*(uint16_t *)req.ucr_request.wValue) = setup->wValue;
+ (*(uint16_t *)req.ucr_request.wIndex) = setup->wIndex;
+ (*(uint16_t *)req.ucr_request.wLength) = setup->wLength;
+ req.ucr_data = transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE;
+
+ if ((transfer->flags & LIBUSB_TRANSFER_SHORT_NOT_OK) == 0)
+ req.ucr_flags = USBD_SHORT_XFER_OK;
+
+ if ((ioctl(dpriv->fd, USB_SET_TIMEOUT, &transfer->timeout)) < 0)
+ return _errno_to_libusb(errno);
+
+ if ((ioctl(dpriv->fd, USB_DO_REQUEST, &req)) < 0)
+ return _errno_to_libusb(errno);
+
+ itransfer->transferred = req.ucr_actlen;
+
+ usbi_dbg("transferred %d", itransfer->transferred);
+
+ return (0);
+}
+
+int
+_access_endpoint(struct libusb_transfer *transfer)
+{
+ struct handle_priv *hpriv;
+ struct device_priv *dpriv;
+ char *s, devnode[16];
+ int fd, endpt;
+ mode_t mode;
+
+ hpriv = (struct handle_priv *)transfer->dev_handle->os_priv;
+ dpriv = (struct device_priv *)transfer->dev_handle->dev->os_priv;
+
+ endpt = UE_GET_ADDR(transfer->endpoint);
+ mode = IS_XFERIN(transfer) ? O_RDONLY : O_WRONLY;
+
+ usbi_dbg("endpoint %d mode %d", endpt, mode);
+
+ if (hpriv->endpoints[endpt] < 0) {
+ /* Pick the right node given the control one */
+ strlcpy(devnode, dpriv->devnode, sizeof(devnode));
+ s = strchr(devnode, '.');
+ snprintf(s, 4, ".%02d", endpt);
+
+ /* We may need to read/write to the same endpoint later. */
+ if (((fd = open(devnode, O_RDWR)) < 0) && (errno == ENXIO))
+ if ((fd = open(devnode, mode)) < 0)
+ return (-1);
+
+ hpriv->endpoints[endpt] = fd;
+ }
+
+ return (hpriv->endpoints[endpt]);
+}
+
+int
+_sync_gen_transfer(struct usbi_transfer *itransfer)
+{
+ struct libusb_transfer *transfer;
+ int fd, nr = 1;
+
+ transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+
+ /*
+ * Bulk, Interrupt or Isochronous transfer depends on the
+ * endpoint and thus the node to open.
+ */
+ if ((fd = _access_endpoint(transfer)) < 0)
+ return _errno_to_libusb(errno);
+
+ if ((ioctl(fd, USB_SET_TIMEOUT, &transfer->timeout)) < 0)
+ return _errno_to_libusb(errno);
+
+ if (IS_XFERIN(transfer)) {
+ if ((transfer->flags & LIBUSB_TRANSFER_SHORT_NOT_OK) == 0)
+ if ((ioctl(fd, USB_SET_SHORT_XFER, &nr)) < 0)
+ return _errno_to_libusb(errno);
+
+ nr = read(fd, transfer->buffer, transfer->length);
+ } else {
+ nr = write(fd, transfer->buffer, transfer->length);
+ }
+
+ if (nr < 0)
+ return _errno_to_libusb(errno);
+
+ itransfer->transferred = nr;
+
+ return (0);
+}
diff --git a/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/openbsd_usb.c b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/openbsd_usb.c
new file mode 100644
index 000000000..c66025711
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/openbsd_usb.c
@@ -0,0 +1,771 @@
+/*
+ * Copyright © 2011-2013 Martin Pieuchot <mpi@openbsd.org>
+ *
+ * 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 <sys/time.h>
+#include <sys/types.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <dev/usb/usb.h>
+
+#include "libusbi.h"
+
+struct device_priv {
+ char *devname; /* name of the ugen(4) node */
+ int fd; /* device file descriptor */
+
+ unsigned char *cdesc; /* active config descriptor */
+ usb_device_descriptor_t ddesc; /* usb device descriptor */
+};
+
+struct handle_priv {
+ int endpoints[USB_MAX_ENDPOINTS];
+};
+
+/*
+ * Backend functions
+ */
+static int obsd_get_device_list(struct libusb_context *,
+ struct discovered_devs **);
+static int obsd_open(struct libusb_device_handle *);
+static void obsd_close(struct libusb_device_handle *);
+
+static int obsd_get_device_descriptor(struct libusb_device *, unsigned char *,
+ int *);
+static int obsd_get_active_config_descriptor(struct libusb_device *,
+ unsigned char *, size_t, int *);
+static int obsd_get_config_descriptor(struct libusb_device *, uint8_t,
+ unsigned char *, size_t, int *);
+
+static int obsd_get_configuration(struct libusb_device_handle *, int *);
+static int obsd_set_configuration(struct libusb_device_handle *, int);
+
+static int obsd_claim_interface(struct libusb_device_handle *, int);
+static int obsd_release_interface(struct libusb_device_handle *, int);
+
+static int obsd_set_interface_altsetting(struct libusb_device_handle *, int,
+ int);
+static int obsd_clear_halt(struct libusb_device_handle *, unsigned char);
+static int obsd_reset_device(struct libusb_device_handle *);
+static void obsd_destroy_device(struct libusb_device *);
+
+static int obsd_submit_transfer(struct usbi_transfer *);
+static int obsd_cancel_transfer(struct usbi_transfer *);
+static void obsd_clear_transfer_priv(struct usbi_transfer *);
+static int obsd_handle_transfer_completion(struct usbi_transfer *);
+static int obsd_clock_gettime(int, struct timespec *);
+
+/*
+ * Private functions
+ */
+static int _errno_to_libusb(int);
+static int _cache_active_config_descriptor(struct libusb_device *);
+static int _sync_control_transfer(struct usbi_transfer *);
+static int _sync_gen_transfer(struct usbi_transfer *);
+static int _access_endpoint(struct libusb_transfer *);
+
+static int _bus_open(int);
+
+
+const struct usbi_os_backend openbsd_backend = {
+ "Synchronous OpenBSD backend",
+ 0,
+ NULL, /* init() */
+ NULL, /* exit() */
+ obsd_get_device_list,
+ NULL, /* hotplug_poll */
+ obsd_open,
+ obsd_close,
+
+ obsd_get_device_descriptor,
+ obsd_get_active_config_descriptor,
+ obsd_get_config_descriptor,
+ NULL, /* get_config_descriptor_by_value() */
+
+ obsd_get_configuration,
+ obsd_set_configuration,
+
+ obsd_claim_interface,
+ obsd_release_interface,
+
+ obsd_set_interface_altsetting,
+ obsd_clear_halt,
+ obsd_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() */
+
+ obsd_destroy_device,
+
+ obsd_submit_transfer,
+ obsd_cancel_transfer,
+ obsd_clear_transfer_priv,
+
+ NULL, /* handle_events() */
+ obsd_handle_transfer_completion,
+
+ obsd_clock_gettime,
+ sizeof(struct device_priv),
+ sizeof(struct handle_priv),
+ 0, /* transfer_priv_size */
+};
+
+#define DEVPATH "/dev/"
+#define USBDEV DEVPATH "usb"
+
+int
+obsd_get_device_list(struct libusb_context * ctx,
+ struct discovered_devs **discdevs)
+{
+ struct discovered_devs *ddd;
+ struct libusb_device *dev;
+ struct device_priv *dpriv;
+ struct usb_device_info di;
+ struct usb_device_ddesc dd;
+ unsigned long session_id;
+ char devices[USB_MAX_DEVICES];
+ char busnode[16];
+ char *udevname;
+ int fd, addr, i, j;
+
+ usbi_dbg("");
+
+ for (i = 0; i < 8; i++) {
+ snprintf(busnode, sizeof(busnode), USBDEV "%d", i);
+
+ if ((fd = open(busnode, O_RDWR)) < 0) {
+ if (errno != ENOENT && errno != ENXIO)
+ usbi_err(ctx, "could not open %s", busnode);
+ continue;
+ }
+
+ bzero(devices, sizeof(devices));
+ for (addr = 1; addr < USB_MAX_DEVICES; addr++) {
+ if (devices[addr])
+ continue;
+
+ di.udi_addr = addr;
+ if (ioctl(fd, USB_DEVICEINFO, &di) < 0)
+ continue;
+
+ /*
+ * XXX If ugen(4) is attached to the USB device
+ * it will be used.
+ */
+ udevname = NULL;
+ for (j = 0; j < USB_MAX_DEVNAMES; j++)
+ if (!strncmp("ugen", di.udi_devnames[j], 4)) {
+ udevname = strdup(di.udi_devnames[j]);
+ break;
+ }
+
+ session_id = (di.udi_bus << 8 | di.udi_addr);
+ dev = usbi_get_device_by_session_id(ctx, session_id);
+
+ if (dev == NULL) {
+ dev = usbi_alloc_device(ctx, session_id);
+ if (dev == NULL) {
+ close(fd);
+ return (LIBUSB_ERROR_NO_MEM);
+ }
+
+ dev->bus_number = di.udi_bus;
+ dev->device_address = di.udi_addr;
+ dev->speed = di.udi_speed;
+
+ dpriv = (struct device_priv *)dev->os_priv;
+ dpriv->fd = -1;
+ dpriv->cdesc = NULL;
+ dpriv->devname = udevname;
+
+ dd.udd_bus = di.udi_bus;
+ dd.udd_addr = di.udi_addr;
+ if (ioctl(fd, USB_DEVICE_GET_DDESC, &dd) < 0) {
+ libusb_unref_device(dev);
+ continue;
+ }
+ dpriv->ddesc = dd.udd_desc;
+
+ if (_cache_active_config_descriptor(dev)) {
+ libusb_unref_device(dev);
+ continue;
+ }
+
+ if (usbi_sanitize_device(dev)) {
+ libusb_unref_device(dev);
+ continue;
+ }
+ }
+
+ ddd = discovered_devs_append(*discdevs, dev);
+ if (ddd == NULL) {
+ close(fd);
+ return (LIBUSB_ERROR_NO_MEM);
+ }
+ libusb_unref_device(dev);
+
+ *discdevs = ddd;
+ devices[addr] = 1;
+ }
+
+ close(fd);
+ }
+
+ return (LIBUSB_SUCCESS);
+}
+
+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];
+
+ if (dpriv->devname) {
+ /*
+ * Only open ugen(4) attached devices read-write, all
+ * read-only operations are done through the bus node.
+ */
+ snprintf(devnode, sizeof(devnode), DEVPATH "%s.00",
+ dpriv->devname);
+ dpriv->fd = open(devnode, O_RDWR);
+ if (dpriv->fd < 0)
+ return _errno_to_libusb(errno);
+
+ usbi_dbg("open %s: fd %d", devnode, dpriv->fd);
+ }
+
+ return (LIBUSB_SUCCESS);
+}
+
+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) {
+ usbi_dbg("close: fd %d", dpriv->fd);
+
+ close(dpriv->fd);
+ dpriv->fd = -1;
+ }
+}
+
+int
+obsd_get_device_descriptor(struct libusb_device *dev, unsigned char *buf,
+ int *host_endian)
+{
+ struct device_priv *dpriv = (struct device_priv *)dev->os_priv;
+
+ usbi_dbg("");
+
+ memcpy(buf, &dpriv->ddesc, DEVICE_DESC_LENGTH);
+
+ *host_endian = 0;
+
+ return (LIBUSB_SUCCESS);
+}
+
+int
+obsd_get_active_config_descriptor(struct libusb_device *dev,
+ unsigned char *buf, size_t len, int *host_endian)
+{
+ struct device_priv *dpriv = (struct device_priv *)dev->os_priv;
+ usb_config_descriptor_t *ucd = (usb_config_descriptor_t *)dpriv->cdesc;
+
+ len = MIN(len, UGETW(ucd->wTotalLength));
+
+ usbi_dbg("len %d", len);
+
+ memcpy(buf, dpriv->cdesc, len);
+
+ *host_endian = 0;
+
+ return (len);
+}
+
+int
+obsd_get_config_descriptor(struct libusb_device *dev, uint8_t idx,
+ unsigned char *buf, size_t len, int *host_endian)
+{
+ struct usb_device_fdesc udf;
+ int fd, err;
+
+ if ((fd = _bus_open(dev->bus_number)) < 0)
+ return _errno_to_libusb(errno);
+
+ udf.udf_bus = dev->bus_number;
+ udf.udf_addr = dev->device_address;
+ udf.udf_config_index = idx;
+ udf.udf_size = len;
+ udf.udf_data = buf;
+
+ usbi_dbg("index %d, len %d", udf.udf_config_index, len);
+
+ if (ioctl(fd, USB_DEVICE_GET_FDESC, &udf) < 0) {
+ err = errno;
+ close(fd);
+ return _errno_to_libusb(err);
+ }
+ close(fd);
+
+ *host_endian = 0;
+
+ return (len);
+}
+
+int
+obsd_get_configuration(struct libusb_device_handle *handle, int *config)
+{
+ struct device_priv *dpriv = (struct device_priv *)handle->dev->os_priv;
+ usb_config_descriptor_t *ucd = (usb_config_descriptor_t *)dpriv->cdesc;
+
+ *config = ucd->bConfigurationValue;
+
+ usbi_dbg("bConfigurationValue %d", *config);
+
+ return (LIBUSB_SUCCESS);
+}
+
+int
+obsd_set_configuration(struct libusb_device_handle *handle, int config)
+{
+ struct device_priv *dpriv = (struct device_priv *)handle->dev->os_priv;
+
+ if (dpriv->devname == NULL)
+ return (LIBUSB_ERROR_NOT_SUPPORTED);
+
+ usbi_dbg("bConfigurationValue %d", config);
+
+ if (ioctl(dpriv->fd, USB_SET_CONFIG, &config) < 0)
+ return _errno_to_libusb(errno);
+
+ return _cache_active_config_descriptor(handle->dev);
+}
+
+int
+obsd_claim_interface(struct libusb_device_handle *handle, int iface)
+{
+ struct handle_priv *hpriv = (struct handle_priv *)handle->os_priv;
+ int i;
+
+ for (i = 0; i < USB_MAX_ENDPOINTS; i++)
+ hpriv->endpoints[i] = -1;
+
+ return (LIBUSB_SUCCESS);
+}
+
+int
+obsd_release_interface(struct libusb_device_handle *handle, int iface)
+{
+ struct handle_priv *hpriv = (struct handle_priv *)handle->os_priv;
+ int i;
+
+ for (i = 0; i < USB_MAX_ENDPOINTS; i++)
+ if (hpriv->endpoints[i] >= 0)
+ close(hpriv->endpoints[i]);
+
+ return (LIBUSB_SUCCESS);
+}
+
+int
+obsd_set_interface_altsetting(struct libusb_device_handle *handle, int iface,
+ int altsetting)
+{
+ struct device_priv *dpriv = (struct device_priv *)handle->dev->os_priv;
+ struct usb_alt_interface intf;
+
+ if (dpriv->devname == NULL)
+ return (LIBUSB_ERROR_NOT_SUPPORTED);
+
+ usbi_dbg("iface %d, setting %d", iface, altsetting);
+
+ memset(&intf, 0, sizeof(intf));
+
+ intf.uai_interface_index = iface;
+ intf.uai_alt_no = altsetting;
+
+ if (ioctl(dpriv->fd, USB_SET_ALTINTERFACE, &intf) < 0)
+ return _errno_to_libusb(errno);
+
+ return (LIBUSB_SUCCESS);
+}
+
+int
+obsd_clear_halt(struct libusb_device_handle *handle, unsigned char endpoint)
+{
+ struct usb_ctl_request req;
+ int fd, err;
+
+ if ((fd = _bus_open(handle->dev->bus_number)) < 0)
+ return _errno_to_libusb(errno);
+
+ usbi_dbg("");
+
+ req.ucr_addr = handle->dev->device_address;
+ req.ucr_request.bmRequestType = UT_WRITE_ENDPOINT;
+ req.ucr_request.bRequest = UR_CLEAR_FEATURE;
+ USETW(req.ucr_request.wValue, UF_ENDPOINT_HALT);
+ USETW(req.ucr_request.wIndex, endpoint);
+ USETW(req.ucr_request.wLength, 0);
+
+ if (ioctl(fd, USB_REQUEST, &req) < 0) {
+ err = errno;
+ close(fd);
+ return _errno_to_libusb(err);
+ }
+ close(fd);
+
+ return (LIBUSB_SUCCESS);
+}
+
+int
+obsd_reset_device(struct libusb_device_handle *handle)
+{
+ usbi_dbg("");
+
+ return (LIBUSB_ERROR_NOT_SUPPORTED);
+}
+
+void
+obsd_destroy_device(struct libusb_device *dev)
+{
+ struct device_priv *dpriv = (struct device_priv *)dev->os_priv;
+
+ usbi_dbg("");
+
+ free(dpriv->cdesc);
+ free(dpriv->devname);
+}
+
+int
+obsd_submit_transfer(struct usbi_transfer *itransfer)
+{
+ struct libusb_transfer *transfer;
+ struct handle_priv *hpriv;
+ int err = 0;
+
+ usbi_dbg("");
+
+ transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ hpriv = (struct handle_priv *)transfer->dev_handle->os_priv;
+
+ switch (transfer->type) {
+ case LIBUSB_TRANSFER_TYPE_CONTROL:
+ err = _sync_control_transfer(itransfer);
+ break;
+ case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
+ if (IS_XFEROUT(transfer)) {
+ /* Isochronous write is not supported */
+ err = LIBUSB_ERROR_NOT_SUPPORTED;
+ break;
+ }
+ err = _sync_gen_transfer(itransfer);
+ break;
+ case LIBUSB_TRANSFER_TYPE_BULK:
+ case LIBUSB_TRANSFER_TYPE_INTERRUPT:
+ if (IS_XFEROUT(transfer) &&
+ transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET) {
+ err = LIBUSB_ERROR_NOT_SUPPORTED;
+ break;
+ }
+ err = _sync_gen_transfer(itransfer);
+ break;
+ case LIBUSB_TRANSFER_TYPE_BULK_STREAM:
+ err = LIBUSB_ERROR_NOT_SUPPORTED;
+ break;
+ }
+
+ if (err)
+ return (err);
+
+ usbi_signal_transfer_completion(itransfer);
+
+ return (LIBUSB_SUCCESS);
+}
+
+int
+obsd_cancel_transfer(struct usbi_transfer *itransfer)
+{
+ usbi_dbg("");
+
+ return (LIBUSB_ERROR_NOT_SUPPORTED);
+}
+
+void
+obsd_clear_transfer_priv(struct usbi_transfer *itransfer)
+{
+ usbi_dbg("");
+
+ /* Nothing to do */
+}
+
+int
+obsd_handle_transfer_completion(struct usbi_transfer *itransfer)
+{
+ return usbi_handle_transfer_completion(itransfer, LIBUSB_TRANSFER_COMPLETED);
+}
+
+int
+obsd_clock_gettime(int clkid, struct timespec *tp)
+{
+ usbi_dbg("clock %d", clkid);
+
+ if (clkid == USBI_CLOCK_REALTIME)
+ return clock_gettime(CLOCK_REALTIME, tp);
+
+ if (clkid == USBI_CLOCK_MONOTONIC)
+ return clock_gettime(CLOCK_MONOTONIC, tp);
+
+ return (LIBUSB_ERROR_INVALID_PARAM);
+}
+
+int
+_errno_to_libusb(int err)
+{
+ usbi_dbg("error: %s (%d)", strerror(err), err);
+
+ switch (err) {
+ case EIO:
+ return (LIBUSB_ERROR_IO);
+ case EACCES:
+ return (LIBUSB_ERROR_ACCESS);
+ case ENOENT:
+ return (LIBUSB_ERROR_NO_DEVICE);
+ case ENOMEM:
+ return (LIBUSB_ERROR_NO_MEM);
+ case ETIMEDOUT:
+ return (LIBUSB_ERROR_TIMEOUT);
+ }
+
+ return (LIBUSB_ERROR_OTHER);
+}
+
+int
+_cache_active_config_descriptor(struct libusb_device *dev)
+{
+ struct device_priv *dpriv = (struct device_priv *)dev->os_priv;
+ struct usb_device_cdesc udc;
+ struct usb_device_fdesc udf;
+ unsigned char* buf;
+ int fd, len, err;
+
+ if ((fd = _bus_open(dev->bus_number)) < 0)
+ return _errno_to_libusb(errno);
+
+ usbi_dbg("fd %d, addr %d", fd, dev->device_address);
+
+ udc.udc_bus = dev->bus_number;
+ udc.udc_addr = dev->device_address;
+ udc.udc_config_index = USB_CURRENT_CONFIG_INDEX;
+ if (ioctl(fd, USB_DEVICE_GET_CDESC, &udc) < 0) {
+ err = errno;
+ close(fd);
+ return _errno_to_libusb(errno);
+ }
+
+ usbi_dbg("active bLength %d", udc.udc_desc.bLength);
+
+ len = UGETW(udc.udc_desc.wTotalLength);
+ buf = malloc(len);
+ if (buf == NULL)
+ return (LIBUSB_ERROR_NO_MEM);
+
+ udf.udf_bus = dev->bus_number;
+ udf.udf_addr = dev->device_address;
+ udf.udf_config_index = udc.udc_config_index;
+ udf.udf_size = len;
+ udf.udf_data = buf;
+
+ usbi_dbg("index %d, len %d", udf.udf_config_index, len);
+
+ if (ioctl(fd, USB_DEVICE_GET_FDESC, &udf) < 0) {
+ err = errno;
+ close(fd);
+ free(buf);
+ return _errno_to_libusb(err);
+ }
+ close(fd);
+
+ if (dpriv->cdesc)
+ free(dpriv->cdesc);
+ dpriv->cdesc = buf;
+
+ return (LIBUSB_SUCCESS);
+}
+
+int
+_sync_control_transfer(struct usbi_transfer *itransfer)
+{
+ struct libusb_transfer *transfer;
+ struct libusb_control_setup *setup;
+ struct device_priv *dpriv;
+ struct usb_ctl_request req;
+
+ transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ dpriv = (struct device_priv *)transfer->dev_handle->dev->os_priv;
+ setup = (struct libusb_control_setup *)transfer->buffer;
+
+ usbi_dbg("type %x request %x value %x index %d length %d timeout %d",
+ setup->bmRequestType, setup->bRequest,
+ libusb_le16_to_cpu(setup->wValue),
+ libusb_le16_to_cpu(setup->wIndex),
+ libusb_le16_to_cpu(setup->wLength), transfer->timeout);
+
+ req.ucr_addr = transfer->dev_handle->dev->device_address;
+ req.ucr_request.bmRequestType = setup->bmRequestType;
+ req.ucr_request.bRequest = setup->bRequest;
+ /* Don't use USETW, libusb already deals with the endianness */
+ (*(uint16_t *)req.ucr_request.wValue) = setup->wValue;
+ (*(uint16_t *)req.ucr_request.wIndex) = setup->wIndex;
+ (*(uint16_t *)req.ucr_request.wLength) = setup->wLength;
+ req.ucr_data = transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE;
+
+ if ((transfer->flags & LIBUSB_TRANSFER_SHORT_NOT_OK) == 0)
+ req.ucr_flags = USBD_SHORT_XFER_OK;
+
+ if (dpriv->devname == NULL) {
+ /*
+ * XXX If the device is not attached to ugen(4) it is
+ * XXX still possible to submit a control transfer but
+ * XXX with the default timeout only.
+ */
+ int fd, err;
+
+ if ((fd = _bus_open(transfer->dev_handle->dev->bus_number)) < 0)
+ return _errno_to_libusb(errno);
+
+ if ((ioctl(fd, USB_REQUEST, &req)) < 0) {
+ err = errno;
+ close(fd);
+ return _errno_to_libusb(err);
+ }
+ close(fd);
+ } else {
+ if ((ioctl(dpriv->fd, USB_SET_TIMEOUT, &transfer->timeout)) < 0)
+ return _errno_to_libusb(errno);
+
+ if ((ioctl(dpriv->fd, USB_DO_REQUEST, &req)) < 0)
+ return _errno_to_libusb(errno);
+ }
+
+ itransfer->transferred = req.ucr_actlen;
+
+ usbi_dbg("transferred %d", itransfer->transferred);
+
+ return (0);
+}
+
+int
+_access_endpoint(struct libusb_transfer *transfer)
+{
+ struct handle_priv *hpriv;
+ struct device_priv *dpriv;
+ char devnode[16];
+ int fd, endpt;
+ mode_t mode;
+
+ hpriv = (struct handle_priv *)transfer->dev_handle->os_priv;
+ dpriv = (struct device_priv *)transfer->dev_handle->dev->os_priv;
+
+ endpt = UE_GET_ADDR(transfer->endpoint);
+ mode = IS_XFERIN(transfer) ? O_RDONLY : O_WRONLY;
+
+ usbi_dbg("endpoint %d mode %d", endpt, mode);
+
+ if (hpriv->endpoints[endpt] < 0) {
+ /* Pick the right endpoint node */
+ snprintf(devnode, sizeof(devnode), DEVPATH "%s.%02d",
+ dpriv->devname, endpt);
+
+ /* We may need to read/write to the same endpoint later. */
+ if (((fd = open(devnode, O_RDWR)) < 0) && (errno == ENXIO))
+ if ((fd = open(devnode, mode)) < 0)
+ return (-1);
+
+ hpriv->endpoints[endpt] = fd;
+ }
+
+ return (hpriv->endpoints[endpt]);
+}
+
+int
+_sync_gen_transfer(struct usbi_transfer *itransfer)
+{
+ struct libusb_transfer *transfer;
+ struct device_priv *dpriv;
+ int fd, nr = 1;
+
+ transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ dpriv = (struct device_priv *)transfer->dev_handle->dev->os_priv;
+
+ if (dpriv->devname == NULL)
+ return (LIBUSB_ERROR_NOT_SUPPORTED);
+
+ /*
+ * Bulk, Interrupt or Isochronous transfer depends on the
+ * endpoint and thus the node to open.
+ */
+ if ((fd = _access_endpoint(transfer)) < 0)
+ return _errno_to_libusb(errno);
+
+ if ((ioctl(fd, USB_SET_TIMEOUT, &transfer->timeout)) < 0)
+ return _errno_to_libusb(errno);
+
+ if (IS_XFERIN(transfer)) {
+ if ((transfer->flags & LIBUSB_TRANSFER_SHORT_NOT_OK) == 0)
+ if ((ioctl(fd, USB_SET_SHORT_XFER, &nr)) < 0)
+ return _errno_to_libusb(errno);
+
+ nr = read(fd, transfer->buffer, transfer->length);
+ } else {
+ nr = write(fd, transfer->buffer, transfer->length);
+ }
+
+ if (nr < 0)
+ return _errno_to_libusb(errno);
+
+ itransfer->transferred = nr;
+
+ return (0);
+}
+
+int
+_bus_open(int number)
+{
+ char busnode[16];
+
+ snprintf(busnode, sizeof(busnode), USBDEV "%d", number);
+
+ return open(busnode, O_RDWR);
+}
diff --git a/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/poll_posix.c b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/poll_posix.c
new file mode 100644
index 000000000..e2f55a57a
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/poll_posix.c
@@ -0,0 +1,53 @@
+/*
+ * poll_posix: poll compatibility wrapper for POSIX systems
+ * Copyright © 2013 RealVNC Ltd.
+ *
+ * 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 <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#include "libusbi.h"
+
+int usbi_pipe(int pipefd[2])
+{
+ int ret = pipe(pipefd);
+ if (ret != 0) {
+ return ret;
+ }
+ ret = fcntl(pipefd[1], F_GETFL);
+ if (ret == -1) {
+ usbi_dbg("Failed to get pipe fd 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);
+ goto err_close_pipe;
+ }
+
+ return 0;
+
+err_close_pipe:
+ usbi_close(pipefd[0]);
+ usbi_close(pipefd[1]);
+ return ret;
+}
diff --git a/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/poll_posix.h b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/poll_posix.h
new file mode 100644
index 000000000..5b4b2c905
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/poll_posix.h
@@ -0,0 +1,11 @@
+#ifndef LIBUSB_POLL_POSIX_H
+#define LIBUSB_POLL_POSIX_H
+
+#define usbi_write write
+#define usbi_read read
+#define usbi_close close
+#define usbi_poll poll
+
+int usbi_pipe(int pipefd[2]);
+
+#endif /* LIBUSB_POLL_POSIX_H */
diff --git a/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/poll_windows.c b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/poll_windows.c
new file mode 100644
index 000000000..982560751
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/poll_windows.c
@@ -0,0 +1,728 @@
+/*
+ * 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;
+}
diff --git a/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/poll_windows.h b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/poll_windows.h
new file mode 100644
index 000000000..aa4c985da
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/poll_windows.h
@@ -0,0 +1,131 @@
+/*
+ * Windows compat: POSIX compatibility wrapper
+ * 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
+ *
+ */
+#pragma once
+
+#if defined(_MSC_VER)
+// disable /W4 MSVC warnings that are benign
+#pragma warning(disable:4127) // conditional expression is constant
+#endif
+
+// Handle synchronous completion through the overlapped structure
+#if !defined(STATUS_REPARSE) // reuse the REPARSE status code
+#define STATUS_REPARSE ((LONG)0x00000104L)
+#endif
+#define STATUS_COMPLETED_SYNCHRONOUSLY STATUS_REPARSE
+#if defined(_WIN32_WCE)
+// WinCE doesn't have a HasOverlappedIoCompleted() macro, so attempt to emulate it
+#define HasOverlappedIoCompleted(lpOverlapped) (((DWORD)(lpOverlapped)->Internal) != STATUS_PENDING)
+#endif
+#define HasOverlappedIoCompletedSync(lpOverlapped) (((DWORD)(lpOverlapped)->Internal) == STATUS_COMPLETED_SYNCHRONOUSLY)
+
+#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 */
+#define POLLPRI 0x0002 /* There is urgent data to read */
+#define POLLOUT 0x0004 /* Writing now will not block */
+#define POLLERR 0x0008 /* Error condition */
+#define POLLHUP 0x0010 /* Hung up */
+#define POLLNVAL 0x0020 /* Invalid request: fd not open */
+
+struct pollfd {
+ int fd; /* file descriptor */
+ short events; /* requested events */
+ short revents; /* returned events */
+};
+
+// 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;
+
+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
+ */
+#if defined(DDKBUILD)
+#include <winsock.h> // defines timeval functions on DDK
+#endif
+
+#if !defined(TIMESPEC_TO_TIMEVAL)
+#define TIMESPEC_TO_TIMEVAL(tv, ts) { \
+ (tv)->tv_sec = (long)(ts)->tv_sec; \
+ (tv)->tv_usec = (long)(ts)->tv_nsec / 1000; \
+}
+#endif
+#if !defined(timersub)
+#define timersub(a, b, result) \
+do { \
+ (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
+ (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
+ if ((result)->tv_usec < 0) { \
+ --(result)->tv_sec; \
+ (result)->tv_usec += 1000000; \
+ } \
+} while (0)
+#endif
diff --git a/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/sunos_usb.c b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/sunos_usb.c
new file mode 100644
index 000000000..cb608976b
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/sunos_usb.c
@@ -0,0 +1,1292 @@
+/*
+ *
+ * Copyright (c) 2016, Oracle and/or its affiliates.
+ *
+ * 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 <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <strings.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <aio.h>
+#include <libdevinfo.h>
+#include <sys/usb/clients/ugen/usb_ugen.h>
+#include <sys/usb/usba.h>
+#include <sys/pci.h>
+
+#include "libusbi.h"
+#include "sunos_usb.h"
+
+/*
+ * Backend functions
+ */
+static int sunos_init(struct libusb_context *);
+static void sunos_exit(void);
+static int sunos_get_device_list(struct libusb_context *,
+ struct discovered_devs **);
+static int sunos_open(struct libusb_device_handle *);
+static void sunos_close(struct libusb_device_handle *);
+static int sunos_get_device_descriptor(struct libusb_device *,
+ uint8_t*, int *);
+static int sunos_get_active_config_descriptor(struct libusb_device *,
+ uint8_t*, size_t, int *);
+static int sunos_get_config_descriptor(struct libusb_device *, uint8_t,
+ uint8_t*, size_t, int *);
+static int sunos_get_configuration(struct libusb_device_handle *, int *);
+static int sunos_set_configuration(struct libusb_device_handle *, int);
+static int sunos_claim_interface(struct libusb_device_handle *, int);
+static int sunos_release_interface(struct libusb_device_handle *, int);
+static int sunos_set_interface_altsetting(struct libusb_device_handle *,
+ int, int);
+static int sunos_clear_halt(struct libusb_device_handle *, uint8_t);
+static int sunos_reset_device(struct libusb_device_handle *);
+static void sunos_destroy_device(struct libusb_device *);
+static int sunos_submit_transfer(struct usbi_transfer *);
+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 *);
+
+/*
+ * Private functions
+ */
+static int _errno_to_libusb(int);
+static int sunos_usb_get_status(int fd);
+
+static int sunos_init(struct libusb_context *ctx)
+{
+ return (LIBUSB_SUCCESS);
+}
+
+static void sunos_exit(void)
+{
+ usbi_dbg("");
+}
+
+static int
+sunos_fill_in_dev_info(di_node_t node, struct libusb_device *dev)
+{
+ int proplen;
+ int n, *addr, *port_prop;
+ char *phypath;
+ uint8_t *rdata;
+ struct libusb_device_descriptor *descr;
+ sunos_dev_priv_t *dpriv = (sunos_dev_priv_t *)dev->os_priv;
+
+ /* Device descriptors */
+ proplen = di_prop_lookup_bytes(DDI_DEV_T_ANY, node,
+ "usb-dev-descriptor", &rdata);
+ if (proplen <= 0) {
+
+ return (LIBUSB_ERROR_IO);
+ }
+
+ descr = (struct libusb_device_descriptor *)rdata;
+ bcopy(descr, &dpriv->dev_descr, LIBUSB_DT_DEVICE_SIZE);
+ dpriv->dev_descr.bcdUSB = libusb_cpu_to_le16(descr->bcdUSB);
+ dpriv->dev_descr.idVendor = libusb_cpu_to_le16(descr->idVendor);
+ dpriv->dev_descr.idProduct = libusb_cpu_to_le16(descr->idProduct);
+ dpriv->dev_descr.bcdDevice = libusb_cpu_to_le16(descr->bcdDevice);
+
+ /* Raw configuration descriptors */
+ proplen = di_prop_lookup_bytes(DDI_DEV_T_ANY, node,
+ "usb-raw-cfg-descriptors", &rdata);
+ if (proplen <= 0) {
+ usbi_dbg("can't find raw config descriptors");
+
+ return (LIBUSB_ERROR_IO);
+ }
+ dpriv->raw_cfgdescr = calloc(1, proplen);
+ if (dpriv->raw_cfgdescr == NULL) {
+ return (LIBUSB_ERROR_NO_MEM);
+ } else {
+ bcopy(rdata, dpriv->raw_cfgdescr, proplen);
+ dpriv->cfgvalue = ((struct libusb_config_descriptor *)
+ rdata)->bConfigurationValue;
+ }
+
+ n = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "reg", &port_prop);
+
+ if ((n != 1) || (*port_prop <= 0)) {
+ return (LIBUSB_ERROR_IO);
+ }
+ dev->port_number = *port_prop;
+
+ /* device physical path */
+ phypath = di_devfs_path(node);
+ if (phypath) {
+ dpriv->phypath = strdup(phypath);
+ di_devfs_path_free(phypath);
+ } else {
+ free(dpriv->raw_cfgdescr);
+
+ return (LIBUSB_ERROR_IO);
+ }
+
+ /* address */
+ n = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "assigned-address", &addr);
+ if (n != 1 || *addr == 0) {
+ usbi_dbg("can't get address");
+ } else {
+ dev->device_address = *addr;
+ }
+
+ /* speed */
+ if (di_prop_exists(DDI_DEV_T_ANY, node, "low-speed") == 1) {
+ dev->speed = LIBUSB_SPEED_LOW;
+ } else if (di_prop_exists(DDI_DEV_T_ANY, node, "high-speed") == 1) {
+ dev->speed = LIBUSB_SPEED_HIGH;
+ } else if (di_prop_exists(DDI_DEV_T_ANY, node, "full-speed") == 1) {
+ dev->speed = LIBUSB_SPEED_FULL;
+ } else if (di_prop_exists(DDI_DEV_T_ANY, node, "super-speed") == 1) {
+ dev->speed = LIBUSB_SPEED_SUPER;
+ }
+
+ usbi_dbg("vid=%x pid=%x, path=%s, bus_nmber=0x%x, port_number=%d, "
+ "speed=%d", dpriv->dev_descr.idVendor, dpriv->dev_descr.idProduct,
+ dpriv->phypath, dev->bus_number, dev->port_number, dev->speed);
+
+ 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;
+ uint64_t session_id = 0;
+ uint16_t bdf = 0;
+ struct libusb_device *dev;
+ sunos_dev_priv_t *devpriv;
+ const char *path, *newpath;
+ int n, i;
+ 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 **)&regbuf);
+ reg = regbuf[0];
+ bdf = (PCI_REG_BUS_G(reg) << 8) |
+ (PCI_REG_DEV_G(reg) << 3) | PCI_REG_FUNC_G(reg);
+ session_id |= (bdf << i * 8);
+
+ /* same as 'unit-address' property */
+ bus_number =
+ (PCI_REG_DEV_G(reg) << 3) | PCI_REG_FUNC_G(reg);
+
+ usbi_dbg("device bus address=%s:%x",
+ di_bus_addr(pnode), bus_number);
+
+ break;
+ }
+
+ /* usb_addr */
+ n = di_prop_lookup_ints(DDI_DEV_T_ANY, pnode,
+ "assigned-address", &addr_prop);
+ if ((n != 1) || (addr_prop[0] == 0)) {
+ usbi_dbg("cannot get valid usb_addr");
+
+ return (DI_WALK_CONTINUE);
+ }
+
+ session_id |= ((addr_prop[0] & 0xff) << i * 8);
+ if (++i > 7)
+ break;
+
+ pnode = di_parent_node(pnode);
+ }
+
+ path = di_devlink_path(link);
+ dev = usbi_get_device_by_session_id(nargs->ctx, session_id);
+ if (dev == NULL) {
+ dev = usbi_alloc_device(nargs->ctx, session_id);
+ if (dev == NULL) {
+ usbi_dbg("can't alloc device");
+
+ return (DI_WALK_TERMINATE);
+ }
+ devpriv = (sunos_dev_priv_t *)dev->os_priv;
+ if ((newpath = strrchr(path, '/')) == NULL) {
+ libusb_unref_device(dev);
+
+ return (DI_WALK_TERMINATE);
+ }
+ devpriv->ugenpath = strndup(path, strlen(path) -
+ strlen(newpath));
+ dev->bus_number = bus_number;
+
+ if (sunos_fill_in_dev_info(myself, dev) != LIBUSB_SUCCESS) {
+ libusb_unref_device(dev);
+
+ return (DI_WALK_TERMINATE);
+ }
+ if (usbi_sanitize_device(dev) < 0) {
+ libusb_unref_device(dev);
+ usbi_dbg("sanatize failed: ");
+ return (DI_WALK_TERMINATE);
+ }
+ } else {
+ usbi_dbg("Dev %s exists", path);
+ }
+
+ devpriv = (sunos_dev_priv_t *)dev->os_priv;
+ if (nargs->last_ugenpath == NULL) {
+ /* first device */
+ nargs->last_ugenpath = devpriv->ugenpath;
+
+ if (discovered_devs_append(*(nargs->discdevs), dev) == NULL) {
+ usbi_dbg("cannot append device");
+ }
+
+ /*
+ * we alloc and hence ref this dev. We don't need to ref it
+ * hereafter. Front end or app should take care of their ref.
+ */
+ libusb_unref_device(dev);
+ }
+
+ usbi_dbg("Device %s %s id=0x%llx, devcount:%d, bdf=%x",
+ devpriv->ugenpath, path, (uint64_t)session_id,
+ (*nargs->discdevs)->len, bdf);
+
+ return (DI_WALK_CONTINUE);
+}
+
+static int
+sunos_walk_minor_node_link(di_node_t node, void *args)
+{
+ di_minor_t minor = DI_MINOR_NIL;
+ char *minor_path;
+ struct devlink_cbarg arg;
+ struct node_args *nargs = (struct node_args *)args;
+ di_devlink_handle_t devlink_hdl = nargs->dlink_hdl;
+
+ /* walk each minor to find ugen 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,
+ DI_PRIMARY_LINK, (void *)&arg, sunos_add_devices);
+ di_devfs_path_free(minor_path);
+ }
+
+ /* switch to a different node */
+ nargs->last_ugenpath = NULL;
+
+ return (DI_WALK_CONTINUE);
+}
+
+int
+sunos_get_device_list(struct libusb_context * ctx,
+ struct discovered_devs **discdevs)
+{
+ di_node_t root_node;
+ struct node_args args;
+ di_devlink_handle_t devlink_hdl;
+
+ args.ctx = ctx;
+ args.discdevs = discdevs;
+ args.last_ugenpath = NULL;
+ if ((root_node = di_init("/", DINFOCPYALL)) == DI_NODE_NIL) {
+ usbi_dbg("di_int() failed: %s", strerror(errno));
+ return (LIBUSB_ERROR_IO);
+ }
+
+ if ((devlink_hdl = di_devlink_init(NULL, 0)) == NULL) {
+ di_fini(root_node);
+ usbi_dbg("di_devlink_init() failed: %s", strerror(errno));
+
+ return (LIBUSB_ERROR_IO);
+ }
+ args.dlink_hdl = devlink_hdl;
+
+ /* walk each node to find USB devices */
+ if (di_walk_node(root_node, DI_WALK_SIBFIRST, &args,
+ sunos_walk_minor_node_link) == -1) {
+ usbi_dbg("di_walk_node() failed: %s", strerror(errno));
+ di_fini(root_node);
+
+ return (LIBUSB_ERROR_IO);
+ }
+
+ di_fini(root_node);
+ di_devlink_fini(&devlink_hdl);
+
+ usbi_dbg("%d devices", (*discdevs)->len);
+
+ return ((*discdevs)->len);
+}
+
+static int
+sunos_usb_open_ep0(sunos_dev_handle_priv_t *hpriv, sunos_dev_priv_t *dpriv)
+{
+ char filename[PATH_MAX + 1];
+
+ if (hpriv->eps[0].datafd > 0) {
+
+ return (LIBUSB_SUCCESS);
+ }
+ snprintf(filename, PATH_MAX, "%s/cntrl0", dpriv->ugenpath);
+
+ usbi_dbg("opening %s", filename);
+ hpriv->eps[0].datafd = open(filename, O_RDWR);
+ if (hpriv->eps[0].datafd < 0) {
+ return(_errno_to_libusb(errno));
+ }
+
+ snprintf(filename, PATH_MAX, "%s/cntrl0stat", dpriv->ugenpath);
+ hpriv->eps[0].statfd = open(filename, O_RDONLY);
+ if (hpriv->eps[0].statfd < 0) {
+ close(hpriv->eps[0].datafd);
+ hpriv->eps[0].datafd = -1;
+
+ return(_errno_to_libusb(errno));
+ }
+
+ return (LIBUSB_SUCCESS);
+}
+
+static void
+sunos_usb_close_all_eps(sunos_dev_handle_priv_t *hdev)
+{
+ int i;
+
+ /* not close ep0 */
+ for (i = 1; i < USB_MAXENDPOINTS; i++) {
+ if (hdev->eps[i].datafd != -1) {
+ (void) close(hdev->eps[i].datafd);
+ hdev->eps[i].datafd = -1;
+ }
+ if (hdev->eps[i].statfd != -1) {
+ (void) close(hdev->eps[i].statfd);
+ hdev->eps[i].statfd = -1;
+ }
+ }
+}
+
+static void
+sunos_usb_close_ep0(sunos_dev_handle_priv_t *hdev, sunos_dev_priv_t *dpriv)
+{
+ if (hdev->eps[0].datafd >= 0) {
+ close(hdev->eps[0].datafd);
+ close(hdev->eps[0].statfd);
+ hdev->eps[0].datafd = -1;
+ hdev->eps[0].statfd = -1;
+ }
+}
+
+static uchar_t
+sunos_usb_ep_index(uint8_t ep_addr)
+{
+ return ((ep_addr & LIBUSB_ENDPOINT_ADDRESS_MASK) +
+ ((ep_addr & LIBUSB_ENDPOINT_DIR_MASK) ? 16 : 0));
+}
+
+static int
+sunos_find_interface(struct libusb_device_handle *hdev,
+ uint8_t endpoint, uint8_t *interface)
+{
+ struct libusb_config_descriptor *config;
+ int r;
+ int iface_idx;
+
+ r = libusb_get_active_config_descriptor(hdev->dev, &config);
+ if (r < 0) {
+ return (LIBUSB_ERROR_INVALID_PARAM);
+ }
+
+ for (iface_idx = 0; iface_idx < config->bNumInterfaces; iface_idx++) {
+ const struct libusb_interface *iface =
+ &config->interface[iface_idx];
+ int altsetting_idx;
+
+ for (altsetting_idx = 0; altsetting_idx < iface->num_altsetting;
+ altsetting_idx++) {
+ const struct libusb_interface_descriptor *altsetting =
+ &iface->altsetting[altsetting_idx];
+ int ep_idx;
+
+ for (ep_idx = 0; ep_idx < altsetting->bNumEndpoints;
+ ep_idx++) {
+ const struct libusb_endpoint_descriptor *ep =
+ &altsetting->endpoint[ep_idx];
+ if (ep->bEndpointAddress == endpoint) {
+ *interface = iface_idx;
+ libusb_free_config_descriptor(config);
+
+ return (LIBUSB_SUCCESS);
+ }
+ }
+ }
+ }
+ libusb_free_config_descriptor(config);
+
+ return (LIBUSB_ERROR_INVALID_PARAM);
+}
+
+static int
+sunos_check_device_and_status_open(struct libusb_device_handle *hdl,
+ uint8_t ep_addr, int ep_type)
+{
+ char filename[PATH_MAX + 1], statfilename[PATH_MAX + 1];
+ char cfg_num[16], alt_num[16];
+ int fd, fdstat, mode;
+ uint8_t ifc = 0;
+ uint8_t ep_index;
+ sunos_dev_handle_priv_t *hpriv;
+
+ usbi_dbg("open ep 0x%02x", ep_addr);
+ hpriv = (sunos_dev_handle_priv_t *)hdl->os_priv;
+ ep_index = sunos_usb_ep_index(ep_addr);
+ /* ep already opened */
+ if ((hpriv->eps[ep_index].datafd > 0) &&
+ (hpriv->eps[ep_index].statfd > 0)) {
+ usbi_dbg("ep 0x%02x already opened, return success",
+ ep_addr);
+
+ return (0);
+ }
+
+ if (sunos_find_interface(hdl, ep_addr, &ifc) < 0) {
+ usbi_dbg("can't find interface for endpoint 0x%02x",
+ ep_addr);
+
+ return (LIBUSB_ERROR_ACCESS);
+ }
+
+ /* create filename */
+ if (hpriv->config_index > 0) {
+ (void) snprintf(cfg_num, sizeof (cfg_num), "cfg%d",
+ hpriv->config_index + 1);
+ } else {
+ bzero(cfg_num, sizeof (cfg_num));
+ }
+
+ if (hpriv->altsetting[ifc] > 0) {
+ (void) snprintf(alt_num, sizeof (alt_num), ".%d",
+ hpriv->altsetting[ifc]);
+ } else {
+ bzero(alt_num, sizeof (alt_num));
+ }
+
+ (void) snprintf(filename, PATH_MAX, "%s/%sif%d%s%s%d",
+ hpriv->dpriv->ugenpath, cfg_num, ifc, alt_num,
+ (ep_addr & LIBUSB_ENDPOINT_DIR_MASK) ? "in" :
+ "out", (ep_addr & LIBUSB_ENDPOINT_ADDRESS_MASK));
+ (void) snprintf(statfilename, PATH_MAX, "%sstat", filename);
+
+ /*
+ * for interrupt IN endpoints, we need to enable one xfer
+ * mode before opening the endpoint
+ */
+ if ((ep_type == LIBUSB_TRANSFER_TYPE_INTERRUPT) &&
+ (ep_addr & LIBUSB_ENDPOINT_IN)) {
+ char control = USB_EP_INTR_ONE_XFER;
+ int count;
+
+ /* open the status device node for the ep first RDWR */
+ if ((fdstat = open(statfilename, O_RDWR)) == -1) {
+ usbi_dbg("can't open %s RDWR: %d",
+ statfilename, errno);
+ } else {
+ count = write(fdstat, &control, sizeof (control));
+ if (count != 1) {
+ /* this should have worked */
+ usbi_dbg("can't write to %s: %d",
+ statfilename, errno);
+ (void) close(fdstat);
+
+ return (errno);
+ }
+ /* close status node and open xfer node first */
+ close (fdstat);
+ }
+ }
+
+ /* open the xfer node first in case alt needs to be changed */
+ if (ep_type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS) {
+ mode = O_RDWR;
+ } else if (ep_addr & LIBUSB_ENDPOINT_IN) {
+ mode = O_RDONLY;
+ } else {
+ mode = O_WRONLY;
+ }
+
+ /*
+ * IMPORTANT: must open data xfer node first and then open stat node
+ * Otherwise, it will fail on multi-config or multi-altsetting devices
+ * with "Device Busy" error. See ugen_epxs_switch_cfg_alt() and
+ * ugen_epxs_check_alt_switch() in ugen driver source code.
+ */
+ if ((fd = open(filename, mode)) == -1) {
+ usbi_dbg("can't open %s: %d(%s)", filename, errno,
+ strerror(errno));
+
+ return (errno);
+ }
+ /* open the status node */
+ if ((fdstat = open(statfilename, O_RDONLY)) == -1) {
+ usbi_dbg("can't open %s: %d", statfilename, errno);
+
+ (void) close(fd);
+
+ return (errno);
+ }
+
+ hpriv->eps[ep_index].datafd = fd;
+ hpriv->eps[ep_index].statfd = fdstat;
+ usbi_dbg("ep=0x%02x datafd=%d, statfd=%d", ep_addr, fd, fdstat);
+
+ return (0);
+}
+
+int
+sunos_open(struct libusb_device_handle *handle)
+{
+ sunos_dev_handle_priv_t *hpriv;
+ sunos_dev_priv_t *dpriv;
+ int i;
+ int ret;
+
+ hpriv = (sunos_dev_handle_priv_t *)handle->os_priv;
+ dpriv = (sunos_dev_priv_t *)handle->dev->os_priv;
+ hpriv->dpriv = dpriv;
+
+ /* set all file descriptors to "closed" */
+ for (i = 0; i < USB_MAXENDPOINTS; i++) {
+ hpriv->eps[i].datafd = -1;
+ hpriv->eps[i].statfd = -1;
+ }
+
+ if ((ret = sunos_usb_open_ep0(hpriv, dpriv)) != LIBUSB_SUCCESS) {
+ usbi_dbg("fail: %d", ret);
+ return (ret);
+ }
+
+ return (LIBUSB_SUCCESS);
+}
+
+void
+sunos_close(struct libusb_device_handle *handle)
+{
+ sunos_dev_handle_priv_t *hpriv;
+ sunos_dev_priv_t *dpriv;
+
+ usbi_dbg("");
+ if (!handle) {
+ return;
+ }
+
+ hpriv = (sunos_dev_handle_priv_t *)handle->os_priv;
+ if (!hpriv) {
+ return;
+ }
+ dpriv = (sunos_dev_priv_t *)handle->dev->os_priv;
+ if (!dpriv) {
+ return;
+ }
+
+ sunos_usb_close_all_eps(hpriv);
+ sunos_usb_close_ep0(hpriv, dpriv);
+}
+
+int
+sunos_get_device_descriptor(struct libusb_device *dev, uint8_t *buf,
+ int *host_endian)
+{
+ sunos_dev_priv_t *dpriv = (sunos_dev_priv_t *)dev->os_priv;
+
+ memcpy(buf, &dpriv->dev_descr, LIBUSB_DT_DEVICE_SIZE);
+ *host_endian = 0;
+
+ return (LIBUSB_SUCCESS);
+}
+
+int
+sunos_get_active_config_descriptor(struct libusb_device *dev,
+ uint8_t *buf, size_t len, int *host_endian)
+{
+ sunos_dev_priv_t *dpriv = (sunos_dev_priv_t *)dev->os_priv;
+ struct libusb_config_descriptor *cfg;
+ int proplen;
+ di_node_t node;
+ uint8_t *rdata;
+
+ /*
+ * Keep raw configuration descriptors updated, in case config
+ * has ever been changed through setCfg.
+ */
+ if ((node = di_init(dpriv->phypath, DINFOCPYALL)) == DI_NODE_NIL) {
+ usbi_dbg("di_int() failed: %s", strerror(errno));
+ return (LIBUSB_ERROR_IO);
+ }
+ proplen = di_prop_lookup_bytes(DDI_DEV_T_ANY, node,
+ "usb-raw-cfg-descriptors", &rdata);
+ if (proplen <= 0) {
+ usbi_dbg("can't find raw config descriptors");
+
+ return (LIBUSB_ERROR_IO);
+ }
+ dpriv->raw_cfgdescr = realloc(dpriv->raw_cfgdescr, proplen);
+ if (dpriv->raw_cfgdescr == NULL) {
+ return (LIBUSB_ERROR_NO_MEM);
+ } else {
+ bcopy(rdata, dpriv->raw_cfgdescr, proplen);
+ dpriv->cfgvalue = ((struct libusb_config_descriptor *)
+ rdata)->bConfigurationValue;
+ }
+ di_fini(node);
+
+ cfg = (struct libusb_config_descriptor *)dpriv->raw_cfgdescr;
+ len = MIN(len, libusb_le16_to_cpu(cfg->wTotalLength));
+ memcpy(buf, dpriv->raw_cfgdescr, len);
+ *host_endian = 0;
+ usbi_dbg("path:%s len %d", dpriv->phypath, len);
+
+ return (len);
+}
+
+int
+sunos_get_config_descriptor(struct libusb_device *dev, uint8_t idx,
+ uint8_t *buf, size_t len, int *host_endian)
+{
+ /* XXX */
+ return(sunos_get_active_config_descriptor(dev, buf, len, host_endian));
+}
+
+int
+sunos_get_configuration(struct libusb_device_handle *handle, int *config)
+{
+ sunos_dev_priv_t *dpriv = (sunos_dev_priv_t *)handle->dev->os_priv;
+
+ *config = dpriv->cfgvalue;
+
+ usbi_dbg("bConfigurationValue %d", *config);
+
+ return (LIBUSB_SUCCESS);
+}
+
+int
+sunos_set_configuration(struct libusb_device_handle *handle, int config)
+{
+ sunos_dev_priv_t *dpriv = (sunos_dev_priv_t *)handle->dev->os_priv;
+ sunos_dev_handle_priv_t *hpriv;
+
+ usbi_dbg("bConfigurationValue %d", config);
+ hpriv = (sunos_dev_handle_priv_t *)handle->os_priv;
+
+ if (dpriv->ugenpath == NULL)
+ return (LIBUSB_ERROR_NOT_SUPPORTED);
+
+ if (config < 1 || config > dpriv->dev_descr.bNumConfigurations)
+ return (LIBUSB_ERROR_INVALID_PARAM);
+
+ dpriv->cfgvalue = config;
+ hpriv->config_index = config - 1;
+
+ return (LIBUSB_SUCCESS);
+}
+
+int
+sunos_claim_interface(struct libusb_device_handle *handle, int iface)
+{
+ usbi_dbg("iface %d", iface);
+ if (iface < 0) {
+ return (LIBUSB_ERROR_INVALID_PARAM);
+ }
+
+ return (LIBUSB_SUCCESS);
+}
+
+int
+sunos_release_interface(struct libusb_device_handle *handle, int iface)
+{
+ sunos_dev_handle_priv_t *hpriv =
+ (sunos_dev_handle_priv_t *)handle->os_priv;
+
+ usbi_dbg("iface %d", iface);
+ if (iface < 0) {
+ return (LIBUSB_ERROR_INVALID_PARAM);
+ }
+
+ /* XXX: can we release it? */
+ hpriv->altsetting[iface] = 0;
+
+ return (LIBUSB_SUCCESS);
+}
+
+int
+sunos_set_interface_altsetting(struct libusb_device_handle *handle, int iface,
+ int altsetting)
+{
+ sunos_dev_priv_t *dpriv = (sunos_dev_priv_t *)handle->dev->os_priv;
+ sunos_dev_handle_priv_t *hpriv =
+ (sunos_dev_handle_priv_t *)handle->os_priv;
+
+ usbi_dbg("iface %d, setting %d", iface, altsetting);
+
+ if (iface < 0 || altsetting < 0) {
+ return (LIBUSB_ERROR_INVALID_PARAM);
+ }
+ if (dpriv->ugenpath == NULL)
+ return (LIBUSB_ERROR_NOT_FOUND);
+
+ /* XXX: can we switch altsetting? */
+ hpriv->altsetting[iface] = altsetting;
+
+ return (LIBUSB_SUCCESS);
+}
+
+static void
+usb_dump_data(unsigned char *data, size_t size)
+{
+ int i;
+
+ if (getenv("LIBUSB_DEBUG") == NULL) {
+ return;
+ }
+
+ (void) fprintf(stderr, "data dump:");
+ for (i = 0; i < size; i++) {
+ if (i % 16 == 0) {
+ (void) fprintf(stderr, "\n%08x ", i);
+ }
+ (void) fprintf(stderr, "%02x ", (uchar_t)data[i]);
+ }
+ (void) fprintf(stderr, "\n");
+}
+
+static void
+sunos_async_callback(union sigval arg)
+{
+ struct sunos_transfer_priv *tpriv =
+ (struct sunos_transfer_priv *)arg.sival_ptr;
+ struct libusb_transfer *xfer = tpriv->transfer;
+ struct aiocb *aiocb = &tpriv->aiocb;
+ int ret;
+ sunos_dev_handle_priv_t *hpriv;
+ uint8_t ep;
+
+ hpriv = (sunos_dev_handle_priv_t *)xfer->dev_handle->os_priv;
+ ep = sunos_usb_ep_index(xfer->endpoint);
+
+ ret = aio_error(aiocb);
+ if (ret != 0) {
+ xfer->status = sunos_usb_get_status(hpriv->eps[ep].statfd);
+ } else {
+ xfer->actual_length =
+ LIBUSB_TRANSFER_TO_USBI_TRANSFER(xfer)->transferred =
+ aio_return(aiocb);
+ }
+
+ usb_dump_data(xfer->buffer, xfer->actual_length);
+
+ usbi_dbg("ret=%d, len=%d, actual_len=%d", ret, xfer->length,
+ xfer->actual_length);
+
+ /* async notification */
+ usbi_signal_transfer_completion(LIBUSB_TRANSFER_TO_USBI_TRANSFER(xfer));
+}
+
+static int
+sunos_do_async_io(struct libusb_transfer *transfer)
+{
+ int ret = -1;
+ struct aiocb *aiocb;
+ sunos_dev_handle_priv_t *hpriv;
+ uint8_t ep;
+ struct sunos_transfer_priv *tpriv;
+
+ usbi_dbg("");
+
+ tpriv = usbi_transfer_get_os_priv(LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer));
+ hpriv = (sunos_dev_handle_priv_t *)transfer->dev_handle->os_priv;
+ ep = sunos_usb_ep_index(transfer->endpoint);
+
+ tpriv->transfer = transfer;
+ aiocb = &tpriv->aiocb;
+ bzero(aiocb, sizeof (*aiocb));
+ aiocb->aio_fildes = hpriv->eps[ep].datafd;
+ aiocb->aio_buf = transfer->buffer;
+ aiocb->aio_nbytes = transfer->length;
+ aiocb->aio_lio_opcode =
+ ((transfer->endpoint & LIBUSB_ENDPOINT_DIR_MASK) ==
+ LIBUSB_ENDPOINT_IN) ? LIO_READ:LIO_WRITE;
+ aiocb->aio_sigevent.sigev_notify = SIGEV_THREAD;
+ aiocb->aio_sigevent.sigev_value.sival_ptr = tpriv;
+ aiocb->aio_sigevent.sigev_notify_function = sunos_async_callback;
+
+ if (aiocb->aio_lio_opcode == LIO_READ) {
+ ret = aio_read(aiocb);
+ } else {
+ ret = aio_write(aiocb);
+ }
+
+ return (ret);
+}
+
+/* return the number of bytes read/written */
+static int
+usb_do_io(int fd, int stat_fd, char *data, size_t size, int flag, int *status)
+{
+ int error;
+ int ret = -1;
+
+ usbi_dbg("usb_do_io(): datafd=%d statfd=%d size=0x%x flag=%s",
+ fd, stat_fd, size, flag? "WRITE":"READ");
+
+ switch (flag) {
+ case READ:
+ errno = 0;
+ ret = read(fd, data, size);
+ usb_dump_data(data, size);
+ break;
+ case WRITE:
+ usb_dump_data(data, size);
+ errno = 0;
+ ret = write(fd, data, size);
+ break;
+ }
+
+ usbi_dbg("usb_do_io(): amount=%d", ret);
+
+ if (ret < 0) {
+ int save_errno = errno;
+
+ usbi_dbg("TID=%x io %s errno=%d(%s) ret=%d", pthread_self(),
+ flag?"WRITE":"READ", errno, strerror(errno), ret);
+
+ /* sunos_usb_get_status will do a read and overwrite errno */
+ error = sunos_usb_get_status(stat_fd);
+ usbi_dbg("io status=%d errno=%d(%s)", error,
+ save_errno, strerror(save_errno));
+
+ if (status) {
+ *status = save_errno;
+ }
+
+ return (save_errno);
+
+ } else if (status) {
+ *status = 0;
+ }
+
+ return (ret);
+}
+
+static int
+solaris_submit_ctrl_on_default(struct libusb_transfer *transfer)
+{
+ int ret = -1, setup_ret;
+ int status;
+ sunos_dev_handle_priv_t *hpriv;
+ struct libusb_device_handle *hdl = transfer->dev_handle;
+ uint16_t wLength;
+ uint8_t *data = transfer->buffer;
+
+ hpriv = (sunos_dev_handle_priv_t *)hdl->os_priv;
+ wLength = transfer->length - LIBUSB_CONTROL_SETUP_SIZE;
+
+ if (hpriv->eps[0].datafd == -1) {
+ usbi_dbg("ep0 not opened");
+
+ return (LIBUSB_ERROR_NOT_FOUND);
+ }
+
+ if ((data[0] & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_IN) {
+ usbi_dbg("IN request");
+ ret = usb_do_io(hpriv->eps[0].datafd,
+ hpriv->eps[0].statfd, (char *)data, LIBUSB_CONTROL_SETUP_SIZE,
+ WRITE, (int *)&status);
+ } else {
+ usbi_dbg("OUT request");
+ ret = usb_do_io(hpriv->eps[0].datafd, hpriv->eps[0].statfd,
+ transfer->buffer, transfer->length, WRITE,
+ (int *)&transfer->status);
+ }
+
+ setup_ret = ret;
+ if (ret < LIBUSB_CONTROL_SETUP_SIZE) {
+ usbi_dbg("error sending control msg: %d", ret);
+
+ return (LIBUSB_ERROR_IO);
+ }
+
+ ret = transfer->length - LIBUSB_CONTROL_SETUP_SIZE;
+
+ /* Read the remaining bytes for IN request */
+ if ((wLength) && ((data[0] & LIBUSB_ENDPOINT_DIR_MASK) ==
+ LIBUSB_ENDPOINT_IN)) {
+ usbi_dbg("DATA: %d", transfer->length - setup_ret);
+ ret = usb_do_io(hpriv->eps[0].datafd,
+ hpriv->eps[0].statfd,
+ (char *)transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE,
+ wLength, READ, (int *)&transfer->status);
+ }
+
+ if (ret >= 0) {
+ transfer->actual_length = ret;
+ LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer)->transferred = ret;
+ }
+ usbi_dbg("Done: ctrl data bytes %d", ret);
+
+ /* sync transfer handling */
+ ret = usbi_handle_transfer_completion(LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer),
+ transfer->status);
+
+ return (ret);
+}
+
+int
+sunos_clear_halt(struct libusb_device_handle *handle, uint8_t endpoint)
+{
+ int ret;
+
+ usbi_dbg("endpoint=0x%02x", endpoint);
+
+ ret = libusb_control_transfer(handle, LIBUSB_ENDPOINT_OUT |
+ LIBUSB_RECIPIENT_ENDPOINT | LIBUSB_REQUEST_TYPE_STANDARD,
+ LIBUSB_REQUEST_CLEAR_FEATURE, 0, endpoint, NULL, 0, 1000);
+
+ usbi_dbg("ret=%d", ret);
+
+ return (ret);
+}
+
+int
+sunos_reset_device(struct libusb_device_handle *handle)
+{
+ usbi_dbg("");
+
+ return (LIBUSB_ERROR_NOT_SUPPORTED);
+}
+
+void
+sunos_destroy_device(struct libusb_device *dev)
+{
+ sunos_dev_priv_t *dpriv = (sunos_dev_priv_t *)dev->os_priv;
+
+ usbi_dbg("");
+
+ free(dpriv->raw_cfgdescr);
+ free(dpriv->ugenpath);
+ free(dpriv->phypath);
+}
+
+int
+sunos_submit_transfer(struct usbi_transfer *itransfer)
+{
+ struct libusb_transfer *transfer;
+ struct libusb_device_handle *hdl;
+ int err = 0;
+
+ transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ hdl = transfer->dev_handle;
+
+ err = sunos_check_device_and_status_open(hdl,
+ transfer->endpoint, transfer->type);
+ if (err < 0) {
+
+ return (_errno_to_libusb(err));
+ }
+
+ switch (transfer->type) {
+ case LIBUSB_TRANSFER_TYPE_CONTROL:
+ /* sync transfer */
+ usbi_dbg("CTRL transfer: %d", transfer->length);
+ err = solaris_submit_ctrl_on_default(transfer);
+ break;
+
+ case LIBUSB_TRANSFER_TYPE_BULK:
+ /* fallthru */
+ case LIBUSB_TRANSFER_TYPE_INTERRUPT:
+ if (transfer->type == LIBUSB_TRANSFER_TYPE_BULK)
+ usbi_dbg("BULK transfer: %d", transfer->length);
+ else
+ usbi_dbg("INTR transfer: %d", transfer->length);
+ err = sunos_do_async_io(transfer);
+ break;
+
+ case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
+ /* Isochronous/Stream is not supported */
+
+ /* fallthru */
+ case LIBUSB_TRANSFER_TYPE_BULK_STREAM:
+ if (transfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS)
+ usbi_dbg("ISOC transfer: %d", transfer->length);
+ else
+ usbi_dbg("BULK STREAM transfer: %d", transfer->length);
+ err = LIBUSB_ERROR_NOT_SUPPORTED;
+ break;
+ }
+
+ return (err);
+}
+
+int
+sunos_cancel_transfer(struct usbi_transfer *itransfer)
+{
+ sunos_xfer_priv_t *tpriv;
+ sunos_dev_handle_priv_t *hpriv;
+ struct libusb_transfer *transfer;
+ struct aiocb *aiocb;
+ uint8_t ep;
+ int ret;
+
+ tpriv = usbi_transfer_get_os_priv(itransfer);
+ aiocb = &tpriv->aiocb;
+ transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ hpriv = (sunos_dev_handle_priv_t *)transfer->dev_handle->os_priv;
+ ep = sunos_usb_ep_index(transfer->endpoint);
+
+ ret = aio_cancel(hpriv->eps[ep].datafd, aiocb);
+
+ usbi_dbg("aio->fd=%d fd=%d ret = %d, %s", aiocb->aio_fildes,
+ hpriv->eps[ep].datafd, ret, (ret == AIO_CANCELED)?
+ strerror(0):strerror(errno));
+
+ if (ret != AIO_CANCELED) {
+ ret = _errno_to_libusb(errno);
+ } else {
+ /*
+ * we don't need to call usbi_handle_transfer_cancellation(),
+ * because we'll handle everything in sunos_async_callback.
+ */
+ ret = LIBUSB_SUCCESS;
+ }
+
+ return (ret);
+}
+
+void
+sunos_clear_transfer_priv(struct usbi_transfer *itransfer)
+{
+ usbi_dbg("");
+
+ /* Nothing to do */
+}
+
+int
+sunos_handle_transfer_completion(struct usbi_transfer *itransfer)
+{
+ return usbi_handle_transfer_completion(itransfer, LIBUSB_TRANSFER_COMPLETED);
+}
+
+int
+sunos_clock_gettime(int clkid, struct timespec *tp)
+{
+ usbi_dbg("clock %d", clkid);
+
+ if (clkid == USBI_CLOCK_REALTIME)
+ return clock_gettime(CLOCK_REALTIME, tp);
+
+ if (clkid == USBI_CLOCK_MONOTONIC)
+ return clock_gettime(CLOCK_MONOTONIC, tp);
+
+ return (LIBUSB_ERROR_INVALID_PARAM);
+}
+
+int
+_errno_to_libusb(int err)
+{
+ usbi_dbg("error: %s (%d)", strerror(err), err);
+
+ switch (err) {
+ case EIO:
+ return (LIBUSB_ERROR_IO);
+ case EACCES:
+ return (LIBUSB_ERROR_ACCESS);
+ case ENOENT:
+ return (LIBUSB_ERROR_NO_DEVICE);
+ case ENOMEM:
+ return (LIBUSB_ERROR_NO_MEM);
+ case ETIMEDOUT:
+ return (LIBUSB_ERROR_TIMEOUT);
+ }
+
+ return (LIBUSB_ERROR_OTHER);
+}
+
+/*
+ * sunos_usb_get_status:
+ * gets status of endpoint
+ *
+ * Returns: ugen's last cmd status
+ */
+static int
+sunos_usb_get_status(int fd)
+{
+ int status, ret;
+
+ usbi_dbg("sunos_usb_get_status(): fd=%d", fd);
+
+ ret = read(fd, &status, sizeof (status));
+ if (ret == sizeof (status)) {
+ switch (status) {
+ case USB_LC_STAT_NOERROR:
+ usbi_dbg("No Error");
+ break;
+ case USB_LC_STAT_CRC:
+ usbi_dbg("CRC Timeout Detected\n");
+ break;
+ case USB_LC_STAT_BITSTUFFING:
+ usbi_dbg("Bit Stuffing Violation\n");
+ break;
+ case USB_LC_STAT_DATA_TOGGLE_MM:
+ usbi_dbg("Data Toggle Mismatch\n");
+ break;
+ case USB_LC_STAT_STALL:
+ usbi_dbg("End Point Stalled\n");
+ break;
+ case USB_LC_STAT_DEV_NOT_RESP:
+ usbi_dbg("Device is Not Responding\n");
+ break;
+ case USB_LC_STAT_PID_CHECKFAILURE:
+ usbi_dbg("PID Check Failure\n");
+ break;
+ case USB_LC_STAT_UNEXP_PID:
+ usbi_dbg("Unexpected PID\n");
+ break;
+ case USB_LC_STAT_DATA_OVERRUN:
+ usbi_dbg("Data Exceeded Size\n");
+ break;
+ case USB_LC_STAT_DATA_UNDERRUN:
+ usbi_dbg("Less data received\n");
+ break;
+ case USB_LC_STAT_BUFFER_OVERRUN:
+ usbi_dbg("Buffer Size Exceeded\n");
+ break;
+ case USB_LC_STAT_BUFFER_UNDERRUN:
+ usbi_dbg("Buffer Underrun\n");
+ break;
+ case USB_LC_STAT_TIMEOUT:
+ usbi_dbg("Command Timed Out\n");
+ break;
+ case USB_LC_STAT_NOT_ACCESSED:
+ usbi_dbg("Not Accessed by h/w\n");
+ break;
+ case USB_LC_STAT_UNSPECIFIED_ERR:
+ usbi_dbg("Unspecified Error\n");
+ break;
+ case USB_LC_STAT_NO_BANDWIDTH:
+ usbi_dbg("No Bandwidth\n");
+ break;
+ case USB_LC_STAT_HW_ERR:
+ usbi_dbg("Host Controller h/w Error\n");
+ break;
+ case USB_LC_STAT_SUSPENDED:
+ usbi_dbg("Device was Suspended\n");
+ break;
+ case USB_LC_STAT_DISCONNECTED:
+ usbi_dbg("Device was Disconnected\n");
+ break;
+ case USB_LC_STAT_INTR_BUF_FULL:
+ usbi_dbg("Interrupt buffer was full\n");
+ break;
+ case USB_LC_STAT_INVALID_REQ:
+ usbi_dbg("Request was Invalid\n");
+ break;
+ case USB_LC_STAT_INTERRUPTED:
+ usbi_dbg("Request was Interrupted\n");
+ break;
+ case USB_LC_STAT_NO_RESOURCES:
+ usbi_dbg("No resources available for "
+ "request\n");
+ break;
+ case USB_LC_STAT_INTR_POLLING_FAILED:
+ usbi_dbg("Failed to Restart Poll");
+ break;
+ default:
+ usbi_dbg("Error Not Determined %d\n",
+ status);
+ break;
+ }
+ } else {
+ usbi_dbg("read stat error: %s",strerror(errno));
+ status = -1;
+ }
+
+ return (status);
+}
+
+const struct usbi_os_backend sunos_backend = {
+ .name = "Solaris",
+ .caps = 0,
+ .init = sunos_init,
+ .exit = sunos_exit,
+ .get_device_list = sunos_get_device_list,
+ .get_device_descriptor = sunos_get_device_descriptor,
+ .get_active_config_descriptor = sunos_get_active_config_descriptor,
+ .get_config_descriptor = sunos_get_config_descriptor,
+ .hotplug_poll = NULL,
+ .open = sunos_open,
+ .close = sunos_close,
+ .get_configuration = sunos_get_configuration,
+ .set_configuration = sunos_set_configuration,
+
+ .claim_interface = sunos_claim_interface,
+ .release_interface = sunos_release_interface,
+ .set_interface_altsetting = sunos_set_interface_altsetting,
+ .clear_halt = sunos_clear_halt,
+ .reset_device = sunos_reset_device, /* TODO */
+ .alloc_streams = NULL,
+ .free_streams = NULL,
+ .kernel_driver_active = NULL,
+ .detach_kernel_driver = NULL,
+ .attach_kernel_driver = NULL,
+ .destroy_device = sunos_destroy_device,
+ .submit_transfer = sunos_submit_transfer,
+ .cancel_transfer = sunos_cancel_transfer,
+ .handle_events = NULL,
+ .clear_transfer_priv = sunos_clear_transfer_priv,
+ .handle_transfer_completion = sunos_handle_transfer_completion,
+ .clock_gettime = sunos_clock_gettime,
+ .device_priv_size = sizeof(sunos_dev_priv_t),
+ .device_handle_priv_size = sizeof(sunos_dev_handle_priv_t),
+ .transfer_priv_size = sizeof(sunos_xfer_priv_t),
+};
diff --git a/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/sunos_usb.h b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/sunos_usb.h
new file mode 100644
index 000000000..574166031
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/sunos_usb.h
@@ -0,0 +1,74 @@
+/*
+ *
+ * Copyright (c) 2016, Oracle and/or its affiliates.
+ *
+ * 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_SUNOS_H
+#define LIBUSB_SUNOS_H
+
+#include <libdevinfo.h>
+#include <pthread.h>
+#include "libusbi.h"
+
+#define READ 0
+#define WRITE 1
+
+typedef struct sunos_device_priv {
+ uint8_t cfgvalue; /* active config value */
+ uint8_t *raw_cfgdescr; /* active config descriptor */
+ struct libusb_device_descriptor dev_descr; /* usb device descriptor */
+ char *ugenpath; /* name of the ugen(4) node */
+ char *phypath; /* physical path */
+} sunos_dev_priv_t;
+
+typedef struct endpoint {
+ int datafd; /* data file */
+ int statfd; /* state file */
+} sunos_ep_priv_t;
+
+typedef struct sunos_device_handle_priv {
+ uint8_t altsetting[USB_MAXINTERFACES]; /* a interface's alt */
+ uint8_t config_index;
+ sunos_ep_priv_t eps[USB_MAXENDPOINTS];
+ sunos_dev_priv_t *dpriv; /* device private */
+} sunos_dev_handle_priv_t;
+
+typedef struct sunos_transfer_priv {
+ struct aiocb aiocb;
+ struct libusb_transfer *transfer;
+} sunos_xfer_priv_t;
+
+struct node_args {
+ struct libusb_context *ctx;
+ struct discovered_devs **discdevs;
+ const char *last_ugenpath;
+ di_devlink_handle_t dlink_hdl;
+};
+
+struct devlink_cbarg {
+ struct node_args *nargs; /* di node walk arguments */
+ di_node_t myself; /* the di node */
+ di_minor_t minor;
+};
+
+/* AIO callback args */
+struct aio_callback_args{
+ struct libusb_transfer *transfer;
+ struct aiocb aiocb;
+};
+
+#endif /* LIBUSB_SUNOS_H */
diff --git a/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/threads_posix.c b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/threads_posix.c
new file mode 100644
index 000000000..a4f270bbe
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/threads_posix.c
@@ -0,0 +1,79 @@
+/*
+ * libusb synchronization using POSIX Threads
+ *
+ * Copyright © 2011 Vitali Lovich <vlovich@aliph.com>
+ * Copyright © 2011 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
+ */
+
+#include <config.h>
+
+#include <time.h>
+#if defined(__linux__) || defined(__OpenBSD__)
+# if defined(__OpenBSD__)
+# define _BSD_SOURCE
+# endif
+# include <unistd.h>
+# include <sys/syscall.h>
+#elif defined(__APPLE__)
+# include <mach/mach.h>
+#elif defined(__CYGWIN__)
+# include <windows.h>
+#endif
+
+#include "threads_posix.h"
+#include "libusbi.h"
+
+int usbi_cond_timedwait(pthread_cond_t *cond,
+ pthread_mutex_t *mutex, const struct timeval *tv)
+{
+ struct timespec timeout;
+ int r;
+
+ r = usbi_backend->clock_gettime(USBI_CLOCK_REALTIME, &timeout);
+ if (r < 0)
+ return r;
+
+ timeout.tv_sec += tv->tv_sec;
+ timeout.tv_nsec += tv->tv_usec * 1000;
+ while (timeout.tv_nsec >= 1000000000L) {
+ timeout.tv_nsec -= 1000000000L;
+ timeout.tv_sec++;
+ }
+
+ return pthread_cond_timedwait(cond, mutex, &timeout);
+}
+
+int usbi_get_tid(void)
+{
+ int ret = -1;
+#if defined(__ANDROID__)
+ ret = gettid();
+#elif defined(__linux__)
+ ret = syscall(SYS_gettid);
+#elif defined(__OpenBSD__)
+ /* The following only works with OpenBSD > 5.1 as it requires
+ 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);
+#elif defined(__CYGWIN__)
+ ret = GetCurrentThreadId();
+#endif
+/* TODO: NetBSD thread ID support */
+ return ret;
+}
diff --git a/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/threads_posix.h b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/threads_posix.h
new file mode 100644
index 000000000..4c1514ea6
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/threads_posix.h
@@ -0,0 +1,55 @@
+/*
+ * 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 */
diff --git a/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/threads_windows.c b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/threads_windows.c
new file mode 100644
index 000000000..7c2e52dba
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/threads_windows.c
@@ -0,0 +1,259 @@
+/*
+ * 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();
+}
diff --git a/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/threads_windows.h b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/threads_windows.h
new file mode 100644
index 000000000..e97ee7875
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/threads_windows.h
@@ -0,0 +1,76 @@
+/*
+ * 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
+ */
+
+#ifndef LIBUSB_THREADS_WINDOWS_H
+#define LIBUSB_THREADS_WINDOWS_H
+
+#define usbi_mutex_static_t volatile LONG
+#define USBI_MUTEX_INITIALIZER 0
+
+#define usbi_mutex_t HANDLE
+
+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;
+
+// We *were* getting timespec from pthread.h:
+#if (!defined(HAVE_STRUCT_TIMESPEC) && !defined(_TIMESPEC_DEFINED))
+#define HAVE_STRUCT_TIMESPEC 1
+#define _TIMESPEC_DEFINED 1
+struct timespec {
+ long tv_sec;
+ long tv_nsec;
+};
+#endif /* HAVE_STRUCT_TIMESPEC | _TIMESPEC_DEFINED */
+
+// We *were* getting ETIMEDOUT from pthread.h:
+#ifndef ETIMEDOUT
+# define ETIMEDOUT 10060 /* This is the value in winsock.h. */
+#endif
+
+#define usbi_tls_key_t DWORD
+
+int usbi_mutex_static_lock(usbi_mutex_static_t *mutex);
+int usbi_mutex_static_unlock(usbi_mutex_static_t *mutex);
+
+int usbi_mutex_init(usbi_mutex_t *mutex);
+int usbi_mutex_lock(usbi_mutex_t *mutex);
+int usbi_mutex_unlock(usbi_mutex_t *mutex);
+int usbi_mutex_trylock(usbi_mutex_t *mutex);
+int usbi_mutex_destroy(usbi_mutex_t *mutex);
+
+int 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);
+
+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);
+
+int usbi_get_tid(void);
+
+#endif /* LIBUSB_THREADS_WINDOWS_H */
diff --git a/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/wince_usb.c b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/wince_usb.c
new file mode 100644
index 000000000..0d466b8c9
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/wince_usb.c
@@ -0,0 +1,899 @@
+/*
+ * Windows CE backend for libusb 1.0
+ * Copyright © 2011-2013 RealVNC Ltd.
+ * Large portions taken from Windows backend, which is
+ * Copyright © 2009-2010 Pete Batard <pbatard@gmail.com>
+ * With contributions from Michael Plante, Orin Eman et al.
+ * Parts of this code adapted from libusb-win32-v1 by Stephan Meyer
+ * Major code testing contribution by Xiaofan Chen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include <stdint.h>
+#include <inttypes.h>
+
+#include "libusbi.h"
+#include "wince_usb.h"
+
+// Global variables
+int windows_version = WINDOWS_CE;
+static uint64_t hires_frequency, hires_ticks_to_ps;
+static HANDLE driver_handle = INVALID_HANDLE_VALUE;
+static int concurrent_usage = -1;
+
+/*
+ * Converts a windows error to human readable string
+ * uses retval as errorcode, or, if 0, use GetLastError()
+ */
+#if defined(ENABLE_LOGGING)
+static const char *windows_error_str(DWORD error_code)
+{
+ static TCHAR wErr_string[ERR_BUFFER_SIZE];
+ static char err_string[ERR_BUFFER_SIZE];
+
+ DWORD size;
+ int len;
+
+ if (error_code == 0)
+ error_code = GetLastError();
+
+ len = sprintf(err_string, "[%u] ", (unsigned int)error_code);
+
+ size = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ wErr_string, ERR_BUFFER_SIZE, NULL);
+ if (size == 0) {
+ DWORD format_error = GetLastError();
+ if (format_error)
+ snprintf(err_string, ERR_BUFFER_SIZE,
+ "Windows error code %u (FormatMessage error code %u)",
+ (unsigned int)error_code, (unsigned int)format_error);
+ else
+ snprintf(err_string, ERR_BUFFER_SIZE, "Unknown error code %u", (unsigned int)error_code);
+ } else {
+ // Remove CR/LF terminators, if present
+ size_t pos = size - 2;
+ if (wErr_string[pos] == 0x0D)
+ wErr_string[pos] = 0;
+
+ if (!WideCharToMultiByte(CP_ACP, 0, wErr_string, -1, &err_string[len], ERR_BUFFER_SIZE - len, NULL, NULL))
+ strcpy(err_string, "Unable to convert error string");
+ }
+
+ return err_string;
+}
+#endif
+
+static struct wince_device_priv *_device_priv(struct libusb_device *dev)
+{
+ return (struct wince_device_priv *)dev->os_priv;
+}
+
+// ceusbkwrapper to libusb error code mapping
+static int translate_driver_error(DWORD error)
+{
+ switch (error) {
+ case ERROR_INVALID_PARAMETER:
+ return LIBUSB_ERROR_INVALID_PARAM;
+ case ERROR_CALL_NOT_IMPLEMENTED:
+ case ERROR_NOT_SUPPORTED:
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ return LIBUSB_ERROR_NO_MEM;
+ case ERROR_INVALID_HANDLE:
+ return LIBUSB_ERROR_NO_DEVICE;
+ case ERROR_BUSY:
+ return LIBUSB_ERROR_BUSY;
+
+ // Error codes that are either unexpected, or have
+ // no suitable LIBUSB_ERROR equivalent.
+ case ERROR_CANCELLED:
+ case ERROR_INTERNAL_ERROR:
+ default:
+ return LIBUSB_ERROR_OTHER;
+ }
+}
+
+static int init_dllimports(void)
+{
+ DLL_GET_HANDLE(ceusbkwrapper);
+ DLL_LOAD_FUNC(ceusbkwrapper, UkwOpenDriver, TRUE);
+ DLL_LOAD_FUNC(ceusbkwrapper, UkwGetDeviceList, TRUE);
+ DLL_LOAD_FUNC(ceusbkwrapper, UkwReleaseDeviceList, TRUE);
+ DLL_LOAD_FUNC(ceusbkwrapper, UkwGetDeviceAddress, TRUE);
+ DLL_LOAD_FUNC(ceusbkwrapper, UkwGetDeviceDescriptor, TRUE);
+ DLL_LOAD_FUNC(ceusbkwrapper, UkwGetConfigDescriptor, TRUE);
+ DLL_LOAD_FUNC(ceusbkwrapper, UkwCloseDriver, TRUE);
+ DLL_LOAD_FUNC(ceusbkwrapper, UkwCancelTransfer, TRUE);
+ DLL_LOAD_FUNC(ceusbkwrapper, UkwIssueControlTransfer, TRUE);
+ DLL_LOAD_FUNC(ceusbkwrapper, UkwClaimInterface, TRUE);
+ DLL_LOAD_FUNC(ceusbkwrapper, UkwReleaseInterface, TRUE);
+ DLL_LOAD_FUNC(ceusbkwrapper, UkwSetInterfaceAlternateSetting, TRUE);
+ DLL_LOAD_FUNC(ceusbkwrapper, UkwClearHaltHost, TRUE);
+ DLL_LOAD_FUNC(ceusbkwrapper, UkwClearHaltDevice, TRUE);
+ DLL_LOAD_FUNC(ceusbkwrapper, UkwGetConfig, TRUE);
+ DLL_LOAD_FUNC(ceusbkwrapper, UkwSetConfig, TRUE);
+ DLL_LOAD_FUNC(ceusbkwrapper, UkwResetDevice, TRUE);
+ DLL_LOAD_FUNC(ceusbkwrapper, UkwKernelDriverActive, TRUE);
+ DLL_LOAD_FUNC(ceusbkwrapper, UkwAttachKernelDriver, TRUE);
+ DLL_LOAD_FUNC(ceusbkwrapper, UkwDetachKernelDriver, TRUE);
+ DLL_LOAD_FUNC(ceusbkwrapper, UkwIssueBulkTransfer, TRUE);
+ DLL_LOAD_FUNC(ceusbkwrapper, UkwIsPipeHalted, TRUE);
+
+ return LIBUSB_SUCCESS;
+}
+
+static void exit_dllimports(void)
+{
+ DLL_FREE_HANDLE(ceusbkwrapper);
+}
+
+static int init_device(
+ struct libusb_device *dev, UKW_DEVICE drv_dev,
+ unsigned char bus_addr, unsigned char dev_addr)
+{
+ struct wince_device_priv *priv = _device_priv(dev);
+ int r = LIBUSB_SUCCESS;
+
+ dev->bus_number = bus_addr;
+ dev->device_address = dev_addr;
+ priv->dev = drv_dev;
+
+ if (!UkwGetDeviceDescriptor(priv->dev, &(priv->desc)))
+ r = translate_driver_error(GetLastError());
+
+ return r;
+}
+
+// Internal API functions
+static int wince_init(struct libusb_context *ctx)
+{
+ int r = LIBUSB_ERROR_OTHER;
+ HANDLE semaphore;
+ LARGE_INTEGER li_frequency;
+ TCHAR sem_name[11 + 8 + 1]; // strlen("libusb_init") + (32-bit hex PID) + '\0'
+
+ _stprintf(sem_name, _T("libusb_init%08X"), (unsigned int)(GetCurrentProcessId() & 0xFFFFFFFF));
+ semaphore = CreateSemaphore(NULL, 1, 1, sem_name);
+ if (semaphore == NULL) {
+ usbi_err(ctx, "could not create semaphore: %s", windows_error_str(0));
+ return LIBUSB_ERROR_NO_MEM;
+ }
+
+ // A successful wait brings our semaphore count to 0 (unsignaled)
+ // => any concurent wait stalls until the semaphore's release
+ if (WaitForSingleObject(semaphore, INFINITE) != WAIT_OBJECT_0) {
+ usbi_err(ctx, "failure to access semaphore: %s", windows_error_str(0));
+ CloseHandle(semaphore);
+ return LIBUSB_ERROR_NO_MEM;
+ }
+
+ // NB: concurrent usage supposes that init calls are equally balanced with
+ // exit calls. If init is called more than exit, we will not exit properly
+ if ( ++concurrent_usage == 0 ) { // First init?
+ // Initialize pollable file descriptors
+ init_polling();
+
+ // Load DLL imports
+ if (init_dllimports() != LIBUSB_SUCCESS) {
+ usbi_err(ctx, "could not resolve DLL functions");
+ r = LIBUSB_ERROR_NOT_SUPPORTED;
+ goto init_exit;
+ }
+
+ // try to open a handle to the driver
+ driver_handle = UkwOpenDriver();
+ if (driver_handle == INVALID_HANDLE_VALUE) {
+ usbi_err(ctx, "could not connect to driver");
+ r = LIBUSB_ERROR_NOT_SUPPORTED;
+ goto init_exit;
+ }
+
+ // find out if we have access to a monotonic (hires) timer
+ if (QueryPerformanceFrequency(&li_frequency)) {
+ hires_frequency = li_frequency.QuadPart;
+ // The hires frequency can go as high as 4 GHz, so we'll use a conversion
+ // to picoseconds to compute the tv_nsecs part in clock_gettime
+ hires_ticks_to_ps = UINT64_C(1000000000000) / hires_frequency;
+ usbi_dbg("hires timer available (Frequency: %"PRIu64" Hz)", hires_frequency);
+ } else {
+ usbi_dbg("no hires timer available on this platform");
+ hires_frequency = 0;
+ hires_ticks_to_ps = UINT64_C(0);
+ }
+ }
+ // At this stage, either we went through full init successfully, or didn't need to
+ r = LIBUSB_SUCCESS;
+
+init_exit: // Holds semaphore here.
+ if (!concurrent_usage && r != LIBUSB_SUCCESS) { // First init failed?
+ exit_dllimports();
+ exit_polling();
+
+ if (driver_handle != INVALID_HANDLE_VALUE) {
+ UkwCloseDriver(driver_handle);
+ driver_handle = INVALID_HANDLE_VALUE;
+ }
+ }
+
+ if (r != LIBUSB_SUCCESS)
+ --concurrent_usage; // Not expected to call libusb_exit if we failed.
+
+ ReleaseSemaphore(semaphore, 1, NULL); // increase count back to 1
+ CloseHandle(semaphore);
+ return r;
+}
+
+static void wince_exit(void)
+{
+ HANDLE semaphore;
+ TCHAR sem_name[11 + 8 + 1]; // strlen("libusb_init") + (32-bit hex PID) + '\0'
+
+ _stprintf(sem_name, _T("libusb_init%08X"), (unsigned int)(GetCurrentProcessId() & 0xFFFFFFFF));
+ semaphore = CreateSemaphore(NULL, 1, 1, sem_name);
+ if (semaphore == NULL)
+ return;
+
+ // A successful wait brings our semaphore count to 0 (unsignaled)
+ // => any concurent wait stalls until the semaphore release
+ if (WaitForSingleObject(semaphore, INFINITE) != WAIT_OBJECT_0) {
+ CloseHandle(semaphore);
+ return;
+ }
+
+ // Only works if exits and inits are balanced exactly
+ if (--concurrent_usage < 0) { // Last exit
+ exit_dllimports();
+ exit_polling();
+
+ if (driver_handle != INVALID_HANDLE_VALUE) {
+ UkwCloseDriver(driver_handle);
+ driver_handle = INVALID_HANDLE_VALUE;
+ }
+ }
+
+ ReleaseSemaphore(semaphore, 1, NULL); // increase count back to 1
+ CloseHandle(semaphore);
+}
+
+static int wince_get_device_list(
+ struct libusb_context *ctx,
+ struct discovered_devs **discdevs)
+{
+ UKW_DEVICE devices[MAX_DEVICE_COUNT];
+ struct discovered_devs *new_devices = *discdevs;
+ DWORD count = 0, i;
+ struct libusb_device *dev = NULL;
+ unsigned char bus_addr, dev_addr;
+ unsigned long session_id;
+ BOOL success;
+ DWORD release_list_offset = 0;
+ int r = LIBUSB_SUCCESS;
+
+ success = UkwGetDeviceList(driver_handle, devices, MAX_DEVICE_COUNT, &count);
+ if (!success) {
+ int libusbErr = translate_driver_error(GetLastError());
+ usbi_err(ctx, "could not get devices: %s", windows_error_str(0));
+ return libusbErr;
+ }
+
+ for (i = 0; i < count; ++i) {
+ release_list_offset = i;
+ success = UkwGetDeviceAddress(devices[i], &bus_addr, &dev_addr, &session_id);
+ if (!success) {
+ r = translate_driver_error(GetLastError());
+ usbi_err(ctx, "could not get device address for %u: %s", (unsigned int)i, windows_error_str(0));
+ goto err_out;
+ }
+
+ dev = usbi_get_device_by_session_id(ctx, session_id);
+ if (dev) {
+ usbi_dbg("using existing device for %u/%u (session %lu)",
+ bus_addr, dev_addr, session_id);
+ // Release just this element in the device list (as we already hold a
+ // reference to it).
+ UkwReleaseDeviceList(driver_handle, &devices[i], 1);
+ release_list_offset++;
+ } else {
+ usbi_dbg("allocating new device for %u/%u (session %lu)",
+ bus_addr, dev_addr, session_id);
+ dev = usbi_alloc_device(ctx, session_id);
+ if (!dev) {
+ r = LIBUSB_ERROR_NO_MEM;
+ goto err_out;
+ }
+
+ r = init_device(dev, devices[i], bus_addr, dev_addr);
+ if (r < 0)
+ goto err_out;
+
+ r = usbi_sanitize_device(dev);
+ if (r < 0)
+ goto err_out;
+ }
+
+ new_devices = discovered_devs_append(new_devices, dev);
+ if (!discdevs) {
+ r = LIBUSB_ERROR_NO_MEM;
+ goto err_out;
+ }
+
+ libusb_unref_device(dev);
+ }
+
+ *discdevs = new_devices;
+ return r;
+err_out:
+ *discdevs = new_devices;
+ libusb_unref_device(dev);
+ // Release the remainder of the unprocessed device list.
+ // The devices added to new_devices already will still be passed up to libusb,
+ // which can dispose of them at its leisure.
+ UkwReleaseDeviceList(driver_handle, &devices[release_list_offset], count - release_list_offset);
+ return r;
+}
+
+static int wince_open(struct libusb_device_handle *handle)
+{
+ // Nothing to do to open devices as a handle to it has
+ // been retrieved by wince_get_device_list
+ return LIBUSB_SUCCESS;
+}
+
+static void wince_close(struct libusb_device_handle *handle)
+{
+ // Nothing to do as wince_open does nothing.
+}
+
+static int wince_get_device_descriptor(
+ struct libusb_device *device,
+ unsigned char *buffer, int *host_endian)
+{
+ struct wince_device_priv *priv = _device_priv(device);
+
+ *host_endian = 1;
+ memcpy(buffer, &priv->desc, DEVICE_DESC_LENGTH);
+ return LIBUSB_SUCCESS;
+}
+
+static int wince_get_active_config_descriptor(
+ struct libusb_device *device,
+ unsigned char *buffer, size_t len, int *host_endian)
+{
+ struct wince_device_priv *priv = _device_priv(device);
+ DWORD actualSize = len;
+
+ *host_endian = 0;
+ if (!UkwGetConfigDescriptor(priv->dev, UKW_ACTIVE_CONFIGURATION, buffer, len, &actualSize))
+ return translate_driver_error(GetLastError());
+
+ return actualSize;
+}
+
+static int wince_get_config_descriptor(
+ struct libusb_device *device,
+ uint8_t config_index,
+ unsigned char *buffer, size_t len, int *host_endian)
+{
+ struct wince_device_priv *priv = _device_priv(device);
+ DWORD actualSize = len;
+
+ *host_endian = 0;
+ if (!UkwGetConfigDescriptor(priv->dev, config_index, buffer, len, &actualSize))
+ return translate_driver_error(GetLastError());
+
+ return actualSize;
+}
+
+static int wince_get_configuration(
+ struct libusb_device_handle *handle,
+ int *config)
+{
+ struct wince_device_priv *priv = _device_priv(handle->dev);
+ UCHAR cv = 0;
+
+ if (!UkwGetConfig(priv->dev, &cv))
+ return translate_driver_error(GetLastError());
+
+ (*config) = cv;
+ return LIBUSB_SUCCESS;
+}
+
+static int wince_set_configuration(
+ struct libusb_device_handle *handle,
+ int config)
+{
+ struct wince_device_priv *priv = _device_priv(handle->dev);
+ // Setting configuration 0 places the device in Address state.
+ // This should correspond to the "unconfigured state" required by
+ // libusb when the specified configuration is -1.
+ UCHAR cv = (config < 0) ? 0 : config;
+ if (!UkwSetConfig(priv->dev, cv))
+ return translate_driver_error(GetLastError());
+
+ return LIBUSB_SUCCESS;
+}
+
+static int wince_claim_interface(
+ struct libusb_device_handle *handle,
+ int interface_number)
+{
+ struct wince_device_priv *priv = _device_priv(handle->dev);
+
+ if (!UkwClaimInterface(priv->dev, interface_number))
+ return translate_driver_error(GetLastError());
+
+ return LIBUSB_SUCCESS;
+}
+
+static int wince_release_interface(
+ struct libusb_device_handle *handle,
+ int interface_number)
+{
+ struct wince_device_priv *priv = _device_priv(handle->dev);
+
+ if (!UkwSetInterfaceAlternateSetting(priv->dev, interface_number, 0))
+ return translate_driver_error(GetLastError());
+
+ if (!UkwReleaseInterface(priv->dev, interface_number))
+ return translate_driver_error(GetLastError());
+
+ return LIBUSB_SUCCESS;
+}
+
+static int wince_set_interface_altsetting(
+ struct libusb_device_handle *handle,
+ int interface_number, int altsetting)
+{
+ struct wince_device_priv *priv = _device_priv(handle->dev);
+
+ if (!UkwSetInterfaceAlternateSetting(priv->dev, interface_number, altsetting))
+ return translate_driver_error(GetLastError());
+
+ return LIBUSB_SUCCESS;
+}
+
+static int wince_clear_halt(
+ struct libusb_device_handle *handle,
+ unsigned char endpoint)
+{
+ struct wince_device_priv *priv = _device_priv(handle->dev);
+
+ if (!UkwClearHaltHost(priv->dev, endpoint))
+ return translate_driver_error(GetLastError());
+
+ if (!UkwClearHaltDevice(priv->dev, endpoint))
+ return translate_driver_error(GetLastError());
+
+ return LIBUSB_SUCCESS;
+}
+
+static int wince_reset_device(
+ struct libusb_device_handle *handle)
+{
+ struct wince_device_priv *priv = _device_priv(handle->dev);
+
+ if (!UkwResetDevice(priv->dev))
+ return translate_driver_error(GetLastError());
+
+ return LIBUSB_SUCCESS;
+}
+
+static int wince_kernel_driver_active(
+ struct libusb_device_handle *handle,
+ int interface_number)
+{
+ struct wince_device_priv *priv = _device_priv(handle->dev);
+ BOOL result = FALSE;
+
+ if (!UkwKernelDriverActive(priv->dev, interface_number, &result))
+ return translate_driver_error(GetLastError());
+
+ return result ? 1 : 0;
+}
+
+static int wince_detach_kernel_driver(
+ struct libusb_device_handle *handle,
+ int interface_number)
+{
+ struct wince_device_priv *priv = _device_priv(handle->dev);
+
+ if (!UkwDetachKernelDriver(priv->dev, interface_number))
+ return translate_driver_error(GetLastError());
+
+ return LIBUSB_SUCCESS;
+}
+
+static int wince_attach_kernel_driver(
+ struct libusb_device_handle *handle,
+ int interface_number)
+{
+ struct wince_device_priv *priv = _device_priv(handle->dev);
+
+ if (!UkwAttachKernelDriver(priv->dev, interface_number))
+ return translate_driver_error(GetLastError());
+
+ return LIBUSB_SUCCESS;
+}
+
+static void wince_destroy_device(struct libusb_device *dev)
+{
+ struct wince_device_priv *priv = _device_priv(dev);
+
+ UkwReleaseDeviceList(driver_handle, &priv->dev, 1);
+}
+
+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);
+}
+
+static int wince_cancel_transfer(struct usbi_transfer *itransfer)
+{
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct wince_device_priv *priv = _device_priv(transfer->dev_handle->dev);
+ struct wince_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
+
+ if (!UkwCancelTransfer(priv->dev, transfer_priv->pollable_fd.overlapped, UKW_TF_NO_WAIT))
+ return translate_driver_error(GetLastError());
+
+ return LIBUSB_SUCCESS;
+}
+
+static int wince_submit_control_or_bulk_transfer(struct usbi_transfer *itransfer)
+{
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev);
+ struct wince_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
+ struct wince_device_priv *priv = _device_priv(transfer->dev_handle->dev);
+ 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;
+
+ transfer_priv->pollable_fd = INVALID_WINFD;
+ if (control_transfer) {
+ setup = (PUKW_CONTROL_HEADER) transfer->buffer;
+ direction_in = setup->bmRequestType & LIBUSB_ENDPOINT_IN;
+ } else {
+ direction_in = transfer->endpoint & LIBUSB_ENDPOINT_IN;
+ }
+ 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");
+ 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;
+ }
+
+ transfer_priv->pollable_fd = wfd;
+ if (control_transfer) {
+ // Split out control setup header and data buffer
+ DWORD bufLen = transfer->length - sizeof(UKW_CONTROL_HEADER);
+ PVOID buf = (PVOID) &transfer->buffer[sizeof(UKW_CONTROL_HEADER)];
+
+ ret = UkwIssueControlTransfer(priv->dev, flags, setup, buf, bufLen, &transfer->actual_length, wfd.overlapped);
+ } else {
+ ret = UkwIssueBulkTransfer(priv->dev, flags, transfer->endpoint, transfer->buffer,
+ transfer->length, &transfer->actual_length, wfd.overlapped);
+ }
+
+ if (!ret) {
+ 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);
+ 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);
+
+ switch (transfer->type) {
+ case LIBUSB_TRANSFER_TYPE_CONTROL:
+ case LIBUSB_TRANSFER_TYPE_BULK:
+ 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:
+ usbi_err(TRANSFER_CTX(transfer), "unknown endpoint type %d", transfer->type);
+ return LIBUSB_ERROR_INVALID_PARAM;
+ }
+}
+
+static void wince_transfer_callback(
+ struct usbi_transfer *itransfer,
+ uint32_t io_result, uint32_t io_size)
+{
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct wince_transfer_priv *transfer_priv = (struct wince_transfer_priv*)usbi_transfer_get_os_priv(itransfer);
+ struct wince_device_priv *priv = _device_priv(transfer->dev_handle->dev);
+ int status;
+
+ usbi_dbg("handling I/O completion with errcode %u", io_result);
+
+ if (io_result == ERROR_NOT_SUPPORTED &&
+ transfer->type != LIBUSB_TRANSFER_TYPE_CONTROL) {
+ /* For functional stalls, the WinCE USB layer (and therefore the USB Kernel Wrapper
+ * Driver) will report USB_ERROR_STALL/ERROR_NOT_SUPPORTED in situations where the
+ * endpoint isn't actually stalled.
+ *
+ * One example of this is that some devices will occasionally fail to reply to an IN
+ * token. The WinCE USB layer carries on with the transaction until it is completed
+ * (or cancelled) but then completes it with USB_ERROR_STALL.
+ *
+ * This code therefore needs to confirm that there really is a stall error, by both
+ * checking the pipe status and requesting the endpoint status from the device.
+ */
+ BOOL halted = FALSE;
+ usbi_dbg("checking I/O completion with errcode ERROR_NOT_SUPPORTED is really a stall");
+ if (UkwIsPipeHalted(priv->dev, transfer->endpoint, &halted)) {
+ /* Pipe status retrieved, so now request endpoint status by sending a GET_STATUS
+ * control request to the device. This is done synchronously, which is a bit
+ * naughty, but this is a special corner case.
+ */
+ WORD wStatus = 0;
+ DWORD written = 0;
+ UKW_CONTROL_HEADER ctrlHeader;
+ ctrlHeader.bmRequestType = LIBUSB_REQUEST_TYPE_STANDARD |
+ LIBUSB_ENDPOINT_IN | LIBUSB_RECIPIENT_ENDPOINT;
+ ctrlHeader.bRequest = LIBUSB_REQUEST_GET_STATUS;
+ ctrlHeader.wValue = 0;
+ ctrlHeader.wIndex = transfer->endpoint;
+ ctrlHeader.wLength = sizeof(wStatus);
+ if (UkwIssueControlTransfer(priv->dev,
+ UKW_TF_IN_TRANSFER | UKW_TF_SEND_TO_ENDPOINT,
+ &ctrlHeader, &wStatus, sizeof(wStatus), &written, NULL)) {
+ if (written == sizeof(wStatus) &&
+ (wStatus & STATUS_HALT_FLAG) == 0) {
+ if (!halted || UkwClearHaltHost(priv->dev, transfer->endpoint)) {
+ usbi_dbg("Endpoint doesn't appear to be stalled, overriding error with success");
+ io_result = ERROR_SUCCESS;
+ } else {
+ usbi_dbg("Endpoint doesn't appear to be stalled, but the host is halted, changing error");
+ io_result = ERROR_IO_DEVICE;
+ }
+ }
+ }
+ }
+ }
+
+ switch(io_result) {
+ case ERROR_SUCCESS:
+ itransfer->transferred += io_size;
+ status = LIBUSB_TRANSFER_COMPLETED;
+ break;
+ case ERROR_CANCELLED:
+ usbi_dbg("detected transfer cancel");
+ status = LIBUSB_TRANSFER_CANCELLED;
+ break;
+ case ERROR_NOT_SUPPORTED:
+ 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:
+ usbi_dbg("detected operation aborted");
+ status = LIBUSB_TRANSFER_CANCELLED;
+ break;
+ default:
+ usbi_err(ITRANSFER_CTX(itransfer), "detected I/O error: %s", windows_error_str(io_result));
+ status = LIBUSB_TRANSFER_ERROR;
+ break;
+ }
+
+ wince_clear_transfer_priv(itransfer);
+ if (status == LIBUSB_TRANSFER_CANCELLED)
+ usbi_handle_transfer_cancellation(itransfer);
+ else
+ usbi_handle_transfer_completion(itransfer, (enum libusb_transfer_status)status);
+}
+
+static void wince_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:
+ wince_transfer_callback (itransfer, io_result, io_size);
+ break;
+ case LIBUSB_TRANSFER_TYPE_BULK_STREAM:
+ break;
+ default:
+ usbi_err(ITRANSFER_CTX(itransfer), "unknown endpoint type %d", transfer->type);
+ }
+}
+
+static int wince_handle_events(
+ struct libusb_context *ctx,
+ struct pollfd *fds, POLL_NFDS_TYPE nfds, int num_ready)
+{
+ struct wince_transfer_priv* transfer_priv = NULL;
+ POLL_NFDS_TYPE i = 0;
+ BOOL found = FALSE;
+ struct usbi_transfer *transfer;
+ 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);
+ list_for_each_entry(transfer, &ctx->flying_transfers, list, struct usbi_transfer) {
+ transfer_priv = usbi_transfer_get_os_priv(transfer);
+ if (transfer_priv->pollable_fd.fd == fds[i].fd) {
+ found = TRUE;
+ break;
+ }
+ }
+ usbi_mutex_unlock(&ctx->flying_transfers_lock);
+
+ if (found && HasOverlappedIoCompleted(transfer_priv->pollable_fd.overlapped)) {
+ io_result = (DWORD)transfer_priv->pollable_fd.overlapped->Internal;
+ io_size = (DWORD)transfer_priv->pollable_fd.overlapped->InternalHigh;
+ usbi_remove_pollfd(ctx, transfer_priv->pollable_fd.fd);
+ // let handle_callback free the event using the transfer wfd
+ // If you don't use the transfer wfd, you run a risk of trying to free a
+ // newly allocated wfd that took the place of the one from the transfer.
+ wince_handle_callback(transfer, io_result, io_size);
+ } else if (found) {
+ usbi_err(ctx, "matching transfer for fd %d has not completed", fds[i]);
+ r = LIBUSB_ERROR_OTHER;
+ break;
+ } 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;
+}
+
+/*
+ * Monotonic and real time functions
+ */
+static int wince_clock_gettime(int clk_id, struct timespec *tp)
+{
+ LARGE_INTEGER hires_counter;
+ ULARGE_INTEGER rtime;
+ FILETIME filetime;
+ SYSTEMTIME st;
+
+ switch(clk_id) {
+ case USBI_CLOCK_MONOTONIC:
+ if (hires_frequency != 0 && QueryPerformanceCounter(&hires_counter)) {
+ tp->tv_sec = (long)(hires_counter.QuadPart / hires_frequency);
+ tp->tv_nsec = (long)(((hires_counter.QuadPart % hires_frequency) / 1000) * hires_ticks_to_ps);
+ return LIBUSB_SUCCESS;
+ }
+ // Fall through and return real-time if monotonic read failed or was not detected @ init
+ case USBI_CLOCK_REALTIME:
+ // We follow http://msdn.microsoft.com/en-us/library/ms724928%28VS.85%29.aspx
+ // with a predef epoch time to have an epoch that starts at 1970.01.01 00:00
+ // Note however that our resolution is bounded by the Windows system time
+ // functions and is at best of the order of 1 ms (or, usually, worse)
+ GetSystemTime(&st);
+ SystemTimeToFileTime(&st, &filetime);
+ rtime.LowPart = filetime.dwLowDateTime;
+ rtime.HighPart = filetime.dwHighDateTime;
+ rtime.QuadPart -= EPOCH_TIME;
+ tp->tv_sec = (long)(rtime.QuadPart / 10000000);
+ tp->tv_nsec = (long)((rtime.QuadPart % 10000000)*100);
+ return LIBUSB_SUCCESS;
+ default:
+ return LIBUSB_ERROR_INVALID_PARAM;
+ }
+}
+
+const struct usbi_os_backend wince_backend = {
+ "Windows CE",
+ 0,
+ wince_init,
+ wince_exit,
+
+ wince_get_device_list,
+ NULL, /* hotplug_poll */
+ wince_open,
+ wince_close,
+
+ wince_get_device_descriptor,
+ wince_get_active_config_descriptor,
+ wince_get_config_descriptor,
+ NULL, /* get_config_descriptor_by_value() */
+
+ wince_get_configuration,
+ wince_set_configuration,
+ wince_claim_interface,
+ wince_release_interface,
+
+ wince_set_interface_altsetting,
+ wince_clear_halt,
+ wince_reset_device,
+
+ NULL, /* alloc_streams */
+ NULL, /* free_streams */
+
+ NULL, /* dev_mem_alloc() */
+ NULL, /* dev_mem_free() */
+
+ wince_kernel_driver_active,
+ wince_detach_kernel_driver,
+ wince_attach_kernel_driver,
+
+ wince_destroy_device,
+
+ wince_submit_transfer,
+ wince_cancel_transfer,
+ wince_clear_transfer_priv,
+
+ wince_handle_events,
+ NULL, /* handle_transfer_completion() */
+
+ wince_clock_gettime,
+ sizeof(struct wince_device_priv),
+ 0,
+ sizeof(struct wince_transfer_priv),
+};
diff --git a/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/wince_usb.h b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/wince_usb.h
new file mode 100644
index 000000000..edcb9fcc4
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/wince_usb.h
@@ -0,0 +1,126 @@
+/*
+ * Windows CE backend for libusb 1.0
+ * Copyright © 2011-2013 RealVNC Ltd.
+ * Portions taken from Windows backend, which is
+ * Copyright © 2009-2010 Pete Batard <pbatard@gmail.com>
+ * With contributions from Michael Plante, Orin Eman et al.
+ * Parts of this code adapted from libusb-win32-v1 by Stephan Meyer
+ * Major code testing contribution by Xiaofan Chen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#pragma once
+
+#include "windows_common.h"
+
+#include <windows.h>
+#include "poll_windows.h"
+
+#define MAX_DEVICE_COUNT 256
+
+// This is a modified dump of the types in the ceusbkwrapper.h library header
+// with functions transformed into extern pointers.
+//
+// This backend dynamically loads ceusbkwrapper.dll and doesn't include
+// ceusbkwrapper.h directly to simplify the build process. The kernel
+// side wrapper driver is built using the platform image build tools,
+// which makes it difficult to reference directly from the libusb build
+// system.
+struct UKW_DEVICE_PRIV;
+typedef struct UKW_DEVICE_PRIV *UKW_DEVICE;
+typedef UKW_DEVICE *PUKW_DEVICE, *LPUKW_DEVICE;
+
+typedef struct {
+ UINT8 bLength;
+ UINT8 bDescriptorType;
+ UINT16 bcdUSB;
+ UINT8 bDeviceClass;
+ UINT8 bDeviceSubClass;
+ UINT8 bDeviceProtocol;
+ UINT8 bMaxPacketSize0;
+ UINT16 idVendor;
+ UINT16 idProduct;
+ UINT16 bcdDevice;
+ UINT8 iManufacturer;
+ UINT8 iProduct;
+ UINT8 iSerialNumber;
+ UINT8 bNumConfigurations;
+} UKW_DEVICE_DESCRIPTOR, *PUKW_DEVICE_DESCRIPTOR, *LPUKW_DEVICE_DESCRIPTOR;
+
+typedef struct {
+ UINT8 bmRequestType;
+ UINT8 bRequest;
+ UINT16 wValue;
+ UINT16 wIndex;
+ UINT16 wLength;
+} UKW_CONTROL_HEADER, *PUKW_CONTROL_HEADER, *LPUKW_CONTROL_HEADER;
+
+// Collection of flags which can be used when issuing transfer requests
+/* Indicates that the transfer direction is 'in' */
+#define UKW_TF_IN_TRANSFER 0x00000001
+/* Indicates that the transfer direction is 'out' */
+#define UKW_TF_OUT_TRANSFER 0x00000000
+/* Specifies that the transfer should complete as soon as possible,
+ * even if no OVERLAPPED structure has been provided. */
+#define UKW_TF_NO_WAIT 0x00000100
+/* Indicates that transfers shorter than the buffer are ok */
+#define UKW_TF_SHORT_TRANSFER_OK 0x00000200
+#define UKW_TF_SEND_TO_DEVICE 0x00010000
+#define UKW_TF_SEND_TO_INTERFACE 0x00020000
+#define UKW_TF_SEND_TO_ENDPOINT 0x00040000
+/* Don't block when waiting for memory allocations */
+#define UKW_TF_DONT_BLOCK_FOR_MEM 0x00080000
+
+/* Value to use when dealing with configuration values, such as UkwGetConfigDescriptor,
+ * to specify the currently active configuration for the device. */
+#define UKW_ACTIVE_CONFIGURATION -1
+
+DLL_DECLARE_HANDLE(ceusbkwrapper);
+DLL_DECLARE_FUNC(WINAPI, HANDLE, UkwOpenDriver, ());
+DLL_DECLARE_FUNC(WINAPI, BOOL, UkwGetDeviceList, (HANDLE, LPUKW_DEVICE, DWORD, LPDWORD));
+DLL_DECLARE_FUNC(WINAPI, void, UkwReleaseDeviceList, (HANDLE, LPUKW_DEVICE, DWORD));
+DLL_DECLARE_FUNC(WINAPI, BOOL, UkwGetDeviceAddress, (UKW_DEVICE, unsigned char*, unsigned char*, unsigned long*));
+DLL_DECLARE_FUNC(WINAPI, BOOL, UkwGetDeviceDescriptor, (UKW_DEVICE, LPUKW_DEVICE_DESCRIPTOR));
+DLL_DECLARE_FUNC(WINAPI, BOOL, UkwGetConfigDescriptor, (UKW_DEVICE, DWORD, LPVOID, DWORD, LPDWORD));
+DLL_DECLARE_FUNC(WINAPI, void, UkwCloseDriver, (HANDLE));
+DLL_DECLARE_FUNC(WINAPI, BOOL, UkwCancelTransfer, (UKW_DEVICE, LPOVERLAPPED, DWORD));
+DLL_DECLARE_FUNC(WINAPI, BOOL, UkwIssueControlTransfer, (UKW_DEVICE, DWORD, LPUKW_CONTROL_HEADER, LPVOID, DWORD, LPDWORD, LPOVERLAPPED));
+DLL_DECLARE_FUNC(WINAPI, BOOL, UkwClaimInterface, (UKW_DEVICE, DWORD));
+DLL_DECLARE_FUNC(WINAPI, BOOL, UkwReleaseInterface, (UKW_DEVICE, DWORD));
+DLL_DECLARE_FUNC(WINAPI, BOOL, UkwSetInterfaceAlternateSetting, (UKW_DEVICE, DWORD, DWORD));
+DLL_DECLARE_FUNC(WINAPI, BOOL, UkwClearHaltHost, (UKW_DEVICE, UCHAR));
+DLL_DECLARE_FUNC(WINAPI, BOOL, UkwClearHaltDevice, (UKW_DEVICE, UCHAR));
+DLL_DECLARE_FUNC(WINAPI, BOOL, UkwGetConfig, (UKW_DEVICE, PUCHAR));
+DLL_DECLARE_FUNC(WINAPI, BOOL, UkwSetConfig, (UKW_DEVICE, UCHAR));
+DLL_DECLARE_FUNC(WINAPI, BOOL, UkwResetDevice, (UKW_DEVICE));
+DLL_DECLARE_FUNC(WINAPI, BOOL, UkwKernelDriverActive, (UKW_DEVICE, DWORD, PBOOL));
+DLL_DECLARE_FUNC(WINAPI, BOOL, UkwAttachKernelDriver, (UKW_DEVICE, DWORD));
+DLL_DECLARE_FUNC(WINAPI, BOOL, UkwDetachKernelDriver, (UKW_DEVICE, DWORD));
+DLL_DECLARE_FUNC(WINAPI, BOOL, UkwIssueBulkTransfer, (UKW_DEVICE, DWORD, UCHAR, LPVOID, DWORD, LPDWORD, LPOVERLAPPED));
+DLL_DECLARE_FUNC(WINAPI, BOOL, UkwIsPipeHalted, (UKW_DEVICE, UCHAR, LPBOOL));
+
+// Used to determine if an endpoint status really is halted on a failed transfer.
+#define STATUS_HALT_FLAG 0x1
+
+struct wince_device_priv {
+ UKW_DEVICE dev;
+ UKW_DEVICE_DESCRIPTOR desc;
+};
+
+struct wince_transfer_priv {
+ struct winfd pollable_fd;
+ uint8_t interface_number;
+};
+
diff --git a/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/windows_common.h b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/windows_common.h
new file mode 100644
index 000000000..55344ca26
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/windows_common.h
@@ -0,0 +1,124 @@
+/*
+ * Windows backend common header for libusb 1.0
+ *
+ * This file brings together header code common between
+ * the desktop Windows and Windows CE 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
+
+// Windows API default is uppercase - ugh!
+#if !defined(bool)
+#define bool BOOL
+#endif
+#if !defined(true)
+#define true TRUE
+#endif
+#if !defined(false)
+#define false FALSE
+#endif
+
+#define EPOCH_TIME UINT64_C(116444736000000000) // 1970.01.01 00:00:000 in MS Filetime
+
+#if defined(__CYGWIN__ )
+#define _stricmp strcasecmp
+#define _strdup strdup
+// _beginthreadex is MSVCRT => unavailable for cygwin. Fallback to using CreateThread
+#define _beginthreadex(a, b, c, d, e, f) CreateThread(a, b, (LPTHREAD_START_ROUTINE)c, d, e, (LPDWORD)f)
+#endif
+
+#define safe_free(p) do {if (p != NULL) {free((void *)p); p = NULL;}} while (0)
+
+#ifndef ARRAYSIZE
+#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
+#endif
+
+#define ERR_BUFFER_SIZE 256
+
+/*
+ * API macros - leveraged from libusb-win32 1.x
+ */
+#ifndef _WIN32_WCE
+#define DLL_STRINGIFY(s) #s
+#define DLL_LOAD_LIBRARY(name) LoadLibraryA(DLL_STRINGIFY(name))
+#else
+#define DLL_STRINGIFY(s) L#s
+#define DLL_LOAD_LIBRARY(name) LoadLibrary(DLL_STRINGIFY(name))
+#endif
+
+/*
+ * Macros for handling DLL themselves
+ */
+#define DLL_DECLARE_HANDLE(name) \
+ static HMODULE __dll_##name##_handle = NULL
+
+#define DLL_GET_HANDLE(name) \
+ do { \
+ __dll_##name##_handle = DLL_LOAD_LIBRARY(name); \
+ if (!__dll_##name##_handle) \
+ return LIBUSB_ERROR_OTHER; \
+ } while (0)
+
+#define DLL_FREE_HANDLE(name) \
+ do { \
+ if (__dll_##name##_handle) { \
+ FreeLibrary(__dll_##name##_handle); \
+ __dll_##name##_handle = NULL; \
+ } \
+ } while(0)
+
+
+/*
+ * Macros for handling functions within a DLL
+ */
+#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
+
+#define DLL_DECLARE_FUNC(api, ret, name, args) \
+ DLL_DECLARE_FUNC_PREFIXNAME(api, ret, name, name, args)
+#define DLL_DECLARE_FUNC_PREFIXED(api, ret, prefix, name, args) \
+ DLL_DECLARE_FUNC_PREFIXNAME(api, ret, prefix##name, name, args)
+
+#define DLL_LOAD_FUNC_PREFIXNAME(dll, prefixname, name, ret_on_failure) \
+ do { \
+ HMODULE h = __dll_##dll##_handle; \
+ prefixname = (__dll_##name##_func_t)GetProcAddress(h, \
+ DLL_STRINGIFY(name)); \
+ if (prefixname) \
+ break; \
+ prefixname = (__dll_##name##_func_t)GetProcAddress(h, \
+ DLL_STRINGIFY(name) DLL_STRINGIFY(A)); \
+ if (prefixname) \
+ break; \
+ prefixname = (__dll_##name##_func_t)GetProcAddress(h, \
+ DLL_STRINGIFY(name) DLL_STRINGIFY(W)); \
+ if (prefixname) \
+ break; \
+ if (ret_on_failure) \
+ return LIBUSB_ERROR_NOT_FOUND; \
+ } while(0)
+
+#define DLL_LOAD_FUNC(dll, name, ret_on_failure) \
+ DLL_LOAD_FUNC_PREFIXNAME(dll, name, name, ret_on_failure)
+#define DLL_LOAD_FUNC_PREFIXED(dll, prefix, name, ret_on_failure) \
+ DLL_LOAD_FUNC_PREFIXNAME(dll, prefix##name, name, ret_on_failure)
diff --git a/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/windows_nt_common.c b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/windows_nt_common.c
new file mode 100644
index 000000000..d935394a6
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/windows_nt_common.c
@@ -0,0 +1,591 @@
+/*
+ * windows backend for libusb 1.0
+ * Copyright © 2009-2012 Pete Batard <pete@akeo.ie>
+ * With contributions from Michael Plante, Orin Eman et al.
+ * Parts of this code adapted from libusb-win32-v1 by Stephan Meyer
+ * HID Reports IOCTLs inspired from HIDAPI by Alan Ott, Signal 11 Software
+ * Hash table functions adapted from glibc, by Ulrich Drepper et al.
+ * Major code testing contribution by Xiaofan Chen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include <inttypes.h>
+#include <process.h>
+#include <stdio.h>
+
+#include "libusbi.h"
+#include "windows_common.h"
+#include "windows_nt_common.h"
+
+// Global variables for clock_gettime mechanism
+static uint64_t hires_ticks_to_ps;
+static uint64_t hires_frequency;
+
+#define TIMER_REQUEST_RETRY_MS 100
+#define WM_TIMER_REQUEST (WM_USER + 1)
+#define WM_TIMER_EXIT (WM_USER + 2)
+
+// used for monotonic clock_gettime()
+struct timer_request {
+ struct timespec *tp;
+ HANDLE event;
+};
+
+// Timer thread
+static HANDLE timer_thread = NULL;
+static DWORD timer_thread_id = 0;
+
+/* User32 dependencies */
+DLL_DECLARE_HANDLE(User32);
+DLL_DECLARE_FUNC_PREFIXED(WINAPI, BOOL, p, GetMessageA, (LPMSG, HWND, UINT, UINT));
+DLL_DECLARE_FUNC_PREFIXED(WINAPI, BOOL, p, PeekMessageA, (LPMSG, HWND, UINT, UINT, UINT));
+DLL_DECLARE_FUNC_PREFIXED(WINAPI, BOOL, p, PostThreadMessageA, (DWORD, UINT, WPARAM, LPARAM));
+
+static unsigned __stdcall windows_clock_gettime_threaded(void *param);
+
+/*
+* Converts a windows error to human readable string
+* uses retval as errorcode, or, if 0, use GetLastError()
+*/
+#if defined(ENABLE_LOGGING)
+const char *windows_error_str(DWORD error_code)
+{
+ static char err_string[ERR_BUFFER_SIZE];
+
+ DWORD size;
+ int len;
+
+ if (error_code == 0)
+ error_code = GetLastError();
+
+ len = sprintf(err_string, "[%u] ", (unsigned int)error_code);
+
+ // Translate codes returned by SetupAPI. The ones we are dealing with are either
+ // in 0x0000xxxx or 0xE000xxxx and can be distinguished from standard error codes.
+ // See http://msdn.microsoft.com/en-us/library/windows/hardware/ff545011.aspx
+ switch (error_code & 0xE0000000) {
+ case 0:
+ error_code = HRESULT_FROM_WIN32(error_code); // Still leaves ERROR_SUCCESS unmodified
+ break;
+ case 0xE0000000:
+ error_code = 0x80000000 | (FACILITY_SETUPAPI << 16) | (error_code & 0x0000FFFF);
+ break;
+ default:
+ break;
+ }
+
+ size = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ &err_string[len], ERR_BUFFER_SIZE - len, NULL);
+ if (size == 0) {
+ DWORD format_error = GetLastError();
+ if (format_error)
+ snprintf(err_string, ERR_BUFFER_SIZE,
+ "Windows error code %u (FormatMessage error code %u)",
+ (unsigned int)error_code, (unsigned int)format_error);
+ else
+ snprintf(err_string, ERR_BUFFER_SIZE, "Unknown error code %u", (unsigned int)error_code);
+ } else {
+ // Remove CRLF from end of message, if present
+ size_t pos = len + size - 2;
+ if (err_string[pos] == '\r')
+ err_string[pos] = '\0';
+ }
+
+ return err_string;
+}
+#endif
+
+/* 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) */
+
+#define HTAB_SIZE 1021UL // *MUST* be a prime number!!
+
+typedef struct htab_entry {
+ unsigned long used;
+ char *str;
+} htab_entry;
+
+static htab_entry *htab_table = NULL;
+static usbi_mutex_t htab_mutex = NULL;
+static unsigned long htab_filled;
+
+/* Before using the hash table we must allocate memory for it.
+ We allocate one element more as the found prime number says.
+ This is done for more effective indexing as explained in the
+ comment for the hash function. */
+static bool htab_create(struct libusb_context *ctx)
+{
+ if (htab_table != NULL) {
+ usbi_err(ctx, "hash table already allocated");
+ return true;
+ }
+
+ // Create a mutex
+ usbi_mutex_init(&htab_mutex);
+
+ usbi_dbg("using %lu entries hash table", HTAB_SIZE);
+ htab_filled = 0;
+
+ // allocate memory and zero out.
+ htab_table = calloc(HTAB_SIZE + 1, sizeof(htab_entry));
+ if (htab_table == NULL) {
+ usbi_err(ctx, "could not allocate space for hash table");
+ return false;
+ }
+
+ return true;
+}
+
+/* After using the hash table it has to be destroyed. */
+static void htab_destroy(void)
+{
+ unsigned long i;
+
+ if (htab_table == NULL)
+ return;
+
+ for (i = 0; i < HTAB_SIZE; i++)
+ free(htab_table[i].str);
+
+ safe_free(htab_table);
+
+ usbi_mutex_destroy(&htab_mutex);
+}
+
+/* This is the search function. It uses double hashing with open addressing.
+ We use a trick to speed up the lookup. The table is created with one
+ more element available. This enables us to use the index zero special.
+ This index will never be used because we store the first hash index in
+ the field used where zero means not used. Every other value means used.
+ The used field can be used as a first fast comparison for equality of
+ the stored and the parameter value. This helps to prevent unnecessary
+ expensive calls of strcmp. */
+unsigned long htab_hash(const char *str)
+{
+ unsigned long hval, hval2;
+ unsigned long idx;
+ unsigned long r = 5381;
+ int c;
+ const char *sz = str;
+
+ if (str == NULL)
+ return 0;
+
+ // Compute main hash value (algorithm suggested by Nokia)
+ while ((c = *sz++) != 0)
+ r = ((r << 5) + r) + c;
+ if (r == 0)
+ ++r;
+
+ // compute table hash: simply take the modulus
+ hval = r % HTAB_SIZE;
+ if (hval == 0)
+ ++hval;
+
+ // Try the first index
+ idx = hval;
+
+ // Mutually exclusive access (R/W lock would be better)
+ usbi_mutex_lock(&htab_mutex);
+
+ if (htab_table[idx].used) {
+ if ((htab_table[idx].used == hval) && (strcmp(str, htab_table[idx].str) == 0))
+ goto out_unlock; // existing hash
+
+ usbi_dbg("hash collision ('%s' vs '%s')", str, htab_table[idx].str);
+
+ // Second hash function, as suggested in [Knuth]
+ hval2 = 1 + hval % (HTAB_SIZE - 2);
+
+ do {
+ // Because size is prime this guarantees to step through all available indexes
+ if (idx <= hval2)
+ idx = HTAB_SIZE + idx - hval2;
+ else
+ idx -= hval2;
+
+ // If we visited all entries leave the loop unsuccessfully
+ if (idx == hval)
+ break;
+
+ // If entry is found use it.
+ if ((htab_table[idx].used == hval) && (strcmp(str, htab_table[idx].str) == 0))
+ goto out_unlock;
+ } while (htab_table[idx].used);
+ }
+
+ // Not found => New entry
+
+ // If the table is full return an error
+ if (htab_filled >= HTAB_SIZE) {
+ usbi_err(NULL, "hash table is full (%lu entries)", HTAB_SIZE);
+ idx = 0;
+ goto out_unlock;
+ }
+
+ htab_table[idx].str = _strdup(str);
+ if (htab_table[idx].str == NULL) {
+ usbi_err(NULL, "could not duplicate string for hash table");
+ idx = 0;
+ goto out_unlock;
+ }
+
+ htab_table[idx].used = hval;
+ ++htab_filled;
+
+out_unlock:
+ usbi_mutex_unlock(&htab_mutex);
+
+ return idx;
+}
+
+static int windows_init_dlls(void)
+{
+ 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;
+}
+
+static void windows_exit_dlls(void)
+{
+ DLL_FREE_HANDLE(User32);
+}
+
+static bool windows_init_clock(struct libusb_context *ctx)
+{
+ DWORD_PTR affinity, dummy;
+ HANDLE event = NULL;
+ 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;
+ hires_ticks_to_ps = UINT64_C(1000000000000) / hires_frequency;
+ usbi_dbg("hires timer available (Frequency: %"PRIu64" Hz)", hires_frequency);
+
+ // Because QueryPerformanceCounter might report different values when
+ // running on different cores, we create a separate thread for the timer
+ // calls, which we glue to the first available core always to prevent timing discrepancies.
+ if (!GetProcessAffinityMask(GetCurrentProcess(), &affinity, &dummy) || (affinity == 0)) {
+ usbi_err(ctx, "could not get process affinity: %s", windows_error_str(0));
+ return false;
+ }
+
+ // The process affinity mask is a bitmask where each set bit represents a core on
+ // which this process is allowed to run, so we find the first set bit
+ for (i = 0; !(affinity & (DWORD_PTR)(1 << i)); i++);
+ affinity = (DWORD_PTR)(1 << i);
+
+ usbi_dbg("timer thread will run on core #%d", i);
+
+ event = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if (event == NULL) {
+ usbi_err(ctx, "could not create event: %s", windows_error_str(0));
+ return false;
+ }
+
+ timer_thread = (HANDLE)_beginthreadex(NULL, 0, windows_clock_gettime_threaded, (void *)event,
+ 0, (unsigned int *)&timer_thread_id);
+ if (timer_thread == NULL) {
+ usbi_err(ctx, "unable to create timer thread - aborting");
+ CloseHandle(event);
+ return false;
+ }
+
+ if (!SetThreadAffinityMask(timer_thread, affinity))
+ usbi_warn(ctx, "unable to set timer thread affinity, timer discrepancies may arise");
+
+ // Wait for timer thread to init before continuing.
+ if (WaitForSingleObject(event, INFINITE) != WAIT_OBJECT_0) {
+ usbi_err(ctx, "failed to wait for timer thread to become ready - aborting");
+ CloseHandle(event);
+ return false;
+ }
+
+ CloseHandle(event);
+ } else {
+ usbi_dbg("no hires timer available on this platform");
+ hires_frequency = 0;
+ hires_ticks_to_ps = UINT64_C(0);
+ }
+
+ return true;
+}
+
+void windows_destroy_clock(void)
+{
+ if (timer_thread) {
+ // actually the signal to quit the thread.
+ if (!pPostThreadMessageA(timer_thread_id, WM_TIMER_EXIT, 0, 0)
+ || (WaitForSingleObject(timer_thread, INFINITE) != WAIT_OBJECT_0)) {
+ usbi_dbg("could not wait for timer thread to quit");
+ TerminateThread(timer_thread, 1);
+ // shouldn't happen, but we're destroying
+ // all objects it might have held anyway.
+ }
+ CloseHandle(timer_thread);
+ timer_thread = NULL;
+ timer_thread_id = 0;
+ }
+}
+
+/*
+* Monotonic and real time functions
+*/
+static unsigned __stdcall windows_clock_gettime_threaded(void *param)
+{
+ struct timer_request *request;
+ LARGE_INTEGER hires_counter;
+ MSG msg;
+
+ // The following call will create this thread's message queue
+ // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms644946.aspx
+ pPeekMessageA(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
+
+ // Signal windows_init_clock() that we're ready to service requests
+ if (!SetEvent((HANDLE)param))
+ usbi_dbg("SetEvent failed for timer init event: %s", windows_error_str(0));
+ param = NULL;
+
+ // Main loop - wait for requests
+ while (1) {
+ if (pGetMessageA(&msg, NULL, WM_TIMER_REQUEST, WM_TIMER_EXIT) == -1) {
+ usbi_err(NULL, "GetMessage failed for timer thread: %s", windows_error_str(0));
+ return 1;
+ }
+
+ switch (msg.message) {
+ case WM_TIMER_REQUEST:
+ // Requests to this thread are for hires always
+ // Microsoft says that this function always succeeds on XP and later
+ // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms644904.aspx
+ request = (struct timer_request *)msg.lParam;
+ QueryPerformanceCounter(&hires_counter);
+ request->tp->tv_sec = (long)(hires_counter.QuadPart / hires_frequency);
+ request->tp->tv_nsec = (long)(((hires_counter.QuadPart % hires_frequency) / 1000) * hires_ticks_to_ps);
+ if (!SetEvent(request->event))
+ usbi_err(NULL, "SetEvent failed for timer request: %s", windows_error_str(0));
+ break;
+ case WM_TIMER_EXIT:
+ usbi_dbg("timer thread quitting");
+ return 0;
+ }
+ }
+}
+
+int windows_clock_gettime(int clk_id, struct timespec *tp)
+{
+ struct timer_request request;
+#if !defined(_MSC_VER) || (_MSC_VER < 1900)
+ FILETIME filetime;
+ ULARGE_INTEGER rtime;
+#endif
+ DWORD r;
+
+ switch (clk_id) {
+ case USBI_CLOCK_MONOTONIC:
+ if (timer_thread) {
+ request.tp = tp;
+ request.event = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if (request.event == NULL)
+ return LIBUSB_ERROR_NO_MEM;
+
+ if (!pPostThreadMessageA(timer_thread_id, WM_TIMER_REQUEST, 0, (LPARAM)&request)) {
+ usbi_err(NULL, "PostThreadMessage failed for timer thread: %s", windows_error_str(0));
+ CloseHandle(request.event);
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ do {
+ r = WaitForSingleObject(request.event, TIMER_REQUEST_RETRY_MS);
+ if (r == WAIT_TIMEOUT)
+ usbi_dbg("could not obtain a timer value within reasonable timeframe - too much load?");
+ else if (r == WAIT_FAILED)
+ usbi_err(NULL, "WaitForSingleObject failed: %s", windows_error_str(0));
+ } while (r == WAIT_TIMEOUT);
+ CloseHandle(request.event);
+
+ if (r == WAIT_OBJECT_0)
+ return LIBUSB_SUCCESS;
+ else
+ return LIBUSB_ERROR_OTHER;
+ }
+ // Fall through and return real-time if monotonic was not detected @ timer init
+ case USBI_CLOCK_REALTIME:
+#if defined(_MSC_VER) && (_MSC_VER >= 1900)
+ timespec_get(tp, TIME_UTC);
+#else
+ // We follow http://msdn.microsoft.com/en-us/library/ms724928%28VS.85%29.aspx
+ // with a predef epoch time to have an epoch that starts at 1970.01.01 00:00
+ // Note however that our resolution is bounded by the Windows system time
+ // functions and is at best of the order of 1 ms (or, usually, worse)
+ GetSystemTimeAsFileTime(&filetime);
+ rtime.LowPart = filetime.dwLowDateTime;
+ rtime.HighPart = filetime.dwHighDateTime;
+ rtime.QuadPart -= EPOCH_TIME;
+ tp->tv_sec = (long)(rtime.QuadPart / 10000000);
+ tp->tv_nsec = (long)((rtime.QuadPart % 10000000) * 100);
+#endif
+ return LIBUSB_SUCCESS;
+ default:
+ return LIBUSB_ERROR_INVALID_PARAM;
+ }
+}
+
+static void windows_transfer_callback(struct usbi_transfer *itransfer, uint32_t io_result, uint32_t io_size)
+{
+ 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();
+}
diff --git a/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/windows_nt_common.h b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/windows_nt_common.h
new file mode 100644
index 000000000..52ea8708b
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/windows_nt_common.h
@@ -0,0 +1,63 @@
+/*
+ * 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
diff --git a/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/windows_usbdk.c b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/windows_usbdk.c
new file mode 100644
index 000000000..7cc579387
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/windows_usbdk.c
@@ -0,0 +1,905 @@
+/*
+ * windows UsbDk backend for libusb 1.0
+ * Copyright © 2014 Red Hat, Inc.
+
+ * Authors:
+ * Dmitry Fleytman <dmitry@daynix.com>
+ * Pavel Gurvich <pavel@daynix.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>
+
+#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)
+typedef LONG NTSTATUS;
+#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
+#endif
+
+#if !defined(STATUS_CANCELLED)
+#define STATUS_CANCELLED ((NTSTATUS)0xC0000120L)
+#endif
+
+#if !defined(STATUS_REQUEST_CANCELED)
+#define STATUS_REQUEST_CANCELED ((NTSTATUS)0xC0000703L)
+#endif
+
+#if !defined(USBD_SUCCESS)
+typedef int32_t 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)
+#define USBD_STATUS_STALL_PID ((USBD_STATUS) 0xc0000004)
+#define USBD_STATUS_ENDPOINT_HALTED ((USBD_STATUS) 0xc0000030)
+#define USBD_STATUS_BAD_START_FRAME ((USBD_STATUS) 0xc0000a00)
+#define USBD_STATUS_TIMEOUT ((USBD_STATUS) 0xc0006000)
+#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;
+}
+
+static inline struct usbdk_transfer_priv *_usbdk_transfer_priv(struct usbi_transfer *itransfer)
+{
+ return (struct usbdk_transfer_priv *)usbi_transfer_get_os_priv(itransfer);
+}
+
+static struct {
+ HMODULE module;
+
+ USBDK_GET_DEVICES_LIST GetDevicesList;
+ USBDK_RELEASE_DEVICES_LIST ReleaseDevicesList;
+ USBDK_START_REDIRECT StartRedirect;
+ USBDK_STOP_REDIRECT StopRedirect;
+ USBDK_GET_CONFIGURATION_DESCRIPTOR GetConfigurationDescriptor;
+ USBDK_RELEASE_CONFIGURATION_DESCRIPTOR ReleaseConfigurationDescriptor;
+ USBDK_READ_PIPE ReadPipe;
+ USBDK_WRITE_PIPE WritePipe;
+ USBDK_ABORT_PIPE AbortPipe;
+ USBDK_RESET_PIPE ResetPipe;
+ USBDK_SET_ALTSETTING SetAltsetting;
+ USBDK_RESET_DEVICE ResetDevice;
+ USBDK_GET_REDIRECTOR_SYSTEM_HANDLE GetRedirectorSystemHandle;
+} usbdk_helper;
+
+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());
+
+ return api_ptr;
+}
+
+static void unload_usbdk_helper_dll(void)
+{
+ if (usbdk_helper.module != NULL) {
+ FreeLibrary(usbdk_helper.module);
+ usbdk_helper.module = NULL;
+ }
+}
+
+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());
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
+
+ usbdk_helper.GetDevicesList = (USBDK_GET_DEVICES_LIST)get_usbdk_proc_addr(ctx, "UsbDk_GetDevicesList");
+ if (usbdk_helper.GetDevicesList == NULL)
+ goto error_unload;
+
+ usbdk_helper.ReleaseDevicesList = (USBDK_RELEASE_DEVICES_LIST)get_usbdk_proc_addr(ctx, "UsbDk_ReleaseDevicesList");
+ if (usbdk_helper.ReleaseDevicesList == NULL)
+ goto error_unload;
+
+ usbdk_helper.StartRedirect = (USBDK_START_REDIRECT)get_usbdk_proc_addr(ctx, "UsbDk_StartRedirect");
+ if (usbdk_helper.StartRedirect == NULL)
+ goto error_unload;
+
+ usbdk_helper.StopRedirect = (USBDK_STOP_REDIRECT)get_usbdk_proc_addr(ctx, "UsbDk_StopRedirect");
+ if (usbdk_helper.StopRedirect == NULL)
+ goto error_unload;
+
+ usbdk_helper.GetConfigurationDescriptor = (USBDK_GET_CONFIGURATION_DESCRIPTOR)get_usbdk_proc_addr(ctx, "UsbDk_GetConfigurationDescriptor");
+ if (usbdk_helper.GetConfigurationDescriptor == NULL)
+ goto error_unload;
+
+ usbdk_helper.ReleaseConfigurationDescriptor = (USBDK_RELEASE_CONFIGURATION_DESCRIPTOR)get_usbdk_proc_addr(ctx, "UsbDk_ReleaseConfigurationDescriptor");
+ if (usbdk_helper.ReleaseConfigurationDescriptor == NULL)
+ goto error_unload;
+
+ usbdk_helper.ReadPipe = (USBDK_READ_PIPE)get_usbdk_proc_addr(ctx, "UsbDk_ReadPipe");
+ if (usbdk_helper.ReadPipe == NULL)
+ goto error_unload;
+
+ usbdk_helper.WritePipe = (USBDK_WRITE_PIPE)get_usbdk_proc_addr(ctx, "UsbDk_WritePipe");
+ if (usbdk_helper.WritePipe == NULL)
+ goto error_unload;
+
+ usbdk_helper.AbortPipe = (USBDK_ABORT_PIPE)get_usbdk_proc_addr(ctx, "UsbDk_AbortPipe");
+ if (usbdk_helper.AbortPipe == NULL)
+ goto error_unload;
+
+ usbdk_helper.ResetPipe = (USBDK_RESET_PIPE)get_usbdk_proc_addr(ctx, "UsbDk_ResetPipe");
+ if (usbdk_helper.ResetPipe == NULL)
+ goto error_unload;
+
+ usbdk_helper.SetAltsetting = (USBDK_SET_ALTSETTING)get_usbdk_proc_addr(ctx, "UsbDk_SetAltsetting");
+ if (usbdk_helper.SetAltsetting == NULL)
+ goto error_unload;
+
+ usbdk_helper.ResetDevice = (USBDK_RESET_DEVICE)get_usbdk_proc_addr(ctx, "UsbDk_ResetDevice");
+ if (usbdk_helper.ResetDevice == NULL)
+ goto error_unload;
+
+ usbdk_helper.GetRedirectorSystemHandle = (USBDK_GET_REDIRECTOR_SYSTEM_HANDLE)get_usbdk_proc_addr(ctx, "UsbDk_GetRedirectorSystemHandle");
+ if (usbdk_helper.GetRedirectorSystemHandle == NULL)
+ goto error_unload;
+
+ return LIBUSB_SUCCESS;
+
+error_unload:
+ FreeLibrary(usbdk_helper.module);
+ usbdk_helper.module = NULL;
+ return LIBUSB_ERROR_NOT_FOUND;
+}
+
+static int usbdk_init(struct libusb_context *ctx)
+{
+ int r;
+
+ 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;
+ }
+ // At this stage, either we went through full init successfully, or didn't need to
+ r = LIBUSB_SUCCESS;
+
+init_exit:
+ if (!concurrent_usage && r != LIBUSB_SUCCESS) { // First init failed?
+ exit_polling();
+ windows_common_exit();
+ unload_usbdk_helper_dll();
+ }
+
+ if (r != LIBUSB_SUCCESS)
+ --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)];
+
+ if (sprintf(dev_identity, "%S%S", id->DeviceID, id->InstanceID) == -1) {
+ usbi_warn(ctx, "cannot form device identity", id->DeviceID);
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+ }
+
+ *session_id = htab_hash(dev_identity);
+
+ return LIBUSB_SUCCESS;
+}
+
+static void usbdk_release_config_descriptors(struct usbdk_device_priv *p, uint8_t count)
+{
+ uint8_t i;
+
+ for (i = 0; i < count; i++)
+ usbdk_helper.ReleaseConfigurationDescriptor(p->config_descriptors[i]);
+
+ free(p->config_descriptors);
+ p->config_descriptors = NULL;
+}
+
+static int usbdk_cache_config_descriptors(struct libusb_context *ctx,
+ struct usbdk_device_priv *p, PUSB_DK_DEVICE_INFO info)
+{
+ uint8_t i;
+ USB_DK_CONFIG_DESCRIPTOR_REQUEST Request;
+ Request.ID = info->ID;
+
+ p->config_descriptors = calloc(info->DeviceDescriptor.bNumConfigurations, sizeof(PUSB_CONFIGURATION_DESCRIPTOR));
+ if (p->config_descriptors == NULL) {
+ usbi_err(ctx, "failed to allocate configuration descriptors holder");
+ return LIBUSB_ERROR_NO_MEM;
+ }
+
+ for (i = 0; i < info->DeviceDescriptor.bNumConfigurations; i++) {
+ ULONG Length;
+
+ Request.Index = i;
+ if (!usbdk_helper.GetConfigurationDescriptor(&Request, &p->config_descriptors[i], &Length)) {
+ usbi_err(ctx, "failed to retrieve configuration descriptors");
+ usbdk_release_config_descriptors(p, i);
+ return LIBUSB_ERROR_OTHER;
+ }
+ }
+
+ return LIBUSB_SUCCESS;
+}
+
+static inline int usbdk_device_priv_init(struct libusb_context *ctx, struct libusb_device *dev, PUSB_DK_DEVICE_INFO info)
+{
+ struct usbdk_device_priv *p = _usbdk_device_priv(dev);
+
+ p->info = *info;
+ p->active_configuration = 0;
+
+ return usbdk_cache_config_descriptors(ctx, p, info);
+}
+
+static void usbdk_device_init(libusb_device *dev, PUSB_DK_DEVICE_INFO info)
+{
+ dev->bus_number = (uint8_t)info->FilterID;
+ dev->port_number = (uint8_t)info->Port;
+ dev->parent_dev = NULL;
+
+ //Addresses in libusb are 1-based
+ dev->device_address = (uint8_t)(info->Port + 1);
+
+ dev->num_configurations = info->DeviceDescriptor.bNumConfigurations;
+ dev->device_descriptor = info->DeviceDescriptor;
+
+ switch (info->Speed) {
+ case LowSpeed:
+ dev->speed = LIBUSB_SPEED_LOW;
+ break;
+ case FullSpeed:
+ dev->speed = LIBUSB_SPEED_FULL;
+ break;
+ case HighSpeed:
+ dev->speed = LIBUSB_SPEED_HIGH;
+ break;
+ case SuperSpeed:
+ dev->speed = LIBUSB_SPEED_SUPER;
+ break;
+ case NoSpeed:
+ default:
+ dev->speed = LIBUSB_SPEED_UNKNOWN;
+ break;
+ }
+}
+
+static int usbdk_get_device_list(struct libusb_context *ctx, struct discovered_devs **_discdevs)
+{
+ int r = LIBUSB_SUCCESS;
+ ULONG i;
+ struct discovered_devs *discdevs = NULL;
+ ULONG dev_number;
+ PUSB_DK_DEVICE_INFO devices;
+
+ if(!usbdk_helper.GetDevicesList(&devices, &dev_number))
+ return LIBUSB_ERROR_OTHER;
+
+ for (i = 0; i < dev_number; i++) {
+ unsigned long session_id;
+ struct libusb_device *dev = NULL;
+
+ if (usbdk_get_session_id_for_device(ctx, &devices[i].ID, &session_id))
+ continue;
+
+ dev = usbi_get_device_by_session_id(ctx, session_id);
+ if (dev == NULL) {
+ dev = usbi_alloc_device(ctx, session_id);
+ if (dev == NULL) {
+ usbi_err(ctx, "failed to allocate a new device structure");
+ continue;
+ }
+
+ usbdk_device_init(dev, &devices[i]);
+ if (usbdk_device_priv_init(ctx, dev, &devices[i]) != LIBUSB_SUCCESS) {
+ libusb_unref_device(dev);
+ continue;
+ }
+ }
+
+ discdevs = discovered_devs_append(*_discdevs, dev);
+ libusb_unref_device(dev);
+ if (!discdevs) {
+ usbi_err(ctx, "cannot append new device to list");
+ r = LIBUSB_ERROR_NO_MEM;
+ goto func_exit;
+ }
+
+ *_discdevs = discdevs;
+ }
+
+func_exit:
+ usbdk_helper.ReleaseDevicesList(devices);
+ 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)
+{
+ 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)
+{
+ struct usbdk_device_priv *priv = _usbdk_device_priv(dev);
+ PUSB_CONFIGURATION_DESCRIPTOR config_header;
+ size_t size;
+
+ if (config_index >= dev->num_configurations)
+ return LIBUSB_ERROR_INVALID_PARAM;
+
+ config_header = (PUSB_CONFIGURATION_DESCRIPTOR)priv->config_descriptors[config_index];
+
+ 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)
+{
+ return usbdk_get_config_descriptor(dev, _usbdk_device_priv(dev)->active_configuration,
+ buffer, len, host_endian);
+}
+
+static int usbdk_open(struct libusb_device_handle *dev_handle)
+{
+ struct usbdk_device_priv *priv = _usbdk_device_priv(dev_handle->dev);
+
+ priv->redirector_handle = usbdk_helper.StartRedirect(&priv->info.ID);
+ if (priv->redirector_handle == INVALID_HANDLE_VALUE) {
+ usbi_err(DEVICE_CTX(dev_handle->dev), "Redirector startup failed");
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ return LIBUSB_SUCCESS;
+}
+
+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");
+ }
+}
+
+static int usbdk_get_configuration(struct libusb_device_handle *dev_handle, int *config)
+{
+ *config = _usbdk_device_priv(dev_handle->dev)->active_configuration;
+
+ return LIBUSB_SUCCESS;
+}
+
+static int usbdk_set_configuration(struct libusb_device_handle *dev_handle, int config)
+{
+ UNUSED(dev_handle);
+ UNUSED(config);
+ return LIBUSB_SUCCESS;
+}
+
+static int usbdk_claim_interface(struct libusb_device_handle *dev_handle, int iface)
+{
+ UNUSED(dev_handle);
+ UNUSED(iface);
+ return LIBUSB_SUCCESS;
+}
+
+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 usbdk_device_priv *priv = _usbdk_device_priv(dev_handle->dev);
+
+ if (!usbdk_helper.SetAltsetting(priv->redirector_handle, iface, altsetting)) {
+ usbi_err(ctx, "SetAltsetting failed: %s", windows_error_str(0));
+ return LIBUSB_ERROR_NO_DEVICE;
+ }
+
+ return LIBUSB_SUCCESS;
+}
+
+static int usbdk_release_interface(struct libusb_device_handle *dev_handle, int iface)
+{
+ UNUSED(dev_handle);
+ UNUSED(iface);
+ return LIBUSB_SUCCESS;
+}
+
+static int usbdk_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint)
+{
+ struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev);
+ struct usbdk_device_priv *priv = _usbdk_device_priv(dev_handle->dev);
+
+ if (!usbdk_helper.ResetPipe(priv->redirector_handle, endpoint)) {
+ usbi_err(ctx, "ResetPipe failed: %s", windows_error_str(0));
+ return LIBUSB_ERROR_NO_DEVICE;
+ }
+
+ return LIBUSB_SUCCESS;
+}
+
+static int usbdk_reset_device(struct libusb_device_handle *dev_handle)
+{
+ struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev);
+ struct usbdk_device_priv *priv = _usbdk_device_priv(dev_handle->dev);
+
+ if (!usbdk_helper.ResetDevice(priv->redirector_handle)) {
+ usbi_err(ctx, "ResetDevice failed: %s", windows_error_str(0));
+ return LIBUSB_ERROR_NO_DEVICE;
+ }
+
+ 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);
+
+ if (p->config_descriptors != NULL)
+ usbdk_release_config_descriptors(p, p->info.DeviceDescriptor.bNumConfigurations);
+}
+
+void windows_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);
+
+ if (transfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS) {
+ safe_free(transfer_priv->IsochronousPacketsArray);
+ safe_free(transfer_priv->IsochronousResultsArray);
+ }
+}
+
+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;
+ 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.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);
+ else
+ transResult = usbdk_helper.WritePipe(priv->redirector_handle, &transfer_priv->request, wfd.overlapped);
+
+ switch (transResult) {
+ case TransferSuccess:
+ wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY;
+ wfd.overlapped->InternalHigh = (DWORD)Length;
+ 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;
+}
+
+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;
+ TransferResult transferRes;
+ HANDLE sysHandle;
+
+ transfer_priv->request.Buffer = (PVOID64)(uintptr_t)transfer->buffer;
+ transfer_priv->request.BufferLength = transfer->length;
+ transfer_priv->request.EndpointAddress = transfer->endpoint;
+
+ switch (transfer->type) {
+ case LIBUSB_TRANSFER_TYPE_BULK:
+ transfer_priv->request.TransferType = BulkTransferType;
+ break;
+ case LIBUSB_TRANSFER_TYPE_INTERRUPT:
+ transfer_priv->request.TransferType = IntertuptTransferType;
+ break;
+ default:
+ usbi_err(ctx, "Wrong transfer type (%d) in usbdk_do_bulk_transfer. %s", transfer->type, windows_error_str(0));
+ 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);
+ else
+ transferRes = usbdk_helper.WritePipe(priv->redirector_handle, &transfer_priv->request, wfd.overlapped);
+
+ switch (transferRes) {
+ case TransferSuccess:
+ wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY;
+ 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;
+}
+
+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;
+ TransferResult transferRes;
+ int i;
+ HANDLE sysHandle;
+
+ transfer_priv->request.Buffer = (PVOID64)(uintptr_t)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;
+ if (!transfer_priv->IsochronousPacketsArray) {
+ usbi_err(ctx, "Allocation of IsochronousPacketsArray is failed, %s", windows_error_str(0));
+ return LIBUSB_ERROR_IO;
+ }
+
+ 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;
+ 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;
+ }
+
+ 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);
+ else
+ transferRes = usbdk_helper.WritePipe(priv->redirector_handle, &transfer_priv->request, wfd.overlapped);
+
+ switch (transferRes) {
+ case TransferSuccess:
+ wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY;
+ 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;
+ }
+
+ transfer_priv->pollable_fd = wfd;
+ usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd, IS_XFERIN(transfer) ? POLLIN : POLLOUT);
+
+ return LIBUSB_SUCCESS;
+}
+
+static int usbdk_submit_transfer(struct usbi_transfer *itransfer)
+{
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+
+ switch (transfer->type) {
+ case LIBUSB_TRANSFER_TYPE_CONTROL:
+ return usbdk_do_control_transfer(itransfer);
+ case LIBUSB_TRANSFER_TYPE_BULK:
+ case LIBUSB_TRANSFER_TYPE_INTERRUPT:
+ if (IS_XFEROUT(transfer) && (transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET))
+ return LIBUSB_ERROR_NOT_SUPPORTED; //TODO: Check whether we can support this in UsbDk
+ else
+ return usbdk_do_bulk_transfer(itransfer);
+ case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
+ return usbdk_do_iso_transfer(itransfer);
+ default:
+ usbi_err(TRANSFER_CTX(transfer), "unknown endpoint type %d", transfer->type);
+ return LIBUSB_ERROR_INVALID_PARAM;
+ }
+}
+
+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 usbdk_device_priv *priv = _usbdk_device_priv(transfer->dev_handle->dev);
+
+ 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;
+}
+
+static int usbdk_cancel_transfer(struct usbi_transfer *itransfer)
+{
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+
+ switch (transfer->type) {
+ case LIBUSB_TRANSFER_TYPE_CONTROL:
+ // Control transfers cancelled by IoCancelXXX() API
+ // No special treatment needed
+ return LIBUSB_SUCCESS;
+ case LIBUSB_TRANSFER_TYPE_BULK:
+ case LIBUSB_TRANSFER_TYPE_INTERRUPT:
+ case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
+ return usbdk_abort_transfers(itransfer);
+ default:
+ usbi_err(ITRANSFER_CTX(itransfer), "unknown endpoint type %d", transfer->type);
+ return LIBUSB_ERROR_INVALID_PARAM;
+ }
+}
+
+int windows_copy_transfer_data(struct usbi_transfer *itransfer, uint32_t io_size)
+{
+ itransfer->transferred += io_size;
+ return LIBUSB_TRANSFER_COMPLETED;
+}
+
+struct winfd *windows_get_fd(struct usbi_transfer *transfer)
+{
+ struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(transfer);
+ return &transfer_priv->pollable_fd;
+}
+
+static DWORD usbdk_translate_usbd_status(USBD_STATUS UsbdStatus)
+{
+ if (USBD_SUCCESS(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;
+ }
+}
+
+void windows_get_overlapped_result(struct usbi_transfer *transfer, struct winfd *pollable_fd, 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);
+
+ if (ltransfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS) {
+ int i;
+ for (i = 0; i < transfer_priv->request.IsochronousPacketsArraySize; i++) {
+ struct libusb_iso_packet_descriptor *lib_desc = &ltransfer->iso_packet_desc[i];
+
+ switch (transfer_priv->IsochronousResultsArray[i].TransferResult) {
+ case STATUS_SUCCESS:
+ case STATUS_CANCELLED:
+ case STATUS_REQUEST_CANCELED:
+ lib_desc->status = LIBUSB_TRANSFER_COMPLETED; // == ERROR_SUCCESS
+ break;
+ default:
+ lib_desc->status = LIBUSB_TRANSFER_ERROR; // ERROR_UNKNOWN_EXCEPTION;
+ break;
+ }
+
+ lib_desc->actual_length = (unsigned int)transfer_priv->IsochronousResultsArray[i].ActualLength;
+ }
+ }
+
+ *io_size = (DWORD) transfer_priv->request.Result.GenResult.BytesTransferred;
+ *io_result = usbdk_translate_usbd_status((USBD_STATUS) transfer_priv->request.Result.GenResult.UsbdStatus);
+ }
+ else {
+ *io_result = GetLastError();
+ }
+}
+
+static int usbdk_clock_gettime(int clk_id, struct timespec *tp)
+{
+ return windows_clock_gettime(clk_id, tp);
+}
+
+const struct usbi_os_backend usbdk_backend = {
+ "Windows",
+ USBI_CAP_HAS_HID_ACCESS,
+ 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_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),
+};
+
+#endif /* USE_USBDK */
diff --git a/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/windows_usbdk.h b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/windows_usbdk.h
new file mode 100644
index 000000000..04a9787f2
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/windows_usbdk.h
@@ -0,0 +1,146 @@
+/*
+* windows UsbDk backend for libusb 1.0
+* Copyright © 2014 Red Hat, Inc.
+
+* Authors:
+* Dmitry Fleytman <dmitry@daynix.com>
+* Pavel Gurvich <pavel@daynix.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
+*/
+
+#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;
+
+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 {
+ 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,
+ TransferSuccessAsync
+} TransferResult;
+
+typedef enum {
+ NoSpeed = 0,
+ LowSpeed,
+ FullSpeed,
+ HighSpeed,
+ SuperSpeed
+} USB_DK_DEVICE_SPEED;
+
+typedef enum {
+ ControlTransferType,
+ BulkTransferType,
+ IntertuptTransferType,
+ IsochronousTransferType
+} USB_DK_TRANSFER_TYPE;
+
+typedef BOOL (__cdecl *USBDK_GET_DEVICES_LIST)(
+ PUSB_DK_DEVICE_INFO *DeviceInfo,
+ PULONG DeviceNumber
+);
+typedef void (__cdecl *USBDK_RELEASE_DEVICES_LIST)(
+ PUSB_DK_DEVICE_INFO DeviceInfo
+);
+typedef HANDLE (__cdecl *USBDK_START_REDIRECT)(
+ PUSB_DK_DEVICE_ID DeviceId
+);
+typedef BOOL (__cdecl *USBDK_STOP_REDIRECT)(
+ HANDLE DeviceHandle
+);
+typedef BOOL (__cdecl *USBDK_GET_CONFIGURATION_DESCRIPTOR)(
+ PUSB_DK_CONFIG_DESCRIPTOR_REQUEST Request,
+ PUSB_CONFIGURATION_DESCRIPTOR *Descriptor,
+ PULONG Length
+);
+typedef void (__cdecl *USBDK_RELEASE_CONFIGURATION_DESCRIPTOR)(
+ PUSB_CONFIGURATION_DESCRIPTOR Descriptor
+);
+typedef TransferResult (__cdecl *USBDK_WRITE_PIPE)(
+ HANDLE DeviceHandle,
+ PUSB_DK_TRANSFER_REQUEST Request,
+ LPOVERLAPPED lpOverlapped
+);
+typedef TransferResult (__cdecl *USBDK_READ_PIPE)(
+ HANDLE DeviceHandle,
+ PUSB_DK_TRANSFER_REQUEST Request,
+ LPOVERLAPPED lpOverlapped
+);
+typedef BOOL (__cdecl *USBDK_ABORT_PIPE)(
+ HANDLE DeviceHandle,
+ ULONG64 PipeAddress
+);
+typedef BOOL (__cdecl *USBDK_RESET_PIPE)(
+ HANDLE DeviceHandle,
+ ULONG64 PipeAddress
+);
+typedef BOOL (__cdecl *USBDK_SET_ALTSETTING)(
+ HANDLE DeviceHandle,
+ ULONG64 InterfaceIdx,
+ ULONG64 AltSettingIdx
+);
+typedef BOOL (__cdecl *USBDK_RESET_DEVICE)(
+ HANDLE DeviceHandle
+);
+typedef HANDLE (__cdecl *USBDK_GET_REDIRECTOR_SYSTEM_HANDLE)(
+ HANDLE DeviceHandle
+);
diff --git a/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/windows_winusb.c b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/windows_winusb.c
new file mode 100644
index 000000000..0dce0ea6c
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/windows_winusb.c
@@ -0,0 +1,4290 @@
+/*
+ * windows backend for libusb 1.0
+ * Copyright © 2009-2012 Pete Batard <pete@akeo.ie>
+ * With contributions from Michael Plante, Orin Eman et al.
+ * Parts of this code adapted from libusb-win32-v1 by Stephan Meyer
+ * HID Reports IOCTLs inspired from HIDAPI by Alan Ott, Signal 11 Software
+ * Hash table functions adapted from glibc, by Ulrich Drepper et al.
+ * Major code testing contribution by Xiaofan Chen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#if !defined(USE_USBDK)
+
+#include <windows.h>
+#include <setupapi.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <process.h>
+#include <stdio.h>
+#include <inttypes.h>
+#include <objbase.h>
+#include <winioctl.h>
+
+#include "libusbi.h"
+#include "poll_windows.h"
+#include "windows_winusb.h"
+
+#define HANDLE_VALID(h) (((h) != 0) && ((h) != INVALID_HANDLE_VALUE))
+
+// The 2 macros below are used in conjunction with safe loops.
+#define LOOP_CHECK(fcall) \
+ { \
+ r = fcall; \
+ if (r != LIBUSB_SUCCESS) \
+ continue; \
+ }
+#define LOOP_BREAK(err) \
+ { \
+ r = err; \
+ continue; \
+ }
+
+// WinUSB-like API prototypes
+static int winusbx_init(int sub_api, struct libusb_context *ctx);
+static int winusbx_exit(int sub_api);
+static int winusbx_open(int sub_api, struct libusb_device_handle *dev_handle);
+static void winusbx_close(int sub_api, struct libusb_device_handle *dev_handle);
+static int winusbx_configure_endpoints(int sub_api, struct libusb_device_handle *dev_handle, int iface);
+static int winusbx_claim_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface);
+static int winusbx_release_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface);
+static int winusbx_submit_control_transfer(int sub_api, struct usbi_transfer *itransfer);
+static int winusbx_set_interface_altsetting(int sub_api, struct libusb_device_handle *dev_handle, int iface, int altsetting);
+static int winusbx_submit_bulk_transfer(int sub_api, struct usbi_transfer *itransfer);
+static int winusbx_clear_halt(int sub_api, struct libusb_device_handle *dev_handle, unsigned char endpoint);
+static int winusbx_abort_transfers(int sub_api, struct usbi_transfer *itransfer);
+static int winusbx_abort_control(int sub_api, struct usbi_transfer *itransfer);
+static int winusbx_reset_device(int sub_api, struct libusb_device_handle *dev_handle);
+static int winusbx_copy_transfer_data(int sub_api, struct usbi_transfer *itransfer, uint32_t io_size);
+// HID API prototypes
+static int hid_init(int sub_api, struct libusb_context *ctx);
+static int hid_exit(int sub_api);
+static int hid_open(int sub_api, struct libusb_device_handle *dev_handle);
+static void hid_close(int sub_api, struct libusb_device_handle *dev_handle);
+static int hid_claim_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface);
+static int hid_release_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface);
+static int hid_set_interface_altsetting(int sub_api, struct libusb_device_handle *dev_handle, int iface, int altsetting);
+static int hid_submit_control_transfer(int sub_api, struct usbi_transfer *itransfer);
+static int hid_submit_bulk_transfer(int sub_api, struct usbi_transfer *itransfer);
+static int hid_clear_halt(int sub_api, struct libusb_device_handle *dev_handle, unsigned char endpoint);
+static int hid_abort_transfers(int sub_api, struct usbi_transfer *itransfer);
+static int hid_reset_device(int sub_api, struct libusb_device_handle *dev_handle);
+static int hid_copy_transfer_data(int sub_api, struct usbi_transfer *itransfer, uint32_t io_size);
+// Composite API prototypes
+static int composite_init(int sub_api, struct libusb_context *ctx);
+static int composite_exit(int sub_api);
+static int composite_open(int sub_api, struct libusb_device_handle *dev_handle);
+static void composite_close(int sub_api, struct libusb_device_handle *dev_handle);
+static int composite_claim_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface);
+static int composite_set_interface_altsetting(int sub_api, struct libusb_device_handle *dev_handle, int iface, int altsetting);
+static int composite_release_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface);
+static int composite_submit_control_transfer(int sub_api, struct usbi_transfer *itransfer);
+static int composite_submit_bulk_transfer(int sub_api, struct usbi_transfer *itransfer);
+static int composite_submit_iso_transfer(int sub_api, struct usbi_transfer *itransfer);
+static int composite_clear_halt(int sub_api, struct libusb_device_handle *dev_handle, unsigned char endpoint);
+static int composite_abort_transfers(int sub_api, struct usbi_transfer *itransfer);
+static int composite_abort_control(int sub_api, struct usbi_transfer *itransfer);
+static int composite_reset_device(int sub_api, struct libusb_device_handle *dev_handle);
+static int composite_copy_transfer_data(int sub_api, struct usbi_transfer *itransfer, uint32_t io_size);
+
+
+// Global variables
+int windows_version = WINDOWS_UNDEFINED;
+static char windows_version_str[128] = "Undefined";
+// Concurrency
+static int concurrent_usage = -1;
+static usbi_mutex_t autoclaim_lock;
+// API globals
+#define CHECK_WINUSBX_AVAILABLE(sub_api) \
+ do { \
+ if (sub_api == SUB_API_NOTSET) \
+ sub_api = priv->sub_api; \
+ if (!WinUSBX[sub_api].initialized) \
+ return LIBUSB_ERROR_ACCESS; \
+ } while(0)
+
+static HMODULE WinUSBX_handle = NULL;
+static struct winusb_interface WinUSBX[SUB_API_MAX];
+static const char *sub_api_name[SUB_API_MAX] = WINUSBX_DRV_NAMES;
+
+static bool api_hid_available = false;
+#define CHECK_HID_AVAILABLE \
+ do { \
+ if (!api_hid_available) \
+ return LIBUSB_ERROR_ACCESS; \
+ } while (0)
+
+static inline BOOLEAN guid_eq(const GUID *guid1, const GUID *guid2)
+{
+ if ((guid1 != NULL) && (guid2 != NULL))
+ return (memcmp(guid1, guid2, sizeof(GUID)) == 0);
+
+ return false;
+}
+
+#if defined(ENABLE_LOGGING)
+static char *guid_to_string(const GUID *guid)
+{
+ static char guid_string[MAX_GUID_STRING_LENGTH];
+
+ if (guid == NULL)
+ return NULL;
+
+ sprintf(guid_string, "{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
+ (unsigned int)guid->Data1, guid->Data2, guid->Data3,
+ guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3],
+ guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]);
+
+ return guid_string;
+}
+#endif
+
+/*
+ * Sanitize Microsoft's paths: convert to uppercase, add prefix and fix backslashes.
+ * Return an allocated sanitized string or NULL on error.
+ */
+static char *sanitize_path(const char *path)
+{
+ const char root_prefix[] = { '\\', '\\', '.', '\\' };
+ size_t j, size;
+ char *ret_path;
+ size_t add_root = 0;
+
+ if (path == NULL)
+ return NULL;
+
+ size = strlen(path) + 1;
+
+ // Microsoft indiscriminately uses '\\?\', '\\.\', '##?#" or "##.#" for root prefixes.
+ if (!((size > 3) && (((path[0] == '\\') && (path[1] == '\\') && (path[3] == '\\'))
+ || ((path[0] == '#') && (path[1] == '#') && (path[3] == '#'))))) {
+ add_root = sizeof(root_prefix);
+ size += add_root;
+ }
+
+ ret_path = malloc(size);
+ if (ret_path == NULL)
+ return NULL;
+
+ strcpy(&ret_path[add_root], path);
+
+ // Ensure consistency with root prefix
+ memcpy(ret_path, root_prefix, sizeof(root_prefix));
+
+ // Same goes for '\' and '#' after the root prefix. Ensure '#' is used
+ for (j = sizeof(root_prefix); j < size; j++) {
+ ret_path[j] = (char)toupper((int)ret_path[j]); // Fix case too
+ if (ret_path[j] == '\\')
+ ret_path[j] = '#';
+ }
+
+ return ret_path;
+}
+
+/*
+ * Cfgmgr32, OLE32 and SetupAPI DLL functions
+ */
+static int init_dlls(void)
+{
+ DLL_GET_HANDLE(Cfgmgr32);
+ DLL_LOAD_FUNC(Cfgmgr32, CM_Get_Parent, TRUE);
+ DLL_LOAD_FUNC(Cfgmgr32, CM_Get_Child, TRUE);
+ DLL_LOAD_FUNC(Cfgmgr32, CM_Get_Sibling, TRUE);
+ DLL_LOAD_FUNC(Cfgmgr32, CM_Get_Device_IDA, TRUE);
+
+ // Prefixed to avoid conflict with header files
+ DLL_GET_HANDLE(AdvAPI32);
+ DLL_LOAD_FUNC_PREFIXED(AdvAPI32, p, RegQueryValueExW, TRUE);
+ DLL_LOAD_FUNC_PREFIXED(AdvAPI32, p, RegCloseKey, TRUE);
+
+ DLL_GET_HANDLE(Kernel32);
+ DLL_LOAD_FUNC_PREFIXED(Kernel32, p, IsWow64Process, FALSE);
+
+ DLL_GET_HANDLE(OLE32);
+ DLL_LOAD_FUNC_PREFIXED(OLE32, p, CLSIDFromString, TRUE);
+
+ DLL_GET_HANDLE(SetupAPI);
+ DLL_LOAD_FUNC_PREFIXED(SetupAPI, p, SetupDiGetClassDevsA, TRUE);
+ DLL_LOAD_FUNC_PREFIXED(SetupAPI, p, SetupDiEnumDeviceInfo, TRUE);
+ DLL_LOAD_FUNC_PREFIXED(SetupAPI, p, SetupDiEnumDeviceInterfaces, TRUE);
+ DLL_LOAD_FUNC_PREFIXED(SetupAPI, p, SetupDiGetDeviceInterfaceDetailA, TRUE);
+ DLL_LOAD_FUNC_PREFIXED(SetupAPI, p, SetupDiDestroyDeviceInfoList, TRUE);
+ DLL_LOAD_FUNC_PREFIXED(SetupAPI, p, SetupDiOpenDevRegKey, TRUE);
+ DLL_LOAD_FUNC_PREFIXED(SetupAPI, p, SetupDiGetDeviceRegistryPropertyA, TRUE);
+ DLL_LOAD_FUNC_PREFIXED(SetupAPI, p, SetupDiOpenDeviceInterfaceRegKey, TRUE);
+
+ return LIBUSB_SUCCESS;
+}
+
+static void exit_dlls(void)
+{
+ DLL_FREE_HANDLE(Cfgmgr32);
+ DLL_FREE_HANDLE(AdvAPI32);
+ DLL_FREE_HANDLE(Kernel32);
+ DLL_FREE_HANDLE(OLE32);
+ DLL_FREE_HANDLE(SetupAPI);
+}
+
+/*
+ * enumerate interfaces for the whole USB class
+ *
+ * Parameters:
+ * dev_info: a pointer to a dev_info list
+ * dev_info_data: a pointer to an SP_DEVINFO_DATA to be filled (or NULL if not needed)
+ * usb_class: the generic USB class for which to retrieve interface details
+ * index: zero based index of the interface in the device info list
+ *
+ * Note: it is the responsibility of the caller to free the DEVICE_INTERFACE_DETAIL_DATA
+ * structure returned and call this function repeatedly using the same guid (with an
+ * incremented index starting at zero) until all interfaces have been returned.
+ */
+static bool get_devinfo_data(struct libusb_context *ctx,
+ HDEVINFO *dev_info, SP_DEVINFO_DATA *dev_info_data, const char *usb_class, unsigned _index)
+{
+ if (_index <= 0) {
+ *dev_info = pSetupDiGetClassDevsA(NULL, usb_class, NULL, DIGCF_PRESENT|DIGCF_ALLCLASSES);
+ if (*dev_info == INVALID_HANDLE_VALUE)
+ return false;
+ }
+
+ dev_info_data->cbSize = sizeof(SP_DEVINFO_DATA);
+ if (!pSetupDiEnumDeviceInfo(*dev_info, _index, dev_info_data)) {
+ if (GetLastError() != ERROR_NO_MORE_ITEMS)
+ usbi_err(ctx, "Could not obtain device info data for index %u: %s",
+ _index, windows_error_str(0));
+
+ pSetupDiDestroyDeviceInfoList(*dev_info);
+ *dev_info = INVALID_HANDLE_VALUE;
+ return false;
+ }
+ return true;
+}
+
+/*
+ * enumerate interfaces for a specific GUID
+ *
+ * Parameters:
+ * dev_info: a pointer to a dev_info list
+ * dev_info_data: a pointer to an SP_DEVINFO_DATA to be filled (or NULL if not needed)
+ * guid: the GUID for which to retrieve interface details
+ * index: zero based index of the interface in the device info list
+ *
+ * Note: it is the responsibility of the caller to free the DEVICE_INTERFACE_DETAIL_DATA
+ * structure returned and call this function repeatedly using the same guid (with an
+ * incremented index starting at zero) until all interfaces have been returned.
+ */
+static SP_DEVICE_INTERFACE_DETAIL_DATA_A *get_interface_details(struct libusb_context *ctx,
+ HDEVINFO *dev_info, SP_DEVINFO_DATA *dev_info_data, const GUID *guid, unsigned _index)
+{
+ SP_DEVICE_INTERFACE_DATA dev_interface_data;
+ SP_DEVICE_INTERFACE_DETAIL_DATA_A *dev_interface_details;
+ DWORD size;
+
+ if (_index <= 0)
+ *dev_info = pSetupDiGetClassDevsA(guid, NULL, NULL, DIGCF_PRESENT|DIGCF_DEVICEINTERFACE);
+
+ if (dev_info_data != NULL) {
+ dev_info_data->cbSize = sizeof(SP_DEVINFO_DATA);
+ if (!pSetupDiEnumDeviceInfo(*dev_info, _index, dev_info_data)) {
+ if (GetLastError() != ERROR_NO_MORE_ITEMS)
+ usbi_err(ctx, "Could not obtain device info data for index %u: %s",
+ _index, windows_error_str(0));
+
+ pSetupDiDestroyDeviceInfoList(*dev_info);
+ *dev_info = INVALID_HANDLE_VALUE;
+ return NULL;
+ }
+ }
+
+ dev_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
+ if (!pSetupDiEnumDeviceInterfaces(*dev_info, NULL, guid, _index, &dev_interface_data)) {
+ if (GetLastError() != ERROR_NO_MORE_ITEMS)
+ usbi_err(ctx, "Could not obtain interface data for index %u: %s",
+ _index, windows_error_str(0));
+
+ pSetupDiDestroyDeviceInfoList(*dev_info);
+ *dev_info = INVALID_HANDLE_VALUE;
+ return NULL;
+ }
+
+ // Read interface data (dummy + actual) to access the device path
+ if (!pSetupDiGetDeviceInterfaceDetailA(*dev_info, &dev_interface_data, NULL, 0, &size, NULL)) {
+ // The dummy call should fail with ERROR_INSUFFICIENT_BUFFER
+ if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+ usbi_err(ctx, "could not access interface data (dummy) for index %u: %s",
+ _index, windows_error_str(0));
+ goto err_exit;
+ }
+ } else {
+ usbi_err(ctx, "program assertion failed - http://msdn.microsoft.com/en-us/library/ms792901.aspx is wrong.");
+ goto err_exit;
+ }
+
+ dev_interface_details = calloc(1, size);
+ if (dev_interface_details == NULL) {
+ usbi_err(ctx, "could not allocate interface data for index %u.", _index);
+ goto err_exit;
+ }
+
+ dev_interface_details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A);
+ if (!pSetupDiGetDeviceInterfaceDetailA(*dev_info, &dev_interface_data,
+ dev_interface_details, size, &size, NULL)) {
+ usbi_err(ctx, "could not access interface data (actual) for index %u: %s",
+ _index, windows_error_str(0));
+ }
+
+ return dev_interface_details;
+
+err_exit:
+ pSetupDiDestroyDeviceInfoList(*dev_info);
+ *dev_info = INVALID_HANDLE_VALUE;
+ return NULL;
+}
+
+/* For libusb0 filter */
+static SP_DEVICE_INTERFACE_DETAIL_DATA_A *get_interface_details_filter(struct libusb_context *ctx,
+ HDEVINFO *dev_info, SP_DEVINFO_DATA *dev_info_data, const GUID *guid, unsigned _index, char *filter_path)
+{
+ SP_DEVICE_INTERFACE_DATA dev_interface_data;
+ SP_DEVICE_INTERFACE_DETAIL_DATA_A *dev_interface_details;
+ DWORD size;
+
+ if (_index <= 0)
+ *dev_info = pSetupDiGetClassDevsA(guid, NULL, NULL, DIGCF_PRESENT|DIGCF_DEVICEINTERFACE);
+
+ if (dev_info_data != NULL) {
+ dev_info_data->cbSize = sizeof(SP_DEVINFO_DATA);
+ if (!pSetupDiEnumDeviceInfo(*dev_info, _index, dev_info_data)) {
+ if (GetLastError() != ERROR_NO_MORE_ITEMS)
+ usbi_err(ctx, "Could not obtain device info data for index %u: %s",
+ _index, windows_error_str(0));
+
+ pSetupDiDestroyDeviceInfoList(*dev_info);
+ *dev_info = INVALID_HANDLE_VALUE;
+ return NULL;
+ }
+ }
+
+ dev_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
+ if (!pSetupDiEnumDeviceInterfaces(*dev_info, NULL, guid, _index, &dev_interface_data)) {
+ if (GetLastError() != ERROR_NO_MORE_ITEMS)
+ usbi_err(ctx, "Could not obtain interface data for index %u: %s",
+ _index, windows_error_str(0));
+
+ pSetupDiDestroyDeviceInfoList(*dev_info);
+ *dev_info = INVALID_HANDLE_VALUE;
+ return NULL;
+ }
+
+ // Read interface data (dummy + actual) to access the device path
+ if (!pSetupDiGetDeviceInterfaceDetailA(*dev_info, &dev_interface_data, NULL, 0, &size, NULL)) {
+ // The dummy call should fail with ERROR_INSUFFICIENT_BUFFER
+ if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+ usbi_err(ctx, "could not access interface data (dummy) for index %u: %s",
+ _index, windows_error_str(0));
+ goto err_exit;
+ }
+ } else {
+ usbi_err(ctx, "program assertion failed - http://msdn.microsoft.com/en-us/library/ms792901.aspx is wrong.");
+ goto err_exit;
+ }
+
+ dev_interface_details = calloc(1, size);
+ if (dev_interface_details == NULL) {
+ usbi_err(ctx, "could not allocate interface data for index %u.", _index);
+ goto err_exit;
+ }
+
+ dev_interface_details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A);
+ if (!pSetupDiGetDeviceInterfaceDetailA(*dev_info, &dev_interface_data, dev_interface_details, size, &size, NULL))
+ usbi_err(ctx, "could not access interface data (actual) for index %u: %s",
+ _index, windows_error_str(0));
+
+ // [trobinso] lookup the libusb0 symbolic index.
+ if (dev_interface_details) {
+ HKEY hkey_device_interface = pSetupDiOpenDeviceInterfaceRegKey(*dev_info, &dev_interface_data, 0, KEY_READ);
+ if (hkey_device_interface != INVALID_HANDLE_VALUE) {
+ DWORD libusb0_symboliclink_index = 0;
+ DWORD value_length = sizeof(DWORD);
+ DWORD value_type = 0;
+ LONG status;
+
+ status = pRegQueryValueExW(hkey_device_interface, L"LUsb0", NULL, &value_type,
+ (LPBYTE)&libusb0_symboliclink_index, &value_length);
+ if (status == ERROR_SUCCESS) {
+ if (libusb0_symboliclink_index < 256) {
+ // libusb0.sys is connected to this device instance.
+ // If the the device interface guid is {F9F3FF14-AE21-48A0-8A25-8011A7A931D9} then it's a filter.
+ sprintf(filter_path, "\\\\.\\libusb0-%04u", (unsigned int)libusb0_symboliclink_index);
+ usbi_dbg("assigned libusb0 symbolic link %s", filter_path);
+ } else {
+ // libusb0.sys was connected to this device instance at one time; but not anymore.
+ }
+ }
+ pRegCloseKey(hkey_device_interface);
+ }
+ }
+
+ return dev_interface_details;
+
+err_exit:
+ pSetupDiDestroyDeviceInfoList(*dev_info);
+ *dev_info = INVALID_HANDLE_VALUE;
+ return NULL;
+}
+
+/*
+ * Returns the session ID of a device's nth level ancestor
+ * If there's no device at the nth level, return 0
+ */
+static unsigned long get_ancestor_session_id(DWORD devinst, unsigned level)
+{
+ DWORD parent_devinst;
+ unsigned long session_id;
+ char *sanitized_path;
+ char path[MAX_PATH_LENGTH];
+ unsigned i;
+
+ if (level < 1)
+ return 0;
+
+ for (i = 0; i < level; i++) {
+ if (CM_Get_Parent(&parent_devinst, devinst, 0) != CR_SUCCESS)
+ return 0;
+ devinst = parent_devinst;
+ }
+
+ if (CM_Get_Device_IDA(devinst, path, MAX_PATH_LENGTH, 0) != CR_SUCCESS)
+ return 0;
+
+ // TODO: (post hotplug): try without sanitizing
+ sanitized_path = sanitize_path(path);
+ if (sanitized_path == NULL)
+ return 0;
+
+ session_id = htab_hash(sanitized_path);
+ free(sanitized_path);
+ return session_id;
+}
+
+/*
+ * Determine which interface the given endpoint address belongs to
+ */
+static int get_interface_by_endpoint(struct libusb_config_descriptor *conf_desc, uint8_t ep)
+{
+ const struct libusb_interface *intf;
+ const struct libusb_interface_descriptor *intf_desc;
+ int i, j, k;
+
+ for (i = 0; i < conf_desc->bNumInterfaces; i++) {
+ intf = &conf_desc->interface[i];
+ for (j = 0; j < intf->num_altsetting; j++) {
+ intf_desc = &intf->altsetting[j];
+ for (k = 0; k < intf_desc->bNumEndpoints; k++) {
+ if (intf_desc->endpoint[k].bEndpointAddress == ep) {
+ usbi_dbg("found endpoint %02X on interface %d", intf_desc->bInterfaceNumber, i);
+ return intf_desc->bInterfaceNumber;
+ }
+ }
+ }
+ }
+
+ usbi_dbg("endpoint %02X not found on any interface", ep);
+ return LIBUSB_ERROR_NOT_FOUND;
+}
+
+/*
+ * Populate the endpoints addresses of the device_priv interface helper structs
+ */
+static int windows_assign_endpoints(struct libusb_device_handle *dev_handle, int iface, int altsetting)
+{
+ int i, r;
+ struct windows_device_priv *priv = _device_priv(dev_handle->dev);
+ struct libusb_config_descriptor *conf_desc;
+ const struct libusb_interface_descriptor *if_desc;
+ struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev);
+
+ r = libusb_get_active_config_descriptor(dev_handle->dev, &conf_desc);
+ if (r != LIBUSB_SUCCESS) {
+ usbi_warn(ctx, "could not read config descriptor: error %d", r);
+ return r;
+ }
+
+ if_desc = &conf_desc->interface[iface].altsetting[altsetting];
+ safe_free(priv->usb_interface[iface].endpoint);
+
+ if (if_desc->bNumEndpoints == 0) {
+ usbi_dbg("no endpoints found for interface %d", iface);
+ libusb_free_config_descriptor(conf_desc);
+ return LIBUSB_SUCCESS;
+ }
+
+ priv->usb_interface[iface].endpoint = malloc(if_desc->bNumEndpoints);
+ if (priv->usb_interface[iface].endpoint == NULL) {
+ libusb_free_config_descriptor(conf_desc);
+ return LIBUSB_ERROR_NO_MEM;
+ }
+
+ priv->usb_interface[iface].nb_endpoints = if_desc->bNumEndpoints;
+ for (i = 0; i < if_desc->bNumEndpoints; i++) {
+ priv->usb_interface[iface].endpoint[i] = if_desc->endpoint[i].bEndpointAddress;
+ usbi_dbg("(re)assigned endpoint %02X to interface %d", priv->usb_interface[iface].endpoint[i], iface);
+ }
+ libusb_free_config_descriptor(conf_desc);
+
+ // Extra init may be required to configure endpoints
+ return priv->apib->configure_endpoints(SUB_API_NOTSET, dev_handle, iface);
+}
+
+// Lookup for a match in the list of API driver names
+// return -1 if not found, driver match number otherwise
+static int get_sub_api(char *driver, int api)
+{
+ int i;
+ const char sep_str[2] = {LIST_SEPARATOR, 0};
+ char *tok, *tmp_str;
+ size_t len = strlen(driver);
+
+ if (len == 0)
+ return SUB_API_NOTSET;
+
+ tmp_str = _strdup(driver);
+ if (tmp_str == NULL)
+ return SUB_API_NOTSET;
+
+ tok = strtok(tmp_str, sep_str);
+ while (tok != NULL) {
+ for (i = 0; i < usb_api_backend[api].nb_driver_names; i++) {
+ if (_stricmp(tok, usb_api_backend[api].driver_name_list[i]) == 0) {
+ free(tmp_str);
+ return i;
+ }
+ }
+ tok = strtok(NULL, sep_str);
+ }
+
+ free(tmp_str);
+ return SUB_API_NOTSET;
+}
+
+/*
+ * auto-claiming and auto-release helper functions
+ */
+static int auto_claim(struct libusb_transfer *transfer, int *interface_number, int api_type)
+{
+ struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev);
+ struct windows_device_handle_priv *handle_priv = _device_handle_priv(
+ transfer->dev_handle);
+ struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev);
+ int current_interface = *interface_number;
+ int r = LIBUSB_SUCCESS;
+
+ switch(api_type) {
+ case USB_API_WINUSBX:
+ case USB_API_HID:
+ break;
+ default:
+ return LIBUSB_ERROR_INVALID_PARAM;
+ }
+
+ usbi_mutex_lock(&autoclaim_lock);
+ if (current_interface < 0) { // No serviceable interface was found
+ for (current_interface = 0; current_interface < USB_MAXINTERFACES; current_interface++) {
+ // Must claim an interface of the same API type
+ if ((priv->usb_interface[current_interface].apib->id == api_type)
+ && (libusb_claim_interface(transfer->dev_handle, current_interface) == LIBUSB_SUCCESS)) {
+ usbi_dbg("auto-claimed interface %d for control request", current_interface);
+ if (handle_priv->autoclaim_count[current_interface] != 0)
+ usbi_warn(ctx, "program assertion failed - autoclaim_count was nonzero");
+ handle_priv->autoclaim_count[current_interface]++;
+ break;
+ }
+ }
+ if (current_interface == USB_MAXINTERFACES) {
+ usbi_err(ctx, "could not auto-claim any interface");
+ r = LIBUSB_ERROR_NOT_FOUND;
+ }
+ } else {
+ // If we have a valid interface that was autoclaimed, we must increment
+ // its autoclaim count so that we can prevent an early release.
+ if (handle_priv->autoclaim_count[current_interface] != 0)
+ handle_priv->autoclaim_count[current_interface]++;
+ }
+ usbi_mutex_unlock(&autoclaim_lock);
+
+ *interface_number = current_interface;
+ return r;
+}
+
+static void auto_release(struct usbi_transfer *itransfer)
+{
+ struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ libusb_device_handle *dev_handle = transfer->dev_handle;
+ struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle);
+ int r;
+
+ usbi_mutex_lock(&autoclaim_lock);
+ if (handle_priv->autoclaim_count[transfer_priv->interface_number] > 0) {
+ handle_priv->autoclaim_count[transfer_priv->interface_number]--;
+ if (handle_priv->autoclaim_count[transfer_priv->interface_number] == 0) {
+ r = libusb_release_interface(dev_handle, transfer_priv->interface_number);
+ if (r == LIBUSB_SUCCESS)
+ usbi_dbg("auto-released interface %d", transfer_priv->interface_number);
+ else
+ usbi_dbg("failed to auto-release interface %d (%s)",
+ transfer_priv->interface_number, libusb_error_name((enum libusb_error)r));
+ }
+ }
+ usbi_mutex_unlock(&autoclaim_lock);
+}
+
+/* Windows version dtection */
+static BOOL is_x64(void)
+{
+ BOOL ret = FALSE;
+
+ // Detect if we're running a 32 or 64 bit system
+ if (sizeof(uintptr_t) < 8) {
+ if (pIsWow64Process != NULL)
+ pIsWow64Process(GetCurrentProcess(), &ret);
+ } else {
+ ret = TRUE;
+ }
+
+ return ret;
+}
+
+static void get_windows_version(void)
+{
+ OSVERSIONINFOEXA vi, vi2;
+ const char *arch, *w = NULL;
+ unsigned major, minor;
+ ULONGLONG major_equal, minor_equal;
+ BOOL ws;
+
+ memset(&vi, 0, sizeof(vi));
+ vi.dwOSVersionInfoSize = sizeof(vi);
+ if (!GetVersionExA((OSVERSIONINFOA *)&vi)) {
+ memset(&vi, 0, sizeof(vi));
+ vi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
+ if (!GetVersionExA((OSVERSIONINFOA *)&vi))
+ return;
+ }
+
+ if (vi.dwPlatformId == VER_PLATFORM_WIN32_NT) {
+ if (vi.dwMajorVersion > 6 || (vi.dwMajorVersion == 6 && vi.dwMinorVersion >= 2)) {
+ // Starting with Windows 8.1 Preview, GetVersionEx() does no longer report the actual OS version
+ // See: http://msdn.microsoft.com/en-us/library/windows/desktop/dn302074.aspx
+
+ major_equal = VerSetConditionMask(0, VER_MAJORVERSION, VER_EQUAL);
+ for (major = vi.dwMajorVersion; major <= 9; major++) {
+ memset(&vi2, 0, sizeof(vi2));
+ vi2.dwOSVersionInfoSize = sizeof(vi2);
+ vi2.dwMajorVersion = major;
+ if (!VerifyVersionInfoA(&vi2, VER_MAJORVERSION, major_equal))
+ continue;
+
+ if (vi.dwMajorVersion < major) {
+ vi.dwMajorVersion = major;
+ vi.dwMinorVersion = 0;
+ }
+
+ minor_equal = VerSetConditionMask(0, VER_MINORVERSION, VER_EQUAL);
+ for (minor = vi.dwMinorVersion; minor <= 9; minor++) {
+ memset(&vi2, 0, sizeof(vi2));
+ vi2.dwOSVersionInfoSize = sizeof(vi2);
+ vi2.dwMinorVersion = minor;
+ if (!VerifyVersionInfoA(&vi2, VER_MINORVERSION, minor_equal))
+ continue;
+
+ vi.dwMinorVersion = minor;
+ break;
+ }
+
+ break;
+ }
+ }
+
+ if (vi.dwMajorVersion <= 0xf && vi.dwMinorVersion <= 0xf) {
+ ws = (vi.wProductType <= VER_NT_WORKSTATION);
+ windows_version = vi.dwMajorVersion << 4 | vi.dwMinorVersion;
+ switch (windows_version) {
+ case 0x50: w = "2000"; break;
+ case 0x51: w = "XP"; break;
+ case 0x52: w = "2003"; break;
+ case 0x60: w = (ws ? "Vista" : "2008"); break;
+ case 0x61: w = (ws ? "7" : "2008_R2"); break;
+ case 0x62: w = (ws ? "8" : "2012"); break;
+ case 0x63: w = (ws ? "8.1" : "2012_R2"); break;
+ case 0x64: w = (ws ? "10" : "2015"); break;
+ default:
+ if (windows_version < 0x50)
+ windows_version = WINDOWS_UNSUPPORTED;
+ else
+ w = "11 or later";
+ break;
+ }
+ }
+ }
+
+ arch = is_x64() ? "64-bit" : "32-bit";
+
+ if (w == NULL)
+ snprintf(windows_version_str, sizeof(windows_version_str), "%s %u.%u %s",
+ (vi.dwPlatformId == VER_PLATFORM_WIN32_NT ? "NT" : "??"),
+ (unsigned int)vi.dwMajorVersion, (unsigned int)vi.dwMinorVersion, arch);
+ else if (vi.wServicePackMinor)
+ snprintf(windows_version_str, sizeof(windows_version_str), "%s SP%u.%u %s",
+ w, vi.wServicePackMajor, vi.wServicePackMinor, arch);
+ else if (vi.wServicePackMajor)
+ snprintf(windows_version_str, sizeof(windows_version_str), "%s SP%u %s",
+ w, vi.wServicePackMajor, arch);
+ else
+ snprintf(windows_version_str, sizeof(windows_version_str), "%s %s",
+ w, arch);
+}
+
+/*
+ * init: libusb backend init function
+ *
+ * This function enumerates the HCDs (Host Controller Drivers) and populates our private HCD list
+ * In our implementation, we equate Windows' "HCD" to libusb's "bus". Note that bus is zero indexed.
+ * HCDs are not expected to change after init (might not hold true for hot pluggable USB PCI card?)
+ */
+static int windows_init(struct libusb_context *ctx)
+{
+ int i, r = LIBUSB_ERROR_OTHER;
+ HANDLE semaphore;
+ char sem_name[11 + 8 + 1]; // strlen("libusb_init") + (32-bit hex PID) + '\0'
+
+ sprintf(sem_name, "libusb_init%08X", (unsigned int)(GetCurrentProcessId() & 0xFFFFFFFF));
+ semaphore = CreateSemaphoreA(NULL, 1, 1, sem_name);
+ if (semaphore == NULL) {
+ usbi_err(ctx, "could not create semaphore: %s", windows_error_str(0));
+ return LIBUSB_ERROR_NO_MEM;
+ }
+
+ // A successful wait brings our semaphore count to 0 (unsignaled)
+ // => any concurent wait stalls until the semaphore's release
+ if (WaitForSingleObject(semaphore, INFINITE) != WAIT_OBJECT_0) {
+ usbi_err(ctx, "failure to access semaphore: %s", windows_error_str(0));
+ CloseHandle(semaphore);
+ return LIBUSB_ERROR_NO_MEM;
+ }
+
+ // NB: concurrent usage supposes that init calls are equally balanced with
+ // exit calls. If init is called more than exit, we will not exit properly
+ if (++concurrent_usage == 0) { // First init?
+ get_windows_version();
+ usbi_dbg("Windows %s", windows_version_str);
+
+ if (windows_version == WINDOWS_UNSUPPORTED) {
+ usbi_err(ctx, "This version of Windows is NOT supported");
+ r = LIBUSB_ERROR_NOT_SUPPORTED;
+ goto init_exit;
+ }
+
+ // We need a lock for proper auto-release
+ usbi_mutex_init(&autoclaim_lock);
+
+ // Initialize pollable file descriptors
+ init_polling();
+
+ // Load DLL imports
+ if (init_dlls() != LIBUSB_SUCCESS) {
+ usbi_err(ctx, "could not resolve DLL functions");
+ goto init_exit;
+ }
+
+ // Initialize the low level APIs (we don't care about errors at this stage)
+ for (i = 0; i < USB_API_MAX; i++)
+ usb_api_backend[i].init(SUB_API_NOTSET, ctx);
+
+ r = windows_common_init(ctx);
+ if (r)
+ goto init_exit;
+ }
+ // At this stage, either we went through full init successfully, or didn't need to
+ r = LIBUSB_SUCCESS;
+
+init_exit: // Holds semaphore here.
+ if (!concurrent_usage && r != LIBUSB_SUCCESS) { // First init failed?
+ for (i = 0; i < USB_API_MAX; i++)
+ usb_api_backend[i].exit(SUB_API_NOTSET);
+ exit_dlls();
+ exit_polling();
+ windows_common_exit();
+ usbi_mutex_destroy(&autoclaim_lock);
+ }
+
+ if (r != LIBUSB_SUCCESS)
+ --concurrent_usage; // Not expected to call libusb_exit if we failed.
+
+ ReleaseSemaphore(semaphore, 1, NULL); // increase count back to 1
+ CloseHandle(semaphore);
+ return r;
+}
+
+/*
+ * HCD (root) hubs need to have their device descriptor manually populated
+ *
+ * Note that, like Microsoft does in the device manager, we populate the
+ * Vendor and Device ID for HCD hubs with the ones from the PCI HCD device.
+ */
+static int force_hcd_device_descriptor(struct libusb_device *dev)
+{
+ struct windows_device_priv *parent_priv, *priv = _device_priv(dev);
+ struct libusb_context *ctx = DEVICE_CTX(dev);
+ int vid, pid;
+
+ dev->num_configurations = 1;
+ priv->dev_descriptor.bLength = sizeof(USB_DEVICE_DESCRIPTOR);
+ priv->dev_descriptor.bDescriptorType = USB_DEVICE_DESCRIPTOR_TYPE;
+ priv->dev_descriptor.bNumConfigurations = 1;
+ priv->active_config = 1;
+
+ if (dev->parent_dev == NULL) {
+ usbi_err(ctx, "program assertion failed - HCD hub has no parent");
+ return LIBUSB_ERROR_NO_DEVICE;
+ }
+
+ parent_priv = _device_priv(dev->parent_dev);
+ if (sscanf(parent_priv->path, "\\\\.\\PCI#VEN_%04x&DEV_%04x%*s", &vid, &pid) == 2) {
+ priv->dev_descriptor.idVendor = (uint16_t)vid;
+ priv->dev_descriptor.idProduct = (uint16_t)pid;
+ } else {
+ usbi_warn(ctx, "could not infer VID/PID of HCD hub from '%s'", parent_priv->path);
+ priv->dev_descriptor.idVendor = 0x1d6b; // Linux Foundation root hub
+ priv->dev_descriptor.idProduct = 1;
+ }
+
+ return LIBUSB_SUCCESS;
+}
+
+/*
+ * fetch and cache all the config descriptors through I/O
+ */
+static int cache_config_descriptors(struct libusb_device *dev, HANDLE hub_handle, char *device_id)
+{
+ DWORD size, ret_size;
+ struct libusb_context *ctx = DEVICE_CTX(dev);
+ struct windows_device_priv *priv = _device_priv(dev);
+ int r;
+ uint8_t i;
+
+ USB_CONFIGURATION_DESCRIPTOR_SHORT cd_buf_short; // dummy request
+ PUSB_DESCRIPTOR_REQUEST cd_buf_actual = NULL; // actual request
+ PUSB_CONFIGURATION_DESCRIPTOR cd_data = NULL;
+
+ if (dev->num_configurations == 0)
+ return LIBUSB_ERROR_INVALID_PARAM;
+
+ priv->config_descriptor = calloc(dev->num_configurations, sizeof(unsigned char *));
+ if (priv->config_descriptor == NULL)
+ return LIBUSB_ERROR_NO_MEM;
+
+ for (i = 0; i < dev->num_configurations; i++)
+ priv->config_descriptor[i] = NULL;
+
+ for (i = 0, r = LIBUSB_SUCCESS; ; i++) {
+ // safe loop: release all dynamic resources
+ safe_free(cd_buf_actual);
+
+ // safe loop: end of loop condition
+ if ((i >= dev->num_configurations) || (r != LIBUSB_SUCCESS))
+ break;
+
+ size = sizeof(USB_CONFIGURATION_DESCRIPTOR_SHORT);
+ memset(&cd_buf_short, 0, size);
+
+ cd_buf_short.req.ConnectionIndex = (ULONG)priv->port;
+ cd_buf_short.req.SetupPacket.bmRequest = LIBUSB_ENDPOINT_IN;
+ cd_buf_short.req.SetupPacket.bRequest = USB_REQUEST_GET_DESCRIPTOR;
+ cd_buf_short.req.SetupPacket.wValue = (USB_CONFIGURATION_DESCRIPTOR_TYPE << 8) | i;
+ cd_buf_short.req.SetupPacket.wIndex = 0;
+ cd_buf_short.req.SetupPacket.wLength = (USHORT)(size - sizeof(USB_DESCRIPTOR_REQUEST));
+
+ // Dummy call to get the required data size. Initial failures are reported as info rather
+ // than error as they can occur for non-penalizing situations, such as with some hubs.
+ // coverity[tainted_data_argument]
+ if (!DeviceIoControl(hub_handle, IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, &cd_buf_short, size,
+ &cd_buf_short, size, &ret_size, NULL)) {
+ usbi_info(ctx, "could not access configuration descriptor (dummy) for '%s': %s", device_id, windows_error_str(0));
+ LOOP_BREAK(LIBUSB_ERROR_IO);
+ }
+
+ if ((ret_size != size) || (cd_buf_short.data.wTotalLength < sizeof(USB_CONFIGURATION_DESCRIPTOR))) {
+ usbi_info(ctx, "unexpected configuration descriptor size (dummy) for '%s'.", device_id);
+ LOOP_BREAK(LIBUSB_ERROR_IO);
+ }
+
+ size = sizeof(USB_DESCRIPTOR_REQUEST) + cd_buf_short.data.wTotalLength;
+ cd_buf_actual = calloc(1, size);
+ if (cd_buf_actual == NULL) {
+ usbi_err(ctx, "could not allocate configuration descriptor buffer for '%s'.", device_id);
+ LOOP_BREAK(LIBUSB_ERROR_NO_MEM);
+ }
+
+ // Actual call
+ cd_buf_actual->ConnectionIndex = (ULONG)priv->port;
+ cd_buf_actual->SetupPacket.bmRequest = LIBUSB_ENDPOINT_IN;
+ cd_buf_actual->SetupPacket.bRequest = USB_REQUEST_GET_DESCRIPTOR;
+ cd_buf_actual->SetupPacket.wValue = (USB_CONFIGURATION_DESCRIPTOR_TYPE << 8) | i;
+ cd_buf_actual->SetupPacket.wIndex = 0;
+ cd_buf_actual->SetupPacket.wLength = (USHORT)(size - sizeof(USB_DESCRIPTOR_REQUEST));
+
+ if (!DeviceIoControl(hub_handle, IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, cd_buf_actual, size,
+ cd_buf_actual, size, &ret_size, NULL)) {
+ usbi_err(ctx, "could not access configuration descriptor (actual) for '%s': %s", device_id, windows_error_str(0));
+ LOOP_BREAK(LIBUSB_ERROR_IO);
+ }
+
+ cd_data = (PUSB_CONFIGURATION_DESCRIPTOR)((UCHAR *)cd_buf_actual + sizeof(USB_DESCRIPTOR_REQUEST));
+
+ if ((size != ret_size) || (cd_data->wTotalLength != cd_buf_short.data.wTotalLength)) {
+ usbi_err(ctx, "unexpected configuration descriptor size (actual) for '%s'.", device_id);
+ LOOP_BREAK(LIBUSB_ERROR_IO);
+ }
+
+ if (cd_data->bDescriptorType != USB_CONFIGURATION_DESCRIPTOR_TYPE) {
+ usbi_err(ctx, "not a configuration descriptor for '%s'", device_id);
+ LOOP_BREAK(LIBUSB_ERROR_IO);
+ }
+
+ usbi_dbg("cached config descriptor %d (bConfigurationValue=%u, %u bytes)",
+ i, cd_data->bConfigurationValue, cd_data->wTotalLength);
+
+ // Cache the descriptor
+ priv->config_descriptor[i] = malloc(cd_data->wTotalLength);
+ if (priv->config_descriptor[i] == NULL)
+ LOOP_BREAK(LIBUSB_ERROR_NO_MEM);
+ memcpy(priv->config_descriptor[i], cd_data, cd_data->wTotalLength);
+ }
+ return LIBUSB_SUCCESS;
+}
+
+/*
+ * Populate a libusb device structure
+ */
+static int init_device(struct libusb_device *dev, struct libusb_device *parent_dev,
+ uint8_t port_number, char *device_id, DWORD devinst)
+{
+ HANDLE handle;
+ DWORD size;
+ USB_NODE_CONNECTION_INFORMATION_EX conn_info;
+ USB_NODE_CONNECTION_INFORMATION_EX_V2 conn_info_v2;
+ struct windows_device_priv *priv, *parent_priv;
+ struct libusb_context *ctx;
+ struct libusb_device *tmp_dev;
+ unsigned long tmp_id;
+ unsigned i;
+
+ if ((dev == NULL) || (parent_dev == NULL))
+ return LIBUSB_ERROR_NOT_FOUND;
+
+ ctx = DEVICE_CTX(dev);
+ priv = _device_priv(dev);
+ parent_priv = _device_priv(parent_dev);
+ if (parent_priv->apib->id != USB_API_HUB) {
+ usbi_warn(ctx, "parent for device '%s' is not a hub", device_id);
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
+
+ // It is possible for the parent hub not to have been initialized yet
+ // If that's the case, lookup the ancestors to set the bus number
+ if (parent_dev->bus_number == 0) {
+ for (i = 2; ; i++) {
+ tmp_id = get_ancestor_session_id(devinst, i);
+ if (tmp_id == 0)
+ break;
+
+ tmp_dev = usbi_get_device_by_session_id(ctx, tmp_id);
+ if (tmp_dev == NULL)
+ continue;
+
+ if (tmp_dev->bus_number != 0) {
+ usbi_dbg("got bus number from ancestor #%u", i);
+ parent_dev->bus_number = tmp_dev->bus_number;
+ libusb_unref_device(tmp_dev);
+ break;
+ }
+
+ libusb_unref_device(tmp_dev);
+ }
+ }
+
+ if (parent_dev->bus_number == 0) {
+ usbi_err(ctx, "program assertion failed: unable to find ancestor bus number for '%s'", device_id);
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
+
+ dev->bus_number = parent_dev->bus_number;
+ priv->port = port_number;
+ dev->port_number = port_number;
+ priv->depth = parent_priv->depth + 1;
+ dev->parent_dev = parent_dev;
+
+ // If the device address is already set, we can stop here
+ if (dev->device_address != 0)
+ return LIBUSB_SUCCESS;
+
+ memset(&conn_info, 0, sizeof(conn_info));
+ if (priv->depth != 0) { // Not a HCD hub
+ handle = CreateFileA(parent_priv->path, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
+ FILE_FLAG_OVERLAPPED, NULL);
+ if (handle == INVALID_HANDLE_VALUE) {
+ usbi_warn(ctx, "could not open hub %s: %s", parent_priv->path, windows_error_str(0));
+ return LIBUSB_ERROR_ACCESS;
+ }
+
+ size = sizeof(conn_info);
+ conn_info.ConnectionIndex = (ULONG)port_number;
+ // coverity[tainted_data_argument]
+ if (!DeviceIoControl(handle, IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX, &conn_info, size,
+ &conn_info, size, &size, NULL)) {
+ usbi_warn(ctx, "could not get node connection information for device '%s': %s",
+ device_id, windows_error_str(0));
+ CloseHandle(handle);
+ return LIBUSB_ERROR_NO_DEVICE;
+ }
+
+ if (conn_info.ConnectionStatus == NoDeviceConnected) {
+ usbi_err(ctx, "device '%s' is no longer connected!", device_id);
+ CloseHandle(handle);
+ return LIBUSB_ERROR_NO_DEVICE;
+ }
+
+ memcpy(&priv->dev_descriptor, &(conn_info.DeviceDescriptor), sizeof(USB_DEVICE_DESCRIPTOR));
+ dev->num_configurations = priv->dev_descriptor.bNumConfigurations;
+ priv->active_config = conn_info.CurrentConfigurationValue;
+ usbi_dbg("found %u configurations (active conf: %u)", dev->num_configurations, priv->active_config);
+
+ // If we can't read the config descriptors, just set the number of confs to zero
+ if (cache_config_descriptors(dev, handle, device_id) != LIBUSB_SUCCESS) {
+ dev->num_configurations = 0;
+ priv->dev_descriptor.bNumConfigurations = 0;
+ }
+
+ // In their great wisdom, Microsoft decided to BREAK the USB speed report between Windows 7 and Windows 8
+ if (windows_version >= WINDOWS_8) {
+ memset(&conn_info_v2, 0, sizeof(conn_info_v2));
+ size = sizeof(conn_info_v2);
+ conn_info_v2.ConnectionIndex = (ULONG)port_number;
+ conn_info_v2.Length = size;
+ conn_info_v2.SupportedUsbProtocols.Usb300 = 1;
+ if (!DeviceIoControl(handle, IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX_V2,
+ &conn_info_v2, size, &conn_info_v2, size, &size, NULL)) {
+ usbi_warn(ctx, "could not get node connection information (V2) for device '%s': %s",
+ device_id, windows_error_str(0));
+ } else if (conn_info_v2.Flags.DeviceIsOperatingAtSuperSpeedOrHigher) {
+ conn_info.Speed = 3;
+ }
+ }
+
+ CloseHandle(handle);
+
+ if (conn_info.DeviceAddress > UINT8_MAX)
+ usbi_err(ctx, "program assertion failed: device address overflow");
+
+ dev->device_address = (uint8_t)conn_info.DeviceAddress + 1;
+ if (dev->device_address == 1)
+ usbi_err(ctx, "program assertion failed: device address collision with root hub");
+
+ switch (conn_info.Speed) {
+ case 0: dev->speed = LIBUSB_SPEED_LOW; break;
+ case 1: dev->speed = LIBUSB_SPEED_FULL; break;
+ case 2: dev->speed = LIBUSB_SPEED_HIGH; break;
+ case 3: dev->speed = LIBUSB_SPEED_SUPER; break;
+ default:
+ usbi_warn(ctx, "Got unknown device speed %u", conn_info.Speed);
+ break;
+ }
+ } else {
+ dev->device_address = 1; // root hubs are set to use device number 1
+ force_hcd_device_descriptor(dev);
+ }
+
+ usbi_sanitize_device(dev);
+
+ usbi_dbg("(bus: %u, addr: %u, depth: %u, port: %u): '%s'",
+ dev->bus_number, dev->device_address, priv->depth, priv->port, device_id);
+
+ return LIBUSB_SUCCESS;
+}
+
+// Returns the api type, or 0 if not found/unsupported
+static void get_api_type(struct libusb_context *ctx, HDEVINFO *dev_info,
+ SP_DEVINFO_DATA *dev_info_data, int *api, int *sub_api)
+{
+ // Precedence for filter drivers vs driver is in the order of this array
+ struct driver_lookup lookup[3] = {
+ {"\0\0", SPDRP_SERVICE, "driver"},
+ {"\0\0", SPDRP_UPPERFILTERS, "upper filter driver"},
+ {"\0\0", SPDRP_LOWERFILTERS, "lower filter driver"}
+ };
+ DWORD size, reg_type;
+ unsigned k, l;
+ int i, j;
+
+ *api = USB_API_UNSUPPORTED;
+ *sub_api = SUB_API_NOTSET;
+
+ // Check the service & filter names to know the API we should use
+ for (k = 0; k < 3; k++) {
+ if (pSetupDiGetDeviceRegistryPropertyA(*dev_info, dev_info_data, lookup[k].reg_prop,
+ &reg_type, (BYTE *)lookup[k].list, MAX_KEY_LENGTH, &size)) {
+ // Turn the REG_SZ SPDRP_SERVICE into REG_MULTI_SZ
+ if (lookup[k].reg_prop == SPDRP_SERVICE)
+ // our buffers are MAX_KEY_LENGTH + 1 so we can overflow if needed
+ lookup[k].list[strlen(lookup[k].list) + 1] = 0;
+
+ // MULTI_SZ is a pain to work with. Turn it into something much more manageable
+ // NB: none of the driver names we check against contain LIST_SEPARATOR,
+ // (currently ';'), so even if an unsuported one does, it's not an issue
+ for (l = 0; (lookup[k].list[l] != 0) || (lookup[k].list[l + 1] != 0); l++) {
+ if (lookup[k].list[l] == 0)
+ lookup[k].list[l] = LIST_SEPARATOR;
+ }
+ usbi_dbg("%s(s): %s", lookup[k].designation, lookup[k].list);
+ } else {
+ if (GetLastError() != ERROR_INVALID_DATA)
+ usbi_dbg("could not access %s: %s", lookup[k].designation, windows_error_str(0));
+ lookup[k].list[0] = 0;
+ }
+ }
+
+ for (i = 1; i < USB_API_MAX; i++) {
+ for (k = 0; k < 3; k++) {
+ j = get_sub_api(lookup[k].list, i);
+ if (j >= 0) {
+ usbi_dbg("matched %s name against %s", lookup[k].designation,
+ (i != USB_API_WINUSBX) ? usb_api_backend[i].designation : sub_api_name[j]);
+ *api = i;
+ *sub_api = j;
+ return;
+ }
+ }
+ }
+}
+
+static int set_composite_interface(struct libusb_context *ctx, struct libusb_device *dev,
+ char *dev_interface_path, char *device_id, int api, int sub_api)
+{
+ unsigned i;
+ struct windows_device_priv *priv = _device_priv(dev);
+ int interface_number;
+
+ if (priv->apib->id != USB_API_COMPOSITE) {
+ usbi_err(ctx, "program assertion failed: '%s' is not composite", device_id);
+ return LIBUSB_ERROR_NO_DEVICE;
+ }
+
+ // Because MI_## are not necessarily in sequential order (some composite
+ // devices will have only MI_00 & MI_03 for instance), we retrieve the actual
+ // interface number from the path's MI value
+ interface_number = 0;
+ for (i = 0; device_id[i] != 0; ) {
+ if ((device_id[i++] == 'M') && (device_id[i++] == 'I')
+ && (device_id[i++] == '_')) {
+ interface_number = (device_id[i++] - '0') * 10;
+ interface_number += device_id[i] - '0';
+ break;
+ }
+ }
+
+ if (device_id[i] == 0)
+ usbi_warn(ctx, "failure to read interface number for %s. Using default value %d",
+ device_id, interface_number);
+
+ if (priv->usb_interface[interface_number].path != NULL) {
+ if (api == USB_API_HID) {
+ // HID devices can have multiple collections (COL##) for each MI_## interface
+ usbi_dbg("interface[%d] already set - ignoring HID collection: %s",
+ interface_number, device_id);
+ return LIBUSB_ERROR_ACCESS;
+ }
+ // In other cases, just use the latest data
+ safe_free(priv->usb_interface[interface_number].path);
+ }
+
+ usbi_dbg("interface[%d] = %s", interface_number, dev_interface_path);
+ priv->usb_interface[interface_number].path = dev_interface_path;
+ priv->usb_interface[interface_number].apib = &usb_api_backend[api];
+ priv->usb_interface[interface_number].sub_api = sub_api;
+ if ((api == USB_API_HID) && (priv->hid == NULL)) {
+ priv->hid = calloc(1, sizeof(struct hid_device_priv));
+ if (priv->hid == NULL)
+ return LIBUSB_ERROR_NO_MEM;
+ }
+
+ return LIBUSB_SUCCESS;
+}
+
+static int set_hid_interface(struct libusb_context *ctx, struct libusb_device *dev,
+ char *dev_interface_path)
+{
+ int i;
+ struct windows_device_priv *priv = _device_priv(dev);
+
+ if (priv->hid == NULL) {
+ usbi_err(ctx, "program assertion failed: parent is not HID");
+ return LIBUSB_ERROR_NO_DEVICE;
+ } else if (priv->hid->nb_interfaces == USB_MAXINTERFACES) {
+ usbi_err(ctx, "program assertion failed: max USB interfaces reached for HID device");
+ return LIBUSB_ERROR_NO_DEVICE;
+ }
+
+ for (i = 0; i < priv->hid->nb_interfaces; i++) {
+ if ((priv->usb_interface[i].path != NULL) && strcmp(priv->usb_interface[i].path, dev_interface_path) == 0) {
+ usbi_dbg("interface[%d] already set to %s", i, dev_interface_path);
+ return LIBUSB_ERROR_ACCESS;
+ }
+ }
+
+ priv->usb_interface[priv->hid->nb_interfaces].path = dev_interface_path;
+ priv->usb_interface[priv->hid->nb_interfaces].apib = &usb_api_backend[USB_API_HID];
+ usbi_dbg("interface[%u] = %s", priv->hid->nb_interfaces, dev_interface_path);
+ priv->hid->nb_interfaces++;
+ return LIBUSB_SUCCESS;
+}
+
+/*
+ * get_device_list: libusb backend device enumeration function
+ */
+static int windows_get_device_list(struct libusb_context *ctx, struct discovered_devs **_discdevs)
+{
+ struct discovered_devs *discdevs;
+ HDEVINFO dev_info = { 0 };
+ const char *usb_class[] = {"USB", "NUSB3", "IUSB3", "IARUSB3"};
+ SP_DEVINFO_DATA dev_info_data = { 0 };
+ SP_DEVICE_INTERFACE_DETAIL_DATA_A *dev_interface_details = NULL;
+ GUID hid_guid;
+#define MAX_ENUM_GUIDS 64
+ const GUID *guid[MAX_ENUM_GUIDS];
+#define HCD_PASS 0
+#define HUB_PASS 1
+#define GEN_PASS 2
+#define DEV_PASS 3
+#define HID_PASS 4
+ int r = LIBUSB_SUCCESS;
+ int api, sub_api;
+ size_t class_index = 0;
+ unsigned int nb_guids, pass, i, j, ancestor;
+ char path[MAX_PATH_LENGTH];
+ char strbuf[MAX_PATH_LENGTH];
+ struct libusb_device *dev, *parent_dev;
+ struct windows_device_priv *priv, *parent_priv;
+ char *dev_interface_path = NULL;
+ char *dev_id_path = NULL;
+ unsigned long session_id;
+ DWORD size, reg_type, port_nr, install_state;
+ HKEY key;
+ WCHAR guid_string_w[MAX_GUID_STRING_LENGTH];
+ GUID *if_guid;
+ LONG s;
+ // Keep a list of newly allocated devs to unref
+ libusb_device **unref_list, **new_unref_list;
+ unsigned int unref_size = 64;
+ unsigned int unref_cur = 0;
+
+ // PASS 1 : (re)enumerate HCDs (allows for HCD hotplug)
+ // PASS 2 : (re)enumerate HUBS
+ // PASS 3 : (re)enumerate generic USB devices (including driverless)
+ // and list additional USB device interface GUIDs to explore
+ // PASS 4 : (re)enumerate master USB devices that have a device interface
+ // PASS 5+: (re)enumerate device interfaced GUIDs (including HID) and
+ // set the device interfaces.
+
+ // Init the GUID table
+ guid[HCD_PASS] = &GUID_DEVINTERFACE_USB_HOST_CONTROLLER;
+ guid[HUB_PASS] = &GUID_DEVINTERFACE_USB_HUB;
+ guid[GEN_PASS] = NULL;
+ guid[DEV_PASS] = &GUID_DEVINTERFACE_USB_DEVICE;
+ HidD_GetHidGuid(&hid_guid);
+ guid[HID_PASS] = &hid_guid;
+ nb_guids = HID_PASS + 1;
+
+ unref_list = calloc(unref_size, sizeof(libusb_device *));
+ if (unref_list == NULL)
+ return LIBUSB_ERROR_NO_MEM;
+
+ for (pass = 0; ((pass < nb_guids) && (r == LIBUSB_SUCCESS)); pass++) {
+//#define ENUM_DEBUG
+#if defined(ENABLE_LOGGING) && defined(ENUM_DEBUG)
+ const char *passname[] = { "HCD", "HUB", "GEN", "DEV", "HID", "EXT" };
+ usbi_dbg("#### PROCESSING %ss %s", passname[(pass <= HID_PASS) ? pass : (HID_PASS + 1)],
+ (pass != GEN_PASS) ? guid_to_string(guid[pass]) : "");
+#endif
+ for (i = 0; ; i++) {
+ // safe loop: free up any (unprotected) dynamic resource
+ // NB: this is always executed before breaking the loop
+ safe_free(dev_interface_details);
+ safe_free(dev_interface_path);
+ safe_free(dev_id_path);
+ priv = parent_priv = NULL;
+ dev = parent_dev = NULL;
+
+ // Safe loop: end of loop conditions
+ if (r != LIBUSB_SUCCESS)
+ break;
+
+ if ((pass == HCD_PASS) && (i == UINT8_MAX)) {
+ usbi_warn(ctx, "program assertion failed - found more than %d buses, skipping the rest.", UINT8_MAX);
+ break;
+ }
+
+ if (pass != GEN_PASS) {
+ // Except for GEN, all passes deal with device interfaces
+ dev_interface_details = get_interface_details(ctx, &dev_info, &dev_info_data, guid[pass], i);
+ if (dev_interface_details == NULL)
+ break;
+
+ dev_interface_path = sanitize_path(dev_interface_details->DevicePath);
+ if (dev_interface_path == NULL) {
+ usbi_warn(ctx, "could not sanitize device interface path for '%s'", dev_interface_details->DevicePath);
+ continue;
+ }
+ } else {
+ // Workaround for a Nec/Renesas USB 3.0 driver bug where root hubs are
+ // being listed under the "NUSB3" PnP Symbolic Name rather than "USB".
+ // The Intel USB 3.0 driver behaves similar, but uses "IUSB3"
+ // The Intel Alpine Ridge USB 3.1 driver uses "IARUSB3"
+ for (; class_index < ARRAYSIZE(usb_class); class_index++) {
+ if (get_devinfo_data(ctx, &dev_info, &dev_info_data, usb_class[class_index], i))
+ break;
+ i = 0;
+ }
+ if (class_index >= ARRAYSIZE(usb_class))
+ break;
+ }
+
+ // Read the Device ID path. This is what we'll use as UID
+ // Note that if the device is plugged in a different port or hub, the Device ID changes
+ if (CM_Get_Device_IDA(dev_info_data.DevInst, path, sizeof(path), 0) != CR_SUCCESS) {
+ usbi_warn(ctx, "could not read the device id path for devinst %X, skipping",
+ (unsigned int)dev_info_data.DevInst);
+ continue;
+ }
+
+ dev_id_path = sanitize_path(path);
+ if (dev_id_path == NULL) {
+ usbi_warn(ctx, "could not sanitize device id path for devinst %X, skipping",
+ (unsigned int)dev_info_data.DevInst);
+ continue;
+ }
+#ifdef ENUM_DEBUG
+ usbi_dbg("PRO: %s", dev_id_path);
+#endif
+
+ // The SPDRP_ADDRESS for USB devices is the device port number on the hub
+ port_nr = 0;
+ if ((pass >= HUB_PASS) && (pass <= GEN_PASS)) {
+ if ((!pSetupDiGetDeviceRegistryPropertyA(dev_info, &dev_info_data, SPDRP_ADDRESS,
+ &reg_type, (BYTE *)&port_nr, 4, &size)) || (size != 4)) {
+ usbi_warn(ctx, "could not retrieve port number for device '%s', skipping: %s",
+ dev_id_path, windows_error_str(0));
+ continue;
+ }
+ }
+
+ // Set API to use or get additional data from generic pass
+ api = USB_API_UNSUPPORTED;
+ sub_api = SUB_API_NOTSET;
+ switch (pass) {
+ case HCD_PASS:
+ break;
+ case GEN_PASS:
+ // We use the GEN pass to detect driverless devices...
+ size = sizeof(strbuf);
+ if (!pSetupDiGetDeviceRegistryPropertyA(dev_info, &dev_info_data, SPDRP_DRIVER,
+ &reg_type, (BYTE *)strbuf, size, &size)) {
+ usbi_info(ctx, "The following device has no driver: '%s'", dev_id_path);
+ usbi_info(ctx, "libusb will not be able to access it.");
+ }
+ // ...and to add the additional device interface GUIDs
+ key = pSetupDiOpenDevRegKey(dev_info, &dev_info_data, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ);
+ if (key != INVALID_HANDLE_VALUE) {
+ size = sizeof(guid_string_w);
+ s = pRegQueryValueExW(key, L"DeviceInterfaceGUIDs", NULL, &reg_type,
+ (BYTE *)guid_string_w, &size);
+ pRegCloseKey(key);
+ if (s == ERROR_SUCCESS) {
+ if (nb_guids >= MAX_ENUM_GUIDS) {
+ // If this assert is ever reported, grow a GUID table dynamically
+ usbi_err(ctx, "program assertion failed: too many GUIDs");
+ LOOP_BREAK(LIBUSB_ERROR_OVERFLOW);
+ }
+ if_guid = calloc(1, sizeof(GUID));
+ if (if_guid == NULL) {
+ usbi_err(ctx, "could not calloc for if_guid: not enough memory");
+ LOOP_BREAK(LIBUSB_ERROR_NO_MEM);
+ }
+ pCLSIDFromString(guid_string_w, if_guid);
+ guid[nb_guids++] = if_guid;
+ usbi_dbg("extra GUID: %s", guid_to_string(if_guid));
+ }
+ }
+ break;
+ case HID_PASS:
+ api = USB_API_HID;
+ break;
+ default:
+ // Get the API type (after checking that the driver installation is OK)
+ if ((!pSetupDiGetDeviceRegistryPropertyA(dev_info, &dev_info_data, SPDRP_INSTALL_STATE,
+ &reg_type, (BYTE *)&install_state, 4, &size)) || (size != 4)) {
+ usbi_warn(ctx, "could not detect installation state of driver for '%s': %s",
+ dev_id_path, windows_error_str(0));
+ } else if (install_state != 0) {
+ usbi_warn(ctx, "driver for device '%s' is reporting an issue (code: %u) - skipping",
+ dev_id_path, (unsigned int)install_state);
+ continue;
+ }
+ get_api_type(ctx, &dev_info, &dev_info_data, &api, &sub_api);
+ break;
+ }
+
+ // Find parent device (for the passes that need it)
+ switch (pass) {
+ case HCD_PASS:
+ case DEV_PASS:
+ case HUB_PASS:
+ break;
+ default:
+ // Go through the ancestors until we see a face we recognize
+ parent_dev = NULL;
+ for (ancestor = 1; parent_dev == NULL; ancestor++) {
+ session_id = get_ancestor_session_id(dev_info_data.DevInst, ancestor);
+ if (session_id == 0)
+ break;
+
+ parent_dev = usbi_get_device_by_session_id(ctx, session_id);
+ }
+
+ if (parent_dev == NULL) {
+ usbi_dbg("unlisted ancestor for '%s' (non USB HID, newly connected, etc.) - ignoring", dev_id_path);
+ continue;
+ }
+
+ parent_priv = _device_priv(parent_dev);
+ // virtual USB devices are also listed during GEN - don't process these yet
+ if ((pass == GEN_PASS) && (parent_priv->apib->id != USB_API_HUB)) {
+ libusb_unref_device(parent_dev);
+ continue;
+ }
+
+ break;
+ }
+
+ // Create new or match existing device, using the (hashed) device_id as session id
+ if (pass <= DEV_PASS) { // For subsequent passes, we'll lookup the parent
+ // These are the passes that create "new" devices
+ session_id = htab_hash(dev_id_path);
+ dev = usbi_get_device_by_session_id(ctx, session_id);
+ if (dev == NULL) {
+ if (pass == DEV_PASS) {
+ // This can occur if the OS only reports a newly plugged device after we started enum
+ usbi_warn(ctx, "'%s' was only detected in late pass (newly connected device?)"
+ " - ignoring", dev_id_path);
+ continue;
+ }
+
+ usbi_dbg("allocating new device for session [%lX]", session_id);
+ dev = usbi_alloc_device(ctx, session_id);
+ if (dev == NULL)
+ LOOP_BREAK(LIBUSB_ERROR_NO_MEM);
+
+ priv = windows_device_priv_init(dev);
+ } else {
+ usbi_dbg("found existing device for session [%lX] (%u.%u)",
+ session_id, dev->bus_number, dev->device_address);
+
+ priv = _device_priv(dev);
+ if ((parent_dev != NULL) && (dev->parent_dev != NULL)) {
+ if (dev->parent_dev != parent_dev) {
+ // It is possible for the actual parent device to not have existed at the
+ // time of enumeration, so the currently assigned parent may in fact be a
+ // grandparent. If the devices differ, we assume the "new" parent device
+ // is in fact closer to the device.
+ usbi_dbg("updating parent device [session %lX -> %lX]",
+ dev->parent_dev->session_data, parent_dev->session_data);
+ libusb_unref_device(dev->parent_dev);
+ dev->parent_dev = parent_dev;
+ } else {
+ // We hold a reference to parent_dev instance, but this device already
+ // has a parent_dev reference (only one per child)
+ libusb_unref_device(parent_dev);
+ }
+ }
+ }
+
+ // Keep track of devices that need unref
+ unref_list[unref_cur++] = dev;
+ if (unref_cur >= unref_size) {
+ unref_size += 64;
+ new_unref_list = usbi_reallocf(unref_list, unref_size * sizeof(libusb_device *));
+ if (new_unref_list == NULL) {
+ usbi_err(ctx, "could not realloc list for unref - aborting.");
+ LOOP_BREAK(LIBUSB_ERROR_NO_MEM);
+ } else {
+ unref_list = new_unref_list;
+ }
+ }
+ }
+
+ // Setup device
+ switch (pass) {
+ case HCD_PASS:
+ // If the hcd has already been setup, don't do it again
+ if (priv->path != NULL)
+ break;
+ dev->bus_number = (uint8_t)(i + 1); // bus 0 is reserved for disconnected
+ dev->device_address = 0;
+ dev->num_configurations = 0;
+ priv->apib = &usb_api_backend[USB_API_HUB];
+ priv->sub_api = SUB_API_NOTSET;
+ priv->depth = UINT8_MAX; // Overflow to 0 for HCD Hubs
+ priv->path = dev_interface_path;
+ dev_interface_path = NULL;
+ break;
+ case HUB_PASS:
+ case DEV_PASS:
+ // If the device has already been setup, don't do it again
+ if (priv->path != NULL)
+ break;
+ // Take care of API initialization
+ priv->path = dev_interface_path;
+ dev_interface_path = NULL;
+ priv->apib = &usb_api_backend[api];
+ priv->sub_api = sub_api;
+ switch(api) {
+ case USB_API_COMPOSITE:
+ case USB_API_HUB:
+ break;
+ case USB_API_HID:
+ priv->hid = calloc(1, sizeof(struct hid_device_priv));
+ if (priv->hid == NULL)
+ LOOP_BREAK(LIBUSB_ERROR_NO_MEM);
+
+ priv->hid->nb_interfaces = 0;
+ break;
+ default:
+ // For other devices, the first interface is the same as the device
+ priv->usb_interface[0].path = _strdup(priv->path);
+ if (priv->usb_interface[0].path == NULL)
+ usbi_warn(ctx, "could not duplicate interface path '%s'", priv->path);
+ // The following is needed if we want API calls to work for both simple
+ // and composite devices.
+ for (j = 0; j < USB_MAXINTERFACES; j++)
+ priv->usb_interface[j].apib = &usb_api_backend[api];
+
+ break;
+ }
+ break;
+ case GEN_PASS:
+ r = init_device(dev, parent_dev, (uint8_t)port_nr, dev_id_path, dev_info_data.DevInst);
+ if (r == LIBUSB_SUCCESS) {
+ // Append device to the list of discovered devices
+ discdevs = discovered_devs_append(*_discdevs, dev);
+ if (!discdevs)
+ LOOP_BREAK(LIBUSB_ERROR_NO_MEM);
+
+ *_discdevs = discdevs;
+ } else if (r == LIBUSB_ERROR_NO_DEVICE) {
+ // This can occur if the device was disconnected but Windows hasn't
+ // refreshed its enumeration yet - in that case, we ignore the device
+ r = LIBUSB_SUCCESS;
+ }
+ break;
+ default: // HID_PASS and later
+ if (parent_priv->apib->id == USB_API_HID || parent_priv->apib->id == USB_API_COMPOSITE) {
+ if (parent_priv->apib->id == USB_API_HID) {
+ usbi_dbg("setting HID interface for [%lX]:", parent_dev->session_data);
+ r = set_hid_interface(ctx, parent_dev, dev_interface_path);
+ } else {
+ usbi_dbg("setting composite interface for [%lX]:", parent_dev->session_data);
+ r = set_composite_interface(ctx, parent_dev, dev_interface_path, dev_id_path, api, sub_api);
+ }
+ switch (r) {
+ case LIBUSB_SUCCESS:
+ dev_interface_path = NULL;
+ break;
+ case LIBUSB_ERROR_ACCESS:
+ // interface has already been set => make sure dev_interface_path is freed then
+ r = LIBUSB_SUCCESS;
+ break;
+ default:
+ LOOP_BREAK(r);
+ break;
+ }
+ }
+ libusb_unref_device(parent_dev);
+ break;
+ }
+ }
+ }
+
+ // Free any additional GUIDs
+ for (pass = HID_PASS + 1; pass < nb_guids; pass++)
+ free((void *)guid[pass]);
+
+ // Unref newly allocated devs
+ for (i = 0; i < unref_cur; i++)
+ libusb_unref_device(unref_list[i]);
+ free(unref_list);
+
+ return r;
+}
+
+/*
+ * exit: libusb backend deinitialization function
+ */
+static void windows_exit(void)
+{
+ int i;
+ HANDLE semaphore;
+ char sem_name[11 + 8 + 1]; // strlen("libusb_init") + (32-bit hex PID) + '\0'
+
+ sprintf(sem_name, "libusb_init%08X", (unsigned int)(GetCurrentProcessId() & 0xFFFFFFFF));
+ semaphore = CreateSemaphoreA(NULL, 1, 1, sem_name);
+ if (semaphore == NULL)
+ return;
+
+ // A successful wait brings our semaphore count to 0 (unsignaled)
+ // => any concurent wait stalls until the semaphore release
+ if (WaitForSingleObject(semaphore, INFINITE) != WAIT_OBJECT_0) {
+ CloseHandle(semaphore);
+ return;
+ }
+
+ // Only works if exits and inits are balanced exactly
+ if (--concurrent_usage < 0) { // Last exit
+ for (i = 0; i < USB_API_MAX; i++)
+ usb_api_backend[i].exit(SUB_API_NOTSET);
+ exit_dlls();
+ exit_polling();
+ windows_common_exit();
+ usbi_mutex_destroy(&autoclaim_lock);
+ }
+
+ ReleaseSemaphore(semaphore, 1, NULL); // increase count back to 1
+ CloseHandle(semaphore);
+}
+
+static int windows_get_device_descriptor(struct libusb_device *dev, unsigned char *buffer, int *host_endian)
+{
+ struct windows_device_priv *priv = _device_priv(dev);
+
+ memcpy(buffer, &priv->dev_descriptor, DEVICE_DESC_LENGTH);
+ *host_endian = 0;
+
+ return LIBUSB_SUCCESS;
+}
+
+static int windows_get_config_descriptor(struct libusb_device *dev, uint8_t config_index, unsigned char *buffer, size_t len, int *host_endian)
+{
+ struct windows_device_priv *priv = _device_priv(dev);
+ PUSB_CONFIGURATION_DESCRIPTOR config_header;
+ size_t size;
+
+ // config index is zero based
+ if (config_index >= dev->num_configurations)
+ return LIBUSB_ERROR_INVALID_PARAM;
+
+ if ((priv->config_descriptor == NULL) || (priv->config_descriptor[config_index] == NULL))
+ return LIBUSB_ERROR_NOT_FOUND;
+
+ config_header = (PUSB_CONFIGURATION_DESCRIPTOR)priv->config_descriptor[config_index];
+
+ size = MIN(config_header->wTotalLength, len);
+ memcpy(buffer, priv->config_descriptor[config_index], size);
+ *host_endian = 0;
+
+ return (int)size;
+}
+
+static int windows_get_config_descriptor_by_value(struct libusb_device *dev, uint8_t bConfigurationValue,
+ unsigned char **buffer, int *host_endian)
+{
+ struct windows_device_priv *priv = _device_priv(dev);
+ PUSB_CONFIGURATION_DESCRIPTOR config_header;
+ uint8_t index;
+
+ *buffer = NULL;
+ *host_endian = 0;
+
+ if (priv->config_descriptor == NULL)
+ return LIBUSB_ERROR_NOT_FOUND;
+
+ for (index = 0; index < dev->num_configurations; index++) {
+ config_header = (PUSB_CONFIGURATION_DESCRIPTOR)priv->config_descriptor[index];
+ if (config_header->bConfigurationValue == bConfigurationValue) {
+ *buffer = priv->config_descriptor[index];
+ return (int)config_header->wTotalLength;
+ }
+ }
+
+ return LIBUSB_ERROR_NOT_FOUND;
+}
+
+/*
+ * return the cached copy of the active config descriptor
+ */
+static int windows_get_active_config_descriptor(struct libusb_device *dev, unsigned char *buffer, size_t len, int *host_endian)
+{
+ struct windows_device_priv *priv = _device_priv(dev);
+ unsigned char *config_desc;
+ int r;
+
+ if (priv->active_config == 0)
+ return LIBUSB_ERROR_NOT_FOUND;
+
+ r = windows_get_config_descriptor_by_value(dev, priv->active_config, &config_desc, host_endian);
+ if (r < 0)
+ return r;
+
+ len = MIN((size_t)r, len);
+ memcpy(buffer, config_desc, len);
+ return (int)len;
+}
+
+static int windows_open(struct libusb_device_handle *dev_handle)
+{
+ struct windows_device_priv *priv = _device_priv(dev_handle->dev);
+ struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev);
+
+ if (priv->apib == NULL) {
+ usbi_err(ctx, "program assertion failed - device is not initialized");
+ return LIBUSB_ERROR_NO_DEVICE;
+ }
+
+ return priv->apib->open(SUB_API_NOTSET, dev_handle);
+}
+
+static void windows_close(struct libusb_device_handle *dev_handle)
+{
+ struct windows_device_priv *priv = _device_priv(dev_handle->dev);
+
+ priv->apib->close(SUB_API_NOTSET, dev_handle);
+}
+
+static int windows_get_configuration(struct libusb_device_handle *dev_handle, int *config)
+{
+ struct windows_device_priv *priv = _device_priv(dev_handle->dev);
+
+ if (priv->active_config == 0) {
+ *config = 0;
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
+
+ *config = priv->active_config;
+ return LIBUSB_SUCCESS;
+}
+
+/*
+ * from http://msdn.microsoft.com/en-us/library/ms793522.aspx: "The port driver
+ * does not currently expose a service that allows higher-level drivers to set
+ * the configuration."
+ */
+static int windows_set_configuration(struct libusb_device_handle *dev_handle, int config)
+{
+ struct windows_device_priv *priv = _device_priv(dev_handle->dev);
+ int r = LIBUSB_SUCCESS;
+
+ if (config >= USB_MAXCONFIG)
+ return LIBUSB_ERROR_INVALID_PARAM;
+
+ r = libusb_control_transfer(dev_handle, LIBUSB_ENDPOINT_OUT |
+ LIBUSB_REQUEST_TYPE_STANDARD | LIBUSB_RECIPIENT_DEVICE,
+ LIBUSB_REQUEST_SET_CONFIGURATION, (uint16_t)config,
+ 0, NULL, 0, 1000);
+
+ if (r == LIBUSB_SUCCESS)
+ priv->active_config = (uint8_t)config;
+
+ return r;
+}
+
+static int windows_claim_interface(struct libusb_device_handle *dev_handle, int iface)
+{
+ int r = LIBUSB_SUCCESS;
+ struct windows_device_priv *priv = _device_priv(dev_handle->dev);
+
+ safe_free(priv->usb_interface[iface].endpoint);
+ priv->usb_interface[iface].nb_endpoints = 0;
+
+ r = priv->apib->claim_interface(SUB_API_NOTSET, dev_handle, iface);
+
+ if (r == LIBUSB_SUCCESS)
+ r = windows_assign_endpoints(dev_handle, iface, 0);
+
+ return r;
+}
+
+static int windows_set_interface_altsetting(struct libusb_device_handle *dev_handle, int iface, int altsetting)
+{
+ int r = LIBUSB_SUCCESS;
+ struct windows_device_priv *priv = _device_priv(dev_handle->dev);
+
+ safe_free(priv->usb_interface[iface].endpoint);
+ priv->usb_interface[iface].nb_endpoints = 0;
+
+ r = priv->apib->set_interface_altsetting(SUB_API_NOTSET, dev_handle, iface, altsetting);
+
+ if (r == LIBUSB_SUCCESS)
+ r = windows_assign_endpoints(dev_handle, iface, altsetting);
+
+ return r;
+}
+
+static int windows_release_interface(struct libusb_device_handle *dev_handle, int iface)
+{
+ struct windows_device_priv *priv = _device_priv(dev_handle->dev);
+
+ return priv->apib->release_interface(SUB_API_NOTSET, dev_handle, iface);
+}
+
+static int windows_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint)
+{
+ struct windows_device_priv *priv = _device_priv(dev_handle->dev);
+ return priv->apib->clear_halt(SUB_API_NOTSET, dev_handle, endpoint);
+}
+
+static int windows_reset_device(struct libusb_device_handle *dev_handle)
+{
+ struct windows_device_priv *priv = _device_priv(dev_handle->dev);
+ return priv->apib->reset_device(SUB_API_NOTSET, dev_handle);
+}
+
+// The 3 functions below are unlikely to ever get supported on Windows
+static int windows_kernel_driver_active(struct libusb_device_handle *dev_handle, int iface)
+{
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+}
+
+static int windows_attach_kernel_driver(struct libusb_device_handle *dev_handle, int iface)
+{
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+}
+
+static int windows_detach_kernel_driver(struct libusb_device_handle *dev_handle, int iface)
+{
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+}
+
+static void windows_destroy_device(struct libusb_device *dev)
+{
+ windows_device_priv_release(dev);
+}
+
+void windows_clear_transfer_priv(struct usbi_transfer *itransfer)
+{
+ struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
+
+ usbi_free_fd(&transfer_priv->pollable_fd);
+ safe_free(transfer_priv->hid_buffer);
+ // When auto claim is in use, attempt to release the auto-claimed interface
+ auto_release(itransfer);
+}
+
+static int submit_bulk_transfer(struct usbi_transfer *itransfer)
+{
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev);
+ struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
+ struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev);
+ int r;
+
+ r = priv->apib->submit_bulk_transfer(SUB_API_NOTSET, itransfer);
+ if (r != LIBUSB_SUCCESS)
+ return r;
+
+ usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd,
+ (short)(IS_XFERIN(transfer) ? POLLIN : POLLOUT));
+
+ return LIBUSB_SUCCESS;
+}
+
+static int submit_iso_transfer(struct usbi_transfer *itransfer)
+{
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev);
+ struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
+ struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev);
+ int r;
+
+ r = priv->apib->submit_iso_transfer(SUB_API_NOTSET, itransfer);
+ if (r != LIBUSB_SUCCESS)
+ return r;
+
+ usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd,
+ (short)(IS_XFERIN(transfer) ? POLLIN : POLLOUT));
+
+ return LIBUSB_SUCCESS;
+}
+
+static int submit_control_transfer(struct usbi_transfer *itransfer)
+{
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev);
+ struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
+ struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev);
+ int r;
+
+ r = priv->apib->submit_control_transfer(SUB_API_NOTSET, itransfer);
+ if (r != LIBUSB_SUCCESS)
+ return r;
+
+ usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd, POLLIN);
+
+ return LIBUSB_SUCCESS;
+}
+
+static int windows_submit_transfer(struct usbi_transfer *itransfer)
+{
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+
+ switch (transfer->type) {
+ case LIBUSB_TRANSFER_TYPE_CONTROL:
+ return submit_control_transfer(itransfer);
+ case LIBUSB_TRANSFER_TYPE_BULK:
+ case LIBUSB_TRANSFER_TYPE_INTERRUPT:
+ if (IS_XFEROUT(transfer) && (transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET))
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+ return submit_bulk_transfer(itransfer);
+ case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
+ return submit_iso_transfer(itransfer);
+ case LIBUSB_TRANSFER_TYPE_BULK_STREAM:
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+ default:
+ usbi_err(TRANSFER_CTX(transfer), "unknown endpoint type %d", transfer->type);
+ return LIBUSB_ERROR_INVALID_PARAM;
+ }
+}
+
+static int windows_abort_control(struct usbi_transfer *itransfer)
+{
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev);
+
+ return priv->apib->abort_control(SUB_API_NOTSET, itransfer);
+}
+
+static int windows_abort_transfers(struct usbi_transfer *itransfer)
+{
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev);
+
+ return priv->apib->abort_transfers(SUB_API_NOTSET, itransfer);
+}
+
+static int windows_cancel_transfer(struct usbi_transfer *itransfer)
+{
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+
+ switch (transfer->type) {
+ case LIBUSB_TRANSFER_TYPE_CONTROL:
+ return windows_abort_control(itransfer);
+ case LIBUSB_TRANSFER_TYPE_BULK:
+ case LIBUSB_TRANSFER_TYPE_INTERRUPT:
+ case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
+ return windows_abort_transfers(itransfer);
+ case LIBUSB_TRANSFER_TYPE_BULK_STREAM:
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+ default:
+ usbi_err(ITRANSFER_CTX(itransfer), "unknown endpoint type %d", transfer->type);
+ return LIBUSB_ERROR_INVALID_PARAM;
+ }
+}
+
+int windows_copy_transfer_data(struct usbi_transfer *itransfer, uint32_t io_size)
+{
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev);
+ return priv->apib->copy_transfer_data(SUB_API_NOTSET, itransfer, io_size);
+}
+
+struct winfd *windows_get_fd(struct usbi_transfer *transfer)
+{
+ struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(transfer);
+ return &transfer_priv->pollable_fd;
+}
+
+void windows_get_overlapped_result(struct usbi_transfer *transfer, struct winfd *pollable_fd, DWORD *io_result, DWORD *io_size)
+{
+ if (HasOverlappedIoCompletedSync(pollable_fd->overlapped)) {
+ *io_result = NO_ERROR;
+ *io_size = (DWORD)pollable_fd->overlapped->InternalHigh;
+ } else if (GetOverlappedResult(pollable_fd->handle, pollable_fd->overlapped, io_size, false)) {
+ // Regular async overlapped
+ *io_result = NO_ERROR;
+ } else {
+ *io_result = GetLastError();
+ }
+}
+
+// NB: MSVC6 does not support named initializers.
+const struct usbi_os_backend windows_backend = {
+ "Windows",
+ USBI_CAP_HAS_HID_ACCESS,
+ windows_init,
+ windows_exit,
+
+ windows_get_device_list,
+ NULL, /* hotplug_poll */
+ windows_open,
+ windows_close,
+
+ windows_get_device_descriptor,
+ windows_get_active_config_descriptor,
+ windows_get_config_descriptor,
+ windows_get_config_descriptor_by_value,
+
+ windows_get_configuration,
+ windows_set_configuration,
+ windows_claim_interface,
+ windows_release_interface,
+
+ windows_set_interface_altsetting,
+ windows_clear_halt,
+ windows_reset_device,
+
+ NULL, /* alloc_streams */
+ NULL, /* free_streams */
+
+ NULL, /* dev_mem_alloc */
+ NULL, /* dev_mem_free */
+
+ windows_kernel_driver_active,
+ windows_detach_kernel_driver,
+ windows_attach_kernel_driver,
+
+ windows_destroy_device,
+
+ windows_submit_transfer,
+ windows_cancel_transfer,
+ windows_clear_transfer_priv,
+
+ windows_handle_events,
+ NULL,
+
+ windows_clock_gettime,
+#if defined(USBI_TIMERFD_AVAILABLE)
+ NULL,
+#endif
+ sizeof(struct windows_device_priv),
+ sizeof(struct windows_device_handle_priv),
+ sizeof(struct windows_transfer_priv),
+};
+
+
+/*
+ * USB API backends
+ */
+static int unsupported_init(int sub_api, struct libusb_context *ctx)
+{
+ return LIBUSB_SUCCESS;
+}
+
+static int unsupported_exit(int sub_api)
+{
+ return LIBUSB_SUCCESS;
+}
+
+static int unsupported_open(int sub_api, struct libusb_device_handle *dev_handle)
+{
+ PRINT_UNSUPPORTED_API(open);
+}
+
+static void unsupported_close(int sub_api, struct libusb_device_handle *dev_handle)
+{
+ usbi_dbg("unsupported API call for 'close'");
+}
+
+static int unsupported_configure_endpoints(int sub_api, struct libusb_device_handle *dev_handle, int iface)
+{
+ PRINT_UNSUPPORTED_API(configure_endpoints);
+}
+
+static int unsupported_claim_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface)
+{
+ PRINT_UNSUPPORTED_API(claim_interface);
+}
+
+static int unsupported_set_interface_altsetting(int sub_api, struct libusb_device_handle *dev_handle, int iface, int altsetting)
+{
+ PRINT_UNSUPPORTED_API(set_interface_altsetting);
+}
+
+static int unsupported_release_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface)
+{
+ PRINT_UNSUPPORTED_API(release_interface);
+}
+
+static int unsupported_clear_halt(int sub_api, struct libusb_device_handle *dev_handle, unsigned char endpoint)
+{
+ PRINT_UNSUPPORTED_API(clear_halt);
+}
+
+static int unsupported_reset_device(int sub_api, struct libusb_device_handle *dev_handle)
+{
+ PRINT_UNSUPPORTED_API(reset_device);
+}
+
+static int unsupported_submit_bulk_transfer(int sub_api, struct usbi_transfer *itransfer)
+{
+ PRINT_UNSUPPORTED_API(submit_bulk_transfer);
+}
+
+static int unsupported_submit_iso_transfer(int sub_api, struct usbi_transfer *itransfer)
+{
+ PRINT_UNSUPPORTED_API(submit_iso_transfer);
+}
+
+static int unsupported_submit_control_transfer(int sub_api, struct usbi_transfer *itransfer)
+{
+ PRINT_UNSUPPORTED_API(submit_control_transfer);
+}
+
+static int unsupported_abort_control(int sub_api, struct usbi_transfer *itransfer)
+{
+ PRINT_UNSUPPORTED_API(abort_control);
+}
+
+static int unsupported_abort_transfers(int sub_api, struct usbi_transfer *itransfer)
+{
+ PRINT_UNSUPPORTED_API(abort_transfers);
+}
+
+static int unsupported_copy_transfer_data(int sub_api, struct usbi_transfer *itransfer, uint32_t io_size)
+{
+ PRINT_UNSUPPORTED_API(copy_transfer_data);
+}
+
+static int common_configure_endpoints(int sub_api, struct libusb_device_handle *dev_handle, int iface)
+{
+ return LIBUSB_SUCCESS;
+}
+
+// These names must be uppercase
+static const char *hub_driver_names[] = {"USBHUB", "USBHUB3", "USB3HUB", "NUSB3HUB", "RUSB3HUB", "FLXHCIH", "TIHUB3", "ETRONHUB3", "VIAHUB3", "ASMTHUB3", "IUSB3HUB", "VUSB3HUB", "AMDHUB30", "VHHUB", "AUSB3HUB"};
+static const char *composite_driver_names[] = {"USBCCGP"};
+static const char *winusbx_driver_names[] = WINUSBX_DRV_NAMES;
+static const char *hid_driver_names[] = {"HIDUSB", "MOUHID", "KBDHID"};
+const struct windows_usb_api_backend usb_api_backend[USB_API_MAX] = {
+ {
+ USB_API_UNSUPPORTED,
+ "Unsupported API",
+ NULL,
+ 0,
+ unsupported_init,
+ unsupported_exit,
+ unsupported_open,
+ unsupported_close,
+ unsupported_configure_endpoints,
+ unsupported_claim_interface,
+ unsupported_set_interface_altsetting,
+ unsupported_release_interface,
+ unsupported_clear_halt,
+ unsupported_reset_device,
+ unsupported_submit_bulk_transfer,
+ unsupported_submit_iso_transfer,
+ unsupported_submit_control_transfer,
+ unsupported_abort_control,
+ unsupported_abort_transfers,
+ unsupported_copy_transfer_data,
+ },
+ {
+ USB_API_HUB,
+ "HUB API",
+ hub_driver_names,
+ ARRAYSIZE(hub_driver_names),
+ unsupported_init,
+ unsupported_exit,
+ unsupported_open,
+ unsupported_close,
+ unsupported_configure_endpoints,
+ unsupported_claim_interface,
+ unsupported_set_interface_altsetting,
+ unsupported_release_interface,
+ unsupported_clear_halt,
+ unsupported_reset_device,
+ unsupported_submit_bulk_transfer,
+ unsupported_submit_iso_transfer,
+ unsupported_submit_control_transfer,
+ unsupported_abort_control,
+ unsupported_abort_transfers,
+ unsupported_copy_transfer_data,
+ },
+ {
+ USB_API_COMPOSITE,
+ "Composite API",
+ composite_driver_names,
+ ARRAYSIZE(composite_driver_names),
+ composite_init,
+ composite_exit,
+ composite_open,
+ composite_close,
+ common_configure_endpoints,
+ composite_claim_interface,
+ composite_set_interface_altsetting,
+ composite_release_interface,
+ composite_clear_halt,
+ composite_reset_device,
+ composite_submit_bulk_transfer,
+ composite_submit_iso_transfer,
+ composite_submit_control_transfer,
+ composite_abort_control,
+ composite_abort_transfers,
+ composite_copy_transfer_data,
+ },
+ {
+ USB_API_WINUSBX,
+ "WinUSB-like APIs",
+ winusbx_driver_names,
+ ARRAYSIZE(winusbx_driver_names),
+ winusbx_init,
+ winusbx_exit,
+ winusbx_open,
+ winusbx_close,
+ winusbx_configure_endpoints,
+ winusbx_claim_interface,
+ winusbx_set_interface_altsetting,
+ winusbx_release_interface,
+ winusbx_clear_halt,
+ winusbx_reset_device,
+ winusbx_submit_bulk_transfer,
+ unsupported_submit_iso_transfer,
+ winusbx_submit_control_transfer,
+ winusbx_abort_control,
+ winusbx_abort_transfers,
+ winusbx_copy_transfer_data,
+ },
+ {
+ USB_API_HID,
+ "HID API",
+ hid_driver_names,
+ ARRAYSIZE(hid_driver_names),
+ hid_init,
+ hid_exit,
+ hid_open,
+ hid_close,
+ common_configure_endpoints,
+ hid_claim_interface,
+ hid_set_interface_altsetting,
+ hid_release_interface,
+ hid_clear_halt,
+ hid_reset_device,
+ hid_submit_bulk_transfer,
+ unsupported_submit_iso_transfer,
+ hid_submit_control_transfer,
+ hid_abort_transfers,
+ hid_abort_transfers,
+ hid_copy_transfer_data,
+ },
+};
+
+
+/*
+ * WinUSB-like (WinUSB, libusb0/libusbK through libusbk DLL) API functions
+ */
+#define WinUSBX_Set(fn) \
+ do { \
+ if (native_winusb) \
+ WinUSBX[i].fn = (WinUsb_##fn##_t)GetProcAddress(h, "WinUsb_" #fn); \
+ else \
+ pLibK_GetProcAddress((PVOID *)&WinUSBX[i].fn, i, KUSB_FNID_##fn); \
+ } while (0)
+
+static int winusbx_init(int sub_api, struct libusb_context *ctx)
+{
+ HMODULE h;
+ bool native_winusb;
+ int i;
+ KLIB_VERSION LibK_Version;
+ LibK_GetProcAddress_t pLibK_GetProcAddress = NULL;
+ LibK_GetVersion_t pLibK_GetVersion;
+
+ h = LoadLibraryA("libusbK");
+
+ if (h == NULL) {
+ usbi_info(ctx, "libusbK DLL is not available, will use native WinUSB");
+ h = LoadLibraryA("WinUSB");
+
+ if (h == NULL) {
+ usbi_warn(ctx, "WinUSB DLL is not available either, "
+ "you will not be able to access devices outside of enumeration");
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
+ } else {
+ usbi_dbg("using libusbK DLL for universal access");
+ pLibK_GetVersion = (LibK_GetVersion_t)GetProcAddress(h, "LibK_GetVersion");
+ if (pLibK_GetVersion != NULL) {
+ pLibK_GetVersion(&LibK_Version);
+ usbi_dbg("libusbK version: %d.%d.%d.%d", LibK_Version.Major, LibK_Version.Minor,
+ LibK_Version.Micro, LibK_Version.Nano);
+ }
+ pLibK_GetProcAddress = (LibK_GetProcAddress_t)GetProcAddress(h, "LibK_GetProcAddress");
+ if (pLibK_GetProcAddress == NULL) {
+ usbi_err(ctx, "LibK_GetProcAddress() not found in libusbK DLL");
+ FreeLibrary(h);
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
+ }
+
+ native_winusb = (pLibK_GetProcAddress == NULL);
+ for (i = SUB_API_LIBUSBK; i < SUB_API_MAX; i++) {
+ WinUSBX_Set(AbortPipe);
+ WinUSBX_Set(ControlTransfer);
+ WinUSBX_Set(FlushPipe);
+ WinUSBX_Set(Free);
+ WinUSBX_Set(GetAssociatedInterface);
+ WinUSBX_Set(GetCurrentAlternateSetting);
+ WinUSBX_Set(GetDescriptor);
+ WinUSBX_Set(GetOverlappedResult);
+ WinUSBX_Set(GetPipePolicy);
+ WinUSBX_Set(GetPowerPolicy);
+ WinUSBX_Set(Initialize);
+ WinUSBX_Set(QueryDeviceInformation);
+ WinUSBX_Set(QueryInterfaceSettings);
+ WinUSBX_Set(QueryPipe);
+ WinUSBX_Set(ReadPipe);
+ WinUSBX_Set(ResetPipe);
+ WinUSBX_Set(SetCurrentAlternateSetting);
+ WinUSBX_Set(SetPipePolicy);
+ WinUSBX_Set(SetPowerPolicy);
+ WinUSBX_Set(WritePipe);
+ if (!native_winusb)
+ WinUSBX_Set(ResetDevice);
+
+ if (WinUSBX[i].Initialize != NULL) {
+ WinUSBX[i].initialized = true;
+ usbi_dbg("initalized sub API %s", sub_api_name[i]);
+ } else {
+ usbi_warn(ctx, "Failed to initalize sub API %s", sub_api_name[i]);
+ WinUSBX[i].initialized = false;
+ }
+ }
+
+ WinUSBX_handle = h;
+ return LIBUSB_SUCCESS;
+}
+
+static int winusbx_exit(int sub_api)
+{
+ if (WinUSBX_handle != NULL) {
+ FreeLibrary(WinUSBX_handle);
+ WinUSBX_handle = NULL;
+
+ /* Reset the WinUSBX API structures */
+ memset(&WinUSBX, 0, sizeof(WinUSBX));
+ }
+
+ return LIBUSB_SUCCESS;
+}
+
+// NB: open and close must ensure that they only handle interface of
+// the right API type, as these functions can be called wholesale from
+// composite_open(), with interfaces belonging to different APIs
+static int winusbx_open(int sub_api, struct libusb_device_handle *dev_handle)
+{
+ struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev);
+ struct windows_device_priv *priv = _device_priv(dev_handle->dev);
+ struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle);
+
+ HANDLE file_handle;
+ int i;
+
+ CHECK_WINUSBX_AVAILABLE(sub_api);
+
+ // WinUSB requires a separate handle for each interface
+ for (i = 0; i < USB_MAXINTERFACES; i++) {
+ if ((priv->usb_interface[i].path != NULL)
+ && (priv->usb_interface[i].apib->id == USB_API_WINUSBX)) {
+ file_handle = CreateFileA(priv->usb_interface[i].path, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ,
+ NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
+ if (file_handle == INVALID_HANDLE_VALUE) {
+ usbi_err(ctx, "could not open device %s (interface %d): %s", priv->usb_interface[i].path, i, windows_error_str(0));
+ switch(GetLastError()) {
+ case ERROR_FILE_NOT_FOUND: // The device was disconnected
+ return LIBUSB_ERROR_NO_DEVICE;
+ case ERROR_ACCESS_DENIED:
+ return LIBUSB_ERROR_ACCESS;
+ default:
+ return LIBUSB_ERROR_IO;
+ }
+ }
+ handle_priv->interface_handle[i].dev_handle = file_handle;
+ }
+ }
+
+ return LIBUSB_SUCCESS;
+}
+
+static void winusbx_close(int sub_api, struct libusb_device_handle *dev_handle)
+{
+ struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle);
+ struct windows_device_priv *priv = _device_priv(dev_handle->dev);
+ HANDLE handle;
+ int i;
+
+ if (sub_api == SUB_API_NOTSET)
+ sub_api = priv->sub_api;
+
+ if (!WinUSBX[sub_api].initialized)
+ return;
+
+ if (priv->apib->id == USB_API_COMPOSITE) {
+ // If this is a composite device, just free and close all WinUSB-like
+ // interfaces directly (each is independent and not associated with another)
+ for (i = 0; i < USB_MAXINTERFACES; i++) {
+ if (priv->usb_interface[i].apib->id == USB_API_WINUSBX) {
+ handle = handle_priv->interface_handle[i].api_handle;
+ if (HANDLE_VALID(handle))
+ WinUSBX[sub_api].Free(handle);
+
+ handle = handle_priv->interface_handle[i].dev_handle;
+ if (HANDLE_VALID(handle))
+ CloseHandle(handle);
+ }
+ }
+ } else {
+ // If this is a WinUSB device, free all interfaces above interface 0,
+ // then free and close interface 0 last
+ for (i = 1; i < USB_MAXINTERFACES; i++) {
+ handle = handle_priv->interface_handle[i].api_handle;
+ if (HANDLE_VALID(handle))
+ WinUSBX[sub_api].Free(handle);
+ }
+ handle = handle_priv->interface_handle[0].api_handle;
+ if (HANDLE_VALID(handle))
+ WinUSBX[sub_api].Free(handle);
+
+ handle = handle_priv->interface_handle[0].dev_handle;
+ if (HANDLE_VALID(handle))
+ CloseHandle(handle);
+ }
+}
+
+static int winusbx_configure_endpoints(int sub_api, struct libusb_device_handle *dev_handle, int iface)
+{
+ struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle);
+ struct windows_device_priv *priv = _device_priv(dev_handle->dev);
+ HANDLE winusb_handle = handle_priv->interface_handle[iface].api_handle;
+ UCHAR policy;
+ ULONG timeout = 0;
+ uint8_t endpoint_address;
+ int i;
+
+ CHECK_WINUSBX_AVAILABLE(sub_api);
+
+ // With handle and enpoints set (in parent), we can setup the default pipe properties
+ // see http://download.microsoft.com/download/D/1/D/D1DD7745-426B-4CC3-A269-ABBBE427C0EF/DVC-T705_DDC08.pptx
+ for (i = -1; i < priv->usb_interface[iface].nb_endpoints; i++) {
+ endpoint_address = (i == -1) ? 0 : priv->usb_interface[iface].endpoint[i];
+ if (!WinUSBX[sub_api].SetPipePolicy(winusb_handle, endpoint_address,
+ PIPE_TRANSFER_TIMEOUT, sizeof(ULONG), &timeout))
+ usbi_dbg("failed to set PIPE_TRANSFER_TIMEOUT for control endpoint %02X", endpoint_address);
+
+ if ((i == -1) || (sub_api == SUB_API_LIBUSB0))
+ continue; // Other policies don't apply to control endpoint or libusb0
+
+ policy = false;
+ if (!WinUSBX[sub_api].SetPipePolicy(winusb_handle, endpoint_address,
+ SHORT_PACKET_TERMINATE, sizeof(UCHAR), &policy))
+ usbi_dbg("failed to disable SHORT_PACKET_TERMINATE for endpoint %02X", endpoint_address);
+
+ if (!WinUSBX[sub_api].SetPipePolicy(winusb_handle, endpoint_address,
+ IGNORE_SHORT_PACKETS, sizeof(UCHAR), &policy))
+ usbi_dbg("failed to disable IGNORE_SHORT_PACKETS for endpoint %02X", endpoint_address);
+
+ policy = true;
+ /* ALLOW_PARTIAL_READS must be enabled due to likely libusbK bug. See:
+ https://sourceforge.net/mailarchive/message.php?msg_id=29736015 */
+ if (!WinUSBX[sub_api].SetPipePolicy(winusb_handle, endpoint_address,
+ ALLOW_PARTIAL_READS, sizeof(UCHAR), &policy))
+ usbi_dbg("failed to enable ALLOW_PARTIAL_READS for endpoint %02X", endpoint_address);
+
+ if (!WinUSBX[sub_api].SetPipePolicy(winusb_handle, endpoint_address,
+ AUTO_CLEAR_STALL, sizeof(UCHAR), &policy))
+ usbi_dbg("failed to enable AUTO_CLEAR_STALL for endpoint %02X", endpoint_address);
+ }
+
+ return LIBUSB_SUCCESS;
+}
+
+static int winusbx_claim_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface)
+{
+ struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev);
+ struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle);
+ struct windows_device_priv *priv = _device_priv(dev_handle->dev);
+ bool is_using_usbccgp = (priv->apib->id == USB_API_COMPOSITE);
+ SP_DEVICE_INTERFACE_DETAIL_DATA_A *dev_interface_details = NULL;
+ HDEVINFO dev_info = INVALID_HANDLE_VALUE;
+ SP_DEVINFO_DATA dev_info_data;
+ char *dev_path_no_guid = NULL;
+ char filter_path[] = "\\\\.\\libusb0-0000";
+ bool found_filter = false;
+ HANDLE file_handle, winusb_handle;
+ DWORD err;
+ int i;
+
+ CHECK_WINUSBX_AVAILABLE(sub_api);
+
+ // If the device is composite, but using the default Windows composite parent driver (usbccgp)
+ // or if it's the first WinUSB-like interface, we get a handle through Initialize().
+ if ((is_using_usbccgp) || (iface == 0)) {
+ // composite device (independent interfaces) or interface 0
+ file_handle = handle_priv->interface_handle[iface].dev_handle;
+ if (!HANDLE_VALID(file_handle))
+ return LIBUSB_ERROR_NOT_FOUND;
+
+ if (!WinUSBX[sub_api].Initialize(file_handle, &winusb_handle)) {
+ handle_priv->interface_handle[iface].api_handle = INVALID_HANDLE_VALUE;
+ err = GetLastError();
+ switch(err) {
+ case ERROR_BAD_COMMAND:
+ // The device was disconnected
+ usbi_err(ctx, "could not access interface %d: %s", iface, windows_error_str(0));
+ return LIBUSB_ERROR_NO_DEVICE;
+ default:
+ // it may be that we're using the libusb0 filter driver.
+ // TODO: can we move this whole business into the K/0 DLL?
+ for (i = 0; ; i++) {
+ safe_free(dev_interface_details);
+ safe_free(dev_path_no_guid);
+
+ dev_interface_details = get_interface_details_filter(ctx, &dev_info, &dev_info_data, &GUID_DEVINTERFACE_LIBUSB0_FILTER, i, filter_path);
+ if ((found_filter) || (dev_interface_details == NULL))
+ break;
+
+ // ignore GUID part
+ dev_path_no_guid = sanitize_path(strtok(dev_interface_details->DevicePath, "{"));
+ if (dev_path_no_guid == NULL)
+ continue;
+
+ if (strncmp(dev_path_no_guid, priv->usb_interface[iface].path, strlen(dev_path_no_guid)) == 0) {
+ file_handle = CreateFileA(filter_path, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ,
+ NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
+ if (file_handle != INVALID_HANDLE_VALUE) {
+ if (WinUSBX[sub_api].Initialize(file_handle, &winusb_handle)) {
+ // Replace the existing file handle with the working one
+ CloseHandle(handle_priv->interface_handle[iface].dev_handle);
+ handle_priv->interface_handle[iface].dev_handle = file_handle;
+ found_filter = true;
+ } else {
+ usbi_err(ctx, "could not initialize filter driver for %s", filter_path);
+ CloseHandle(file_handle);
+ }
+ } else {
+ usbi_err(ctx, "could not open device %s: %s", filter_path, windows_error_str(0));
+ }
+ }
+ }
+ free(dev_interface_details);
+ if (!found_filter) {
+ usbi_err(ctx, "could not access interface %d: %s", iface, windows_error_str(err));
+ return LIBUSB_ERROR_ACCESS;
+ }
+ }
+ }
+ handle_priv->interface_handle[iface].api_handle = winusb_handle;
+ } else {
+ // For all other interfaces, use GetAssociatedInterface()
+ winusb_handle = handle_priv->interface_handle[0].api_handle;
+ // It is a requirement for multiple interface devices on Windows that, to you
+ // must first claim the first interface before you claim the others
+ if (!HANDLE_VALID(winusb_handle)) {
+ file_handle = handle_priv->interface_handle[0].dev_handle;
+ if (WinUSBX[sub_api].Initialize(file_handle, &winusb_handle)) {
+ handle_priv->interface_handle[0].api_handle = winusb_handle;
+ usbi_warn(ctx, "auto-claimed interface 0 (required to claim %d with WinUSB)", iface);
+ } else {
+ usbi_warn(ctx, "failed to auto-claim interface 0 (required to claim %d with WinUSB): %s", iface, windows_error_str(0));
+ return LIBUSB_ERROR_ACCESS;
+ }
+ }
+ if (!WinUSBX[sub_api].GetAssociatedInterface(winusb_handle, (UCHAR)(iface - 1),
+ &handle_priv->interface_handle[iface].api_handle)) {
+ handle_priv->interface_handle[iface].api_handle = INVALID_HANDLE_VALUE;
+ switch(GetLastError()) {
+ case ERROR_NO_MORE_ITEMS: // invalid iface
+ return LIBUSB_ERROR_NOT_FOUND;
+ case ERROR_BAD_COMMAND: // The device was disconnected
+ return LIBUSB_ERROR_NO_DEVICE;
+ case ERROR_ALREADY_EXISTS: // already claimed
+ return LIBUSB_ERROR_BUSY;
+ default:
+ usbi_err(ctx, "could not claim interface %d: %s", iface, windows_error_str(0));
+ return LIBUSB_ERROR_ACCESS;
+ }
+ }
+ }
+ usbi_dbg("claimed interface %d", iface);
+ handle_priv->active_interface = iface;
+
+ return LIBUSB_SUCCESS;
+}
+
+static int winusbx_release_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface)
+{
+ struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle);
+ struct windows_device_priv *priv = _device_priv(dev_handle->dev);
+ HANDLE winusb_handle;
+
+ CHECK_WINUSBX_AVAILABLE(sub_api);
+
+ winusb_handle = handle_priv->interface_handle[iface].api_handle;
+ if (!HANDLE_VALID(winusb_handle))
+ return LIBUSB_ERROR_NOT_FOUND;
+
+ WinUSBX[sub_api].Free(winusb_handle);
+ handle_priv->interface_handle[iface].api_handle = INVALID_HANDLE_VALUE;
+
+ return LIBUSB_SUCCESS;
+}
+
+/*
+ * Return the first valid interface (of the same API type), for control transfers
+ */
+static int get_valid_interface(struct libusb_device_handle *dev_handle, int api_id)
+{
+ struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle);
+ struct windows_device_priv *priv = _device_priv(dev_handle->dev);
+ int i;
+
+ if ((api_id < USB_API_WINUSBX) || (api_id > USB_API_HID)) {
+ usbi_dbg("unsupported API ID");
+ return -1;
+ }
+
+ for (i = 0; i < USB_MAXINTERFACES; i++) {
+ if (HANDLE_VALID(handle_priv->interface_handle[i].dev_handle)
+ && HANDLE_VALID(handle_priv->interface_handle[i].api_handle)
+ && (priv->usb_interface[i].apib->id == api_id))
+ return i;
+ }
+
+ return -1;
+}
+
+/*
+ * Lookup interface by endpoint address. -1 if not found
+ */
+static int interface_by_endpoint(struct windows_device_priv *priv,
+ struct windows_device_handle_priv *handle_priv, uint8_t endpoint_address)
+{
+ int i, j;
+
+ for (i = 0; i < USB_MAXINTERFACES; i++) {
+ if (!HANDLE_VALID(handle_priv->interface_handle[i].api_handle))
+ continue;
+ if (priv->usb_interface[i].endpoint == NULL)
+ continue;
+ for (j = 0; j < priv->usb_interface[i].nb_endpoints; j++) {
+ if (priv->usb_interface[i].endpoint[j] == endpoint_address)
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+static int winusbx_submit_control_transfer(int sub_api, struct usbi_transfer *itransfer)
+{
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev);
+ struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev);
+ struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
+ struct windows_device_handle_priv *handle_priv = _device_handle_priv(transfer->dev_handle);
+ WINUSB_SETUP_PACKET *setup = (WINUSB_SETUP_PACKET *)transfer->buffer;
+ ULONG size;
+ HANDLE winusb_handle;
+ int current_interface;
+ struct winfd wfd;
+
+ CHECK_WINUSBX_AVAILABLE(sub_api);
+
+ transfer_priv->pollable_fd = INVALID_WINFD;
+ size = transfer->length - LIBUSB_CONTROL_SETUP_SIZE;
+
+ // Windows places upper limits on the control transfer size
+ // See: https://msdn.microsoft.com/en-us/library/windows/hardware/ff538112.aspx
+ if (size > MAX_CTRL_BUFFER_LENGTH)
+ return LIBUSB_ERROR_INVALID_PARAM;
+
+ current_interface = get_valid_interface(transfer->dev_handle, USB_API_WINUSBX);
+ if (current_interface < 0) {
+ if (auto_claim(transfer, &current_interface, USB_API_WINUSBX) != LIBUSB_SUCCESS)
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
+
+ usbi_dbg("will use interface %d", current_interface);
+ winusb_handle = handle_priv->interface_handle[current_interface].api_handle;
+
+ wfd = usbi_create_fd(winusb_handle, RW_READ, NULL, NULL);
+ // Always use the handle returned from usbi_create_fd (wfd.handle)
+ if (wfd.fd < 0)
+ return LIBUSB_ERROR_NO_MEM;
+
+ // Sending of set configuration control requests from WinUSB creates issues
+ if (((setup->request_type & (0x03 << 5)) == LIBUSB_REQUEST_TYPE_STANDARD)
+ && (setup->request == LIBUSB_REQUEST_SET_CONFIGURATION)) {
+ if (setup->value != priv->active_config) {
+ usbi_warn(ctx, "cannot set configuration other than the default one");
+ usbi_free_fd(&wfd);
+ return LIBUSB_ERROR_INVALID_PARAM;
+ }
+ wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY;
+ wfd.overlapped->InternalHigh = 0;
+ } else {
+ if (!WinUSBX[sub_api].ControlTransfer(wfd.handle, *setup, transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE, size, NULL, wfd.overlapped)) {
+ if (GetLastError() != ERROR_IO_PENDING) {
+ usbi_warn(ctx, "ControlTransfer failed: %s", windows_error_str(0));
+ usbi_free_fd(&wfd);
+ return LIBUSB_ERROR_IO;
+ }
+ } else {
+ wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY;
+ wfd.overlapped->InternalHigh = (DWORD)size;
+ }
+ }
+
+ // Use priv_transfer to store data needed for async polling
+ transfer_priv->pollable_fd = wfd;
+ transfer_priv->interface_number = (uint8_t)current_interface;
+
+ return LIBUSB_SUCCESS;
+}
+
+static int winusbx_set_interface_altsetting(int sub_api, struct libusb_device_handle *dev_handle, int iface, int altsetting)
+{
+ struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev);
+ struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle);
+ struct windows_device_priv *priv = _device_priv(dev_handle->dev);
+ HANDLE winusb_handle;
+
+ CHECK_WINUSBX_AVAILABLE(sub_api);
+
+ if (altsetting > 255)
+ return LIBUSB_ERROR_INVALID_PARAM;
+
+ winusb_handle = handle_priv->interface_handle[iface].api_handle;
+ if (!HANDLE_VALID(winusb_handle)) {
+ usbi_err(ctx, "interface must be claimed first");
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
+
+ if (!WinUSBX[sub_api].SetCurrentAlternateSetting(winusb_handle, (UCHAR)altsetting)) {
+ usbi_err(ctx, "SetCurrentAlternateSetting failed: %s", windows_error_str(0));
+ return LIBUSB_ERROR_IO;
+ }
+
+ return LIBUSB_SUCCESS;
+}
+
+static int winusbx_submit_bulk_transfer(int sub_api, struct usbi_transfer *itransfer)
+{
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev);
+ struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
+ struct windows_device_handle_priv *handle_priv = _device_handle_priv(transfer->dev_handle);
+ struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev);
+ HANDLE winusb_handle;
+ bool ret;
+ int current_interface;
+ struct winfd wfd;
+
+ CHECK_WINUSBX_AVAILABLE(sub_api);
+
+ transfer_priv->pollable_fd = INVALID_WINFD;
+
+ current_interface = interface_by_endpoint(priv, handle_priv, transfer->endpoint);
+ if (current_interface < 0) {
+ usbi_err(ctx, "unable to match endpoint to an open interface - cancelling transfer");
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
+
+ usbi_dbg("matched endpoint %02X with interface %d", transfer->endpoint, current_interface);
+
+ winusb_handle = handle_priv->interface_handle[current_interface].api_handle;
+
+ wfd = usbi_create_fd(winusb_handle, IS_XFERIN(transfer) ? RW_READ : RW_WRITE, NULL, NULL);
+ // Always use the handle returned from usbi_create_fd (wfd.handle)
+ if (wfd.fd < 0)
+ return LIBUSB_ERROR_NO_MEM;
+
+ if (IS_XFERIN(transfer)) {
+ usbi_dbg("reading %d bytes", transfer->length);
+ ret = WinUSBX[sub_api].ReadPipe(wfd.handle, transfer->endpoint, transfer->buffer, transfer->length, NULL, wfd.overlapped);
+ } else {
+ usbi_dbg("writing %d bytes", transfer->length);
+ ret = WinUSBX[sub_api].WritePipe(wfd.handle, transfer->endpoint, transfer->buffer, transfer->length, NULL, wfd.overlapped);
+ }
+
+ if (!ret) {
+ if (GetLastError() != ERROR_IO_PENDING) {
+ usbi_err(ctx, "ReadPipe/WritePipe failed: %s", windows_error_str(0));
+ usbi_free_fd(&wfd);
+ return LIBUSB_ERROR_IO;
+ }
+ } else {
+ wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY;
+ wfd.overlapped->InternalHigh = (DWORD)transfer->length;
+ }
+
+ transfer_priv->pollable_fd = wfd;
+ transfer_priv->interface_number = (uint8_t)current_interface;
+
+ return LIBUSB_SUCCESS;
+}
+
+static int winusbx_clear_halt(int sub_api, struct libusb_device_handle *dev_handle, unsigned char endpoint)
+{
+ struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev);
+ struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle);
+ struct windows_device_priv *priv = _device_priv(dev_handle->dev);
+ HANDLE winusb_handle;
+ int current_interface;
+
+ CHECK_WINUSBX_AVAILABLE(sub_api);
+
+ current_interface = interface_by_endpoint(priv, handle_priv, endpoint);
+ if (current_interface < 0) {
+ usbi_err(ctx, "unable to match endpoint to an open interface - cannot clear");
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
+
+ usbi_dbg("matched endpoint %02X with interface %d", endpoint, current_interface);
+ winusb_handle = handle_priv->interface_handle[current_interface].api_handle;
+
+ if (!WinUSBX[sub_api].ResetPipe(winusb_handle, endpoint)) {
+ usbi_err(ctx, "ResetPipe failed: %s", windows_error_str(0));
+ return LIBUSB_ERROR_NO_DEVICE;
+ }
+
+ return LIBUSB_SUCCESS;
+}
+
+/*
+ * from http://www.winvistatips.com/winusb-bugchecks-t335323.html (confirmed
+ * through testing as well):
+ * "You can not call WinUsb_AbortPipe on control pipe. You can possibly cancel
+ * the control transfer using CancelIo"
+ */
+static int winusbx_abort_control(int sub_api, struct usbi_transfer *itransfer)
+{
+ // Cancelling of the I/O is done in the parent
+ return LIBUSB_SUCCESS;
+}
+
+static int winusbx_abort_transfers(int sub_api, struct usbi_transfer *itransfer)
+{
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev);
+ struct windows_device_handle_priv *handle_priv = _device_handle_priv(transfer->dev_handle);
+ struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
+ struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev);
+ HANDLE winusb_handle;
+ int current_interface;
+
+ CHECK_WINUSBX_AVAILABLE(sub_api);
+
+ current_interface = transfer_priv->interface_number;
+ if ((current_interface < 0) || (current_interface >= USB_MAXINTERFACES)) {
+ usbi_err(ctx, "program assertion failed: invalid interface_number");
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
+ usbi_dbg("will use interface %d", current_interface);
+
+ winusb_handle = handle_priv->interface_handle[current_interface].api_handle;
+
+ if (!WinUSBX[sub_api].AbortPipe(winusb_handle, transfer->endpoint)) {
+ usbi_err(ctx, "AbortPipe failed: %s", windows_error_str(0));
+ return LIBUSB_ERROR_NO_DEVICE;
+ }
+
+ return LIBUSB_SUCCESS;
+}
+
+/*
+ * from the "How to Use WinUSB to Communicate with a USB Device" Microsoft white paper
+ * (http://www.microsoft.com/whdc/connect/usb/winusb_howto.mspx):
+ * "WinUSB does not support host-initiated reset port and cycle port operations" and
+ * IOCTL_INTERNAL_USB_CYCLE_PORT is only available in kernel mode and the
+ * IOCTL_USB_HUB_CYCLE_PORT ioctl was removed from Vista => the best we can do is
+ * cycle the pipes (and even then, the control pipe can not be reset using WinUSB)
+ */
+// TODO: (post hotplug): see if we can force eject the device and redetect it (reuse hotplug?)
+static int winusbx_reset_device(int sub_api, struct libusb_device_handle *dev_handle)
+{
+ struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev);
+ struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle);
+ struct windows_device_priv *priv = _device_priv(dev_handle->dev);
+ struct winfd wfd;
+ HANDLE winusb_handle;
+ int i, j;
+
+ CHECK_WINUSBX_AVAILABLE(sub_api);
+
+ // Reset any available pipe (except control)
+ for (i = 0; i < USB_MAXINTERFACES; i++) {
+ winusb_handle = handle_priv->interface_handle[i].api_handle;
+ for (wfd = handle_to_winfd(winusb_handle); wfd.fd > 0; ) {
+ // Cancel any pollable I/O
+ usbi_remove_pollfd(ctx, wfd.fd);
+ usbi_free_fd(&wfd);
+ wfd = handle_to_winfd(winusb_handle);
+ }
+
+ if (HANDLE_VALID(winusb_handle)) {
+ for (j = 0; j < priv->usb_interface[i].nb_endpoints; j++) {
+ usbi_dbg("resetting ep %02X", priv->usb_interface[i].endpoint[j]);
+ if (!WinUSBX[sub_api].AbortPipe(winusb_handle, priv->usb_interface[i].endpoint[j]))
+ usbi_err(ctx, "AbortPipe (pipe address %02X) failed: %s",
+ priv->usb_interface[i].endpoint[j], windows_error_str(0));
+
+ // FlushPipe seems to fail on OUT pipes
+ if (IS_EPIN(priv->usb_interface[i].endpoint[j])
+ && (!WinUSBX[sub_api].FlushPipe(winusb_handle, priv->usb_interface[i].endpoint[j])))
+ usbi_err(ctx, "FlushPipe (pipe address %02X) failed: %s",
+ priv->usb_interface[i].endpoint[j], windows_error_str(0));
+
+ if (!WinUSBX[sub_api].ResetPipe(winusb_handle, priv->usb_interface[i].endpoint[j]))
+ usbi_err(ctx, "ResetPipe (pipe address %02X) failed: %s",
+ priv->usb_interface[i].endpoint[j], windows_error_str(0));
+ }
+ }
+ }
+
+ // libusbK & libusb0 have the ability to issue an actual device reset
+ if (WinUSBX[sub_api].ResetDevice != NULL) {
+ winusb_handle = handle_priv->interface_handle[0].api_handle;
+ if (HANDLE_VALID(winusb_handle))
+ WinUSBX[sub_api].ResetDevice(winusb_handle);
+ }
+
+ return LIBUSB_SUCCESS;
+}
+
+static int winusbx_copy_transfer_data(int sub_api, struct usbi_transfer *itransfer, uint32_t io_size)
+{
+ itransfer->transferred += io_size;
+ return LIBUSB_TRANSFER_COMPLETED;
+}
+
+/*
+ * Internal HID Support functions (from libusb-win32)
+ * Note that functions that complete data transfer synchronously must return
+ * LIBUSB_COMPLETED instead of LIBUSB_SUCCESS
+ */
+static int _hid_get_hid_descriptor(struct hid_device_priv *dev, void *data, size_t *size);
+static int _hid_get_report_descriptor(struct hid_device_priv *dev, void *data, size_t *size);
+
+static int _hid_wcslen(WCHAR *str)
+{
+ int i = 0;
+
+ while (str[i] && (str[i] != 0x409))
+ i++;
+
+ return i;
+}
+
+static int _hid_get_device_descriptor(struct hid_device_priv *dev, void *data, size_t *size)
+{
+ struct libusb_device_descriptor d;
+
+ d.bLength = LIBUSB_DT_DEVICE_SIZE;
+ d.bDescriptorType = LIBUSB_DT_DEVICE;
+ d.bcdUSB = 0x0200; /* 2.00 */
+ d.bDeviceClass = 0;
+ d.bDeviceSubClass = 0;
+ d.bDeviceProtocol = 0;
+ d.bMaxPacketSize0 = 64; /* fix this! */
+ d.idVendor = (uint16_t)dev->vid;
+ d.idProduct = (uint16_t)dev->pid;
+ d.bcdDevice = 0x0100;
+ d.iManufacturer = dev->string_index[0];
+ d.iProduct = dev->string_index[1];
+ d.iSerialNumber = dev->string_index[2];
+ d.bNumConfigurations = 1;
+
+ if (*size > LIBUSB_DT_DEVICE_SIZE)
+ *size = LIBUSB_DT_DEVICE_SIZE;
+ memcpy(data, &d, *size);
+
+ return LIBUSB_COMPLETED;
+}
+
+static int _hid_get_config_descriptor(struct hid_device_priv *dev, void *data, size_t *size)
+{
+ char num_endpoints = 0;
+ size_t config_total_len = 0;
+ char tmp[HID_MAX_CONFIG_DESC_SIZE];
+ struct libusb_config_descriptor *cd;
+ struct libusb_interface_descriptor *id;
+ struct libusb_hid_descriptor *hd;
+ struct libusb_endpoint_descriptor *ed;
+ size_t tmp_size;
+
+ if (dev->input_report_size)
+ num_endpoints++;
+ if (dev->output_report_size)
+ num_endpoints++;
+
+ config_total_len = LIBUSB_DT_CONFIG_SIZE + LIBUSB_DT_INTERFACE_SIZE
+ + LIBUSB_DT_HID_SIZE + num_endpoints * LIBUSB_DT_ENDPOINT_SIZE;
+
+ cd = (struct libusb_config_descriptor *)tmp;
+ id = (struct libusb_interface_descriptor *)(tmp + LIBUSB_DT_CONFIG_SIZE);
+ hd = (struct libusb_hid_descriptor *)(tmp + LIBUSB_DT_CONFIG_SIZE
+ + LIBUSB_DT_INTERFACE_SIZE);
+ ed = (struct libusb_endpoint_descriptor *)(tmp + LIBUSB_DT_CONFIG_SIZE
+ + LIBUSB_DT_INTERFACE_SIZE
+ + LIBUSB_DT_HID_SIZE);
+
+ cd->bLength = LIBUSB_DT_CONFIG_SIZE;
+ cd->bDescriptorType = LIBUSB_DT_CONFIG;
+ cd->wTotalLength = (uint16_t)config_total_len;
+ cd->bNumInterfaces = 1;
+ cd->bConfigurationValue = 1;
+ cd->iConfiguration = 0;
+ cd->bmAttributes = 1 << 7; /* bus powered */
+ cd->MaxPower = 50;
+
+ id->bLength = LIBUSB_DT_INTERFACE_SIZE;
+ id->bDescriptorType = LIBUSB_DT_INTERFACE;
+ id->bInterfaceNumber = 0;
+ id->bAlternateSetting = 0;
+ id->bNumEndpoints = num_endpoints;
+ id->bInterfaceClass = 3;
+ id->bInterfaceSubClass = 0;
+ id->bInterfaceProtocol = 0;
+ id->iInterface = 0;
+
+ tmp_size = LIBUSB_DT_HID_SIZE;
+ _hid_get_hid_descriptor(dev, hd, &tmp_size);
+
+ if (dev->input_report_size) {
+ ed->bLength = LIBUSB_DT_ENDPOINT_SIZE;
+ ed->bDescriptorType = LIBUSB_DT_ENDPOINT;
+ ed->bEndpointAddress = HID_IN_EP;
+ ed->bmAttributes = 3;
+ ed->wMaxPacketSize = dev->input_report_size - 1;
+ ed->bInterval = 10;
+ ed = (struct libusb_endpoint_descriptor *)((char *)ed + LIBUSB_DT_ENDPOINT_SIZE);
+ }
+
+ if (dev->output_report_size) {
+ ed->bLength = LIBUSB_DT_ENDPOINT_SIZE;
+ ed->bDescriptorType = LIBUSB_DT_ENDPOINT;
+ ed->bEndpointAddress = HID_OUT_EP;
+ ed->bmAttributes = 3;
+ ed->wMaxPacketSize = dev->output_report_size - 1;
+ ed->bInterval = 10;
+ }
+
+ if (*size > config_total_len)
+ *size = config_total_len;
+ memcpy(data, tmp, *size);
+
+ return LIBUSB_COMPLETED;
+}
+
+static int _hid_get_string_descriptor(struct hid_device_priv *dev, int _index,
+ void *data, size_t *size)
+{
+ void *tmp = NULL;
+ size_t tmp_size = 0;
+ int i;
+
+ /* language ID, EN-US */
+ char string_langid[] = {0x09, 0x04};
+
+ if ((*size < 2) || (*size > 255))
+ return LIBUSB_ERROR_OVERFLOW;
+
+ if (_index == 0) {
+ tmp = string_langid;
+ tmp_size = sizeof(string_langid) + 2;
+ } else {
+ for (i = 0; i < 3; i++) {
+ if (_index == (dev->string_index[i])) {
+ tmp = dev->string[i];
+ tmp_size = (_hid_wcslen(dev->string[i]) + 1) * sizeof(WCHAR);
+ break;
+ }
+ }
+
+ if (i == 3) // not found
+ return LIBUSB_ERROR_INVALID_PARAM;
+ }
+
+ if (!tmp_size)
+ return LIBUSB_ERROR_INVALID_PARAM;
+
+ if (tmp_size < *size)
+ *size = tmp_size;
+
+ // 2 byte header
+ ((uint8_t *)data)[0] = (uint8_t)*size;
+ ((uint8_t *)data)[1] = LIBUSB_DT_STRING;
+ memcpy((uint8_t *)data + 2, tmp, *size - 2);
+
+ return LIBUSB_COMPLETED;
+}
+
+static int _hid_get_hid_descriptor(struct hid_device_priv *dev, void *data, size_t *size)
+{
+ struct libusb_hid_descriptor d;
+ uint8_t tmp[MAX_HID_DESCRIPTOR_SIZE];
+ size_t report_len = MAX_HID_DESCRIPTOR_SIZE;
+
+ _hid_get_report_descriptor(dev, tmp, &report_len);
+
+ d.bLength = LIBUSB_DT_HID_SIZE;
+ d.bDescriptorType = LIBUSB_DT_HID;
+ d.bcdHID = 0x0110; /* 1.10 */
+ d.bCountryCode = 0;
+ d.bNumDescriptors = 1;
+ d.bClassDescriptorType = LIBUSB_DT_REPORT;
+ d.wClassDescriptorLength = (uint16_t)report_len;
+
+ if (*size > LIBUSB_DT_HID_SIZE)
+ *size = LIBUSB_DT_HID_SIZE;
+ memcpy(data, &d, *size);
+
+ return LIBUSB_COMPLETED;
+}
+
+static int _hid_get_report_descriptor(struct hid_device_priv *dev, void *data, size_t *size)
+{
+ uint8_t d[MAX_HID_DESCRIPTOR_SIZE];
+ size_t i = 0;
+
+ /* usage page (0xFFA0 == vendor defined) */
+ d[i++] = 0x06; d[i++] = 0xA0; d[i++] = 0xFF;
+ /* usage (vendor defined) */
+ d[i++] = 0x09; d[i++] = 0x01;
+ /* start collection (application) */
+ d[i++] = 0xA1; d[i++] = 0x01;
+ /* input report */
+ if (dev->input_report_size) {
+ /* usage (vendor defined) */
+ d[i++] = 0x09; d[i++] = 0x01;
+ /* logical minimum (0) */
+ d[i++] = 0x15; d[i++] = 0x00;
+ /* logical maximum (255) */
+ d[i++] = 0x25; d[i++] = 0xFF;
+ /* report size (8 bits) */
+ d[i++] = 0x75; d[i++] = 0x08;
+ /* report count */
+ d[i++] = 0x95; d[i++] = (uint8_t)dev->input_report_size - 1;
+ /* input (data, variable, absolute) */
+ d[i++] = 0x81; d[i++] = 0x00;
+ }
+ /* output report */
+ if (dev->output_report_size) {
+ /* usage (vendor defined) */
+ d[i++] = 0x09; d[i++] = 0x02;
+ /* logical minimum (0) */
+ d[i++] = 0x15; d[i++] = 0x00;
+ /* logical maximum (255) */
+ d[i++] = 0x25; d[i++] = 0xFF;
+ /* report size (8 bits) */
+ d[i++] = 0x75; d[i++] = 0x08;
+ /* report count */
+ d[i++] = 0x95; d[i++] = (uint8_t)dev->output_report_size - 1;
+ /* output (data, variable, absolute) */
+ d[i++] = 0x91; d[i++] = 0x00;
+ }
+ /* feature report */
+ if (dev->feature_report_size) {
+ /* usage (vendor defined) */
+ d[i++] = 0x09; d[i++] = 0x03;
+ /* logical minimum (0) */
+ d[i++] = 0x15; d[i++] = 0x00;
+ /* logical maximum (255) */
+ d[i++] = 0x25; d[i++] = 0xFF;
+ /* report size (8 bits) */
+ d[i++] = 0x75; d[i++] = 0x08;
+ /* report count */
+ d[i++] = 0x95; d[i++] = (uint8_t)dev->feature_report_size - 1;
+ /* feature (data, variable, absolute) */
+ d[i++] = 0xb2; d[i++] = 0x02; d[i++] = 0x01;
+ }
+
+ /* end collection */
+ d[i++] = 0xC0;
+
+ if (*size > i)
+ *size = i;
+ memcpy(data, d, *size);
+
+ return LIBUSB_COMPLETED;
+}
+
+static int _hid_get_descriptor(struct hid_device_priv *dev, HANDLE hid_handle, int recipient,
+ int type, int _index, void *data, size_t *size)
+{
+ switch(type) {
+ case LIBUSB_DT_DEVICE:
+ usbi_dbg("LIBUSB_DT_DEVICE");
+ return _hid_get_device_descriptor(dev, data, size);
+ case LIBUSB_DT_CONFIG:
+ usbi_dbg("LIBUSB_DT_CONFIG");
+ if (!_index)
+ return _hid_get_config_descriptor(dev, data, size);
+ return LIBUSB_ERROR_INVALID_PARAM;
+ case LIBUSB_DT_STRING:
+ usbi_dbg("LIBUSB_DT_STRING");
+ return _hid_get_string_descriptor(dev, _index, data, size);
+ case LIBUSB_DT_HID:
+ usbi_dbg("LIBUSB_DT_HID");
+ if (!_index)
+ return _hid_get_hid_descriptor(dev, data, size);
+ return LIBUSB_ERROR_INVALID_PARAM;
+ case LIBUSB_DT_REPORT:
+ usbi_dbg("LIBUSB_DT_REPORT");
+ if (!_index)
+ return _hid_get_report_descriptor(dev, data, size);
+ return LIBUSB_ERROR_INVALID_PARAM;
+ case LIBUSB_DT_PHYSICAL:
+ usbi_dbg("LIBUSB_DT_PHYSICAL");
+ if (HidD_GetPhysicalDescriptor(hid_handle, data, (ULONG)*size))
+ return LIBUSB_COMPLETED;
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ usbi_dbg("unsupported");
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+}
+
+static int _hid_get_report(struct hid_device_priv *dev, HANDLE hid_handle, int id, void *data,
+ struct windows_transfer_priv *tp, size_t *size, OVERLAPPED *overlapped, int report_type)
+{
+ uint8_t *buf;
+ DWORD ioctl_code, read_size, expected_size = (DWORD)*size;
+ int r = LIBUSB_SUCCESS;
+
+ if (tp->hid_buffer != NULL)
+ usbi_dbg("program assertion failed: hid_buffer is not NULL");
+
+ if ((*size == 0) || (*size > MAX_HID_REPORT_SIZE)) {
+ usbi_dbg("invalid size (%u)", *size);
+ return LIBUSB_ERROR_INVALID_PARAM;
+ }
+
+ switch (report_type) {
+ case HID_REPORT_TYPE_INPUT:
+ ioctl_code = IOCTL_HID_GET_INPUT_REPORT;
+ break;
+ case HID_REPORT_TYPE_FEATURE:
+ ioctl_code = IOCTL_HID_GET_FEATURE;
+ break;
+ default:
+ usbi_dbg("unknown HID report type %d", report_type);
+ return LIBUSB_ERROR_INVALID_PARAM;
+ }
+
+ // Add a trailing byte to detect overflows
+ buf = calloc(1, expected_size + 1);
+ if (buf == NULL)
+ return LIBUSB_ERROR_NO_MEM;
+
+ buf[0] = (uint8_t)id; // Must be set always
+ usbi_dbg("report ID: 0x%02X", buf[0]);
+
+ tp->hid_expected_size = expected_size;
+ read_size = expected_size;
+
+ // NB: The size returned by DeviceIoControl doesn't include report IDs when not in use (0)
+ if (!DeviceIoControl(hid_handle, ioctl_code, buf, expected_size + 1,
+ buf, expected_size + 1, &read_size, overlapped)) {
+ if (GetLastError() != ERROR_IO_PENDING) {
+ usbi_dbg("Failed to Read HID Report: %s", windows_error_str(0));
+ free(buf);
+ return LIBUSB_ERROR_IO;
+ }
+ // Asynchronous wait
+ tp->hid_buffer = buf;
+ tp->hid_dest = data; // copy dest, as not necessarily the start of the transfer buffer
+ return LIBUSB_SUCCESS;
+ }
+
+ // Transfer completed synchronously => copy and discard extra buffer
+ if (read_size == 0) {
+ usbi_warn(NULL, "program assertion failed - read completed synchronously, but no data was read");
+ *size = 0;
+ } else {
+ if (buf[0] != id)
+ usbi_warn(NULL, "mismatched report ID (data is %02X, parameter is %02X)", buf[0], id);
+
+ if ((size_t)read_size > expected_size) {
+ r = LIBUSB_ERROR_OVERFLOW;
+ usbi_dbg("OVERFLOW!");
+ } else {
+ r = LIBUSB_COMPLETED;
+ }
+
+ *size = MIN((size_t)read_size, *size);
+ if (id == 0)
+ memcpy(data, buf + 1, *size); // Discard report ID
+ else
+ memcpy(data, buf, *size);
+ }
+
+ free(buf);
+ return r;
+}
+
+static int _hid_set_report(struct hid_device_priv *dev, HANDLE hid_handle, int id, void *data,
+ struct windows_transfer_priv *tp, size_t *size, OVERLAPPED *overlapped, int report_type)
+{
+ uint8_t *buf = NULL;
+ DWORD ioctl_code, write_size = (DWORD)*size;
+
+ if (tp->hid_buffer != NULL)
+ usbi_dbg("program assertion failed: hid_buffer is not NULL");
+
+ if ((*size == 0) || (*size > MAX_HID_REPORT_SIZE)) {
+ usbi_dbg("invalid size (%u)", *size);
+ return LIBUSB_ERROR_INVALID_PARAM;
+ }
+
+ switch (report_type) {
+ case HID_REPORT_TYPE_OUTPUT:
+ ioctl_code = IOCTL_HID_SET_OUTPUT_REPORT;
+ break;
+ case HID_REPORT_TYPE_FEATURE:
+ ioctl_code = IOCTL_HID_SET_FEATURE;
+ break;
+ default:
+ usbi_dbg("unknown HID report type %d", report_type);
+ return LIBUSB_ERROR_INVALID_PARAM;
+ }
+
+ usbi_dbg("report ID: 0x%02X", id);
+ // When report IDs are not used (i.e. when id == 0), we must add
+ // a null report ID. Otherwise, we just use original data buffer
+ if (id == 0)
+ write_size++;
+
+ buf = malloc(write_size);
+ if (buf == NULL)
+ return LIBUSB_ERROR_NO_MEM;
+
+ if (id == 0) {
+ buf[0] = 0;
+ memcpy(buf + 1, data, *size);
+ } else {
+ // This seems like a waste, but if we don't duplicate the
+ // data, we'll get issues when freeing hid_buffer
+ memcpy(buf, data, *size);
+ if (buf[0] != id)
+ usbi_warn(NULL, "mismatched report ID (data is %02X, parameter is %02X)", buf[0], id);
+ }
+
+ // NB: The size returned by DeviceIoControl doesn't include report IDs when not in use (0)
+ if (!DeviceIoControl(hid_handle, ioctl_code, buf, write_size,
+ buf, write_size, &write_size, overlapped)) {
+ if (GetLastError() != ERROR_IO_PENDING) {
+ usbi_dbg("Failed to Write HID Output Report: %s", windows_error_str(0));
+ free(buf);
+ return LIBUSB_ERROR_IO;
+ }
+ tp->hid_buffer = buf;
+ tp->hid_dest = NULL;
+ return LIBUSB_SUCCESS;
+ }
+
+ // Transfer completed synchronously
+ *size = write_size;
+ if (write_size == 0)
+ usbi_dbg("program assertion failed - write completed synchronously, but no data was written");
+
+ free(buf);
+ return LIBUSB_COMPLETED;
+}
+
+static int _hid_class_request(struct hid_device_priv *dev, HANDLE hid_handle, int request_type,
+ int request, int value, int _index, void *data, struct windows_transfer_priv *tp,
+ size_t *size, OVERLAPPED *overlapped)
+{
+ int report_type = (value >> 8) & 0xFF;
+ int report_id = value & 0xFF;
+
+ if ((LIBUSB_REQ_RECIPIENT(request_type) != LIBUSB_RECIPIENT_INTERFACE)
+ && (LIBUSB_REQ_RECIPIENT(request_type) != LIBUSB_RECIPIENT_DEVICE))
+ return LIBUSB_ERROR_INVALID_PARAM;
+
+ if (LIBUSB_REQ_OUT(request_type) && request == HID_REQ_SET_REPORT)
+ return _hid_set_report(dev, hid_handle, report_id, data, tp, size, overlapped, report_type);
+
+ if (LIBUSB_REQ_IN(request_type) && request == HID_REQ_GET_REPORT)
+ return _hid_get_report(dev, hid_handle, report_id, data, tp, size, overlapped, report_type);
+
+ return LIBUSB_ERROR_INVALID_PARAM;
+}
+
+
+/*
+ * HID API functions
+ */
+static int hid_init(int sub_api, struct libusb_context *ctx)
+{
+ DLL_GET_HANDLE(hid);
+ DLL_LOAD_FUNC(hid, HidD_GetAttributes, TRUE);
+ DLL_LOAD_FUNC(hid, HidD_GetHidGuid, TRUE);
+ DLL_LOAD_FUNC(hid, HidD_GetPreparsedData, TRUE);
+ DLL_LOAD_FUNC(hid, HidD_FreePreparsedData, TRUE);
+ DLL_LOAD_FUNC(hid, HidD_GetManufacturerString, TRUE);
+ DLL_LOAD_FUNC(hid, HidD_GetProductString, TRUE);
+ DLL_LOAD_FUNC(hid, HidD_GetSerialNumberString, TRUE);
+ DLL_LOAD_FUNC(hid, HidP_GetCaps, TRUE);
+ DLL_LOAD_FUNC(hid, HidD_SetNumInputBuffers, TRUE);
+ DLL_LOAD_FUNC(hid, HidD_SetFeature, TRUE);
+ DLL_LOAD_FUNC(hid, HidD_GetFeature, TRUE);
+ DLL_LOAD_FUNC(hid, HidD_GetPhysicalDescriptor, TRUE);
+ DLL_LOAD_FUNC(hid, HidD_GetInputReport, FALSE);
+ DLL_LOAD_FUNC(hid, HidD_SetOutputReport, FALSE);
+ DLL_LOAD_FUNC(hid, HidD_FlushQueue, TRUE);
+ DLL_LOAD_FUNC(hid, HidP_GetValueCaps, TRUE);
+
+ api_hid_available = true;
+ return LIBUSB_SUCCESS;
+}
+
+static int hid_exit(int sub_api)
+{
+ DLL_FREE_HANDLE(hid);
+
+ return LIBUSB_SUCCESS;
+}
+
+// NB: open and close must ensure that they only handle interface of
+// the right API type, as these functions can be called wholesale from
+// composite_open(), with interfaces belonging to different APIs
+static int hid_open(int sub_api, struct libusb_device_handle *dev_handle)
+{
+ struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev);
+ struct windows_device_priv *priv = _device_priv(dev_handle->dev);
+ struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle);
+ HIDD_ATTRIBUTES hid_attributes;
+ PHIDP_PREPARSED_DATA preparsed_data = NULL;
+ HIDP_CAPS capabilities;
+ HIDP_VALUE_CAPS *value_caps;
+ HANDLE hid_handle = INVALID_HANDLE_VALUE;
+ int i, j;
+ // report IDs handling
+ ULONG size[3];
+ int nb_ids[2]; // zero and nonzero report IDs
+#if defined(ENABLE_LOGGING)
+ const char *type[3] = {"input", "output", "feature"};
+#endif
+
+ CHECK_HID_AVAILABLE;
+
+ if (priv->hid == NULL) {
+ usbi_err(ctx, "program assertion failed - private HID structure is unitialized");
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
+
+ for (i = 0; i < USB_MAXINTERFACES; i++) {
+ if ((priv->usb_interface[i].path != NULL)
+ && (priv->usb_interface[i].apib->id == USB_API_HID)) {
+ hid_handle = CreateFileA(priv->usb_interface[i].path, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ,
+ NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
+ /*
+ * http://www.lvr.com/hidfaq.htm: Why do I receive "Access denied" when attempting to access my HID?
+ * "Windows 2000 and later have exclusive read/write access to HIDs that are configured as a system
+ * keyboards or mice. An application can obtain a handle to a system keyboard or mouse by not
+ * requesting READ or WRITE access with CreateFile. Applications can then use HidD_SetFeature and
+ * HidD_GetFeature (if the device supports Feature reports)."
+ */
+ if (hid_handle == INVALID_HANDLE_VALUE) {
+ usbi_warn(ctx, "could not open HID device in R/W mode (keyboard or mouse?) - trying without");
+ hid_handle = CreateFileA(priv->usb_interface[i].path, 0, FILE_SHARE_WRITE | FILE_SHARE_READ,
+ NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
+ if (hid_handle == INVALID_HANDLE_VALUE) {
+ usbi_err(ctx, "could not open device %s (interface %d): %s", priv->path, i, windows_error_str(0));
+ switch(GetLastError()) {
+ case ERROR_FILE_NOT_FOUND: // The device was disconnected
+ return LIBUSB_ERROR_NO_DEVICE;
+ case ERROR_ACCESS_DENIED:
+ return LIBUSB_ERROR_ACCESS;
+ default:
+ return LIBUSB_ERROR_IO;
+ }
+ }
+ priv->usb_interface[i].restricted_functionality = true;
+ }
+ handle_priv->interface_handle[i].api_handle = hid_handle;
+ }
+ }
+
+ hid_attributes.Size = sizeof(hid_attributes);
+ do {
+ if (!HidD_GetAttributes(hid_handle, &hid_attributes)) {
+ usbi_err(ctx, "could not gain access to HID top collection (HidD_GetAttributes)");
+ break;
+ }
+
+ priv->hid->vid = hid_attributes.VendorID;
+ priv->hid->pid = hid_attributes.ProductID;
+
+ // Set the maximum available input buffer size
+ for (i = 32; HidD_SetNumInputBuffers(hid_handle, i); i *= 2);
+ usbi_dbg("set maximum input buffer size to %d", i / 2);
+
+ // Get the maximum input and output report size
+ if (!HidD_GetPreparsedData(hid_handle, &preparsed_data) || !preparsed_data) {
+ usbi_err(ctx, "could not read HID preparsed data (HidD_GetPreparsedData)");
+ break;
+ }
+ if (HidP_GetCaps(preparsed_data, &capabilities) != HIDP_STATUS_SUCCESS) {
+ usbi_err(ctx, "could not parse HID capabilities (HidP_GetCaps)");
+ break;
+ }
+
+ // Find out if interrupt will need report IDs
+ size[0] = capabilities.NumberInputValueCaps;
+ size[1] = capabilities.NumberOutputValueCaps;
+ size[2] = capabilities.NumberFeatureValueCaps;
+ for (j = HidP_Input; j <= HidP_Feature; j++) {
+ usbi_dbg("%u HID %s report value(s) found", (unsigned int)size[j], type[j]);
+ priv->hid->uses_report_ids[j] = false;
+ if (size[j] > 0) {
+ value_caps = calloc(size[j], sizeof(HIDP_VALUE_CAPS));
+ if ((value_caps != NULL)
+ && (HidP_GetValueCaps((HIDP_REPORT_TYPE)j, value_caps, &size[j], preparsed_data) == HIDP_STATUS_SUCCESS)
+ && (size[j] >= 1)) {
+ nb_ids[0] = 0;
+ nb_ids[1] = 0;
+ for (i = 0; i < (int)size[j]; i++) {
+ usbi_dbg(" Report ID: 0x%02X", value_caps[i].ReportID);
+ if (value_caps[i].ReportID != 0)
+ nb_ids[1]++;
+ else
+ nb_ids[0]++;
+ }
+ if (nb_ids[1] != 0) {
+ if (nb_ids[0] != 0)
+ usbi_warn(ctx, "program assertion failed: zero and nonzero report IDs used for %s",
+ type[j]);
+ priv->hid->uses_report_ids[j] = true;
+ }
+ } else {
+ usbi_warn(ctx, " could not process %s report IDs", type[j]);
+ }
+ free(value_caps);
+ }
+ }
+
+ // Set the report sizes
+ priv->hid->input_report_size = capabilities.InputReportByteLength;
+ priv->hid->output_report_size = capabilities.OutputReportByteLength;
+ priv->hid->feature_report_size = capabilities.FeatureReportByteLength;
+
+ // Fetch string descriptors
+ priv->hid->string_index[0] = priv->dev_descriptor.iManufacturer;
+ if (priv->hid->string_index[0] != 0)
+ HidD_GetManufacturerString(hid_handle, priv->hid->string[0], sizeof(priv->hid->string[0]));
+ else
+ priv->hid->string[0][0] = 0;
+
+ priv->hid->string_index[1] = priv->dev_descriptor.iProduct;
+ if (priv->hid->string_index[1] != 0)
+ HidD_GetProductString(hid_handle, priv->hid->string[1], sizeof(priv->hid->string[1]));
+ else
+ priv->hid->string[1][0] = 0;
+
+ priv->hid->string_index[2] = priv->dev_descriptor.iSerialNumber;
+ if (priv->hid->string_index[2] != 0)
+ HidD_GetSerialNumberString(hid_handle, priv->hid->string[2], sizeof(priv->hid->string[2]));
+ else
+ priv->hid->string[2][0] = 0;
+ } while(0);
+
+ if (preparsed_data)
+ HidD_FreePreparsedData(preparsed_data);
+
+ return LIBUSB_SUCCESS;
+}
+
+static void hid_close(int sub_api, struct libusb_device_handle *dev_handle)
+{
+ struct windows_device_priv *priv = _device_priv(dev_handle->dev);
+ struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle);
+ HANDLE file_handle;
+ int i;
+
+ if (!api_hid_available)
+ return;
+
+ for (i = 0; i < USB_MAXINTERFACES; i++) {
+ if (priv->usb_interface[i].apib->id == USB_API_HID) {
+ file_handle = handle_priv->interface_handle[i].api_handle;
+ if (HANDLE_VALID(file_handle))
+ CloseHandle(file_handle);
+ }
+ }
+}
+
+static int hid_claim_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface)
+{
+ struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle);
+ struct windows_device_priv *priv = _device_priv(dev_handle->dev);
+
+ CHECK_HID_AVAILABLE;
+
+ // NB: Disconnection detection is not possible in this function
+ if (priv->usb_interface[iface].path == NULL)
+ return LIBUSB_ERROR_NOT_FOUND; // invalid iface
+
+ // We use dev_handle as a flag for interface claimed
+ if (handle_priv->interface_handle[iface].dev_handle == INTERFACE_CLAIMED)
+ return LIBUSB_ERROR_BUSY; // already claimed
+
+
+ handle_priv->interface_handle[iface].dev_handle = INTERFACE_CLAIMED;
+
+ usbi_dbg("claimed interface %d", iface);
+ handle_priv->active_interface = iface;
+
+ return LIBUSB_SUCCESS;
+}
+
+static int hid_release_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface)
+{
+ struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle);
+ struct windows_device_priv *priv = _device_priv(dev_handle->dev);
+
+ CHECK_HID_AVAILABLE;
+
+ if (priv->usb_interface[iface].path == NULL)
+ return LIBUSB_ERROR_NOT_FOUND; // invalid iface
+
+ if (handle_priv->interface_handle[iface].dev_handle != INTERFACE_CLAIMED)
+ return LIBUSB_ERROR_NOT_FOUND; // invalid iface
+
+ handle_priv->interface_handle[iface].dev_handle = INVALID_HANDLE_VALUE;
+
+ return LIBUSB_SUCCESS;
+}
+
+static int hid_set_interface_altsetting(int sub_api, struct libusb_device_handle *dev_handle, int iface, int altsetting)
+{
+ struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev);
+
+ CHECK_HID_AVAILABLE;
+
+ if (altsetting > 255)
+ return LIBUSB_ERROR_INVALID_PARAM;
+
+ if (altsetting != 0) {
+ usbi_err(ctx, "set interface altsetting not supported for altsetting >0");
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+ }
+
+ return LIBUSB_SUCCESS;
+}
+
+static int hid_submit_control_transfer(int sub_api, struct usbi_transfer *itransfer)
+{
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
+ struct windows_device_handle_priv *handle_priv = _device_handle_priv(transfer->dev_handle);
+ struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev);
+ struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev);
+ WINUSB_SETUP_PACKET *setup = (WINUSB_SETUP_PACKET *)transfer->buffer;
+ HANDLE hid_handle;
+ struct winfd wfd;
+ int current_interface, config;
+ size_t size;
+ int r = LIBUSB_ERROR_INVALID_PARAM;
+
+ CHECK_HID_AVAILABLE;
+
+ transfer_priv->pollable_fd = INVALID_WINFD;
+ safe_free(transfer_priv->hid_buffer);
+ transfer_priv->hid_dest = NULL;
+ size = transfer->length - LIBUSB_CONTROL_SETUP_SIZE;
+
+ if (size > MAX_CTRL_BUFFER_LENGTH)
+ return LIBUSB_ERROR_INVALID_PARAM;
+
+ current_interface = get_valid_interface(transfer->dev_handle, USB_API_HID);
+ if (current_interface < 0) {
+ if (auto_claim(transfer, &current_interface, USB_API_HID) != LIBUSB_SUCCESS)
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
+
+ usbi_dbg("will use interface %d", current_interface);
+ hid_handle = handle_priv->interface_handle[current_interface].api_handle;
+ // Always use the handle returned from usbi_create_fd (wfd.handle)
+ wfd = usbi_create_fd(hid_handle, RW_READ, NULL, NULL);
+ if (wfd.fd < 0)
+ return LIBUSB_ERROR_NOT_FOUND;
+
+ switch(LIBUSB_REQ_TYPE(setup->request_type)) {
+ case LIBUSB_REQUEST_TYPE_STANDARD:
+ switch(setup->request) {
+ case LIBUSB_REQUEST_GET_DESCRIPTOR:
+ r = _hid_get_descriptor(priv->hid, wfd.handle, LIBUSB_REQ_RECIPIENT(setup->request_type),
+ (setup->value >> 8) & 0xFF, setup->value & 0xFF, transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE, &size);
+ break;
+ case LIBUSB_REQUEST_GET_CONFIGURATION:
+ r = windows_get_configuration(transfer->dev_handle, &config);
+ if (r == LIBUSB_SUCCESS) {
+ size = 1;
+ ((uint8_t *)transfer->buffer)[LIBUSB_CONTROL_SETUP_SIZE] = (uint8_t)config;
+ r = LIBUSB_COMPLETED;
+ }
+ break;
+ case LIBUSB_REQUEST_SET_CONFIGURATION:
+ if (setup->value == priv->active_config) {
+ r = LIBUSB_COMPLETED;
+ } else {
+ usbi_warn(ctx, "cannot set configuration other than the default one");
+ r = LIBUSB_ERROR_NOT_SUPPORTED;
+ }
+ break;
+ case LIBUSB_REQUEST_GET_INTERFACE:
+ size = 1;
+ ((uint8_t *)transfer->buffer)[LIBUSB_CONTROL_SETUP_SIZE] = 0;
+ r = LIBUSB_COMPLETED;
+ break;
+ case LIBUSB_REQUEST_SET_INTERFACE:
+ r = hid_set_interface_altsetting(0, transfer->dev_handle, setup->index, setup->value);
+ if (r == LIBUSB_SUCCESS)
+ r = LIBUSB_COMPLETED;
+ break;
+ default:
+ usbi_warn(ctx, "unsupported HID control request");
+ r = LIBUSB_ERROR_NOT_SUPPORTED;
+ break;
+ }
+ break;
+ case LIBUSB_REQUEST_TYPE_CLASS:
+ r = _hid_class_request(priv->hid, wfd.handle, setup->request_type, setup->request, setup->value,
+ setup->index, transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE, transfer_priv,
+ &size, wfd.overlapped);
+ break;
+ default:
+ usbi_warn(ctx, "unsupported HID control request");
+ r = LIBUSB_ERROR_NOT_SUPPORTED;
+ break;
+ }
+
+ if (r == LIBUSB_COMPLETED) {
+ // Force request to be completed synchronously. Transferred size has been set by previous call
+ wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY;
+ // http://msdn.microsoft.com/en-us/library/ms684342%28VS.85%29.aspx
+ // set InternalHigh to the number of bytes transferred
+ wfd.overlapped->InternalHigh = (DWORD)size;
+ r = LIBUSB_SUCCESS;
+ }
+
+ if (r == LIBUSB_SUCCESS) {
+ // Use priv_transfer to store data needed for async polling
+ transfer_priv->pollable_fd = wfd;
+ transfer_priv->interface_number = (uint8_t)current_interface;
+ } else {
+ usbi_free_fd(&wfd);
+ }
+
+ return r;
+}
+
+static int hid_submit_bulk_transfer(int sub_api, struct usbi_transfer *itransfer)
+{
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
+ struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev);
+ struct windows_device_handle_priv *handle_priv = _device_handle_priv(transfer->dev_handle);
+ struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev);
+ struct winfd wfd;
+ HANDLE hid_handle;
+ bool direction_in, ret;
+ int current_interface, length;
+ DWORD size;
+ int r = LIBUSB_SUCCESS;
+
+ CHECK_HID_AVAILABLE;
+
+ transfer_priv->pollable_fd = INVALID_WINFD;
+ transfer_priv->hid_dest = NULL;
+ safe_free(transfer_priv->hid_buffer);
+
+ current_interface = interface_by_endpoint(priv, handle_priv, transfer->endpoint);
+ if (current_interface < 0) {
+ usbi_err(ctx, "unable to match endpoint to an open interface - cancelling transfer");
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
+
+ usbi_dbg("matched endpoint %02X with interface %d", transfer->endpoint, current_interface);
+
+ hid_handle = handle_priv->interface_handle[current_interface].api_handle;
+ direction_in = transfer->endpoint & LIBUSB_ENDPOINT_IN;
+
+ wfd = usbi_create_fd(hid_handle, direction_in?RW_READ:RW_WRITE, NULL, NULL);
+ // Always use the handle returned from usbi_create_fd (wfd.handle)
+ if (wfd.fd < 0)
+ return LIBUSB_ERROR_NO_MEM;
+
+ // If report IDs are not in use, an extra prefix byte must be added
+ if (((direction_in) && (!priv->hid->uses_report_ids[0]))
+ || ((!direction_in) && (!priv->hid->uses_report_ids[1])))
+ length = transfer->length + 1;
+ else
+ length = transfer->length;
+
+ // Add a trailing byte to detect overflows on input
+ transfer_priv->hid_buffer = calloc(1, length + 1);
+ if (transfer_priv->hid_buffer == NULL)
+ return LIBUSB_ERROR_NO_MEM;
+
+ transfer_priv->hid_expected_size = length;
+
+ if (direction_in) {
+ transfer_priv->hid_dest = transfer->buffer;
+ usbi_dbg("reading %d bytes (report ID: 0x00)", length);
+ ret = ReadFile(wfd.handle, transfer_priv->hid_buffer, length + 1, &size, wfd.overlapped);
+ } else {
+ if (!priv->hid->uses_report_ids[1])
+ memcpy(transfer_priv->hid_buffer + 1, transfer->buffer, transfer->length);
+ else
+ // We could actually do without the calloc and memcpy in this case
+ memcpy(transfer_priv->hid_buffer, transfer->buffer, transfer->length);
+
+ usbi_dbg("writing %d bytes (report ID: 0x%02X)", length, transfer_priv->hid_buffer[0]);
+ ret = WriteFile(wfd.handle, transfer_priv->hid_buffer, length, &size, wfd.overlapped);
+ }
+
+ if (!ret) {
+ if (GetLastError() != ERROR_IO_PENDING) {
+ usbi_err(ctx, "HID transfer failed: %s", windows_error_str(0));
+ usbi_free_fd(&wfd);
+ safe_free(transfer_priv->hid_buffer);
+ return LIBUSB_ERROR_IO;
+ }
+ } else {
+ // Only write operations that completed synchronously need to free up
+ // hid_buffer. For reads, copy_transfer_data() handles that process.
+ if (!direction_in)
+ safe_free(transfer_priv->hid_buffer);
+
+ if (size == 0) {
+ usbi_err(ctx, "program assertion failed - no data was transferred");
+ size = 1;
+ }
+ if (size > (size_t)length) {
+ usbi_err(ctx, "OVERFLOW!");
+ r = LIBUSB_ERROR_OVERFLOW;
+ }
+ wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY;
+ wfd.overlapped->InternalHigh = size;
+ }
+
+ transfer_priv->pollable_fd = wfd;
+ transfer_priv->interface_number = (uint8_t)current_interface;
+
+ return r;
+}
+
+static int hid_abort_transfers(int sub_api, struct usbi_transfer *itransfer)
+{
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
+ struct windows_device_handle_priv *handle_priv = _device_handle_priv(transfer->dev_handle);
+ HANDLE hid_handle;
+ int current_interface;
+
+ CHECK_HID_AVAILABLE;
+
+ current_interface = transfer_priv->interface_number;
+ hid_handle = handle_priv->interface_handle[current_interface].api_handle;
+ CancelIo(hid_handle);
+
+ return LIBUSB_SUCCESS;
+}
+
+static int hid_reset_device(int sub_api, struct libusb_device_handle *dev_handle)
+{
+ struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle);
+ HANDLE hid_handle;
+ int current_interface;
+
+ CHECK_HID_AVAILABLE;
+
+ // Flushing the queues on all interfaces is the best we can achieve
+ for (current_interface = 0; current_interface < USB_MAXINTERFACES; current_interface++) {
+ hid_handle = handle_priv->interface_handle[current_interface].api_handle;
+ if (HANDLE_VALID(hid_handle))
+ HidD_FlushQueue(hid_handle);
+ }
+
+ return LIBUSB_SUCCESS;
+}
+
+static int hid_clear_halt(int sub_api, struct libusb_device_handle *dev_handle, unsigned char endpoint)
+{
+ struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev);
+ struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle);
+ struct windows_device_priv *priv = _device_priv(dev_handle->dev);
+ HANDLE hid_handle;
+ int current_interface;
+
+ CHECK_HID_AVAILABLE;
+
+ current_interface = interface_by_endpoint(priv, handle_priv, endpoint);
+ if (current_interface < 0) {
+ usbi_err(ctx, "unable to match endpoint to an open interface - cannot clear");
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
+
+ usbi_dbg("matched endpoint %02X with interface %d", endpoint, current_interface);
+ hid_handle = handle_priv->interface_handle[current_interface].api_handle;
+
+ // No endpoint selection with Microsoft's implementation, so we try to flush the
+ // whole interface. Should be OK for most case scenarios
+ if (!HidD_FlushQueue(hid_handle)) {
+ usbi_err(ctx, "Flushing of HID queue failed: %s", windows_error_str(0));
+ // Device was probably disconnected
+ return LIBUSB_ERROR_NO_DEVICE;
+ }
+
+ return LIBUSB_SUCCESS;
+}
+
+// This extra function is only needed for HID
+static int hid_copy_transfer_data(int sub_api, struct usbi_transfer *itransfer, uint32_t io_size)
+{
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev);
+ struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
+ int r = LIBUSB_TRANSFER_COMPLETED;
+ uint32_t corrected_size = io_size;
+
+ if (transfer_priv->hid_buffer != NULL) {
+ // If we have a valid hid_buffer, it means the transfer was async
+ if (transfer_priv->hid_dest != NULL) { // Data readout
+ if (corrected_size > 0) {
+ // First, check for overflow
+ if (corrected_size > transfer_priv->hid_expected_size) {
+ usbi_err(ctx, "OVERFLOW!");
+ corrected_size = (uint32_t)transfer_priv->hid_expected_size;
+ r = LIBUSB_TRANSFER_OVERFLOW;
+ }
+
+ if (transfer_priv->hid_buffer[0] == 0) {
+ // Discard the 1 byte report ID prefix
+ corrected_size--;
+ memcpy(transfer_priv->hid_dest, transfer_priv->hid_buffer + 1, corrected_size);
+ } else {
+ memcpy(transfer_priv->hid_dest, transfer_priv->hid_buffer, corrected_size);
+ }
+ }
+ transfer_priv->hid_dest = NULL;
+ }
+ // For write, we just need to free the hid buffer
+ safe_free(transfer_priv->hid_buffer);
+ }
+
+ itransfer->transferred += corrected_size;
+ return r;
+}
+
+
+/*
+ * Composite API functions
+ */
+static int composite_init(int sub_api, struct libusb_context *ctx)
+{
+ return LIBUSB_SUCCESS;
+}
+
+static int composite_exit(int sub_api)
+{
+ return LIBUSB_SUCCESS;
+}
+
+static int composite_open(int sub_api, struct libusb_device_handle *dev_handle)
+{
+ struct windows_device_priv *priv = _device_priv(dev_handle->dev);
+ int r = LIBUSB_ERROR_NOT_FOUND;
+ uint8_t i;
+ // SUB_API_MAX + 1 as the SUB_API_MAX pos is used to indicate availability of HID
+ bool available[SUB_API_MAX + 1] = { 0 };
+
+ for (i = 0; i < USB_MAXINTERFACES; i++) {
+ switch (priv->usb_interface[i].apib->id) {
+ case USB_API_WINUSBX:
+ if (priv->usb_interface[i].sub_api != SUB_API_NOTSET)
+ available[priv->usb_interface[i].sub_api] = true;
+ break;
+ case USB_API_HID:
+ available[SUB_API_MAX] = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ for (i = 0; i < SUB_API_MAX; i++) { // WinUSB-like drivers
+ if (available[i]) {
+ r = usb_api_backend[USB_API_WINUSBX].open(i, dev_handle);
+ if (r != LIBUSB_SUCCESS)
+ return r;
+ }
+ }
+
+ if (available[SUB_API_MAX]) // HID driver
+ r = hid_open(SUB_API_NOTSET, dev_handle);
+
+ return r;
+}
+
+static void composite_close(int sub_api, struct libusb_device_handle *dev_handle)
+{
+ struct windows_device_priv *priv = _device_priv(dev_handle->dev);
+ uint8_t i;
+ // SUB_API_MAX + 1 as the SUB_API_MAX pos is used to indicate availability of HID
+ bool available[SUB_API_MAX + 1] = { 0 };
+
+ for (i = 0; i < USB_MAXINTERFACES; i++) {
+ switch (priv->usb_interface[i].apib->id) {
+ case USB_API_WINUSBX:
+ if (priv->usb_interface[i].sub_api != SUB_API_NOTSET)
+ available[priv->usb_interface[i].sub_api] = true;
+ break;
+ case USB_API_HID:
+ available[SUB_API_MAX] = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ for (i = 0; i < SUB_API_MAX; i++) { // WinUSB-like drivers
+ if (available[i])
+ usb_api_backend[USB_API_WINUSBX].close(i, dev_handle);
+ }
+
+ if (available[SUB_API_MAX]) // HID driver
+ hid_close(SUB_API_NOTSET, dev_handle);
+}
+
+static int composite_claim_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface)
+{
+ struct windows_device_priv *priv = _device_priv(dev_handle->dev);
+
+ return priv->usb_interface[iface].apib->
+ claim_interface(priv->usb_interface[iface].sub_api, dev_handle, iface);
+}
+
+static int composite_set_interface_altsetting(int sub_api, struct libusb_device_handle *dev_handle, int iface, int altsetting)
+{
+ struct windows_device_priv *priv = _device_priv(dev_handle->dev);
+
+ return priv->usb_interface[iface].apib->
+ set_interface_altsetting(priv->usb_interface[iface].sub_api, dev_handle, iface, altsetting);
+}
+
+static int composite_release_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface)
+{
+ struct windows_device_priv *priv = _device_priv(dev_handle->dev);
+
+ return priv->usb_interface[iface].apib->
+ release_interface(priv->usb_interface[iface].sub_api, dev_handle, iface);
+}
+
+static int composite_submit_control_transfer(int sub_api, struct usbi_transfer *itransfer)
+{
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev);
+ struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev);
+ struct libusb_config_descriptor *conf_desc;
+ WINUSB_SETUP_PACKET *setup = (WINUSB_SETUP_PACKET *)transfer->buffer;
+ int iface, pass, r;
+
+ // Interface shouldn't matter for control, but it does in practice, with Windows'
+ // restrictions with regards to accessing HID keyboards and mice. Try to target
+ // a specific interface first, if possible.
+ switch (LIBUSB_REQ_RECIPIENT(setup->request_type)) {
+ case LIBUSB_RECIPIENT_INTERFACE:
+ iface = setup->index & 0xFF;
+ break;
+ case LIBUSB_RECIPIENT_ENDPOINT:
+ r = libusb_get_active_config_descriptor(transfer->dev_handle->dev, &conf_desc);
+ if (r == LIBUSB_SUCCESS) {
+ iface = get_interface_by_endpoint(conf_desc, (setup->index & 0xFF));
+ libusb_free_config_descriptor(conf_desc);
+ break;
+ }
+ // Fall through if not able to determine interface
+ default:
+ iface = -1;
+ break;
+ }
+
+ // Try and target a specific interface if the control setup indicates such
+ if ((iface >= 0) && (iface < USB_MAXINTERFACES)) {
+ usbi_dbg("attempting control transfer targeted to interface %d", iface);
+ if (priv->usb_interface[iface].path != NULL) {
+ r = priv->usb_interface[iface].apib->submit_control_transfer(priv->usb_interface[iface].sub_api, itransfer);
+ if (r == LIBUSB_SUCCESS)
+ return r;
+ }
+ }
+
+ // Either not targeted to a specific interface or no luck in doing so.
+ // Try a 2 pass approach with all interfaces.
+ for (pass = 0; pass < 2; pass++) {
+ for (iface = 0; iface < USB_MAXINTERFACES; iface++) {
+ if (priv->usb_interface[iface].path != NULL) {
+ if ((pass == 0) && (priv->usb_interface[iface].restricted_functionality)) {
+ usbi_dbg("trying to skip restricted interface #%d (HID keyboard or mouse?)", iface);
+ continue;
+ }
+ usbi_dbg("using interface %d", iface);
+ r = priv->usb_interface[iface].apib->submit_control_transfer(priv->usb_interface[iface].sub_api, itransfer);
+ // If not supported on this API, it may be supported on another, so don't give up yet!!
+ if (r == LIBUSB_ERROR_NOT_SUPPORTED)
+ continue;
+ return r;
+ }
+ }
+ }
+
+ usbi_err(ctx, "no libusb supported interfaces to complete request");
+ return LIBUSB_ERROR_NOT_FOUND;
+}
+
+static int composite_submit_bulk_transfer(int sub_api, struct usbi_transfer *itransfer) {
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev);
+ struct windows_device_handle_priv *handle_priv = _device_handle_priv(transfer->dev_handle);
+ struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev);
+ int current_interface;
+
+ current_interface = interface_by_endpoint(priv, handle_priv, transfer->endpoint);
+ if (current_interface < 0) {
+ usbi_err(ctx, "unable to match endpoint to an open interface - cancelling transfer");
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
+
+ return priv->usb_interface[current_interface].apib->
+ submit_bulk_transfer(priv->usb_interface[current_interface].sub_api, itransfer);
+}
+
+static int composite_submit_iso_transfer(int sub_api, struct usbi_transfer *itransfer) {
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev);
+ struct windows_device_handle_priv *handle_priv = _device_handle_priv(transfer->dev_handle);
+ struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev);
+ int current_interface;
+
+ current_interface = interface_by_endpoint(priv, handle_priv, transfer->endpoint);
+ if (current_interface < 0) {
+ usbi_err(ctx, "unable to match endpoint to an open interface - cancelling transfer");
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
+
+ return priv->usb_interface[current_interface].apib->
+ submit_iso_transfer(priv->usb_interface[current_interface].sub_api, itransfer);
+}
+
+static int composite_clear_halt(int sub_api, struct libusb_device_handle *dev_handle, unsigned char endpoint)
+{
+ struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev);
+ struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle);
+ struct windows_device_priv *priv = _device_priv(dev_handle->dev);
+ int current_interface;
+
+ current_interface = interface_by_endpoint(priv, handle_priv, endpoint);
+ if (current_interface < 0) {
+ usbi_err(ctx, "unable to match endpoint to an open interface - cannot clear");
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
+
+ return priv->usb_interface[current_interface].apib->
+ clear_halt(priv->usb_interface[current_interface].sub_api, dev_handle, endpoint);
+}
+
+static int composite_abort_control(int sub_api, struct usbi_transfer *itransfer)
+{
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
+ struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev);
+
+ return priv->usb_interface[transfer_priv->interface_number].apib->
+ abort_control(priv->usb_interface[transfer_priv->interface_number].sub_api, itransfer);
+}
+
+static int composite_abort_transfers(int sub_api, struct usbi_transfer *itransfer)
+{
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
+ struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev);
+
+ return priv->usb_interface[transfer_priv->interface_number].apib->
+ abort_transfers(priv->usb_interface[transfer_priv->interface_number].sub_api, itransfer);
+}
+
+static int composite_reset_device(int sub_api, struct libusb_device_handle *dev_handle)
+{
+ struct windows_device_priv *priv = _device_priv(dev_handle->dev);
+ int r;
+ uint8_t i;
+ bool available[SUB_API_MAX];
+
+ for (i = 0; i < SUB_API_MAX; i++)
+ available[i] = false;
+
+ for (i = 0; i < USB_MAXINTERFACES; i++) {
+ if ((priv->usb_interface[i].apib->id == USB_API_WINUSBX)
+ && (priv->usb_interface[i].sub_api != SUB_API_NOTSET))
+ available[priv->usb_interface[i].sub_api] = true;
+ }
+
+ for (i = 0; i < SUB_API_MAX; i++) {
+ if (available[i]) {
+ r = usb_api_backend[USB_API_WINUSBX].reset_device(i, dev_handle);
+ if (r != LIBUSB_SUCCESS)
+ return r;
+ }
+ }
+
+ return LIBUSB_SUCCESS;
+}
+
+static int composite_copy_transfer_data(int sub_api, struct usbi_transfer *itransfer, uint32_t io_size)
+{
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
+ struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev);
+
+ return priv->usb_interface[transfer_priv->interface_number].apib->
+ copy_transfer_data(priv->usb_interface[transfer_priv->interface_number].sub_api, itransfer, io_size);
+}
+
+#endif /* !USE_USBDK */
diff --git a/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/windows_winusb.h b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/windows_winusb.h
new file mode 100644
index 000000000..b7b9cd919
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/windows_winusb.h
@@ -0,0 +1,937 @@
+/*
+ * Windows backend for libusb 1.0
+ * Copyright © 2009-2012 Pete Batard <pete@akeo.ie>
+ * With contributions from Michael Plante, Orin Eman et al.
+ * Parts of this code adapted from libusb-win32-v1 by Stephan Meyer
+ * 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_common.h"
+#include "windows_nt_common.h"
+
+#if defined(_MSC_VER)
+// disable /W4 MSVC warnings that are benign
+#pragma warning(disable:4100) // unreferenced formal parameter
+#pragma warning(disable:4127) // conditional expression is constant
+#pragma warning(disable:4201) // nameless struct/union
+#pragma warning(disable:4214) // bit field types other than int
+#pragma warning(disable:4996) // deprecated API calls
+#pragma warning(disable:28159) // more deprecated API calls
+#endif
+
+// Missing from MSVC6 setupapi.h
+#if !defined(SPDRP_ADDRESS)
+#define SPDRP_ADDRESS 28
+#endif
+#if !defined(SPDRP_INSTALL_STATE)
+#define SPDRP_INSTALL_STATE 34
+#endif
+
+#define MAX_CTRL_BUFFER_LENGTH 4096
+#define MAX_USB_DEVICES 256
+#define MAX_USB_STRING_LENGTH 128
+#define MAX_HID_REPORT_SIZE 1024
+#define MAX_HID_DESCRIPTOR_SIZE 256
+#define MAX_GUID_STRING_LENGTH 40
+#define MAX_PATH_LENGTH 128
+#define MAX_KEY_LENGTH 256
+#define LIST_SEPARATOR ';'
+
+// Handle code for HID interface that have been claimed ("dibs")
+#define INTERFACE_CLAIMED ((HANDLE)(intptr_t)0xD1B5)
+// Additional return code for HID operations that completed synchronously
+#define LIBUSB_COMPLETED (LIBUSB_SUCCESS + 1)
+
+// http://msdn.microsoft.com/en-us/library/ff545978.aspx
+// http://msdn.microsoft.com/en-us/library/ff545972.aspx
+// http://msdn.microsoft.com/en-us/library/ff545982.aspx
+#if !defined(GUID_DEVINTERFACE_USB_HOST_CONTROLLER)
+const GUID GUID_DEVINTERFACE_USB_HOST_CONTROLLER = { 0x3ABF6F2D, 0x71C4, 0x462A, {0x8A, 0x92, 0x1E, 0x68, 0x61, 0xE6, 0xAF, 0x27} };
+#endif
+#if !defined(GUID_DEVINTERFACE_USB_DEVICE)
+const GUID GUID_DEVINTERFACE_USB_DEVICE = { 0xA5DCBF10, 0x6530, 0x11D2, {0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED} };
+#endif
+#if !defined(GUID_DEVINTERFACE_USB_HUB)
+const GUID GUID_DEVINTERFACE_USB_HUB = { 0xF18A0E88, 0xC30C, 0x11D0, {0x88, 0x15, 0x00, 0xA0, 0xC9, 0x06, 0xBE, 0xD8} };
+#endif
+#if !defined(GUID_DEVINTERFACE_LIBUSB0_FILTER)
+const GUID GUID_DEVINTERFACE_LIBUSB0_FILTER = { 0xF9F3FF14, 0xAE21, 0x48A0, {0x8A, 0x25, 0x80, 0x11, 0xA7, 0xA9, 0x31, 0xD9} };
+#endif
+
+
+/*
+ * Multiple USB API backend support
+ */
+#define USB_API_UNSUPPORTED 0
+#define USB_API_HUB 1
+#define USB_API_COMPOSITE 2
+#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
+#define SUB_API_NOTSET -1
+#define SUB_API_LIBUSBK 0
+#define SUB_API_LIBUSB0 1
+#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 uint8_t nb_driver_names;
+ int (*init)(int sub_api, struct libusb_context *ctx);
+ int (*exit)(int sub_api);
+ 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);
+ int (*claim_interface)(int sub_api, struct libusb_device_handle *dev_handle, int iface);
+ int (*set_interface_altsetting)(int sub_api, struct libusb_device_handle *dev_handle, int iface, int altsetting);
+ int (*release_interface)(int sub_api, struct libusb_device_handle *dev_handle, int iface);
+ int (*clear_halt)(int sub_api, struct libusb_device_handle *dev_handle, unsigned char endpoint);
+ int (*reset_device)(int sub_api, struct libusb_device_handle *dev_handle);
+ int (*submit_bulk_transfer)(int sub_api, struct usbi_transfer *itransfer);
+ int (*submit_iso_transfer)(int sub_api, struct usbi_transfer *itransfer);
+ int (*submit_control_transfer)(int sub_api, struct usbi_transfer *itransfer);
+ int (*abort_control)(int sub_api, struct usbi_transfer *itransfer);
+ int (*abort_transfers)(int sub_api, struct usbi_transfer *itransfer);
+ int (*copy_transfer_data)(int sub_api, struct usbi_transfer *itransfer, uint32_t io_size);
+};
+
+extern const struct windows_usb_api_backend usb_api_backend[USB_API_MAX];
+
+#define PRINT_UNSUPPORTED_API(fname) \
+ usbi_dbg("unsupported API call for '" \
+ #fname "' (unrecognized device driver)"); \
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+
+/*
+ * private structures definition
+ * with inline pseudo constructors/destructors
+ */
+
+// TODO (v2+): move hid desc to libusb.h?
+struct libusb_hid_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint16_t bcdHID;
+ uint8_t bCountryCode;
+ uint8_t bNumDescriptors;
+ uint8_t bClassDescriptorType;
+ uint16_t wClassDescriptorLength;
+};
+
+#define LIBUSB_DT_HID_SIZE 9
+#define HID_MAX_CONFIG_DESC_SIZE (LIBUSB_DT_CONFIG_SIZE + LIBUSB_DT_INTERFACE_SIZE \
+ + LIBUSB_DT_HID_SIZE + 2 * LIBUSB_DT_ENDPOINT_SIZE)
+#define HID_MAX_REPORT_SIZE 1024
+#define HID_IN_EP 0x81
+#define HID_OUT_EP 0x02
+#define LIBUSB_REQ_RECIPIENT(request_type) ((request_type) & 0x1F)
+#define LIBUSB_REQ_TYPE(request_type) ((request_type) & (0x03 << 5))
+#define LIBUSB_REQ_IN(request_type) ((request_type) & LIBUSB_ENDPOINT_IN)
+#define LIBUSB_REQ_OUT(request_type) (!LIBUSB_REQ_IN(request_type))
+
+// 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) \
+ CTL_CODE (FILE_DEVICE_KEYBOARD, (id), METHOD_OUT_DIRECT, FILE_ANY_ACCESS)
+
+#define IOCTL_HID_GET_FEATURE HID_OUT_CTL_CODE(100)
+#define IOCTL_HID_GET_INPUT_REPORT HID_OUT_CTL_CODE(104)
+#define IOCTL_HID_SET_FEATURE HID_IN_CTL_CODE(100)
+#define IOCTL_HID_SET_OUTPUT_REPORT HID_IN_CTL_CODE(101)
+
+enum libusb_hid_request_type {
+ HID_REQ_GET_REPORT = 0x01,
+ HID_REQ_GET_IDLE = 0x02,
+ HID_REQ_GET_PROTOCOL = 0x03,
+ HID_REQ_SET_REPORT = 0x09,
+ HID_REQ_SET_IDLE = 0x0A,
+ HID_REQ_SET_PROTOCOL = 0x0B
+};
+
+enum libusb_hid_report_type {
+ HID_REPORT_TYPE_INPUT = 0x01,
+ HID_REPORT_TYPE_OUTPUT = 0x02,
+ HID_REPORT_TYPE_FEATURE = 0x03
+};
+
+struct hid_device_priv {
+ uint16_t vid;
+ uint16_t pid;
+ uint8_t config;
+ uint8_t nb_interfaces;
+ bool uses_report_ids[3]; // input, ouptput, feature
+ uint16_t input_report_size;
+ uint16_t output_report_size;
+ uint16_t feature_report_size;
+ WCHAR string[3][MAX_USB_STRING_LENGTH];
+ uint8_t string_index[3]; // man, prod, ser
+};
+
+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)
+{
+ return (struct windows_device_priv *)dev->os_priv;
+}
+
+static inline struct windows_device_priv *windows_device_priv_init(struct libusb_device *dev)
+{
+ struct windows_device_priv *p = _device_priv(dev);
+ int i;
+
+ p->apib = &usb_api_backend[USB_API_UNSUPPORTED];
+ p->sub_api = SUB_API_NOTSET;
+ for (i = 0; i < USB_MAXINTERFACES; i++) {
+ p->usb_interface[i].apib = &usb_api_backend[USB_API_UNSUPPORTED];
+ p->usb_interface[i].sub_api = SUB_API_NOTSET;
+ }
+
+ return p;
+}
+
+static inline void windows_device_priv_release(struct libusb_device *dev)
+{
+ struct windows_device_priv *p = _device_priv(dev);
+ int i;
+
+ free(p->path);
+ if ((dev->num_configurations > 0) && (p->config_descriptor != NULL)) {
+ for (i = 0; i < dev->num_configurations; i++)
+ free(p->config_descriptor[i]);
+ }
+ free(p->config_descriptor);
+ free(p->hid);
+ for (i = 0; i < USB_MAXINTERFACES; i++) {
+ free(p->usb_interface[i].path);
+ free(p->usb_interface[i].endpoint);
+ }
+}
+
+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(
+ struct libusb_device_handle *handle)
+{
+ return (struct windows_device_handle_priv *)handle->os_priv;
+}
+
+// used for async polling functions
+struct windows_transfer_priv {
+ struct winfd pollable_fd;
+ uint8_t interface_number;
+ uint8_t *hid_buffer; // 1 byte extended data buffer, required for HID
+ uint8_t *hid_dest; // transfer buffer destination, required for HID
+ size_t hid_expected_size;
+};
+
+// 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
+ const DWORD reg_prop; // SPDRP registry key to use to retrieve list
+ 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
+ */
+typedef DWORD DEVNODE, DEVINST;
+typedef DEVNODE *PDEVNODE, *PDEVINST;
+typedef DWORD RETURN_TYPE;
+typedef RETURN_TYPE CONFIGRET;
+
+#define CR_SUCCESS 0x00000000
+#define CR_NO_SUCH_DEVNODE 0x0000000D
+
+#define USB_DEVICE_DESCRIPTOR_TYPE LIBUSB_DT_DEVICE
+#define USB_CONFIGURATION_DESCRIPTOR_TYPE LIBUSB_DT_CONFIG
+#define USB_STRING_DESCRIPTOR_TYPE LIBUSB_DT_STRING
+#define USB_INTERFACE_DESCRIPTOR_TYPE LIBUSB_DT_INTERFACE
+#define USB_ENDPOINT_DESCRIPTOR_TYPE LIBUSB_DT_ENDPOINT
+
+#define USB_REQUEST_GET_STATUS LIBUSB_REQUEST_GET_STATUS
+#define USB_REQUEST_CLEAR_FEATURE LIBUSB_REQUEST_CLEAR_FEATURE
+#define USB_REQUEST_SET_FEATURE LIBUSB_REQUEST_SET_FEATURE
+#define USB_REQUEST_SET_ADDRESS LIBUSB_REQUEST_SET_ADDRESS
+#define USB_REQUEST_GET_DESCRIPTOR LIBUSB_REQUEST_GET_DESCRIPTOR
+#define USB_REQUEST_SET_DESCRIPTOR LIBUSB_REQUEST_SET_DESCRIPTOR
+#define USB_REQUEST_GET_CONFIGURATION LIBUSB_REQUEST_GET_CONFIGURATION
+#define USB_REQUEST_SET_CONFIGURATION LIBUSB_REQUEST_SET_CONFIGURATION
+#define USB_REQUEST_GET_INTERFACE LIBUSB_REQUEST_GET_INTERFACE
+#define USB_REQUEST_SET_INTERFACE LIBUSB_REQUEST_SET_INTERFACE
+#define USB_REQUEST_SYNC_FRAME LIBUSB_REQUEST_SYNCH_FRAME
+
+#define USB_GET_NODE_INFORMATION 258
+#define USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION 260
+#define USB_GET_NODE_CONNECTION_NAME 261
+#define USB_GET_HUB_CAPABILITIES 271
+#if !defined(USB_GET_NODE_CONNECTION_INFORMATION_EX)
+#define USB_GET_NODE_CONNECTION_INFORMATION_EX 274
+#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)
+#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
+
+typedef enum USB_CONNECTION_STATUS {
+ NoDeviceConnected,
+ DeviceConnected,
+ DeviceFailedEnumeration,
+ DeviceGeneralFailure,
+ DeviceCausedOvercurrent,
+ DeviceNotEnoughPower,
+ DeviceNotEnoughBandwidth,
+ DeviceHubNestedTooDeeply,
+ DeviceInLegacyHub
+} USB_CONNECTION_STATUS, *PUSB_CONNECTION_STATUS;
+
+typedef enum USB_HUB_NODE {
+ UsbHub,
+ UsbMIParent
+} USB_HUB_NODE;
+
+/* Cfgmgr32.dll interface */
+DLL_DECLARE_HANDLE(Cfgmgr32);
+DLL_DECLARE_FUNC(WINAPI, CONFIGRET, CM_Get_Parent, (PDEVINST, DEVINST, ULONG));
+DLL_DECLARE_FUNC(WINAPI, CONFIGRET, CM_Get_Child, (PDEVINST, DEVINST, ULONG));
+DLL_DECLARE_FUNC(WINAPI, CONFIGRET, CM_Get_Sibling, (PDEVINST, DEVINST, ULONG));
+DLL_DECLARE_FUNC(WINAPI, CONFIGRET, CM_Get_Device_IDA, (DEVINST, PCHAR, ULONG, ULONG));
+
+#define IOCTL_USB_GET_HUB_CAPABILITIES_EX \
+ CTL_CODE( FILE_DEVICE_USB, USB_GET_HUB_CAPABILITIES_EX, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define IOCTL_USB_GET_HUB_CAPABILITIES \
+ CTL_CODE(FILE_DEVICE_USB, USB_GET_HUB_CAPABILITIES, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION \
+ CTL_CODE(FILE_DEVICE_USB, USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define IOCTL_USB_GET_ROOT_HUB_NAME \
+ CTL_CODE(FILE_DEVICE_USB, HCD_GET_ROOT_HUB_NAME, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define IOCTL_USB_GET_NODE_INFORMATION \
+ CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_INFORMATION, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX \
+ CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_INFORMATION_EX, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX_V2 \
+ CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_INFORMATION_EX_V2, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define IOCTL_USB_GET_NODE_CONNECTION_ATTRIBUTES \
+ CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_ATTRIBUTES, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define IOCTL_USB_GET_NODE_CONNECTION_NAME \
+ CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_NAME, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+// Most of the structures below need to be packed
+#pragma pack(push, 1)
+
+typedef struct USB_INTERFACE_DESCRIPTOR {
+ UCHAR bLength;
+ UCHAR bDescriptorType;
+ UCHAR bInterfaceNumber;
+ UCHAR bAlternateSetting;
+ UCHAR bNumEndpoints;
+ UCHAR bInterfaceClass;
+ UCHAR bInterfaceSubClass;
+ UCHAR bInterfaceProtocol;
+ UCHAR iInterface;
+} USB_INTERFACE_DESCRIPTOR, *PUSB_INTERFACE_DESCRIPTOR;
+
+typedef struct USB_CONFIGURATION_DESCRIPTOR_SHORT {
+ struct {
+ ULONG ConnectionIndex;
+ struct {
+ UCHAR bmRequest;
+ UCHAR bRequest;
+ USHORT wValue;
+ USHORT wIndex;
+ USHORT wLength;
+ } SetupPacket;
+ } req;
+ USB_CONFIGURATION_DESCRIPTOR data;
+} USB_CONFIGURATION_DESCRIPTOR_SHORT;
+
+typedef struct USB_ENDPOINT_DESCRIPTOR {
+ UCHAR bLength;
+ UCHAR bDescriptorType;
+ UCHAR bEndpointAddress;
+ UCHAR bmAttributes;
+ USHORT wMaxPacketSize;
+ UCHAR bInterval;
+} USB_ENDPOINT_DESCRIPTOR, *PUSB_ENDPOINT_DESCRIPTOR;
+
+typedef struct USB_DESCRIPTOR_REQUEST {
+ ULONG ConnectionIndex;
+ struct {
+ UCHAR bmRequest;
+ UCHAR bRequest;
+ USHORT wValue;
+ USHORT wIndex;
+ USHORT wLength;
+ } SetupPacket;
+// UCHAR Data[0];
+} USB_DESCRIPTOR_REQUEST, *PUSB_DESCRIPTOR_REQUEST;
+
+typedef struct USB_HUB_DESCRIPTOR {
+ UCHAR bDescriptorLength;
+ UCHAR bDescriptorType;
+ UCHAR bNumberOfPorts;
+ USHORT wHubCharacteristics;
+ UCHAR bPowerOnToPowerGood;
+ UCHAR bHubControlCurrent;
+ UCHAR bRemoveAndPowerMask[64];
+} USB_HUB_DESCRIPTOR, *PUSB_HUB_DESCRIPTOR;
+
+typedef struct USB_ROOT_HUB_NAME {
+ ULONG ActualLength;
+ WCHAR RootHubName[1];
+} USB_ROOT_HUB_NAME, *PUSB_ROOT_HUB_NAME;
+
+typedef struct USB_ROOT_HUB_NAME_FIXED {
+ ULONG ActualLength;
+ WCHAR RootHubName[MAX_PATH_LENGTH];
+} USB_ROOT_HUB_NAME_FIXED;
+
+typedef struct USB_NODE_CONNECTION_NAME {
+ ULONG ConnectionIndex;
+ ULONG ActualLength;
+ WCHAR NodeName[1];
+} USB_NODE_CONNECTION_NAME, *PUSB_NODE_CONNECTION_NAME;
+
+typedef struct USB_NODE_CONNECTION_NAME_FIXED {
+ ULONG ConnectionIndex;
+ ULONG ActualLength;
+ WCHAR NodeName[MAX_PATH_LENGTH];
+} USB_NODE_CONNECTION_NAME_FIXED;
+
+typedef struct USB_HUB_NAME_FIXED {
+ union {
+ USB_ROOT_HUB_NAME_FIXED root;
+ USB_NODE_CONNECTION_NAME_FIXED node;
+ } u;
+} USB_HUB_NAME_FIXED;
+
+typedef struct USB_HUB_INFORMATION {
+ USB_HUB_DESCRIPTOR HubDescriptor;
+ BOOLEAN HubIsBusPowered;
+} USB_HUB_INFORMATION, *PUSB_HUB_INFORMATION;
+
+typedef struct USB_MI_PARENT_INFORMATION {
+ ULONG NumberOfInterfaces;
+} USB_MI_PARENT_INFORMATION, *PUSB_MI_PARENT_INFORMATION;
+
+typedef struct USB_NODE_INFORMATION {
+ USB_HUB_NODE NodeType;
+ union {
+ USB_HUB_INFORMATION HubInformation;
+ USB_MI_PARENT_INFORMATION MiParentInformation;
+ } u;
+} USB_NODE_INFORMATION, *PUSB_NODE_INFORMATION;
+
+typedef struct USB_PIPE_INFO {
+ USB_ENDPOINT_DESCRIPTOR EndpointDescriptor;
+ ULONG ScheduleOffset;
+} USB_PIPE_INFO, *PUSB_PIPE_INFO;
+
+typedef struct USB_NODE_CONNECTION_INFORMATION_EX {
+ ULONG ConnectionIndex;
+ USB_DEVICE_DESCRIPTOR DeviceDescriptor;
+ UCHAR CurrentConfigurationValue;
+ UCHAR Speed;
+ BOOLEAN DeviceIsHub;
+ USHORT DeviceAddress;
+ ULONG NumberOfOpenPipes;
+ USB_CONNECTION_STATUS ConnectionStatus;
+// USB_PIPE_INFO PipeList[0];
+} USB_NODE_CONNECTION_INFORMATION_EX, *PUSB_NODE_CONNECTION_INFORMATION_EX;
+
+typedef union _USB_PROTOCOLS {
+ ULONG ul;
+ struct {
+ ULONG Usb110:1;
+ ULONG Usb200:1;
+ ULONG Usb300:1;
+ ULONG ReservedMBZ:29;
+ };
+} USB_PROTOCOLS, *PUSB_PROTOCOLS;
+
+typedef union _USB_NODE_CONNECTION_INFORMATION_EX_V2_FLAGS {
+ ULONG ul;
+ struct {
+ ULONG DeviceIsOperatingAtSuperSpeedOrHigher:1;
+ ULONG DeviceIsSuperSpeedCapableOrHigher:1;
+ ULONG ReservedMBZ:30;
+ };
+} USB_NODE_CONNECTION_INFORMATION_EX_V2_FLAGS, *PUSB_NODE_CONNECTION_INFORMATION_EX_V2_FLAGS;
+
+typedef struct _USB_NODE_CONNECTION_INFORMATION_EX_V2 {
+ ULONG ConnectionIndex;
+ ULONG Length;
+ USB_PROTOCOLS SupportedUsbProtocols;
+ 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)
+
+/* winusb.dll interface */
+
+#define SHORT_PACKET_TERMINATE 0x01
+#define AUTO_CLEAR_STALL 0x02
+#define PIPE_TRANSFER_TIMEOUT 0x03
+#define IGNORE_SHORT_PACKETS 0x04
+#define ALLOW_PARTIAL_READS 0x05
+#define AUTO_FLUSH 0x06
+#define RAW_IO 0x07
+#define MAXIMUM_TRANSFER_SIZE 0x08
+#define AUTO_SUSPEND 0x81
+#define SUSPEND_DELAY 0x83
+#define DEVICE_SPEED 0x01
+#define LowSpeed 0x01
+#define FullSpeed 0x02
+#define HighSpeed 0x03
+
+typedef enum USBD_PIPE_TYPE {
+ UsbdPipeTypeControl,
+ UsbdPipeTypeIsochronous,
+ UsbdPipeTypeBulk,
+ UsbdPipeTypeInterrupt
+} USBD_PIPE_TYPE;
+
+typedef struct {
+ USBD_PIPE_TYPE PipeType;
+ UCHAR PipeId;
+ USHORT MaximumPacketSize;
+ UCHAR Interval;
+} WINUSB_PIPE_INFORMATION, *PWINUSB_PIPE_INFORMATION;
+
+#pragma pack(1)
+typedef struct {
+ UCHAR request_type;
+ UCHAR request;
+ USHORT value;
+ USHORT index;
+ USHORT length;
+} WINUSB_SETUP_PACKET, *PWINUSB_SETUP_PACKET;
+#pragma pack()
+
+typedef void *WINUSB_INTERFACE_HANDLE, *PWINUSB_INTERFACE_HANDLE;
+
+typedef BOOL (WINAPI *WinUsb_AbortPipe_t)(
+ WINUSB_INTERFACE_HANDLE InterfaceHandle,
+ UCHAR PipeID
+);
+typedef BOOL (WINAPI *WinUsb_ControlTransfer_t)(
+ WINUSB_INTERFACE_HANDLE InterfaceHandle,
+ WINUSB_SETUP_PACKET SetupPacket,
+ PUCHAR Buffer,
+ ULONG BufferLength,
+ PULONG LengthTransferred,
+ LPOVERLAPPED Overlapped
+);
+typedef BOOL (WINAPI *WinUsb_FlushPipe_t)(
+ WINUSB_INTERFACE_HANDLE InterfaceHandle,
+ UCHAR PipeID
+);
+typedef BOOL (WINAPI *WinUsb_Free_t)(
+ WINUSB_INTERFACE_HANDLE InterfaceHandle
+);
+typedef BOOL (WINAPI *WinUsb_GetAssociatedInterface_t)(
+ WINUSB_INTERFACE_HANDLE InterfaceHandle,
+ 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,
+ PUCHAR Buffer,
+ ULONG BufferLength,
+ PULONG LengthTransferred,
+ LPOVERLAPPED Overlapped
+);
+typedef BOOL (WINAPI *WinUsb_ResetPipe_t)(
+ WINUSB_INTERFACE_HANDLE InterfaceHandle,
+ UCHAR PipeID
+);
+typedef BOOL (WINAPI *WinUsb_SetCurrentAlternateSetting_t)(
+ WINUSB_INTERFACE_HANDLE InterfaceHandle,
+ UCHAR AlternateSetting
+);
+typedef BOOL (WINAPI *WinUsb_SetPipePolicy_t)(
+ WINUSB_INTERFACE_HANDLE InterfaceHandle,
+ UCHAR PipeID,
+ ULONG PolicyType,
+ 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,
+ PUCHAR Buffer,
+ ULONG BufferLength,
+ 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 {
+ KUSB_FNID_Init,
+ KUSB_FNID_Free,
+ KUSB_FNID_ClaimInterface,
+ KUSB_FNID_ReleaseInterface,
+ KUSB_FNID_SetAltInterface,
+ KUSB_FNID_GetAltInterface,
+ KUSB_FNID_GetDescriptor,
+ KUSB_FNID_ControlTransfer,
+ KUSB_FNID_SetPowerPolicy,
+ KUSB_FNID_GetPowerPolicy,
+ KUSB_FNID_SetConfiguration,
+ KUSB_FNID_GetConfiguration,
+ KUSB_FNID_ResetDevice,
+ KUSB_FNID_Initialize,
+ KUSB_FNID_SelectInterface,
+ KUSB_FNID_GetAssociatedInterface,
+ KUSB_FNID_Clone,
+ KUSB_FNID_QueryInterfaceSettings,
+ KUSB_FNID_QueryDeviceInformation,
+ KUSB_FNID_SetCurrentAlternateSetting,
+ KUSB_FNID_GetCurrentAlternateSetting,
+ KUSB_FNID_QueryPipe,
+ KUSB_FNID_SetPipePolicy,
+ KUSB_FNID_GetPipePolicy,
+ KUSB_FNID_ReadPipe,
+ KUSB_FNID_WritePipe,
+ KUSB_FNID_ResetPipe,
+ KUSB_FNID_AbortPipe,
+ KUSB_FNID_FlushPipe,
+ KUSB_FNID_IsoReadPipe,
+ KUSB_FNID_IsoWritePipe,
+ KUSB_FNID_GetCurrentFrameNumber,
+ KUSB_FNID_GetOverlappedResult,
+ KUSB_FNID_GetProperty,
+ KUSB_FNID_COUNT,
+} KUSB_FNID;
+
+typedef struct _KLIB_VERSION {
+ INT Major;
+ INT Minor;
+ INT Micro;
+ INT Nano;
+} KLIB_VERSION;
+typedef KLIB_VERSION* PKLIB_VERSION;
+
+typedef BOOL (WINAPI *LibK_GetProcAddress_t)(
+ PVOID *ProcAddress,
+ ULONG DriverID,
+ ULONG FunctionID
+);
+
+typedef VOID (WINAPI *LibK_GetVersion_t)(
+ PKLIB_VERSION Version
+);
+
+struct winusb_interface {
+ bool initialized;
+ 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_ResetPipe_t ResetPipe;
+ WinUsb_SetCurrentAlternateSetting_t SetCurrentAlternateSetting;
+ WinUsb_SetPipePolicy_t SetPipePolicy;
+ WinUsb_SetPowerPolicy_t SetPowerPolicy;
+ WinUsb_WritePipe_t WritePipe;
+ WinUsb_ResetDevice_t ResetDevice;
+};
+
+/* hid.dll interface */
+
+#define HIDP_STATUS_SUCCESS 0x110000
+typedef void * PHIDP_PREPARSED_DATA;
+
+#pragma pack(1)
+typedef struct {
+ ULONG Size;
+ USHORT VendorID;
+ USHORT ProductID;
+ USHORT VersionNumber;
+} HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES;
+#pragma pack()
+
+typedef USHORT USAGE;
+typedef struct {
+ USAGE Usage;
+ USAGE UsagePage;
+ USHORT InputReportByteLength;
+ USHORT OutputReportByteLength;
+ USHORT FeatureReportByteLength;
+ USHORT Reserved[17];
+ USHORT NumberLinkCollectionNodes;
+ USHORT NumberInputButtonCaps;
+ USHORT NumberInputValueCaps;
+ USHORT NumberInputDataIndices;
+ USHORT NumberOutputButtonCaps;
+ USHORT NumberOutputValueCaps;
+ USHORT NumberOutputDataIndices;
+ USHORT NumberFeatureButtonCaps;
+ USHORT NumberFeatureValueCaps;
+ USHORT NumberFeatureDataIndices;
+} HIDP_CAPS, *PHIDP_CAPS;
+
+typedef enum _HIDP_REPORT_TYPE {
+ HidP_Input,
+ HidP_Output,
+ HidP_Feature
+} HIDP_REPORT_TYPE;
+
+typedef struct _HIDP_VALUE_CAPS {
+ USAGE UsagePage;
+ UCHAR ReportID;
+ BOOLEAN IsAlias;
+ USHORT BitField;
+ USHORT LinkCollection;
+ USAGE LinkUsage;
+ USAGE LinkUsagePage;
+ BOOLEAN IsRange;
+ BOOLEAN IsStringRange;
+ BOOLEAN IsDesignatorRange;
+ BOOLEAN IsAbsolute;
+ BOOLEAN HasNull;
+ UCHAR Reserved;
+ USHORT BitSize;
+ USHORT ReportCount;
+ USHORT Reserved2[5];
+ ULONG UnitsExp;
+ ULONG Units;
+ LONG LogicalMin, LogicalMax;
+ LONG PhysicalMin, PhysicalMax;
+ union {
+ struct {
+ USAGE UsageMin, UsageMax;
+ USHORT StringMin, StringMax;
+ USHORT DesignatorMin, DesignatorMax;
+ USHORT DataIndexMin, DataIndexMax;
+ } Range;
+ struct {
+ USAGE Usage, Reserved1;
+ USHORT StringIndex, Reserved2;
+ USHORT DesignatorIndex, Reserved3;
+ USHORT DataIndex, Reserved4;
+ } NotRange;
+ } u;
+} HIDP_VALUE_CAPS, *PHIDP_VALUE_CAPS;
+
+DLL_DECLARE_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)); \ No newline at end of file
diff --git a/vendor/github.com/karalabe/gousb/internal/libusb/libusb/strerror.c b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/strerror.c
new file mode 100644
index 000000000..d2be0e2a0
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/strerror.c
@@ -0,0 +1,202 @@
+/*
+ * libusb strerror code
+ * Copyright © 2013 Hans de Goede <hdegoede@redhat.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 <locale.h>
+#include <stdlib.h>
+#include <string.h>
+#if defined(HAVE_STRINGS_H)
+#include <strings.h>
+#endif
+
+#include "libusbi.h"
+
+#if defined(_MSC_VER)
+#define strncasecmp _strnicmp
+#endif
+
+static size_t usbi_locale = 0;
+
+/** \ingroup libusb_misc
+ * How to add a new \ref libusb_strerror() translation:
+ * <ol>
+ * <li> Download the latest \c strerror.c from:<br>
+ * https://raw.github.com/libusb/libusb/master/libusb/sterror.c </li>
+ * <li> Open the file in an UTF-8 capable editor </li>
+ * <li> Add the 2 letter <a href="http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes">ISO 639-1</a>
+ * code for your locale at the end of \c usbi_locale_supported[]<br>
+ * Eg. for Chinese, you would add "zh" so that:
+ * \code... usbi_locale_supported[] = { "en", "nl", "fr" };\endcode
+ * becomes:
+ * \code... usbi_locale_supported[] = { "en", "nl", "fr", "zh" };\endcode </li>
+ * <li> Copy the <tt>{ / * English (en) * / ... }</tt> section and add it at the end of \c usbi_localized_errors<br>
+ * Eg. for Chinese, the last section of \c usbi_localized_errors could look like:
+ * \code
+ * }, { / * Chinese (zh) * /
+ * "Success",
+ * ...
+ * "Other error",
+ * }
+ * };\endcode </li>
+ * <li> Translate each of the English messages from the section you copied into your language </li>
+ * <li> Save the file (in UTF-8 format) and send it to \c libusb-devel\@lists.sourceforge.net </li>
+ * </ol>
+ */
+
+static const char* usbi_locale_supported[] = { "en", "nl", "fr", "ru" };
+static const char* usbi_localized_errors[ARRAYSIZE(usbi_locale_supported)][LIBUSB_ERROR_COUNT] = {
+ { /* English (en) */
+ "Success",
+ "Input/Output Error",
+ "Invalid parameter",
+ "Access denied (insufficient permissions)",
+ "No such device (it may have been disconnected)",
+ "Entity not found",
+ "Resource busy",
+ "Operation timed out",
+ "Overflow",
+ "Pipe error",
+ "System call interrupted (perhaps due to signal)",
+ "Insufficient memory",
+ "Operation not supported or unimplemented on this platform",
+ "Other error",
+ }, { /* Dutch (nl) */
+ "Gelukt",
+ "Invoer-/uitvoerfout",
+ "Ongeldig argument",
+ "Toegang geweigerd (onvoldoende toegangsrechten)",
+ "Apparaat bestaat niet (verbinding met apparaat verbroken?)",
+ "Niet gevonden",
+ "Apparaat of hulpbron is bezig",
+ "Bewerking verlopen",
+ "Waarde is te groot",
+ "Gebroken pijp",
+ "Onderbroken systeemaanroep",
+ "Onvoldoende geheugen beschikbaar",
+ "Bewerking wordt niet ondersteund",
+ "Andere fout",
+ }, { /* French (fr) */
+ "Succès",
+ "Erreur d'entrée/sortie",
+ "Paramètre invalide",
+ "Accès refusé (permissions insuffisantes)",
+ "Périphérique introuvable (peut-être déconnecté)",
+ "Elément introuvable",
+ "Resource déjà occupée",
+ "Operation expirée",
+ "Débordement",
+ "Erreur de pipe",
+ "Appel système abandonné (peut-être à cause d’un signal)",
+ "Mémoire insuffisante",
+ "Opération non supportée or non implémentée sur cette plateforme",
+ "Autre erreur",
+ }, { /* Russian (ru) */
+ "Успех",
+ "Ошибка ввода/вывода",
+ "Неверный параметр",
+ "Доступ запрещён (не хватает прав)",
+ "Устройство отсутствует (возможно, оно было отсоединено)",
+ "Элемент не найден",
+ "Ресурс занят",
+ "Истекло время ожидания операции",
+ "Переполнение",
+ "Ошибка канала",
+ "Системный вызов прерван (возможно, сигналом)",
+ "Память исчерпана",
+ "Операция не поддерживается данной платформой",
+ "Неизвестная ошибка"
+ }
+};
+
+/** \ingroup libusb_misc
+ * Set the language, and only the language, not the encoding! used for
+ * translatable libusb messages.
+ *
+ * This takes a locale string in the default setlocale format: lang[-region]
+ * or lang[_country_region][.codeset]. Only the lang part of the string is
+ * used, and only 2 letter ISO 639-1 codes are accepted for it, such as "de".
+ * The optional region, country_region or codeset parts are ignored. This
+ * means that functions which return translatable strings will NOT honor the
+ * specified encoding.
+ * All strings returned are encoded as UTF-8 strings.
+ *
+ * If libusb_setlocale() is not called, all messages will be in English.
+ *
+ * The following functions return translatable strings: libusb_strerror().
+ * Note that the libusb log messages controlled through libusb_set_debug()
+ * are not translated, they are always in English.
+ *
+ * For POSIX UTF-8 environments if you want libusb to follow the standard
+ * locale settings, call libusb_setlocale(setlocale(LC_MESSAGES, NULL)),
+ * after your app has done its locale setup.
+ *
+ * \param locale locale-string in the form of lang[_country_region][.codeset]
+ * or lang[-region], where lang is a 2 letter ISO 639-1 code
+ * \returns LIBUSB_SUCCESS on success
+ * \returns LIBUSB_ERROR_INVALID_PARAM if the locale doesn't meet the requirements
+ * \returns LIBUSB_ERROR_NOT_FOUND if the requested language is not supported
+ * \returns a LIBUSB_ERROR code on other errors
+ */
+
+int API_EXPORTED libusb_setlocale(const char *locale)
+{
+ size_t i;
+
+ if ( (locale == NULL) || (strlen(locale) < 2)
+ || ((strlen(locale) > 2) && (locale[2] != '-') && (locale[2] != '_') && (locale[2] != '.')) )
+ return LIBUSB_ERROR_INVALID_PARAM;
+
+ for (i=0; i<ARRAYSIZE(usbi_locale_supported); i++) {
+ if (strncasecmp(usbi_locale_supported[i], locale, 2) == 0)
+ break;
+ }
+ if (i >= ARRAYSIZE(usbi_locale_supported)) {
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
+
+ usbi_locale = i;
+
+ return LIBUSB_SUCCESS;
+}
+
+/** \ingroup libusb_misc
+ * Returns a constant string with a short description of the given error code,
+ * this description is intended for displaying to the end user and will be in
+ * the language set by libusb_setlocale().
+ *
+ * The returned string is encoded in UTF-8.
+ *
+ * The messages always start with a capital letter and end without any dot.
+ * The caller must not free() the returned string.
+ *
+ * \param errcode the error code whose description is desired
+ * \returns a short description of the error code in UTF-8 encoding
+ */
+DEFAULT_VISIBILITY const char* LIBUSB_CALL libusb_strerror(enum libusb_error errcode)
+{
+ int errcode_index = -errcode;
+
+ if ((errcode_index < 0) || (errcode_index >= LIBUSB_ERROR_COUNT)) {
+ /* "Other Error", which should always be our last message, is returned */
+ errcode_index = LIBUSB_ERROR_COUNT - 1;
+ }
+
+ return usbi_localized_errors[usbi_locale][errcode_index];
+}
diff --git a/vendor/github.com/karalabe/gousb/internal/libusb/libusb/sync.c b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/sync.c
new file mode 100644
index 000000000..a609f65f4
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/sync.c
@@ -0,0 +1,327 @@
+/*
+ * Synchronous I/O functions for libusb
+ * Copyright © 2007-2008 Daniel Drake <dsd@gentoo.org>
+ *
+ * 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 <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libusbi.h"
+
+/**
+ * @defgroup libusb_syncio Synchronous device I/O
+ *
+ * This page documents libusb's synchronous (blocking) API for USB device I/O.
+ * This interface is easy to use but has some limitations. More advanced users
+ * may wish to consider using the \ref libusb_asyncio "asynchronous I/O API" instead.
+ */
+
+static void LIBUSB_CALL sync_transfer_cb(struct libusb_transfer *transfer)
+{
+ int *completed = transfer->user_data;
+ *completed = 1;
+ usbi_dbg("actual_length=%d", transfer->actual_length);
+ /* caller interprets result and frees transfer */
+}
+
+static void sync_transfer_wait_for_completion(struct libusb_transfer *transfer)
+{
+ int r, *completed = transfer->user_data;
+ struct libusb_context *ctx = HANDLE_CTX(transfer->dev_handle);
+
+ while (!*completed) {
+ r = libusb_handle_events_completed(ctx, completed);
+ if (r < 0) {
+ if (r == LIBUSB_ERROR_INTERRUPTED)
+ continue;
+ usbi_err(ctx, "libusb_handle_events failed: %s, cancelling transfer and retrying",
+ libusb_error_name(r));
+ libusb_cancel_transfer(transfer);
+ continue;
+ }
+ }
+}
+
+/** \ingroup libusb_syncio
+ * Perform a USB control transfer.
+ *
+ * The direction of the transfer is inferred from the bmRequestType field of
+ * the setup packet.
+ *
+ * The wValue, wIndex and wLength fields values should be given in host-endian
+ * byte order.
+ *
+ * \param dev_handle a handle for the device to communicate with
+ * \param bmRequestType the request type field for the setup packet
+ * \param bRequest the request field for the setup packet
+ * \param wValue the value field for the setup packet
+ * \param wIndex the index field for the setup packet
+ * \param data a suitably-sized data buffer for either input or output
+ * (depending on direction bits within bmRequestType)
+ * \param wLength the length field for the setup packet. The data buffer should
+ * be at least this size.
+ * \param timeout timeout (in millseconds) that this function should wait
+ * before giving up due to no response being received. For an unlimited
+ * timeout, use value 0.
+ * \returns on success, the number of bytes actually transferred
+ * \returns LIBUSB_ERROR_TIMEOUT if the transfer timed out
+ * \returns LIBUSB_ERROR_PIPE if the control request was not supported by the
+ * device
+ * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * \returns LIBUSB_ERROR_BUSY if called from event handling context
+ * \returns LIBUSB_ERROR_INVALID_PARAM if the transfer size is larger than
+ * the operating system and/or hardware can support
+ * \returns another LIBUSB_ERROR code on other failures
+ */
+int API_EXPORTED libusb_control_transfer(libusb_device_handle *dev_handle,
+ uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex,
+ unsigned char *data, uint16_t wLength, unsigned int timeout)
+{
+ struct libusb_transfer *transfer;
+ unsigned char *buffer;
+ int completed = 0;
+ int r;
+
+ if (usbi_handling_events(HANDLE_CTX(dev_handle)))
+ return LIBUSB_ERROR_BUSY;
+
+ transfer = libusb_alloc_transfer(0);
+ if (!transfer)
+ return LIBUSB_ERROR_NO_MEM;
+
+ buffer = (unsigned char*) malloc(LIBUSB_CONTROL_SETUP_SIZE + wLength);
+ if (!buffer) {
+ libusb_free_transfer(transfer);
+ return LIBUSB_ERROR_NO_MEM;
+ }
+
+ libusb_fill_control_setup(buffer, bmRequestType, bRequest, wValue, wIndex,
+ wLength);
+ if ((bmRequestType & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_OUT)
+ memcpy(buffer + LIBUSB_CONTROL_SETUP_SIZE, data, wLength);
+
+ libusb_fill_control_transfer(transfer, dev_handle, buffer,
+ sync_transfer_cb, &completed, timeout);
+ transfer->flags = LIBUSB_TRANSFER_FREE_BUFFER;
+ r = libusb_submit_transfer(transfer);
+ if (r < 0) {
+ libusb_free_transfer(transfer);
+ return r;
+ }
+
+ sync_transfer_wait_for_completion(transfer);
+
+ if ((bmRequestType & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_IN)
+ memcpy(data, libusb_control_transfer_get_data(transfer),
+ transfer->actual_length);
+
+ switch (transfer->status) {
+ case LIBUSB_TRANSFER_COMPLETED:
+ r = transfer->actual_length;
+ break;
+ case LIBUSB_TRANSFER_TIMED_OUT:
+ r = LIBUSB_ERROR_TIMEOUT;
+ break;
+ case LIBUSB_TRANSFER_STALL:
+ r = LIBUSB_ERROR_PIPE;
+ break;
+ case LIBUSB_TRANSFER_NO_DEVICE:
+ r = LIBUSB_ERROR_NO_DEVICE;
+ break;
+ case LIBUSB_TRANSFER_OVERFLOW:
+ r = LIBUSB_ERROR_OVERFLOW;
+ break;
+ case LIBUSB_TRANSFER_ERROR:
+ case LIBUSB_TRANSFER_CANCELLED:
+ r = LIBUSB_ERROR_IO;
+ break;
+ default:
+ usbi_warn(HANDLE_CTX(dev_handle),
+ "unrecognised status code %d", transfer->status);
+ r = LIBUSB_ERROR_OTHER;
+ }
+
+ libusb_free_transfer(transfer);
+ return r;
+}
+
+static int do_sync_bulk_transfer(struct libusb_device_handle *dev_handle,
+ unsigned char endpoint, unsigned char *buffer, int length,
+ int *transferred, unsigned int timeout, unsigned char type)
+{
+ struct libusb_transfer *transfer;
+ int completed = 0;
+ int r;
+
+ if (usbi_handling_events(HANDLE_CTX(dev_handle)))
+ return LIBUSB_ERROR_BUSY;
+
+ transfer = libusb_alloc_transfer(0);
+ if (!transfer)
+ return LIBUSB_ERROR_NO_MEM;
+
+ libusb_fill_bulk_transfer(transfer, dev_handle, endpoint, buffer, length,
+ sync_transfer_cb, &completed, timeout);
+ transfer->type = type;
+
+ r = libusb_submit_transfer(transfer);
+ if (r < 0) {
+ libusb_free_transfer(transfer);
+ return r;
+ }
+
+ sync_transfer_wait_for_completion(transfer);
+
+ if (transferred)
+ *transferred = transfer->actual_length;
+
+ switch (transfer->status) {
+ case LIBUSB_TRANSFER_COMPLETED:
+ r = 0;
+ break;
+ case LIBUSB_TRANSFER_TIMED_OUT:
+ r = LIBUSB_ERROR_TIMEOUT;
+ break;
+ case LIBUSB_TRANSFER_STALL:
+ r = LIBUSB_ERROR_PIPE;
+ break;
+ case LIBUSB_TRANSFER_OVERFLOW:
+ r = LIBUSB_ERROR_OVERFLOW;
+ break;
+ case LIBUSB_TRANSFER_NO_DEVICE:
+ r = LIBUSB_ERROR_NO_DEVICE;
+ break;
+ case LIBUSB_TRANSFER_ERROR:
+ case LIBUSB_TRANSFER_CANCELLED:
+ r = LIBUSB_ERROR_IO;
+ break;
+ default:
+ usbi_warn(HANDLE_CTX(dev_handle),
+ "unrecognised status code %d", transfer->status);
+ r = LIBUSB_ERROR_OTHER;
+ }
+
+ libusb_free_transfer(transfer);
+ return r;
+}
+
+/** \ingroup libusb_syncio
+ * Perform a USB bulk transfer. The direction of the transfer is inferred from
+ * the direction bits of the endpoint address.
+ *
+ * For bulk reads, the <tt>length</tt> field indicates the maximum length of
+ * data you are expecting to receive. If less data arrives than expected,
+ * this function will return that data, so be sure to check the
+ * <tt>transferred</tt> output parameter.
+ *
+ * You should also check the <tt>transferred</tt> parameter for bulk writes.
+ * Not all of the data may have been written.
+ *
+ * Also check <tt>transferred</tt> when dealing with a timeout error code.
+ * libusb may have to split your transfer into a number of chunks to satisfy
+ * underlying O/S requirements, meaning that the timeout may expire after
+ * the first few chunks have completed. libusb is careful not to lose any data
+ * that may have been transferred; do not assume that timeout conditions
+ * indicate a complete lack of I/O.
+ *
+ * \param dev_handle a handle for the device to communicate with
+ * \param endpoint the address of a valid endpoint to communicate with
+ * \param data a suitably-sized data buffer for either input or output
+ * (depending on endpoint)
+ * \param length for bulk writes, the number of bytes from data to be sent. for
+ * bulk reads, the maximum number of bytes to receive into the data buffer.
+ * \param transferred output location for the number of bytes actually
+ * transferred. Since version 1.0.21 (\ref LIBUSB_API_VERSION >= 0x01000105),
+ * it is legal to pass a NULL pointer if you do not wish to receive this
+ * information.
+ * \param timeout timeout (in millseconds) that this function should wait
+ * before giving up due to no response being received. For an unlimited
+ * timeout, use value 0.
+ *
+ * \returns 0 on success (and populates <tt>transferred</tt>)
+ * \returns LIBUSB_ERROR_TIMEOUT if the transfer timed out (and populates
+ * <tt>transferred</tt>)
+ * \returns LIBUSB_ERROR_PIPE if the endpoint halted
+ * \returns LIBUSB_ERROR_OVERFLOW if the device offered more data, see
+ * \ref libusb_packetoverflow
+ * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * \returns LIBUSB_ERROR_BUSY if called from event handling context
+ * \returns another LIBUSB_ERROR code on other failures
+ */
+int API_EXPORTED libusb_bulk_transfer(struct libusb_device_handle *dev_handle,
+ unsigned char endpoint, unsigned char *data, int length, int *transferred,
+ unsigned int timeout)
+{
+ return do_sync_bulk_transfer(dev_handle, endpoint, data, length,
+ transferred, timeout, LIBUSB_TRANSFER_TYPE_BULK);
+}
+
+/** \ingroup libusb_syncio
+ * Perform a USB interrupt transfer. The direction of the transfer is inferred
+ * from the direction bits of the endpoint address.
+ *
+ * For interrupt reads, the <tt>length</tt> field indicates the maximum length
+ * of data you are expecting to receive. If less data arrives than expected,
+ * this function will return that data, so be sure to check the
+ * <tt>transferred</tt> output parameter.
+ *
+ * You should also check the <tt>transferred</tt> parameter for interrupt
+ * writes. Not all of the data may have been written.
+ *
+ * Also check <tt>transferred</tt> when dealing with a timeout error code.
+ * libusb may have to split your transfer into a number of chunks to satisfy
+ * underlying O/S requirements, meaning that the timeout may expire after
+ * the first few chunks have completed. libusb is careful not to lose any data
+ * that may have been transferred; do not assume that timeout conditions
+ * indicate a complete lack of I/O.
+ *
+ * The default endpoint bInterval value is used as the polling interval.
+ *
+ * \param dev_handle a handle for the device to communicate with
+ * \param endpoint the address of a valid endpoint to communicate with
+ * \param data a suitably-sized data buffer for either input or output
+ * (depending on endpoint)
+ * \param length for bulk writes, the number of bytes from data to be sent. for
+ * bulk reads, the maximum number of bytes to receive into the data buffer.
+ * \param transferred output location for the number of bytes actually
+ * transferred. Since version 1.0.21 (\ref LIBUSB_API_VERSION >= 0x01000105),
+ * it is legal to pass a NULL pointer if you do not wish to receive this
+ * information.
+ * \param timeout timeout (in millseconds) that this function should wait
+ * before giving up due to no response being received. For an unlimited
+ * timeout, use value 0.
+ *
+ * \returns 0 on success (and populates <tt>transferred</tt>)
+ * \returns LIBUSB_ERROR_TIMEOUT if the transfer timed out
+ * \returns LIBUSB_ERROR_PIPE if the endpoint halted
+ * \returns LIBUSB_ERROR_OVERFLOW if the device offered more data, see
+ * \ref libusb_packetoverflow
+ * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * \returns LIBUSB_ERROR_BUSY if called from event handling context
+ * \returns another LIBUSB_ERROR code on other error
+ */
+int API_EXPORTED libusb_interrupt_transfer(
+ struct libusb_device_handle *dev_handle, unsigned char endpoint,
+ unsigned char *data, int length, int *transferred, unsigned int timeout)
+{
+ return do_sync_bulk_transfer(dev_handle, endpoint, data, length,
+ transferred, timeout, LIBUSB_TRANSFER_TYPE_INTERRUPT);
+}
diff --git a/vendor/github.com/karalabe/gousb/internal/libusb/libusb/version.h b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/version.h
new file mode 100644
index 000000000..6ce48a7d0
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/version.h
@@ -0,0 +1,18 @@
+/* This file is parsed by m4 and windres and RC.EXE so please keep it simple. */
+#include "version_nano.h"
+#ifndef LIBUSB_MAJOR
+#define LIBUSB_MAJOR 1
+#endif
+#ifndef LIBUSB_MINOR
+#define LIBUSB_MINOR 0
+#endif
+#ifndef LIBUSB_MICRO
+#define LIBUSB_MICRO 21
+#endif
+#ifndef LIBUSB_NANO
+#define LIBUSB_NANO 0
+#endif
+/* LIBUSB_RC is the release candidate suffix. Should normally be empty. */
+#ifndef LIBUSB_RC
+#define LIBUSB_RC ""
+#endif
diff --git a/vendor/github.com/karalabe/gousb/internal/libusb/libusb/version_nano.h b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/version_nano.h
new file mode 100644
index 000000000..0a5d1c992
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/internal/libusb/libusb/version_nano.h
@@ -0,0 +1 @@
+#define LIBUSB_NANO 11182
diff --git a/vendor/github.com/karalabe/gousb/usb/config.go b/vendor/github.com/karalabe/gousb/usb/config.go
new file mode 100644
index 000000000..840bce63c
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/usb/config.go
@@ -0,0 +1,153 @@
+// Copyright 2013 Google Inc. 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
+
+/*
+#ifndef OS_WINDOWS
+ #include "os/threads_posix.h"
+#endif
+#include "libusbi.h"
+#include "libusb.h"
+*/
+import "C"
+
+import (
+ "fmt"
+ "reflect"
+ "unsafe"
+)
+
+type EndpointInfo struct {
+ Address uint8
+ Attributes uint8
+ MaxPacketSize uint16
+ MaxIsoPacket uint32
+ PollInterval uint8
+ RefreshRate uint8
+ SynchAddress uint8
+}
+
+func (e EndpointInfo) Number() int {
+ return int(e.Address) & ENDPOINT_NUM_MASK
+}
+
+func (e EndpointInfo) Direction() EndpointDirection {
+ return EndpointDirection(e.Address) & ENDPOINT_DIR_MASK
+}
+
+func (e EndpointInfo) String() string {
+ return fmt.Sprintf("Endpoint %d %-3s %s - %s %s [%d %d]",
+ e.Number(), e.Direction(),
+ TransferType(e.Attributes)&TRANSFER_TYPE_MASK,
+ IsoSyncType(e.Attributes)&ISO_SYNC_TYPE_MASK,
+ IsoUsageType(e.Attributes)&ISO_USAGE_TYPE_MASK,
+ e.MaxPacketSize, e.MaxIsoPacket,
+ )
+}
+
+type InterfaceInfo struct {
+ Number uint8
+ Setups []InterfaceSetup
+}
+
+func (i InterfaceInfo) String() string {
+ return fmt.Sprintf("Interface %02x (%d setups)", i.Number, len(i.Setups))
+}
+
+type InterfaceSetup struct {
+ Number uint8
+ Alternate uint8
+ IfClass uint8
+ IfSubClass uint8
+ IfProtocol uint8
+ Endpoints []EndpointInfo
+}
+
+func (a InterfaceSetup) String() string {
+ return fmt.Sprintf("Interface %02x Setup %02x", a.Number, a.Alternate)
+}
+
+type ConfigInfo struct {
+ Config uint8
+ Attributes uint8
+ MaxPower uint8
+ Interfaces []InterfaceInfo
+}
+
+func (c ConfigInfo) String() string {
+ return fmt.Sprintf("Config %02x", c.Config)
+}
+
+func newConfig(dev *C.libusb_device, cfg *C.struct_libusb_config_descriptor) ConfigInfo {
+ c := ConfigInfo{
+ Config: uint8(cfg.bConfigurationValue),
+ Attributes: uint8(cfg.bmAttributes),
+ MaxPower: uint8(cfg.MaxPower),
+ }
+
+ 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),
+ }
+ c.Interfaces = make([]InterfaceInfo, 0, len(ifaces))
+ for _, 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),
+ }
+ descs := make([]InterfaceSetup, 0, len(alts))
+ for _, alt := range alts {
+ i := InterfaceSetup{
+ Number: uint8(alt.bInterfaceNumber),
+ Alternate: uint8(alt.bAlternateSetting),
+ IfClass: uint8(alt.bInterfaceClass),
+ IfSubClass: uint8(alt.bInterfaceSubClass),
+ IfProtocol: uint8(alt.bInterfaceProtocol),
+ }
+ 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),
+ }
+ i.Endpoints = make([]EndpointInfo, 0, len(ends))
+ for _, end := range ends {
+ i.Endpoints = append(i.Endpoints, EndpointInfo{
+ Address: uint8(end.bEndpointAddress),
+ Attributes: uint8(end.bmAttributes),
+ MaxPacketSize: uint16(end.wMaxPacketSize),
+ //MaxIsoPacket: uint32(C.libusb_get_max_iso_packet_size(dev, C.uchar(end.bEndpointAddress))),
+ PollInterval: uint8(end.bInterval),
+ RefreshRate: uint8(end.bRefresh),
+ SynchAddress: uint8(end.bSynchAddress),
+ })
+ }
+ descs = append(descs, i)
+ }
+ c.Interfaces = append(c.Interfaces, InterfaceInfo{
+ Number: descs[0].Number,
+ Setups: descs,
+ })
+ }
+ return c
+}
diff --git a/vendor/github.com/karalabe/gousb/usb/constants.go b/vendor/github.com/karalabe/gousb/usb/constants.go
new file mode 100644
index 000000000..7f0287d5c
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/usb/constants.go
@@ -0,0 +1,183 @@
+// Copyright 2013 Google Inc. 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
+
+// #include "libusb.h"
+import "C"
+
+type Class uint8
+
+const (
+ CLASS_PER_INTERFACE Class = C.LIBUSB_CLASS_PER_INTERFACE
+ CLASS_AUDIO Class = C.LIBUSB_CLASS_AUDIO
+ CLASS_COMM Class = C.LIBUSB_CLASS_COMM
+ CLASS_HID Class = C.LIBUSB_CLASS_HID
+ CLASS_PRINTER Class = C.LIBUSB_CLASS_PRINTER
+ CLASS_PTP Class = C.LIBUSB_CLASS_PTP
+ CLASS_MASS_STORAGE Class = C.LIBUSB_CLASS_MASS_STORAGE
+ CLASS_HUB Class = C.LIBUSB_CLASS_HUB
+ CLASS_DATA Class = C.LIBUSB_CLASS_DATA
+ CLASS_WIRELESS Class = C.LIBUSB_CLASS_WIRELESS
+ CLASS_APPLICATION Class = C.LIBUSB_CLASS_APPLICATION
+ CLASS_VENDOR_SPEC Class = C.LIBUSB_CLASS_VENDOR_SPEC
+)
+
+var classDescription = map[Class]string{
+ CLASS_PER_INTERFACE: "per-interface",
+ CLASS_AUDIO: "audio",
+ CLASS_COMM: "communications",
+ CLASS_HID: "human interface device",
+ CLASS_PRINTER: "printer dclass",
+ CLASS_PTP: "picture transfer protocol",
+ CLASS_MASS_STORAGE: "mass storage",
+ CLASS_HUB: "hub",
+ CLASS_DATA: "data",
+ CLASS_WIRELESS: "wireless",
+ CLASS_APPLICATION: "application",
+ CLASS_VENDOR_SPEC: "vendor-specific",
+}
+
+func (c Class) String() string {
+ return classDescription[c]
+}
+
+type DescriptorType uint8
+
+const (
+ DT_DEVICE DescriptorType = C.LIBUSB_DT_DEVICE
+ DT_CONFIG DescriptorType = C.LIBUSB_DT_CONFIG
+ DT_STRING DescriptorType = C.LIBUSB_DT_STRING
+ DT_INTERFACE DescriptorType = C.LIBUSB_DT_INTERFACE
+ DT_ENDPOINT DescriptorType = C.LIBUSB_DT_ENDPOINT
+ DT_HID DescriptorType = C.LIBUSB_DT_HID
+ DT_REPORT DescriptorType = C.LIBUSB_DT_REPORT
+ DT_PHYSICAL DescriptorType = C.LIBUSB_DT_PHYSICAL
+ DT_HUB DescriptorType = C.LIBUSB_DT_HUB
+)
+
+var descriptorTypeDescription = map[DescriptorType]string{
+ DT_DEVICE: "device",
+ DT_CONFIG: "configuration",
+ DT_STRING: "string",
+ DT_INTERFACE: "interface",
+ DT_ENDPOINT: "endpoint",
+ DT_HID: "HID",
+ DT_REPORT: "HID report",
+ DT_PHYSICAL: "physical",
+ DT_HUB: "hub",
+}
+
+func (dt DescriptorType) String() string {
+ return descriptorTypeDescription[dt]
+}
+
+type EndpointDirection uint8
+
+const (
+ ENDPOINT_NUM_MASK = 0x03
+ ENDPOINT_DIR_IN EndpointDirection = C.LIBUSB_ENDPOINT_IN
+ ENDPOINT_DIR_OUT EndpointDirection = C.LIBUSB_ENDPOINT_OUT
+ ENDPOINT_DIR_MASK EndpointDirection = 0x80
+)
+
+var endpointDirectionDescription = map[EndpointDirection]string{
+ ENDPOINT_DIR_IN: "IN",
+ ENDPOINT_DIR_OUT: "OUT",
+}
+
+func (ed EndpointDirection) String() string {
+ return endpointDirectionDescription[ed]
+}
+
+type TransferType uint8
+
+const (
+ TRANSFER_TYPE_CONTROL TransferType = C.LIBUSB_TRANSFER_TYPE_CONTROL
+ TRANSFER_TYPE_ISOCHRONOUS TransferType = C.LIBUSB_TRANSFER_TYPE_ISOCHRONOUS
+ TRANSFER_TYPE_BULK TransferType = C.LIBUSB_TRANSFER_TYPE_BULK
+ TRANSFER_TYPE_INTERRUPT TransferType = C.LIBUSB_TRANSFER_TYPE_INTERRUPT
+ TRANSFER_TYPE_MASK TransferType = 0x03
+)
+
+var transferTypeDescription = map[TransferType]string{
+ TRANSFER_TYPE_CONTROL: "control",
+ TRANSFER_TYPE_ISOCHRONOUS: "isochronous",
+ TRANSFER_TYPE_BULK: "bulk",
+ TRANSFER_TYPE_INTERRUPT: "interrupt",
+}
+
+func (tt TransferType) String() string {
+ return transferTypeDescription[tt]
+}
+
+type IsoSyncType uint8
+
+const (
+ ISO_SYNC_TYPE_NONE IsoSyncType = C.LIBUSB_ISO_SYNC_TYPE_NONE << 2
+ ISO_SYNC_TYPE_ASYNC IsoSyncType = C.LIBUSB_ISO_SYNC_TYPE_ASYNC << 2
+ ISO_SYNC_TYPE_ADAPTIVE IsoSyncType = C.LIBUSB_ISO_SYNC_TYPE_ADAPTIVE << 2
+ ISO_SYNC_TYPE_SYNC IsoSyncType = C.LIBUSB_ISO_SYNC_TYPE_SYNC << 2
+ ISO_SYNC_TYPE_MASK IsoSyncType = 0x0C
+)
+
+var isoSyncTypeDescription = map[IsoSyncType]string{
+ ISO_SYNC_TYPE_NONE: "unsynchronized",
+ ISO_SYNC_TYPE_ASYNC: "asynchronous",
+ ISO_SYNC_TYPE_ADAPTIVE: "adaptive",
+ ISO_SYNC_TYPE_SYNC: "synchronous",
+}
+
+func (ist IsoSyncType) String() string {
+ return isoSyncTypeDescription[ist]
+}
+
+type IsoUsageType uint8
+
+const (
+ ISO_USAGE_TYPE_DATA IsoUsageType = C.LIBUSB_ISO_USAGE_TYPE_DATA << 4
+ ISO_USAGE_TYPE_FEEDBACK IsoUsageType = C.LIBUSB_ISO_USAGE_TYPE_FEEDBACK << 4
+ ISO_USAGE_TYPE_IMPLICIT IsoUsageType = C.LIBUSB_ISO_USAGE_TYPE_IMPLICIT << 4
+ ISO_USAGE_TYPE_MASK IsoUsageType = 0x30
+)
+
+var isoUsageTypeDescription = map[IsoUsageType]string{
+ ISO_USAGE_TYPE_DATA: "data",
+ ISO_USAGE_TYPE_FEEDBACK: "feedback",
+ ISO_USAGE_TYPE_IMPLICIT: "implicit data",
+}
+
+func (iut IsoUsageType) String() string {
+ return isoUsageTypeDescription[iut]
+}
+
+type RequestType uint8
+
+const (
+ REQUEST_TYPE_STANDARD = C.LIBUSB_REQUEST_TYPE_STANDARD
+ REQUEST_TYPE_CLASS = C.LIBUSB_REQUEST_TYPE_CLASS
+ REQUEST_TYPE_VENDOR = C.LIBUSB_REQUEST_TYPE_VENDOR
+ REQUEST_TYPE_RESERVED = C.LIBUSB_REQUEST_TYPE_RESERVED
+)
+
+var requestTypeDescription = map[RequestType]string{
+ REQUEST_TYPE_STANDARD: "standard",
+ REQUEST_TYPE_CLASS: "class",
+ REQUEST_TYPE_VENDOR: "vendor",
+ REQUEST_TYPE_RESERVED: "reserved",
+}
+
+func (rt RequestType) String() string {
+ return requestTypeDescription[rt]
+}
diff --git a/vendor/github.com/karalabe/gousb/usb/debug.go b/vendor/github.com/karalabe/gousb/usb/debug.go
new file mode 100644
index 000000000..e7de8e522
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/usb/debug.go
@@ -0,0 +1,36 @@
+// Copyright 2013 Google Inc. 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
+
+// To enable internal debugging:
+// -ldflags "-X github.com/karalabe/gousb/usb.debugInternal true"
+
+import (
+ "io"
+ "io/ioutil"
+ "log" // TODO(kevlar): make a logger
+ "os"
+)
+
+var debug *log.Logger
+var debugInternal string
+
+func init() {
+ var out io.Writer = ioutil.Discard
+ if debugInternal != "" {
+ out = os.Stderr
+ }
+ debug = log.New(out, "usb", log.LstdFlags|log.Lshortfile)
+}
diff --git a/vendor/github.com/karalabe/gousb/usb/descriptor.go b/vendor/github.com/karalabe/gousb/usb/descriptor.go
new file mode 100644
index 000000000..2551dfc23
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/usb/descriptor.go
@@ -0,0 +1,77 @@
+// Copyright 2013 Google Inc. 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
+
+/*
+#ifndef OS_WINDOWS
+ #include "os/threads_posix.h"
+#endif
+#include "libusbi.h"
+#include "libusb.h"
+*/
+import "C"
+
+type Descriptor struct {
+ // Bus information
+ Bus uint8 // The bus on which the device was detected
+ Address uint8 // The address of the device on the bus
+
+ // Version information
+ Spec BCD // USB Specification Release Number
+ Device BCD // The device version
+
+ // Product information
+ Vendor ID // The Vendor identifer
+ Product ID // The Product identifier
+
+ // Protocol information
+ Class uint8 // The class of this device
+ SubClass uint8 // The sub-class (within the class) of this device
+ Protocol uint8 // The protocol (within the sub-class) of this device
+
+ // Configuration information
+ Configs []ConfigInfo
+}
+
+func newDescriptor(dev *C.libusb_device) (*Descriptor, error) {
+ var desc C.struct_libusb_device_descriptor
+ if errno := C.libusb_get_device_descriptor(dev, &desc); errno < 0 {
+ return nil, usbError(errno)
+ }
+
+ // Enumerate configurations
+ var cfgs []ConfigInfo
+ for i := 0; i < int(desc.bNumConfigurations); i++ {
+ var cfg *C.struct_libusb_config_descriptor
+ if errno := C.libusb_get_config_descriptor(dev, C.uint8_t(i), &cfg); errno < 0 {
+ return nil, usbError(errno)
+ }
+ cfgs = append(cfgs, newConfig(dev, cfg))
+ C.libusb_free_config_descriptor(cfg)
+ }
+
+ return &Descriptor{
+ Bus: uint8(C.libusb_get_bus_number(dev)),
+ Address: uint8(C.libusb_get_device_address(dev)),
+ Spec: BCD(desc.bcdUSB),
+ Device: BCD(desc.bcdDevice),
+ Vendor: ID(desc.idVendor),
+ Product: ID(desc.idProduct),
+ Class: uint8(desc.bDeviceClass),
+ SubClass: uint8(desc.bDeviceSubClass),
+ Protocol: uint8(desc.bDeviceProtocol),
+ Configs: cfgs,
+ }, nil
+}
diff --git a/vendor/github.com/karalabe/gousb/usb/device.go b/vendor/github.com/karalabe/gousb/usb/device.go
new file mode 100644
index 000000000..b4c4131fa
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/usb/device.go
@@ -0,0 +1,295 @@
+// Copyright 2013 Google Inc. 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
+
+/*
+#ifndef OS_WINDOWS
+ #include "os/threads_posix.h"
+#endif
+#include "libusbi.h"
+#include "libusb.h"
+*/
+import "C"
+
+import (
+ "fmt"
+ "reflect"
+ "sync"
+ "time"
+ "unsafe"
+)
+
+var DefaultReadTimeout = 1 * time.Second
+var DefaultWriteTimeout = 1 * time.Second
+var DefaultControlTimeout = 250 * time.Millisecond //5 * time.Second
+
+type Device struct {
+ handle *C.libusb_device_handle
+
+ // Embed the device information for easy access
+ *Descriptor
+
+ // Timeouts
+ ReadTimeout time.Duration
+ WriteTimeout time.Duration
+ ControlTimeout time.Duration
+
+ // Claimed interfaces
+ lock *sync.Mutex
+ claimed map[uint8]int
+
+ // Detached kernel interfaces
+ detached map[uint8]int
+}
+
+func newDevice(handle *C.libusb_device_handle, desc *Descriptor) (*Device, error) {
+ ifaces := 0
+ d := &Device{
+ handle: handle,
+ Descriptor: desc,
+ ReadTimeout: DefaultReadTimeout,
+ WriteTimeout: DefaultWriteTimeout,
+ ControlTimeout: DefaultControlTimeout,
+ lock: new(sync.Mutex),
+ claimed: make(map[uint8]int, ifaces),
+ detached: make(map[uint8]int),
+ }
+
+ if err := d.detachKernelDriver(); err != nil {
+ d.Close()
+ return nil, err
+ }
+
+ return d, nil
+}
+
+// detachKernelDriver detaches any active kernel drivers, if supported by the platform.
+// If there are any errors, like Context.ListDevices, only the final one will be returned.
+func (d *Device) detachKernelDriver() (err error) {
+ for _, cfg := range d.Configs {
+ for _, iface := range cfg.Interfaces {
+ switch activeErr := C.libusb_kernel_driver_active(d.handle, C.int(iface.Number)); activeErr {
+ case C.LIBUSB_ERROR_NOT_SUPPORTED:
+ // no need to do any futher checking, no platform support
+ return
+ case 0:
+ continue
+ case 1:
+ switch detachErr := C.libusb_detach_kernel_driver(d.handle, C.int(iface.Number)); detachErr {
+ case C.LIBUSB_ERROR_NOT_SUPPORTED:
+ // shouldn't ever get here, should be caught by the outer switch
+ return
+ case 0:
+ d.detached[iface.Number]++
+ case C.LIBUSB_ERROR_NOT_FOUND:
+ // this status is returned if libusb's driver is already attached to the device
+ d.detached[iface.Number]++
+ default:
+ err = fmt.Errorf("usb: detach kernel driver: %s", usbError(detachErr))
+ }
+ default:
+ err = fmt.Errorf("usb: active kernel driver check: %s", usbError(activeErr))
+ }
+ }
+ }
+
+ return
+}
+
+// attachKernelDriver re-attaches kernel drivers to any previously detached interfaces, if supported by the platform.
+// If there are any errors, like Context.ListDevices, only the final one will be returned.
+func (d *Device) attachKernelDriver() (err error) {
+ for iface := range d.detached {
+ switch attachErr := C.libusb_attach_kernel_driver(d.handle, C.int(iface)); attachErr {
+ case C.LIBUSB_ERROR_NOT_SUPPORTED:
+ // no need to do any futher checking, no platform support
+ return
+ case 0:
+ continue
+ default:
+ err = fmt.Errorf("usb: attach kernel driver: %s", usbError(attachErr))
+ }
+ }
+
+ return
+}
+
+func (d *Device) Reset() error {
+ if errno := C.libusb_reset_device(d.handle); errno != 0 {
+ return usbError(errno)
+ }
+ return nil
+}
+
+func (d *Device) Control(rType, request uint8, val, idx uint16, data []byte) (int, error) {
+ //log.Printf("control xfer: %d:%d/%d:%d %x", idx, rType, request, val, string(data))
+ dataSlice := (*reflect.SliceHeader)(unsafe.Pointer(&data))
+ n := C.libusb_control_transfer(
+ d.handle,
+ C.uint8_t(rType),
+ C.uint8_t(request),
+ C.uint16_t(val),
+ C.uint16_t(idx),
+ (*C.uchar)(unsafe.Pointer(dataSlice.Data)),
+ C.uint16_t(len(data)),
+ C.uint(d.ControlTimeout/time.Millisecond))
+ if n < 0 {
+ return int(n), usbError(n)
+ }
+ return int(n), nil
+}
+
+// ActiveConfig returns the config id (not the index) of the active configuration.
+// This corresponds to the ConfigInfo.Config field.
+func (d *Device) ActiveConfig() (uint8, error) {
+ var cfg C.int
+ if errno := C.libusb_get_configuration(d.handle, &cfg); errno < 0 {
+ return 0, usbError(errno)
+ }
+ return uint8(cfg), nil
+}
+
+// SetConfig attempts to change the active configuration.
+// The cfg provided is the config id (not the index) of the configuration to set,
+// which corresponds to the ConfigInfo.Config field.
+func (d *Device) SetConfig(cfg uint8) error {
+ if errno := C.libusb_set_configuration(d.handle, C.int(cfg)); errno < 0 {
+ return usbError(errno)
+ }
+ return nil
+}
+
+// Close the device.
+func (d *Device) Close() error {
+ if d.handle == nil {
+ return fmt.Errorf("usb: double close on device")
+ }
+ d.lock.Lock()
+ defer d.lock.Unlock()
+ for iface := range d.claimed {
+ C.libusb_release_interface(d.handle, C.int(iface))
+ }
+ d.attachKernelDriver()
+ C.libusb_close(d.handle)
+ d.handle = nil
+ return nil
+}
+
+func (d *Device) OpenEndpoint(conf, iface, setup, epoint uint8) (Endpoint, error) {
+ end := &endpoint{
+ Device: d,
+ }
+
+ var setAlternate bool
+ for _, c := range d.Configs {
+ if c.Config != conf {
+ continue
+ }
+ debug.Printf("found conf: %#v\n", c)
+ for _, i := range c.Interfaces {
+ if i.Number != iface {
+ continue
+ }
+ debug.Printf("found iface: %#v\n", i)
+ for i, s := range i.Setups {
+ if s.Alternate != setup {
+ continue
+ }
+ setAlternate = i != 0
+
+ debug.Printf("found setup: %#v [default: %v]\n", s, !setAlternate)
+ for _, e := range s.Endpoints {
+ debug.Printf("ep %02x search: %#v\n", epoint, s)
+ if e.Address != epoint {
+ continue
+ }
+ end.InterfaceSetup = s
+ end.EndpointInfo = e
+ switch tt := TransferType(e.Attributes) & TRANSFER_TYPE_MASK; tt {
+ case TRANSFER_TYPE_BULK:
+ end.xfer = bulk_xfer
+ case TRANSFER_TYPE_INTERRUPT:
+ end.xfer = interrupt_xfer
+ case TRANSFER_TYPE_ISOCHRONOUS:
+ end.xfer = isochronous_xfer
+ default:
+ return nil, fmt.Errorf("usb: %s transfer is unsupported", tt)
+ }
+ goto found
+ }
+ return nil, fmt.Errorf("usb: unknown endpoint %02x", epoint)
+ }
+ return nil, fmt.Errorf("usb: unknown setup %02x", setup)
+ }
+ return nil, fmt.Errorf("usb: unknown interface %02x", iface)
+ }
+ return nil, fmt.Errorf("usb: unknown configuration %02x", conf)
+
+found:
+
+ // Set the configuration
+ var activeConf C.int
+ if errno := C.libusb_get_configuration(d.handle, &activeConf); errno < 0 {
+ return nil, fmt.Errorf("usb: getcfg: %s", usbError(errno))
+ }
+ if int(activeConf) != int(conf) {
+ if errno := C.libusb_set_configuration(d.handle, C.int(conf)); errno < 0 {
+ return nil, fmt.Errorf("usb: setcfg: %s", usbError(errno))
+ }
+ }
+
+ // Claim the interface
+ if errno := C.libusb_claim_interface(d.handle, C.int(iface)); errno < 0 {
+ return nil, fmt.Errorf("usb: claim: %s", usbError(errno))
+ }
+
+ // Increment the claim count
+ d.lock.Lock()
+ d.claimed[iface]++
+ d.lock.Unlock() // unlock immediately because the next calls may block
+
+ // Choose the alternate
+ if setAlternate {
+ if errno := C.libusb_set_interface_alt_setting(d.handle, C.int(iface), C.int(setup)); errno < 0 {
+ debug.Printf("altsetting error: %s", usbError(errno))
+ return nil, fmt.Errorf("usb: setalt: %s", usbError(errno))
+ }
+ }
+
+ return end, nil
+}
+
+func (d *Device) GetStringDescriptor(desc_index int) (string, error) {
+
+ // allocate 200-byte array limited the length of string descriptor
+ goBuffer := make([]byte, 200)
+
+ // get string descriptor from libusb. if errno < 0 then there are any errors.
+ // if errno >= 0; it is a length of result string descriptor
+ errno := C.libusb_get_string_descriptor_ascii(
+ d.handle,
+ C.uint8_t(desc_index),
+ (*C.uchar)(unsafe.Pointer(&goBuffer[0])),
+ 200)
+
+ // if any errors occur
+ if errno < 0 {
+ return "", fmt.Errorf("usb: getstr: %s", usbError(errno))
+ }
+ // convert slice of byte to string with limited length from errno
+ stringDescriptor := string(goBuffer[:errno])
+
+ return stringDescriptor, nil
+}
diff --git a/vendor/github.com/karalabe/gousb/usb/endpoint.go b/vendor/github.com/karalabe/gousb/usb/endpoint.go
new file mode 100644
index 000000000..12b4ccf95
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/usb/endpoint.go
@@ -0,0 +1,100 @@
+// Copyright 2013 Google Inc. 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
+
+// #include "libusb.h"
+import "C"
+
+import (
+ "fmt"
+ "reflect"
+ "time"
+ "unsafe"
+)
+
+type Endpoint interface {
+ Read(b []byte) (int, error)
+ Write(b []byte) (int, error)
+ Interface() InterfaceSetup
+ Info() EndpointInfo
+}
+
+type endpoint struct {
+ *Device
+ InterfaceSetup
+ EndpointInfo
+ xfer func(*endpoint, []byte, time.Duration) (int, error)
+}
+
+func (e *endpoint) Read(buf []byte) (int, error) {
+ if EndpointDirection(e.Address)&ENDPOINT_DIR_MASK != ENDPOINT_DIR_IN {
+ return 0, fmt.Errorf("usb: read: not an IN endpoint")
+ }
+
+ return e.xfer(e, buf, e.ReadTimeout)
+}
+
+func (e *endpoint) Write(buf []byte) (int, error) {
+ if EndpointDirection(e.Address)&ENDPOINT_DIR_MASK != ENDPOINT_DIR_OUT {
+ return 0, fmt.Errorf("usb: write: not an OUT endpoint")
+ }
+
+ return e.xfer(e, buf, e.WriteTimeout)
+}
+
+func (e *endpoint) Interface() InterfaceSetup { return e.InterfaceSetup }
+func (e *endpoint) Info() EndpointInfo { return e.EndpointInfo }
+
+// TODO(kevlar): (*Endpoint).Close
+
+func bulk_xfer(e *endpoint, buf []byte, timeout time.Duration) (int, error) {
+ if len(buf) == 0 {
+ return 0, nil
+ }
+
+ data := (*reflect.SliceHeader)(unsafe.Pointer(&buf)).Data
+
+ var cnt C.int
+ if errno := C.libusb_bulk_transfer(
+ e.handle,
+ C.uchar(e.Address),
+ (*C.uchar)(unsafe.Pointer(data)),
+ C.int(len(buf)),
+ &cnt,
+ C.uint(timeout/time.Millisecond)); errno < 0 {
+ return 0, usbError(errno)
+ }
+ return int(cnt), nil
+}
+
+func interrupt_xfer(e *endpoint, buf []byte, timeout time.Duration) (int, error) {
+ if len(buf) == 0 {
+ return 0, nil
+ }
+
+ data := (*reflect.SliceHeader)(unsafe.Pointer(&buf)).Data
+
+ var cnt C.int
+ if errno := C.libusb_interrupt_transfer(
+ e.handle,
+ C.uchar(e.Address),
+ (*C.uchar)(unsafe.Pointer(data)),
+ C.int(len(buf)),
+ &cnt,
+ C.uint(timeout/time.Millisecond)); errno < 0 {
+ return 0, usbError(errno)
+ }
+ return int(cnt), nil
+}
diff --git a/vendor/github.com/karalabe/gousb/usb/error.go b/vendor/github.com/karalabe/gousb/usb/error.go
new file mode 100644
index 000000000..30d47bce3
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/usb/error.go
@@ -0,0 +1,92 @@
+// Copyright 2013 Google Inc. 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.h"
+import "C"
+
+type usbError C.int
+
+func (e usbError) Error() string {
+ return fmt.Sprintf("libusb: %s [code %d]", usbErrorString[e], int(e))
+}
+
+const (
+ SUCCESS usbError = C.LIBUSB_SUCCESS
+ ERROR_IO usbError = C.LIBUSB_ERROR_IO
+ ERROR_INVALID_PARAM usbError = C.LIBUSB_ERROR_INVALID_PARAM
+ ERROR_ACCESS usbError = C.LIBUSB_ERROR_ACCESS
+ ERROR_NO_DEVICE usbError = C.LIBUSB_ERROR_NO_DEVICE
+ ERROR_NOT_FOUND usbError = C.LIBUSB_ERROR_NOT_FOUND
+ ERROR_BUSY usbError = C.LIBUSB_ERROR_BUSY
+ ERROR_TIMEOUT usbError = C.LIBUSB_ERROR_TIMEOUT
+ ERROR_OVERFLOW usbError = C.LIBUSB_ERROR_OVERFLOW
+ ERROR_PIPE usbError = C.LIBUSB_ERROR_PIPE
+ ERROR_INTERRUPTED usbError = C.LIBUSB_ERROR_INTERRUPTED
+ ERROR_NO_MEM usbError = C.LIBUSB_ERROR_NO_MEM
+ ERROR_NOT_SUPPORTED usbError = C.LIBUSB_ERROR_NOT_SUPPORTED
+ ERROR_OTHER usbError = C.LIBUSB_ERROR_OTHER
+)
+
+var usbErrorString = map[usbError]string{
+ C.LIBUSB_SUCCESS: "success",
+ C.LIBUSB_ERROR_IO: "i/o error",
+ C.LIBUSB_ERROR_INVALID_PARAM: "invalid param",
+ C.LIBUSB_ERROR_ACCESS: "bad access",
+ C.LIBUSB_ERROR_NO_DEVICE: "no device",
+ C.LIBUSB_ERROR_NOT_FOUND: "not found",
+ C.LIBUSB_ERROR_BUSY: "device or resource busy",
+ C.LIBUSB_ERROR_TIMEOUT: "timeout",
+ C.LIBUSB_ERROR_OVERFLOW: "overflow",
+ C.LIBUSB_ERROR_PIPE: "pipe error",
+ C.LIBUSB_ERROR_INTERRUPTED: "interrupted",
+ C.LIBUSB_ERROR_NO_MEM: "out of memory",
+ C.LIBUSB_ERROR_NOT_SUPPORTED: "not supported",
+ C.LIBUSB_ERROR_OTHER: "unknown error",
+}
+
+type TransferStatus uint8
+
+const (
+ LIBUSB_TRANSFER_COMPLETED TransferStatus = C.LIBUSB_TRANSFER_COMPLETED
+ LIBUSB_TRANSFER_ERROR TransferStatus = C.LIBUSB_TRANSFER_ERROR
+ LIBUSB_TRANSFER_TIMED_OUT TransferStatus = C.LIBUSB_TRANSFER_TIMED_OUT
+ LIBUSB_TRANSFER_CANCELLED TransferStatus = C.LIBUSB_TRANSFER_CANCELLED
+ LIBUSB_TRANSFER_STALL TransferStatus = C.LIBUSB_TRANSFER_STALL
+ LIBUSB_TRANSFER_NO_DEVICE TransferStatus = C.LIBUSB_TRANSFER_NO_DEVICE
+ LIBUSB_TRANSFER_OVERFLOW TransferStatus = C.LIBUSB_TRANSFER_OVERFLOW
+)
+
+var transferStatusDescription = map[TransferStatus]string{
+ LIBUSB_TRANSFER_COMPLETED: "transfer completed without error",
+ LIBUSB_TRANSFER_ERROR: "transfer failed",
+ LIBUSB_TRANSFER_TIMED_OUT: "transfer timed out",
+ LIBUSB_TRANSFER_CANCELLED: "transfer was cancelled",
+ LIBUSB_TRANSFER_STALL: "halt condition detected (endpoint stalled) or control request not supported",
+ LIBUSB_TRANSFER_NO_DEVICE: "device was disconnected",
+ LIBUSB_TRANSFER_OVERFLOW: "device sent more data than requested",
+}
+
+func (ts TransferStatus) String() string {
+ return transferStatusDescription[ts]
+}
+
+func (ts TransferStatus) Error() string {
+ return "libusb: " + ts.String()
+}
diff --git a/vendor/github.com/karalabe/gousb/usb/iso.c b/vendor/github.com/karalabe/gousb/usb/iso.c
new file mode 100644
index 000000000..054486318
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/usb/iso.c
@@ -0,0 +1,79 @@
+#include "libusb.h"
+#include <stdio.h>
+#include <string.h>
+
+void print_xfer(struct libusb_transfer *xfer);
+void iso_callback(void *);
+
+void callback(struct libusb_transfer *xfer) {
+ //printf("Callback!\n");
+ //print_xfer(xfer);
+ iso_callback(xfer->user_data);
+}
+
+int submit(struct libusb_transfer *xfer) {
+ xfer->callback = &callback;
+ xfer->status = -1;
+ //print_xfer(xfer);
+ //printf("Transfer submitted\n");
+
+ /* fake
+ strcpy(xfer->buffer, "hello");
+ xfer->actual_length = 5;
+ callback(xfer);
+ return 0; */
+ return libusb_submit_transfer(xfer);
+}
+
+void print_xfer(struct libusb_transfer *xfer) {
+ int i;
+
+ printf("Transfer:\n");
+ printf(" dev_handle: %p\n", xfer->dev_handle);
+ printf(" flags: %08x\n", xfer->flags);
+ printf(" endpoint: %x\n", xfer->endpoint);
+ printf(" type: %x\n", xfer->type);
+ printf(" timeout: %dms\n", xfer->timeout);
+ printf(" status: %x\n", xfer->status);
+ printf(" length: %d (act: %d)\n", xfer->length, xfer->actual_length);
+ printf(" callback: %p\n", xfer->callback);
+ printf(" user_data: %p\n", xfer->user_data);
+ printf(" buffer: %p\n", xfer->buffer);
+ printf(" num_iso_pkts: %d\n", xfer->num_iso_packets);
+ printf(" packets:\n");
+ for (i = 0; i < xfer->num_iso_packets; i++) {
+ printf(" [%04d] %d (act: %d) %x\n", i,
+ xfer->iso_packet_desc[i].length,
+ xfer->iso_packet_desc[i].actual_length,
+ xfer->iso_packet_desc[i].status);
+ }
+}
+
+int extract_data(struct libusb_transfer *xfer, void *raw, int max, unsigned char *status) {
+ int i;
+ int copied = 0;
+ unsigned char *in = xfer->buffer;
+ unsigned char *out = raw;
+ for (i = 0; i < xfer->num_iso_packets; i++) {
+ struct libusb_iso_packet_descriptor pkt = xfer->iso_packet_desc[i];
+
+ // Copy the data
+ int len = pkt.actual_length;
+ if (len > max) {
+ len = max;
+ }
+ memcpy(out, in, len);
+ copied += len;
+
+ // Increment offsets
+ in += pkt.length;
+ out += len;
+
+ // Extract first error
+ if (pkt.status == 0 || *status != 0) {
+ continue;
+ }
+ *status = pkt.status;
+ }
+ return copied;
+}
diff --git a/vendor/github.com/karalabe/gousb/usb/iso.go b/vendor/github.com/karalabe/gousb/usb/iso.go
new file mode 100644
index 000000000..1ba694e0e
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/usb/iso.go
@@ -0,0 +1,148 @@
+// Copyright 2013 Google Inc. 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
+
+/*
+#include "libusb.h"
+
+int submit(struct libusb_transfer *xfer);
+void print_xfer(struct libusb_transfer *xfer);
+int extract_data(struct libusb_transfer *xfer, void *data, int max, unsigned char *status);
+*/
+import "C"
+
+import (
+ "fmt"
+ "log"
+ "time"
+ "unsafe"
+)
+
+//export iso_callback
+func iso_callback(cptr unsafe.Pointer) {
+ ch := *(*chan struct{})(cptr)
+ close(ch)
+}
+
+func (end *endpoint) allocTransfer() *Transfer {
+ // Use libusb_get_max_iso_packet_size ?
+ const (
+ iso_packets = 8 // 128 // 242
+ packet_size = 2 * 960 // 1760
+ )
+
+ xfer := C.libusb_alloc_transfer(C.int(iso_packets))
+ if xfer == nil {
+ log.Printf("usb: transfer allocation failed?!")
+ return nil
+ }
+
+ buf := make([]byte, iso_packets*packet_size)
+ done := make(chan struct{}, 1)
+
+ xfer.dev_handle = end.Device.handle
+ xfer.endpoint = C.uchar(end.Address)
+ xfer._type = C.LIBUSB_TRANSFER_TYPE_ISOCHRONOUS
+
+ xfer.buffer = (*C.uchar)((unsafe.Pointer)(&buf[0]))
+ xfer.length = C.int(len(buf))
+ xfer.num_iso_packets = iso_packets
+
+ C.libusb_set_iso_packet_lengths(xfer, packet_size)
+ /*
+ pkts := *(*[]C.struct_libusb_packet_descriptor)(unsafe.Pointer(&reflect.SliceHeader{
+ Data: uintptr(unsafe.Pointer(&xfer.iso_packet_desc)),
+ Len: iso_packets,
+ Cap: iso_packets,
+ }))
+ */
+
+ t := &Transfer{
+ xfer: xfer,
+ done: done,
+ buf: buf,
+ }
+ xfer.user_data = (unsafe.Pointer)(&t.done)
+
+ return t
+}
+
+type Transfer struct {
+ xfer *C.struct_libusb_transfer
+ pkts []*C.struct_libusb_packet_descriptor
+ done chan struct{}
+ buf []byte
+}
+
+func (t *Transfer) Submit(timeout time.Duration) error {
+ //log.Printf("iso: submitting %#v", t.xfer)
+ t.xfer.timeout = C.uint(timeout / time.Millisecond)
+ if errno := C.submit(t.xfer); errno < 0 {
+ return usbError(errno)
+ }
+ return nil
+}
+
+func (t *Transfer) Wait(b []byte) (n int, err error) {
+ select {
+ case <-time.After(10 * time.Second):
+ return 0, fmt.Errorf("wait timed out after 10s")
+ case <-t.done:
+ }
+ // Non-iso transfers:
+ //n = int(t.xfer.actual_length)
+ //copy(b, ((*[1 << 16]byte)(unsafe.Pointer(t.xfer.buffer)))[:n])
+
+ //C.print_xfer(t.xfer)
+ /*
+ buf, offset := ((*[1 << 16]byte)(unsafe.Pointer(t.xfer.buffer))), 0
+ for i, pkt := range *t.pkts {
+ log.Printf("Type is %T", t.pkts)
+ n += copy(b[n:], buf[offset:][:pkt.actual_length])
+ offset += pkt.Length
+ if pkt.status != 0 && err == nil {
+ err = error(TransferStatus(pkt.status))
+ }
+ }
+ */
+ var status uint8
+ n = int(C.extract_data(t.xfer, unsafe.Pointer(&b[0]), C.int(len(b)), (*C.uchar)(unsafe.Pointer(&status))))
+ if status != 0 {
+ err = TransferStatus(status)
+ }
+ return n, err
+}
+
+func (t *Transfer) Close() error {
+ C.libusb_free_transfer(t.xfer)
+ return nil
+}
+
+func isochronous_xfer(e *endpoint, buf []byte, timeout time.Duration) (int, error) {
+ t := e.allocTransfer()
+ defer t.Close()
+
+ if err := t.Submit(timeout); err != nil {
+ log.Printf("iso: xfer failed to submit: %s", err)
+ return 0, err
+ }
+
+ n, err := t.Wait(buf)
+ if err != nil {
+ log.Printf("iso: xfer failed: %s", err)
+ return 0, err
+ }
+ return n, err
+}
diff --git a/vendor/github.com/karalabe/gousb/usb/misc.go b/vendor/github.com/karalabe/gousb/usb/misc.go
new file mode 100644
index 000000000..6e25431bf
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/usb/misc.go
@@ -0,0 +1,47 @@
+// Copyright 2013 Google Inc. 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"
+)
+
+type BCD uint16
+
+const (
+ USB_2_0 BCD = 0x0200
+ USB_1_1 BCD = 0x0110
+ USB_1_0 BCD = 0x0100
+)
+
+func (d BCD) Int() (i int) {
+ ten := 1
+ for o := uint(0); o < 4; o++ {
+ n := ((0xF << (o * 4)) & d) >> (o * 4)
+ i += int(n) * ten
+ ten *= 10
+ }
+ return
+}
+
+func (d BCD) String() string {
+ return fmt.Sprintf("%02x.%02x", int(d>>8), int(d&0xFF))
+}
+
+type ID uint16
+
+func (id ID) String() string {
+ return fmt.Sprintf("%04x", int(id))
+}
diff --git a/vendor/github.com/karalabe/gousb/usb/usb.go b/vendor/github.com/karalabe/gousb/usb/usb.go
new file mode 100644
index 000000000..366827e69
--- /dev/null
+++ b/vendor/github.com/karalabe/gousb/usb/usb.go
@@ -0,0 +1,178 @@
+// Copyright 2013 Google Inc. 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 provides a wrapper around libusb-1.0.
+package usb
+
+/*
+#cgo CFLAGS: -I../internal/libusb/libusb
+#cgo CFLAGS: -DDEFAULT_VISIBILITY=""
+#cgo linux CFLAGS: -DOS_LINUX -D_GNU_SOURCE -DPOLL_NFDS_TYPE=int
+#cgo darwin CFLAGS: -DOS_DARWIN -DPOLL_NFDS_TYPE=int
+#cgo darwin LDFLAGS: -framework CoreFoundation -framework IOKit -lobjc
+#cgo openbsd CFLAGS: -DOS_OPENBSD -DPOLL_NFDS_TYPE=int
+#cgo windows CFLAGS: -DOS_WINDOWS -DUSE_USBDK -DPOLL_NFDS_TYPE=int
+
+#if defined(OS_LINUX) || defined(OS_DARWIN) || defined(OS_OPENBSD)
+ #include <sys/poll.h>
+ #include "os/threads_posix.c"
+ #include "os/poll_posix.c"
+#elif defined(OS_WINDOWS)
+ #include "os/threads_windows.c"
+ #include "os/poll_windows.c"
+#endif
+
+#ifdef OS_LINUX
+ #include "os/linux_usbfs.c"
+ #include "os/linux_netlink.c"
+#elif OS_DARWIN
+ #include "os/darwin_usb.c"
+#elif OS_OPENBSD
+ #include "os/openbsd_usb.c"
+#elif OS_WINDOWS
+ #include "os/windows_nt_common.c"
+ #include "os/windows_usbdk.c"
+#endif
+
+#include "core.c"
+#include "descriptor.c"
+#include "hotplug.c"
+#include "io.c"
+#include "strerror.c"
+#include "sync.c"
+*/
+import "C"
+
+import (
+ "log"
+ "reflect"
+ "unsafe"
+)
+
+type Context struct {
+ ctx *C.libusb_context
+ done chan struct{}
+}
+
+func (c *Context) Debug(level int) {
+ C.libusb_set_debug(c.ctx, C.int(level))
+}
+
+func NewContext() (*Context, error) {
+ c := &Context{
+ done: make(chan struct{}),
+ }
+
+ if errno := C.libusb_init(&c.ctx); errno != 0 {
+ return nil, usbError(errno)
+ }
+
+ go func() {
+ tv := C.struct_timeval{
+ tv_sec: 0,
+ tv_usec: 100000,
+ }
+ for {
+ select {
+ case <-c.done:
+ return
+ default:
+ }
+ if errno := C.libusb_handle_events_timeout_completed(c.ctx, &tv, nil); errno < 0 {
+ log.Printf("handle_events: error: %s", usbError(errno))
+ continue
+ }
+ //log.Printf("handle_events returned")
+ }
+ }()
+
+ return c, nil
+}
+
+// ListDevices calls each with each enumerated device.
+// If the function returns true, the device is opened and a Device is returned if the operation succeeds.
+// Every Device returned (whether an error is also returned or not) must be closed.
+// If there are any errors enumerating the devices,
+// the final one is returned along with any successfully opened devices.
+func (c *Context) ListDevices(each func(desc *Descriptor) bool) ([]*Device, error) {
+ var list **C.libusb_device
+ cnt := C.libusb_get_device_list(c.ctx, &list)
+ if cnt < 0 {
+ return nil, usbError(cnt)
+ }
+ defer C.libusb_free_device_list(list, 1)
+
+ var slice []*C.libusb_device
+ *(*reflect.SliceHeader)(unsafe.Pointer(&slice)) = reflect.SliceHeader{
+ Data: uintptr(unsafe.Pointer(list)),
+ Len: int(cnt),
+ Cap: int(cnt),
+ }
+
+ var reterr error
+ var ret []*Device
+ for _, dev := range slice {
+ desc, err := newDescriptor(dev)
+ if err != nil {
+ reterr = err
+ continue
+ }
+
+ if each(desc) {
+ var handle *C.libusb_device_handle
+ if errno := C.libusb_open(dev, &handle); errno != 0 {
+ reterr = usbError(errno)
+ continue
+ }
+ if dev, err := newDevice(handle, desc); err != nil {
+ reterr = err
+ } else {
+ ret = append(ret, dev)
+ }
+ }
+ }
+ return ret, reterr
+}
+
+// OpenDeviceWithVidPid opens Device from specific VendorId and ProductId.
+// If there are any errors, it'll returns at second value.
+func (c *Context) OpenDeviceWithVidPid(vid, pid int) (*Device, error) {
+
+ handle := C.libusb_open_device_with_vid_pid(c.ctx, (C.uint16_t)(vid), (C.uint16_t)(pid))
+ if handle == nil {
+ return nil, ERROR_NOT_FOUND
+ }
+
+ dev := C.libusb_get_device(handle)
+ if dev == nil {
+ return nil, ERROR_NO_DEVICE
+ }
+
+ desc, err := newDescriptor(dev)
+
+ // return an error from nil-handle and nil-device
+ if err != nil {
+ return nil, err
+ }
+ return newDevice(handle, desc)
+}
+
+func (c *Context) Close() error {
+ close(c.done)
+ if c.ctx != nil {
+ C.libusb_exit(c.ctx)
+ }
+ c.ctx = nil
+ return nil
+}