diff --git a/.travis.yml b/.travis.yml index a78f196..816a977 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,6 @@ language: go dist: trusty go: - - 1.6 - 1.7 - 1.8 - tip diff --git a/usb/device.go b/usb/device.go index ae5f3b5..86a0a31 100644 --- a/usb/device.go +++ b/usb/device.go @@ -122,9 +122,7 @@ func (d *Device) Close() error { } func (d *Device) OpenEndpoint(conf, iface, setup, epoint uint8) (Endpoint, error) { - end := &endpoint{ - Device: d, - } + end := newEndpoint(d) var setAlternate bool for _, c := range d.Configs { diff --git a/usb/endpoint.go b/usb/endpoint.go index 0a3b174..a33dd15 100644 --- a/usb/endpoint.go +++ b/usb/endpoint.go @@ -17,7 +17,6 @@ package usb import ( "fmt" - "log" "time" ) @@ -28,10 +27,22 @@ type Endpoint interface { Info() EndpointInfo } +type transferIntf interface { + submit() error + wait() (int, error) + free() error +} + type endpoint struct { - *Device + h *deviceHandle + InterfaceSetup EndpointInfo + + readTimeout time.Duration + writeTimeout time.Duration + + newUSBTransfer func([]byte, time.Duration) (transferIntf, error) } func (e *endpoint) Read(buf []byte) (int, error) { @@ -39,7 +50,7 @@ func (e *endpoint) Read(buf []byte) (int, error) { return 0, fmt.Errorf("usb: read: not an IN endpoint") } - return e.transfer(buf, e.ReadTimeout) + return e.transfer(buf, e.readTimeout) } func (e *endpoint) Write(buf []byte) (int, error) { @@ -47,33 +58,44 @@ func (e *endpoint) Write(buf []byte) (int, error) { 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) newLibUSBTransfer(buf []byte, timeout time.Duration) (transferIntf, error) { + return newUSBTransfer(e.h, e.EndpointInfo, buf, timeout) +} + func (e *endpoint) transfer(buf []byte, timeout time.Duration) (int, error) { if len(buf) == 0 { return 0, nil } - tt := e.TransferType() - t, err := newUSBTransfer((*deviceHandle)(e.Device.handle), e.EndpointInfo, buf, timeout) + t, err := e.newUSBTransfer(buf, timeout) if err != nil { return 0, err } defer t.free() if err := t.submit(); err != nil { - log.Printf("bulk: %s failed to submit: %s", tt, err) return 0, err } n, err := t.wait() if err != nil { - log.Printf("bulk: %s failed: %s", tt, err) - return 0, err + return n, err } - return n, err + return n, nil +} + +func newEndpoint(d *Device) *endpoint { + ep := &endpoint{ + h: (*deviceHandle)(d.handle), + readTimeout: d.ReadTimeout, + writeTimeout: d.WriteTimeout, + } + ep.newUSBTransfer = ep.newLibUSBTransfer + return ep } diff --git a/usb/endpoint_info_test.go b/usb/endpoint_info_test.go new file mode 100644 index 0000000..d19a323 --- /dev/null +++ b/usb/endpoint_info_test.go @@ -0,0 +1,46 @@ +// Copyright 2017 the gousb Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package usb + +// IN bulk endpoint +var testBulkInEP = EndpointInfo{ + Address: 0x82, + Attributes: uint8(TRANSFER_TYPE_BULK), + MaxPacketSize: 512, + PollInterval: 1, +} + +var testBulkInSetup = InterfaceSetup{ + Number: 0, + Alternate: 0, + IfClass: uint8(CLASS_VENDOR_SPEC), + Endpoints: []EndpointInfo{testBulkInEP}, +} + +// OUT iso endpoint +var testIsoOutEP = EndpointInfo{ + Address: 0x06, + Attributes: uint8(TRANSFER_TYPE_ISOCHRONOUS), + MaxPacketSize: 3<<11 + 1024, + MaxIsoPacket: 3 * 1024, + PollInterval: 1, +} + +var testIsoOutSetup = InterfaceSetup{ + Number: 0, + Alternate: 0, + IfClass: uint8(CLASS_VENDOR_SPEC), + Endpoints: []EndpointInfo{testIsoOutEP}, +} diff --git a/usb/endpoint_test.go b/usb/endpoint_test.go new file mode 100644 index 0000000..eb05960 --- /dev/null +++ b/usb/endpoint_test.go @@ -0,0 +1,97 @@ +// Copyright 2017 the gousb Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package usb + +import ( + "errors" + "reflect" + "testing" + "time" +) + +type fakeTransfer struct { + buf []byte + ret int + err error +} + +func (t *fakeTransfer) submit() error { return nil } +func (t *fakeTransfer) wait() (int, error) { return t.ret, t.err } +func (t *fakeTransfer) free() error { return nil } + +func TestEndpoint(t *testing.T) { + for _, epCfg := range []struct { + method string + InterfaceSetup + EndpointInfo + }{ + {"Read", testBulkInSetup, testBulkInEP}, + {"Write", testIsoOutSetup, testIsoOutEP}, + } { + t.Run(epCfg.method, func(t *testing.T) { + for _, tc := range []struct { + buf []byte + ret int + err error + want int + wantErr bool + }{ + {buf: nil, ret: 10, want: 0}, + {buf: make([]byte, 128), ret: 60, want: 60}, + {buf: make([]byte, 128), ret: 10, err: errors.New("some error"), want: 10, wantErr: true}, + } { + ep := &endpoint{ + InterfaceSetup: epCfg.InterfaceSetup, + EndpointInfo: epCfg.EndpointInfo, + newUSBTransfer: func(buf []byte, timeout time.Duration) (transferIntf, error) { + return &fakeTransfer{buf: buf, ret: tc.ret, err: tc.err}, nil + }, + } + op, ok := reflect.TypeOf(ep).MethodByName(epCfg.method) + if !ok { + t.Fatalf("method %s not found in endpoint struct", epCfg.method) + } + opv := op.Func.Interface().(func(*endpoint, []byte) (int, error)) + got, err := opv(ep, tc.buf) + if (err != nil) != tc.wantErr { + t.Errorf("bulkInEP.Read(): got err: %v, err != nil is %v, want %v", err, err != nil, tc.wantErr) + continue + } + if got != tc.want { + t.Errorf("bulkInEP.Read(): got %d bytes, want %d", got, tc.want) + } + } + }) + } +} + +func TestEndpointWrongDirection(t *testing.T) { + ep := &endpoint{ + InterfaceSetup: testBulkInSetup, + EndpointInfo: testBulkInEP, + } + _, err := ep.Write([]byte{1, 2, 3}) + if err == nil { + t.Error("bulkInEP.Write(): got nil error, want non-nil") + } + ep = &endpoint{ + InterfaceSetup: testIsoOutSetup, + EndpointInfo: testIsoOutEP, + } + _, err = ep.Read(make([]byte, 64)) + if err == nil { + t.Error("isoOutEP.Read(): got nil error, want non-nil") + } +}