From 2efb1a354f1b427676e9c4ba64e7d69fae3f36b1 Mon Sep 17 00:00:00 2001 From: Sebastian Zagrodzki Date: Sun, 19 Feb 2017 15:35:35 +0100 Subject: [PATCH 1/9] Use newEndpoint() for initialization. Add an indirection for newUSBTransfer. --- usb/device.go | 4 +--- usb/endpoint.go | 21 ++++++++++++++++++++- 2 files changed, 21 insertions(+), 4 deletions(-) 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..ac23ab1 100644 --- a/usb/endpoint.go +++ b/usb/endpoint.go @@ -28,10 +28,17 @@ type Endpoint interface { Info() EndpointInfo } +type transferIntf interface { + submit() error + wait() (int, error) + free() error +} + type endpoint struct { *Device InterfaceSetup EndpointInfo + newUSBTransfer func([]byte, time.Duration) (transferIntf, error) } func (e *endpoint) Read(buf []byte) (int, error) { @@ -53,13 +60,17 @@ func (e *endpoint) Write(buf []byte) (int, error) { func (e *endpoint) Interface() InterfaceSetup { return e.InterfaceSetup } func (e *endpoint) Info() EndpointInfo { return e.EndpointInfo } +func (e *endpoint) newRealUSBTransfer(buf []byte, timeout time.Duration) (transferIntf, error) { + return newUSBTransfer((*deviceHandle)(e.Device.handle), 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 } @@ -77,3 +88,11 @@ func (e *endpoint) transfer(buf []byte, timeout time.Duration) (int, error) { } return n, err } + +func newEndpoint(d *Device) *endpoint { + ep := &endpoint{ + Device: d, + } + ep.newUSBTransfer = ep.newRealUSBTransfer + return ep +} From c7c647e70190f53427e981077fd6c82312af2ee0 Mon Sep 17 00:00:00 2001 From: Sebastian Zagrodzki Date: Sun, 19 Feb 2017 16:04:16 +0100 Subject: [PATCH 2/9] First tests. --- usb/endpoint.go | 13 ++++++++--- usb/endpoint_info_test.go | 30 +++++++++++++++++++++++++ usb/endpoint_test.go | 46 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+), 3 deletions(-) create mode 100644 usb/endpoint_info_test.go create mode 100644 usb/endpoint_test.go diff --git a/usb/endpoint.go b/usb/endpoint.go index ac23ab1..0f5f9e5 100644 --- a/usb/endpoint.go +++ b/usb/endpoint.go @@ -35,9 +35,14 @@ type transferIntf interface { } type endpoint struct { - *Device + h *deviceHandle + InterfaceSetup EndpointInfo + + ReadTimeout time.Duration + WriteTimeout time.Duration + newUSBTransfer func([]byte, time.Duration) (transferIntf, error) } @@ -61,7 +66,7 @@ func (e *endpoint) Interface() InterfaceSetup { return e.InterfaceSetup } func (e *endpoint) Info() EndpointInfo { return e.EndpointInfo } func (e *endpoint) newRealUSBTransfer(buf []byte, timeout time.Duration) (transferIntf, error) { - return newUSBTransfer((*deviceHandle)(e.Device.handle), e.EndpointInfo, buf, timeout) + return newUSBTransfer(e.h, e.EndpointInfo, buf, timeout) } func (e *endpoint) transfer(buf []byte, timeout time.Duration) (int, error) { @@ -91,7 +96,9 @@ func (e *endpoint) transfer(buf []byte, timeout time.Duration) (int, error) { func newEndpoint(d *Device) *endpoint { ep := &endpoint{ - Device: d, + h: (*deviceHandle)(d.handle), + ReadTimeout: d.ReadTimeout, + WriteTimeout: d.WriteTimeout, } ep.newUSBTransfer = ep.newRealUSBTransfer return ep diff --git a/usb/endpoint_info_test.go b/usb/endpoint_info_test.go new file mode 100644 index 0000000..b212f95 --- /dev/null +++ b/usb/endpoint_info_test.go @@ -0,0 +1,30 @@ +// 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}, +} diff --git a/usb/endpoint_test.go b/usb/endpoint_test.go new file mode 100644 index 0000000..be765a7 --- /dev/null +++ b/usb/endpoint_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 + +import ( + "testing" + "time" +) + +type fakeTransfer struct { + buf []byte +} + +func (*fakeTransfer) submit() error { return nil } +func (t *fakeTransfer) wait() (int, error) { return len(t.buf) / 2, nil } +func (*fakeTransfer) free() error { return nil } + +func TestEndpoint(t *testing.T) { + ep := endpoint{ + InterfaceSetup: testBulkInSetup, + EndpointInfo: testBulkInEP, + newUSBTransfer: func(buf []byte, timeout time.Duration) (transferIntf, error) { + return &fakeTransfer{buf}, nil + }, + } + b := make([]byte, 128) + got, err := ep.Read(b) + if err != nil { + t.Fatalf("bulkInEP.Read(): got error: %v, want nil", err) + } + if want := len(b)/2 + 1; got != want { + t.Errorf("bulkInEP.Read(): got %d bytes, want half of the buffer length: %d", got, want) + } +} From 0e465201371a3aafab5e26b4685b08f84213af14 Mon Sep 17 00:00:00 2001 From: Sebastian Zagrodzki Date: Wed, 22 Feb 2017 00:58:50 +0100 Subject: [PATCH 3/9] change test slightly, add test for write on in endpoint --- usb/endpoint_test.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/usb/endpoint_test.go b/usb/endpoint_test.go index be765a7..990c250 100644 --- a/usb/endpoint_test.go +++ b/usb/endpoint_test.go @@ -21,10 +21,12 @@ import ( type fakeTransfer struct { buf []byte + ret int + err error } func (*fakeTransfer) submit() error { return nil } -func (t *fakeTransfer) wait() (int, error) { return len(t.buf) / 2, nil } +func (t *fakeTransfer) wait() (int, error) { return t.ret, t.err } func (*fakeTransfer) free() error { return nil } func TestEndpoint(t *testing.T) { @@ -43,4 +45,8 @@ func TestEndpoint(t *testing.T) { if want := len(b)/2 + 1; got != want { t.Errorf("bulkInEP.Read(): got %d bytes, want half of the buffer length: %d", got, want) } + _, err := ep.Write(b) + if err == nil { + t.Error("bulkInEP.Write(): got nil error, want non-nil") + } } From 34c00f398b6b841c7f6bbac0e90352fe70d57b79 Mon Sep 17 00:00:00 2001 From: Sebastian Zagrodzki Date: Wed, 22 Feb 2017 01:35:13 +0100 Subject: [PATCH 4/9] - more tests, for "out" endpoint. --- usb/endpoint.go | 8 ++--- usb/endpoint_info_test.go | 16 +++++++++ usb/endpoint_test.go | 71 ++++++++++++++++++++++++++++++++------- 3 files changed, 76 insertions(+), 19 deletions(-) diff --git a/usb/endpoint.go b/usb/endpoint.go index 0f5f9e5..044f70b 100644 --- a/usb/endpoint.go +++ b/usb/endpoint.go @@ -17,7 +17,6 @@ package usb import ( "fmt" - "log" "time" ) @@ -74,7 +73,6 @@ func (e *endpoint) transfer(buf []byte, timeout time.Duration) (int, error) { return 0, nil } - tt := e.TransferType() t, err := e.newUSBTransfer(buf, timeout) if err != nil { return 0, err @@ -82,16 +80,14 @@ func (e *endpoint) transfer(buf []byte, timeout time.Duration) (int, error) { 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 { diff --git a/usb/endpoint_info_test.go b/usb/endpoint_info_test.go index b212f95..d19a323 100644 --- a/usb/endpoint_info_test.go +++ b/usb/endpoint_info_test.go @@ -28,3 +28,19 @@ var testBulkInSetup = InterfaceSetup{ 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 index 990c250..459ae22 100644 --- a/usb/endpoint_test.go +++ b/usb/endpoint_test.go @@ -15,6 +15,8 @@ package usb import ( + "errors" + "reflect" "testing" "time" ) @@ -30,23 +32,66 @@ func (t *fakeTransfer) wait() (int, error) { return t.ret, t.err } func (*fakeTransfer) free() error { return nil } func TestEndpoint(t *testing.T) { - ep := endpoint{ + for _, epSetup := range []struct { + method string + InterfaceSetup + EndpointInfo + }{ + {"Read", testBulkInSetup, testBulkInEP}, + {"Write", testIsoOutSetup, testIsoOutEP}, + } { + t.Run(epSetup.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: epSetup.InterfaceSetup, + EndpointInfo: epSetup.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(epSetup.method) + if !ok { + t.Fatalf("method %s not found in endpoint", epSetup.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, - newUSBTransfer: func(buf []byte, timeout time.Duration) (transferIntf, error) { - return &fakeTransfer{buf}, nil - }, } - b := make([]byte, 128) - got, err := ep.Read(b) - if err != nil { - t.Fatalf("bulkInEP.Read(): got error: %v, want nil", err) - } - if want := len(b)/2 + 1; got != want { - t.Errorf("bulkInEP.Read(): got %d bytes, want half of the buffer length: %d", got, want) - } - _, err := ep.Write(b) + _, 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") + } } From 9fdd8c7eb8f1984093c5e97575d03fdf93fb050d Mon Sep 17 00:00:00 2001 From: Sebastian Zagrodzki Date: Wed, 22 Feb 2017 01:36:27 +0100 Subject: [PATCH 5/9] Rename epSetup to epCfg. --- usb/endpoint_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/usb/endpoint_test.go b/usb/endpoint_test.go index 459ae22..4361829 100644 --- a/usb/endpoint_test.go +++ b/usb/endpoint_test.go @@ -32,7 +32,7 @@ func (t *fakeTransfer) wait() (int, error) { return t.ret, t.err } func (*fakeTransfer) free() error { return nil } func TestEndpoint(t *testing.T) { - for _, epSetup := range []struct { + for _, epCfg := range []struct { method string InterfaceSetup EndpointInfo @@ -40,7 +40,7 @@ func TestEndpoint(t *testing.T) { {"Read", testBulkInSetup, testBulkInEP}, {"Write", testIsoOutSetup, testIsoOutEP}, } { - t.Run(epSetup.method, func(t *testing.T) { + t.Run(epCfg.method, func(t *testing.T) { for _, tc := range []struct { buf []byte ret int @@ -53,15 +53,15 @@ func TestEndpoint(t *testing.T) { {buf: make([]byte, 128), ret: 10, err: errors.New("some error"), want: 10, wantErr: true}, } { ep := &endpoint{ - InterfaceSetup: epSetup.InterfaceSetup, - EndpointInfo: epSetup.EndpointInfo, + 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(epSetup.method) + op, ok := reflect.TypeOf(ep).MethodByName(epCfg.method) if !ok { - t.Fatalf("method %s not found in endpoint", epSetup.method) + 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) From 01ec3a240ea56194bbb62cc3276ecca9279b2bca Mon Sep 17 00:00:00 2001 From: Sebastian Zagrodzki Date: Wed, 22 Feb 2017 23:36:28 +0100 Subject: [PATCH 6/9] use named receiver in all methods for nicer indentation. --- usb/endpoint_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/usb/endpoint_test.go b/usb/endpoint_test.go index 4361829..eb05960 100644 --- a/usb/endpoint_test.go +++ b/usb/endpoint_test.go @@ -27,9 +27,9 @@ type fakeTransfer struct { err error } -func (*fakeTransfer) submit() error { return nil } +func (t *fakeTransfer) submit() error { return nil } func (t *fakeTransfer) wait() (int, error) { return t.ret, t.err } -func (*fakeTransfer) free() error { return nil } +func (t *fakeTransfer) free() error { return nil } func TestEndpoint(t *testing.T) { for _, epCfg := range []struct { From 5fd1c334130ad9b3e7e693a1c191b4597e019d7a Mon Sep 17 00:00:00 2001 From: Sebastian Zagrodzki Date: Wed, 22 Feb 2017 23:36:53 +0100 Subject: [PATCH 7/9] We now use subtests, introduced in Go 1.7. Remove 1.6 from compatibility check. --- .travis.yml | 1 - 1 file changed, 1 deletion(-) 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 From 313ba15ee6d3cc85cfa1b28c824873b45a3262b3 Mon Sep 17 00:00:00 2001 From: Sebastian Zagrodzki Date: Wed, 22 Feb 2017 23:51:36 +0100 Subject: [PATCH 8/9] unexport read/write timeout. --- usb/endpoint.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/usb/endpoint.go b/usb/endpoint.go index 044f70b..6a53f9b 100644 --- a/usb/endpoint.go +++ b/usb/endpoint.go @@ -39,8 +39,8 @@ type endpoint struct { InterfaceSetup EndpointInfo - ReadTimeout time.Duration - WriteTimeout time.Duration + readTimeout time.Duration + writeTimeout time.Duration newUSBTransfer func([]byte, time.Duration) (transferIntf, error) } @@ -50,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) { @@ -58,7 +58,7 @@ 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 } @@ -93,8 +93,8 @@ func (e *endpoint) transfer(buf []byte, timeout time.Duration) (int, error) { func newEndpoint(d *Device) *endpoint { ep := &endpoint{ h: (*deviceHandle)(d.handle), - ReadTimeout: d.ReadTimeout, - WriteTimeout: d.WriteTimeout, + readTimeout: d.ReadTimeout, + writeTimeout: d.WriteTimeout, } ep.newUSBTransfer = ep.newRealUSBTransfer return ep From 577447ecbf5ce2adbaf2f01dfd84bff48ea009d4 Mon Sep 17 00:00:00 2001 From: Sebastian Zagrodzki Date: Wed, 22 Feb 2017 23:52:28 +0100 Subject: [PATCH 9/9] s/newRealUSBTransfer/newLibUSBTransfer/ --- usb/endpoint.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/usb/endpoint.go b/usb/endpoint.go index 6a53f9b..a33dd15 100644 --- a/usb/endpoint.go +++ b/usb/endpoint.go @@ -64,7 +64,7 @@ func (e *endpoint) Write(buf []byte) (int, error) { func (e *endpoint) Interface() InterfaceSetup { return e.InterfaceSetup } func (e *endpoint) Info() EndpointInfo { return e.EndpointInfo } -func (e *endpoint) newRealUSBTransfer(buf []byte, timeout time.Duration) (transferIntf, error) { +func (e *endpoint) newLibUSBTransfer(buf []byte, timeout time.Duration) (transferIntf, error) { return newUSBTransfer(e.h, e.EndpointInfo, buf, timeout) } @@ -96,6 +96,6 @@ func newEndpoint(d *Device) *endpoint { readTimeout: d.ReadTimeout, writeTimeout: d.WriteTimeout, } - ep.newUSBTransfer = ep.newRealUSBTransfer + ep.newUSBTransfer = ep.newLibUSBTransfer return ep }