
Do not assume interface numbers follow the slice indices.
This is a continuation of
9ad54830f4
which tried to solve the problem of non-contiguous interface indices;
this commit modifies another code path that had the same assumption.
149 lines
4.8 KiB
Go
149 lines
4.8 KiB
Go
// 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 gousb
|
|
|
|
import (
|
|
"fmt"
|
|
"sync"
|
|
)
|
|
|
|
// ConfigDesc contains the information about a USB device configuration,
|
|
// extracted from the device descriptor.
|
|
type ConfigDesc struct {
|
|
// Number is the configuration number.
|
|
Number int
|
|
// SelfPowered is true if the device is powered externally, i.e. not
|
|
// drawing power from the USB bus.
|
|
SelfPowered bool
|
|
// RemoteWakeup is true if the device supports remote wakeup, i.e.
|
|
// an external signal that will wake up a suspended USB device. An example
|
|
// might be a keyboard that can wake up through a keypress after
|
|
// the host put it in suspend mode. Note that gousb does not support
|
|
// device power management, RemoteWakeup only refers to the reported device
|
|
// capability.
|
|
RemoteWakeup bool
|
|
// MaxPower is the maximum current the device draws from the USB bus
|
|
// in this configuration.
|
|
MaxPower Milliamperes
|
|
// Interfaces has a list of USB interfaces available in this configuration.
|
|
Interfaces []InterfaceDesc
|
|
|
|
iConfiguration int // index of a string descriptor describing this configuration
|
|
}
|
|
|
|
// String returns the human-readable description of the configuration descriptor.
|
|
func (c ConfigDesc) String() string {
|
|
return fmt.Sprintf("Configuration %d", c.Number)
|
|
}
|
|
|
|
func (c ConfigDesc) intfDesc(num int) (*InterfaceDesc, error) {
|
|
// In an ideal world, interfaces in the descriptor would be numbered
|
|
// contiguously starting from 0, as required by the specification. In the
|
|
// real world however the specification is sometimes ignored:
|
|
// https://github.com/google/gousb/issues/65
|
|
ifs := make([]int, len(c.Interfaces))
|
|
for i, desc := range c.Interfaces {
|
|
if desc.Number == num {
|
|
return &desc, nil
|
|
}
|
|
ifs[i] = desc.Number
|
|
}
|
|
return nil, fmt.Errorf("interface %d not found, available interface numbers: %v", num, ifs)
|
|
}
|
|
|
|
// 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.
|
|
// To access device endpoints, claim an interface and it's alternate
|
|
// setting number through a call to Interface().
|
|
type Config struct {
|
|
Desc ConfigDesc
|
|
|
|
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 {
|
|
if c.dev == nil {
|
|
return nil
|
|
}
|
|
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
|
|
c.dev = nil
|
|
return nil
|
|
}
|
|
|
|
// String returns the human-readable description of the configuration.
|
|
func (c *Config) String() string {
|
|
return fmt.Sprintf("%s,config=%d", c.dev.String(), c.Desc.Number)
|
|
}
|
|
|
|
// Interface claims and returns an interface on a USB device.
|
|
// num specifies the number of an interface to claim, and alt specifies the
|
|
// alternate setting number for that interface.
|
|
func (c *Config) Interface(num, alt int) (*Interface, error) {
|
|
if c.dev == nil {
|
|
return nil, fmt.Errorf("Interface(%d, %d) called on %s after Close", num, alt, c)
|
|
}
|
|
|
|
intf, err := c.Desc.intfDesc(num)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("descriptor of interface %d in %s: %v", num, c, err)
|
|
}
|
|
altInfo, err := intf.altSetting(alt)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("descriptor of alternate setting %d of interface %d in %s: %v", alt, num, c, err)
|
|
}
|
|
|
|
c.mu.Lock()
|
|
defer c.mu.Unlock()
|
|
if c.claimed[num] {
|
|
return nil, fmt.Errorf("interface %d on %s is already claimed", num, c)
|
|
}
|
|
|
|
// Claim the interface
|
|
if err := c.dev.ctx.libusb.claim(c.dev.handle, uint8(num)); err != nil {
|
|
return nil, fmt.Errorf("failed to claim interface %d on %s: %v", num, c, err)
|
|
}
|
|
|
|
// Select an alternate setting if needed (device has multiple alternate settings).
|
|
if len(intf.AltSettings) > 1 {
|
|
if err := c.dev.ctx.libusb.setAlt(c.dev.handle, uint8(num), uint8(alt)); err != nil {
|
|
c.dev.ctx.libusb.release(c.dev.handle, uint8(num))
|
|
return nil, fmt.Errorf("failed to set alternate config %d on interface %d of %s: %v", alt, num, c, err)
|
|
}
|
|
}
|
|
|
|
c.claimed[num] = true
|
|
return &Interface{
|
|
Setting: *altInfo,
|
|
config: c,
|
|
}, nil
|
|
}
|