diff --git a/lsusb/main.go b/lsusb/main.go index e4ca387..c7c3b28 100644 --- a/lsusb/main.go +++ b/lsusb/main.go @@ -24,54 +24,17 @@ func main() { ctx.Debug(*debug) // ListDevices is used to find the devices to open. - devs, err := ctx.ListDevices(func(bus, addr int, desc *usb.Descriptor) bool { - // After inspecting the descriptor, return true or false depending on whether - // the device is "interesting" or not. Any descriptor for which true is returned - // generates a DeviceInfo which is retuned in a slice. - return true - }) - - // All DeviceInfo returned from ListDevices must be closed. - defer func() { - for _, d := range devs { - d.Close() - } - }() - - // ListDevices can occaionally fail, so be sure to check its return value. - if err != nil { - log.Fatalf("list: %s", err) - } - - for _, dev := range devs { - // The descriptor (which contains useful information) can always be obtained again - // from a DeviceInfo. - desc, err := dev.Descriptor() - if err != nil { - log.Printf("desc: %s", err) - continue - } - - // The DeviceInfo contains the bus number and bus address of the device. - bus := dev.BusNumber() - addr := dev.Address() - + devs, err := ctx.ListDevices(func(desc *usb.Descriptor) bool { // The usbid package can be used to print out human readable information. - fmt.Printf("%03d:%03d %s\n", bus, addr, usbid.Describe(desc)) + fmt.Printf("%03d.%03d %s\n", desc.Bus, desc.Address, usbid.Describe(desc)) fmt.Printf(" Protocol: %s\n", usbid.Classify(desc)) - // The configurations can be examined from the DeviceInfo, though they can only + // The configurations can be examined from the Descriptor, though they can only // be set once the device is opened. All configuration references must be closed, // to free up the memory in libusb. - cfgs, err := dev.Configurations() - if err != nil { - log.Printf(" - configs: %s", err) - continue - } - - // This loop just uses more of the built-in and usbid pretty printing to list - // the USB devices. - for _, cfg := range cfgs { + for _, cfg := range desc.Configs { + // This loop just uses more of the built-in and usbid pretty printing to list + // the USB devices. fmt.Printf(" %s:\n", cfg) for _, alt := range cfg.Interfaces { fmt.Printf(" --------------\n") @@ -86,7 +49,27 @@ func main() { fmt.Printf(" --------------\n") } - // To actually interact with a device, DevInfo.Open() returns a device handle - // which can be used to do more I/O with the device and its endpoints. + // After inspecting the descriptor, return true or false depending on whether + // the device is "interesting" or not. Any descriptor for which true is returned + // opens a Device which is retuned in a slice (and must be subsequently closed). + return false + }) + + // All Devices returned from ListDevices must be closed. + defer func() { + for _, d := range devs { + d.Close() + } + }() + + // ListDevices can occaionally fail, so be sure to check its return value. + if err != nil { + log.Fatalf("list: %s", err) + } + + for _, dev := range devs { + // Once the device has been selected from ListDevices, it is opened + // and can be interacted with. + _ = dev } } diff --git a/usb/config.go b/usb/config.go index 394df0a..80d5eb3 100644 --- a/usb/config.go +++ b/usb/config.go @@ -44,27 +44,27 @@ type InterfaceInfo struct { IfClass uint8 IfSubClass uint8 IfProtocol uint8 - Endpoints []*EndpointInfo + Endpoints []EndpointInfo } func (i InterfaceInfo) String() string { return fmt.Sprintf("Interface %02x (config %02x)", i.Number, i.Alternate) } -type Config struct { +type ConfigInfo struct { Type DescriptorType Config uint8 Attributes uint8 MaxPower uint8 - Interfaces [][]*InterfaceInfo + Interfaces [][]InterfaceInfo } -func (c Config) String() string { +func (c ConfigInfo) String() string { return fmt.Sprintf("Config %02x", c.Config) } -func newConfig(cfg *C.struct_libusb_config_descriptor) *Config { - c := &Config{ +func newConfig(cfg *C.struct_libusb_config_descriptor) ConfigInfo { + c := ConfigInfo{ Type: DescriptorType(cfg.bDescriptorType), Config: uint8(cfg.bConfigurationValue), Attributes: uint8(cfg.bmAttributes), @@ -77,7 +77,7 @@ func newConfig(cfg *C.struct_libusb_config_descriptor) *Config { Len: int(cfg.bNumInterfaces), Cap: int(cfg.bNumInterfaces), } - c.Interfaces = make([][]*InterfaceInfo, 0, len(ifaces)) + c.Interfaces = make([][]InterfaceInfo, 0, len(ifaces)) for _, iface := range ifaces { var alts []C.struct_libusb_interface_descriptor *(*reflect.SliceHeader)(unsafe.Pointer(&alts)) = reflect.SliceHeader{ @@ -85,9 +85,9 @@ func newConfig(cfg *C.struct_libusb_config_descriptor) *Config { Len: int(iface.num_altsetting), Cap: int(iface.num_altsetting), } - descs := make([]*InterfaceInfo, 0, len(alts)) + descs := make([]InterfaceInfo, 0, len(alts)) for _, alt := range alts { - i := &InterfaceInfo{ + i := InterfaceInfo{ Type: DescriptorType(alt.bDescriptorType), Number: uint8(alt.bInterfaceNumber), Alternate: uint8(alt.bAlternateSetting), @@ -101,9 +101,9 @@ func newConfig(cfg *C.struct_libusb_config_descriptor) *Config { Len: int(alt.bNumEndpoints), Cap: int(alt.bNumEndpoints), } - i.Endpoints = make([]*EndpointInfo, 0, len(ends)) + i.Endpoints = make([]EndpointInfo, 0, len(ends)) for _, end := range ends { - i.Endpoints = append(i.Endpoints, &EndpointInfo{ + i.Endpoints = append(i.Endpoints, EndpointInfo{ Type: DescriptorType(end.bDescriptorType), Address: uint8(end.bEndpointAddress), Attributes: uint8(end.bmAttributes), diff --git a/usb/constants.go b/usb/constants.go new file mode 100644 index 0000000..e64e00c --- /dev/null +++ b/usb/constants.go @@ -0,0 +1,150 @@ +package usb + +// #cgo LDFLAGS: -lusb-1.0 +// #include +import "C" + +type Class uint8 + +const ( + CLASS_PER_INTERFACE Class = C.LIBUSB_CLASS_PER_INTERFACE + CLASS_AUDIO Class = C.LIBUSB_CLASS_AUDIO + CLASS_COMM Class = C.LIBUSB_CLASS_COMM + CLASS_HID Class = C.LIBUSB_CLASS_HID + CLASS_PRINTER Class = C.LIBUSB_CLASS_PRINTER + CLASS_PTP Class = C.LIBUSB_CLASS_PTP + CLASS_MASS_STORAGE Class = C.LIBUSB_CLASS_MASS_STORAGE + CLASS_HUB Class = C.LIBUSB_CLASS_HUB + CLASS_DATA Class = C.LIBUSB_CLASS_DATA + CLASS_WIRELESS Class = C.LIBUSB_CLASS_WIRELESS + CLASS_APPLICATION Class = C.LIBUSB_CLASS_APPLICATION + CLASS_VENDOR_SPEC Class = C.LIBUSB_CLASS_VENDOR_SPEC +) + +var classDescription = map[Class]string{ + CLASS_PER_INTERFACE: "per-interface", + CLASS_AUDIO: "audio", + CLASS_COMM: "communications", + CLASS_HID: "human interface device", + CLASS_PRINTER: "printer dclass", + CLASS_PTP: "picture transfer protocol", + CLASS_MASS_STORAGE: "mass storage", + CLASS_HUB: "hub", + CLASS_DATA: "data", + CLASS_WIRELESS: "wireless", + CLASS_APPLICATION: "application", + CLASS_VENDOR_SPEC: "vendor-specific", +} + +func (c Class) String() string { + return classDescription[c] +} + +type DescriptorType uint8 + +const ( + DT_DEVICE DescriptorType = C.LIBUSB_DT_DEVICE + DT_CONFIG DescriptorType = C.LIBUSB_DT_CONFIG + DT_STRING DescriptorType = C.LIBUSB_DT_STRING + DT_INTERFACE DescriptorType = C.LIBUSB_DT_INTERFACE + DT_ENDPOINT DescriptorType = C.LIBUSB_DT_ENDPOINT + DT_HID DescriptorType = C.LIBUSB_DT_HID + DT_REPORT DescriptorType = C.LIBUSB_DT_REPORT + DT_PHYSICAL DescriptorType = C.LIBUSB_DT_PHYSICAL + DT_HUB DescriptorType = C.LIBUSB_DT_HUB +) + +var descriptorTypeDescription = map[DescriptorType]string{ + DT_DEVICE: "device", + DT_CONFIG: "configuration", + DT_STRING: "string", + DT_INTERFACE: "interface", + DT_ENDPOINT: "endpoint", + DT_HID: "HID", + DT_REPORT: "HID report", + DT_PHYSICAL: "physical", + DT_HUB: "hub", +} + +func (dt DescriptorType) String() string { + return descriptorTypeDescription[dt] +} + +type EndpointDirection uint8 + +const ( + ENDPOINT_NUM_MASK = 0x03 + ENDPOINT_DIR_IN EndpointDirection = C.LIBUSB_ENDPOINT_IN + ENDPOINT_DIR_OUT EndpointDirection = C.LIBUSB_ENDPOINT_OUT + ENDPOINT_DIR_MASK EndpointDirection = 0x80 +) + +var endpointDirectionDescription = map[EndpointDirection]string{ + ENDPOINT_DIR_IN: "IN", + ENDPOINT_DIR_OUT: "OUT", +} + +func (ed EndpointDirection) String() string { + return endpointDirectionDescription[ed] +} + +type TransferType uint8 + +const ( + TRANSFER_TYPE_CONTROL TransferType = C.LIBUSB_TRANSFER_TYPE_CONTROL + TRANSFER_TYPE_ISOCHRONOUS TransferType = C.LIBUSB_TRANSFER_TYPE_ISOCHRONOUS + TRANSFER_TYPE_BULK TransferType = C.LIBUSB_TRANSFER_TYPE_BULK + TRANSFER_TYPE_INTERRUPT TransferType = C.LIBUSB_TRANSFER_TYPE_INTERRUPT + TRANSFER_TYPE_MASK TransferType = 0x03 +) + +var transferTypeDescription = map[TransferType]string{ + TRANSFER_TYPE_CONTROL: "control", + TRANSFER_TYPE_ISOCHRONOUS: "isochronous", + TRANSFER_TYPE_BULK: "bulk", + TRANSFER_TYPE_INTERRUPT: "interrupt", +} + +func (tt TransferType) String() string { + return transferTypeDescription[tt] +} + +type IsoSyncType uint8 + +const ( + ISO_SYNC_TYPE_NONE IsoSyncType = C.LIBUSB_ISO_SYNC_TYPE_NONE << 2 + ISO_SYNC_TYPE_ASYNC IsoSyncType = C.LIBUSB_ISO_SYNC_TYPE_ASYNC << 2 + ISO_SYNC_TYPE_ADAPTIVE IsoSyncType = C.LIBUSB_ISO_SYNC_TYPE_ADAPTIVE << 2 + ISO_SYNC_TYPE_SYNC IsoSyncType = C.LIBUSB_ISO_SYNC_TYPE_SYNC << 2 + ISO_SYNC_TYPE_MASK IsoSyncType = 0x0C +) + +var isoSyncTypeDescription = map[IsoSyncType]string{ + ISO_SYNC_TYPE_NONE: "unsynchronized", + ISO_SYNC_TYPE_ASYNC: "asynchronous", + ISO_SYNC_TYPE_ADAPTIVE: "adaptive", + ISO_SYNC_TYPE_SYNC: "synchronous", +} + +func (ist IsoSyncType) String() string { + return isoSyncTypeDescription[ist] +} + +type IsoUsageType uint8 + +const ( + ISO_USAGE_TYPE_DATA IsoUsageType = C.LIBUSB_ISO_USAGE_TYPE_DATA << 4 + ISO_USAGE_TYPE_FEEDBACK IsoUsageType = C.LIBUSB_ISO_USAGE_TYPE_FEEDBACK << 4 + ISO_USAGE_TYPE_IMPLICIT IsoUsageType = C.LIBUSB_ISO_USAGE_TYPE_IMPLICIT << 4 + ISO_USAGE_TYPE_MASK IsoUsageType = 0x30 +) + +var isoUsageTypeDescription = map[IsoUsageType]string{ + ISO_USAGE_TYPE_DATA: "data", + ISO_USAGE_TYPE_FEEDBACK: "feedback", + ISO_USAGE_TYPE_IMPLICIT: "implicit data", +} + +func (iut IsoUsageType) String() string { + return isoUsageTypeDescription[iut] +} diff --git a/usb/descriptor.go b/usb/descriptor.go index a0954ce..1de4ed0 100644 --- a/usb/descriptor.go +++ b/usb/descriptor.go @@ -5,16 +5,25 @@ package usb import "C" type Descriptor struct { - desc *C.struct_libusb_device_descriptor + // Bus information + Bus uint8 // The bus on which the device was detected + Address uint8 // The address of the device on the bus - Type DescriptorType // The type of this descriptor - Spec BCD // USB Specification Release Number - Class uint8 // 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 + // Version information + Spec BCD // USB Specification Release Number + Device BCD // The device version + + // Product information + Vendor ID // The Vendor identifer + Product ID // The Product identifier + + // Protocol information + Class uint8 // 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 + + // Configuration information + Configs []ConfigInfo } func newDescriptor(dev *C.libusb_device) (*Descriptor, error) { @@ -22,196 +31,28 @@ func newDescriptor(dev *C.libusb_device) (*Descriptor, error) { if errno := C.libusb_get_device_descriptor(dev, &desc); errno < 0 { return nil, usbError(errno) } - return &Descriptor{ - desc: &desc, - Type: DescriptorType(desc.bDescriptorType), - Spec: BCD(desc.bcdUSB), - Class: uint8(desc.bDeviceClass), - SubClass: uint8(desc.bDeviceSubClass), - Protocol: uint8(desc.bDeviceProtocol), - Vendor: ID(desc.idVendor), - Product: ID(desc.idProduct), - Device: BCD(desc.bcdDevice), - }, nil -} -// Configurations returns a list of configurations configured on this -// device. Each config must be Closed. -func (d *DeviceInfo) Configurations() ([]*Config, 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 + // Enumerate configurations + var cfgs []ConfigInfo 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 { + if errno := C.libusb_get_config_descriptor(dev, C.uint8_t(i), &cfg); errno < 0 { return nil, usbError(errno) } cfgs = append(cfgs, newConfig(cfg)) C.libusb_free_config_descriptor(cfg) } - return cfgs, nil -} - -/* -func (d Descriptor) str(idx int) (string, error) { - str := [64]byte{} - n := C.libusb_get_string_descriptor_ascii( - d.dev.handle, - C.uint8_t(idx), - (*C.uchar)(unsafe.Pointer(&str)), - 64, - ) - if n < 0 { - return "", usbError(n) - } - return string(str[:n]), nil -} -*/ - -type Class int - -const ( - CLASS_PER_INTERFACE Class = C.LIBUSB_CLASS_PER_INTERFACE - CLASS_AUDIO Class = C.LIBUSB_CLASS_AUDIO - CLASS_COMM Class = C.LIBUSB_CLASS_COMM - CLASS_HID Class = C.LIBUSB_CLASS_HID - CLASS_PRINTER Class = C.LIBUSB_CLASS_PRINTER - CLASS_PTP Class = C.LIBUSB_CLASS_PTP - CLASS_MASS_STORAGE Class = C.LIBUSB_CLASS_MASS_STORAGE - CLASS_HUB Class = C.LIBUSB_CLASS_HUB - CLASS_DATA Class = C.LIBUSB_CLASS_DATA - CLASS_WIRELESS Class = C.LIBUSB_CLASS_WIRELESS - CLASS_APPLICATION Class = C.LIBUSB_CLASS_APPLICATION - CLASS_VENDOR_SPEC Class = C.LIBUSB_CLASS_VENDOR_SPEC -) - -var classDescription = map[Class]string{ - CLASS_PER_INTERFACE: "per-interface", - CLASS_AUDIO: "audio", - CLASS_COMM: "communications", - CLASS_HID: "human interface device", - CLASS_PRINTER: "printer dclass", - CLASS_PTP: "picture transfer protocol", - CLASS_MASS_STORAGE: "mass storage", - CLASS_HUB: "hub", - CLASS_DATA: "data", - CLASS_WIRELESS: "wireless", - CLASS_APPLICATION: "application", - CLASS_VENDOR_SPEC: "vendor-specific", -} - -func (c Class) String() string { - return classDescription[c] -} - -type DescriptorType int - -const ( - DT_DEVICE DescriptorType = C.LIBUSB_DT_DEVICE - DT_CONFIG DescriptorType = C.LIBUSB_DT_CONFIG - DT_STRING DescriptorType = C.LIBUSB_DT_STRING - DT_INTERFACE DescriptorType = C.LIBUSB_DT_INTERFACE - DT_ENDPOINT DescriptorType = C.LIBUSB_DT_ENDPOINT - DT_HID DescriptorType = C.LIBUSB_DT_HID - DT_REPORT DescriptorType = C.LIBUSB_DT_REPORT - DT_PHYSICAL DescriptorType = C.LIBUSB_DT_PHYSICAL - DT_HUB DescriptorType = C.LIBUSB_DT_HUB -) - -var descriptorTypeDescription = map[DescriptorType]string{ - DT_DEVICE: "device", - DT_CONFIG: "configuration", - DT_STRING: "string", - DT_INTERFACE: "interface", - DT_ENDPOINT: "endpoint", - DT_HID: "HID", - DT_REPORT: "HID report", - DT_PHYSICAL: "physical", - DT_HUB: "hub", -} - -func (dt DescriptorType) String() string { - return descriptorTypeDescription[dt] -} - -type EndpointDirection int - -const ( - ENDPOINT_NUM_MASK = 0x03 - ENDPOINT_DIR_IN EndpointDirection = C.LIBUSB_ENDPOINT_IN - ENDPOINT_DIR_OUT EndpointDirection = C.LIBUSB_ENDPOINT_OUT - ENDPOINT_DIR_MASK EndpointDirection = 0x80 -) - -var endpointDirectionDescription = map[EndpointDirection]string{ - ENDPOINT_DIR_IN: "IN", - ENDPOINT_DIR_OUT: "OUT", -} - -func (ed EndpointDirection) String() string { - return endpointDirectionDescription[ed] -} - -type TransferType int - -const ( - TRANSFER_TYPE_CONTROL TransferType = C.LIBUSB_TRANSFER_TYPE_CONTROL - TRANSFER_TYPE_ISOCHRONOUS TransferType = C.LIBUSB_TRANSFER_TYPE_ISOCHRONOUS - TRANSFER_TYPE_BULK TransferType = C.LIBUSB_TRANSFER_TYPE_BULK - TRANSFER_TYPE_INTERRUPT TransferType = C.LIBUSB_TRANSFER_TYPE_INTERRUPT - TRANSFER_TYPE_MASK TransferType = 0x03 -) - -var transferTypeDescription = map[TransferType]string{ - TRANSFER_TYPE_CONTROL: "control", - TRANSFER_TYPE_ISOCHRONOUS: "isochronous", - TRANSFER_TYPE_BULK: "bulk", - TRANSFER_TYPE_INTERRUPT: "interrupt", -} - -func (tt TransferType) String() string { - return transferTypeDescription[tt] -} - -type IsoSyncType int - -const ( - ISO_SYNC_TYPE_NONE IsoSyncType = C.LIBUSB_ISO_SYNC_TYPE_NONE << 2 - ISO_SYNC_TYPE_ASYNC IsoSyncType = C.LIBUSB_ISO_SYNC_TYPE_ASYNC << 2 - ISO_SYNC_TYPE_ADAPTIVE IsoSyncType = C.LIBUSB_ISO_SYNC_TYPE_ADAPTIVE << 2 - ISO_SYNC_TYPE_SYNC IsoSyncType = C.LIBUSB_ISO_SYNC_TYPE_SYNC << 2 - ISO_SYNC_TYPE_MASK IsoSyncType = 0x0C -) - -var isoSyncTypeDescription = map[IsoSyncType]string{ - ISO_SYNC_TYPE_NONE: "unsynchronized", - ISO_SYNC_TYPE_ASYNC: "asynchronous", - ISO_SYNC_TYPE_ADAPTIVE: "adaptive", - ISO_SYNC_TYPE_SYNC: "synchronous", -} - -func (ist IsoSyncType) String() string { - return isoSyncTypeDescription[ist] -} - -type IsoUsageType int - -const ( - ISO_USAGE_TYPE_DATA IsoUsageType = C.LIBUSB_ISO_USAGE_TYPE_DATA << 4 - ISO_USAGE_TYPE_FEEDBACK IsoUsageType = C.LIBUSB_ISO_USAGE_TYPE_FEEDBACK << 4 - ISO_USAGE_TYPE_IMPLICIT IsoUsageType = C.LIBUSB_ISO_USAGE_TYPE_IMPLICIT << 4 - ISO_USAGE_TYPE_MASK IsoUsageType = 0x30 -) - -var isoUsageTypeDescription = map[IsoUsageType]string{ - ISO_USAGE_TYPE_DATA: "data", - ISO_USAGE_TYPE_FEEDBACK: "feedback", - ISO_USAGE_TYPE_IMPLICIT: "implicit data", -} - -func (iut IsoUsageType) String() string { - return isoUsageTypeDescription[iut] + + return &Descriptor{ + Bus: uint8(C.libusb_get_bus_number(dev)), + Address: uint8(C.libusb_get_device_address(dev)), + Spec: BCD(desc.bcdUSB), + Device: BCD(desc.bcdDevice), + Vendor: ID(desc.idVendor), + Product: ID(desc.idProduct), + Class: uint8(desc.bDeviceClass), + SubClass: uint8(desc.bDeviceSubClass), + Protocol: uint8(desc.bDeviceProtocol), + Configs: cfgs, + }, nil } diff --git a/usb/device.go b/usb/device.go index c5659dc..b182d38 100644 --- a/usb/device.go +++ b/usb/device.go @@ -5,80 +5,35 @@ package usb import "C" import ( + "fmt" "reflect" "runtime" "time" "unsafe" ) -type DeviceInfo struct { - dev *C.libusb_device -} - -func newDeviceInfo(dev *C.libusb_device) *DeviceInfo { - d := &DeviceInfo{ - dev: dev, - } - - // Reference this device - C.libusb_ref_device(dev) - - // I still can't get this to be called - runtime.SetFinalizer(d, (*DeviceInfo).Close) - - //log.Printf("deviceInfo %p initialized", d.dev) - return d -} - -// Get the assigned Bus Number for this USB device. -func (d *DeviceInfo) BusNumber() byte { - return byte(C.libusb_get_bus_number(d.dev)) -} - -// Get the assigned Bus Address for this USB device. -func (d *DeviceInfo) Address() byte { - return byte(C.libusb_get_device_address(d.dev)) -} - -// Get a descriptor for the device. -func (d *DeviceInfo) Descriptor() (*Descriptor, error) { - return newDescriptor(d.dev) -} - -// Open the given device for I/O. -func (d *DeviceInfo) Open() (*Device, error) { - var handle *C.libusb_device_handle - if errno := C.libusb_open(d.dev, &handle); errno != 0 { - return nil, usbError(errno) - } - - return newDevice(handle), nil -} - -// Close decrements the reference count for the device in the libusb driver -// code. It should be called exactly once! -func (d *DeviceInfo) Close() error { - if d.dev != nil { - //log.Printf("deviceInfo %p closed", d.dev) - C.libusb_unref_device(d.dev) - } - d.dev = nil - return nil -} +var DefaultControlTimeout = 5 * time.Second type Device struct { handle *C.libusb_device_handle + + // Embed the device information for easy access + *Descriptor + + // Timeouts + ControlTimeout time.Duration } -func newDevice(handle *C.libusb_device_handle) *Device { +func newDevice(handle *C.libusb_device_handle, desc *Descriptor) *Device { d := &Device{ - handle: handle, + handle: handle, + Descriptor: desc, + ControlTimeout: DefaultControlTimeout, } - // :( + // This doesn't seem to actually get called runtime.SetFinalizer(d, (*Device).Close) - //log.Printf("device %p initialized", d.handle) return d } @@ -89,8 +44,6 @@ func (d *Device) Reset() error { return nil } -var ControlTimeout = 5 * time.Second - 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( @@ -101,33 +54,39 @@ func (d *Device) Control(rType, request uint8, val, idx uint16, data []byte) (in C.uint16_t(idx), (*C.uchar)(unsafe.Pointer(dataSlice.Data)), C.uint16_t(len(data)), - C.uint(ControlTimeout/time.Millisecond)) + C.uint(d.ControlTimeout/time.Millisecond)) if n < 0 { return int(n), usbError(n) } return int(n), nil } -func (d *Device) ActiveConfig() (int, error) { +// ActiveConfig returns the config id (not the index) of the active configuration. +// This corresponds to the ConfigInfo.Config field. +func (d *Device) ActiveConfig() (uint8, error) { var cfg C.int if errno := C.libusb_get_configuration(d.handle, &cfg); errno < 0 { return 0, usbError(errno) } - return int(cfg), nil + return uint8(cfg), nil } -func (d *Device) SetConfig(cfg int) error { +// SetConfig attempts to change the active configuration. +// The cfg provided is the config id (not the index) of the configuration to set, +// which corresponds to the ConfigInfo.Config field. +func (d *Device) SetConfig(cfg uint8) error { if errno := C.libusb_set_configuration(d.handle, C.int(cfg)); errno < 0 { return usbError(errno) } return nil } +// Close the device. func (d *Device) Close() error { - if d.handle != nil { - //log.Printf("device %p closed", d.handle) - C.libusb_unref_device(d.handle) + if d.handle == nil { + return fmt.Errorf("usb: double close on device") } + C.libusb_close(d.handle) d.handle = nil return nil } diff --git a/usb/usb.go b/usb/usb.go index 614e6f9..8b719ad 100644 --- a/usb/usb.go +++ b/usb/usb.go @@ -21,7 +21,6 @@ func (c *Context) Debug(level int) { func NewContext() *Context { c := new(Context) - //log.Printf("gousb initialized") if errno := C.libusb_init(&c.ctx); errno != 0 { panic(usbError(errno)) } @@ -32,10 +31,12 @@ func NewContext() *Context { return c } -// ListDevices calls each with each enumerated device. If the function returns -// true, the device is added to the list of *DeviceInfo to return. All of -// these must be Closed, even if an error is also returned. -func (c *Context) ListDevices(each func(bus, addr int, desc *Descriptor) bool) ([]*DeviceInfo, error) { +// ListDevices calls each with each enumerated device. +// If the function returns true, the device is opened and a Device is returned if the operation succeeds. +// Every Device returned (whether an error is also returned or not) must be closed. +// If there are any errors enumerating the devices, +// the final one is returned along with any successfully opened devices. +func (c *Context) ListDevices(each func(desc *Descriptor) bool) ([]*Device, error) { var list **C.libusb_device cnt := C.libusb_get_device_list(c.ctx, &list) if cnt < 0 { @@ -50,25 +51,30 @@ func (c *Context) ListDevices(each func(bus, addr int, desc *Descriptor) bool) ( Cap: int(cnt), } - ret := []*DeviceInfo{} + var reterr error + ret := []*Device{} for _, dev := range slice { - bus := int(C.libusb_get_bus_number(dev)) - addr := int(C.libusb_get_device_address(dev)) desc, err := newDescriptor(dev) if err != nil { - return ret, err + reterr = err + continue } - if each(bus, addr, desc) { - ret = append(ret, newDeviceInfo(dev)) + + if each(desc) { + var handle *C.libusb_device_handle + if errno := C.libusb_open(dev, &handle); errno != 0 { + reterr = err + continue + } + ret = append(ret, newDevice(handle, desc)) } } - return ret, nil + return ret, reterr } func (c *Context) Close() error { if c.ctx != nil { C.libusb_exit(c.ctx) - //log.Printf("gousb finished") } c.ctx = nil return nil diff --git a/usb/usb_test.go b/usb/usb_test.go index 5ce52e5..f139b86 100644 --- a/usb/usb_test.go +++ b/usb/usb_test.go @@ -18,9 +18,30 @@ func TestEnum(t *testing.T) { defer c.Close() c.Debug(0) - cnt := 0 - devs, err := c.ListDevices(func(bus, addr int, desc *Descriptor) bool { - cnt++ + logDevice := func(t *testing.T, desc *Descriptor) { + t.Logf("%03d.%03d %s", desc.Bus, desc.Address, usbid.Describe(desc)) + t.Logf("- Protocol: %s", usbid.Classify(desc)) + + for _, cfg := range desc.Configs { + t.Logf("- %s:", cfg) + for _, alt := range cfg.Interfaces { + t.Logf(" --------------") + for _, iface := range alt { + t.Logf(" - %s", iface) + t.Logf(" - %s", usbid.Classify(iface)) + for _, end := range iface.Endpoints { + t.Logf(" - %s (packet size: %d bytes)", end, end.MaxPacketSize) + } + } + } + t.Logf(" --------------") + } + } + + descs := []*Descriptor{} + devs, err := c.ListDevices(func(desc *Descriptor) bool { + logDevice(t, desc) + descs = append(descs, desc) return true }) defer func() { @@ -32,41 +53,13 @@ func TestEnum(t *testing.T) { t.Fatalf("list: %s", err) } - if got, want := len(devs), cnt; got != want { - t.Errorf("len(devs) = %d, want %d", got, want) + if got, want := len(devs), len(descs); got != want { + t.Fatalf("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 %s", bus, addr, usbid.Describe(desc)) - t.Logf("- Protocol: %s", usbid.Classify(desc)) - - cfgs, err := dev.Configurations() - if err != nil { - t.Errorf(" - configs: %s", err) - continue - } - - for _, cfg := range cfgs { - t.Logf("- %s:", cfg) - for _, alt := range cfg.Interfaces { - t.Logf(" --------------") - for _, iface := range alt { - t.Logf(" - %s", iface) - t.Logf(" - %s", usbid.Classify(iface)) - for _, end := range iface.Endpoints { - t.Logf(" - %s", end) - } - } - } - t.Logf(" --------------") + for i := range devs { + if got, want := devs[i].Descriptor, descs[i]; got != want { + t.Errorf("dev[%d].Descriptor = %p, want %p", i, got, want) } } }