Merge pull request #1 from google/same-endpoint-in-out

Allow the same endpoint number to be reused for IN and OUT endpoints separately
This commit is contained in:
zagrodzki
2017-06-14 11:15:38 +02:00
committed by GitHub
7 changed files with 141 additions and 45 deletions

View File

@@ -28,7 +28,7 @@ func TestClaimAndRelease(t *testing.T) {
cfgNum = 1 cfgNum = 1
if1Num = 1 if1Num = 1
alt1Num = 1 alt1Num = 1
ep1Num = 6 ep1Addr = 0x86
alt2Num = 0 alt2Num = 0
if2Num = 0 if2Num = 0
) )
@@ -58,12 +58,12 @@ func TestClaimAndRelease(t *testing.T) {
t.Fatalf("%s.Interface(%d, %d): %v", cfg, if1Num, alt1Num, err) t.Fatalf("%s.Interface(%d, %d): %v", cfg, if1Num, alt1Num, err)
} }
defer intf.Close() defer intf.Close()
got, err := intf.InEndpoint(ep1Num) got, err := intf.InEndpoint(ep1Addr)
if err != nil { if err != nil {
t.Fatalf("%s.InEndpoint(%d): got error %v, want nil", intf, ep1Num, err) t.Fatalf("%s.InEndpoint(%d): got error %v, want nil", intf, ep1Addr, err)
} }
if want := fakeDevices[devIdx].Configs[cfgNum].Interfaces[if1Num].AltSettings[alt1Num].Endpoints[ep1Num]; !reflect.DeepEqual(got.Desc, want) { if want := fakeDevices[devIdx].Configs[cfgNum].Interfaces[if1Num].AltSettings[alt1Num].Endpoints[ep1Addr]; !reflect.DeepEqual(got.Desc, want) {
t.Errorf("%s.InEndpoint(%d): got %+v, want %+v", intf, ep1Num, got, want) t.Errorf("%s.InEndpoint(%d): got %+v, want %+v", intf, ep1Addr, got, want)
} }
if _, err := cfg.Interface(1, 0); err == nil { if _, err := cfg.Interface(1, 0); err == nil {

View File

@@ -21,9 +21,19 @@ import (
"time" "time"
) )
// EndpointAddress is a unique identifier for the endpoint, combining the endpoint number and direction.
type EndpointAddress uint8
// String implements the Stringer interface.
func (a EndpointAddress) String() string {
return fmt.Sprintf("0x%02x", uint8(a))
}
// EndpointDesc contains the information about an interface endpoint, extracted // EndpointDesc contains the information about an interface endpoint, extracted
// from the descriptor. // from the descriptor.
type EndpointDesc struct { type EndpointDesc struct {
// Address is the unique identifier of the endpoint within the interface.
Address EndpointAddress
// Number represents the endpoint number. Note that the endpoint number is different from the // Number represents the endpoint number. Note that the endpoint number is different from the
// address field in the descriptor - address 0x82 means endpoint number 2, // address field in the descriptor - address 0x82 means endpoint number 2,
// with endpoint direction IN. // with endpoint direction IN.
@@ -46,18 +56,10 @@ type EndpointDesc struct {
UsageType UsageType UsageType UsageType
} }
func endpointAddr(n int, d EndpointDirection) int {
addr := n
if d == EndpointDirectionIn {
addr |= 0x80
}
return addr
}
// String returns the human-readable description of the endpoint. // String returns the human-readable description of the endpoint.
func (e EndpointDesc) String() string { func (e EndpointDesc) String() string {
ret := make([]string, 0, 3) ret := make([]string, 0, 3)
ret = append(ret, fmt.Sprintf("ep #%d %s (address 0x%02x) %s", e.Number, e.Direction, endpointAddr(e.Number, e.Direction), e.TransferType)) ret = append(ret, fmt.Sprintf("ep #%d %s (address %s) %s", e.Number, e.Direction, e.Address, e.TransferType))
switch e.TransferType { switch e.TransferType {
case TransferTypeIsochronous: case TransferTypeIsochronous:
ret = append(ret, fmt.Sprintf("- %s %s", e.IsoSyncType, e.UsageType)) ret = append(ret, fmt.Sprintf("- %s %s", e.IsoSyncType, e.UsageType))

View File

@@ -29,6 +29,7 @@ func TestEndpoint(t *testing.T) {
}{ }{
{ {
ei: EndpointDesc{ ei: EndpointDesc{
Address: 0x82,
Number: 2, Number: 2,
Direction: EndpointDirectionIn, Direction: EndpointDirectionIn,
MaxPacketSize: 512, MaxPacketSize: 512,
@@ -42,6 +43,7 @@ func TestEndpoint(t *testing.T) {
}, },
{ {
ei: EndpointDesc{ ei: EndpointDesc{
Address: 0x06,
Number: 6, Number: 6,
MaxPacketSize: 3 * 1024, MaxPacketSize: 3 * 1024,
TransferType: TransferTypeIsochronous, TransferType: TransferTypeIsochronous,
@@ -55,7 +57,7 @@ func TestEndpoint(t *testing.T) {
}, },
}, },
} { } {
epData.intf.Endpoints = map[int]EndpointDesc{epData.ei.Number: epData.ei} epData.intf.Endpoints = map[EndpointAddress]EndpointDesc{epData.ei.Address: epData.ei}
for _, tc := range []struct { for _, tc := range []struct {
desc string desc string
buf []byte buf []byte
@@ -120,6 +122,7 @@ func TestEndpointInfo(t *testing.T) {
}{ }{
{ {
ep: EndpointDesc{ ep: EndpointDesc{
Address: 0x86,
Number: 6, Number: 6,
Direction: EndpointDirectionIn, Direction: EndpointDirectionIn,
TransferType: TransferTypeBulk, TransferType: TransferTypeBulk,
@@ -129,6 +132,7 @@ func TestEndpointInfo(t *testing.T) {
}, },
{ {
ep: EndpointDesc{ ep: EndpointDesc{
Address: 0x02,
Number: 2, Number: 2,
Direction: EndpointDirectionOut, Direction: EndpointDirectionOut,
TransferType: TransferTypeIsochronous, TransferType: TransferTypeIsochronous,
@@ -140,6 +144,7 @@ func TestEndpointInfo(t *testing.T) {
}, },
{ {
ep: EndpointDesc{ ep: EndpointDesc{
Address: 0x83,
Number: 3, Number: 3,
Direction: EndpointDirectionIn, Direction: EndpointDirectionIn,
TransferType: TransferTypeInterrupt, TransferType: TransferTypeInterrupt,
@@ -235,3 +240,43 @@ func TestEndpointInOut(t *testing.T) {
t.Errorf("%s.OutEndpoint(2): got nil, want error", intf) t.Errorf("%s.OutEndpoint(2): got nil, want error", intf)
} }
} }
func TestSameEndpointNumberInOut(t *testing.T) {
defer func(i libusbIntf) { libusb = i }(libusb)
_, done := newFakeLibusb()
defer done()
ctx := NewContext()
defer ctx.Close()
d, err := ctx.OpenDeviceWithVIDPID(0x1111, 0x1111)
if err != nil {
t.Fatalf("OpenDeviceWithVIDPID(0x1111, 0x1111): got error %v, want nil", err)
}
defer func() {
if err := d.Close(); err != nil {
t.Errorf("%s.Close(): %v", d, err)
}
}()
cfg, err := d.Config(1)
if err != nil {
t.Fatalf("%s.Config(1): %v", d, err)
}
defer func() {
if err := cfg.Close(); err != nil {
t.Errorf("%s.Close(): %v", cfg, err)
}
}()
intf, err := cfg.Interface(0, 0)
if err != nil {
t.Fatalf("%s.Interface(0, 0): %v", cfg, err)
}
defer intf.Close()
if _, err := intf.InEndpoint(1); err != nil {
t.Errorf("%s.InEndpoint(1): got error %v, want nil", intf, err)
}
if _, err := intf.OutEndpoint(1); err != nil {
t.Errorf("%s.OutEndpoint(1): got error %v, want nil", intf, err)
}
}

View File

@@ -36,14 +36,16 @@ var fakeDevices = []*DeviceDesc{
Number: 0, Number: 0,
Alternate: 0, Alternate: 0,
Class: ClassVendorSpec, Class: ClassVendorSpec,
Endpoints: map[int]EndpointDesc{ Endpoints: map[EndpointAddress]EndpointDesc{
1: { 0x01: {
Address: 0x01,
Number: 1, Number: 1,
Direction: EndpointDirectionOut, Direction: EndpointDirectionOut,
MaxPacketSize: 512, MaxPacketSize: 512,
TransferType: TransferTypeBulk, TransferType: TransferTypeBulk,
}, },
2: { 0x82: {
Address: 0x82,
Number: 2, Number: 2,
Direction: EndpointDirectionIn, Direction: EndpointDirectionIn,
MaxPacketSize: 512, MaxPacketSize: 512,
@@ -82,15 +84,17 @@ var fakeDevices = []*DeviceDesc{
Number: 1, Number: 1,
Alternate: 0, Alternate: 0,
Class: ClassVendorSpec, Class: ClassVendorSpec,
Endpoints: map[int]EndpointDesc{ Endpoints: map[EndpointAddress]EndpointDesc{
5: { 0x05: {
Address: 0x05,
Number: 5, Number: 5,
Direction: EndpointDirectionOut, Direction: EndpointDirectionOut,
MaxPacketSize: 3 * 1024, MaxPacketSize: 3 * 1024,
TransferType: TransferTypeIsochronous, TransferType: TransferTypeIsochronous,
UsageType: IsoUsageTypeData, UsageType: IsoUsageTypeData,
}, },
6: { 0x86: {
Address: 0x86,
Number: 6, Number: 6,
Direction: EndpointDirectionIn, Direction: EndpointDirectionIn,
MaxPacketSize: 3 * 1024, MaxPacketSize: 3 * 1024,
@@ -102,14 +106,16 @@ var fakeDevices = []*DeviceDesc{
Number: 1, Number: 1,
Alternate: 1, Alternate: 1,
Class: ClassVendorSpec, Class: ClassVendorSpec,
Endpoints: map[int]EndpointDesc{ Endpoints: map[EndpointAddress]EndpointDesc{
5: { 0x05: {
Address: 0x05,
Number: 5, Number: 5,
Direction: EndpointDirectionOut, Direction: EndpointDirectionOut,
MaxPacketSize: 2 * 1024, MaxPacketSize: 2 * 1024,
TransferType: TransferTypeIsochronous, TransferType: TransferTypeIsochronous,
}, },
6: { 0x86: {
Address: 0x86,
Number: 6, Number: 6,
Direction: EndpointDirectionIn, Direction: EndpointDirectionIn,
MaxPacketSize: 2 * 1024, MaxPacketSize: 2 * 1024,
@@ -120,14 +126,16 @@ var fakeDevices = []*DeviceDesc{
Number: 1, Number: 1,
Alternate: 2, Alternate: 2,
Class: ClassVendorSpec, Class: ClassVendorSpec,
Endpoints: map[int]EndpointDesc{ Endpoints: map[EndpointAddress]EndpointDesc{
5: { 0x05: {
Address: 0x05,
Number: 5, Number: 5,
Direction: EndpointDirectionIn, Direction: EndpointDirectionIn,
MaxPacketSize: 1024, MaxPacketSize: 1024,
TransferType: TransferTypeIsochronous, TransferType: TransferTypeIsochronous,
}, },
6: { 0x86: {
Address: 0x86,
Number: 6, Number: 6,
Direction: EndpointDirectionIn, Direction: EndpointDirectionIn,
MaxPacketSize: 1024, MaxPacketSize: 1024,
@@ -138,4 +146,44 @@ var fakeDevices = []*DeviceDesc{
}}, }},
}}, }},
}, },
// Bus 001 Device 003: ID 9999:0002
// One config, one interface, one setup,
// two endpoints: 0x01 OUT, 0x81 IN.
&DeviceDesc{
Bus: 1,
Address: 3,
Spec: Version(2, 0),
Device: Version(1, 0),
Vendor: ID(0x1111),
Product: ID(0x1111),
Protocol: 255,
Configs: map[int]ConfigDesc{1: {
Number: 1,
MaxPower: Milliamperes(100),
Interfaces: []InterfaceDesc{{
Number: 0,
AltSettings: []InterfaceSetting{{
Number: 0,
Alternate: 0,
Class: ClassVendorSpec,
Endpoints: map[EndpointAddress]EndpointDesc{
0x01: {
Address: 0x01,
Number: 1,
Direction: EndpointDirectionOut,
MaxPacketSize: 512,
TransferType: TransferTypeBulk,
},
0x81: {
Address: 0x81,
Number: 1,
Direction: EndpointDirectionIn,
MaxPacketSize: 512,
TransferType: TransferTypeBulk,
},
},
}},
}},
}},
},
} }

View File

@@ -51,13 +51,13 @@ type InterfaceSetting struct {
Protocol Protocol Protocol Protocol
// Endpoints enumerates the endpoints available on this interface with // Endpoints enumerates the endpoints available on this interface with
// this alternate setting. // this alternate setting.
Endpoints map[int]EndpointDesc Endpoints map[EndpointAddress]EndpointDesc
} }
func (a InterfaceSetting) sortedEndpointIds() []string { func (a InterfaceSetting) sortedEndpointIds() []string {
var eps []string var eps []string
for _, ei := range a.Endpoints { for _, ei := range a.Endpoints {
eps = append(eps, fmt.Sprintf("%d(%s)", ei.Number, ei.Direction)) eps = append(eps, fmt.Sprintf("%s(%d,%s)", ei.Address, ei.Number, ei.Direction))
} }
sort.Strings(eps) sort.Strings(eps)
return eps return eps
@@ -94,11 +94,11 @@ func (i *Interface) Close() {
i.config = nil i.config = nil
} }
func (i *Interface) openEndpoint(epNum int) (*endpoint, error) { func (i *Interface) openEndpoint(epAddr EndpointAddress) (*endpoint, error) {
var ep EndpointDesc var ep EndpointDesc
ep, ok := i.Setting.Endpoints[epNum] ep, ok := i.Setting.Endpoints[epAddr]
if !ok { if !ok {
return nil, fmt.Errorf("%s does not have endpoint number %d. Available endpoints: %v", i, epNum, i.Setting.sortedEndpointIds()) return nil, fmt.Errorf("%s does not have endpoint with address %s. Available endpoints: %v", i, epAddr, i.Setting.sortedEndpointIds())
} }
return &endpoint{ return &endpoint{
InterfaceSetting: i.Setting, InterfaceSetting: i.Setting,
@@ -112,13 +112,10 @@ func (i *Interface) InEndpoint(epNum int) (*InEndpoint, error) {
if i.config == nil { if i.config == nil {
return nil, fmt.Errorf("InEndpoint(%d) called on %s after Close", epNum, i) return nil, fmt.Errorf("InEndpoint(%d) called on %s after Close", epNum, i)
} }
ep, err := i.openEndpoint(epNum) ep, err := i.openEndpoint(EndpointAddress(0x80 | epNum))
if err != nil { if err != nil {
return nil, err return nil, err
} }
if ep.Desc.Direction != EndpointDirectionIn {
return nil, fmt.Errorf("%s is not an IN endpoint", ep)
}
return &InEndpoint{ return &InEndpoint{
endpoint: ep, endpoint: ep,
}, nil }, nil
@@ -129,13 +126,10 @@ func (i *Interface) OutEndpoint(epNum int) (*OutEndpoint, error) {
if i.config == nil { if i.config == nil {
return nil, fmt.Errorf("OutEndpoint(%d) called on %s after Close", epNum, i) return nil, fmt.Errorf("OutEndpoint(%d) called on %s after Close", epNum, i)
} }
ep, err := i.openEndpoint(epNum) ep, err := i.openEndpoint(EndpointAddress(epNum))
if err != nil { if err != nil {
return nil, err return nil, err
} }
if ep.Desc.Direction != EndpointDirectionOut {
return nil, fmt.Errorf("%s is not an OUT endpoint", ep)
}
return &OutEndpoint{ return &OutEndpoint{
endpoint: ep, endpoint: ep,
}, nil }, nil

View File

@@ -40,6 +40,7 @@ type libusbEndpoint C.struct_libusb_endpoint_descriptor
func (ep libusbEndpoint) endpointDesc(dev *DeviceDesc) EndpointDesc { func (ep libusbEndpoint) endpointDesc(dev *DeviceDesc) EndpointDesc {
ei := EndpointDesc{ ei := EndpointDesc{
Address: EndpointAddress(ep.bEndpointAddress),
Number: int(ep.bEndpointAddress & endpointNumMask), Number: int(ep.bEndpointAddress & endpointNumMask),
Direction: EndpointDirection((ep.bEndpointAddress & endpointDirectionMask) != 0), Direction: EndpointDirection((ep.bEndpointAddress & endpointDirectionMask) != 0),
TransferType: TransferType(ep.bmAttributes & transferTypeMask), TransferType: TransferType(ep.bmAttributes & transferTypeMask),
@@ -291,10 +292,10 @@ func (libusbImpl) getDeviceDesc(d *libusbDevice) (*DeviceDesc, error) {
Len: int(alt.bNumEndpoints), Len: int(alt.bNumEndpoints),
Cap: int(alt.bNumEndpoints), Cap: int(alt.bNumEndpoints),
} }
i.Endpoints = make(map[int]EndpointDesc, len(ends)) i.Endpoints = make(map[EndpointAddress]EndpointDesc, len(ends))
for _, end := range ends { for _, end := range ends {
epi := libusbEndpoint(end).endpointDesc(dev) epi := libusbEndpoint(end).endpointDesc(dev)
i.Endpoints[epi.Number] = epi i.Endpoints[epi.Address] = epi
} }
descs = append(descs, i) descs = append(descs, i)
} }
@@ -405,7 +406,7 @@ func (libusbImpl) alloc(d *libusbDevHandle, ep *EndpointDesc, timeout time.Durat
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(endpointAddr(ep.Number, ep.Direction)) xfer.endpoint = C.uchar(ep.Address)
xfer.timeout = C.uint(timeout / time.Millisecond) xfer.timeout = C.uint(timeout / time.Millisecond)
xfer._type = C.uchar(ep.TransferType) xfer._type = C.uchar(ep.TransferType)
xfer.num_iso_packets = C.int(isoPackets) xfer.num_iso_packets = C.int(isoPackets)

10
usb.go
View File

@@ -64,8 +64,12 @@ Config.Interface(num, altNum). The Interface struct is the representation
of the claimed interface with a particular alternate setting. of the claimed interface with a particular alternate setting.
The descriptor of the interface is available through Interface.Setting. The descriptor of the interface is available through Interface.Setting.
An interface with a particular alternate setting defines up to 15 An interface with a particular alternate setting defines up to 30 data
endpoints. An endpoint can be considered similar to a UDP/IP port, endpoints, each identified by a unique address. The endpoint address is a combination
of endpoint number (1..15) and endpoint directionality (IN/OUT).
IN endpoints have addresses 0x81..0x8f, while OUT endpoints 0x01..0x0f.
An endpoint can be considered similar to a UDP/IP port,
except the data transfers are unidirectional. except the data transfers are unidirectional.
Endpoints are represented by the Endpoint struct, and all defined endpoints Endpoints are represented by the Endpoint struct, and all defined endpoints
@@ -74,6 +78,8 @@ can be obtained through the Endpoints field of the Interface.Setting.
Each endpoint descriptor (EndpointDesc) defined in the interface's endpoint Each endpoint descriptor (EndpointDesc) defined in the interface's endpoint
map includes information about the type of the endpoint: map includes information about the type of the endpoint:
- endpoint address
- endpoint number - endpoint number
- direction: IN (device-to-host) or OUT (host-to-device) - direction: IN (device-to-host) or OUT (host-to-device)