From bc91dd3f2c71ca4aba28505b7aa2c811fc852d5c Mon Sep 17 00:00:00 2001 From: Nico MT Date: Fri, 21 Jul 2017 21:15:49 +0200 Subject: [PATCH] Extend the autodetach behavior Detach interfaces before trying to change the config, as libusb doesn't handle that automatically. --- AUTHORS | 1 + device.go | 19 +++++++++++----- device_test.go | 50 +++++++++++++++++++++++++++++++++++++++++ endpoint_stream_test.go | 2 +- fakelibusb_test.go | 2 ++ libusb.go | 11 +++++++++ 6 files changed, 79 insertions(+), 6 deletions(-) diff --git a/AUTHORS b/AUTHORS index c7743c9..9bcf017 100644 --- a/AUTHORS +++ b/AUTHORS @@ -12,3 +12,4 @@ Jirawat I. Thordur Bjornsson Vincent Serpoul Josef Filzmaier +Nico MT diff --git a/device.go b/device.go index 3083717..3be599a 100644 --- a/device.go +++ b/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) } diff --git a/device_test.go b/device_test.go index 0ce25d4..76c5aa4 100644 --- a/device_test.go +++ b/device_test.go @@ -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) + } } diff --git a/endpoint_stream_test.go b/endpoint_stream_test.go index acf7aeb..ad11c8c 100644 --- a/endpoint_stream_test.go +++ b/endpoint_stream_test.go @@ -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) } } diff --git a/fakelibusb_test.go b/fakelibusb_test.go index 7afb9c3..6e2fb3b 100644 --- a/fakelibusb_test.go +++ b/fakelibusb_test.go @@ -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() diff --git a/libusb.go b/libusb.go index 0a2d6c8..4a6ae37 100644 --- a/libusb.go +++ b/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))) }