Added isochronous transfer

This commit is contained in:
Kyle Lemons
2012-04-12 20:15:30 -07:00
parent 71bbb57904
commit 9849a972c6
6 changed files with 271 additions and 158 deletions

View File

@@ -13,6 +13,7 @@ type EndpointInfo struct {
Address uint8
Attributes uint8
MaxPacketSize uint16
MaxIsoPacket uint32
PollInterval uint8
RefreshRate uint8
SynchAddress uint8
@@ -27,11 +28,12 @@ func (e EndpointInfo) Direction() EndpointDirection {
}
func (e EndpointInfo) String() string {
return fmt.Sprintf("Endpoint %d %-3s %s - %s %s",
return fmt.Sprintf("Endpoint %d %-3s %s - %s %s [%d %d]",
e.Number(), e.Direction(),
TransferType(e.Attributes)&TRANSFER_TYPE_MASK,
IsoSyncType(e.Attributes)&ISO_SYNC_TYPE_MASK,
IsoUsageType(e.Attributes)&ISO_USAGE_TYPE_MASK,
e.MaxPacketSize, e.MaxIsoPacket,
)
}
@@ -68,7 +70,7 @@ func (c ConfigInfo) String() string {
return fmt.Sprintf("Config %02x", c.Config)
}
func newConfig(cfg *C.struct_libusb_config_descriptor) ConfigInfo {
func newConfig(dev *C.libusb_device, cfg *C.struct_libusb_config_descriptor) ConfigInfo {
c := ConfigInfo{
Config: uint8(cfg.bConfigurationValue),
Attributes: uint8(cfg.bmAttributes),
@@ -114,6 +116,7 @@ func newConfig(cfg *C.struct_libusb_config_descriptor) ConfigInfo {
Address: uint8(end.bEndpointAddress),
Attributes: uint8(end.bmAttributes),
MaxPacketSize: uint16(end.wMaxPacketSize),
//MaxIsoPacket: uint32(C.libusb_get_max_iso_packet_size(dev, C.uchar(end.bEndpointAddress))),
PollInterval: uint8(end.bInterval),
RefreshRate: uint8(end.bRefresh),
SynchAddress: uint8(end.bSynchAddress),

View File

@@ -38,7 +38,7 @@ func newDescriptor(dev *C.libusb_device) (*Descriptor, error) {
if errno := C.libusb_get_config_descriptor(dev, C.uint8_t(i), &cfg); errno < 0 {
return nil, usbError(errno)
}
cfgs = append(cfgs, newConfig(cfg))
cfgs = append(cfgs, newConfig(dev, cfg))
C.libusb_free_config_descriptor(cfg)
}

View File

@@ -4,6 +4,182 @@ package usb
import "C"
import (
"fmt"
"log"
"reflect"
"runtime"
"sync"
"time"
"unsafe"
)
var DefaultReadTimeout = 1 * time.Second
var DefaultWriteTimeout = 1 * time.Second
var DefaultControlTimeout = 5 * time.Second
type Device struct {
handle *C.libusb_device_handle
// Embed the device information for easy access
*Descriptor
// Timeouts
ReadTimeout time.Duration
WriteTimeout time.Duration
ControlTimeout time.Duration
// Claimed interfaces
lock *sync.Mutex
claimed map[uint8]int
}
func newDevice(handle *C.libusb_device_handle, desc *Descriptor) *Device {
ifaces := 0
d := &Device{
handle: handle,
Descriptor: desc,
ReadTimeout: DefaultReadTimeout,
WriteTimeout: DefaultWriteTimeout,
ControlTimeout: DefaultControlTimeout,
lock: new(sync.Mutex),
claimed: make(map[uint8]int, ifaces),
}
// This doesn't seem to actually get called
runtime.SetFinalizer(d, (*Device).Close)
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)),
C.uint(d.ControlTimeout/time.Millisecond))
if n < 0 {
return int(n), usbError(n)
}
return int(n), nil
}
// 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 uint8(cfg), nil
}
// 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 {
return fmt.Errorf("usb: double close on device")
}
d.lock.Lock()
defer d.lock.Unlock()
for iface := range d.claimed {
C.libusb_release_interface(d.handle, C.int(iface))
}
C.libusb_close(d.handle)
d.handle = nil
return nil
}
func (d *Device) OpenEndpoint(conf, iface, setup, epoint uint8) (Endpoint, error) {
end := &endpoint{
Device: d,
}
for _, c := range d.Configs {
if c.Config != conf {
continue
}
fmt.Printf("found conf: %#v\n", c)
for _, i := range c.Interfaces {
if i.Number != iface {
continue
}
fmt.Printf("found iface: %#v\n", i)
for _, s := range i.Setups {
if s.Alternate != setup {
continue
}
fmt.Printf("found setup: %#v\n", s)
for _, e := range s.Endpoints {
fmt.Printf("ep %02x search: %#v\n", epoint, s)
if e.Address != epoint {
continue
}
end.InterfaceSetup = s
end.EndpointInfo = e
switch tt := TransferType(e.Attributes) & TRANSFER_TYPE_MASK; tt {
case TRANSFER_TYPE_BULK:
end.xfer = bulk_xfer
case TRANSFER_TYPE_INTERRUPT:
end.xfer = interrupt_xfer
case TRANSFER_TYPE_ISOCHRONOUS:
end.xfer = isochronous_xfer
default:
return nil, fmt.Errorf("usb: %s transfer is unsupported", tt)
}
goto found
}
return nil, fmt.Errorf("usb: unknown endpoint %02x", epoint)
}
return nil, fmt.Errorf("usb: unknown setup %02x", setup)
}
return nil, fmt.Errorf("usb: unknown interface %02x", iface)
}
return nil, fmt.Errorf("usb: unknown configuration %02x", conf)
found:
// Claim the interface
if errno := C.libusb_claim_interface(d.handle, C.int(iface)); errno < 0 {
return nil, fmt.Errorf("usb: claim: %s", usbError(errno))
}
// Increment the claim count
d.lock.Lock()
d.claimed[iface]++
d.lock.Unlock() // unlock immediately because the next calls may block
// Set the configuration
if errno := C.libusb_set_configuration(d.handle, C.int(conf)); errno < 0 {
return nil, fmt.Errorf("usb: setcfg: %s", usbError(errno))
}
// Choose the alternate
// This doesn't seem to work...
if errno := C.libusb_set_interface_alt_setting(d.handle, C.int(iface), C.int(setup)); errno < 0 {
log.Printf("ignoring altsetting error: %s", usbError(errno))
//return nil, fmt.Errorf("usb: setalt: %s", usbError(errno))
}
return end, nil
}

