Compare commits
17 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
e840be9d06 | ||
![]() |
606016adee | ||
![]() |
8c7a7eb841 | ||
![]() |
2f9ed92cb8 | ||
![]() |
5514912016 | ||
![]() |
844d4b8a71 | ||
![]() |
6ad122da6a | ||
![]() |
1f6abc1f14 | ||
![]() |
bdb184b25c | ||
![]() |
d3bdbe53c8 | ||
![]() |
0ce3a07f8e | ||
![]() |
0eba1b1264 | ||
![]() |
c9efe54672 | ||
![]() |
dd9486a141 | ||
![]() |
cb06b9fb0d | ||
![]() |
76f12f69ac | ||
![]() |
0995919da4 |
@@ -1,15 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
export PATH="/mingw64/bin:${PATH}"
|
|
||||||
export PKG_CONFIG_PATH="/mingw64/lib/pkgconfig:${PKG_CONFIG_PATH}"
|
|
||||||
export GOROOT=/mingw64/lib/go
|
|
||||||
export GOPATH=/go
|
|
||||||
export CGO_ENABLED=1
|
|
||||||
|
|
||||||
pacman --noconfirm -S \
|
|
||||||
mingw64/mingw-w64-x86_64-go \
|
|
||||||
mingw64/mingw-w64-x86_64-libusb
|
|
||||||
|
|
||||||
go version
|
|
||||||
go get -t github.com/google/gousb/...
|
|
||||||
go test github.com/google/gousb/...
|
|
4
.github/test-coverage.sh
vendored
Normal file
4
.github/test-coverage.sh
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
echo 'mode: count' > coverage.merged &&\
|
||||||
|
go list -f '{{.Dir}}' ./... | xargs -I'{}' sh -c ': > coverage.tmp; go test -v -covermode=count -coverprofile=coverage.tmp {} && tail -n +2 coverage.tmp >> coverage.merged' &&\
|
||||||
|
rm coverage.tmp
|
35
.github/workflows/build-and-test.yaml
vendored
Normal file
35
.github/workflows/build-and-test.yaml
vendored
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
name: build-and-test
|
||||||
|
on: [pull_request, push, workflow_dispatch]
|
||||||
|
jobs:
|
||||||
|
linux:
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: 1.23
|
||||||
|
- run: DIFF="$( find . -name '*.go' -print0 | xargs -0 gofmt -l )"; if [ -n "$DIFF" ]; then echo "Files not formatted, run gofmt:"; echo "$DIFF"; exit 1; fi
|
||||||
|
- run: sudo apt-get install libusb-1.0-0-dev
|
||||||
|
- run: go install golang.org/x/tools/cmd/cover@latest
|
||||||
|
- run: go install golang.org/x/lint/golint@latest
|
||||||
|
- run: $HOME/go/bin/golint -set_exit_status ./...
|
||||||
|
- run: sh ./.github/test-coverage.sh
|
||||||
|
- uses: shogo82148/actions-goveralls@v1
|
||||||
|
with:
|
||||||
|
path-to-profile: coverage.merged
|
||||||
|
ignore: libusb.go,error.go
|
||||||
|
windows:
|
||||||
|
runs-on: windows-2022
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: 1.22
|
||||||
|
- uses: msys2/setup-msys2@v2
|
||||||
|
with:
|
||||||
|
install: |-
|
||||||
|
mingw64/mingw-w64-x86_64-libusb
|
||||||
|
mingw64/mingw-w64-x86_64-pkg-config
|
||||||
|
- run: echo "D:\a\_temp\msys64\mingw64\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
||||||
|
- run: go test ./...
|
||||||
|
- run: go run ./lsusb
|
30
.travis.yml
30
.travis.yml
@@ -1,30 +0,0 @@
|
|||||||
language: go
|
|
||||||
dist: trusty
|
|
||||||
|
|
||||||
go:
|
|
||||||
- 1.7.x
|
|
||||||
- 1.8.x
|
|
||||||
- 1.9.x
|
|
||||||
- 1.10.x
|
|
||||||
- tip
|
|
||||||
|
|
||||||
addons:
|
|
||||||
apt:
|
|
||||||
packages:
|
|
||||||
libusb-1.0-0-dev
|
|
||||||
|
|
||||||
install:
|
|
||||||
- go get golang.org/x/tools/cmd/cover
|
|
||||||
# Golint requires Go 1.9 or later.
|
|
||||||
- if ! [[ "$TRAVIS_GO_VERSION" =~ ^1\.7\.([0-9]+|x)$ || "$TRAVIS_GO_VERSION" =~ ^1\.8\.([0-9]+|x)$ ]]; then go get golang.org/x/lint/golint; fi
|
|
||||||
- go get github.com/mattn/goveralls
|
|
||||||
|
|
||||||
script:
|
|
||||||
# a workaround for go test not supporting coverage for multiple packages in a single invocation.
|
|
||||||
# If goveralls upload fails, ignore the result.
|
|
||||||
# Golint requires Go 1.9 or later.
|
|
||||||
- if ! [[ "$TRAVIS_GO_VERSION" =~ ^1\.7\.([0-9]+|x)+$ || "$TRAVIS_GO_VERSION" =~ ^1\.8\.([0-9]+|x)$ ]]; then $HOME/gopath/bin/golint ./...; fi
|
|
||||||
- |-
|
|
||||||
echo 'mode: count' > coverage.merged && go list ./... | xargs -n1 -I{} sh -c ': > coverage.tmp; go test -v -covermode=count -coverprofile=coverage.tmp {} && tail -n +2 coverage.tmp >> coverage.merged' && rm coverage.tmp
|
|
||||||
- |-
|
|
||||||
$HOME/gopath/bin/goveralls -coverprofile=coverage.merged -service=travis-ci -ignore libusb.go,error.go || true
|
|
@@ -4,7 +4,6 @@ Introduction
|
|||||||
[![Build Status][ciimg]][ci]
|
[![Build Status][ciimg]][ci]
|
||||||
[![GoDoc][docimg]][doc]
|
[![GoDoc][docimg]][doc]
|
||||||
[![Coverage Status][coverimg]][cover]
|
[![Coverage Status][coverimg]][cover]
|
||||||
[![Build status][appveimg]][appveyor]
|
|
||||||
|
|
||||||
|
|
||||||
The gousb package is an attempt at wrapping the libusb library into a Go-like binding.
|
The gousb package is an attempt at wrapping the libusb library into a Go-like binding.
|
||||||
@@ -21,12 +20,10 @@ As of 2017-07-13 the 2.0 API is considered stable and 1.0 is deprecated.
|
|||||||
|
|
||||||
[coverimg]: https://coveralls.io/repos/github/google/gousb/badge.svg
|
[coverimg]: https://coveralls.io/repos/github/google/gousb/badge.svg
|
||||||
[cover]: https://coveralls.io/github/google/gousb
|
[cover]: https://coveralls.io/github/google/gousb
|
||||||
[ciimg]: https://travis-ci.org/google/gousb.svg
|
[ciimg]: https://github.com/google/gousb/actions/workflows/build-and-test.yaml/badge.svg
|
||||||
[ci]: https://travis-ci.org/google/gousb
|
[ci]: https://github.com/google/gousb/actions/workflows/build-and-test.yaml
|
||||||
[docimg]: https://godoc.org/github.com/google/gousb?status.svg
|
[docimg]: https://godoc.org/github.com/google/gousb?status.svg
|
||||||
[doc]: https://godoc.org/github.com/google/gousb
|
[doc]: https://godoc.org/github.com/google/gousb
|
||||||
[appveimg]: https://ci.appveyor.com/api/projects/status/661qp7x33o3wqe4o?svg=true
|
|
||||||
[appveyor]: https://ci.appveyor.com/project/zagrodzki/gousb
|
|
||||||
|
|
||||||
Documentation
|
Documentation
|
||||||
=============
|
=============
|
||||||
|
@@ -1,8 +0,0 @@
|
|||||||
platform: x64
|
|
||||||
|
|
||||||
clone_folder: C:\msys64\go\src\github.com\google\gousb
|
|
||||||
|
|
||||||
install:
|
|
||||||
- C:\msys64\usr\bin\bash.exe -lc "cd /go/src/github.com/google/gousb/ && .appveyor/./install.sh"
|
|
||||||
|
|
||||||
build: off
|
|
28
config.go
28
config.go
@@ -49,25 +49,17 @@ func (c ConfigDesc) String() string {
|
|||||||
return fmt.Sprintf("Configuration %d", c.Number)
|
return fmt.Sprintf("Configuration %d", c.Number)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c ConfigDesc) intfDesc(num, alt int) (*InterfaceSetting, error) {
|
func (c ConfigDesc) intfDesc(num int) (*InterfaceDesc, error) {
|
||||||
// In an ideal world, interfaces in the descriptor would be numbered
|
// In an ideal world, interfaces in the descriptor would be numbered
|
||||||
// contiguously starting from 0, as required by the specification. In the
|
// contiguously starting from 0, as required by the specification. In the
|
||||||
// real world however the specification is sometimes ignored:
|
// real world however the specification is sometimes ignored:
|
||||||
// https://github.com/google/gousb/issues/65
|
// https://github.com/google/gousb/issues/65
|
||||||
ifs := make([]int, len(c.Interfaces))
|
ifs := make([]int, len(c.Interfaces))
|
||||||
for i := range c.Interfaces {
|
for i, desc := range c.Interfaces {
|
||||||
ifs[i] = c.Interfaces[i].Number
|
if desc.Number == num {
|
||||||
if ifs[i] != num {
|
return &desc, nil
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
alts := make([]int, len(c.Interfaces[i].AltSettings))
|
ifs[i] = desc.Number
|
||||||
for a := range c.Interfaces[i].AltSettings {
|
|
||||||
alts[a] = c.Interfaces[i].AltSettings[a].Alternate
|
|
||||||
if alts[a] == alt {
|
|
||||||
return &c.Interfaces[i].AltSettings[a], nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("alternate setting %d not found for %s, available alt settings: %v", alt, c.Interfaces[i], alts)
|
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("interface %d not found, available interface numbers: %v", num, ifs)
|
return nil, fmt.Errorf("interface %d not found, available interface numbers: %v", num, ifs)
|
||||||
}
|
}
|
||||||
@@ -120,9 +112,13 @@ func (c *Config) Interface(num, alt int) (*Interface, error) {
|
|||||||
return nil, fmt.Errorf("Interface(%d, %d) called on %s after Close", num, alt, c)
|
return nil, fmt.Errorf("Interface(%d, %d) called on %s after Close", num, alt, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
altInfo, err := c.Desc.intfDesc(num, alt)
|
intf, err := c.Desc.intfDesc(num)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("descriptor of interface (%d, %d) in %s: %v", num, alt, c, err)
|
return nil, fmt.Errorf("descriptor of interface %d in %s: %v", num, c, err)
|
||||||
|
}
|
||||||
|
altInfo, err := intf.altSetting(alt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("descriptor of alternate setting %d of interface %d in %s: %v", alt, num, c, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
@@ -137,7 +133,7 @@ func (c *Config) Interface(num, alt int) (*Interface, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Select an alternate setting if needed (device has multiple alternate settings).
|
// Select an alternate setting if needed (device has multiple alternate settings).
|
||||||
if len(c.Desc.Interfaces[num].AltSettings) > 1 {
|
if len(intf.AltSettings) > 1 {
|
||||||
if err := c.dev.ctx.libusb.setAlt(c.dev.handle, uint8(num), uint8(alt)); err != nil {
|
if err := c.dev.ctx.libusb.setAlt(c.dev.handle, uint8(num), uint8(alt)); err != nil {
|
||||||
c.dev.ctx.libusb.release(c.dev.handle, uint8(num))
|
c.dev.ctx.libusb.release(c.dev.handle, uint8(num))
|
||||||
return nil, fmt.Errorf("failed to set alternate config %d on interface %d of %s: %v", alt, num, c, err)
|
return nil, fmt.Errorf("failed to set alternate config %d on interface %d of %s: %v", alt, num, c, err)
|
||||||
|
31
device.go
31
device.go
@@ -24,11 +24,22 @@ import (
|
|||||||
|
|
||||||
// DeviceDesc is a representation of a USB device descriptor.
|
// DeviceDesc is a representation of a USB device descriptor.
|
||||||
type DeviceDesc struct {
|
type DeviceDesc struct {
|
||||||
// Bus information
|
// The bus on which the device was detected
|
||||||
Bus int // The bus on which the device was detected
|
Bus int
|
||||||
Address int // The address of the device on the bus
|
// The address of the device on the bus
|
||||||
Speed Speed // The negotiated operating speed for the device
|
Address int
|
||||||
Port int // The usb port on which the device was detected
|
// The negotiated operating speed for the device
|
||||||
|
Speed Speed
|
||||||
|
// The pyhsical port on the parent hub on which the device is connected.
|
||||||
|
// Ports are numbered from 1, excepting root hub devices which are always 0.
|
||||||
|
Port int
|
||||||
|
// Physical path of connected parent ports, starting at the root hub device.
|
||||||
|
// A path length of 0 represents a root hub device,
|
||||||
|
// a path length of 1 represents a device directly connected to a root hub,
|
||||||
|
// a path length of 2 or more are connected to intermediate hub devices.
|
||||||
|
// e.g. [1,2,3] represents a device connected to port 3 of a hub connected
|
||||||
|
// to port 2 of a hub connected to port 1 of a root hub.
|
||||||
|
Path []int
|
||||||
|
|
||||||
// Version information
|
// Version information
|
||||||
Spec BCD // USB Specification Release Number
|
Spec BCD // USB Specification Release Number
|
||||||
@@ -209,7 +220,7 @@ func (d *Device) Close() error {
|
|||||||
if d.claimed != nil {
|
if d.claimed != nil {
|
||||||
return fmt.Errorf("can't release the device %s, it has an open config %d", d, d.claimed.Desc.Number)
|
return fmt.Errorf("can't release the device %s, it has an open config %d", d, d.claimed.Desc.Number)
|
||||||
}
|
}
|
||||||
d.ctx.libusb.close(d.handle)
|
d.ctx.closeDev(d)
|
||||||
d.handle = nil
|
d.handle = nil
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -265,9 +276,13 @@ func (d *Device) InterfaceDescription(cfgNum, intfNum, altNum int) (string, erro
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("%s: %v", d, err)
|
return "", fmt.Errorf("%s: %v", d, err)
|
||||||
}
|
}
|
||||||
alt, err := cfg.intfDesc(intfNum, altNum)
|
intf, err := cfg.intfDesc(intfNum)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("%s, configuration %d: %v", d, cfgNum, err)
|
return "", fmt.Errorf("%s, configuration %d interface %d: %v", d, cfgNum, intfNum, err)
|
||||||
|
}
|
||||||
|
alt, err := intf.altSetting(altNum)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("%s, configuration %d interface %d alternate setting %d: %v", d, cfgNum, intfNum, altNum, err)
|
||||||
}
|
}
|
||||||
return d.GetStringDescriptor(alt.iInterface)
|
return d.GetStringDescriptor(alt.iInterface)
|
||||||
}
|
}
|
||||||
|
@@ -117,18 +117,18 @@ func TestClaimAndRelease(t *testing.T) {
|
|||||||
t.Errorf("%s.InEndpoint(%d): got %+v, want %+v", intf, ep1Addr, got, want)
|
t.Errorf("%s.InEndpoint(%d): got %+v, want %+v", intf, ep1Addr, got, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := cfg.Interface(1, 0); err == nil {
|
if _, err := cfg.Interface(if1Num, 0); err == nil {
|
||||||
t.Fatalf("%s.Interface(1, 0): got nil, want non nil, because Interface 1 is already claimed.", cfg)
|
t.Fatalf("%s.Interface(1, 0): got nil, want non nil, because Interface 1 is already claimed.", cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// intf2 is interface #0, not claimed yet.
|
// intf2 is interface #0, not claimed yet.
|
||||||
intf2, err := cfg.Interface(0, 0)
|
intf2, err := cfg.Interface(if2Num, alt2Num)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("%s.Interface(0, 0): got %v, want nil", cfg, err)
|
t.Fatalf("%s.Interface(0, 0): got %v, want nil", cfg, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := cfg.Close(); err == nil {
|
if err := cfg.Close(); err == nil {
|
||||||
t.Fatalf("%s.Close(): got nil, want non nil, because the Interface was not released.", cfg)
|
t.Fatalf("%s.Close(): got nil, want non nil, because the Interfaces #1/2 was not released.", cfg)
|
||||||
}
|
}
|
||||||
if err := dev.Close(); err == nil {
|
if err := dev.Close(); err == nil {
|
||||||
t.Fatalf("%s.Close(): got nil, want non nil, because the Config was not released.", cfg)
|
t.Fatalf("%s.Close(): got nil, want non nil, because the Config was not released.", cfg)
|
||||||
@@ -136,7 +136,7 @@ func TestClaimAndRelease(t *testing.T) {
|
|||||||
|
|
||||||
intf.Close()
|
intf.Close()
|
||||||
if err := cfg.Close(); err == nil {
|
if err := cfg.Close(); err == nil {
|
||||||
t.Fatalf("%s.Close(): got nil, want non nil, because the Interface was not released.", cfg)
|
t.Fatalf("%s.Close(): got nil, want non nil, because the Interface #2 was not released.", cfg)
|
||||||
}
|
}
|
||||||
if err := dev.Close(); err == nil {
|
if err := dev.Close(); err == nil {
|
||||||
t.Fatalf("%s.Close(): got nil, want non nil, because the Config was not released.", dev)
|
t.Fatalf("%s.Close(): got nil, want non nil, because the Config was not released.", dev)
|
||||||
@@ -244,3 +244,61 @@ func TestAutoDetachFailure(t *testing.T) {
|
|||||||
t.Fatalf("%s.Config(1) got nil, but want no nil because interface fails to detach", dev)
|
t.Fatalf("%s.Config(1) got nil, but want no nil because interface fails to detach", dev)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestInterface(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
c := newContextWithImpl(newFakeLibusb())
|
||||||
|
defer func() {
|
||||||
|
if err := c.Close(); err != nil {
|
||||||
|
t.Errorf("Context.Close: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
for _, tc := range []struct {
|
||||||
|
name string
|
||||||
|
vid, pid ID
|
||||||
|
cfgNum, ifNum, altNum int
|
||||||
|
}{{
|
||||||
|
name: "simple",
|
||||||
|
vid: 0x8888,
|
||||||
|
pid: 0x0002,
|
||||||
|
cfgNum: 1,
|
||||||
|
ifNum: 0,
|
||||||
|
altNum: 0,
|
||||||
|
}, {
|
||||||
|
name: "alt_setting",
|
||||||
|
vid: 0x8888,
|
||||||
|
pid: 0x0002,
|
||||||
|
cfgNum: 1,
|
||||||
|
ifNum: 1,
|
||||||
|
altNum: 1,
|
||||||
|
}, {
|
||||||
|
name: "noncontiguous_interfaces",
|
||||||
|
vid: 0x8888,
|
||||||
|
pid: 0x0002,
|
||||||
|
cfgNum: 1,
|
||||||
|
ifNum: 3,
|
||||||
|
altNum: 2,
|
||||||
|
}} {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
dev, err := c.OpenDeviceWithVIDPID(0x8888, 0x0002)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("OpenDeviceWithVIDPID(0x8888, 0x0002): %v", err)
|
||||||
|
}
|
||||||
|
if dev == nil {
|
||||||
|
t.Fatal("OpenDeviceWithVIDPID(0x8888, 0x0002): got nil device, need non-nil")
|
||||||
|
}
|
||||||
|
defer dev.Close()
|
||||||
|
cfg, err := dev.Config(tc.cfgNum)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%s.Config(%d): %v", dev, tc.cfgNum, err)
|
||||||
|
}
|
||||||
|
defer cfg.Close()
|
||||||
|
intf, err := cfg.Interface(tc.ifNum, tc.altNum)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%s.Interface(%d, %d): %v", cfg, tc.ifNum, tc.altNum, err)
|
||||||
|
}
|
||||||
|
intf.Close()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -16,9 +16,10 @@ package gousb
|
|||||||
|
|
||||||
// fake devices connected through the fakeLibusb stack.
|
// fake devices connected through the fakeLibusb stack.
|
||||||
type fakeDevice struct {
|
type fakeDevice struct {
|
||||||
devDesc *DeviceDesc
|
devDesc *DeviceDesc
|
||||||
strDesc map[int]string
|
strDesc map[int]string
|
||||||
alt uint8
|
alt uint8
|
||||||
|
sysDevPtr uintptr
|
||||||
}
|
}
|
||||||
|
|
||||||
var fakeDevices = []fakeDevice{
|
var fakeDevices = []fakeDevice{
|
||||||
@@ -64,6 +65,7 @@ var fakeDevices = []fakeDevice{
|
|||||||
}},
|
}},
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
|
sysDevPtr: 78,
|
||||||
},
|
},
|
||||||
// Bus 001 Device 002: ID 8888:0002
|
// Bus 001 Device 002: ID 8888:0002
|
||||||
// One config, two interfaces. interface #0 with no endpoints,
|
// One config, two interfaces. interface #0 with no endpoints,
|
||||||
@@ -186,6 +188,7 @@ var fakeDevices = []fakeDevice{
|
|||||||
8: "Slower streaming",
|
8: "Slower streaming",
|
||||||
9: "Interface for https://github.com/google/gousb/issues/65",
|
9: "Interface for https://github.com/google/gousb/issues/65",
|
||||||
},
|
},
|
||||||
|
sysDevPtr: 94,
|
||||||
},
|
},
|
||||||
// Bus 001 Device 003: ID 9999:0002
|
// Bus 001 Device 003: ID 9999:0002
|
||||||
// One config, one interface, one setup,
|
// One config, one interface, one setup,
|
||||||
|
@@ -85,8 +85,10 @@ func (t *fakeTransfer) setStatus(st TransferStatus) {
|
|||||||
// that allows the test to explicitly control individual transfer behavior.
|
// that allows the test to explicitly control individual transfer behavior.
|
||||||
type fakeLibusb struct {
|
type fakeLibusb struct {
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
// fakeDevices has a map of devices and their descriptors.
|
// devices has a map of devices and their descriptors.
|
||||||
fakeDevices map[*libusbDevice]*fakeDevice
|
devices map[*libusbDevice]*fakeDevice
|
||||||
|
// sysDevices keeps the order of devices to be accessd by wrapSysDevice
|
||||||
|
sysDevices map[uintptr]*libusbDevice
|
||||||
// ts has a map of all allocated transfers, indexed by the pointer of
|
// ts has a map of all allocated transfers, indexed by the pointer of
|
||||||
// underlying libusbTransfer.
|
// underlying libusbTransfer.
|
||||||
ts map[*libusbTransfer]*fakeTransfer
|
ts map[*libusbTransfer]*fakeTransfer
|
||||||
@@ -98,16 +100,32 @@ type fakeLibusb struct {
|
|||||||
claims map[*libusbDevice]map[uint8]bool
|
claims map[*libusbDevice]map[uint8]bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *fakeLibusb) init() (*libusbContext, error) { return new(libusbContext), nil }
|
func (f *fakeLibusb) init() (*libusbContext, error) { return newContextPointer(), nil }
|
||||||
func (f *fakeLibusb) handleEvents(c *libusbContext, done <-chan struct{}) { <-done }
|
func (f *fakeLibusb) handleEvents(c *libusbContext, done <-chan struct{}) { <-done }
|
||||||
func (f *fakeLibusb) getDevices(*libusbContext) ([]*libusbDevice, error) {
|
func (f *fakeLibusb) getDevices(*libusbContext) ([]*libusbDevice, error) {
|
||||||
ret := make([]*libusbDevice, 0, len(fakeDevices))
|
ret := make([]*libusbDevice, 0, len(fakeDevices))
|
||||||
for d := range f.fakeDevices {
|
for d := range f.devices {
|
||||||
ret = append(ret, d)
|
ret = append(ret, d)
|
||||||
}
|
}
|
||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *fakeLibusb) wrapSysDevice(ctx *libusbContext, systemDeviceHandle uintptr) (*libusbDevHandle, error) {
|
||||||
|
dev, ok := f.sysDevices[systemDeviceHandle]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("the passed file descriptor %d does not point to a valid device", systemDeviceHandle)
|
||||||
|
}
|
||||||
|
h := newDevHandlePointer()
|
||||||
|
f.mu.Lock()
|
||||||
|
defer f.mu.Unlock()
|
||||||
|
f.handles[h] = dev
|
||||||
|
return h, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *fakeLibusb) getDevice(handle *libusbDevHandle) *libusbDevice {
|
||||||
|
return f.handles[handle]
|
||||||
|
}
|
||||||
|
|
||||||
func (f *fakeLibusb) exit(*libusbContext) error {
|
func (f *fakeLibusb) exit(*libusbContext) error {
|
||||||
close(f.submitted)
|
close(f.submitted)
|
||||||
if got := len(f.ts); got > 0 {
|
if got := len(f.ts); got > 0 {
|
||||||
@@ -122,13 +140,13 @@ func (f *fakeLibusb) exit(*libusbContext) error {
|
|||||||
func (f *fakeLibusb) setDebug(*libusbContext, int) {}
|
func (f *fakeLibusb) setDebug(*libusbContext, int) {}
|
||||||
func (f *fakeLibusb) dereference(d *libusbDevice) {}
|
func (f *fakeLibusb) dereference(d *libusbDevice) {}
|
||||||
func (f *fakeLibusb) getDeviceDesc(d *libusbDevice) (*DeviceDesc, error) {
|
func (f *fakeLibusb) getDeviceDesc(d *libusbDevice) (*DeviceDesc, error) {
|
||||||
if dev, ok := f.fakeDevices[d]; ok {
|
if dev, ok := f.devices[d]; ok {
|
||||||
return dev.devDesc, nil
|
return dev.devDesc, nil
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("invalid USB device %p", d)
|
return nil, fmt.Errorf("invalid USB device %p", d)
|
||||||
}
|
}
|
||||||
func (f *fakeLibusb) open(d *libusbDevice) (*libusbDevHandle, error) {
|
func (f *fakeLibusb) open(d *libusbDevice) (*libusbDevHandle, error) {
|
||||||
h := new(libusbDevHandle)
|
h := newDevHandlePointer()
|
||||||
f.mu.Lock()
|
f.mu.Lock()
|
||||||
defer f.mu.Unlock()
|
defer f.mu.Unlock()
|
||||||
f.handles[h] = d
|
f.handles[h] = d
|
||||||
@@ -158,7 +176,7 @@ func (f *fakeLibusb) setConfig(d *libusbDevHandle, cfg uint8) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func (f *fakeLibusb) getStringDesc(d *libusbDevHandle, index int) (string, error) {
|
func (f *fakeLibusb) getStringDesc(d *libusbDevHandle, index int) (string, error) {
|
||||||
dev, ok := f.fakeDevices[f.handles[d]]
|
dev, ok := f.devices[f.handles[d]]
|
||||||
if !ok {
|
if !ok {
|
||||||
return "", fmt.Errorf("invalid USB device %p", d)
|
return "", fmt.Errorf("invalid USB device %p", d)
|
||||||
}
|
}
|
||||||
@@ -201,7 +219,7 @@ func (f *fakeLibusb) setAlt(d *libusbDevHandle, intf, alt uint8) error {
|
|||||||
if !f.claims[f.handles[d]][intf] {
|
if !f.claims[f.handles[d]][intf] {
|
||||||
return fmt.Errorf("interface %d must be claimed before alt setup can be set", intf)
|
return fmt.Errorf("interface %d must be claimed before alt setup can be set", intf)
|
||||||
}
|
}
|
||||||
f.fakeDevices[f.handles[d]].alt = alt
|
f.devices[f.handles[d]].alt = alt
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -288,11 +306,12 @@ func (f *fakeLibusb) empty() bool {
|
|||||||
|
|
||||||
func newFakeLibusb() *fakeLibusb {
|
func newFakeLibusb() *fakeLibusb {
|
||||||
fl := &fakeLibusb{
|
fl := &fakeLibusb{
|
||||||
fakeDevices: make(map[*libusbDevice]*fakeDevice),
|
devices: make(map[*libusbDevice]*fakeDevice),
|
||||||
ts: make(map[*libusbTransfer]*fakeTransfer),
|
sysDevices: make(map[uintptr]*libusbDevice),
|
||||||
submitted: make(chan *fakeTransfer, 10),
|
ts: make(map[*libusbTransfer]*fakeTransfer),
|
||||||
handles: make(map[*libusbDevHandle]*libusbDevice),
|
submitted: make(chan *fakeTransfer, 10),
|
||||||
claims: make(map[*libusbDevice]map[uint8]bool),
|
handles: make(map[*libusbDevHandle]*libusbDevice),
|
||||||
|
claims: make(map[*libusbDevice]map[uint8]bool),
|
||||||
}
|
}
|
||||||
for _, d := range fakeDevices {
|
for _, d := range fakeDevices {
|
||||||
// libusb does not export a way to allocate a new libusb_device struct
|
// libusb does not export a way to allocate a new libusb_device struct
|
||||||
@@ -301,7 +320,11 @@ func newFakeLibusb() *fakeLibusb {
|
|||||||
// The contents of these pointers is never accessed.
|
// The contents of these pointers is never accessed.
|
||||||
fd := new(fakeDevice)
|
fd := new(fakeDevice)
|
||||||
*fd = d
|
*fd = d
|
||||||
fl.fakeDevices[newDevicePointer()] = fd
|
devPointer := newDevicePointer()
|
||||||
|
fl.devices[devPointer] = fd
|
||||||
|
if fd.sysDevPtr != 0 { // the sysDevPtr not being set in the fakeDevices list
|
||||||
|
fl.sysDevices[fd.sysDevPtr] = devPointer
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return fl
|
return fl
|
||||||
}
|
}
|
||||||
|
6
go.mod
Normal file
6
go.mod
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
module github.com/google/gousb
|
||||||
|
|
||||||
|
go 1.16
|
||||||
|
|
||||||
|
// Released in error, v1.* remains the current version.
|
||||||
|
retract v2.1.0+incompatible
|
11
interface.go
11
interface.go
@@ -29,6 +29,17 @@ type InterfaceDesc struct {
|
|||||||
AltSettings []InterfaceSetting
|
AltSettings []InterfaceSetting
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *InterfaceDesc) altSetting(alt int) (*InterfaceSetting, error) {
|
||||||
|
alts := make([]int, len(i.AltSettings))
|
||||||
|
for a, s := range i.AltSettings {
|
||||||
|
if s.Alternate == alt {
|
||||||
|
return &s, nil
|
||||||
|
}
|
||||||
|
alts[a] = s.Alternate
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("alternate setting %d not found for %s, available alt settings: %v", alt, i, alts)
|
||||||
|
}
|
||||||
|
|
||||||
// String returns a human-readable description of the interface descriptor and
|
// String returns a human-readable description of the interface descriptor and
|
||||||
// its alternate settings.
|
// its alternate settings.
|
||||||
func (i InterfaceDesc) String() string {
|
func (i InterfaceDesc) String() string {
|
||||||
|
65
libusb.go
65
libusb.go
@@ -143,6 +143,7 @@ type libusbIntf interface {
|
|||||||
dereference(*libusbDevice)
|
dereference(*libusbDevice)
|
||||||
getDeviceDesc(*libusbDevice) (*DeviceDesc, error)
|
getDeviceDesc(*libusbDevice) (*DeviceDesc, error)
|
||||||
open(*libusbDevice) (*libusbDevHandle, error)
|
open(*libusbDevice) (*libusbDevHandle, error)
|
||||||
|
wrapSysDevice(*libusbContext, uintptr) (*libusbDevHandle, error)
|
||||||
|
|
||||||
close(*libusbDevHandle)
|
close(*libusbDevHandle)
|
||||||
reset(*libusbDevHandle) error
|
reset(*libusbDevHandle) error
|
||||||
@@ -152,6 +153,7 @@ type libusbIntf interface {
|
|||||||
getStringDesc(*libusbDevHandle, int) (string, error)
|
getStringDesc(*libusbDevHandle, int) (string, error)
|
||||||
setAutoDetach(*libusbDevHandle, int) error
|
setAutoDetach(*libusbDevHandle, int) error
|
||||||
detachKernelDriver(*libusbDevHandle, uint8) error
|
detachKernelDriver(*libusbDevHandle, uint8) error
|
||||||
|
getDevice(*libusbDevHandle) *libusbDevice
|
||||||
|
|
||||||
// interface
|
// interface
|
||||||
claim(*libusbDevHandle, uint8) error
|
claim(*libusbDevHandle, uint8) error
|
||||||
@@ -169,13 +171,24 @@ type libusbIntf interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// libusbImpl is an implementation of libusbIntf using real CGo-wrapped libusb.
|
// libusbImpl is an implementation of libusbIntf using real CGo-wrapped libusb.
|
||||||
type libusbImpl struct{}
|
type libusbImpl struct {
|
||||||
|
discovery DeviceDiscovery
|
||||||
|
}
|
||||||
|
|
||||||
func (libusbImpl) init() (*libusbContext, error) {
|
func (impl libusbImpl) init() (*libusbContext, error) {
|
||||||
var ctx *C.libusb_context
|
var ctx *C.libusb_context
|
||||||
if err := fromErrNo(C.libusb_init(&ctx)); err != nil {
|
|
||||||
|
var libusbOpts [4]C.struct_libusb_init_option // fixed to 4 - there are maximum 4 options
|
||||||
|
nOpts := 0
|
||||||
|
if impl.discovery == DisableDeviceDiscovery {
|
||||||
|
libusbOpts[nOpts].option = C.LIBUSB_OPTION_NO_DEVICE_DISCOVERY
|
||||||
|
nOpts++
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := fromErrNo(C.libusb_init_context(&ctx, &(libusbOpts[0]), C.int(nOpts))); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return (*libusbContext)(ctx), nil
|
return (*libusbContext)(ctx), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,7 +201,10 @@ func (libusbImpl) handleEvents(c *libusbContext, done <-chan struct{}) {
|
|||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
if errno := C.libusb_handle_events_timeout_completed((*C.libusb_context)(c), &tv, nil); errno < 0 {
|
if errno := C.libusb_handle_events_timeout_completed((*C.libusb_context)(c), &tv, nil); errno < 0 {
|
||||||
log.Printf("handle_events: error: %s", Error(errno))
|
// handler can be interrupted by a signal and this doesn't indicate an error, we'll retry on the next loop iteration
|
||||||
|
if Error(errno) != ErrorInterrupted {
|
||||||
|
log.Printf("handle_events: error: %s", Error(errno))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -209,11 +225,25 @@ func (libusbImpl) getDevices(ctx *libusbContext) ([]*libusbDevice, error) {
|
|||||||
for _, d := range devs {
|
for _, d := range devs {
|
||||||
ret = append(ret, (*libusbDevice)(d))
|
ret = append(ret, (*libusbDevice)(d))
|
||||||
}
|
}
|
||||||
// devices will be dereferenced later, during close.
|
// devices must be dereferenced by the caller to prevent memory leaks.
|
||||||
C.libusb_free_device_list(list, 0)
|
C.libusb_free_device_list(list, 0)
|
||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (libusbImpl) wrapSysDevice(ctx *libusbContext, fd uintptr) (*libusbDevHandle, error) {
|
||||||
|
var handle *C.libusb_device_handle
|
||||||
|
if ret := C.libusb_wrap_sys_device((*C.libusb_context)(ctx), C.intptr_t(fd), &handle); ret < 0 {
|
||||||
|
return nil, fromErrNo(C.int(ret))
|
||||||
|
}
|
||||||
|
|
||||||
|
return (*libusbDevHandle)(handle), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (libusbImpl) getDevice(d *libusbDevHandle) *libusbDevice {
|
||||||
|
device := C.libusb_get_device((*C.libusb_device_handle)(d))
|
||||||
|
return (*libusbDevice)(device)
|
||||||
|
}
|
||||||
|
|
||||||
func (libusbImpl) exit(c *libusbContext) error {
|
func (libusbImpl) exit(c *libusbContext) error {
|
||||||
C.libusb_exit((*C.libusb_context)(c))
|
C.libusb_exit((*C.libusb_context)(c))
|
||||||
return nil
|
return nil
|
||||||
@@ -224,14 +254,26 @@ func (libusbImpl) setDebug(c *libusbContext, lvl int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (libusbImpl) getDeviceDesc(d *libusbDevice) (*DeviceDesc, error) {
|
func (libusbImpl) getDeviceDesc(d *libusbDevice) (*DeviceDesc, error) {
|
||||||
var desc C.struct_libusb_device_descriptor
|
var (
|
||||||
|
desc C.struct_libusb_device_descriptor
|
||||||
|
pathData [8]uint8
|
||||||
|
path []int
|
||||||
|
port int
|
||||||
|
)
|
||||||
if err := fromErrNo(C.libusb_get_device_descriptor((*C.libusb_device)(d), &desc)); err != nil {
|
if err := fromErrNo(C.libusb_get_device_descriptor((*C.libusb_device)(d), &desc)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
pathLen := int(C.libusb_get_port_numbers((*C.libusb_device)(d), (*C.uint8_t)(&pathData[0]), 8))
|
||||||
|
for _, nPort := range pathData[:pathLen] {
|
||||||
|
port = int(nPort)
|
||||||
|
path = append(path, port)
|
||||||
|
}
|
||||||
|
// Defaults to port = 0, path = [] for root device
|
||||||
dev := &DeviceDesc{
|
dev := &DeviceDesc{
|
||||||
Bus: int(C.libusb_get_bus_number((*C.libusb_device)(d))),
|
Bus: int(C.libusb_get_bus_number((*C.libusb_device)(d))),
|
||||||
Address: int(C.libusb_get_device_address((*C.libusb_device)(d))),
|
Address: int(C.libusb_get_device_address((*C.libusb_device)(d))),
|
||||||
Port: int(C.libusb_get_port_number((*C.libusb_device)(d))),
|
Port: port,
|
||||||
|
Path: path,
|
||||||
Speed: Speed(C.libusb_get_device_speed((*C.libusb_device)(d))),
|
Speed: Speed(C.libusb_get_device_speed((*C.libusb_device)(d))),
|
||||||
Spec: BCD(desc.bcdUSB),
|
Spec: BCD(desc.bcdUSB),
|
||||||
Device: BCD(desc.bcdDevice),
|
Device: BCD(desc.bcdDevice),
|
||||||
@@ -508,6 +550,7 @@ func libusbSetDebug(c *libusbContext, lvl int) {
|
|||||||
C.gousb_set_debug((*C.libusb_context)(c), C.int(lvl))
|
C.gousb_set_debug((*C.libusb_context)(c), C.int(lvl))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// for obtaining unique CGo pointers.
|
||||||
func newDevicePointer() *libusbDevice {
|
func newDevicePointer() *libusbDevice {
|
||||||
return (*libusbDevice)(unsafe.Pointer(C.malloc(1)))
|
return (*libusbDevice)(unsafe.Pointer(C.malloc(1)))
|
||||||
}
|
}
|
||||||
@@ -515,3 +558,11 @@ func newDevicePointer() *libusbDevice {
|
|||||||
func newFakeTransferPointer() *libusbTransfer {
|
func newFakeTransferPointer() *libusbTransfer {
|
||||||
return (*libusbTransfer)(unsafe.Pointer(C.malloc(1)))
|
return (*libusbTransfer)(unsafe.Pointer(C.malloc(1)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newContextPointer() *libusbContext {
|
||||||
|
return (*libusbContext)(unsafe.Pointer(C.malloc(1)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func newDevHandlePointer() *libusbDevHandle {
|
||||||
|
return (*libusbDevHandle)(unsafe.Pointer(C.malloc(1)))
|
||||||
|
}
|
||||||
|
@@ -269,14 +269,15 @@ func (w *WriteStream) Close() error {
|
|||||||
return w.CloseContext(context.Background())
|
return w.CloseContext(context.Background())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close signals end of data to write. Close blocks until all transfers
|
// CloseContext signals end of data to write. CloseContext blocks until all
|
||||||
// that were sent are finished. The error returned by Close is the first
|
// transfers that were sent are finished or until the context is canceled. The
|
||||||
// error encountered during writing the entire stream (if any).
|
// error returned by CloseContext is the first error encountered during writing
|
||||||
// Close returning nil indicates all transfers completed successfully.
|
// the entire stream (if any).
|
||||||
// After Close, the total number of bytes successfully written can be
|
// CloseContext returning nil indicates all transfers completed successfully.
|
||||||
|
// After CloseContext, the total number of bytes successfully written can be
|
||||||
// retrieved using Written().
|
// retrieved using Written().
|
||||||
// Close may not be called concurrently with Write, Close or Written.
|
// CloseContext may not be called concurrently with Write, WriteContext, Close,
|
||||||
// CloseContext
|
// CloseContext or Written.
|
||||||
func (w *WriteStream) CloseContext(ctx context.Context) error {
|
func (w *WriteStream) CloseContext(ctx context.Context) error {
|
||||||
if w.s.transfers == nil {
|
if w.s.transfers == nil {
|
||||||
return io.ErrClosedPipe
|
return io.ErrClosedPipe
|
||||||
|
144
usb.go
144
usb.go
@@ -16,7 +16,7 @@
|
|||||||
/*
|
/*
|
||||||
Package gousb provides an low-level interface to attached USB devices.
|
Package gousb provides an low-level interface to attached USB devices.
|
||||||
|
|
||||||
A Short Tutorial
|
# A Short Tutorial
|
||||||
|
|
||||||
A Context manages all resources necessary for communicating with USB
|
A Context manages all resources necessary for communicating with USB
|
||||||
devices.
|
devices.
|
||||||
@@ -117,19 +117,27 @@ standard for control commands though, and many devices implement their custom co
|
|||||||
|
|
||||||
Control commands can be issued through Device.Control().
|
Control commands can be issued through Device.Control().
|
||||||
|
|
||||||
See Also
|
# See Also
|
||||||
|
|
||||||
For more information about USB protocol and handling USB devices,
|
For more information about USB protocol and handling USB devices,
|
||||||
see the excellent "USB in a nutshell" guide: http://www.beyondlogic.org/usbnutshell/
|
see the excellent "USB in a nutshell" guide: http://www.beyondlogic.org/usbnutshell/
|
||||||
|
|
||||||
*/
|
*/
|
||||||
package gousb
|
package gousb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
// Context manages all resources related to USB device handling.
|
// Context manages all resources related to USB device handling.
|
||||||
type Context struct {
|
type Context struct {
|
||||||
ctx *libusbContext
|
ctx *libusbContext
|
||||||
done chan struct{}
|
done chan struct{}
|
||||||
libusb libusbIntf
|
libusb libusbIntf
|
||||||
|
|
||||||
|
mu sync.Mutex
|
||||||
|
devices map[*Device]bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debug changes the debug level. Level 0 means no debug, higher levels
|
// Debug changes the debug level. Level 0 means no debug, higher levels
|
||||||
@@ -146,17 +154,45 @@ func newContextWithImpl(impl libusbIntf) *Context {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
ctx := &Context{
|
ctx := &Context{
|
||||||
ctx: c,
|
ctx: c,
|
||||||
done: make(chan struct{}),
|
done: make(chan struct{}),
|
||||||
libusb: impl,
|
libusb: impl,
|
||||||
|
devices: make(map[*Device]bool),
|
||||||
}
|
}
|
||||||
go impl.handleEvents(ctx.ctx, ctx.done)
|
go impl.handleEvents(ctx.ctx, ctx.done)
|
||||||
return ctx
|
return ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewContext returns a new Context instance.
|
// NewContext returns a new Context instance with default ContextOptions.
|
||||||
func NewContext() *Context {
|
func NewContext() *Context {
|
||||||
return newContextWithImpl(libusbImpl{})
|
return ContextOptions{}.New()
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeviceDiscovery controls USB device discovery.
|
||||||
|
type DeviceDiscovery int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// EnableDeviceDiscovery means the connected USB devices will be enumerated
|
||||||
|
// on Context initialization. This enables the use of OpenDevices and
|
||||||
|
// OpenWithVIDPID. This is the default.
|
||||||
|
EnableDeviceDiscovery = iota
|
||||||
|
// DisableDeviceDiscovery means the USB devices are not enumerated and
|
||||||
|
// OpenDevices will not return any devices.
|
||||||
|
// Without device discovery, OpenDeviceWithFileDescriptor can be used
|
||||||
|
// to open devices.
|
||||||
|
DisableDeviceDiscovery
|
||||||
|
)
|
||||||
|
|
||||||
|
// ContextOptions holds parameters for Context initialization.
|
||||||
|
type ContextOptions struct {
|
||||||
|
DeviceDiscovery DeviceDiscovery
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a Context, taking into account the optional flags contained in ContextOptions
|
||||||
|
func (o ContextOptions) New() *Context {
|
||||||
|
return newContextWithImpl(libusbImpl{
|
||||||
|
discovery: o.DeviceDiscovery,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// OpenDevices calls opener with each enumerated device.
|
// OpenDevices calls opener with each enumerated device.
|
||||||
@@ -165,6 +201,9 @@ func NewContext() *Context {
|
|||||||
// If there are any errors enumerating the devices,
|
// If there are any errors enumerating the devices,
|
||||||
// the final one is returned along with any successfully opened devices.
|
// the final one is returned along with any successfully opened devices.
|
||||||
func (c *Context) OpenDevices(opener func(desc *DeviceDesc) bool) ([]*Device, error) {
|
func (c *Context) OpenDevices(opener func(desc *DeviceDesc) bool) ([]*Device, error) {
|
||||||
|
if c.ctx == nil {
|
||||||
|
return nil, errors.New("OpenDevices called on a closed or uninitialized Context")
|
||||||
|
}
|
||||||
list, err := c.libusb.getDevices(c.ctx)
|
list, err := c.libusb.getDevices(c.ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -174,27 +213,65 @@ func (c *Context) OpenDevices(opener func(desc *DeviceDesc) bool) ([]*Device, er
|
|||||||
var ret []*Device
|
var ret []*Device
|
||||||
for _, dev := range list {
|
for _, dev := range list {
|
||||||
desc, err := c.libusb.getDeviceDesc(dev)
|
desc, err := c.libusb.getDeviceDesc(dev)
|
||||||
|
defer c.libusb.dereference(dev)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.libusb.dereference(dev)
|
|
||||||
reterr = err
|
reterr = err
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if opener(desc) {
|
if !opener(desc) {
|
||||||
handle, err := c.libusb.open(dev)
|
continue
|
||||||
if err != nil {
|
|
||||||
c.libusb.dereference(dev)
|
|
||||||
reterr = err
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
ret = append(ret, &Device{handle: handle, ctx: c, Desc: desc})
|
|
||||||
} else {
|
|
||||||
c.libusb.dereference(dev)
|
|
||||||
}
|
}
|
||||||
|
handle, err := c.libusb.open(dev)
|
||||||
|
if err != nil {
|
||||||
|
reterr = err
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
o := &Device{handle: handle, ctx: c, Desc: desc}
|
||||||
|
ret = append(ret, o)
|
||||||
|
c.mu.Lock()
|
||||||
|
c.devices[o] = true
|
||||||
|
c.mu.Unlock()
|
||||||
|
|
||||||
}
|
}
|
||||||
return ret, reterr
|
return ret, reterr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OpenDeviceWithFileDescriptor takes a (Unix) file descriptor of an opened USB
|
||||||
|
// device and wraps the library around it.
|
||||||
|
// This is particularly useful when working on Android, where the USB device can be
|
||||||
|
// opened by the SDK (Java), giving access to the device through the file descriptor
|
||||||
|
// (https://developer.android.com/reference/android/hardware/usb/UsbDeviceConnection#getFileDescriptor()).
|
||||||
|
//
|
||||||
|
// Do note that for this to work the automatic device discovery must be disabled
|
||||||
|
// at the time when the new Context is created, through the use of
|
||||||
|
// ContextOptions.DeviceDiscovery.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// ctx := ContextOptions{DeviceDiscovery: DisableDeviceDiscovery}.New()
|
||||||
|
// device, err := ctx.OpenDeviceWithFileDescriptor(fd)
|
||||||
|
//
|
||||||
|
// An error is returned in case the file descriptor is not valid.
|
||||||
|
func (c *Context) OpenDeviceWithFileDescriptor(fd uintptr) (*Device, error) {
|
||||||
|
handle, err := c.libusb.wrapSysDevice(c.ctx, fd)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
dev := c.libusb.getDevice(handle)
|
||||||
|
desc, err := c.libusb.getDeviceDesc(dev)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("device was opened, but getting device descriptor failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
o := &Device{handle: handle, ctx: c, Desc: desc}
|
||||||
|
c.mu.Lock()
|
||||||
|
c.devices[o] = true
|
||||||
|
c.mu.Unlock()
|
||||||
|
|
||||||
|
return o, nil
|
||||||
|
}
|
||||||
|
|
||||||
// OpenDeviceWithVIDPID opens Device from specific VendorId and ProductId.
|
// OpenDeviceWithVIDPID opens Device from specific VendorId and ProductId.
|
||||||
// If none is found, it returns nil and nil error. If there are multiple devices
|
// If none is found, it returns nil and nil error. If there are multiple devices
|
||||||
// with the same VID/PID, it will return one of them, picked arbitrarily.
|
// with the same VID/PID, it will return one of them, picked arbitrarily.
|
||||||
@@ -219,13 +296,32 @@ func (c *Context) OpenDeviceWithVIDPID(vid, pid ID) (*Device, error) {
|
|||||||
return devs[0], nil
|
return devs[0], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Context) closeDev(d *Device) {
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
c.libusb.close(d.handle)
|
||||||
|
delete(c.devices, d)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) checkOpenDevs() error {
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
if l := len(c.devices); l > 0 {
|
||||||
|
return fmt.Errorf("Context.Close called while %d Devices are still open, Close may be called only after all previously opened devices were successfuly closed", l)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Close releases the Context and all associated resources.
|
// Close releases the Context and all associated resources.
|
||||||
func (c *Context) Close() error {
|
func (c *Context) Close() error {
|
||||||
var ret error
|
if c.ctx == nil {
|
||||||
c.done <- struct{}{}
|
return nil
|
||||||
if c.ctx != nil {
|
|
||||||
ret = c.libusb.exit(c.ctx)
|
|
||||||
}
|
}
|
||||||
|
if err := c.checkOpenDevs(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.done <- struct{}{}
|
||||||
|
err := c.libusb.exit(c.ctx)
|
||||||
c.ctx = nil
|
c.ctx = nil
|
||||||
return ret
|
return err
|
||||||
}
|
}
|
||||||
|
46
usb_test.go
46
usb_test.go
@@ -41,6 +41,11 @@ func TestOPenDevices(t *testing.T) {
|
|||||||
t.Fatalf("OpenDevices(): %s", err)
|
t.Fatalf("OpenDevices(): %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// attempt to Close() should fail because of open devices
|
||||||
|
if err := c.Close(); err == nil {
|
||||||
|
t.Fatal("Context.Close succeeded while some devices were still open")
|
||||||
|
}
|
||||||
|
|
||||||
if got, want := len(devs), len(fakeDevices); got != want {
|
if got, want := len(devs), len(fakeDevices); got != want {
|
||||||
t.Fatalf("len(devs) = %d, want %d (based on num fake devs)", got, want)
|
t.Fatalf("len(devs) = %d, want %d (based on num fake devs)", got, want)
|
||||||
}
|
}
|
||||||
@@ -89,3 +94,44 @@ func TestOpenDeviceWithVIDPID(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestOpenDeviceWithFileDescriptor(t *testing.T) {
|
||||||
|
ctx := newContextWithImpl(newFakeLibusb())
|
||||||
|
defer ctx.Close()
|
||||||
|
|
||||||
|
// file descriptor is an index to the FakeDevices array
|
||||||
|
for _, d := range []struct {
|
||||||
|
vid, pid ID
|
||||||
|
sysDevPtr uintptr
|
||||||
|
}{
|
||||||
|
{0x9999, 0x0001, 78},
|
||||||
|
{0x8888, 0x0002, 94},
|
||||||
|
} {
|
||||||
|
dev, err := ctx.OpenDeviceWithFileDescriptor(d.sysDevPtr)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("OpenDeviceWithFileDescriptor(%d): err != nil for a valid device: %v", d.sysDevPtr, err)
|
||||||
|
}
|
||||||
|
if dev == nil {
|
||||||
|
t.Fatalf("OpenDeviceWithFileDescriptor(%d): device == nil for a valid device", d.sysDevPtr)
|
||||||
|
}
|
||||||
|
if dev != nil && (dev.Desc.Vendor != ID(d.vid) || dev.Desc.Product != ID(d.pid)) {
|
||||||
|
t.Errorf("OpenDeviceWithFileDescriptor(%d): device's VID/PID %s/%s don't match expected: %s/%s", d.sysDevPtr, dev.Desc.Vendor, dev.Desc.Product, ID(d.vid), ID(d.pid))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOpenDeviceWithFileDescriptorOnMissingDevice(t *testing.T) {
|
||||||
|
ctx := newContextWithImpl(newFakeLibusb())
|
||||||
|
defer ctx.Close()
|
||||||
|
|
||||||
|
for _, sysDevPtr := range []uintptr{
|
||||||
|
7, // set, but does not exist in the fakeDevices array
|
||||||
|
0, // unset
|
||||||
|
} {
|
||||||
|
if _, err := ctx.OpenDeviceWithFileDescriptor(sysDevPtr); err == nil {
|
||||||
|
t.Errorf("OpenDeviceWithFileDescriptor(%d): got nil error for invalid device", sysDevPtr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
@@ -20,8 +20,9 @@
|
|||||||
// a reader.
|
// a reader.
|
||||||
//
|
//
|
||||||
// The bread and butter of this package are the following two functions:
|
// The bread and butter of this package are the following two functions:
|
||||||
// Describe - Pretty-print the vendor and product of a device descriptor
|
//
|
||||||
// Classify - Pretty-print the class/protocol info for a device/interface
|
// Describe - Pretty-print the vendor and product of a device descriptor
|
||||||
|
// Classify - Pretty-print the class/protocol info for a device/interface
|
||||||
package usbid
|
package usbid
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@@ -20,7 +20,8 @@ import "time"
|
|||||||
// LastUpdate stores the latest time that the library was updated.
|
// LastUpdate stores the latest time that the library was updated.
|
||||||
//
|
//
|
||||||
// The baked-in data was last generated:
|
// The baked-in data was last generated:
|
||||||
// 2017-03-10 09:09:14.940548227 -0500 EST
|
//
|
||||||
|
// 2017-03-10 09:09:14.940548227 -0500 EST
|
||||||
var LastUpdate = time.Unix(0, 1489154954940548227)
|
var LastUpdate = time.Unix(0, 1489154954940548227)
|
||||||
|
|
||||||
const usbIDListData = `#
|
const usbIDListData = `#
|
||||||
|
Reference in New Issue
Block a user