Add Device.Manufacturer(), .Product() and .SerialNumber() (#14)

This commit is contained in:
Deomid Ryabkov
2017-09-04 01:42:55 +03:00
committed by zagrodzki
parent 1aaa100bdb
commit 757722bf8e
6 changed files with 233 additions and 159 deletions

View File

@@ -13,3 +13,4 @@ Thordur Bjornsson <thorduri@secnorth.net>
Vincent Serpoul <vincent@serpoul.com> Vincent Serpoul <vincent@serpoul.com>
Josef Filzmaier <josef.filzmaier@gmail.com> Josef Filzmaier <josef.filzmaier@gmail.com>
Nico MT <nicovell3@gmail.com> Nico MT <nicovell3@gmail.com>
Deomid "rojer" Ryabkov <rojer@rojer.me>

View File

@@ -45,6 +45,10 @@ type DeviceDesc struct {
// Configuration information // Configuration information
Configs map[int]ConfigDesc Configs map[int]ConfigDesc
iManufacturer int // The Manufacturer descriptor index
iProduct int // The Product descriptor index
iSerialNumber int // The SerialNumber descriptor index
} }
// String returns a human-readable version of the device descriptor. // String returns a human-readable version of the device descriptor.
@@ -211,6 +215,24 @@ func (d *Device) GetStringDescriptor(descIndex int) (string, error) {
return libusb.getStringDesc(d.handle, descIndex) return libusb.getStringDesc(d.handle, descIndex)
} }
// Manufacturer returns the device's manufacturer name.
// GetStringDescriptor's string conversion rules apply.
func (d *Device) Manufacturer() (string, error) {
return d.GetStringDescriptor(d.Desc.iManufacturer)
}
// Product returns the device's product name.
// GetStringDescriptor's string conversion rules apply.
func (d *Device) Product() (string, error) {
return d.GetStringDescriptor(d.Desc.iProduct)
}
// SerialNumber returns the device's serial number.
// GetStringDescriptor's string conversion rules apply.
func (d *Device) SerialNumber() (string, error) {
return d.GetStringDescriptor(d.Desc.iSerialNumber)
}
// SetAutoDetach enables/disables automatic kernel driver detachment. // SetAutoDetach enables/disables automatic kernel driver detachment.
// When autodetach is enabled gousb will automatically detach the kernel driver // When autodetach is enabled gousb will automatically detach the kernel driver
// on the interface and reattach it when releasing the interface. // on the interface and reattach it when releasing the interface.

View File

@@ -44,6 +44,28 @@ func TestClaimAndRelease(t *testing.T) {
t.Fatalf("OpenDeviceWithVIDPID(0x8888, 0x0002): %v", err) t.Fatalf("OpenDeviceWithVIDPID(0x8888, 0x0002): %v", err)
} }
mfg, err := dev.Manufacturer()
if err != nil {
t.Errorf("%s.Manufacturer(): %v", dev, err)
}
if mfg != "ACME Industries" {
t.Errorf("%s.Manufacturer(): %q", dev, mfg)
}
prod, err := dev.Product()
if err != nil {
t.Errorf("%s.Product(): %v", dev, err)
}
if prod != "Fidgety Gadget" {
t.Errorf("%s.Product(): %q", dev, prod)
}
sn, err := dev.SerialNumber()
if err != nil {
t.Errorf("%s.SerialNumber(): %v", dev, err)
}
if sn != "01234567" {
t.Errorf("%s.SerialNumber(): %q", dev, sn)
}
if err = dev.SetAutoDetach(true); err != nil { if err = dev.SetAutoDetach(true); err != nil {
t.Fatalf("%s.SetAutoDetach(true): %v", dev, err) t.Fatalf("%s.SetAutoDetach(true): %v", dev, err)
} }
@@ -67,7 +89,7 @@ func TestClaimAndRelease(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("%s.InEndpoint(%d): got error %v, want nil", intf, ep1Addr, err) t.Fatalf("%s.InEndpoint(%d): got error %v, want nil", intf, ep1Addr, err)
} }
if want := fakeDevices[devIdx].Configs[cfgNum].Interfaces[if1Num].AltSettings[alt1Num].Endpoints[ep1Addr]; !reflect.DeepEqual(got.Desc, want) { if want := fakeDevices[devIdx].devDesc.Configs[cfgNum].Interfaces[if1Num].AltSettings[alt1Num].Endpoints[ep1Addr]; !reflect.DeepEqual(got.Desc, want) {
t.Errorf("%s.InEndpoint(%d): got %+v, want %+v", intf, ep1Addr, got, want) t.Errorf("%s.InEndpoint(%d): got %+v, want %+v", intf, ep1Addr, got, want)
} }
@@ -117,6 +139,10 @@ func TestClaimAndRelease(t *testing.T) {
t.Fatalf("%s.Close(): got error %v, want nil", dev, err) t.Fatalf("%s.Close(): got error %v, want nil", dev, err)
} }
if _, err := dev.Manufacturer(); err == nil {
t.Errorf("%s.Manufacturer(): expected an error after device is closed", dev)
}
if _, err := dev.Config(cfgNum); err == nil { if _, err := dev.Config(cfgNum); err == nil {
t.Fatalf("%s.Config(1): got error nil, want no nil because it is closed", dev) t.Fatalf("%s.Config(1): got error nil, want no nil because it is closed", dev)
} }

