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>
|
Thordur Bjornsson <thorduri@secnorth.net>
|
||||||
Vincent Serpoul <vincent@serpoul.com>
|
Vincent Serpoul <vincent@serpoul.com>
|
||||||
Josef Filzmaier <josef.filzmaier@gmail.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
|
// Claimed config
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
claimed *Config
|
claimed *Config
|
||||||
|
|
||||||
|
// Handle AutoDetach in this library
|
||||||
|
autodetach bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DeviceDesc) sortedConfigIds() []int {
|
func (d *DeviceDesc) sortedConfigIds() []int {
|
||||||
@@ -129,6 +132,14 @@ func (d *Device) Config(cfgNum int) (*Config, error) {
|
|||||||
claimed: make(map[int]bool),
|
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 {
|
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 {
|
||||||
@@ -182,7 +193,7 @@ func (d *Device) Close() error {
|
|||||||
d.mu.Lock()
|
d.mu.Lock()
|
||||||
defer d.mu.Unlock()
|
defer d.mu.Unlock()
|
||||||
if d.claimed != nil {
|
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)
|
libusb.close(d.handle)
|
||||||
d.handle = nil
|
d.handle = nil
|
||||||
@@ -208,12 +219,10 @@ func (d *Device) SetAutoDetach(autodetach bool) error {
|
|||||||
if d.handle == nil {
|
if d.handle == nil {
|
||||||
return fmt.Errorf("SetAutoDetach(%v) called on %s after Close", autodetach, d)
|
return fmt.Errorf("SetAutoDetach(%v) called on %s after Close", autodetach, d)
|
||||||
}
|
}
|
||||||
|
d.autodetach = autodetach
|
||||||
var autodetachInt int
|
var autodetachInt int
|
||||||
switch autodetach {
|
if autodetach {
|
||||||
case true:
|
|
||||||
autodetachInt = 1
|
autodetachInt = 1
|
||||||
case false:
|
|
||||||
autodetachInt = 0
|
|
||||||
}
|
}
|
||||||
return libusb.setAutoDetach(d.handle, autodetachInt)
|
return libusb.setAutoDetach(d.handle, autodetachInt)
|
||||||
}
|
}
|
||||||
|
@@ -15,6 +15,7 @@
|
|||||||
package gousb
|
package gousb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
@@ -42,6 +43,10 @@ func TestClaimAndRelease(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("OpenDeviceWithVIDPID(0x8888, 0x0002): %v", err)
|
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)
|
cfg, err := dev.Config(cfgNum)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("%s.Config(1): %v", dev, err)
|
t.Fatalf("%s.Config(1): %v", dev, err)
|
||||||
@@ -111,4 +116,49 @@ func TestClaimAndRelease(t *testing.T) {
|
|||||||
if err := dev.Close(); err != nil {
|
if err := dev.Close(); err != nil {
|
||||||
t.Fatalf("%s.Close(): got error %v, want nil", dev, err)
|
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
|
got += num
|
||||||
}
|
}
|
||||||
if got != want {
|
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) setAutoDetach(*libusbDevHandle, int) error { return nil }
|
||||||
|
|
||||||
|
func (f *fakeLibusb) detachKernelDriver(*libusbDevHandle, uint8) error { return nil }
|
||||||
|
|
||||||
func (f *fakeLibusb) claim(d *libusbDevHandle, intf uint8) error {
|
func (f *fakeLibusb) claim(d *libusbDevHandle, intf uint8) error {
|
||||||
debug.Printf("claim(%p, %d)\n", d, intf)
|
debug.Printf("claim(%p, %d)\n", d, intf)
|
||||||
f.mu.Lock()
|
f.mu.Lock()
|
||||||
|
11
libusb.go
11
libusb.go
@@ -148,6 +148,7 @@ type libusbIntf interface {
|
|||||||
setConfig(*libusbDevHandle, uint8) error
|
setConfig(*libusbDevHandle, uint8) error
|
||||||
getStringDesc(*libusbDevHandle, int) (string, error)
|
getStringDesc(*libusbDevHandle, int) (string, error)
|
||||||
setAutoDetach(*libusbDevHandle, int) error
|
setAutoDetach(*libusbDevHandle, int) error
|
||||||
|
detachKernelDriver(*libusbDevHandle, uint8) error
|
||||||
|
|
||||||
// interface
|
// interface
|
||||||
claim(*libusbDevHandle, uint8) error
|
claim(*libusbDevHandle, uint8) error
|
||||||
@@ -388,6 +389,16 @@ func (libusbImpl) setAutoDetach(d *libusbDevHandle, val int) error {
|
|||||||
return nil
|
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 {
|
func (libusbImpl) claim(d *libusbDevHandle, iface uint8) error {
|
||||||
return fromErrNo(C.libusb_claim_interface((*C.libusb_device_handle)(d), C.int(iface)))
|
return fromErrNo(C.libusb_claim_interface((*C.libusb_device_handle)(d), C.int(iface)))
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user