Merge branch 'gousb2.0-dev' of https://github.com/kylelemons/gousb into gousb2.0-dev

This commit is contained in:
Sebastian Zagrodzki
2017-04-29 00:04:01 +02:00
12 changed files with 311 additions and 253 deletions

View File

@@ -15,47 +15,11 @@
package usb
import "fmt"
// InterfaceInfo contains information about a USB interface, extracted from
// the descriptor.
type InterfaceInfo struct {
// Number is the number of this interface, a zero-based index in the array
// of interfaces supported by the device configuration.
Number int
// AltSettings is a list of alternate settings supported by the interface.
AltSettings []InterfaceSetting
}
// String returns a human-readable descripton of the interface and it's
// alternate settings.
func (i InterfaceInfo) String() string {
return fmt.Sprintf("Interface %d (%d alternate settings)", i.Number, len(i.AltSettings))
}
// InterfaceSetting contains information about a USB interface with a particular
// alternate setting, extracted from the descriptor.
type InterfaceSetting struct {
// Number is the number of this interface, the same as in InterfaceInfo.
Number int
// Alternate is the number of this alternate setting.
Alternate int
// Class is the USB-IF class code, as defined by the USB spec.
Class Class
// SubClass is the USB-IF subclass code, as defined by the USB spec.
SubClass Class
// Protocol is USB protocol code, as defined by the USB spe.c
Protocol Protocol
// Endpoints has the list of endpoints available on this interface with
// this alternate setting.
Endpoints []EndpointInfo
}
// String returns a human-readable descripton of the particular
// alternate setting of an interface.
func (a InterfaceSetting) String() string {
return fmt.Sprintf("Interface %d alternate setting %d", a.Number, a.Alternate)
}
import (
"fmt"
"sync"
"time"
)
// ConfigInfo contains the information about a USB device configuration.
type ConfigInfo struct {
@@ -75,5 +39,73 @@ type ConfigInfo struct {
// String returns the human-readable description of the configuration.
func (c ConfigInfo) String() string {
return fmt.Sprintf("Config %d", c.Config)
return fmt.Sprintf("config=%d", c.Config)
}
// Config represents a USB device set to use a particular configuration.
// Only one Config of a particular device can be used at any one time.
type Config struct {
Info ConfigInfo
ControlTimeout time.Duration
dev *Device
// Claimed interfaces
mu sync.Mutex
claimed map[int]bool
}
// Close releases the underlying device, allowing the caller to switch the device to a different configuration.
func (c *Config) Close() error {
c.mu.Lock()
defer c.mu.Unlock()
if len(c.claimed) > 0 {
var ifs []int
for k := range c.claimed {
ifs = append(ifs, k)
}
return fmt.Errorf("failed to release %s, interfaces %v are still open", c, ifs)
}
c.dev.mu.Lock()
defer c.dev.mu.Unlock()
c.dev.claimed = nil
return nil
}
func (c *Config) String() string {
return fmt.Sprintf("%s,%s", c.dev.String(), c.Info.String())
}
// Control sends a control request to the device.
func (c *Config) Control(rType, request uint8, val, idx uint16, data []byte) (int, error) {
return libusb.control(c.dev.handle, c.ControlTimeout, rType, request, val, idx, data)
}
// Interface claims and returns an interface on a USB device.
func (c *Config) Interface(intf, alt int) (*Interface, error) {
if intf < 0 || intf >= len(c.Info.Interfaces) {
return nil, fmt.Errorf("interface %d not found in %s. Interface number needs to be a 0-based index into the interface table, which has %d elements.", intf, c, len(c.Info.Interfaces))
}
ifInfo := c.Info.Interfaces[intf]
if alt < 0 || alt >= len(ifInfo.AltSettings) {
return nil, fmt.Errorf("Inteface %d does not have alternate setting %d. Alt setting needs to be a 0-based index into the settings table, which has %d elements.", ifInfo, alt, len(ifInfo.AltSettings))
}
// Claim the interface
if err := libusb.claim(c.dev.handle, uint8(intf)); err != nil {
return nil, fmt.Errorf("failed to claim interface %d on %s: %v", intf, c, err)
}
if err := libusb.setAlt(c.dev.handle, uint8(intf), uint8(alt)); err != nil {
libusb.release(c.dev.handle, uint8(intf))
return nil, fmt.Errorf("failed to set alternate config %d on interface %d of %s: %v", alt, intf, c, err)
}
c.mu.Lock()
defer c.mu.Unlock()
c.claimed[intf] = true
return &Interface{
Setting: ifInfo.AltSettings[alt],
config: c,
}, nil
}

View File

@@ -33,5 +33,5 @@ func init() {
if os.Getenv(debugEnvVarName) != "" {
out = os.Stderr
}
debug = log.New(out, "usb", log.LstdFlags|log.Lshortfile)
debug = log.New(out, "gousb: ", log.LstdFlags|log.Lshortfile)
}

View File

@@ -15,6 +15,8 @@
package usb
import "fmt"
// Descriptor is a representation of a USB device descriptor.
type Descriptor struct {
// Bus information
@@ -37,3 +39,8 @@ type Descriptor struct {
// Configuration information
Configs []ConfigInfo
}
// String represents a human readable representation of the device in the descriptor.
func (d *Descriptor) String() string {
return fmt.Sprintf("vid=%s,pid=%s,bus=%d,addr=%d", d.Vendor, d.Product, d.Bus, d.Address)
}

View File

@@ -18,16 +18,6 @@ package usb
import (
"fmt"
"sync"
"time"
)
var (
// DefaultReadTimeout controls the default timeout for IN endpoint transfers.
DefaultReadTimeout = 1 * time.Second
// DefaultWriteTimeout controls the default timeout for OUT endpoint transfers.
DefaultWriteTimeout = 1 * time.Second
// DefaultControlTimeout controls the default timeout for control transfers.
DefaultControlTimeout = 250 * time.Millisecond
)
// Device represents an opened USB device.
@@ -37,41 +27,21 @@ type Device struct {
// Embed the device information for easy access
*Descriptor
// Timeouts
ReadTimeout time.Duration
WriteTimeout time.Duration
ControlTimeout time.Duration
// Claimed interfaces
lock *sync.Mutex
claimed map[int]int
}
func newDevice(handle *libusbDevHandle, desc *Descriptor) *Device {
ifaces := 0
d := &Device{
handle: handle,
Descriptor: desc,
ReadTimeout: DefaultReadTimeout,
WriteTimeout: DefaultWriteTimeout,
ControlTimeout: DefaultControlTimeout,
lock: new(sync.Mutex),
claimed: make(map[int]int, ifaces),
}
return d
// Claimed config
mu sync.Mutex
claimed *Config
}
// Reset performs a USB port reset to reinitialize a device.
func (d *Device) Reset() error {
d.mu.Lock()
defer d.mu.Unlock()
if d.claimed != nil {
return fmt.Errorf("can't reset a device with an open configuration")
}
return libusb.reset(d.handle)
}
// Control sends a control request to the device.
func (d *Device) Control(rType, request uint8, val, idx uint16, data []byte) (int, error) {
return libusb.control(d.handle, d.ControlTimeout, rType, request, val, idx, data)
}
// ActiveConfig returns the config id (not the index) of the active configuration.
// This corresponds to the ConfigInfo.Config field.
func (d *Device) ActiveConfig() (int, error) {
@@ -79,136 +49,51 @@ func (d *Device) ActiveConfig() (int, error) {
return int(ret), err
}
// SetConfig attempts to change the active configuration.
// Config returns a USB device set to use a particular config.
// The cfg provided is the config id (not the index) of the configuration to set,
// which corresponds to the ConfigInfo.Config field.
func (d *Device) SetConfig(cfg int) error {
return libusb.setConfig(d.handle, uint8(cfg))
// USB supports only one active config per device at a time. Config claims the
// device before setting the desired config and keeps it locked until Close is called.
func (d *Device) Config(cfgNum int) (*Config, error) {
cfg := &Config{
dev: d,
claimed: make(map[int]bool),
}
var found bool
for _, info := range d.Descriptor.Configs {
if info.Config == cfgNum {
found = true
cfg.Info = info
break
}
}
if !found {
return nil, fmt.Errorf("configuration id %d not found in the descriptor of the device %s", cfg, d)
}
if err := 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)
}
d.mu.Lock()
defer d.mu.Unlock()
d.claimed = cfg
return cfg, nil
}
// Close closes the device.
func (d *Device) Close() error {
if d.handle == nil {
return fmt.Errorf("usb: double close on device")
return fmt.Errorf("double close on device %s", d)
}
d.lock.Lock()
defer d.lock.Unlock()
for iface := range d.claimed {
libusb.release(d.handle, uint8(iface))
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.Info.Config)
}
libusb.close(d.handle)
d.handle = nil
return nil
}
func (d *Device) openEndpoint(cfgNum, ifNum, setNum, epAddr int) (*endpoint, error) {
var cfg *ConfigInfo
for _, c := range d.Configs {
if c.Config == cfgNum {
debug.Printf("found conf: %+v\n", c)
cfg = &c
break
}
}
if cfg == nil {
return nil, fmt.Errorf("usb: unknown configuration 0x%02x", cfgNum)
}
var intf *InterfaceInfo
for _, i := range cfg.Interfaces {
if i.Number == ifNum {
debug.Printf("found iface: %+v\n", i)
intf = &i
break
}
}
if intf == nil {
return nil, fmt.Errorf("usb: unknown interface 0x%02x", ifNum)
}
var setAlternate bool
var ifs *InterfaceSetting
for i, s := range intf.AltSettings {
if s.Alternate == setNum {
setAlternate = i != 0
debug.Printf("found setup: %+v [default: %v]\n", s, !setAlternate)
ifs = &s
break
}
}
if ifs == nil {
return nil, fmt.Errorf("usb: unknown setup 0x%02x", setNum)
}
var ep *EndpointInfo
for _, e := range ifs.Endpoints {
if endpointAddr(e.Number, e.Direction) == epAddr {
debug.Printf("found ep #%d %s in %+v\n", e.Number, e.Direction, *ifs)
ep = &e
break
}
}
if ep == nil {
return nil, fmt.Errorf("usb: didn't find endpoint address 0x%02x", epAddr)
}
end := newEndpoint(d.handle, *ifs, *ep)
// Set the configuration
activeConf, err := libusb.getConfig(d.handle)
if err != nil {
return nil, fmt.Errorf("usb: getcfg: %s", err)
}
if activeConf != uint8(cfgNum) {
if err := libusb.setConfig(d.handle, uint8(cfgNum)); err != nil {
return nil, fmt.Errorf("usb: setcfg: %s", err)
}
}
// Claim the interface
if err := libusb.claim(d.handle, uint8(ifNum)); err != nil {
return nil, fmt.Errorf("usb: claim: %s", err)
}
// Increment the claim count
d.lock.Lock()
d.claimed[ifNum]++
d.lock.Unlock() // unlock immediately because the next calls may block
// Choose the alternate
if setAlternate {
if err := libusb.setAlt(d.handle, uint8(ifNum), uint8(setNum)); err != nil {
return nil, fmt.Errorf("usb: setalt: %s", err)
}
}
return end, nil
}
// InEndpoint prepares an IN endpoint for transfer.
func (d *Device) InEndpoint(cfgNum, ifNum, setNum, epNum int) (*InEndpoint, error) {
ep, err := d.openEndpoint(cfgNum, ifNum, setNum, endpointAddr(epNum, EndpointDirectionIn))
if err != nil {
return nil, err
}
ep.SetTimeout(d.ReadTimeout)
return &InEndpoint{
endpoint: ep,
}, nil
}
// OutEndpoint prepares an OUT endpoint for transfer.
func (d *Device) OutEndpoint(cfgNum, ifNum, setNum, epNum int) (*OutEndpoint, error) {
ep, err := d.openEndpoint(cfgNum, ifNum, setNum, endpointAddr(epNum, EndpointDirectionOut))
if err != nil {
return nil, err
}
ep.SetTimeout(d.WriteTimeout)
return &OutEndpoint{
endpoint: ep,
}, nil
}
// GetStringDescriptor returns a device string descriptor with the given index
// number. The first supported language is always used and the returned
// descriptor string is converted to ASCII (non-ASCII characters are replaced

View File

@@ -31,9 +31,19 @@ func TestOpenEndpoint(t *testing.T) {
}
defer dev.Close()
if err != nil {
t.Fatalf("OpenDeviceWithVidPid(0x8888, 0x0002): got error %v, want nil", err)
t.Fatalf("OpenDeviceWithVidPid(0x8888, 0x0002): %v", err)
}
got, err := dev.InEndpoint(1, 1, 1, 6)
cfg, err := dev.Config(1)
if err != nil {
t.Fatalf("%s Config(1): %v", dev, err)
}
defer cfg.Close()
intf, err := cfg.Interface(1, 1)
if err != nil {
t.Fatalf("%s Interface(1, 1): %v", cfg, err)
}
defer intf.Close()
got, err := intf.InEndpoint(6)
if err != nil {
t.Fatalf("InEndpoint(cfg=1, if=1, alt=1, ep=6IN): got error %v, want nil", err)
}

View File

@@ -57,7 +57,7 @@ func endpointAddr(n int, d EndpointDirection) int {
// String returns the human-readable description of the endpoint.
func (e EndpointInfo) String() string {
ret := make([]string, 0, 3)
ret = append(ret, fmt.Sprintf("Endpoint #%d %s (address 0x%02x) %s", e.Number, e.Direction, endpointAddr(e.Number, e.Direction), e.TransferType))
ret = append(ret, fmt.Sprintf("ep #%d %s (address 0x%02x) %s", e.Number, e.Direction, endpointAddr(e.Number, e.Direction), e.TransferType))
switch e.TransferType {
case TransferTypeIsochronous:
ret = append(ret, fmt.Sprintf("- %s %s", e.IsoSyncType, e.UsageType))
@@ -74,7 +74,7 @@ type endpoint struct {
InterfaceSetting
Info EndpointInfo
timeout time.Duration
Timeout time.Duration
}
// String returns a human-readable description of the endpoint.
@@ -82,18 +82,12 @@ func (e *endpoint) String() string {
return e.Info.String()
}
// SetTimeout sets a timeout duration for all new USB transfers involving
// this endpoint.
func (e *endpoint) SetTimeout(t time.Duration) {
e.timeout = t
}
func (e *endpoint) transfer(buf []byte) (int, error) {
if len(buf) == 0 {
return 0, nil
}
t, err := newUSBTransfer(e.h, &e.Info, buf, e.timeout)
t, err := newUSBTransfer(e.h, &e.Info, buf, e.Timeout)
if err != nil {
return 0, err
}
@@ -110,14 +104,6 @@ func (e *endpoint) transfer(buf []byte) (int, error) {
return n, nil
}
func newEndpoint(h *libusbDevHandle, s InterfaceSetting, e EndpointInfo) *endpoint {
return &endpoint{
InterfaceSetting: s,
Info: e,
h: h,
}
}
// InEndpoint represents an IN endpoint open for transfer.
type InEndpoint struct {
*endpoint

View File

@@ -17,7 +17,7 @@ package usb
func (e *endpoint) newStream(size, count int, submit bool) (*stream, error) {
var ts []transferIntf
for i := 0; i < count; i++ {
t, err := newUSBTransfer(e.h, &e.Info, make([]byte, size), e.timeout)
t, err := newUSBTransfer(e.h, &e.Info, make([]byte, size), e.Timeout)
if err != nil {
for _, t := range ts {
t.free()

View File

@@ -85,7 +85,7 @@ func TestEndpoint(t *testing.T) {
wantErr: true,
},
} {
ep := newEndpoint(nil, epData.intf, epData.ei)
ep := &endpoint{h: nil, InterfaceSetting: epData.intf, Info: epData.ei}
go func() {
fakeT := lib.waitForSubmitted()
fakeT.length = tc.ret
@@ -116,7 +116,7 @@ func TestEndpointInfo(t *testing.T) {
TransferType: TransferTypeBulk,
MaxPacketSize: 512,
},
want: "Endpoint #6 IN (address 0x86) bulk [512 bytes]",
want: "ep #6 IN (address 0x86) bulk [512 bytes]",
},
{
ep: EndpointInfo{
@@ -127,7 +127,7 @@ func TestEndpointInfo(t *testing.T) {
IsoSyncType: IsoSyncTypeAsync,
UsageType: IsoUsageTypeData,
},
want: "Endpoint #2 OUT (address 0x02) isochronous - asynchronous data [512 bytes]",
want: "ep #2 OUT (address 0x02) isochronous - asynchronous data [512 bytes]",
},
{
ep: EndpointInfo{
@@ -137,7 +137,7 @@ func TestEndpointInfo(t *testing.T) {
MaxPacketSize: 16,
UsageType: InterruptUsageTypePeriodic,
},
want: "Endpoint #3 IN (address 0x83) interrupt - periodic [16 bytes]",
want: "ep #3 IN (address 0x83) interrupt - periodic [16 bytes]",
},
} {
if got := tc.ep.String(); got != tc.want {
@@ -146,7 +146,7 @@ func TestEndpointInfo(t *testing.T) {
}
}
func TestEndpointIn(t *testing.T) {
func TestEndpointInOut(t *testing.T) {
defer func(i libusbIntf) { libusb = i }(libusb)
lib, done := newFakeLibusb()
@@ -158,9 +158,30 @@ func TestEndpointIn(t *testing.T) {
if err != nil {
t.Fatalf("OpenDeviceWithVidPid(0x9999, 0x0001): got error %v, want nil", err)
}
ep, err := d.InEndpoint(1, 0, 0, 2)
defer func() {
if err := d.Close(); err != nil {
t.Errorf("%s.Close(): %v", d, err)
}
}()
cfg, err := d.Config(1)
if err != nil {
t.Fatalf("InEndpoint(1, 0, 0, 2): got error %v, want nil", err)
t.Fatalf("%s.Config(1): %v", d, err)
}
defer func() {
if err := cfg.Close(); err != nil {
t.Errorf("%s.Close(): %v", cfg, err)
}
}()
intf, err := cfg.Interface(0, 0)
if err != nil {
t.Fatalf("%s.Interface(0, 0): %v", cfg, err)
}
defer intf.Close()
// IN endpoint 2
iep, err := intf.InEndpoint(2)
if err != nil {
t.Fatalf("%s.InEndpoint(2): got error %v, want nil", intf, err)
}
dataTransferred := 100
go func() {
@@ -170,52 +191,38 @@ func TestEndpointIn(t *testing.T) {
close(fakeT.done)
}()
buf := make([]byte, 512)
got, err := ep.Read(buf)
got, err := iep.Read(buf)
if err != nil {
t.Errorf("ep.Read: got error %v, want nil", err)
t.Errorf("%s.Read: got error %v, want nil", iep, err)
} else if got != dataTransferred {
t.Errorf("ep.Read: got %d, want %d", got, dataTransferred)
t.Errorf("%s.Read: got %d, want %d", iep, got, dataTransferred)
}
_, err = d.InEndpoint(1, 0, 0, 1)
_, err = intf.InEndpoint(1)
if err == nil {
t.Error("InEndpoint(1, 0, 0, 1): got nil, want error")
t.Errorf("%s.InEndpoint(1): got nil, want error", intf)
}
}
func TestEndpointOut(t *testing.T) {
defer func(i libusbIntf) { libusb = i }(libusb)
lib, done := newFakeLibusb()
defer done()
ctx := NewContext()
defer ctx.Close()
d, err := ctx.OpenDeviceWithVidPid(0x9999, 0x0001)
// OUT endpoint 1
oep, err := intf.OutEndpoint(1)
if err != nil {
t.Fatalf("OpenDeviceWithVidPid(0x9999, 0x0001): got error %v, want nil", err)
t.Fatalf("%s.OutEndpoint(1): got error %v, want nil", intf, err)
}
ep, err := d.OutEndpoint(1, 0, 0, 1)
if err != nil {
t.Fatalf("OutEndpoint(1, 0, 0, 1): got error %v, want nil", err)
}
dataTransferred := 100
go func() {
fakeT := lib.waitForSubmitted()
fakeT.length = dataTransferred
fakeT.status = TransferCompleted
close(fakeT.done)
}()
buf := make([]byte, 512)
got, err := ep.Write(buf)
got, err = oep.Write(buf)
if err != nil {
t.Errorf("ep.Write: got error %v, want nil", err)
t.Errorf("%s.Write: got error %v, want nil", oep, err)
} else if got != dataTransferred {
t.Errorf("ep.Write: got %d, want %d", got, dataTransferred)
t.Errorf("%s.Write: got %d, want %d", oep, got, dataTransferred)
}
_, err = d.OutEndpoint(1, 0, 0, 2)
_, err = intf.OutEndpoint(2)
if err == nil {
t.Error("OutEndpoint(1, 0, 0, 2): got nil, want error")
t.Errorf("%s.OutEndpoint(2): got nil, want error", intf)
}
}

View File

@@ -214,6 +214,7 @@ func (f *fakeLibusb) control(*libusbDevHandle, time.Duration, uint8, uint8, uint
}
func (f *fakeLibusb) getConfig(*libusbDevHandle) (uint8, error) { return 1, nil }
func (f *fakeLibusb) setConfig(d *libusbDevHandle, cfg uint8) error {
debug.Printf("setConfig(%p, %d)\n", d, cfg)
f.mu.Lock()
defer f.mu.Unlock()
if len(f.claims[f.handles[d]]) != 0 {
@@ -230,6 +231,7 @@ func (f *fakeLibusb) getStringDesc(*libusbDevHandle, int) (string, error) {
func (f *fakeLibusb) setAutoDetach(*libusbDevHandle, int) error { return nil }
func (f *fakeLibusb) claim(d *libusbDevHandle, intf uint8) error {
debug.Printf("claim(%p, %d)\n", d, intf)
f.mu.Lock()
defer f.mu.Unlock()
c := f.claims[f.handles[d]]
@@ -241,6 +243,7 @@ func (f *fakeLibusb) claim(d *libusbDevHandle, intf uint8) error {
return nil
}
func (f *fakeLibusb) release(d *libusbDevHandle, intf uint8) {
debug.Printf("release(%p, %d)\n", d, intf)
f.mu.Lock()
defer f.mu.Unlock()
c := f.claims[f.handles[d]]
@@ -250,6 +253,7 @@ func (f *fakeLibusb) release(d *libusbDevHandle, intf uint8) {
c[intf] = false
}
func (f *fakeLibusb) setAlt(d *libusbDevHandle, intf, alt uint8) error {
debug.Printf("setAlt(%p, %d, %d)\n", d, intf, alt)
f.mu.Lock()
defer f.mu.Unlock()
if !f.claims[f.handles[d]][intf] {

127
usb/interface.go Normal file
View File

@@ -0,0 +1,127 @@
// Copyright 2013 Google Inc. All rights reserved.
// Copyright 2016 the gousb Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package usb
import (
"fmt"
)
// InterfaceInfo contains information about a USB interface, extracted from
// the descriptor.
type InterfaceInfo struct {
// Number is the number of this interface, a zero-based index in the array
// of interfaces supported by the device configuration.
Number int
// AltSettings is a list of alternate settings supported by the interface.
AltSettings []InterfaceSetting
}
// String returns a human-readable descripton of the interface and it's
// alternate settings.
func (i InterfaceInfo) String() string {
return fmt.Sprintf("if=%d", i.Number)
}
// InterfaceSetting contains information about a USB interface with a particular
// alternate setting, extracted from the descriptor.
type InterfaceSetting struct {
// Number is the number of this interface, the same as in InterfaceInfo.
Number int
// Alternate is the number of this alternate setting.
Alternate int
// Class is the USB-IF class code, as defined by the USB spec.
Class Class
// SubClass is the USB-IF subclass code, as defined by the USB spec.
SubClass Class
// Protocol is USB protocol code, as defined by the USB spe.c
Protocol Protocol
// Endpoints has the list of endpoints available on this interface with
// this alternate setting.
Endpoints []EndpointInfo
}
// String returns a human-readable descripton of the particular
// alternate setting of an interface.
func (a InterfaceSetting) String() string {
return fmt.Sprintf("Interface %d alternate setting %d", a.Number, a.Alternate)
}
type Interface struct {
Setting InterfaceSetting
config *Config
}
func (i *Interface) String() string {
return fmt.Sprintf("%s,if=%d,alt=%d", i.config, i.Setting.Number, i.Setting.Alternate)
}
// Close releases the interface.
func (i *Interface) Close() {
libusb.release(i.config.dev.handle, uint8(i.Setting.Number))
i.config.mu.Lock()
defer i.config.mu.Unlock()
delete(i.config.claimed, i.Setting.Number)
}
func (i *Interface) openEndpoint(epNum int) (*endpoint, error) {
var ep EndpointInfo
var found bool
for _, e := range i.Setting.Endpoints {
if e.Number == epNum {
debug.Printf("found ep %s in %s\n", e, i)
ep = e
found = true
break
}
}
if !found {
return nil, fmt.Errorf("%s does not have endpoint number %d. Available endpoints: %v", epNum, i.Setting.Endpoints)
}
return &endpoint{
InterfaceSetting: i.Setting,
Info: ep,
h: i.config.dev.handle,
}, nil
}
// InEndpoint prepares an IN endpoint for transfer.
func (i *Interface) InEndpoint(epNum int) (*InEndpoint, error) {
ep, err := i.openEndpoint(epNum)
if err != nil {
return nil, err
}
if ep.Info.Direction != EndpointDirectionIn {
return nil, fmt.Errorf("%s is not an IN endpoint", ep)
}
return &InEndpoint{
endpoint: ep,
}, nil
}
// OutEndpoint prepares an OUT endpoint for transfer.
func (i *Interface) OutEndpoint(epNum int) (*OutEndpoint, error) {
ep, err := i.openEndpoint(epNum)
if err != nil {
return nil, err
}
if ep.Info.Direction != EndpointDirectionOut {
return nil, fmt.Errorf("%s is not an OUT endpoint", ep)
}
return &OutEndpoint{
endpoint: ep,
}, nil
}

View File

@@ -226,7 +226,7 @@ func TestReadStream(t *testing.T) {
got := make([]readRes, len(tc.want))
for i := range tc.want {
if i == tc.closeBefore-1 {
t.Logf("Close()", tcNum)
t.Log("Close()")
s.Close()
}
n, err := s.Read(buf)

View File

@@ -68,7 +68,7 @@ func (c *Context) ListDevices(each func(desc *Descriptor) bool) ([]*Device, erro
reterr = err
continue
}
ret = append(ret, newDevice(handle, desc))
ret = append(ret, &Device{handle: handle, Descriptor: desc})
} else {
libusb.dereference(dev)
}