diff --git a/config.go b/config.go index eae1828..3c53510 100644 --- a/config.go +++ b/config.go @@ -50,14 +50,26 @@ func (c ConfigDesc) String() string { } func (c ConfigDesc) intfDesc(num, alt int) (*InterfaceSetting, error) { - if num < 0 || num >= len(c.Interfaces) { - return nil, fmt.Errorf("interface %d not found, available interfaces 0..%d", num, len(c.Interfaces)-1) + // 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 := range c.Interfaces { + ifs[i] = c.Interfaces[i].Number + if ifs[i] != num { + continue + } + alts := make([]int, len(c.Interfaces[i].AltSettings)) + for a := range c.Interfaces[i].AltSettings { + alts[a] = c.Interfaces[i].AltSettings[a].Alternate + if alts[a] == alt { + return &c.Interfaces[i].AltSettings[a], nil + } + } + return nil, fmt.Errorf("alternate setting %d not found for %s, available alt settings: %v", alt, c.Interfaces[i], alts) } - ifInfo := c.Interfaces[num] - if alt < 0 || alt >= len(ifInfo.AltSettings) { - return nil, fmt.Errorf("alternate setting %d not found for %s, available alt settings 0..%d", alt, ifInfo, len(ifInfo.AltSettings)-1) - } - return &ifInfo.AltSettings[alt], nil + 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. diff --git a/device_test.go b/device_test.go index d16e8ac..4710a8a 100644 --- a/device_test.go +++ b/device_test.go @@ -81,6 +81,7 @@ func TestClaimAndRelease(t *testing.T) { {1, 0, "Fast streaming"}, {1, 1, "Slower streaming"}, {1, 2, ""}, + {3, 2, "Interface for https://github.com/google/gousb/issues/65"}, } { if got, err := dev.InterfaceDescription(1, tc.intf, tc.alt); err != nil { t.Errorf("%s.InterfaceDescription(1, %d, %d): %v", dev, tc.intf, tc.alt, err) diff --git a/fakelibusb_devices.go b/fakelibusb_devices.go index fbc5aaa..ff1cf1a 100644 --- a/fakelibusb_devices.go +++ b/fakelibusb_devices.go @@ -29,7 +29,7 @@ var fakeDevices = []fakeDevice{ devDesc: &DeviceDesc{ Bus: 1, Address: 1, - Port: 1, + Port: 1, Spec: Version(2, 0), Device: Version(1, 0), Vendor: ID(0x9999), @@ -73,7 +73,7 @@ var fakeDevices = []fakeDevice{ devDesc: &DeviceDesc{ Bus: 1, Address: 2, - Port: 2, + Port: 2, Spec: Version(2, 0), Device: Version(1, 3), Vendor: ID(0x8888), @@ -158,6 +158,18 @@ var fakeDevices = []fakeDevice{ }, }, }}, + }, { + // Malformed descriptior, non contiguous interface + // number (previous interface is #1), alt settings + // not starting from 0. + // See https://github.com/google/gousb/issues/65 + Number: 3, + AltSettings: []InterfaceSetting{{ + Number: 3, + Alternate: 2, + Class: ClassVendorSpec, + iInterface: 9, + }}, }}, }}, iManufacturer: 1, @@ -172,6 +184,7 @@ var fakeDevices = []fakeDevice{ 6: "Boring setting", 7: "Fast streaming", 8: "Slower streaming", + 9: "Interface for https://github.com/google/gousb/issues/65", }, }, // Bus 001 Device 003: ID 9999:0002 @@ -181,7 +194,7 @@ var fakeDevices = []fakeDevice{ devDesc: &DeviceDesc{ Bus: 1, Address: 3, - Port: 3, + Port: 3, Spec: Version(2, 0), Device: Version(1, 0), Vendor: ID(0x1111), diff --git a/interface.go b/interface.go index 318de2d..0af77a9 100644 --- a/interface.go +++ b/interface.go @@ -23,8 +23,7 @@ import ( // InterfaceDesc contains information about a USB interface, extracted from // the descriptor. type InterfaceDesc struct { - // Number is the number of this interface, a zero-based index in the array - // of interfaces supported by the device configuration. + // Number is the number of this interface. Number int // AltSettings is a list of alternate settings supported by the interface. AltSettings []InterfaceSetting diff --git a/libusb.go b/libusb.go index 2af2dac..44daf87 100644 --- a/libusb.go +++ b/libusb.go @@ -271,7 +271,9 @@ func (libusbImpl) getDeviceDesc(d *libusbDevice) (*DeviceDesc, error) { Cap: int(cfg.bNumInterfaces), } c.Interfaces = make([]InterfaceDesc, 0, len(ifaces)) - for ifNum, iface := range ifaces { + // a map of interface numbers to a set of alternate settings numbers + hasIntf := make(map[int]map[int]bool) + for _, iface := range ifaces { if iface.num_altsetting == 0 { continue } @@ -283,7 +285,7 @@ func (libusbImpl) getDeviceDesc(d *libusbDevice) (*DeviceDesc, error) { Cap: int(iface.num_altsetting), } descs := make([]InterfaceSetting, 0, len(alts)) - for altNum, alt := range alts { + for _, alt := range alts { i := InterfaceSetting{ Number: int(alt.bInterfaceNumber), Alternate: int(alt.bAlternateSetting), @@ -292,12 +294,16 @@ func (libusbImpl) getDeviceDesc(d *libusbDevice) (*DeviceDesc, error) { Protocol: Protocol(alt.bInterfaceProtocol), iInterface: int(alt.iInterface), } - if ifNum != i.Number { - return nil, fmt.Errorf("config %d interface at index %d has number %d, USB standard states they should be identical", c.Number, ifNum, i.Number) + + if hasIntf[i.Number][i.Alternate] { + log.Printf("Device on bus %d address %d offered a descriptor for config %d with two different entries with the same interface number (%d) and the same alternate setting number (%d). gousb will use only the first one.", dev.Bus, dev.Address, c.Number, i.Number, i.Alternate) + continue } - if altNum != i.Alternate { - return nil, fmt.Errorf("config %d interface %d alternate settings at index %d has number %d, USB standard states they should be identical", c.Number, i.Number, altNum, i.Alternate) + if hasIntf[i.Number] == nil { + hasIntf[i.Number] = make(map[int]bool) } + hasIntf[i.Number][i.Alternate] = true + var ends []C.struct_libusb_endpoint_descriptor *(*reflect.SliceHeader)(unsafe.Pointer(&ends)) = reflect.SliceHeader{ Data: uintptr(unsafe.Pointer(alt.endpoint)),