// 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 }