From 757722bf8ee7481874da28bb990dbd1f253eed18 Mon Sep 17 00:00:00 2001 From: Deomid Ryabkov Date: Mon, 4 Sep 2017 01:42:55 +0300 Subject: [PATCH] Add Device.Manufacturer(), .Product() and .SerialNumber() (#14) --- AUTHORS | 1 + device.go | 22 +++ device_test.go | 28 +++- fakelibusb_devices.go | 312 ++++++++++++++++++++++-------------------- fakelibusb_test.go | 26 ++-- libusb.go | 3 + 6 files changed, 233 insertions(+), 159 deletions(-) diff --git a/AUTHORS b/AUTHORS index 9bcf017..ac153c9 100644 --- a/AUTHORS +++ b/AUTHORS @@ -13,3 +13,4 @@ Thordur Bjornsson Vincent Serpoul Josef Filzmaier Nico MT +Deomid "rojer" Ryabkov diff --git a/device.go b/device.go index 3be599a..2cb3fce 100644 --- a/device.go +++ b/device.go @@ -45,6 +45,10 @@ type DeviceDesc struct { // Configuration information 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. @@ -211,6 +215,24 @@ func (d *Device) GetStringDescriptor(descIndex int) (string, error) { 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. // When autodetach is enabled gousb will automatically detach the kernel driver // on the interface and reattach it when releasing the interface. diff --git a/device_test.go b/device_test.go index 76c5aa4..7e1d856 100644 --- a/device_test.go +++ b/device_test.go @@ -44,6 +44,28 @@ func TestClaimAndRelease(t *testing.T) { 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 { t.Fatalf("%s.SetAutoDetach(true): %v", dev, err) } @@ -67,7 +89,7 @@ func TestClaimAndRelease(t *testing.T) { if err != nil { 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) } @@ -117,6 +139,10 @@ func TestClaimAndRelease(t *testing.T) { 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 { t.Fatalf("%s.Config(1): got error nil, want no nil because it is closed", dev) } diff --git a/fakelibusb_devices.go b/fakelibusb_devices.go index 8469237..a99ebe0 100644 --- a/fakelibusb_devices.go +++ b/fakelibusb_devices.go @@ -15,175 +15,195 @@ package gousb // 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 // One config, one interface, one setup, // two endpoints: 0x01 OUT, 0x82 IN. - &DeviceDesc{ - Bus: 1, - Address: 1, - Spec: Version(2, 0), - Device: Version(1, 0), - Vendor: ID(0x9999), - Product: ID(0x0001), - Protocol: 255, - Configs: map[int]ConfigDesc{1: { - Number: 1, - MaxPower: Milliamperes(100), - Interfaces: []InterfaceDesc{{ - Number: 0, - AltSettings: []InterfaceSetting{{ - Number: 0, - Alternate: 0, - Class: ClassVendorSpec, - Endpoints: map[EndpointAddress]EndpointDesc{ - 0x01: { - Address: 0x01, - Number: 1, - Direction: EndpointDirectionOut, - MaxPacketSize: 512, - TransferType: TransferTypeBulk, + { + devDesc: &DeviceDesc{ + Bus: 1, + Address: 1, + Spec: Version(2, 0), + Device: Version(1, 0), + Vendor: ID(0x9999), + Product: ID(0x0001), + Protocol: 255, + Configs: map[int]ConfigDesc{1: { + Number: 1, + MaxPower: Milliamperes(100), + Interfaces: []InterfaceDesc{{ + Number: 0, + AltSettings: []InterfaceSetting{{ + Number: 0, + Alternate: 0, + Class: ClassVendorSpec, + Endpoints: map[EndpointAddress]EndpointDesc{ + 0x01: { + Address: 0x01, + Number: 1, + Direction: EndpointDirectionOut, + 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 // One config, two interfaces. interface #0 with no endpoints, // interface #1 with two alt setups with different packet sizes for // endpoints. Two isochronous endpoints, 0x05 OUT and 0x86 OUT. - &DeviceDesc{ - Bus: 1, - Address: 2, - Spec: Version(2, 0), - Device: Version(1, 3), - Vendor: ID(0x8888), - Product: ID(0x0002), - Protocol: 255, - Configs: map[int]ConfigDesc{1: { - Number: 1, - MaxPower: Milliamperes(100), - Interfaces: []InterfaceDesc{{ - Number: 0, - AltSettings: []InterfaceSetting{{ - Number: 0, - 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, - }, - }, + { + devDesc: &DeviceDesc{ + Bus: 1, + Address: 2, + Spec: Version(2, 0), + Device: Version(1, 3), + Vendor: ID(0x8888), + Product: ID(0x0002), + Protocol: 255, + Configs: map[int]ConfigDesc{1: { + Number: 1, + MaxPower: Milliamperes(100), + Interfaces: []InterfaceDesc{{ + Number: 0, + AltSettings: []InterfaceSetting{{ + Number: 0, + Alternate: 0, + Class: ClassVendorSpec, + }}, }, { - Number: 1, - Alternate: 1, - Class: ClassVendorSpec, - Endpoints: map[EndpointAddress]EndpointDesc{ - 0x05: { - Address: 0x05, - Number: 5, - Direction: EndpointDirectionOut, - MaxPacketSize: 2 * 1024, - TransferType: TransferTypeIsochronous, + 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, + }, }, - 0x86: { - Address: 0x86, - Number: 6, - Direction: EndpointDirectionIn, - MaxPacketSize: 2 * 1024, - TransferType: TransferTypeIsochronous, + }, { + Number: 1, + Alternate: 1, + Class: ClassVendorSpec, + Endpoints: map[EndpointAddress]EndpointDesc{ + 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, - Alternate: 2, - Class: ClassVendorSpec, - Endpoints: map[EndpointAddress]EndpointDesc{ - 0x05: { - Address: 0x05, - Number: 5, - Direction: EndpointDirectionIn, - MaxPacketSize: 1024, - TransferType: TransferTypeIsochronous, + }, { + Number: 1, + Alternate: 2, + Class: ClassVendorSpec, + Endpoints: map[EndpointAddress]EndpointDesc{ + 0x05: { + Address: 0x05, + Number: 5, + Direction: EndpointDirectionIn, + MaxPacketSize: 1024, + 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 // One config, one interface, one setup, // two endpoints: 0x01 OUT, 0x81 IN. - &DeviceDesc{ - Bus: 1, - Address: 3, - Spec: Version(2, 0), - Device: Version(1, 0), - Vendor: ID(0x1111), - Product: ID(0x1111), - Protocol: 255, - Configs: map[int]ConfigDesc{1: { - Number: 1, - MaxPower: Milliamperes(100), - Interfaces: []InterfaceDesc{{ - Number: 0, - AltSettings: []InterfaceSetting{{ - Number: 0, - Alternate: 0, - Class: ClassVendorSpec, - Endpoints: map[EndpointAddress]EndpointDesc{ - 0x01: { - Address: 0x01, - Number: 1, - Direction: EndpointDirectionOut, - MaxPacketSize: 512, - TransferType: TransferTypeBulk, + { + devDesc: &DeviceDesc{ + Bus: 1, + Address: 3, + Spec: Version(2, 0), + Device: Version(1, 0), + Vendor: ID(0x1111), + Product: ID(0x1111), + Protocol: 255, + Configs: map[int]ConfigDesc{1: { + Number: 1, + MaxPower: Milliamperes(100), + Interfaces: []InterfaceDesc{{ + Number: 0, + AltSettings: []InterfaceSetting{{ + Number: 0, + Alternate: 0, + Class: ClassVendorSpec, + Endpoints: map[EndpointAddress]EndpointDesc{ + 0x01: { + Address: 0x01, + Number: 1, + Direction: EndpointDirectionOut, + 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, - }, - }, + }}, }}, }}, - }}, + }, }, } diff --git a/fakelibusb_test.go b/fakelibusb_test.go index 3dd1fb4..9b50e86 100644 --- a/fakelibusb_test.go +++ b/fakelibusb_test.go @@ -21,11 +21,6 @@ import ( "time" ) -type fakeDevice struct { - desc *DeviceDesc - alt uint8 -} - type fakeTransfer struct { // done is the channel that needs to be closed when the transfer has finished. done chan struct{} @@ -75,7 +70,7 @@ func (f *fakeLibusb) setDebug(*libusbContext, int) {} func (f *fakeLibusb) dereference(d *libusbDevice) {} func (f *fakeLibusb) getDeviceDesc(d *libusbDevice) (*DeviceDesc, error) { if dev, ok := f.fakeDevices[d]; ok { - return dev.desc, nil + return dev.devDesc, nil } return nil, fmt.Errorf("invalid USB device %p", d) } @@ -109,8 +104,16 @@ func (f *fakeLibusb) setConfig(d *libusbDevHandle, cfg uint8) error { } return nil } -func (f *fakeLibusb) getStringDesc(*libusbDevHandle, int) (string, error) { - return "", errors.New("not implemented") +func (f *fakeLibusb) getStringDesc(d *libusbDevHandle, index int) (string, error) { + 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 } @@ -202,10 +205,9 @@ func newFakeLibusb() (*fakeLibusb, func() error) { // without using the full USB stack. Since the fake library uses the // libusbDevice only as an identifier, use an arbitrary unique pointer. // The contents of these pointers is never accessed. - fl.fakeDevices[newDevicePointer()] = &fakeDevice{ - desc: d, - alt: 0, - } + fd := new(fakeDevice) + *fd = d + fl.fakeDevices[newDevicePointer()] = fd } libusb = fl return fl, func() error { diff --git a/libusb.go b/libusb.go index 016b7db..c5218b9 100644 --- a/libusb.go +++ b/libusb.go @@ -238,6 +238,9 @@ func (libusbImpl) getDeviceDesc(d *libusbDevice) (*DeviceDesc, error) { SubClass: Class(desc.bDeviceSubClass), Protocol: Protocol(desc.bDeviceProtocol), MaxControlPacketSize: int(desc.bMaxPacketSize0), + iManufacturer: int(desc.iManufacturer), + iProduct: int(desc.iProduct), + iSerialNumber: int(desc.iSerialNumber), } // Enumerate configurations cfgs := make(map[int]ConfigDesc)