Merge branch 'gousb2.0-dev' of https://github.com/kylelemons/gousb into gousb2.0-dev
This commit is contained in:
116
usb/config.go
116
usb/config.go
@@ -15,47 +15,11 @@
|
|||||||
|
|
||||||
package usb
|
package usb
|
||||||
|
|
||||||
import "fmt"
|
import (
|
||||||
|
"fmt"
|
||||||
// InterfaceInfo contains information about a USB interface, extracted from
|
"sync"
|
||||||
// the descriptor.
|
"time"
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConfigInfo contains the information about a USB device configuration.
|
// ConfigInfo contains the information about a USB device configuration.
|
||||||
type ConfigInfo struct {
|
type ConfigInfo struct {
|
||||||
@@ -75,5 +39,73 @@ type ConfigInfo struct {
|
|||||||
|
|
||||||
// String returns the human-readable description of the configuration.
|
// String returns the human-readable description of the configuration.
|
||||||
func (c ConfigInfo) String() string {
|
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
|
||||||
}
|
}
|
||||||
|
@@ -33,5 +33,5 @@ func init() {
|
|||||||
if os.Getenv(debugEnvVarName) != "" {
|
if os.Getenv(debugEnvVarName) != "" {
|
||||||
out = os.Stderr
|
out = os.Stderr
|
||||||
}
|
}
|
||||||
debug = log.New(out, "usb", log.LstdFlags|log.Lshortfile)
|
debug = log.New(out, "gousb: ", log.LstdFlags|log.Lshortfile)
|
||||||
}
|
}
|
||||||
|
@@ -15,6 +15,8 @@
|
|||||||
|
|
||||||
package usb
|
package usb
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
// Descriptor is a representation of a USB device descriptor.
|
// Descriptor is a representation of a USB device descriptor.
|
||||||
type Descriptor struct {
|
type Descriptor struct {
|
||||||
// Bus information
|
// Bus information
|
||||||
@@ -37,3 +39,8 @@ type Descriptor struct {
|
|||||||
// Configuration information
|
// Configuration information
|
||||||
Configs []ConfigInfo
|
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 (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
"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.
|
// Device represents an opened USB device.
|
||||||
@@ -37,39 +27,19 @@ type Device struct {
|
|||||||
// Embed the device information for easy access
|
// Embed the device information for easy access
|
||||||
*Descriptor
|
*Descriptor
|
||||||
|
|
||||||
// Timeouts
|
// Claimed config
|
||||||
ReadTimeout time.Duration
|
mu sync.Mutex
|
||||||
WriteTimeout time.Duration
|
claimed *Config
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset performs a USB port reset to reinitialize a device.
|
// Reset performs a USB port reset to reinitialize a device.
|
||||||
func (d *Device) Reset() error {
|
func (d *Device) Reset() error {
|
||||||
return libusb.reset(d.handle)
|
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.
|
// ActiveConfig returns the config id (not the index) of the active configuration.
|
||||||
@@ -79,136 +49,51 @@ func (d *Device) ActiveConfig() (int, error) {
|
|||||||
return int(ret), err
|
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,
|
// The cfg provided is the config id (not the index) of the configuration to set,
|
||||||
// which corresponds to the ConfigInfo.Config field.
|
// which corresponds to the ConfigInfo.Config field.
|
||||||
func (d *Device) SetConfig(cfg int) error {
|
// USB supports only one active config per device at a time. Config claims the
|
||||||
return libusb.setConfig(d.handle, uint8(cfg))
|
// 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.
|
// Close closes the device.
|
||||||
func (d *Device) Close() error {
|
func (d *Device) Close() error {
|
||||||
if d.handle == nil {
|
if d.handle == nil {
|
||||||
return fmt.Errorf("usb: double close on device")
|
return fmt.Errorf("double close on device %s", d)
|
||||||
}
|
}
|
||||||
d.lock.Lock()
|
d.mu.Lock()
|
||||||
defer d.lock.Unlock()
|
defer d.mu.Unlock()
|
||||||
for iface := range d.claimed {
|
if d.claimed != nil {
|
||||||
libusb.release(d.handle, uint8(iface))
|
return fmt.Errorf("can't release the device %s, it has an open config %s", d, d.claimed.Info.Config)
|
||||||
}
|
}
|
||||||
libusb.close(d.handle)
|
libusb.close(d.handle)
|
||||||
d.handle = nil
|
d.handle = nil
|
||||||
return 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
|
// GetStringDescriptor returns a device string descriptor with the given index
|
||||||
// number. The first supported language is always used and the returned
|
// number. The first supported language is always used and the returned
|
||||||
// descriptor string is converted to ASCII (non-ASCII characters are replaced
|
// descriptor string is converted to ASCII (non-ASCII characters are replaced
|
||||||
|
@@ -31,9 +31,19 @@ func TestOpenEndpoint(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer dev.Close()
|
defer dev.Close()
|
||||||
if err != nil {
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("InEndpoint(cfg=1, if=1, alt=1, ep=6IN): got error %v, want nil", err)
|
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.
|
// String returns the human-readable description of the endpoint.
|
||||||
func (e EndpointInfo) String() string {
|
func (e EndpointInfo) String() string {
|
||||||
ret := make([]string, 0, 3)
|
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 {
|
switch e.TransferType {
|
||||||
case TransferTypeIsochronous:
|
case TransferTypeIsochronous:
|
||||||
ret = append(ret, fmt.Sprintf("- %s %s", e.IsoSyncType, e.UsageType))
|
ret = append(ret, fmt.Sprintf("- %s %s", e.IsoSyncType, e.UsageType))
|
||||||
@@ -74,7 +74,7 @@ type endpoint struct {
|
|||||||
InterfaceSetting
|
InterfaceSetting
|
||||||
Info EndpointInfo
|
Info EndpointInfo
|
||||||
|
|
||||||
timeout time.Duration
|
Timeout time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
// String returns a human-readable description of the endpoint.
|
// String returns a human-readable description of the endpoint.
|
||||||
@@ -82,18 +82,12 @@ func (e *endpoint) String() string {
|
|||||||
return e.Info.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) {
|
func (e *endpoint) transfer(buf []byte) (int, error) {
|
||||||
if len(buf) == 0 {
|
if len(buf) == 0 {
|
||||||
return 0, nil
|
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 {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
@@ -110,14 +104,6 @@ func (e *endpoint) transfer(buf []byte) (int, error) {
|
|||||||
return n, nil
|
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.
|
// InEndpoint represents an IN endpoint open for transfer.
|
||||||
type InEndpoint struct {
|
type InEndpoint struct {
|
||||||
*endpoint
|
*endpoint
|
||||||
|
@@ -17,7 +17,7 @@ package usb
|
|||||||
func (e *endpoint) newStream(size, count int, submit bool) (*stream, error) {
|
func (e *endpoint) newStream(size, count int, submit bool) (*stream, error) {
|
||||||
var ts []transferIntf
|
var ts []transferIntf
|
||||||
for i := 0; i < count; i++ {
|
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 {
|
if err != nil {
|
||||||
for _, t := range ts {
|
for _, t := range ts {
|
||||||
t.free()
|
t.free()
|
||||||
|
@@ -85,7 +85,7 @@ func TestEndpoint(t *testing.T) {
|
|||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
ep := newEndpoint(nil, epData.intf, epData.ei)
|
ep := &endpoint{h: nil, InterfaceSetting: epData.intf, Info: epData.ei}
|
||||||
go func() {
|
go func() {
|
||||||
fakeT := lib.waitForSubmitted()
|
fakeT := lib.waitForSubmitted()
|
||||||
fakeT.length = tc.ret
|
fakeT.length = tc.ret
|
||||||
@@ -116,7 +116,7 @@ func TestEndpointInfo(t *testing.T) {
|
|||||||
TransferType: TransferTypeBulk,
|
TransferType: TransferTypeBulk,
|
||||||
MaxPacketSize: 512,
|
MaxPacketSize: 512,
|
||||||
},
|
},
|
||||||
want: "Endpoint #6 IN (address 0x86) bulk [512 bytes]",
|
want: "ep #6 IN (address 0x86) bulk [512 bytes]",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ep: EndpointInfo{
|
ep: EndpointInfo{
|
||||||
@@ -127,7 +127,7 @@ func TestEndpointInfo(t *testing.T) {
|
|||||||
IsoSyncType: IsoSyncTypeAsync,
|
IsoSyncType: IsoSyncTypeAsync,
|
||||||
UsageType: IsoUsageTypeData,
|
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{
|
ep: EndpointInfo{
|
||||||
@@ -137,7 +137,7 @@ func TestEndpointInfo(t *testing.T) {
|
|||||||
MaxPacketSize: 16,
|
MaxPacketSize: 16,
|
||||||
UsageType: InterruptUsageTypePeriodic,
|
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 {
|
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)
|
defer func(i libusbIntf) { libusb = i }(libusb)
|
||||||
|
|
||||||
lib, done := newFakeLibusb()
|
lib, done := newFakeLibusb()
|
||||||
@@ -158,9 +158,30 @@ func TestEndpointIn(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("OpenDeviceWithVidPid(0x9999, 0x0001): got error %v, want nil", err)
|
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 {
|
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
|
dataTransferred := 100
|
||||||
go func() {
|
go func() {
|
||||||
@@ -170,52 +191,38 @@ func TestEndpointIn(t *testing.T) {
|
|||||||
close(fakeT.done)
|
close(fakeT.done)
|
||||||
}()
|
}()
|
||||||
buf := make([]byte, 512)
|
buf := make([]byte, 512)
|
||||||
got, err := ep.Read(buf)
|
got, err := iep.Read(buf)
|
||||||
if err != nil {
|
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 {
|
} 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 {
|
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) {
|
// OUT endpoint 1
|
||||||
defer func(i libusbIntf) { libusb = i }(libusb)
|
oep, err := intf.OutEndpoint(1)
|
||||||
|
|
||||||
lib, done := newFakeLibusb()
|
|
||||||
defer done()
|
|
||||||
|
|
||||||
ctx := NewContext()
|
|
||||||
defer ctx.Close()
|
|
||||||
d, err := ctx.OpenDeviceWithVidPid(0x9999, 0x0001)
|
|
||||||
if err != nil {
|
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() {
|
go func() {
|
||||||
fakeT := lib.waitForSubmitted()
|
fakeT := lib.waitForSubmitted()
|
||||||
fakeT.length = dataTransferred
|
fakeT.length = dataTransferred
|
||||||
fakeT.status = TransferCompleted
|
fakeT.status = TransferCompleted
|
||||||
close(fakeT.done)
|
close(fakeT.done)
|
||||||
}()
|
}()
|
||||||
buf := make([]byte, 512)
|
got, err = oep.Write(buf)
|
||||||
got, err := ep.Write(buf)
|
|
||||||
if err != nil {
|
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 {
|
} 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 {
|
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) getConfig(*libusbDevHandle) (uint8, error) { return 1, nil }
|
||||||
func (f *fakeLibusb) setConfig(d *libusbDevHandle, cfg uint8) error {
|
func (f *fakeLibusb) setConfig(d *libusbDevHandle, cfg uint8) error {
|
||||||
|
debug.Printf("setConfig(%p, %d)\n", d, cfg)
|
||||||
f.mu.Lock()
|
f.mu.Lock()
|
||||||
defer f.mu.Unlock()
|
defer f.mu.Unlock()
|
||||||
if len(f.claims[f.handles[d]]) != 0 {
|
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) setAutoDetach(*libusbDevHandle, int) 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)
|
||||||
f.mu.Lock()
|
f.mu.Lock()
|
||||||
defer f.mu.Unlock()
|
defer f.mu.Unlock()
|
||||||
c := f.claims[f.handles[d]]
|
c := f.claims[f.handles[d]]
|
||||||
@@ -241,6 +243,7 @@ func (f *fakeLibusb) claim(d *libusbDevHandle, intf uint8) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func (f *fakeLibusb) release(d *libusbDevHandle, intf uint8) {
|
func (f *fakeLibusb) release(d *libusbDevHandle, intf uint8) {
|
||||||
|
debug.Printf("release(%p, %d)\n", d, intf)
|
||||||
f.mu.Lock()
|
f.mu.Lock()
|
||||||
defer f.mu.Unlock()
|
defer f.mu.Unlock()
|
||||||
c := f.claims[f.handles[d]]
|
c := f.claims[f.handles[d]]
|
||||||
@@ -250,6 +253,7 @@ func (f *fakeLibusb) release(d *libusbDevHandle, intf uint8) {
|
|||||||
c[intf] = false
|
c[intf] = false
|
||||||
}
|
}
|
||||||
func (f *fakeLibusb) setAlt(d *libusbDevHandle, intf, alt uint8) error {
|
func (f *fakeLibusb) setAlt(d *libusbDevHandle, intf, alt uint8) error {
|
||||||
|
debug.Printf("setAlt(%p, %d, %d)\n", d, intf, alt)
|
||||||
f.mu.Lock()
|
f.mu.Lock()
|
||||||
defer f.mu.Unlock()
|
defer f.mu.Unlock()
|
||||||
if !f.claims[f.handles[d]][intf] {
|
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
|
||||||
|
}
|
@@ -226,7 +226,7 @@ func TestReadStream(t *testing.T) {
|
|||||||
got := make([]readRes, len(tc.want))
|
got := make([]readRes, len(tc.want))
|
||||||
for i := range tc.want {
|
for i := range tc.want {
|
||||||
if i == tc.closeBefore-1 {
|
if i == tc.closeBefore-1 {
|
||||||
t.Logf("Close()", tcNum)
|
t.Log("Close()")
|
||||||
s.Close()
|
s.Close()
|
||||||
}
|
}
|
||||||
n, err := s.Read(buf)
|
n, err := s.Read(buf)
|
||||||
|
@@ -68,7 +68,7 @@ func (c *Context) ListDevices(each func(desc *Descriptor) bool) ([]*Device, erro
|
|||||||
reterr = err
|
reterr = err
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
ret = append(ret, newDevice(handle, desc))
|
ret = append(ret, &Device{handle: handle, Descriptor: desc})
|
||||||
} else {
|
} else {
|
||||||
libusb.dereference(dev)
|
libusb.dereference(dev)
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user