Add config, interfaces, endpoints, etc

This commit is contained in:
Kyle Lemons
2012-03-26 00:06:53 -07:00
parent 804a3c6ab8
commit ba2dd5404f
8 changed files with 235 additions and 28 deletions

114
usb/config.go Normal file
View File

@@ -0,0 +1,114 @@
package usb
// #cgo LDFLAGS: -lusb-1.0
// #include <libusb-1.0/libusb.h>
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
}

View File

@@ -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{}

View File

@@ -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)

View File

@@ -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

View File

@@ -5,7 +5,7 @@ import (
)
func TestBCD(t *testing.T) {
tests := []struct{
tests := []struct {
BCD
Int int
Str string

View File

@@ -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(" -----")
}
}
}
}

View File

@@ -1,9 +1,9 @@
package usbid
import (
"log"
"net/http"
"strings"
"log"
"github.com/kylelemons/gousb/usb"
)

View File

@@ -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)