View File

@@ -15,175 +15,195 @@
package gousb package gousb
// fake devices connected through the fakeLibusb stack. // fake devices connected through the fakeLibusb stack.
var fakeDevices = []*DeviceDesc{ type fakeDevice struct {
devDesc *DeviceDesc
strDesc map[int]string
alt uint8
}
var fakeDevices = []fakeDevice{
// Bus 001 Device 001: ID 9999:0001 // Bus 001 Device 001: ID 9999:0001
// One config, one interface, one setup, // One config, one interface, one setup,
// two endpoints: 0x01 OUT, 0x82 IN. // two endpoints: 0x01 OUT, 0x82 IN.
&DeviceDesc{ {
Bus: 1, devDesc: &DeviceDesc{
Address: 1, Bus: 1,
Spec: Version(2, 0), Address: 1,
Device: Version(1, 0), Spec: Version(2, 0),
Vendor: ID(0x9999), Device: Version(1, 0),
Product: ID(0x0001), Vendor: ID(0x9999),
Protocol: 255, Product: ID(0x0001),
Configs: map[int]ConfigDesc{1: { Protocol: 255,
Number: 1, Configs: map[int]ConfigDesc{1: {
MaxPower: Milliamperes(100), Number: 1,
Interfaces: []InterfaceDesc{{ MaxPower: Milliamperes(100),
Number: 0, Interfaces: []InterfaceDesc{{
AltSettings: []InterfaceSetting{{ Number: 0,
Number: 0, AltSettings: []InterfaceSetting{{
Alternate: 0, Number: 0,
Class: ClassVendorSpec, Alternate: 0,
Endpoints: map[EndpointAddress]EndpointDesc{ Class: ClassVendorSpec,
0x01: { Endpoints: map[EndpointAddress]EndpointDesc{
Address: 0x01, 0x01: {
Number: 1, Address: 0x01,
Direction: EndpointDirectionOut, Number: 1,
MaxPacketSize: 512, Direction: EndpointDirectionOut,
TransferType: TransferTypeBulk, MaxPacketSize: 512,
TransferType: TransferTypeBulk,
},
0x82: {
Address: 0x82,
Number: 2,
Direction: EndpointDirectionIn,
MaxPacketSize: 512,
TransferType: TransferTypeBulk,
},
}, },
0x82: { }},
Address: 0x82,
Number: 2,
Direction: EndpointDirectionIn,
MaxPacketSize: 512,
TransferType: TransferTypeBulk,
},
},
}}, }},
}}, }},
}}, },
}, },
// Bus 001 Device 002: ID 8888:0002 // Bus 001 Device 002: ID 8888:0002
// One config, two interfaces. interface #0 with no endpoints, // One config, two interfaces. interface #0 with no endpoints,
// interface #1 with two alt setups with different packet sizes for // interface #1 with two alt setups with different packet sizes for
// endpoints. Two isochronous endpoints, 0x05 OUT and 0x86 OUT. // endpoints. Two isochronous endpoints, 0x05 OUT and 0x86 OUT.
&DeviceDesc{ {
Bus: 1, devDesc: &DeviceDesc{
Address: 2, Bus: 1,
Spec: Version(2, 0), Address: 2,
Device: Version(1, 3), Spec: Version(2, 0),
Vendor: ID(0x8888), Device: Version(1, 3),
Product: ID(0x0002), Vendor: ID(0x8888),
Protocol: 255, Product: ID(0x0002),
Configs: map[int]ConfigDesc{1: { Protocol: 255,
Number: 1, Configs: map[int]ConfigDesc{1: {
MaxPower: Milliamperes(100), Number: 1,
Interfaces: []InterfaceDesc{{ MaxPower: Milliamperes(100),
Number: 0, Interfaces: []InterfaceDesc{{
AltSettings: []InterfaceSetting{{ Number: 0,
Number: 0, AltSettings: []InterfaceSetting{{
Alternate: 0, Number: 0,
Class: ClassVendorSpec, Alternate: 0,
}}, Class: ClassVendorSpec,
}, { }},
Number: 1,
AltSettings: []InterfaceSetting{{
Number: 1,
Alternate: 0,
Class: ClassVendorSpec,
Endpoints: map[EndpointAddress]EndpointDesc{
0x05: {
Address: 0x05,
Number: 5,
Direction: EndpointDirectionOut,
MaxPacketSize: 3 * 1024,
TransferType: TransferTypeIsochronous,
UsageType: IsoUsageTypeData,
},
0x86: {
Address: 0x86,
Number: 6,
Direction: EndpointDirectionIn,
MaxPacketSize: 3 * 1024,
TransferType: TransferTypeIsochronous,
UsageType: IsoUsageTypeData,
},
},
}, { }, {
Number: 1, Number: 1,
Alternate: 1, AltSettings: []InterfaceSetting{{
Class: ClassVendorSpec, Number: 1,
Endpoints: map[EndpointAddress]EndpointDesc{ Alternate: 0,
0x05: { Class: ClassVendorSpec,
Address: 0x05, Endpoints: map[EndpointAddress]EndpointDesc{
Number: 5, 0x05: {
Direction: EndpointDirectionOut, Address: 0x05,
MaxPacketSize: 2 * 1024, Number: 5,
TransferType: TransferTypeIsochronous, Direction: EndpointDirectionOut,
MaxPacketSize: 3 * 1024,
TransferType: TransferTypeIsochronous,
UsageType: IsoUsageTypeData,
},
0x86: {
Address: 0x86,
Number: 6,
Direction: EndpointDirectionIn,
MaxPacketSize: 3 * 1024,
TransferType: TransferTypeIsochronous,
UsageType: IsoUsageTypeData,
},
}, },
0x86: { }, {
Address: 0x86, Number: 1,
Number: 6, Alternate: 1,
Direction: EndpointDirectionIn, Class: ClassVendorSpec,
MaxPacketSize: 2 * 1024, Endpoints: map[EndpointAddress]EndpointDesc{
TransferType: TransferTypeIsochronous, 0x05: {
Address: 0x05,
Number: 5,
Direction: EndpointDirectionOut,
MaxPacketSize: 2 * 1024,
TransferType: TransferTypeIsochronous,
},
0x86: {
Address: 0x86,
Number: 6,
Direction: EndpointDirectionIn,
MaxPacketSize: 2 * 1024,
TransferType: TransferTypeIsochronous,
},
}, },
}, }, {
}, { Number: 1,
Number: 1, Alternate: 2,
Alternate: 2, Class: ClassVendorSpec,
Class: ClassVendorSpec, Endpoints: map[EndpointAddress]EndpointDesc{
Endpoints: map[EndpointAddress]EndpointDesc{ 0x05: {
0x05: { Address: 0x05,
Address: 0x05, Number: 5,
Number: 5, Direction: EndpointDirectionIn,
Direction: EndpointDirectionIn, MaxPacketSize: 1024,
MaxPacketSize: 1024, TransferType: TransferTypeIsochronous,
TransferType: TransferTypeIsochronous, },
0x86: {
Address: 0x86,
Number: 6,
Direction: EndpointDirectionIn,
MaxPacketSize: 1024,
TransferType: TransferTypeIsochronous,
},
}, },
0x86: { }},
Address: 0x86,
Number: 6,
Direction: EndpointDirectionIn,
MaxPacketSize: 1024,
TransferType: TransferTypeIsochronous,
},
},
}}, }},
}}, }},
}}, iManufacturer: 1,
iProduct: 2,
iSerialNumber: 3,
},
strDesc: map[int]string{
1: "ACME Industries",
2: "Fidgety Gadget",
3: "01234567",
},
}, },
// Bus 001 Device 003: ID 9999:0002 // Bus 001 Device 003: ID 9999:0002
// One config, one interface, one setup, // One config, one interface, one setup,
// two endpoints: 0x01 OUT, 0x81 IN. // two endpoints: 0x01 OUT, 0x81 IN.
&DeviceDesc{ {
Bus: 1, devDesc: &DeviceDesc{
Address: 3, Bus: 1,
Spec: Version(2, 0), Address: 3,
Device: Version(1, 0), Spec: Version(2, 0),
Vendor: ID(0x1111), Device: Version(1, 0),
Product: ID(0x1111), Vendor: ID(0x1111),
Protocol: 255, Product: ID(0x1111),
Configs: map[int]ConfigDesc{1: { Protocol: 255,
Number: 1, Configs: map[int]ConfigDesc{1: {
MaxPower: Milliamperes(100), Number: 1,
Interfaces: []InterfaceDesc{{ MaxPower: Milliamperes(100),
Number: 0, Interfaces: []InterfaceDesc{{
AltSettings: []InterfaceSetting{{ Number: 0,
Number: 0, AltSettings: []InterfaceSetting{{
Alternate: 0, Number: 0,
Class: ClassVendorSpec, Alternate: 0,
Endpoints: map[EndpointAddress]EndpointDesc{ Class: ClassVendorSpec,
0x01: { Endpoints: map[EndpointAddress]EndpointDesc{
Address: 0x01, 0x01: {
Number: 1, Address: 0x01,
Direction: EndpointDirectionOut, Number: 1,
MaxPacketSize: 512, Direction: EndpointDirectionOut,
TransferType: TransferTypeBulk, MaxPacketSize: 512,
TransferType: TransferTypeBulk,
},
0x81: {
Address: 0x81,
Number: 1,
Direction: EndpointDirectionIn,
MaxPacketSize: 512,
TransferType: TransferTypeBulk,
},
}, },
0x81: { }},
Address: 0x81,
Number: 1,
Direction: EndpointDirectionIn,
MaxPacketSize: 512,
TransferType: TransferTypeBulk,
},
},
}}, }},
}}, }},
}}, },
}, },
} }

