From ba2dd5404fcf086fa24205829ef4d922e9260fcd Mon Sep 17 00:00:00 2001 From: Kyle Lemons Date: Mon, 26 Mar 2012 00:06:53 -0700 Subject: [PATCH] Add config, interfaces, endpoints, etc --- usb/config.go | 114 ++++++++++++++++++++++++++++++++++++++++++++++ usb/descriptor.go | 47 ++++++++++++++----- usb/device.go | 26 +++++++++++ usb/misc.go | 4 +- usb/misc_test.go | 2 +- usb/usb_test.go | 63 ++++++++++++++++++++----- usbid/load.go | 2 +- usbid/parse.go | 5 +- 8 files changed, 235 insertions(+), 28 deletions(-) create mode 100644 usb/config.go diff --git a/usb/config.go b/usb/config.go new file mode 100644 index 0000000..de3e12b --- /dev/null +++ b/usb/config.go @@ -0,0 +1,114 @@ +package usb + +// #cgo LDFLAGS: -lusb-1.0 +// #include +import "C" + +import ( + "log" + "reflect" + "runtime" + "unsafe" +) + +type Endpoint struct { + Type DescriptorType + Address ID + MaxPacketSize uint16 + PollInterval uint8 + RefreshRate uint8 + SynchAddress ID +} + +type Interface struct { + Type DescriptorType + Number ID + Alternate ID + IfClass Class + IfSubClass uint8 + IfProtocol uint8 + Endpoints []*Endpoint +} + +type Config struct { + cfg *C.struct_libusb_config_descriptor + + Type DescriptorType + Config ID + Attributes uint8 + MaxPower uint8 + Interfaces [][]*Interface +} + +func newConfig(cfg *C.struct_libusb_config_descriptor) *Config { + c := &Config{ + cfg: cfg, + Type: DescriptorType(cfg.bDescriptorType), + Config: ID(cfg.bConfigurationValue), + Attributes: uint8(cfg.bmAttributes), + MaxPower: uint8(cfg.MaxPower), + } + + var ifaces []C.struct_libusb_interface + *(*reflect.SliceHeader)(unsafe.Pointer(&ifaces)) = reflect.SliceHeader{ + Data: uintptr(unsafe.Pointer(cfg._interface)), + Len: int(cfg.bNumInterfaces), + Cap: int(cfg.bNumInterfaces), + } + c.Interfaces = make([][]*Interface, 0, len(ifaces)) + for _, iface := range ifaces { + var alts []C.struct_libusb_interface_descriptor + *(*reflect.SliceHeader)(unsafe.Pointer(&alts)) = reflect.SliceHeader{ + Data: uintptr(unsafe.Pointer(iface.altsetting)), + Len: int(iface.num_altsetting), + Cap: int(iface.num_altsetting), + } + descs := make([]*Interface, 0, len(alts)) + for _, alt := range alts { + i := &Interface{ + Type: DescriptorType(alt.bDescriptorType), + Number: ID(alt.bInterfaceNumber), + Alternate: ID(alt.bAlternateSetting), + IfClass: Class(alt.bInterfaceClass), + IfSubClass: uint8(alt.bInterfaceSubClass), + IfProtocol: uint8(alt.bInterfaceProtocol), + } + var ends []C.struct_libusb_endpoint_descriptor + *(*reflect.SliceHeader)(unsafe.Pointer(&ends)) = reflect.SliceHeader{ + Data: uintptr(unsafe.Pointer(alt.endpoint)), + Len: int(alt.bNumEndpoints), + Cap: int(alt.bNumEndpoints), + } + i.Endpoints = make([]*Endpoint, 0, len(ends)) + for _, end := range ends { + i.Endpoints = append(i.Endpoints, &Endpoint{ + Type: DescriptorType(end.bDescriptorType), + Address: ID(end.bEndpointAddress), + MaxPacketSize: uint16(end.wMaxPacketSize), + PollInterval: uint8(end.bInterval), + RefreshRate: uint8(end.bRefresh), + SynchAddress: ID(end.bSynchAddress), + }) + } + descs = append(descs, i) + } + c.Interfaces = append(c.Interfaces, descs) + } + + // *sigh* + runtime.SetFinalizer(c, (*Config).Close) + + log.Printf("config %p initialized", c.cfg) + return c +} + +// Close decrements the reference count for the device in the libusb driver +// code. It should be called exactly once! +func (c *Config) Close() error { + if c.cfg != nil { + log.Printf("config %p closed", c.cfg) + C.libusb_free_config_descriptor(c.cfg) + } + c.cfg = nil + return nil +} diff --git a/usb/descriptor.go b/usb/descriptor.go index aadef59..d5abf02 100644 --- a/usb/descriptor.go +++ b/usb/descriptor.go @@ -5,17 +5,16 @@ package usb import "C" type Descriptor struct { - desc *C.struct_libusb_device_descriptor + desc *C.struct_libusb_device_descriptor - Type DescriptorType // The type of this descriptor - Spec BCD // USB Specification Release Number - Class Class // The class of this device - SubClass uint8 // The sub-class (within the class) of this device - Protocol uint8 // The protocol (within the sub-class) of this device - Vendor ID // The 8-bit Vendor identifer - Product ID // The 8-bit Product identifier - Device BCD // The device version - Configs int // Number of configurations + Type DescriptorType // The type of this descriptor + Spec BCD // USB Specification Release Number + Class Class // The class of this device + SubClass uint8 // The sub-class (within the class) of this device + Protocol uint8 // The protocol (within the sub-class) of this device + Vendor ID // The 8-bit Vendor identifer + Product ID // The 8-bit Product identifier + Device BCD // The device version } func newDescriptor(dev *C.libusb_device) (*Descriptor, error) { @@ -33,10 +32,36 @@ func newDescriptor(dev *C.libusb_device) (*Descriptor, error) { Vendor: ID(desc.idVendor), Product: ID(desc.idProduct), Device: BCD(desc.bcdDevice), - Configs: int(desc.bNumConfigurations), }, nil } +// Configurations returns a list of configurations configured on this +// device. Each config must be Closed. +func (d *DeviceInfo) Configurations() (_c []*Config, _e error) { + var desc C.struct_libusb_device_descriptor + if errno := C.libusb_get_device_descriptor(d.dev, &desc); errno < 0 { + return nil, usbError(errno) + } + + var cfgs []*Config + defer func() { + if _e != nil { + for _, c := range cfgs { + c.Close() + } + } + }() + + for i := 0; i < int(desc.bNumConfigurations); i++ { + var cfg *C.struct_libusb_config_descriptor + if errno := C.libusb_get_config_descriptor(d.dev, C.uint8_t(i), &cfg); errno < 0 { + return nil, usbError(errno) + } + cfgs = append(cfgs, newConfig(cfg)) + } + return cfgs, nil +} + /* func (d Descriptor) str(idx int) (string, error) { str := [64]byte{} diff --git a/usb/device.go b/usb/device.go index cb8bf02..5d87a5c 100644 --- a/usb/device.go +++ b/usb/device.go @@ -6,7 +6,9 @@ import "C" import ( "log" + "reflect" "runtime" + "unsafe" ) type DeviceInfo struct { @@ -80,6 +82,30 @@ func newDevice(handle *C.libusb_device_handle) *Device { return d } +func (d *Device) Reset() error { + if errno := C.libusb_reset_device(d.handle); errno != 0 { + return usbError(errno) + } + return nil +} + +func (d *Device) Control(rType, request uint8, val, idx uint16, data []byte) (int, error) { + dataSlice := (*reflect.SliceHeader)(unsafe.Pointer(&data)) + n := C.libusb_control_transfer( + d.handle, + C.uint8_t(rType), + C.uint8_t(request), + C.uint16_t(val), + C.uint16_t(idx), + (*C.uchar)(unsafe.Pointer(dataSlice.Data)), + C.uint16_t(len(data)), + 0) + if n < 0 { + return int(n), usbError(n) + } + return int(n), nil +} + func (d *Device) Close() error { if d.handle != nil { log.Printf("device %p closed", d.handle) diff --git a/usb/misc.go b/usb/misc.go index 9d84e5d..adee55e 100644 --- a/usb/misc.go +++ b/usb/misc.go @@ -15,7 +15,7 @@ const ( func (d BCD) Int() (i int) { ten := 1 for o := uint(0); o < 4; o++ { - n := ((0xF << (o*4)) & d) >> (o*4) + n := ((0xF << (o * 4)) & d) >> (o * 4) i += int(n) * ten ten *= 10 } @@ -23,7 +23,7 @@ func (d BCD) Int() (i int) { } func (d BCD) String() string { - return fmt.Sprintf("%02x.%02x", int(d >> 8), int(d & 0xFF)) + return fmt.Sprintf("%02x.%02x", int(d>>8), int(d&0xFF)) } type ID uint16 diff --git a/usb/misc_test.go b/usb/misc_test.go index 50fe3a1..461993a 100644 --- a/usb/misc_test.go +++ b/usb/misc_test.go @@ -5,7 +5,7 @@ import ( ) func TestBCD(t *testing.T) { - tests := []struct{ + tests := []struct { BCD Int int Str string diff --git a/usb/usb_test.go b/usb/usb_test.go index 7e1b182..10c8fd0 100644 --- a/usb/usb_test.go +++ b/usb/usb_test.go @@ -10,24 +10,18 @@ import ( func TestNoop(t *testing.T) { c := NewContext() defer c.Close() - c.Debug(3) + c.Debug(0) } func TestEnum(t *testing.T) { c := NewContext() defer c.Close() - c.Debug(3) + c.Debug(0) + cnt := 0 devs, err := c.ListDevices(func(bus, addr int, desc *Descriptor) bool { - t.Logf("%03d:%03d %+v", bus, addr, desc) - if v, ok := usbid.Vendors[desc.Vendor]; ok { - if p, ok := v.Devices[desc.Product]; ok { - t.Logf(" - %s (%s) %s (%s)", v, desc.Vendor, p, desc.Product) - } else { - t.Logf(" - %s (%s) Unknown", v, desc.Vendor) - } - } - return false + cnt++ + return true }) defer func() { for _, d := range devs { @@ -37,4 +31,51 @@ func TestEnum(t *testing.T) { if err != nil { t.Fatalf("list: %s", err) } + + if got, want := len(devs), cnt; got != want { + t.Errorf("len(devs) = %d, want %d", got, want) + } + + for _, dev := range devs { + desc, err := dev.Descriptor() + if err != nil { + t.Errorf("desc: %s", err) + continue + } + bus := dev.BusNumber() + addr := dev.Address() + + t.Logf("%03d:%03d %+v", bus, addr, desc) + if v, ok := usbid.Vendors[desc.Vendor]; ok { + if p, ok := v.Devices[desc.Product]; ok { + t.Logf(" - %s (%s) %s (%s)", v, desc.Vendor, p, desc.Product) + } else { + t.Logf(" - %s (%s) Unknown", v, desc.Vendor) + } + } + + cfgs, err := dev.Configurations() + defer func() { + for _, cfg := range cfgs { + cfg.Close() + } + }() + if err != nil { + t.Errorf(" - configs: %s", err) + continue + } + + for _, cfg := range cfgs { + t.Logf(" - %#v", cfg) + for _, alt := range cfg.Interfaces { + for _, iface := range alt { + t.Logf(" - %#v", iface) + for _, end := range iface.Endpoints { + t.Logf(" - %#v", end) + } + } + t.Logf(" -----") + } + } + } } diff --git a/usbid/load.go b/usbid/load.go index 264fd9e..719462a 100644 --- a/usbid/load.go +++ b/usbid/load.go @@ -1,9 +1,9 @@ package usbid import ( + "log" "net/http" "strings" - "log" "github.com/kylelemons/gousb/usb" ) diff --git a/usbid/parse.go b/usbid/parse.go index 862699b..a8c1ab2 100644 --- a/usbid/parse.go +++ b/usbid/parse.go @@ -72,7 +72,7 @@ func ParseIDs(r io.Reader) (map[usb.ID]*Vendor, error) { return } id = usb.ID(i) - + return } @@ -151,7 +151,8 @@ parseLines: } switch kind { - case "": err = parseVendor(level, id, name) + case "": + err = parseVendor(level, id, name) } if err != nil { return nil, fmt.Errorf("line %d: %s", lineno, err)