Extend the autodetach behavior
Detach interfaces before trying to change the config, as libusb doesn't handle that automatically.
This commit is contained in:
1
AUTHORS
1
AUTHORS
@@ -12,3 +12,4 @@ Jirawat I. <nodtem66@gmail.com>
|
||||
Thordur Bjornsson <thorduri@secnorth.net>
|
||||
Vincent Serpoul <vincent@serpoul.com>
|
||||
Josef Filzmaier <josef.filzmaier@gmail.com>
|
||||
Nico MT <nicovell3@gmail.com>
|
||||
|
19
device.go
19
device.go
@@ -68,6 +68,9 @@ type Device struct {
|
||||
// Claimed config
|
||||
mu sync.Mutex
|
||||
claimed *Config
|
||||
|
||||
// Handle AutoDetach in this library
|
||||
autodetach bool
|
||||
}
|
||||
|
||||
func (d *DeviceDesc) sortedConfigIds() []int {
|
||||
@@ -129,6 +132,14 @@ func (d *Device) Config(cfgNum int) (*Config, error) {
|
||||
claimed: make(map[int]bool),
|
||||
}
|
||||
|
||||
if d.autodetach {
|
||||
for _, iface := range cfg.Desc.Interfaces {
|
||||
if err := 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if activeCfgNum, err := d.ActiveConfigNum(); err != nil {
|
||||
return nil, fmt.Errorf("failed to query active config of the device %s: %v", d, err)
|
||||
} else if cfgNum != activeCfgNum {
|
||||
@@ -182,7 +193,7 @@ func (d *Device) Close() error {
|
||||
d.mu.Lock()
|
||||
defer d.mu.Unlock()
|
||||
if d.claimed != nil {
|
||||
return fmt.Errorf("can't release the device %s, it has an open config %s", 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.handle = nil
|
||||
@@ -208,12 +219,10 @@ func (d *Device) SetAutoDetach(autodetach bool) error {
|
||||
if d.handle == nil {
|
||||
return fmt.Errorf("SetAutoDetach(%v) called on %s after Close", autodetach, d)
|
||||
}
|
||||
d.autodetach = autodetach
|
||||
var autodetachInt int
|
||||
switch autodetach {
|
||||
case true:
|
||||
if autodetach {
|
||||
autodetachInt = 1
|
||||
case false:
|
||||
autodetachInt = 0
|
||||
}
|
||||
return libusb.setAutoDetach(d.handle, autodetachInt)
|
||||
}
|
||||
|
@@ -15,6 +15,7 @@
|
||||
package gousb
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
@@ -42,6 +43,10 @@ func TestClaimAndRelease(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("OpenDeviceWithVIDPID(0x8888, 0x0002): %v", err)
|
||||
}
|
||||
|
||||
if err = dev.SetAutoDetach(true); err != nil {
|
||||
t.Fatalf("%s.SetAutoDetach(true): %v", dev, err)
|
||||
}
|
||||
cfg, err := dev.Config(cfgNum)
|
||||
if err != nil {
|
||||
t.Fatalf("%s.Config(1): %v", dev, err)
|
||||
@@ -111,4 +116,49 @@ func TestClaimAndRelease(t *testing.T) {
|
||||
if err := dev.Close(); err != nil {
|
||||
t.Fatalf("%s.Close(): got error %v, want nil", dev, err)
|
||||
}
|
||||
|
||||
if _, err := dev.Config(cfgNum); err == nil {
|
||||
t.Fatalf("%s.Config(1): got error nil, want no nil because it is closed", dev)
|
||||
}
|
||||
|
||||
if err := dev.Reset(); err == nil {
|
||||
t.Fatalf("%s.Reset(): got error nil, want no nil because it is closed", dev)
|
||||
}
|
||||
|
||||
if err := dev.SetAutoDetach(false); err == nil {
|
||||
t.Fatalf("%s.SetAutoDetach(false): got error nil, want no nil because it is closed", dev)
|
||||
}
|
||||
}
|
||||
|
||||
type failDetachLib struct {
|
||||
*fakeLibusb
|
||||
}
|
||||
|
||||
func (*failDetachLib) detachKernelDriver(h *libusbDevHandle, i uint8) error {
|
||||
if i == 1 {
|
||||
return errors.New("test detach fail")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestAutoDetachFailure(t *testing.T) {
|
||||
fake, done := newFakeLibusb()
|
||||
defer done()
|
||||
libusb = &failDetachLib{fake}
|
||||
|
||||
c := NewContext()
|
||||
defer c.Close()
|
||||
dev, err := c.OpenDeviceWithVIDPID(0x8888, 0x0002)
|
||||
if dev == nil {
|
||||
t.Fatal("OpenDeviceWithVIDPID(0x8888, 0x0002): got nil device, need non-nil")
|
||||
}
|
||||
defer dev.Close()
|
||||
if err != nil {
|
||||
t.Fatalf("OpenDeviceWithVIDPID(0x8888, 0x0002): %v", err)
|
||||
}
|
||||
dev.SetAutoDetach(true)
|
||||
_, err = dev.Config(1)
|
||||
if err == nil {
|
||||
t.Fatalf("%s.Config(1) got nil, but want no nil because interface fails to detach", dev)
|
||||
}
|
||||
}
|
||||
|
@@ -76,6 +76,6 @@ func TestEndpointReadStream(t *testing.T) {
|
||||
got += num
|
||||
}
|
||||
if got != want {
|
||||
t.Errorf("%s.Read(): read %d bytes, want %d")
|
||||
t.Errorf("stream.Read(): read %d bytes, want %d", got, want)
|
||||
}
|
||||
}
|
||||
|
@@ -114,6 +114,8 @@ func (f *fakeLibusb) getStringDesc(*libusbDevHandle, int) (string, error) {
|
||||
}
|
||||
func (f *fakeLibusb) setAutoDetach(*libusbDevHandle, int) error { return nil }
|
||||
|
||||
func (f *fakeLibusb) detachKernelDriver(*libusbDevHandle, uint8) error { return nil }
|
||||
|
||||
func (f *fakeLibusb) claim(d *libusbDevHandle, intf uint8) error {
|
||||
debug.Printf("claim(%p, %d)\n", d, intf)
|
||||
f.mu.Lock()
|
||||
|
11
libusb.go
11
libusb.go
@@ -148,6 +148,7 @@ type libusbIntf interface {
|
||||
setConfig(*libusbDevHandle, uint8) error
|
||||
getStringDesc(*libusbDevHandle, int) (string, error)
|
||||
setAutoDetach(*libusbDevHandle, int) error
|
||||
detachKernelDriver(*libusbDevHandle, uint8) error
|
||||
|
||||
// interface
|
||||
claim(*libusbDevHandle, uint8) error
|
||||
@@ -388,6 +389,16 @@ func (libusbImpl) setAutoDetach(d *libusbDevHandle, val int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (libusbImpl) detachKernelDriver(d *libusbDevHandle, iface uint8) error {
|
||||
err := fromErrNo(C.libusb_detach_kernel_driver((*C.libusb_device_handle)(d), C.int(iface)))
|
||||
if err != nil && err != ErrorNotSupported && err != ErrorNotFound {
|
||||
// ErrorNotSupported is returned in non linux systems
|
||||
// ErrorNotFound is returned if libusb's driver is already attached to the device
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (libusbImpl) claim(d *libusbDevHandle, iface uint8) error {
|
||||
return fromErrNo(C.libusb_claim_interface((*C.libusb_device_handle)(d), C.int(iface)))
|
||||
}
|
||||
|
Reference in New Issue
Block a user