Parallelize tests (#17)
* Store a reference to libusb implementation in the context, transfers and some more places. Remove the global libusb variable. * Parallelize tests. * Fix the link in README.
This commit is contained in:
@@ -13,7 +13,8 @@ Supported platforms include:
|
|||||||
- darwin
|
- darwin
|
||||||
- windows
|
- windows
|
||||||
|
|
||||||
This is the release 2.0 of the package [github.com/kylelemons/gousb]. Its API is not backwards-compatible with version 1.0.
|
This is the release 2.0 of the package [github.com/kylelemons/gousb](https://github.com/kylelemons/gousb).
|
||||||
|
Its API is not backwards-compatible with version 1.0.
|
||||||
As of 2017-07-13 the 2.0 API is considered stable and 1.0 is deprecated.
|
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
|
||||||
|
@@ -120,12 +120,12 @@ func (c *Config) Interface(num, alt int) (*Interface, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Claim the interface
|
// Claim the interface
|
||||||
if err := libusb.claim(c.dev.handle, uint8(num)); err != nil {
|
if err := c.dev.ctx.libusb.claim(c.dev.handle, uint8(num)); err != nil {
|
||||||
return nil, fmt.Errorf("failed to claim interface %d on %s: %v", num, c, err)
|
return nil, fmt.Errorf("failed to claim interface %d on %s: %v", num, c, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := 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 {
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
17
device.go
17
device.go
@@ -80,6 +80,7 @@ func (d *DeviceDesc) cfgDesc(cfgNum int) (*ConfigDesc, error) {
|
|||||||
// A Device must be Close()d after use.
|
// A Device must be Close()d after use.
|
||||||
type Device struct {
|
type Device struct {
|
||||||
handle *libusbDevHandle
|
handle *libusbDevHandle
|
||||||
|
ctx *Context
|
||||||
|
|
||||||
// Embed the device information for easy access
|
// Embed the device information for easy access
|
||||||
Desc *DeviceDesc
|
Desc *DeviceDesc
|
||||||
@@ -109,7 +110,7 @@ func (d *Device) Reset() error {
|
|||||||
if d.claimed != nil {
|
if d.claimed != nil {
|
||||||
return fmt.Errorf("can't reset device %s while it has an active configuration %s", d, d.claimed)
|
return fmt.Errorf("can't reset device %s while it has an active configuration %s", d, d.claimed)
|
||||||
}
|
}
|
||||||
return libusb.reset(d.handle)
|
return d.ctx.libusb.reset(d.handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ActiveConfigNum returns the config id of the active configuration.
|
// ActiveConfigNum returns the config id of the active configuration.
|
||||||
@@ -119,7 +120,7 @@ func (d *Device) ActiveConfigNum() (int, error) {
|
|||||||
if d.handle == nil {
|
if d.handle == nil {
|
||||||
return 0, fmt.Errorf("ActiveConfig() called on %s after Close", d)
|
return 0, fmt.Errorf("ActiveConfig() called on %s after Close", d)
|
||||||
}
|
}
|
||||||
ret, err := libusb.getConfig(d.handle)
|
ret, err := d.ctx.libusb.getConfig(d.handle)
|
||||||
return int(ret), err
|
return int(ret), err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,7 +147,7 @@ func (d *Device) Config(cfgNum int) (*Config, error) {
|
|||||||
|
|
||||||
if d.autodetach {
|
if d.autodetach {
|
||||||
for _, iface := range cfg.Desc.Interfaces {
|
for _, iface := range cfg.Desc.Interfaces {
|
||||||
if err := libusb.detachKernelDriver(d.handle, uint8(iface.Number)); err != nil {
|
if err := d.ctx.libusb.detachKernelDriver(d.handle, uint8(iface.Number)); err != nil {
|
||||||
return nil, fmt.Errorf("Can't detach kernel driver of the device %s and interface %d: %v", d, iface.Number, err)
|
return nil, fmt.Errorf("Can't detach kernel driver of the device %s and interface %d: %v", d, iface.Number, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -155,7 +156,7 @@ func (d *Device) Config(cfgNum int) (*Config, error) {
|
|||||||
if activeCfgNum, err := d.ActiveConfigNum(); err != nil {
|
if activeCfgNum, err := d.ActiveConfigNum(); err != nil {
|
||||||
return nil, fmt.Errorf("failed to query active config of the device %s: %v", d, err)
|
return nil, fmt.Errorf("failed to query active config of the device %s: %v", d, err)
|
||||||
} else if cfgNum != activeCfgNum {
|
} else if cfgNum != activeCfgNum {
|
||||||
if err := libusb.setConfig(d.handle, uint8(cfgNum)); err != nil {
|
if err := d.ctx.libusb.setConfig(d.handle, uint8(cfgNum)); err != nil {
|
||||||
return nil, fmt.Errorf("failed to set active config %d for the device %s: %v", cfgNum, d, err)
|
return nil, fmt.Errorf("failed to set active config %d for the device %s: %v", cfgNum, d, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -194,7 +195,7 @@ func (d *Device) Control(rType, request uint8, val, idx uint16, data []byte) (in
|
|||||||
if d.handle == nil {
|
if d.handle == nil {
|
||||||
return 0, fmt.Errorf("Control() called on %s after Close", d)
|
return 0, fmt.Errorf("Control() called on %s after Close", d)
|
||||||
}
|
}
|
||||||
return libusb.control(d.handle, d.ControlTimeout, rType, request, val, idx, data)
|
return d.ctx.libusb.control(d.handle, d.ControlTimeout, rType, request, val, idx, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close closes the device.
|
// Close closes the device.
|
||||||
@@ -207,7 +208,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)
|
||||||
}
|
}
|
||||||
libusb.close(d.handle)
|
d.ctx.libusb.close(d.handle)
|
||||||
d.handle = nil
|
d.handle = nil
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -224,7 +225,7 @@ func (d *Device) GetStringDescriptor(descIndex int) (string, error) {
|
|||||||
if descIndex == 0 {
|
if descIndex == 0 {
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
return libusb.getStringDesc(d.handle, descIndex)
|
return d.ctx.libusb.getStringDesc(d.handle, descIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Manufacturer returns the device's manufacturer name.
|
// Manufacturer returns the device's manufacturer name.
|
||||||
@@ -283,5 +284,5 @@ func (d *Device) SetAutoDetach(autodetach bool) error {
|
|||||||
if autodetach {
|
if autodetach {
|
||||||
autodetachInt = 1
|
autodetachInt = 1
|
||||||
}
|
}
|
||||||
return libusb.setAutoDetach(d.handle, autodetachInt)
|
return d.ctx.libusb.setAutoDetach(d.handle, autodetachInt)
|
||||||
}
|
}
|
||||||
|
@@ -21,10 +21,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestClaimAndRelease(t *testing.T) {
|
func TestClaimAndRelease(t *testing.T) {
|
||||||
// Can't be parallelized, newFakeLibusb modifies a shared global state.
|
t.Parallel()
|
||||||
_, done := newFakeLibusb()
|
|
||||||
defer done()
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
devIdx = 1
|
devIdx = 1
|
||||||
cfgNum = 1
|
cfgNum = 1
|
||||||
@@ -34,8 +31,14 @@ func TestClaimAndRelease(t *testing.T) {
|
|||||||
alt2Num = 0
|
alt2Num = 0
|
||||||
if2Num = 0
|
if2Num = 0
|
||||||
)
|
)
|
||||||
c := NewContext()
|
|
||||||
defer c.Close()
|
c := newContextWithImpl(newFakeLibusb())
|
||||||
|
defer func() {
|
||||||
|
if err := c.Close(); err != nil {
|
||||||
|
t.Errorf("Context.Close: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
dev, err := c.OpenDeviceWithVIDPID(0x8888, 0x0002)
|
dev, err := c.OpenDeviceWithVIDPID(0x8888, 0x0002)
|
||||||
if dev == nil {
|
if dev == nil {
|
||||||
t.Fatal("OpenDeviceWithVIDPID(0x8888, 0x0002): got nil device, need non-nil")
|
t.Fatal("OpenDeviceWithVIDPID(0x8888, 0x0002): got nil device, need non-nil")
|
||||||
@@ -177,10 +180,7 @@ func TestClaimAndRelease(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestInterfaceDescriptionError(t *testing.T) {
|
func TestInterfaceDescriptionError(t *testing.T) {
|
||||||
// Can't be parallelized, newFakeLibusb modifies a shared global state.
|
t.Parallel()
|
||||||
_, done := newFakeLibusb()
|
|
||||||
defer done()
|
|
||||||
|
|
||||||
for _, tc := range []struct {
|
for _, tc := range []struct {
|
||||||
name string
|
name string
|
||||||
cfg, intf, alt int
|
cfg, intf, alt int
|
||||||
@@ -189,10 +189,15 @@ func TestInterfaceDescriptionError(t *testing.T) {
|
|||||||
{"no interface", 1, 3, 1},
|
{"no interface", 1, 3, 1},
|
||||||
{"no alt setting", 1, 1, 5},
|
{"no alt setting", 1, 1, 5},
|
||||||
} {
|
} {
|
||||||
|
tc := tc
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
// Can't be parallelized, depends on the shared global state set before the loop.
|
t.Parallel()
|
||||||
c := NewContext()
|
c := newContextWithImpl(newFakeLibusb())
|
||||||
defer c.Close()
|
defer func() {
|
||||||
|
if err := c.Close(); err != nil {
|
||||||
|
t.Errorf("Context.Close(): %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
dev, err := c.OpenDeviceWithVIDPID(0x8888, 0x0002)
|
dev, err := c.OpenDeviceWithVIDPID(0x8888, 0x0002)
|
||||||
if dev == nil {
|
if dev == nil {
|
||||||
t.Fatal("OpenDeviceWithVIDPID(0x8888, 0x0002): got nil device, need non-nil")
|
t.Fatal("OpenDeviceWithVIDPID(0x8888, 0x0002): got nil device, need non-nil")
|
||||||
@@ -220,12 +225,9 @@ func (*failDetachLib) detachKernelDriver(h *libusbDevHandle, i uint8) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestAutoDetachFailure(t *testing.T) {
|
func TestAutoDetachFailure(t *testing.T) {
|
||||||
// Can't be parallelized, newFakeLibusb modifies a shared global state.
|
t.Parallel()
|
||||||
fake, done := newFakeLibusb()
|
fake := newFakeLibusb()
|
||||||
defer done()
|
c := newContextWithImpl(&failDetachLib{fake})
|
||||||
libusb = &failDetachLib{fake}
|
|
||||||
|
|
||||||
c := NewContext()
|
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
dev, err := c.OpenDeviceWithVIDPID(0x8888, 0x0002)
|
dev, err := c.OpenDeviceWithVIDPID(0x8888, 0x0002)
|
||||||
if dev == nil {
|
if dev == nil {
|
||||||
|
@@ -77,6 +77,8 @@ type endpoint struct {
|
|||||||
Desc EndpointDesc
|
Desc EndpointDesc
|
||||||
|
|
||||||
Timeout time.Duration
|
Timeout time.Duration
|
||||||
|
|
||||||
|
ctx *Context
|
||||||
}
|
}
|
||||||
|
|
||||||
// String returns a human-readable description of the endpoint.
|
// String returns a human-readable description of the endpoint.
|
||||||
@@ -89,7 +91,7 @@ func (e *endpoint) transfer(buf []byte) (int, error) {
|
|||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
t, err := newUSBTransfer(e.h, &e.Desc, len(buf), e.Timeout)
|
t, err := newUSBTransfer(e.ctx, e.h, &e.Desc, len(buf), e.Timeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
@@ -17,7 +17,7 @@ package gousb
|
|||||||
func (e *endpoint) newStream(size, count int, submit bool) (*stream, error) {
|
func (e *endpoint) newStream(size, count int, submit bool) (*stream, error) {
|
||||||
var ts []transferIntf
|
var ts []transferIntf
|
||||||
for i := 0; i < count; i++ {
|
for i := 0; i < count; i++ {
|
||||||
t, err := newUSBTransfer(e.h, &e.Desc, size, e.Timeout)
|
t, err := newUSBTransfer(e.ctx, e.h, &e.Desc, size, e.Timeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
for _, t := range ts {
|
for _, t := range ts {
|
||||||
t.free()
|
t.free()
|
||||||
|
@@ -17,9 +17,14 @@ package gousb
|
|||||||
import "testing"
|
import "testing"
|
||||||
|
|
||||||
func TestEndpointReadStream(t *testing.T) {
|
func TestEndpointReadStream(t *testing.T) {
|
||||||
// Can't be parallelized, newFakeLibusb modifies a shared global state.
|
t.Parallel()
|
||||||
lib, done := newFakeLibusb()
|
lib := newFakeLibusb()
|
||||||
defer done()
|
ctx := newContextWithImpl(lib)
|
||||||
|
defer func() {
|
||||||
|
if err := ctx.Close(); err != nil {
|
||||||
|
t.Errorf("Context.Close: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
goodTransfers := 7
|
goodTransfers := 7
|
||||||
go func() {
|
go func() {
|
||||||
@@ -41,7 +46,6 @@ func TestEndpointReadStream(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
ctx := NewContext()
|
|
||||||
dev, err := ctx.OpenDeviceWithVIDPID(0x9999, 0x0001)
|
dev, err := ctx.OpenDeviceWithVIDPID(0x9999, 0x0001)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("OpenDeviceWithVIDPID(9999, 0001): %v", err)
|
t.Fatalf("OpenDeviceWithVIDPID(9999, 0001): %v", err)
|
||||||
|
@@ -20,9 +20,14 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestEndpoint(t *testing.T) {
|
func TestEndpoint(t *testing.T) {
|
||||||
// Can't be parallelized, newFakeLibusb modifies a shared global state.
|
t.Parallel()
|
||||||
lib, done := newFakeLibusb()
|
lib := newFakeLibusb()
|
||||||
defer done()
|
ctx := newContextWithImpl(lib)
|
||||||
|
defer func() {
|
||||||
|
if err := ctx.Close(); err != nil {
|
||||||
|
t.Errorf("Context.Close(): %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
for _, epData := range []struct {
|
for _, epData := range []struct {
|
||||||
ei EndpointDesc
|
ei EndpointDesc
|
||||||
@@ -92,7 +97,7 @@ func TestEndpoint(t *testing.T) {
|
|||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
ep := &endpoint{h: nil, InterfaceSetting: epData.intf, Desc: epData.ei}
|
ep := &endpoint{h: nil, ctx: ctx, InterfaceSetting: epData.intf, Desc: epData.ei}
|
||||||
if tc.wantSubmit {
|
if tc.wantSubmit {
|
||||||
go func() {
|
go func() {
|
||||||
fakeT := lib.waitForSubmitted()
|
fakeT := lib.waitForSubmitted()
|
||||||
@@ -163,12 +168,15 @@ func TestEndpointInfo(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestEndpointInOut(t *testing.T) {
|
func TestEndpointInOut(t *testing.T) {
|
||||||
// Can't be parallelized, newFakeLibusb modifies a shared global state.
|
t.Parallel()
|
||||||
lib, done := newFakeLibusb()
|
lib := newFakeLibusb()
|
||||||
defer done()
|
ctx := newContextWithImpl(lib)
|
||||||
|
defer func() {
|
||||||
|
if err := ctx.Close(); err != nil {
|
||||||
|
t.Errorf("Context.Close(): %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
ctx := NewContext()
|
|
||||||
defer ctx.Close()
|
|
||||||
d, err := ctx.OpenDeviceWithVIDPID(0x9999, 0x0001)
|
d, err := ctx.OpenDeviceWithVIDPID(0x9999, 0x0001)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("OpenDeviceWithVIDPID(0x9999, 0x0001): got error %v, want nil", err)
|
t.Fatalf("OpenDeviceWithVIDPID(0x9999, 0x0001): got error %v, want nil", err)
|
||||||
@@ -243,12 +251,14 @@ func TestEndpointInOut(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestSameEndpointNumberInOut(t *testing.T) {
|
func TestSameEndpointNumberInOut(t *testing.T) {
|
||||||
// Can't be parallelized, newFakeLibusb modifies a shared global state.
|
t.Parallel()
|
||||||
_, done := newFakeLibusb()
|
ctx := newContextWithImpl(newFakeLibusb())
|
||||||
defer done()
|
defer func() {
|
||||||
|
if err := ctx.Close(); err != nil {
|
||||||
|
t.Errorf("Context.Close(): %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
ctx := NewContext()
|
|
||||||
defer ctx.Close()
|
|
||||||
d, err := ctx.OpenDeviceWithVIDPID(0x1111, 0x1111)
|
d, err := ctx.OpenDeviceWithVIDPID(0x1111, 0x1111)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("OpenDeviceWithVIDPID(0x1111, 0x1111): got error %v, want nil", err)
|
t.Fatalf("OpenDeviceWithVIDPID(0x1111, 0x1111): got error %v, want nil", err)
|
||||||
|
@@ -64,9 +64,19 @@ func (f *fakeLibusb) getDevices(*libusbContext) ([]*libusbDevice, error) {
|
|||||||
}
|
}
|
||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
func (f *fakeLibusb) exit(*libusbContext) {}
|
|
||||||
func (f *fakeLibusb) setDebug(*libusbContext, int) {}
|
|
||||||
|
|
||||||
|
func (f *fakeLibusb) exit(*libusbContext) error {
|
||||||
|
close(f.submitted)
|
||||||
|
if got := len(f.ts); got > 0 {
|
||||||
|
for t := range f.ts {
|
||||||
|
f.free(t)
|
||||||
|
}
|
||||||
|
return fmt.Errorf("fakeLibusb has %d remaining transfers that should have been freed", got)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
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.fakeDevices[d]; ok {
|
||||||
@@ -191,8 +201,7 @@ func (f *fakeLibusb) empty() bool {
|
|||||||
return len(f.submitted) == 0
|
return len(f.submitted) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func newFakeLibusb() (*fakeLibusb, func() error) {
|
func newFakeLibusb() *fakeLibusb {
|
||||||
origLibusb := libusb
|
|
||||||
fl := &fakeLibusb{
|
fl := &fakeLibusb{
|
||||||
fakeDevices: make(map[*libusbDevice]*fakeDevice),
|
fakeDevices: make(map[*libusbDevice]*fakeDevice),
|
||||||
ts: make(map[*libusbTransfer]*fakeTransfer),
|
ts: make(map[*libusbTransfer]*fakeTransfer),
|
||||||
@@ -209,16 +218,5 @@ func newFakeLibusb() (*fakeLibusb, func() error) {
|
|||||||
*fd = d
|
*fd = d
|
||||||
fl.fakeDevices[newDevicePointer()] = fd
|
fl.fakeDevices[newDevicePointer()] = fd
|
||||||
}
|
}
|
||||||
libusb = fl
|
return fl
|
||||||
return fl, func() error {
|
|
||||||
defer func() { libusb = origLibusb }()
|
|
||||||
close(fl.submitted)
|
|
||||||
if got := len(fl.ts); got > 0 {
|
|
||||||
for t := range fl.ts {
|
|
||||||
fl.free(t)
|
|
||||||
}
|
|
||||||
return fmt.Errorf("fakeLibusb has %d remaining transfers that should have been freed", got)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -89,7 +89,7 @@ func (i *Interface) Close() {
|
|||||||
if i.config == nil {
|
if i.config == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
libusb.release(i.config.dev.handle, uint8(i.Setting.Number))
|
i.config.dev.ctx.libusb.release(i.config.dev.handle, uint8(i.Setting.Number))
|
||||||
i.config.mu.Lock()
|
i.config.mu.Lock()
|
||||||
defer i.config.mu.Unlock()
|
defer i.config.mu.Unlock()
|
||||||
delete(i.config.claimed, i.Setting.Number)
|
delete(i.config.claimed, i.Setting.Number)
|
||||||
@@ -106,6 +106,7 @@ func (i *Interface) openEndpoint(epAddr EndpointAddress) (*endpoint, error) {
|
|||||||
InterfaceSetting: i.Setting,
|
InterfaceSetting: i.Setting,
|
||||||
Desc: ep,
|
Desc: ep,
|
||||||
h: i.config.dev.handle,
|
h: i.config.dev.handle,
|
||||||
|
ctx: i.config.dev.ctx,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -135,7 +135,7 @@ type libusbIntf interface {
|
|||||||
init() (*libusbContext, error)
|
init() (*libusbContext, error)
|
||||||
handleEvents(*libusbContext, <-chan struct{})
|
handleEvents(*libusbContext, <-chan struct{})
|
||||||
getDevices(*libusbContext) ([]*libusbDevice, error)
|
getDevices(*libusbContext) ([]*libusbDevice, error)
|
||||||
exit(*libusbContext)
|
exit(*libusbContext) error
|
||||||
setDebug(*libusbContext, int)
|
setDebug(*libusbContext, int)
|
||||||
|
|
||||||
// device
|
// device
|
||||||
@@ -213,8 +213,9 @@ func (libusbImpl) getDevices(ctx *libusbContext) ([]*libusbDevice, error) {
|
|||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (libusbImpl) exit(c *libusbContext) {
|
func (libusbImpl) exit(c *libusbContext) error {
|
||||||
C.libusb_exit((*C.libusb_context)(c))
|
C.libusb_exit((*C.libusb_context)(c))
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (libusbImpl) setDebug(c *libusbContext, lvl int) {
|
func (libusbImpl) setDebug(c *libusbContext, lvl int) {
|
||||||
@@ -479,9 +480,6 @@ func (libusbImpl) setIsoPacketLengths(t *libusbTransfer, length uint32) {
|
|||||||
C.libusb_set_iso_packet_lengths((*C.struct_libusb_transfer)(t), C.uint(length))
|
C.libusb_set_iso_packet_lengths((*C.struct_libusb_transfer)(t), C.uint(length))
|
||||||
}
|
}
|
||||||
|
|
||||||
// libusb is an injection point for tests
|
|
||||||
var libusb libusbIntf = libusbImpl{}
|
|
||||||
|
|
||||||
// xferDoneMap keeps a map of done callback channels for all allocated transfers.
|
// xferDoneMap keeps a map of done callback channels for all allocated transfers.
|
||||||
var xferDoneMap = struct {
|
var xferDoneMap = struct {
|
||||||
m map[*libusbTransfer]chan struct{}
|
m map[*libusbTransfer]chan struct{}
|
||||||
|
19
transfer.go
19
transfer.go
@@ -35,6 +35,8 @@ type usbTransfer struct {
|
|||||||
done chan struct{}
|
done chan struct{}
|
||||||
// submitted is true if submit() was called on this transfer.
|
// submitted is true if submit() was called on this transfer.
|
||||||
submitted bool
|
submitted bool
|
||||||
|
// ctx is the Context that created this transfer.
|
||||||
|
ctx *Context
|
||||||
}
|
}
|
||||||
|
|
||||||
// submits the transfer. After submit() the transfer is in flight and is owned by libusb.
|
// submits the transfer. After submit() the transfer is in flight and is owned by libusb.
|
||||||
@@ -46,7 +48,7 @@ func (t *usbTransfer) submit() error {
|
|||||||
if t.submitted {
|
if t.submitted {
|
||||||
return errors.New("transfer was already submitted and is not finished yet")
|
return errors.New("transfer was already submitted and is not finished yet")
|
||||||
}
|
}
|
||||||
if err := libusb.submit(t.xfer); err != nil {
|
if err := t.ctx.libusb.submit(t.xfer); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
t.submitted = true
|
t.submitted = true
|
||||||
@@ -66,7 +68,7 @@ func (t *usbTransfer) wait() (n int, err error) {
|
|||||||
}
|
}
|
||||||
<-t.done
|
<-t.done
|
||||||
t.submitted = false
|
t.submitted = false
|
||||||
n, status := libusb.data(t.xfer)
|
n, status := t.ctx.libusb.data(t.xfer)
|
||||||
if status != TransferCompleted {
|
if status != TransferCompleted {
|
||||||
return n, status
|
return n, status
|
||||||
}
|
}
|
||||||
@@ -81,7 +83,7 @@ func (t *usbTransfer) cancel() error {
|
|||||||
if !t.submitted {
|
if !t.submitted {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
err := libusb.cancel(t.xfer)
|
err := t.ctx.libusb.cancel(t.xfer)
|
||||||
if err == ErrorNotFound {
|
if err == ErrorNotFound {
|
||||||
// transfer already completed
|
// transfer already completed
|
||||||
return nil
|
return nil
|
||||||
@@ -101,7 +103,7 @@ func (t *usbTransfer) free() error {
|
|||||||
if t.xfer == nil {
|
if t.xfer == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
libusb.free(t.xfer)
|
t.ctx.libusb.free(t.xfer)
|
||||||
t.xfer = nil
|
t.xfer = nil
|
||||||
t.buf = nil
|
t.buf = nil
|
||||||
t.done = nil
|
t.done = nil
|
||||||
@@ -115,7 +117,7 @@ func (t *usbTransfer) data() []byte {
|
|||||||
|
|
||||||
// newUSBTransfer allocates a new transfer structure and a new buffer for
|
// newUSBTransfer allocates a new transfer structure and a new buffer for
|
||||||
// communication with a given device/endpoint.
|
// communication with a given device/endpoint.
|
||||||
func newUSBTransfer(dev *libusbDevHandle, ei *EndpointDesc, bufLen int, timeout time.Duration) (*usbTransfer, error) {
|
func newUSBTransfer(ctx *Context, dev *libusbDevHandle, ei *EndpointDesc, bufLen int, timeout time.Duration) (*usbTransfer, error) {
|
||||||
var isoPackets, isoPktSize int
|
var isoPackets, isoPktSize int
|
||||||
if ei.TransferType == TransferTypeIsochronous {
|
if ei.TransferType == TransferTypeIsochronous {
|
||||||
isoPktSize = ei.MaxPacketSize
|
isoPktSize = ei.MaxPacketSize
|
||||||
@@ -127,19 +129,20 @@ func newUSBTransfer(dev *libusbDevHandle, ei *EndpointDesc, bufLen int, timeout
|
|||||||
}
|
}
|
||||||
|
|
||||||
done := make(chan struct{}, 1)
|
done := make(chan struct{}, 1)
|
||||||
xfer, err := libusb.alloc(dev, ei, timeout, isoPackets, bufLen, done)
|
xfer, err := ctx.libusb.alloc(dev, ei, timeout, isoPackets, bufLen, done)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if ei.TransferType == TransferTypeIsochronous {
|
if ei.TransferType == TransferTypeIsochronous {
|
||||||
libusb.setIsoPacketLengths(xfer, uint32(isoPktSize))
|
ctx.libusb.setIsoPacketLengths(xfer, uint32(isoPktSize))
|
||||||
}
|
}
|
||||||
|
|
||||||
t := &usbTransfer{
|
t := &usbTransfer{
|
||||||
xfer: xfer,
|
xfer: xfer,
|
||||||
buf: libusb.buffer(xfer),
|
buf: ctx.libusb.buffer(xfer),
|
||||||
done: done,
|
done: done,
|
||||||
|
ctx: ctx,
|
||||||
}
|
}
|
||||||
runtime.SetFinalizer(t, func(t *usbTransfer) {
|
runtime.SetFinalizer(t, func(t *usbTransfer) {
|
||||||
t.cancel()
|
t.cancel()
|
||||||
|
@@ -20,9 +20,13 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestNewTransfer(t *testing.T) {
|
func TestNewTransfer(t *testing.T) {
|
||||||
// Can't be parallelized, newFakeLibusb modifies a shared global state.
|
t.Parallel()
|
||||||
_, done := newFakeLibusb()
|
ctx := newContextWithImpl(newFakeLibusb())
|
||||||
defer done()
|
defer func() {
|
||||||
|
if err := ctx.Close(); err != nil {
|
||||||
|
t.Errorf("Context.Close(): %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
for _, tc := range []struct {
|
for _, tc := range []struct {
|
||||||
desc string
|
desc string
|
||||||
@@ -53,7 +57,7 @@ func TestNewTransfer(t *testing.T) {
|
|||||||
wantLength: 10000,
|
wantLength: 10000,
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
xfer, err := newUSBTransfer(nil, &EndpointDesc{
|
xfer, err := newUSBTransfer(ctx, nil, &EndpointDesc{
|
||||||
Number: 2,
|
Number: 2,
|
||||||
Direction: tc.dir,
|
Direction: tc.dir,
|
||||||
TransferType: tc.tt,
|
TransferType: tc.tt,
|
||||||
@@ -71,14 +75,19 @@ func TestNewTransfer(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestTransferProtocol(t *testing.T) {
|
func TestTransferProtocol(t *testing.T) {
|
||||||
// Can't be parallelized, newFakeLibusb modifies a shared global state.
|
t.Parallel()
|
||||||
f, done := newFakeLibusb()
|
f := newFakeLibusb()
|
||||||
defer done()
|
ctx := newContextWithImpl(f)
|
||||||
|
defer func() {
|
||||||
|
if err := ctx.Close(); err != nil {
|
||||||
|
t.Errorf("Context.Close(): %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
xfers := make([]*usbTransfer, 2)
|
xfers := make([]*usbTransfer, 2)
|
||||||
var err error
|
var err error
|
||||||
for i := 0; i < 2; i++ {
|
for i := 0; i < 2; i++ {
|
||||||
xfers[i], err = newUSBTransfer(nil, &EndpointDesc{
|
xfers[i], err = newUSBTransfer(ctx, nil, &EndpointDesc{
|
||||||
Number: 6,
|
Number: 6,
|
||||||
Direction: EndpointDirectionIn,
|
Direction: EndpointDirectionIn,
|
||||||
TransferType: TransferTypeBulk,
|
TransferType: TransferTypeBulk,
|
||||||
|
35
usb.go
35
usb.go
@@ -129,35 +129,41 @@ package gousb
|
|||||||
type Context struct {
|
type Context struct {
|
||||||
ctx *libusbContext
|
ctx *libusbContext
|
||||||
done chan struct{}
|
done chan struct{}
|
||||||
|
libusb libusbIntf
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debug changes the debug level. Level 0 means no debug, higher levels
|
// Debug changes the debug level. Level 0 means no debug, higher levels
|
||||||
// will print out more debugging information.
|
// will print out more debugging information.
|
||||||
func (c *Context) Debug(level int) {
|
func (c *Context) Debug(level int) {
|
||||||
libusb.setDebug(c.ctx, level)
|
c.libusb.setDebug(c.ctx, level)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewContext returns a new Context instance.
|
func newContextWithImpl(impl libusbIntf) *Context {
|
||||||
func NewContext() *Context {
|
c, err := impl.init()
|
||||||
c, err := libusb.init()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
ctx := &Context{
|
ctx := &Context{
|
||||||
ctx: c,
|
ctx: c,
|
||||||
done: make(chan struct{}),
|
done: make(chan struct{}),
|
||||||
|
libusb: impl,
|
||||||
}
|
}
|
||||||
go libusb.handleEvents(ctx.ctx, ctx.done)
|
go impl.handleEvents(ctx.ctx, ctx.done)
|
||||||
return ctx
|
return ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewContext returns a new Context instance.
|
||||||
|
func NewContext() *Context {
|
||||||
|
return newContextWithImpl(libusbImpl{})
|
||||||
|
}
|
||||||
|
|
||||||
// OpenDevices calls opener with each enumerated device.
|
// OpenDevices calls opener with each enumerated device.
|
||||||
// If the opener returns true, the device is opened and a Device is returned if the operation succeeds.
|
// If the opener 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.
|
// Every Device returned (whether an error is also returned or not) must be closed.
|
||||||
// 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) {
|
||||||
list, err := libusb.getDevices(c.ctx)
|
list, err := c.libusb.getDevices(c.ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -165,23 +171,23 @@ func (c *Context) OpenDevices(opener func(desc *DeviceDesc) bool) ([]*Device, er
|
|||||||
var reterr error
|
var reterr error
|
||||||
var ret []*Device
|
var ret []*Device
|
||||||
for _, dev := range list {
|
for _, dev := range list {
|
||||||
desc, err := libusb.getDeviceDesc(dev)
|
desc, err := c.libusb.getDeviceDesc(dev)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
libusb.dereference(dev)
|
c.libusb.dereference(dev)
|
||||||
reterr = err
|
reterr = err
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if opener(desc) {
|
if opener(desc) {
|
||||||
handle, err := libusb.open(dev)
|
handle, err := c.libusb.open(dev)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
libusb.dereference(dev)
|
c.libusb.dereference(dev)
|
||||||
reterr = err
|
reterr = err
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
ret = append(ret, &Device{handle: handle, Desc: desc})
|
ret = append(ret, &Device{handle: handle, ctx: c, Desc: desc})
|
||||||
} else {
|
} else {
|
||||||
libusb.dereference(dev)
|
c.libusb.dereference(dev)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ret, reterr
|
return ret, reterr
|
||||||
@@ -213,10 +219,11 @@ func (c *Context) OpenDeviceWithVIDPID(vid, pid ID) (*Device, error) {
|
|||||||
|
|
||||||
// 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
|
||||||
c.done <- struct{}{}
|
c.done <- struct{}{}
|
||||||
if c.ctx != nil {
|
if c.ctx != nil {
|
||||||
libusb.exit(c.ctx)
|
ret = c.libusb.exit(c.ctx)
|
||||||
}
|
}
|
||||||
c.ctx = nil
|
c.ctx = nil
|
||||||
return nil
|
return ret
|
||||||
}
|
}
|
||||||
|
27
usb_test.go
27
usb_test.go
@@ -18,12 +18,13 @@ package gousb
|
|||||||
import "testing"
|
import "testing"
|
||||||
|
|
||||||
func TestOPenDevices(t *testing.T) {
|
func TestOPenDevices(t *testing.T) {
|
||||||
// Can't be parallelized, newFakeLibusb modifies a shared global state.
|
t.Parallel()
|
||||||
_, done := newFakeLibusb()
|
c := newContextWithImpl(newFakeLibusb())
|
||||||
defer done()
|
defer func() {
|
||||||
|
if err := c.Close(); err != nil {
|
||||||
c := NewContext()
|
t.Errorf("Context.Close(): %v", err)
|
||||||
defer c.Close()
|
}
|
||||||
|
}()
|
||||||
c.Debug(0)
|
c.Debug(0)
|
||||||
|
|
||||||
descs := []*DeviceDesc{}
|
descs := []*DeviceDesc{}
|
||||||
@@ -55,9 +56,13 @@ func TestOPenDevices(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestOpenDeviceWithVIDPID(t *testing.T) {
|
func TestOpenDeviceWithVIDPID(t *testing.T) {
|
||||||
// Can't be parallelized, newFakeLibusb modifies a shared global state.
|
t.Parallel()
|
||||||
_, done := newFakeLibusb()
|
ctx := newContextWithImpl(newFakeLibusb())
|
||||||
defer done()
|
defer func() {
|
||||||
|
if err := ctx.Close(); err != nil {
|
||||||
|
t.Errorf("Context.Close(): %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
for _, d := range []struct {
|
for _, d := range []struct {
|
||||||
vid, pid ID
|
vid, pid ID
|
||||||
@@ -69,9 +74,7 @@ func TestOpenDeviceWithVIDPID(t *testing.T) {
|
|||||||
{0x9999, 0x0001, true},
|
{0x9999, 0x0001, true},
|
||||||
{0x9999, 0x0002, false},
|
{0x9999, 0x0002, false},
|
||||||
} {
|
} {
|
||||||
c := NewContext()
|
dev, err := ctx.OpenDeviceWithVIDPID(d.vid, d.pid)
|
||||||
defer c.Close()
|
|
||||||
dev, err := c.OpenDeviceWithVIDPID(d.vid, d.pid)
|
|
||||||
if (dev != nil) != d.exists {
|
if (dev != nil) != d.exists {
|
||||||
t.Errorf("OpenDeviceWithVIDPID(%s/%s): device != nil is %v, want %v", ID(d.vid), ID(d.pid), dev != nil, d.exists)
|
t.Errorf("OpenDeviceWithVIDPID(%s/%s): device != nil is %v, want %v", ID(d.vid), ID(d.pid), dev != nil, d.exists)
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user