View File

@@ -6,8 +6,8 @@ import "C"
import (
"fmt"
"reflect"
"unsafe"
"time"
"unsafe"
)
type Endpoint interface {
@@ -25,7 +25,7 @@ type endpoint struct {
}
func (e *endpoint) Read(buf []byte) (int, error) {
if EndpointDirection(e.Address) & ENDPOINT_DIR_MASK != ENDPOINT_DIR_OUT {
if EndpointDirection(e.Address)&ENDPOINT_DIR_MASK != ENDPOINT_DIR_OUT {
return 0, fmt.Errorf("usb: read: not an IN endpoint")
}
@@ -33,7 +33,7 @@ func (e *endpoint) Read(buf []byte) (int, error) {
}
func (e *endpoint) Write(buf []byte) (int, error) {
if EndpointDirection(e.Address) & ENDPOINT_DIR_MASK != ENDPOINT_DIR_OUT {
if EndpointDirection(e.Address)&ENDPOINT_DIR_MASK != ENDPOINT_DIR_OUT {
return 0, fmt.Errorf("usb: write: not an OUT endpoint")
}
@@ -41,7 +41,7 @@ func (e *endpoint) Write(buf []byte) (int, error) {
}
func (e *endpoint) Interface() InterfaceSetup { return InterfaceSetup{} }
func (e *endpoint) Info() EndpointInfo { return EndpointInfo{} }
func (e *endpoint) Info() EndpointInfo { return EndpointInfo{} }
// TODO(kevlar): (*Endpoint).Close

9
usb/iso.c Normal file
View File

@@ -0,0 +1,9 @@
#include <libusb-1.0/libusb.h>
void _callback(struct libusb_transfer *xfer) {
iso_callback(xfer->user_data);
}
libusb_transfer_cb_fn callback() {
return &_callback;
}

View File

@@ -1,182 +1,107 @@
package usb
// #include <libusb-1.0/libusb.h>
/*
#include <libusb-1.0/libusb.h>
libusb_transfer_cb_fn callback();
*/
import "C"
import (
"fmt"
"log"
"reflect"
"runtime"
"sync"
"time"
"unsafe"
)
var DefaultReadTimeout = 1 * time.Second
var DefaultWriteTimeout = 1 * time.Second
var DefaultControlTimeout = 5 * time.Second
type Device struct {
handle *C.libusb_device_handle
// Embed the device information for easy access
*Descriptor
// Timeouts
ReadTimeout time.Duration
WriteTimeout time.Duration
ControlTimeout time.Duration
// Claimed interfaces
lock *sync.Mutex
claimed map[uint8]int
//export iso_callback
func iso_callback(cptr unsafe.Pointer) {
ch := *(*chan struct{})(cptr)
close(ch)
}
func newDevice(handle *C.libusb_device_handle, desc *Descriptor) *Device {
ifaces := 0
d := &Device{
handle: handle,
Descriptor: desc,
ReadTimeout: DefaultReadTimeout,
WriteTimeout: DefaultWriteTimeout,
ControlTimeout: DefaultControlTimeout,
lock: new(sync.Mutex),
claimed: make(map[uint8]int, ifaces),
func (end *endpoint) allocTransfer() *Transfer {
// Use libusb_get_max_iso_packet_size ?
const (
iso_packets = 242
packet_size = 1760
)
xfer := C.libusb_alloc_transfer(C.int(iso_packets))
if xfer == nil {
log.Printf("usb: transfer allocation failed?!")
return nil
}
// This doesn't seem to actually get called
runtime.SetFinalizer(d, (*Device).Close)
xfer.dev_handle = end.Device.handle
xfer.endpoint = C.uchar(end.Address)
xfer._type = C.LIBUSB_TRANSFER_TYPE_ISOCHRONOUS
return d
xfer.callback = C.callback()
xfer.timeout = C.uint(5 * time.Second / time.Millisecond)
buf := make([]byte, iso_packets*packet_size)
xfer.num_iso_packets = iso_packets
xfer.buffer = (*C.uchar)((unsafe.Pointer)(&buf[0]))
xfer.length = C.int(len(buf))
C.libusb_set_iso_packet_lengths(xfer, C.uint(len(buf)))
pkts := *(*[]C.struct_libusb_packet_descriptor)(unsafe.Pointer(&reflect.SliceHeader{
Data: uintptr(unsafe.Pointer(&xfer.iso_packet_desc)),
Len: iso_packets,
Cap: iso_packets,
}))
done := make(chan struct{})
xfer.user_data = (unsafe.Pointer)(&done)
t := &Transfer{
xfer: xfer,
pkts: pkts,
done: done,
buf: buf,
}
runtime.SetFinalizer(t, (*Transfer).Close)
return t
}
func (d *Device) Reset() error {
if errno := C.libusb_reset_device(d.handle); errno != 0 {
type Transfer struct {
xfer *C.struct_libusb_transfer
pkts []C.struct_libusb_packet_descriptor
done chan struct{}
buf []byte
}
func (xfer *Transfer) Submit() error {
if errno := C.libusb_submit_transfer(xfer.xfer); 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)),
C.uint(d.ControlTimeout/time.Millisecond))
if n < 0 {
return int(n), usbError(n)
func (xfer *Transfer) Wait() (n int, err error) {
<-xfer.done
n = int(xfer.xfer.actual_length)
for _, pkt := range xfer.pkts {
log.Printf("PACKET[%4d] - %#v", pkt)
}
return int(n), nil
return n, err
}
// 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 uint8(cfg), nil
}
// 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)
}
func (xfer *Transfer) Close() error {
C.libusb_free_transfer(xfer.xfer)
return nil
}
// Close the device.
func (d *Device) Close() error {
if d.handle == nil {
return fmt.Errorf("usb: double close on device")
func isochronous_xfer(e *endpoint, buf []byte, timeout time.Duration) (int, error) {
xfer := e.allocTransfer()
defer xfer.Close()
if err := xfer.Submit(); err != nil {
return 0, err
}
d.lock.Lock()
defer d.lock.Unlock()
for iface := range d.claimed {
C.libusb_release_interface(d.handle, C.int(iface))
}
C.libusb_close(d.handle)
d.handle = nil
return nil
}
func (d *Device) OpenEndpoint(conf, iface, setup, epoint uint8) (Endpoint, error) {
end := &endpoint{
Device: d,
}
for _, c := range d.Configs {
if c.Config != conf {
continue
}
fmt.Printf("found conf: %#v\n", c)
for _, i := range c.Interfaces {
if i.Number != iface {
continue
}
fmt.Printf("found iface: %#v\n", i)
for _, s := range i.Setups {
if s.Alternate != setup {
continue
}
fmt.Printf("found setup: %#v\n", s)
for _, e := range s.Endpoints {
fmt.Printf("ep %02x search: %#v\n", epoint, s)
if e.Address != epoint {
continue
}
end.InterfaceSetup = s
end.EndpointInfo = e
switch tt := TransferType(e.Attributes) & TRANSFER_TYPE_MASK; tt {
case TRANSFER_TYPE_BULK:
end.xfer = bulk_xfer
case TRANSFER_TYPE_INTERRUPT:
end.xfer = interrupt_xfer
default:
return nil, fmt.Errorf("usb: %s transfer is unsupported", tt)
}
goto found
}
return nil, fmt.Errorf("usb: unknown endpoint %02x", epoint)
}
return nil, fmt.Errorf("usb: unknown setup %02x", setup)
}
return nil, fmt.Errorf("usb: unknown interface %02x", iface)
}
return nil, fmt.Errorf("usb: unknown configuration %02x", conf)
found:
// Claim the interface
if errno := C.libusb_claim_interface(d.handle, C.int(iface)); errno < 0 {
return nil, fmt.Errorf("usb: claim: %s", usbError(errno))
}
// Increment the claim count
d.lock.Lock()
d.claimed[iface]++
d.lock.Unlock() // unlock immediately because the next calls may block
// Set the configuration
if errno := C.libusb_set_configuration(d.handle, C.int(conf)); errno < 0 {
return nil, fmt.Errorf("usb: setcfg: %s", usbError(errno))
}
// Choose the alternate
/* This doesn't seem to work...
if errno := C.libusb_set_interface_alt_setting(d.handle, C.int(iface), C.int(setup)); errno < 0 {
return nil, fmt.Errorf("usb: setalt: %s", usbError(errno))
}
*/
return end, nil
return xfer.Wait()
}