Introduce two more layers between device and endpoint:
device -> active config -> claimed interface -> endpoints. Device can have at most one active config. A config can have multiple claimed interfaces, each with a specific alt setting. An interface with an alt setting defines a list of endpoints.
This commit is contained in:
116
usb/config.go
116
usb/config.go
@@ -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
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
|
193
usb/device.go
193
usb/device.go
@@ -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
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -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()
|
||||
|
@@ -86,7 +86,7 @@ func TestEndpoint(t *testing.T) {
|
||||
lib := newFakeLibusb()
|
||||
libusb = lib
|
||||
|
||||
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
|
||||
@@ -117,7 +117,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{
|
||||
@@ -128,7 +128,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{
|
||||
@@ -138,7 +138,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 {
|
||||
@@ -147,7 +147,7 @@ func TestEndpointInfo(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestEndpointIn(t *testing.T) {
|
||||
func TestEndpointInOut(t *testing.T) {
|
||||
defer func(i libusbIntf) { libusb = i }(libusb)
|
||||
|
||||
lib := 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,51 +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 := newFakeLibusb()
|
||||
libusb = lib
|
||||
|
||||
ctx := NewContext()
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@@ -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
127
usb/interface.go
Normal 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
|
||||
}
|
@@ -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)
|
||||
}
|
||||
|
Reference in New Issue
Block a user