Modify EndpointInfo to provide meaningful data rather than raw USB
descriptor values. E.g. for the user, the distinction between MaxIsoPkt and MaxPktSize is irrelevant, only the calculated max packet size matters.
This commit is contained in:
@@ -17,37 +17,31 @@ package usb
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type EndpointInfo struct {
|
type EndpointInfo struct {
|
||||||
Address uint8
|
Number uint8
|
||||||
Attributes uint8
|
Direction EndpointDirection
|
||||||
MaxPacketSize uint16
|
MaxPacketSize uint32
|
||||||
MaxIsoPacket uint32
|
TransferType TransferType
|
||||||
PollInterval uint8
|
PollInterval time.Duration
|
||||||
RefreshRate uint8
|
IsoSyncType IsoSyncType
|
||||||
SynchAddress uint8
|
UsageType UsageType
|
||||||
}
|
|
||||||
|
|
||||||
func (e EndpointInfo) Number() int {
|
|
||||||
return int(e.Address & EndpointNumMask)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e EndpointInfo) TransferType() TransferType {
|
|
||||||
return TransferType(e.Attributes & TransferTypeMask)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e EndpointInfo) Direction() EndpointDirection {
|
|
||||||
return EndpointDirection(e.Address & EndpointDirectionMask)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e EndpointInfo) String() string {
|
func (e EndpointInfo) String() string {
|
||||||
return fmt.Sprintf("Endpoint #%d %-3s %s - %s %s [%d %d]",
|
ret := make([]string, 0, 3)
|
||||||
e.Number(), e.Direction(), e.TransferType(),
|
ret = append(ret, fmt.Sprintf("Endpoint #%d %s (address 0x%02x) %s", e.Number, e.Direction, uint8(e.Number)|uint8(e.Direction), e.TransferType))
|
||||||
IsoSyncType(e.Attributes)&IsoSyncTypeMask,
|
switch e.TransferType {
|
||||||
IsoUsageType(e.Attributes)&IsoUsageTypeMask,
|
case TransferTypeIsochronous:
|
||||||
e.MaxPacketSize, e.MaxIsoPacket,
|
ret = append(ret, fmt.Sprintf("- %s %s", e.IsoSyncType, e.UsageType))
|
||||||
)
|
case TransferTypeInterrupt:
|
||||||
|
ret = append(ret, fmt.Sprintf("- %s", e.UsageType))
|
||||||
|
}
|
||||||
|
ret = append(ret, fmt.Sprintf("[%d bytes]", e.MaxPacketSize))
|
||||||
|
return strings.Join(ret, " ")
|
||||||
}
|
}
|
||||||
|
|
||||||
type InterfaceInfo struct {
|
type InterfaceInfo struct {
|
||||||
|
@@ -9,20 +9,23 @@ func TestEndpointInfo(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
ep: EndpointInfo{
|
ep: EndpointInfo{
|
||||||
Address: 0x86,
|
Number: 6,
|
||||||
Attributes: 0x02,
|
Direction: EndpointDirectionIn,
|
||||||
|
TransferType: TransferTypeBulk,
|
||||||
MaxPacketSize: 512,
|
MaxPacketSize: 512,
|
||||||
},
|
},
|
||||||
want: "Endpoint #6 IN bulk - unsynchronized data [512 0]",
|
want: "Endpoint #6 IN (address 0x86) bulk [512 bytes]",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ep: EndpointInfo{
|
ep: EndpointInfo{
|
||||||
Address: 0x02,
|
Number: 2,
|
||||||
Attributes: 0x05,
|
Direction: EndpointDirectionOut,
|
||||||
|
TransferType: TransferTypeIsochronous,
|
||||||
MaxPacketSize: 512,
|
MaxPacketSize: 512,
|
||||||
MaxIsoPacket: 512,
|
IsoSyncType: IsoSyncTypeAsync,
|
||||||
|
UsageType: IsoUsageTypeData,
|
||||||
},
|
},
|
||||||
want: "Endpoint #2 OUT isochronous - asynchronous data [512 512]",
|
want: "Endpoint #2 OUT (address 0x02) isochronous - asynchronous data [512 bytes]",
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
if got := tc.ep.String(); got != tc.want {
|
if got := tc.ep.String(); got != tc.want {
|
||||||
|
@@ -144,23 +144,32 @@ func (ist IsoSyncType) String() string {
|
|||||||
return isoSyncTypeDescription[ist]
|
return isoSyncTypeDescription[ist]
|
||||||
}
|
}
|
||||||
|
|
||||||
type IsoUsageType uint8
|
type UsageType uint8
|
||||||
|
|
||||||
const (
|
const (
|
||||||
IsoUsageTypeData IsoUsageType = C.LIBUSB_ISO_USAGE_TYPE_DATA << 4
|
// Note: USB3.0 defines usage type for both isochronous and interrupt
|
||||||
IsoUsageTypeFeedback IsoUsageType = C.LIBUSB_ISO_USAGE_TYPE_FEEDBACK << 4
|
// endpoints, with the same constants representing different usage types.
|
||||||
IsoUsageTypeImplicit IsoUsageType = C.LIBUSB_ISO_USAGE_TYPE_IMPLICIT << 4
|
// UsageType constants do not correspond to bmAttribute values.
|
||||||
IsoUsageTypeMask = 0x30
|
UsageTypeMask = 0x30
|
||||||
|
UsageTypeUndefined UsageType = iota
|
||||||
|
IsoUsageTypeData
|
||||||
|
IsoUsageTypeFeedback
|
||||||
|
IsoUsageTypeImplicit
|
||||||
|
InterruptUsageTypePeriodic
|
||||||
|
InterruptUsageTypeNotification
|
||||||
)
|
)
|
||||||
|
|
||||||
var isoUsageTypeDescription = map[IsoUsageType]string{
|
var usageTypeDescription = map[UsageType]string{
|
||||||
IsoUsageTypeData: "data",
|
UsageTypeUndefined: "undefined usage",
|
||||||
IsoUsageTypeFeedback: "feedback",
|
IsoUsageTypeData: "data",
|
||||||
IsoUsageTypeImplicit: "implicit data",
|
IsoUsageTypeFeedback: "feedback",
|
||||||
|
IsoUsageTypeImplicit: "implicit data",
|
||||||
|
InterruptUsageTypePeriodic: "periodic",
|
||||||
|
InterruptUsageTypeNotification: "notification",
|
||||||
}
|
}
|
||||||
|
|
||||||
func (iut IsoUsageType) String() string {
|
func (ut UsageType) String() string {
|
||||||
return isoUsageTypeDescription[iut]
|
return usageTypeDescription[ut]
|
||||||
}
|
}
|
||||||
|
|
||||||
type RequestType uint8
|
type RequestType uint8
|
||||||
|
@@ -92,7 +92,7 @@ func (d *Device) Close() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Device) OpenEndpoint(cfgNum, ifNum, setNum, epNum uint8) (*Endpoint, error) {
|
func (d *Device) OpenEndpoint(epNum, cfgNum, ifNum, setNum uint8) (*Endpoint, error) {
|
||||||
var cfg *ConfigInfo
|
var cfg *ConfigInfo
|
||||||
for _, c := range d.Configs {
|
for _, c := range d.Configs {
|
||||||
if c.Config == cfgNum {
|
if c.Config == cfgNum {
|
||||||
@@ -132,7 +132,7 @@ func (d *Device) OpenEndpoint(cfgNum, ifNum, setNum, epNum uint8) (*Endpoint, er
|
|||||||
|
|
||||||
var ep *EndpointInfo
|
var ep *EndpointInfo
|
||||||
for _, e := range ifs.Endpoints {
|
for _, e := range ifs.Endpoints {
|
||||||
if e.Address == epNum {
|
if e.Number == epNum {
|
||||||
debug.Printf("found ep %02x in %#v\n", epNum, *ifs)
|
debug.Printf("found ep %02x in %#v\n", epNum, *ifs)
|
||||||
ep = &e
|
ep = &e
|
||||||
}
|
}
|
||||||
|
@@ -24,14 +24,14 @@ type Endpoint struct {
|
|||||||
h *libusbDevHandle
|
h *libusbDevHandle
|
||||||
|
|
||||||
InterfaceSetup
|
InterfaceSetup
|
||||||
EndpointInfo
|
Info EndpointInfo
|
||||||
|
|
||||||
readTimeout time.Duration
|
readTimeout time.Duration
|
||||||
writeTimeout time.Duration
|
writeTimeout time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Endpoint) Read(buf []byte) (int, error) {
|
func (e *Endpoint) Read(buf []byte) (int, error) {
|
||||||
if EndpointDirection(e.Address)&EndpointDirectionMask != EndpointDirectionIn {
|
if e.Info.Direction != EndpointDirectionIn {
|
||||||
return 0, fmt.Errorf("usb: read: not an IN endpoint")
|
return 0, fmt.Errorf("usb: read: not an IN endpoint")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,22 +39,19 @@ func (e *Endpoint) Read(buf []byte) (int, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (e *Endpoint) Write(buf []byte) (int, error) {
|
func (e *Endpoint) Write(buf []byte) (int, error) {
|
||||||
if EndpointDirection(e.Address)&EndpointDirectionMask != EndpointDirectionOut {
|
if e.Info.Direction != EndpointDirectionOut {
|
||||||
return 0, fmt.Errorf("usb: write: not an OUT endpoint")
|
return 0, fmt.Errorf("usb: write: not an OUT endpoint")
|
||||||
}
|
}
|
||||||
|
|
||||||
return e.transfer(buf, e.writeTimeout)
|
return e.transfer(buf, e.writeTimeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Endpoint) Interface() InterfaceSetup { return e.InterfaceSetup }
|
|
||||||
func (e *Endpoint) Info() EndpointInfo { return e.EndpointInfo }
|
|
||||||
|
|
||||||
func (e *Endpoint) transfer(buf []byte, timeout time.Duration) (int, error) {
|
func (e *Endpoint) transfer(buf []byte, timeout time.Duration) (int, error) {
|
||||||
if len(buf) == 0 {
|
if len(buf) == 0 {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
t, err := newUSBTransfer(e.h, e.EndpointInfo, buf, timeout)
|
t, err := newUSBTransfer(e.h, &e.Info, buf, timeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
@@ -74,7 +71,7 @@ func (e *Endpoint) transfer(buf []byte, timeout time.Duration) (int, error) {
|
|||||||
func newEndpoint(h *libusbDevHandle, s InterfaceSetup, e EndpointInfo, rt, wt time.Duration) *Endpoint {
|
func newEndpoint(h *libusbDevHandle, s InterfaceSetup, e EndpointInfo, rt, wt time.Duration) *Endpoint {
|
||||||
return &Endpoint{
|
return &Endpoint{
|
||||||
InterfaceSetup: s,
|
InterfaceSetup: s,
|
||||||
EndpointInfo: e,
|
Info: e,
|
||||||
h: h,
|
h: h,
|
||||||
readTimeout: rt,
|
readTimeout: rt,
|
||||||
writeTimeout: wt,
|
writeTimeout: wt,
|
||||||
|
@@ -14,12 +14,14 @@
|
|||||||
|
|
||||||
package usb
|
package usb
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
// IN bulk endpoint
|
// IN bulk endpoint
|
||||||
var testBulkInEP = EndpointInfo{
|
var testBulkInEP = EndpointInfo{
|
||||||
Address: 0x82,
|
Number: 2,
|
||||||
Attributes: uint8(TransferTypeBulk),
|
Direction: EndpointDirectionIn,
|
||||||
MaxPacketSize: 512,
|
MaxPacketSize: 512,
|
||||||
PollInterval: 1,
|
TransferType: TransferTypeBulk,
|
||||||
}
|
}
|
||||||
|
|
||||||
var testBulkInSetup = InterfaceSetup{
|
var testBulkInSetup = InterfaceSetup{
|
||||||
@@ -31,11 +33,11 @@ var testBulkInSetup = InterfaceSetup{
|
|||||||
|
|
||||||
// OUT iso endpoint
|
// OUT iso endpoint
|
||||||
var testIsoOutEP = EndpointInfo{
|
var testIsoOutEP = EndpointInfo{
|
||||||
Address: 0x06,
|
Number: 6,
|
||||||
Attributes: uint8(TransferTypeIsochronous),
|
MaxPacketSize: 3 * 1024,
|
||||||
MaxPacketSize: 3<<11 + 1024,
|
TransferType: TransferTypeIsochronous,
|
||||||
MaxIsoPacket: 3 * 1024,
|
PollInterval: 125 * time.Microsecond,
|
||||||
PollInterval: 1,
|
UsageType: IsoUsageTypeData,
|
||||||
}
|
}
|
||||||
|
|
||||||
var testIsoOutSetup = InterfaceSetup{
|
var testIsoOutSetup = InterfaceSetup{
|
||||||
|
@@ -90,7 +90,7 @@ func TestEndpoint(t *testing.T) {
|
|||||||
func TestEndpointWrongDirection(t *testing.T) {
|
func TestEndpointWrongDirection(t *testing.T) {
|
||||||
ep := &Endpoint{
|
ep := &Endpoint{
|
||||||
InterfaceSetup: testBulkInSetup,
|
InterfaceSetup: testBulkInSetup,
|
||||||
EndpointInfo: testBulkInEP,
|
Info: testBulkInEP,
|
||||||
}
|
}
|
||||||
_, err := ep.Write([]byte{1, 2, 3})
|
_, err := ep.Write([]byte{1, 2, 3})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -98,7 +98,7 @@ func TestEndpointWrongDirection(t *testing.T) {
|
|||||||
}
|
}
|
||||||
ep = &Endpoint{
|
ep = &Endpoint{
|
||||||
InterfaceSetup: testIsoOutSetup,
|
InterfaceSetup: testIsoOutSetup,
|
||||||
EndpointInfo: testIsoOutEP,
|
Info: testIsoOutEP,
|
||||||
}
|
}
|
||||||
_, err = ep.Read(make([]byte, 64))
|
_, err = ep.Read(make([]byte, 64))
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -120,15 +120,11 @@ func TestOpenEndpoint(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("OpenDeviceWithVidPid(0x8888, 0x0002): got error %v, want nil", err)
|
t.Fatalf("OpenDeviceWithVidPid(0x8888, 0x0002): got error %v, want nil", err)
|
||||||
}
|
}
|
||||||
ep, err := dev.OpenEndpoint(1, 1, 2, 0x86)
|
got, err := dev.OpenEndpoint(6, 1, 1, 2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("OpenEndpoint(cfg=1, if=1, alt=2, ep=0x86): got error %v, want nil", err)
|
t.Fatalf("OpenEndpoint(cfg=1, if=1, alt=2, ep=0x86): got error %v, want nil", err)
|
||||||
}
|
}
|
||||||
i := ep.Info()
|
if want := fakeDevices[1].Configs[0].Interfaces[1].Setups[2].Endpoints[1]; !reflect.DeepEqual(got.Info, want) {
|
||||||
if got, want := i.Address, uint8(0x86); got != want {
|
t.Errorf("OpenEndpoint(cfg=1, if=1, alt=2, ep=0x86): got %+v, want %+v", got, want)
|
||||||
t.Errorf("OpenEndpoint(cfg=1, if=1, alt=2, ep=0x86): ep.Info.Address = %x, want %x", got, want)
|
|
||||||
}
|
|
||||||
if got, want := i.MaxIsoPacket, uint32(1024); got != want {
|
|
||||||
t.Errorf("OpenEndpoint(cfg=1, if=1, alt=2, ep=0x86): ep.Info.MaxIsoPacket = %d, want %d", got, want)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -46,13 +46,15 @@ var (
|
|||||||
Alternate: 0,
|
Alternate: 0,
|
||||||
IfClass: uint8(ClassVendorSpec),
|
IfClass: uint8(ClassVendorSpec),
|
||||||
Endpoints: []EndpointInfo{{
|
Endpoints: []EndpointInfo{{
|
||||||
Address: uint8(0x01 | EndpointDirectionOut),
|
Number: 1,
|
||||||
Attributes: uint8(TransferTypeBulk),
|
Direction: EndpointDirectionOut,
|
||||||
MaxPacketSize: 512,
|
MaxPacketSize: 512,
|
||||||
|
TransferType: TransferTypeBulk,
|
||||||
}, {
|
}, {
|
||||||
Address: uint8(0x02 | EndpointDirectionIn),
|
Number: 2,
|
||||||
Attributes: uint8(TransferTypeBulk),
|
Direction: EndpointDirectionIn,
|
||||||
MaxPacketSize: 512,
|
MaxPacketSize: 512,
|
||||||
|
TransferType: TransferTypeBulk,
|
||||||
}},
|
}},
|
||||||
}},
|
}},
|
||||||
}},
|
}},
|
||||||
@@ -87,45 +89,47 @@ var (
|
|||||||
Alternate: 0,
|
Alternate: 0,
|
||||||
IfClass: uint8(ClassVendorSpec),
|
IfClass: uint8(ClassVendorSpec),
|
||||||
Endpoints: []EndpointInfo{{
|
Endpoints: []EndpointInfo{{
|
||||||
Address: uint8(0x05 | EndpointDirectionOut),
|
Number: 5,
|
||||||
Attributes: uint8(TransferTypeIsochronous),
|
Direction: EndpointDirectionOut,
|
||||||
MaxPacketSize: 2<<11 | 1024,
|
MaxPacketSize: 3 * 1024,
|
||||||
MaxIsoPacket: 3 * 1024,
|
TransferType: TransferTypeIsochronous,
|
||||||
|
UsageType: IsoUsageTypeData,
|
||||||
}, {
|
}, {
|
||||||
Address: uint8(0x06 | EndpointDirectionIn),
|
Number: 6,
|
||||||
Attributes: uint8(TransferTypeIsochronous),
|
Direction: EndpointDirectionIn,
|
||||||
MaxPacketSize: 2<<11 | 1024,
|
MaxPacketSize: 3 * 1024,
|
||||||
MaxIsoPacket: 3 * 1024,
|
TransferType: TransferTypeIsochronous,
|
||||||
|
UsageType: IsoUsageTypeData,
|
||||||
}},
|
}},
|
||||||
}, {
|
}, {
|
||||||
Number: 1,
|
Number: 1,
|
||||||
Alternate: 1,
|
Alternate: 1,
|
||||||
IfClass: uint8(ClassVendorSpec),
|
IfClass: uint8(ClassVendorSpec),
|
||||||
Endpoints: []EndpointInfo{{
|
Endpoints: []EndpointInfo{{
|
||||||
Address: uint8(0x05 | EndpointDirectionOut),
|
Number: 5,
|
||||||
Attributes: uint8(TransferTypeIsochronous),
|
Direction: EndpointDirectionOut,
|
||||||
MaxPacketSize: 1<<11 | 1024,
|
MaxPacketSize: 2 * 1024,
|
||||||
MaxIsoPacket: 2 * 1024,
|
TransferType: TransferTypeIsochronous,
|
||||||
}, {
|
}, {
|
||||||
Address: uint8(0x06 | EndpointDirectionIn),
|
Number: 6,
|
||||||
Attributes: uint8(TransferTypeIsochronous),
|
Direction: EndpointDirectionIn,
|
||||||
MaxPacketSize: 1<<11 | 1024,
|
MaxPacketSize: 2 * 1024,
|
||||||
MaxIsoPacket: 2 * 1024,
|
TransferType: TransferTypeIsochronous,
|
||||||
}},
|
}},
|
||||||
}, {
|
}, {
|
||||||
Number: 1,
|
Number: 1,
|
||||||
Alternate: 2,
|
Alternate: 2,
|
||||||
IfClass: uint8(ClassVendorSpec),
|
IfClass: uint8(ClassVendorSpec),
|
||||||
Endpoints: []EndpointInfo{{
|
Endpoints: []EndpointInfo{{
|
||||||
Address: uint8(0x05 | EndpointDirectionOut),
|
Number: 5,
|
||||||
Attributes: uint8(TransferTypeIsochronous),
|
Direction: EndpointDirectionIn,
|
||||||
MaxPacketSize: 1024,
|
MaxPacketSize: 1024,
|
||||||
MaxIsoPacket: 1024,
|
TransferType: TransferTypeIsochronous,
|
||||||
}, {
|
}, {
|
||||||
Address: uint8(0x06 | EndpointDirectionIn),
|
Number: 6,
|
||||||
Attributes: uint8(TransferTypeIsochronous),
|
Direction: EndpointDirectionIn,
|
||||||
MaxPacketSize: 1024,
|
MaxPacketSize: 1024,
|
||||||
MaxIsoPacket: 1024,
|
TransferType: TransferTypeIsochronous,
|
||||||
}},
|
}},
|
||||||
}},
|
}},
|
||||||
}},
|
}},
|
||||||
@@ -256,7 +260,7 @@ func (f *fakeLibusb) setAlt(d *libusbDevHandle, intf, alt uint8) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *fakeLibusb) alloc(_ *libusbDevHandle, _ uint8, _ TransferType, _ time.Duration, _ int, buf []byte) (*libusbTransfer, error) {
|
func (f *fakeLibusb) alloc(_ *libusbDevHandle, _ *EndpointInfo, _ time.Duration, _ int, buf []byte) (*libusbTransfer, error) {
|
||||||
f.mu.Lock()
|
f.mu.Lock()
|
||||||
defer f.mu.Unlock()
|
defer f.mu.Unlock()
|
||||||
t := new(libusbTransfer)
|
t := new(libusbTransfer)
|
||||||
|
@@ -36,6 +36,74 @@ type libusbDevice C.libusb_device
|
|||||||
type libusbDevHandle C.libusb_device_handle
|
type libusbDevHandle C.libusb_device_handle
|
||||||
type libusbTransfer C.struct_libusb_transfer
|
type libusbTransfer C.struct_libusb_transfer
|
||||||
type libusbIso C.struct_libusb_iso_packet_descriptor
|
type libusbIso C.struct_libusb_iso_packet_descriptor
|
||||||
|
type libusbEndpoint C.struct_libusb_endpoint_descriptor
|
||||||
|
|
||||||
|
func (ep libusbEndpoint) endpointInfo(dev *Descriptor) EndpointInfo {
|
||||||
|
ei := EndpointInfo{
|
||||||
|
Number: uint8(ep.bEndpointAddress & EndpointNumMask),
|
||||||
|
Direction: EndpointDirection(ep.bEndpointAddress & EndpointDirectionMask),
|
||||||
|
TransferType: TransferType(ep.bmAttributes & TransferTypeMask),
|
||||||
|
MaxPacketSize: uint32(ep.wMaxPacketSize),
|
||||||
|
}
|
||||||
|
if ei.TransferType == TransferTypeIsochronous {
|
||||||
|
// bits 0-10 identify the packet size, bits 11-12 are the number of additional transactions per microframe.
|
||||||
|
// Don't use libusb_get_max_iso_packet_size, as it has a bug where it returns the same value
|
||||||
|
// regardless of alternative setting used, where different alternative settings might define different
|
||||||
|
// max packet sizes.
|
||||||
|
// See http://libusb.org/ticket/77 for more background.
|
||||||
|
ei.MaxPacketSize = uint32(ep.wMaxPacketSize) & 0x07ff * (uint32(ep.wMaxPacketSize)>>11&3 + 1)
|
||||||
|
ei.IsoSyncType = IsoSyncType(ep.bmAttributes & IsoSyncTypeMask)
|
||||||
|
switch ep.bmAttributes & UsageTypeMask {
|
||||||
|
case C.LIBUSB_ISO_USAGE_TYPE_DATA:
|
||||||
|
ei.UsageType = IsoUsageTypeData
|
||||||
|
case C.LIBUSB_ISO_USAGE_TYPE_FEEDBACK:
|
||||||
|
ei.UsageType = IsoUsageTypeFeedback
|
||||||
|
case C.LIBUSB_ISO_USAGE_TYPE_IMPLICIT:
|
||||||
|
ei.UsageType = IsoUsageTypeImplicit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO(sebek): PollInterval does not work yet. The meaning of bInterval
|
||||||
|
// in the descriptor varies depending on the device and USB version:
|
||||||
|
// - if the device conforms to USB1.x:
|
||||||
|
// Interval for polling endpoint for data transfers. Expressed in
|
||||||
|
// milliseconds.
|
||||||
|
// This field is ignored for bulk and control endpoints. For
|
||||||
|
// isochronous endpoints this field must be set to 1. For interrupt
|
||||||
|
// endpoints, this field may range from 1 to 255.
|
||||||
|
// - if the device conforms to USB[23].x and the device is in low speed
|
||||||
|
// of full speed mode:
|
||||||
|
// Interval for polling endpoint for data transfers. Expressed in
|
||||||
|
// frames or microframes depending on the device operating speed
|
||||||
|
// (i.e., either 1 millisecond or 125 µs units).
|
||||||
|
// For full-/high-speed isochronous endpoints, this value must be in
|
||||||
|
// the range from 1 to 16. The bInterval value is used as the exponent
|
||||||
|
// for a 2bInterval-1 value; e.g., a bInterval of 4 means a period
|
||||||
|
// of 8 (24-1).
|
||||||
|
// For full-/low-speed interrupt endpoints, the value of this field may
|
||||||
|
// be from 1 to 255.
|
||||||
|
// For high-speed interrupt endpoints, the bInterval value is used as
|
||||||
|
// the exponent for a 2bInterval-1 value; e.g., a bInterval of 4 means
|
||||||
|
// a period of 8 (24-1). This value must be from 1 to 16.
|
||||||
|
// For high-speed bulk/control OUT endpoints, the bInterval must
|
||||||
|
// specify the maximum NAK rate of the endpoint. A value of 0 indicates
|
||||||
|
// the endpoint never NAKs. Other values indicate at most 1 NAK each
|
||||||
|
// bInterval number of microframes. This value must be in the range
|
||||||
|
// from 0 to 255.
|
||||||
|
// - if the device conforms to USB3.x and the device is in SuperSpeed mode:
|
||||||
|
// Interval for servicing the endpoint for data transfers. Expressed in
|
||||||
|
// 125-µs units.
|
||||||
|
// For Enhanced SuperSpeed isochronous and interrupt endpoints, this
|
||||||
|
// value shall be in the range from 1 to 16. However, the valid ranges
|
||||||
|
// are 8 to 16 for Notification type Interrupt endpoints. The bInterval
|
||||||
|
// value is used as the exponent for a 2(bInterval-1) value; e.g., a
|
||||||
|
// bInterval of 4 means a period of 8 (2(4-1) → 23 → 8).
|
||||||
|
// This field is reserved and shall not be used for Enhanced SuperSpeed
|
||||||
|
// bulk or control endpoints.
|
||||||
|
//
|
||||||
|
// Note: in low-speed mode, isochronous transfers are not supported.
|
||||||
|
ei.PollInterval = 0
|
||||||
|
return ei
|
||||||
|
}
|
||||||
|
|
||||||
// libusbIntf is a set of trivial idiomatic Go wrappers around libusb C functions.
|
// libusbIntf is a set of trivial idiomatic Go wrappers around libusb C functions.
|
||||||
// The underlying code is generally not testable or difficult to test,
|
// The underlying code is generally not testable or difficult to test,
|
||||||
@@ -70,7 +138,7 @@ type libusbIntf interface {
|
|||||||
setAlt(*libusbDevHandle, uint8, uint8) error
|
setAlt(*libusbDevHandle, uint8, uint8) error
|
||||||
|
|
||||||
// transfer
|
// transfer
|
||||||
alloc(*libusbDevHandle, uint8, TransferType, time.Duration, int, []byte) (*libusbTransfer, error)
|
alloc(*libusbDevHandle, *EndpointInfo, time.Duration, int, []byte) (*libusbTransfer, error)
|
||||||
cancel(*libusbTransfer) error
|
cancel(*libusbTransfer) error
|
||||||
submit(*libusbTransfer, chan struct{}) error
|
submit(*libusbTransfer, chan struct{}) error
|
||||||
data(*libusbTransfer) (int, TransferStatus)
|
data(*libusbTransfer) (int, TransferStatus)
|
||||||
@@ -184,24 +252,9 @@ func (libusbImpl) getDeviceDesc(d *libusbDevice) (*Descriptor, error) {
|
|||||||
Cap: 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 {
|
for n, end := range ends {
|
||||||
ei := EndpointInfo{
|
// TODO(sebek): pass the device descriptor too.
|
||||||
Address: uint8(end.bEndpointAddress),
|
i.Endpoints[n] = libusbEndpoint(end).endpointInfo(nil)
|
||||||
Attributes: uint8(end.bmAttributes),
|
|
||||||
MaxPacketSize: uint16(end.wMaxPacketSize),
|
|
||||||
PollInterval: uint8(end.bInterval),
|
|
||||||
RefreshRate: uint8(end.bRefresh),
|
|
||||||
SynchAddress: uint8(end.bSynchAddress),
|
|
||||||
}
|
|
||||||
if ei.TransferType() == TransferTypeIsochronous {
|
|
||||||
// bits 0-10 identify the packet size, bits 11-12 are the number of additional transactions per microframe.
|
|
||||||
// Don't use libusb_get_max_iso_packet_size, as it has a bug where it returns the same value
|
|
||||||
// regardless of alternative setting used, where different alternative settings might define different
|
|
||||||
// max packet sizes.
|
|
||||||
// See http://libusb.org/ticket/77 for more background.
|
|
||||||
ei.MaxIsoPacket = uint32(end.wMaxPacketSize) & 0x07ff * (uint32(end.wMaxPacketSize)>>11&3 + 1)
|
|
||||||
}
|
|
||||||
i.Endpoints = append(i.Endpoints, ei)
|
|
||||||
}
|
}
|
||||||
descs = append(descs, i)
|
descs = append(descs, i)
|
||||||
}
|
}
|
||||||
@@ -313,15 +366,15 @@ func (libusbImpl) setAlt(d *libusbDevHandle, iface, setup uint8) error {
|
|||||||
return fromUSBError(C.libusb_set_interface_alt_setting((*C.libusb_device_handle)(d), C.int(iface), C.int(setup)))
|
return fromUSBError(C.libusb_set_interface_alt_setting((*C.libusb_device_handle)(d), C.int(iface), C.int(setup)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (libusbImpl) alloc(d *libusbDevHandle, addr uint8, tt TransferType, timeout time.Duration, isoPackets int, buf []byte) (*libusbTransfer, error) {
|
func (libusbImpl) alloc(d *libusbDevHandle, ep *EndpointInfo, timeout time.Duration, isoPackets int, buf []byte) (*libusbTransfer, error) {
|
||||||
xfer := C.libusb_alloc_transfer(C.int(isoPackets))
|
xfer := C.libusb_alloc_transfer(C.int(isoPackets))
|
||||||
if xfer == nil {
|
if xfer == nil {
|
||||||
return nil, fmt.Errorf("libusb_alloc_transfer(%d) failed", isoPackets)
|
return nil, fmt.Errorf("libusb_alloc_transfer(%d) failed", isoPackets)
|
||||||
}
|
}
|
||||||
xfer.dev_handle = (*C.libusb_device_handle)(d)
|
xfer.dev_handle = (*C.libusb_device_handle)(d)
|
||||||
xfer.endpoint = C.uchar(addr)
|
xfer.endpoint = C.uchar(uint8(ep.Number&EndpointNumMask) | uint8(ep.Direction&EndpointDirectionMask))
|
||||||
xfer.timeout = C.uint(timeout / time.Millisecond)
|
xfer.timeout = C.uint(timeout / time.Millisecond)
|
||||||
xfer._type = C.uchar(tt)
|
xfer._type = C.uchar(ep.TransferType)
|
||||||
xfer.num_iso_packets = C.int(isoPackets)
|
xfer.num_iso_packets = C.int(isoPackets)
|
||||||
xfer.buffer = (*C.uchar)((unsafe.Pointer)(&buf[0]))
|
xfer.buffer = (*C.uchar)((unsafe.Pointer)(&buf[0]))
|
||||||
xfer.length = C.int(len(buf))
|
xfer.length = C.int(len(buf))
|
||||||
|
@@ -112,23 +112,22 @@ func (t *usbTransfer) free() error {
|
|||||||
|
|
||||||
// newUSBTransfer allocates a new transfer structure for communication with a
|
// newUSBTransfer allocates a new transfer structure for communication with a
|
||||||
// given device/endpoint, with buf as the underlying transfer buffer.
|
// given device/endpoint, with buf as the underlying transfer buffer.
|
||||||
func newUSBTransfer(dev *libusbDevHandle, ei EndpointInfo, buf []byte, timeout time.Duration) (*usbTransfer, error) {
|
func newUSBTransfer(dev *libusbDevHandle, ei *EndpointInfo, buf []byte, timeout time.Duration) (*usbTransfer, error) {
|
||||||
var isoPackets int
|
var isoPackets int
|
||||||
tt := ei.TransferType()
|
if ei.TransferType == TransferTypeIsochronous {
|
||||||
if tt == TransferTypeIsochronous {
|
isoPackets = len(buf) / int(ei.MaxPacketSize)
|
||||||
isoPackets = len(buf) / int(ei.MaxIsoPacket)
|
if int(ei.MaxPacketSize)*isoPackets < len(buf) {
|
||||||
if int(ei.MaxIsoPacket)*isoPackets < len(buf) {
|
|
||||||
isoPackets++
|
isoPackets++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
xfer, err := libusb.alloc(dev, ei.Address, tt, timeout, isoPackets, buf)
|
xfer, err := libusb.alloc(dev, ei, timeout, isoPackets, buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if tt == TransferTypeIsochronous {
|
if ei.TransferType == TransferTypeIsochronous {
|
||||||
libusb.setIsoPacketLengths(xfer, ei.MaxIsoPacket)
|
libusb.setIsoPacketLengths(xfer, ei.MaxPacketSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
t := &usbTransfer{
|
t := &usbTransfer{
|
||||||
|
@@ -27,8 +27,7 @@ func TestNewTransfer(t *testing.T) {
|
|||||||
desc string
|
desc string
|
||||||
dir EndpointDirection
|
dir EndpointDirection
|
||||||
tt TransferType
|
tt TransferType
|
||||||
maxPkt uint16
|
maxPkt uint32
|
||||||
maxIso uint32
|
|
||||||
buf int
|
buf int
|
||||||
timeout time.Duration
|
timeout time.Duration
|
||||||
wantIso int
|
wantIso int
|
||||||
@@ -48,18 +47,16 @@ func TestNewTransfer(t *testing.T) {
|
|||||||
desc: "iso out transfer, 3 * 1024B packets",
|
desc: "iso out transfer, 3 * 1024B packets",
|
||||||
dir: EndpointDirectionOut,
|
dir: EndpointDirectionOut,
|
||||||
tt: TransferTypeIsochronous,
|
tt: TransferTypeIsochronous,
|
||||||
maxPkt: 2<<11 + 1024,
|
maxPkt: 3 * 1024,
|
||||||
maxIso: 3 * 1024,
|
|
||||||
buf: 10000,
|
buf: 10000,
|
||||||
wantLength: 10000,
|
wantLength: 10000,
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
xfer, err := newUSBTransfer(nil, EndpointInfo{
|
xfer, err := newUSBTransfer(nil, &EndpointInfo{
|
||||||
Address: uint8(tc.dir) | 0x02,
|
Number: 2,
|
||||||
Attributes: uint8(tc.tt),
|
Direction: tc.dir,
|
||||||
|
TransferType: tc.tt,
|
||||||
MaxPacketSize: tc.maxPkt,
|
MaxPacketSize: tc.maxPkt,
|
||||||
MaxIsoPacket: tc.maxIso,
|
|
||||||
PollInterval: 1,
|
|
||||||
}, make([]byte, tc.buf), tc.timeout)
|
}, make([]byte, tc.buf), tc.timeout)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -80,11 +77,11 @@ func TestTransferProtocol(t *testing.T) {
|
|||||||
xfers := make([]*usbTransfer, 2)
|
xfers := make([]*usbTransfer, 2)
|
||||||
var err error
|
var err error
|
||||||
for i := 0; i < 2; i++ {
|
for i := 0; i < 2; i++ {
|
||||||
xfers[i], err = newUSBTransfer(nil, EndpointInfo{
|
xfers[i], err = newUSBTransfer(nil, &EndpointInfo{
|
||||||
Address: 0x86,
|
Number: 6,
|
||||||
Attributes: uint8(TransferTypeBulk),
|
Direction: EndpointDirectionIn,
|
||||||
|
TransferType: TransferTypeBulk,
|
||||||
MaxPacketSize: 512,
|
MaxPacketSize: 512,
|
||||||
PollInterval: 1,
|
|
||||||
}, make([]byte, 10240), time.Second)
|
}, make([]byte, 10240), time.Second)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("newUSBTransfer: %v", err)
|
t.Fatalf("newUSBTransfer: %v", err)
|
||||||
|
Reference in New Issue
Block a user