diff --git a/device_test.go b/device_test.go index 54fc252..0ce25d4 100644 --- a/device_test.go +++ b/device_test.go @@ -28,7 +28,7 @@ func TestClaimAndRelease(t *testing.T) { cfgNum = 1 if1Num = 1 alt1Num = 1 - ep1Num = 6 + ep1Addr = 0x86 alt2Num = 0 if2Num = 0 ) @@ -58,12 +58,12 @@ func TestClaimAndRelease(t *testing.T) { t.Fatalf("%s.Interface(%d, %d): %v", cfg, if1Num, alt1Num, err) } defer intf.Close() - got, err := intf.InEndpoint(ep1Num) + got, err := intf.InEndpoint(ep1Addr) 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) { - t.Errorf("%s.InEndpoint(%d): got %+v, want %+v", intf, ep1Num, got, 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, ep1Addr, got, want) } if _, err := cfg.Interface(1, 0); err == nil { diff --git a/endpoint.go b/endpoint.go index 0700286..f7e074f 100644 --- a/endpoint.go +++ b/endpoint.go @@ -21,9 +21,19 @@ import ( "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 // from the descriptor. 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 // address field in the descriptor - address 0x82 means endpoint number 2, // with endpoint direction IN. @@ -46,18 +56,10 @@ type EndpointDesc struct { 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. func (e EndpointDesc) String() string { 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 { case TransferTypeIsochronous: ret = append(ret, fmt.Sprintf("- %s %s", e.IsoSyncType, e.UsageType)) diff --git a/endpoint_test.go b/endpoint_test.go index 3036740..1dfcaf0 100644 --- a/endpoint_test.go +++ b/endpoint_test.go @@ -29,6 +29,7 @@ func TestEndpoint(t *testing.T) { }{ { ei: EndpointDesc{ + Address: 0x82, Number: 2, Direction: EndpointDirectionIn, MaxPacketSize: 512, @@ -42,6 +43,7 @@ func TestEndpoint(t *testing.T) { }, { ei: EndpointDesc{ + Address: 0x06, Number: 6, MaxPacketSize: 3 * 1024, 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 { desc string buf []byte @@ -120,6 +122,7 @@ func TestEndpointInfo(t *testing.T) { }{ { ep: EndpointDesc{ + Address: 0x86, Number: 6, Direction: EndpointDirectionIn, TransferType: TransferTypeBulk, @@ -129,6 +132,7 @@ func TestEndpointInfo(t *testing.T) { }, { ep: EndpointDesc{ + Address: 0x02, Number: 2, Direction: EndpointDirectionOut, TransferType: TransferTypeIsochronous, @@ -140,6 +144,7 @@ func TestEndpointInfo(t *testing.T) { }, { ep: EndpointDesc{ + Address: 0x83, Number: 3, Direction: EndpointDirectionIn, TransferType: TransferTypeInterrupt, @@ -235,3 +240,43 @@ func TestEndpointInOut(t *testing.T) { 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) + } +} diff --git a/fakelibusb_devices.go b/fakelibusb_devices.go index 03aa72e..8469237 100644 --- a/fakelibusb_devices.go +++ b/fakelibusb_devices.go @@ -36,14 +36,16 @@ var fakeDevices = []*DeviceDesc{ Number: 0, Alternate: 0, Class: ClassVendorSpec, - Endpoints: map[int]EndpointDesc{ - 1: { + Endpoints: map[EndpointAddress]EndpointDesc{ + 0x01: { + Address: 0x01, Number: 1, Direction: EndpointDirectionOut, MaxPacketSize: 512, TransferType: TransferTypeBulk, }, - 2: { + 0x82: { + Address: 0x82, Number: 2, Direction: EndpointDirectionIn, MaxPacketSize: 512, @@ -82,15 +84,17 @@ var fakeDevices = []*DeviceDesc{ Number: 1, Alternate: 0, Class: ClassVendorSpec, - Endpoints: map[int]EndpointDesc{ - 5: { + Endpoints: map[EndpointAddress]EndpointDesc{ + 0x05: { + Address: 0x05, Number: 5, Direction: EndpointDirectionOut, MaxPacketSize: 3 * 1024, TransferType: TransferTypeIsochronous, UsageType: IsoUsageTypeData, }, - 6: { + 0x86: { + Address: 0x86, Number: 6, Direction: EndpointDirectionIn, MaxPacketSize: 3 * 1024, @@ -102,14 +106,16 @@ var fakeDevices = []*DeviceDesc{ Number: 1, Alternate: 1, Class: ClassVendorSpec, - Endpoints: map[int]EndpointDesc{ - 5: { + Endpoints: map[EndpointAddress]EndpointDesc{ + 0x05: { + Address: 0x05, Number: 5, Direction: EndpointDirectionOut, MaxPacketSize: 2 * 1024, TransferType: TransferTypeIsochronous, }, - 6: { + 0x86: { + Address: 0x86, Number: 6, Direction: EndpointDirectionIn, MaxPacketSize: 2 * 1024, @@ -120,14 +126,16 @@ var fakeDevices = []*DeviceDesc{ Number: 1, Alternate: 2, Class: ClassVendorSpec, - Endpoints: map[int]EndpointDesc{ - 5: { + Endpoints: map[EndpointAddress]EndpointDesc{ + 0x05: { + Address: 0x05, Number: 5, Direction: EndpointDirectionIn, MaxPacketSize: 1024, TransferType: TransferTypeIsochronous, }, - 6: { + 0x86: { + Address: 0x86, Number: 6, Direction: EndpointDirectionIn, 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, + }, + }, + }}, + }}, + }}, + }, } diff --git a/interface.go b/interface.go index cc156a1..96f1790 100644 --- a/interface.go +++ b/interface.go @@ -51,13 +51,13 @@ type InterfaceSetting struct { Protocol Protocol // Endpoints enumerates the endpoints available on this interface with // this alternate setting. - Endpoints map[int]EndpointDesc + Endpoints map[EndpointAddress]EndpointDesc } func (a InterfaceSetting) sortedEndpointIds() []string { var eps []string 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) return eps @@ -94,11 +94,11 @@ func (i *Interface) Close() { i.config = nil } -func (i *Interface) openEndpoint(epNum int) (*endpoint, error) { +func (i *Interface) openEndpoint(epAddr EndpointAddress) (*endpoint, error) { var ep EndpointDesc - ep, ok := i.Setting.Endpoints[epNum] + ep, ok := i.Setting.Endpoints[epAddr] 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{ InterfaceSetting: i.Setting, @@ -112,13 +112,10 @@ func (i *Interface) InEndpoint(epNum int) (*InEndpoint, error) { if i.config == nil { 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 { return nil, err } - if ep.Desc.Direction != EndpointDirectionIn { - return nil, fmt.Errorf("%s is not an IN endpoint", ep) - } return &InEndpoint{ endpoint: ep, }, nil @@ -129,13 +126,10 @@ func (i *Interface) OutEndpoint(epNum int) (*OutEndpoint, error) { if i.config == nil { 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 { return nil, err } - if ep.Desc.Direction != EndpointDirectionOut { - return nil, fmt.Errorf("%s is not an OUT endpoint", ep) - } return &OutEndpoint{ endpoint: ep, }, nil diff --git a/libusb.go b/libusb.go index a131c11..0a2d6c8 100644 --- a/libusb.go +++ b/libusb.go @@ -40,6 +40,7 @@ type libusbEndpoint C.struct_libusb_endpoint_descriptor func (ep libusbEndpoint) endpointDesc(dev *DeviceDesc) EndpointDesc { ei := EndpointDesc{ + Address: EndpointAddress(ep.bEndpointAddress), Number: int(ep.bEndpointAddress & endpointNumMask), Direction: EndpointDirection((ep.bEndpointAddress & endpointDirectionMask) != 0), TransferType: TransferType(ep.bmAttributes & transferTypeMask), @@ -291,10 +292,10 @@ func (libusbImpl) getDeviceDesc(d *libusbDevice) (*DeviceDesc, error) { Len: 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 { epi := libusbEndpoint(end).endpointDesc(dev) - i.Endpoints[epi.Number] = epi + i.Endpoints[epi.Address] = epi } 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) } 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._type = C.uchar(ep.TransferType) xfer.num_iso_packets = C.int(isoPackets) diff --git a/usb.go b/usb.go index 2093de8..a84b458 100644 --- a/usb.go +++ b/usb.go @@ -64,8 +64,12 @@ Config.Interface(num, altNum). The Interface struct is the representation of the claimed interface with a particular alternate setting. The descriptor of the interface is available through Interface.Setting. -An interface with a particular alternate setting defines up to 15 -endpoints. An endpoint can be considered similar to a UDP/IP port, +An interface with a particular alternate setting defines up to 30 data +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. 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 map includes information about the type of the endpoint: +- endpoint address + - endpoint number - direction: IN (device-to-host) or OUT (host-to-device)