View File

@@ -21,11 +21,6 @@ import (
"time" "time"
) )
type fakeDevice struct {
desc *DeviceDesc
alt uint8
}
type fakeTransfer struct { type fakeTransfer struct {
// done is the channel that needs to be closed when the transfer has finished. // done is the channel that needs to be closed when the transfer has finished.
done chan struct{} done chan struct{}
@@ -75,7 +70,7 @@ func (f *fakeLibusb) setDebug(*libusbContext, int) {}
func (f *fakeLibusb) dereference(d *libusbDevice) {} func (f *fakeLibusb) dereference(d *libusbDevice) {}
func (f *fakeLibusb) getDeviceDesc(d *libusbDevice) (*DeviceDesc, error) { func (f *fakeLibusb) getDeviceDesc(d *libusbDevice) (*DeviceDesc, error) {
if dev, ok := f.fakeDevices[d]; ok { if dev, ok := f.fakeDevices[d]; ok {
return dev.desc, nil return dev.devDesc, nil
} }
return nil, fmt.Errorf("invalid USB device %p", d) return nil, fmt.Errorf("invalid USB device %p", d)
} }
@@ -109,8 +104,16 @@ func (f *fakeLibusb) setConfig(d *libusbDevHandle, cfg uint8) error {
} }
return nil return nil
} }
func (f *fakeLibusb) getStringDesc(*libusbDevHandle, int) (string, error) { func (f *fakeLibusb) getStringDesc(d *libusbDevHandle, index int) (string, error) {
return "", errors.New("not implemented") dev, ok := f.fakeDevices[f.handles[d]]
if !ok {
return "", fmt.Errorf("invalid USB device %p", d)
}
str, ok := dev.strDesc[index]
if !ok {
return "", fmt.Errorf("invalid string descriptor index %d", index)
}
return str, nil
} }
func (f *fakeLibusb) setAutoDetach(*libusbDevHandle, int) error { return nil } func (f *fakeLibusb) setAutoDetach(*libusbDevHandle, int) error { return nil }
@@ -202,10 +205,9 @@ func newFakeLibusb() (*fakeLibusb, func() error) {
// without using the full USB stack. Since the fake library uses the // without using the full USB stack. Since the fake library uses the
// libusbDevice only as an identifier, use an arbitrary unique pointer. // libusbDevice only as an identifier, use an arbitrary unique pointer.
// The contents of these pointers is never accessed. // The contents of these pointers is never accessed.
fl.fakeDevices[newDevicePointer()] = &fakeDevice{ fd := new(fakeDevice)
desc: d, *fd = d
alt: 0, fl.fakeDevices[newDevicePointer()] = fd
}
} }
libusb = fl libusb = fl
return fl, func() error { return fl, func() error {

View File

@@ -238,6 +238,9 @@ func (libusbImpl) getDeviceDesc(d *libusbDevice) (*DeviceDesc, error) {
SubClass: Class(desc.bDeviceSubClass), SubClass: Class(desc.bDeviceSubClass),
Protocol: Protocol(desc.bDeviceProtocol), Protocol: Protocol(desc.bDeviceProtocol),
MaxControlPacketSize: int(desc.bMaxPacketSize0), MaxControlPacketSize: int(desc.bMaxPacketSize0),
iManufacturer: int(desc.iManufacturer),
iProduct: int(desc.iProduct),
iSerialNumber: int(desc.iSerialNumber),
} }
// Enumerate configurations // Enumerate configurations
cfgs := make(map[int]ConfigDesc) cfgs := make(map[int]ConfigDesc)