From 500caaeda74dd9c660279036293f4b2997cf0b03 Mon Sep 17 00:00:00 2001 From: Dimitri Sokolyuk Date: Sat, 9 Sep 2017 09:42:37 +0200 Subject: Add vendor --- vendor/github.com/kylelemons/gousb/usb/config.go | 84 +++++ .../github.com/kylelemons/gousb/usb/config_test.go | 32 ++ .../github.com/kylelemons/gousb/usb/constants.go | 184 +++++++++++ vendor/github.com/kylelemons/gousb/usb/debug.go | 37 +++ .../github.com/kylelemons/gousb/usb/descriptor.go | 38 +++ vendor/github.com/kylelemons/gousb/usb/device.go | 196 +++++++++++ vendor/github.com/kylelemons/gousb/usb/endpoint.go | 89 +++++ .../kylelemons/gousb/usb/endpoint_info_test.go | 46 +++ .../kylelemons/gousb/usb/endpoint_test.go | 134 ++++++++ vendor/github.com/kylelemons/gousb/usb/error.go | 101 ++++++ .../kylelemons/gousb/usb/fakelibusb_test.go | 312 +++++++++++++++++ vendor/github.com/kylelemons/gousb/usb/libusb.go | 368 +++++++++++++++++++++ .../gousb/usb/libusb_cgo_benchmark_test.go | 46 +++ vendor/github.com/kylelemons/gousb/usb/misc.go | 48 +++ .../github.com/kylelemons/gousb/usb/misc_test.go | 39 +++ vendor/github.com/kylelemons/gousb/usb/transfer.c | 80 +++++ vendor/github.com/kylelemons/gousb/usb/transfer.go | 147 ++++++++ .../kylelemons/gousb/usb/transfer_test.go | 145 ++++++++ vendor/github.com/kylelemons/gousb/usb/usb.go | 110 ++++++ vendor/github.com/kylelemons/gousb/usb/usb_test.go | 87 +++++ 20 files changed, 2323 insertions(+) create mode 100644 vendor/github.com/kylelemons/gousb/usb/config.go create mode 100644 vendor/github.com/kylelemons/gousb/usb/config_test.go create mode 100644 vendor/github.com/kylelemons/gousb/usb/constants.go create mode 100644 vendor/github.com/kylelemons/gousb/usb/debug.go create mode 100644 vendor/github.com/kylelemons/gousb/usb/descriptor.go create mode 100644 vendor/github.com/kylelemons/gousb/usb/device.go create mode 100644 vendor/github.com/kylelemons/gousb/usb/endpoint.go create mode 100644 vendor/github.com/kylelemons/gousb/usb/endpoint_info_test.go create mode 100644 vendor/github.com/kylelemons/gousb/usb/endpoint_test.go create mode 100644 vendor/github.com/kylelemons/gousb/usb/error.go create mode 100644 vendor/github.com/kylelemons/gousb/usb/fakelibusb_test.go create mode 100644 vendor/github.com/kylelemons/gousb/usb/libusb.go create mode 100644 vendor/github.com/kylelemons/gousb/usb/libusb_cgo_benchmark_test.go create mode 100644 vendor/github.com/kylelemons/gousb/usb/misc.go create mode 100644 vendor/github.com/kylelemons/gousb/usb/misc_test.go create mode 100644 vendor/github.com/kylelemons/gousb/usb/transfer.c create mode 100644 vendor/github.com/kylelemons/gousb/usb/transfer.go create mode 100644 vendor/github.com/kylelemons/gousb/usb/transfer_test.go create mode 100644 vendor/github.com/kylelemons/gousb/usb/usb.go create mode 100644 vendor/github.com/kylelemons/gousb/usb/usb_test.go (limited to 'vendor/github.com/kylelemons/gousb/usb') diff --git a/vendor/github.com/kylelemons/gousb/usb/config.go b/vendor/github.com/kylelemons/gousb/usb/config.go new file mode 100644 index 0000000..f967595 --- /dev/null +++ b/vendor/github.com/kylelemons/gousb/usb/config.go @@ -0,0 +1,84 @@ +// Copyright 2013 Google Inc. All rights reserved. +// Copyright 2016 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 ( + "fmt" +) + +type EndpointInfo struct { + Address uint8 + Attributes uint8 + MaxPacketSize uint16 + MaxIsoPacket uint32 + PollInterval uint8 + RefreshRate uint8 + SynchAddress uint8 +} + +func (e EndpointInfo) Number() int { + return int(e.Address) & ENDPOINT_NUM_MASK +} + +func (e EndpointInfo) TransferType() TransferType { + return TransferType(e.Attributes) & TRANSFER_TYPE_MASK +} + +func (e EndpointInfo) Direction() EndpointDirection { + return EndpointDirection(e.Address) & ENDPOINT_DIR_MASK +} + +func (e EndpointInfo) String() string { + return fmt.Sprintf("Endpoint #%d %-3s %s - %s %s [%d %d]", + e.Number(), e.Direction(), e.TransferType(), + IsoSyncType(e.Attributes)&ISO_SYNC_TYPE_MASK, + IsoUsageType(e.Attributes)&ISO_USAGE_TYPE_MASK, + e.MaxPacketSize, e.MaxIsoPacket, + ) +} + +type InterfaceInfo struct { + Number uint8 + Setups []InterfaceSetup +} + +func (i InterfaceInfo) String() string { + return fmt.Sprintf("Interface %02x (%d setups)", i.Number, len(i.Setups)) +} + +type InterfaceSetup struct { + Number uint8 + Alternate uint8 + IfClass uint8 + IfSubClass uint8 + IfProtocol uint8 + Endpoints []EndpointInfo +} + +func (a InterfaceSetup) String() string { + return fmt.Sprintf("Interface %02x Setup %02x", a.Number, a.Alternate) +} + +type ConfigInfo struct { + Config uint8 + Attributes uint8 + MaxPower uint8 + Interfaces []InterfaceInfo +} + +func (c ConfigInfo) String() string { + return fmt.Sprintf("Config %02x", c.Config) +} diff --git a/vendor/github.com/kylelemons/gousb/usb/config_test.go b/vendor/github.com/kylelemons/gousb/usb/config_test.go new file mode 100644 index 0000000..f4cbbcf --- /dev/null +++ b/vendor/github.com/kylelemons/gousb/usb/config_test.go @@ -0,0 +1,32 @@ +package usb + +import "testing" + +func TestEndpointInfo(t *testing.T) { + for _, tc := range []struct { + ep EndpointInfo + want string + }{ + { + ep: EndpointInfo{ + Address: 0x86, + Attributes: 0x02, + MaxPacketSize: 512, + }, + want: "Endpoint #6 IN bulk - unsynchronized data [512 0]", + }, + { + ep: EndpointInfo{ + Address: 0x02, + Attributes: 0x05, + MaxPacketSize: 512, + MaxIsoPacket: 512, + }, + want: "Endpoint #2 OUT isochronous - asynchronous data [512 512]", + }, + } { + if got := tc.ep.String(); got != tc.want { + t.Errorf("%#v.String(): got %q, want %q", tc.ep, got, tc.want) + } + } +} diff --git a/vendor/github.com/kylelemons/gousb/usb/constants.go b/vendor/github.com/kylelemons/gousb/usb/constants.go new file mode 100644 index 0000000..67c41ce --- /dev/null +++ b/vendor/github.com/kylelemons/gousb/usb/constants.go @@ -0,0 +1,184 @@ +// Copyright 2013 Google Inc. All rights reserved. +// Copyright 2016 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 + +// #include +import "C" + +type Class uint8 + +const ( + CLASS_PER_INTERFACE Class = C.LIBUSB_CLASS_PER_INTERFACE + CLASS_AUDIO Class = C.LIBUSB_CLASS_AUDIO + CLASS_COMM Class = C.LIBUSB_CLASS_COMM + CLASS_HID Class = C.LIBUSB_CLASS_HID + CLASS_PRINTER Class = C.LIBUSB_CLASS_PRINTER + CLASS_PTP Class = C.LIBUSB_CLASS_PTP + CLASS_MASS_STORAGE Class = C.LIBUSB_CLASS_MASS_STORAGE + CLASS_HUB Class = C.LIBUSB_CLASS_HUB + CLASS_DATA Class = C.LIBUSB_CLASS_DATA + CLASS_WIRELESS Class = C.LIBUSB_CLASS_WIRELESS + CLASS_APPLICATION Class = C.LIBUSB_CLASS_APPLICATION + CLASS_VENDOR_SPEC Class = C.LIBUSB_CLASS_VENDOR_SPEC +) + +var classDescription = map[Class]string{ + CLASS_PER_INTERFACE: "per-interface", + CLASS_AUDIO: "audio", + CLASS_COMM: "communications", + CLASS_HID: "human interface device", + CLASS_PRINTER: "printer dclass", + CLASS_PTP: "picture transfer protocol", + CLASS_MASS_STORAGE: "mass storage", + CLASS_HUB: "hub", + CLASS_DATA: "data", + CLASS_WIRELESS: "wireless", + CLASS_APPLICATION: "application", + CLASS_VENDOR_SPEC: "vendor-specific", +} + +func (c Class) String() string { + return classDescription[c] +} + +type DescriptorType uint8 + +const ( + DT_DEVICE DescriptorType = C.LIBUSB_DT_DEVICE + DT_CONFIG DescriptorType = C.LIBUSB_DT_CONFIG + DT_STRING DescriptorType = C.LIBUSB_DT_STRING + DT_INTERFACE DescriptorType = C.LIBUSB_DT_INTERFACE + DT_ENDPOINT DescriptorType = C.LIBUSB_DT_ENDPOINT + DT_HID DescriptorType = C.LIBUSB_DT_HID + DT_REPORT DescriptorType = C.LIBUSB_DT_REPORT + DT_PHYSICAL DescriptorType = C.LIBUSB_DT_PHYSICAL + DT_HUB DescriptorType = C.LIBUSB_DT_HUB +) + +var descriptorTypeDescription = map[DescriptorType]string{ + DT_DEVICE: "device", + DT_CONFIG: "configuration", + DT_STRING: "string", + DT_INTERFACE: "interface", + DT_ENDPOINT: "endpoint", + DT_HID: "HID", + DT_REPORT: "HID report", + DT_PHYSICAL: "physical", + DT_HUB: "hub", +} + +func (dt DescriptorType) String() string { + return descriptorTypeDescription[dt] +} + +type EndpointDirection uint8 + +const ( + ENDPOINT_NUM_MASK = 0x0f + ENDPOINT_DIR_IN EndpointDirection = C.LIBUSB_ENDPOINT_IN + ENDPOINT_DIR_OUT EndpointDirection = C.LIBUSB_ENDPOINT_OUT + ENDPOINT_DIR_MASK EndpointDirection = 0x80 +) + +var endpointDirectionDescription = map[EndpointDirection]string{ + ENDPOINT_DIR_IN: "IN", + ENDPOINT_DIR_OUT: "OUT", +} + +func (ed EndpointDirection) String() string { + return endpointDirectionDescription[ed] +} + +type TransferType uint8 + +const ( + TRANSFER_TYPE_CONTROL TransferType = C.LIBUSB_TRANSFER_TYPE_CONTROL + TRANSFER_TYPE_ISOCHRONOUS TransferType = C.LIBUSB_TRANSFER_TYPE_ISOCHRONOUS + TRANSFER_TYPE_BULK TransferType = C.LIBUSB_TRANSFER_TYPE_BULK + TRANSFER_TYPE_INTERRUPT TransferType = C.LIBUSB_TRANSFER_TYPE_INTERRUPT + TRANSFER_TYPE_MASK TransferType = 0x03 +) + +var transferTypeDescription = map[TransferType]string{ + TRANSFER_TYPE_CONTROL: "control", + TRANSFER_TYPE_ISOCHRONOUS: "isochronous", + TRANSFER_TYPE_BULK: "bulk", + TRANSFER_TYPE_INTERRUPT: "interrupt", +} + +func (tt TransferType) String() string { + return transferTypeDescription[tt] +} + +type IsoSyncType uint8 + +const ( + ISO_SYNC_TYPE_NONE IsoSyncType = C.LIBUSB_ISO_SYNC_TYPE_NONE << 2 + ISO_SYNC_TYPE_ASYNC IsoSyncType = C.LIBUSB_ISO_SYNC_TYPE_ASYNC << 2 + ISO_SYNC_TYPE_ADAPTIVE IsoSyncType = C.LIBUSB_ISO_SYNC_TYPE_ADAPTIVE << 2 + ISO_SYNC_TYPE_SYNC IsoSyncType = C.LIBUSB_ISO_SYNC_TYPE_SYNC << 2 + ISO_SYNC_TYPE_MASK IsoSyncType = 0x0C +) + +var isoSyncTypeDescription = map[IsoSyncType]string{ + ISO_SYNC_TYPE_NONE: "unsynchronized", + ISO_SYNC_TYPE_ASYNC: "asynchronous", + ISO_SYNC_TYPE_ADAPTIVE: "adaptive", + ISO_SYNC_TYPE_SYNC: "synchronous", +} + +func (ist IsoSyncType) String() string { + return isoSyncTypeDescription[ist] +} + +type IsoUsageType uint8 + +const ( + ISO_USAGE_TYPE_DATA IsoUsageType = C.LIBUSB_ISO_USAGE_TYPE_DATA << 4 + ISO_USAGE_TYPE_FEEDBACK IsoUsageType = C.LIBUSB_ISO_USAGE_TYPE_FEEDBACK << 4 + ISO_USAGE_TYPE_IMPLICIT IsoUsageType = C.LIBUSB_ISO_USAGE_TYPE_IMPLICIT << 4 + ISO_USAGE_TYPE_MASK IsoUsageType = 0x30 +) + +var isoUsageTypeDescription = map[IsoUsageType]string{ + ISO_USAGE_TYPE_DATA: "data", + ISO_USAGE_TYPE_FEEDBACK: "feedback", + ISO_USAGE_TYPE_IMPLICIT: "implicit data", +} + +func (iut IsoUsageType) String() string { + return isoUsageTypeDescription[iut] +} + +type RequestType uint8 + +const ( + REQUEST_TYPE_STANDARD = C.LIBUSB_REQUEST_TYPE_STANDARD + REQUEST_TYPE_CLASS = C.LIBUSB_REQUEST_TYPE_CLASS + REQUEST_TYPE_VENDOR = C.LIBUSB_REQUEST_TYPE_VENDOR + REQUEST_TYPE_RESERVED = C.LIBUSB_REQUEST_TYPE_RESERVED +) + +var requestTypeDescription = map[RequestType]string{ + REQUEST_TYPE_STANDARD: "standard", + REQUEST_TYPE_CLASS: "class", + REQUEST_TYPE_VENDOR: "vendor", + REQUEST_TYPE_RESERVED: "reserved", +} + +func (rt RequestType) String() string { + return requestTypeDescription[rt] +} diff --git a/vendor/github.com/kylelemons/gousb/usb/debug.go b/vendor/github.com/kylelemons/gousb/usb/debug.go new file mode 100644 index 0000000..c3e061d --- /dev/null +++ b/vendor/github.com/kylelemons/gousb/usb/debug.go @@ -0,0 +1,37 @@ +// Copyright 2013 Google Inc. All rights reserved. +// Copyright 2016 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 + +// To enable internal debugging: +// -ldflags "-X github.com/kylelemons/gousb/usb.debugInternal true" + +import ( + "io" + "io/ioutil" + "log" // TODO(kevlar): make a logger + "os" +) + +var debug *log.Logger +var debugInternal string + +func init() { + var out io.Writer = ioutil.Discard + if debugInternal != "" { + out = os.Stderr + } + debug = log.New(out, "usb", log.LstdFlags|log.Lshortfile) +} diff --git a/vendor/github.com/kylelemons/gousb/usb/descriptor.go b/vendor/github.com/kylelemons/gousb/usb/descriptor.go new file mode 100644 index 0000000..76a9b77 --- /dev/null +++ b/vendor/github.com/kylelemons/gousb/usb/descriptor.go @@ -0,0 +1,38 @@ +// Copyright 2013 Google Inc. All rights reserved. +// Copyright 2016 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 + +type Descriptor struct { + // Bus information + Bus uint8 // The bus on which the device was detected + Address uint8 // The address of the device on the bus + + // Version information + Spec BCD // USB Specification Release Number + Device BCD // The device version + + // Product information + Vendor ID // The Vendor identifer + Product ID // The Product identifier + + // Protocol information + Class uint8 // The class of this device + SubClass uint8 // The sub-class (within the class) of this device + Protocol uint8 // The protocol (within the sub-class) of this device + + // Configuration information + Configs []ConfigInfo +} diff --git a/vendor/github.com/kylelemons/gousb/usb/device.go b/vendor/github.com/kylelemons/gousb/usb/device.go new file mode 100644 index 0000000..bc22287 --- /dev/null +++ b/vendor/github.com/kylelemons/gousb/usb/device.go @@ -0,0 +1,196 @@ +// Copyright 2013 Google Inc. All rights reserved. +// Copyright 2016 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 ( + "fmt" + "sync" + "time" +) + +var DefaultReadTimeout = 1 * time.Second +var DefaultWriteTimeout = 1 * time.Second +var DefaultControlTimeout = 250 * time.Millisecond //5 * time.Second + +type Device struct { + handle *libusbDevHandle + + // Embed the device information for easy access + *Descriptor + + // Timeouts + ReadTimeout time.Duration + WriteTimeout time.Duration + ControlTimeout time.Duration + + // Claimed interfaces + lock *sync.Mutex + claimed map[uint8]int +} + +func newDevice(handle *libusbDevHandle, desc *Descriptor) *Device { + ifaces := 0 + d := &Device{ + handle: handle, + Descriptor: desc, + ReadTimeout: DefaultReadTimeout, + WriteTimeout: DefaultWriteTimeout, + ControlTimeout: DefaultControlTimeout, + lock: new(sync.Mutex), + claimed: make(map[uint8]int, ifaces), + } + + return d +} + +func (d *Device) Reset() error { + return libusb.reset(d.handle) +} + +func (d *Device) Control(rType, request uint8, val, idx uint16, data []byte) (int, error) { + return libusb.control(d.handle, d.ControlTimeout, rType, request, val, idx, data) +} + +// ActiveConfig returns the config id (not the index) of the active configuration. +// This corresponds to the ConfigInfo.Config field. +func (d *Device) ActiveConfig() (uint8, error) { + return libusb.getConfig(d.handle) +} + +// SetConfig attempts to change the active configuration. +// The cfg provided is the config id (not the index) of the configuration to set, +// which corresponds to the ConfigInfo.Config field. +func (d *Device) SetConfig(cfg uint8) error { + return libusb.setConfig(d.handle, cfg) +} + +// Close the device. +func (d *Device) Close() error { + if d.handle == nil { + return fmt.Errorf("usb: double close on device") + } + d.lock.Lock() + defer d.lock.Unlock() + for iface := range d.claimed { + libusb.release(d.handle, iface) + } + libusb.close(d.handle) + d.handle = nil + return nil +} + +func (d *Device) OpenEndpoint(cfgNum, ifNum, setNum, epNum uint8) (Endpoint, error) { + var cfg *ConfigInfo + for _, c := range d.Configs { + if c.Config == cfgNum { + debug.Printf("found conf: %#v\n", c) + cfg = &c + break + } + } + if cfg == nil { + return nil, fmt.Errorf("usb: unknown configuration %02x", cfgNum) + } + + var intf *InterfaceInfo + for _, i := range cfg.Interfaces { + if i.Number == ifNum { + debug.Printf("found iface: %#v\n", i) + intf = &i + break + } + } + if intf == nil { + return nil, fmt.Errorf("usb: unknown interface %02x", ifNum) + } + + var setAlternate bool + var ifs *InterfaceSetup + for i, s := range intf.Setups { + if s.Alternate == setNum { + setAlternate = i != 0 + debug.Printf("found setup: %#v [default: %v]\n", s, !setAlternate) + ifs = &s + break + } + } + if ifs == nil { + return nil, fmt.Errorf("usb: unknown setup %02x", setNum) + } + + var ep *EndpointInfo + for _, e := range ifs.Endpoints { + if e.Address == epNum { + debug.Printf("found ep %02x in %#v\n", epNum, *ifs) + ep = &e + break + } + } + if ep == nil { + return nil, fmt.Errorf("usb: unknown endpoint %02x", epNum) + } + + end := newEndpoint(d.handle, *ifs, *ep, d.ReadTimeout, d.WriteTimeout) + + // Set the configuration + activeConf, err := libusb.getConfig(d.handle) + if err != nil { + return nil, fmt.Errorf("usb: getcfg: %s", err) + } + if activeConf != cfgNum { + if err := libusb.setConfig(d.handle, cfgNum); err != nil { + return nil, fmt.Errorf("usb: setcfg: %s", err) + } + } + + // Claim the interface + if err := libusb.claim(d.handle, ifNum); err != nil { + return nil, fmt.Errorf("usb: claim: %s", err) + } + + // Increment the claim count + d.lock.Lock() + d.claimed[ifNum]++ + d.lock.Unlock() // unlock immediately because the next calls may block + + // Choose the alternate + if setAlternate { + if err := libusb.setAlt(d.handle, ifNum, setNum); err != nil { + return nil, fmt.Errorf("usb: setalt: %s", err) + } + } + + return end, nil +} + +func (d *Device) GetStringDescriptor(desc_index int) (string, error) { + return libusb.getStringDesc(d.handle, desc_index) +} + +// SetAutoDetach enables/disables libusb's automatic kernel driver detachment. +// When autodetach is enabled libusb will automatically detach the kernel driver +// on the interface and reattach it when releasing the interface. +// Automatic kernel driver detachment is disabled on newly opened device handles by default. +func (d *Device) SetAutoDetach(autodetach bool) error { + var autodetachInt int + switch autodetach { + case true: + autodetachInt = 1 + case false: + autodetachInt = 0 + } + return libusb.setAutoDetach(d.handle, autodetachInt) +} diff --git a/vendor/github.com/kylelemons/gousb/usb/endpoint.go b/vendor/github.com/kylelemons/gousb/usb/endpoint.go new file mode 100644 index 0000000..067fa63 --- /dev/null +++ b/vendor/github.com/kylelemons/gousb/usb/endpoint.go @@ -0,0 +1,89 @@ +// Copyright 2013 Google Inc. All rights reserved. +// Copyright 2016 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 ( + "fmt" + "time" +) + +type Endpoint interface { + Read(b []byte) (int, error) + Write(b []byte) (int, error) + Interface() InterfaceSetup + Info() EndpointInfo +} + +type endpoint struct { + h *libusbDevHandle + + InterfaceSetup + EndpointInfo + + readTimeout time.Duration + writeTimeout time.Duration +} + +func (e *endpoint) Read(buf []byte) (int, error) { + if EndpointDirection(e.Address)&ENDPOINT_DIR_MASK != ENDPOINT_DIR_IN { + return 0, fmt.Errorf("usb: read: not an IN endpoint") + } + + return e.transfer(buf, e.readTimeout) +} + +func (e *endpoint) Write(buf []byte) (int, error) { + if EndpointDirection(e.Address)&ENDPOINT_DIR_MASK != ENDPOINT_DIR_OUT { + return 0, fmt.Errorf("usb: write: not an OUT endpoint") + } + + 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) { + if len(buf) == 0 { + return 0, nil + } + + t, err := newUSBTransfer(e.h, e.EndpointInfo, buf, timeout) + if err != nil { + return 0, err + } + defer t.free() + + if err := t.submit(); err != nil { + return 0, err + } + + n, err := t.wait() + if err != nil { + return n, err + } + return n, nil +} + +func newEndpoint(h *libusbDevHandle, s InterfaceSetup, e EndpointInfo, rt, wt time.Duration) *endpoint { + return &endpoint{ + InterfaceSetup: s, + EndpointInfo: e, + h: h, + readTimeout: rt, + writeTimeout: wt, + } +} diff --git a/vendor/github.com/kylelemons/gousb/usb/endpoint_info_test.go b/vendor/github.com/kylelemons/gousb/usb/endpoint_info_test.go new file mode 100644 index 0000000..d19a323 --- /dev/null +++ b/vendor/github.com/kylelemons/gousb/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/vendor/github.com/kylelemons/gousb/usb/endpoint_test.go b/vendor/github.com/kylelemons/gousb/usb/endpoint_test.go new file mode 100644 index 0000000..8f31099 --- /dev/null +++ b/vendor/github.com/kylelemons/gousb/usb/endpoint_test.go @@ -0,0 +1,134 @@ +// 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 ( + "reflect" + "testing" + "time" +) + +func TestEndpoint(t *testing.T) { + defer func(i libusbIntf) { libusb = i }(libusb) + 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 { + desc string + buf []byte + ret int + status TransferStatus + want int + wantErr bool + }{ + { + desc: "empty buffer", + buf: nil, + ret: 10, + want: 0, + }, + { + desc: "128B buffer, 60 transferred", + buf: make([]byte, 128), + ret: 60, + want: 60, + }, + { + desc: "128B buffer, 10 transferred and then error", + buf: make([]byte, 128), + ret: 10, + status: LIBUSB_TRANSFER_ERROR, + want: 10, + wantErr: true, + }, + } { + lib := newFakeLibusb() + libusb = lib + ep := newEndpoint(nil, epCfg.InterfaceSetup, epCfg.EndpointInfo, time.Second, time.Second) + op, ok := reflect.TypeOf(ep).MethodByName(epCfg.method) + if !ok { + t.Fatalf("method %s not found in endpoint struct", epCfg.method) + } + go func() { + fakeT := lib.waitForSubmitted() + fakeT.length = tc.ret + fakeT.status = tc.status + close(fakeT.done) + }() + opv := op.Func.Interface().(func(*endpoint, []byte) (int, error)) + got, err := opv(ep, tc.buf) + if (err != nil) != tc.wantErr { + t.Errorf("%s: bulkInEP.Read(): got err: %v, err != nil is %v, want %v", tc.desc, err, err != nil, tc.wantErr) + continue + } + if got != tc.want { + t.Errorf("%s: bulkInEP.Read(): got %d bytes, want %d", tc.desc, 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") + } +} + +func TestOpenEndpoint(t *testing.T) { + origLib := libusb + defer func() { libusb = origLib }() + libusb = newFakeLibusb() + + c := NewContext() + dev, err := c.OpenDeviceWithVidPid(0x8888, 0x0002) + if dev == nil { + t.Fatal("OpenDeviceWithVidPid(0x8888, 0x0002): got nil device, need non-nil") + } + defer dev.Close() + if err != nil { + t.Fatalf("OpenDeviceWithVidPid(0x8888, 0x0002): got error %v, want nil", err) + } + ep, err := dev.OpenEndpoint(1, 1, 1, 0x86) + if err != nil { + t.Errorf("OpenEndpoint(cfg=1, if=1, alt=1, ep=0x86): got error %v, want nil", err) + } + i := ep.Info() + if got, want := i.Address, uint8(0x86); got != want { + t.Errorf("OpenEndpoint(cfg=1, if=1, alt=1, ep=0x86): ep.Info.Address = %x, want %x", got, want) + } + if got, want := i.MaxIsoPacket, uint32(2*1024); got != want { + t.Errorf("OpenEndpoint(cfg=1, if=1, alt=1, ep=0x86): ep.Info.MaxIsoPacket = %d, want %d", got, want) + } +} diff --git a/vendor/github.com/kylelemons/gousb/usb/error.go b/vendor/github.com/kylelemons/gousb/usb/error.go new file mode 100644 index 0000000..892c54d --- /dev/null +++ b/vendor/github.com/kylelemons/gousb/usb/error.go @@ -0,0 +1,101 @@ +// Copyright 2013 Google Inc. All rights reserved. +// Copyright 2016 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 ( + "fmt" +) + +// #include +import "C" + +type usbError C.int + +func (e usbError) Error() string { + return fmt.Sprintf("libusb: %s [code %d]", usbErrorString[e], int(e)) +} + +func fromUSBError(errno C.int) error { + err := usbError(errno) + if err == SUCCESS { + return nil + } + return err +} + +const ( + SUCCESS usbError = C.LIBUSB_SUCCESS + ERROR_IO usbError = C.LIBUSB_ERROR_IO + ERROR_INVALID_PARAM usbError = C.LIBUSB_ERROR_INVALID_PARAM + ERROR_ACCESS usbError = C.LIBUSB_ERROR_ACCESS + ERROR_NO_DEVICE usbError = C.LIBUSB_ERROR_NO_DEVICE + ERROR_NOT_FOUND usbError = C.LIBUSB_ERROR_NOT_FOUND + ERROR_BUSY usbError = C.LIBUSB_ERROR_BUSY + ERROR_TIMEOUT usbError = C.LIBUSB_ERROR_TIMEOUT + ERROR_OVERFLOW usbError = C.LIBUSB_ERROR_OVERFLOW + ERROR_PIPE usbError = C.LIBUSB_ERROR_PIPE + ERROR_INTERRUPTED usbError = C.LIBUSB_ERROR_INTERRUPTED + ERROR_NO_MEM usbError = C.LIBUSB_ERROR_NO_MEM + ERROR_NOT_SUPPORTED usbError = C.LIBUSB_ERROR_NOT_SUPPORTED + ERROR_OTHER usbError = C.LIBUSB_ERROR_OTHER +) + +var usbErrorString = map[usbError]string{ + C.LIBUSB_SUCCESS: "success", + C.LIBUSB_ERROR_IO: "i/o error", + C.LIBUSB_ERROR_INVALID_PARAM: "invalid param", + C.LIBUSB_ERROR_ACCESS: "bad access", + C.LIBUSB_ERROR_NO_DEVICE: "no device", + C.LIBUSB_ERROR_NOT_FOUND: "not found", + C.LIBUSB_ERROR_BUSY: "device or resource busy", + C.LIBUSB_ERROR_TIMEOUT: "timeout", + C.LIBUSB_ERROR_OVERFLOW: "overflow", + C.LIBUSB_ERROR_PIPE: "pipe error", + C.LIBUSB_ERROR_INTERRUPTED: "interrupted", + C.LIBUSB_ERROR_NO_MEM: "out of memory", + C.LIBUSB_ERROR_NOT_SUPPORTED: "not supported", + C.LIBUSB_ERROR_OTHER: "unknown error", +} + +type TransferStatus uint8 + +const ( + LIBUSB_TRANSFER_COMPLETED TransferStatus = C.LIBUSB_TRANSFER_COMPLETED + LIBUSB_TRANSFER_ERROR TransferStatus = C.LIBUSB_TRANSFER_ERROR + LIBUSB_TRANSFER_TIMED_OUT TransferStatus = C.LIBUSB_TRANSFER_TIMED_OUT + LIBUSB_TRANSFER_CANCELLED TransferStatus = C.LIBUSB_TRANSFER_CANCELLED + LIBUSB_TRANSFER_STALL TransferStatus = C.LIBUSB_TRANSFER_STALL + LIBUSB_TRANSFER_NO_DEVICE TransferStatus = C.LIBUSB_TRANSFER_NO_DEVICE + LIBUSB_TRANSFER_OVERFLOW TransferStatus = C.LIBUSB_TRANSFER_OVERFLOW +) + +var transferStatusDescription = map[TransferStatus]string{ + LIBUSB_TRANSFER_COMPLETED: "transfer completed without error", + LIBUSB_TRANSFER_ERROR: "transfer failed", + LIBUSB_TRANSFER_TIMED_OUT: "transfer timed out", + LIBUSB_TRANSFER_CANCELLED: "transfer was cancelled", + LIBUSB_TRANSFER_STALL: "halt condition detected (endpoint stalled) or control request not supported", + LIBUSB_TRANSFER_NO_DEVICE: "device was disconnected", + LIBUSB_TRANSFER_OVERFLOW: "device sent more data than requested", +} + +func (ts TransferStatus) String() string { + return transferStatusDescription[ts] +} + +func (ts TransferStatus) Error() string { + return "libusb: " + ts.String() +} diff --git a/vendor/github.com/kylelemons/gousb/usb/fakelibusb_test.go b/vendor/github.com/kylelemons/gousb/usb/fakelibusb_test.go new file mode 100644 index 0000000..c658d2e --- /dev/null +++ b/vendor/github.com/kylelemons/gousb/usb/fakelibusb_test.go @@ -0,0 +1,312 @@ +// 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" + "fmt" + "sync" + "time" + "unsafe" +) + +var ( + // fake devices connected through the fakeLibusb stack. + fakeDevices = []*Descriptor{ + // Bus 001 Device 001: ID 9999:0001 + // One config, one interface, one setup, + // two endpoints: 0x01 OUT, 0x82 IN. + &Descriptor{ + Bus: 1, + Address: 1, + Spec: USB_2_0, + Device: BCD(0x0100), // 1.00 + Vendor: ID(0x9999), + Product: ID(0x0001), + Protocol: 255, + Configs: []ConfigInfo{{ + Config: 1, + MaxPower: 50, // * 2mA + Interfaces: []InterfaceInfo{{ + Number: 0, + Setups: []InterfaceSetup{{ + Number: 0, + Alternate: 0, + IfClass: uint8(CLASS_VENDOR_SPEC), + Endpoints: []EndpointInfo{{ + Address: uint8(0x01 | ENDPOINT_DIR_OUT), + Attributes: uint8(TRANSFER_TYPE_BULK), + MaxPacketSize: 512, + }, { + Address: uint8(0x02 | ENDPOINT_DIR_IN), + Attributes: uint8(TRANSFER_TYPE_BULK), + MaxPacketSize: 512, + }}, + }}, + }}, + }}, + }, + // Bus 001 Device 002: ID 8888:0002 + // One config, two interfaces. interface #0 with no endpoints, + // interface #1 with two alt setups with different packet sizes for + // endpoints. Two isochronous endpoints, 0x05 OUT and 0x86 OUT. + &Descriptor{ + Bus: 1, + Address: 2, + Spec: USB_2_0, + Device: BCD(0x0103), // 1.03 + Vendor: ID(0x8888), + Product: ID(0x0002), + Protocol: 255, + Configs: []ConfigInfo{{ + Config: 1, + MaxPower: 50, // * 2mA + Interfaces: []InterfaceInfo{{ + Number: 0, + Setups: []InterfaceSetup{{ + Number: 0, + Alternate: 0, + IfClass: uint8(CLASS_VENDOR_SPEC), + }}, + }, { + Number: 1, + Setups: []InterfaceSetup{{ + Number: 1, + Alternate: 0, + IfClass: uint8(CLASS_VENDOR_SPEC), + Endpoints: []EndpointInfo{{ + Address: uint8(0x05 | ENDPOINT_DIR_OUT), + Attributes: uint8(TRANSFER_TYPE_ISOCHRONOUS), + MaxPacketSize: 2<<11 | 1024, + MaxIsoPacket: 3 * 1024, + }, { + Address: uint8(0x06 | ENDPOINT_DIR_IN), + Attributes: uint8(TRANSFER_TYPE_ISOCHRONOUS), + MaxPacketSize: 2<<11 | 1024, + MaxIsoPacket: 3 * 1024, + }}, + }, { + Number: 1, + Alternate: 1, + IfClass: uint8(CLASS_VENDOR_SPEC), + Endpoints: []EndpointInfo{{ + Address: uint8(0x05 | ENDPOINT_DIR_OUT), + Attributes: uint8(TRANSFER_TYPE_ISOCHRONOUS), + MaxPacketSize: 1<<11 | 1024, + MaxIsoPacket: 2 * 1024, + }, { + Address: uint8(0x06 | ENDPOINT_DIR_IN), + Attributes: uint8(TRANSFER_TYPE_ISOCHRONOUS), + MaxPacketSize: 1<<11 | 1024, + MaxIsoPacket: 2 * 1024, + }}, + }, { + Number: 1, + Alternate: 2, + IfClass: uint8(CLASS_VENDOR_SPEC), + Endpoints: []EndpointInfo{{ + Address: uint8(0x05 | ENDPOINT_DIR_OUT), + Attributes: uint8(TRANSFER_TYPE_ISOCHRONOUS), + MaxPacketSize: 1024, + MaxIsoPacket: 1024, + }, { + Address: uint8(0x06 | ENDPOINT_DIR_IN), + Attributes: uint8(TRANSFER_TYPE_ISOCHRONOUS), + MaxPacketSize: 1024, + MaxIsoPacket: 1024, + }}, + }}, + }}, + }}, + }, + } +) + +type fakeDevice struct { + desc *Descriptor + alt uint8 +} + +type fakeTransfer struct { + // done is the channel that needs to be closed when the transfer has finished. + done chan struct{} + // buf is the slice for reading/writing data between the submit() and wait() returning. + buf []byte + // status will be returned by wait() on this transfer + status TransferStatus + // length is the number of bytes used from the buffer (write) or available + // in the buffer (read). + length int +} + +// fakeLibusb implements a fake libusb stack that pretends to have a number of +// devices connected to it (see fakeDevices variable for a list of devices). +// fakeLibusb is expected to implement all the functions related to device +// enumeration, configuration etc. according to fakeDevices descriptors. +// The fake devices endpoints don't have any particular behavior implemented, +// instead fakeLibusb provides additional functions, like waitForSubmitted, +// that allows the test to explicitly control individual transfer behavior. +type fakeLibusb struct { + mu sync.Mutex + // fakeDevices has a map of devices and their descriptors. + fakeDevices map[*libusbDevice]*fakeDevice + // ts has a map of all allocated transfers, indexed by the pointer of + // underlying libusbTransfer. + ts map[*libusbTransfer]*fakeTransfer + // submitted receives a fakeTransfers when submit() is called. + submitted chan *fakeTransfer + // handles is a map of device handles pointing at opened devices. + handles map[*libusbDevHandle]*libusbDevice + // claims is a map of devices to a set of claimed interfaces + claims map[*libusbDevice]map[uint8]bool +} + +func (f *fakeLibusb) init() (*libusbContext, error) { return new(libusbContext), nil } +func (f *fakeLibusb) handleEvents(c *libusbContext, done <-chan struct{}) { <-done } +func (f *fakeLibusb) getDevices(*libusbContext) ([]*libusbDevice, error) { + ret := make([]*libusbDevice, 0, len(fakeDevices)) + for d := range f.fakeDevices { + ret = append(ret, d) + } + return ret, nil +} +func (f *fakeLibusb) exit(*libusbContext) {} +func (f *fakeLibusb) setDebug(*libusbContext, int) {} + +func (f *fakeLibusb) dereference(d *libusbDevice) {} +func (f *fakeLibusb) getDeviceDesc(d *libusbDevice) (*Descriptor, error) { + if dev, ok := f.fakeDevices[d]; ok { + return dev.desc, nil + } + return nil, fmt.Errorf("invalid USB device %p", d) +} +func (f *fakeLibusb) open(d *libusbDevice) (*libusbDevHandle, error) { + h := new(libusbDevHandle) + f.mu.Lock() + defer f.mu.Unlock() + f.handles[h] = d + return h, nil +} + +func (f *fakeLibusb) close(h *libusbDevHandle) { + f.mu.Lock() + defer f.mu.Unlock() + delete(f.handles, h) +} +func (f *fakeLibusb) reset(*libusbDevHandle) error { return nil } +func (f *fakeLibusb) control(*libusbDevHandle, time.Duration, uint8, uint8, uint16, uint16, []byte) (int, error) { + return 0, errors.New("not implemented") +} +func (f *fakeLibusb) getConfig(*libusbDevHandle) (uint8, error) { return 1, nil } +func (f *fakeLibusb) setConfig(d *libusbDevHandle, cfg uint8) error { + f.mu.Lock() + defer f.mu.Unlock() + if len(f.claims[f.handles[d]]) != 0 { + return fmt.Errorf("can't set device config while interfaces are claimed: %v", f.claims[f.handles[d]]) + } + if cfg != 1 { + return fmt.Errorf("device doesn't have config number %d", cfg) + } + return nil +} +func (f *fakeLibusb) getStringDesc(*libusbDevHandle, int) (string, error) { + return "", errors.New("not implemented") +} +func (f *fakeLibusb) setAutoDetach(*libusbDevHandle, int) error { return nil } + +func (f *fakeLibusb) claim(d *libusbDevHandle, intf uint8) error { + f.mu.Lock() + defer f.mu.Unlock() + c := f.claims[f.handles[d]] + if c == nil { + c = make(map[uint8]bool) + f.claims[f.handles[d]] = c + } + c[intf] = true + return nil +} +func (f *fakeLibusb) release(d *libusbDevHandle, intf uint8) { + f.mu.Lock() + defer f.mu.Unlock() + c := f.claims[f.handles[d]] + if c == nil { + return + } + c[intf] = false +} +func (f *fakeLibusb) setAlt(d *libusbDevHandle, intf, alt uint8) error { + f.mu.Lock() + defer f.mu.Unlock() + if !f.claims[f.handles[d]][intf] { + return fmt.Errorf("interface %d must be claimed before alt setup can be set", intf) + } + f.fakeDevices[f.handles[d]].alt = alt + return nil +} + +func (f *fakeLibusb) alloc(_ *libusbDevHandle, _ uint8, _ TransferType, _ time.Duration, _ int, buf []byte) (*libusbTransfer, error) { + f.mu.Lock() + defer f.mu.Unlock() + t := new(libusbTransfer) + f.ts[t] = &fakeTransfer{buf: buf} + return t, nil +} +func (f *fakeLibusb) cancel(t *libusbTransfer) error { return errors.New("not implemented") } +func (f *fakeLibusb) submit(t *libusbTransfer, done chan struct{}) error { + f.mu.Lock() + ft := f.ts[t] + f.mu.Unlock() + ft.done = done + f.submitted <- ft + return nil +} +func (f *fakeLibusb) data(t *libusbTransfer) (int, TransferStatus) { + f.mu.Lock() + defer f.mu.Unlock() + return f.ts[t].length, f.ts[t].status +} +func (f *fakeLibusb) free(t *libusbTransfer) { + f.mu.Lock() + defer f.mu.Unlock() + delete(f.ts, t) +} +func (f *fakeLibusb) setIsoPacketLengths(*libusbTransfer, uint32) {} + +// waitForSubmitted can be used by tests to define custom behavior of the transfers submitted on the USB bus. +// TODO(sebek): add fields in fakeTransfer to differentiate between different devices/endpoints used concurrently. +func (f *fakeLibusb) waitForSubmitted() *fakeTransfer { + return <-f.submitted +} + +func newFakeLibusb() *fakeLibusb { + fl := &fakeLibusb{ + fakeDevices: make(map[*libusbDevice]*fakeDevice), + ts: make(map[*libusbTransfer]*fakeTransfer), + submitted: make(chan *fakeTransfer, 10), + handles: make(map[*libusbDevHandle]*libusbDevice), + claims: make(map[*libusbDevice]map[uint8]bool), + } + for i, d := range fakeDevices { + // libusb does not export a way to allocate a new libusb_device struct + // without using the full USB stack. Since the fake library uses the + // libusbDevice only as an identifier, use arbitrary numbers pretending + // to be pointers. The contents of these pointers is never accessed. + fl.fakeDevices[(*libusbDevice)(unsafe.Pointer(uintptr(i)))] = &fakeDevice{ + desc: d, + alt: 0, + } + } + return fl +} diff --git a/vendor/github.com/kylelemons/gousb/usb/libusb.go b/vendor/github.com/kylelemons/gousb/usb/libusb.go new file mode 100644 index 0000000..22856e4 --- /dev/null +++ b/vendor/github.com/kylelemons/gousb/usb/libusb.go @@ -0,0 +1,368 @@ +// 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 ( + "fmt" + "log" + "reflect" + "time" + "unsafe" +) + +/* +#cgo pkg-config: libusb-1.0 +#include + +int compact_iso_data(struct libusb_transfer *xfer, unsigned char *status); +int submit(struct libusb_transfer *xfer); +*/ +import "C" + +type libusbContext C.libusb_context +type libusbDevice C.libusb_device +type libusbDevHandle C.libusb_device_handle +type libusbTransfer C.struct_libusb_transfer + +// libusbIntf is a set of trivial idiomatic Go wrappers around libusb C functions. +// The underlying code is generally not testable or difficult to test, +// since libusb interacts directly with the host USB stack. +// +// All functions here should operate on types defined on C.libusb* data types, +// and occasionally on convenience data types (like TransferType or Descriptor). +type libusbIntf interface { + // context + init() (*libusbContext, error) + handleEvents(*libusbContext, <-chan struct{}) + getDevices(*libusbContext) ([]*libusbDevice, error) + exit(*libusbContext) + setDebug(*libusbContext, int) + + // device + dereference(*libusbDevice) + getDeviceDesc(*libusbDevice) (*Descriptor, error) + open(*libusbDevice) (*libusbDevHandle, error) + + close(*libusbDevHandle) + reset(*libusbDevHandle) error + control(*libusbDevHandle, time.Duration, uint8, uint8, uint16, uint16, []byte) (int, error) + getConfig(*libusbDevHandle) (uint8, error) + setConfig(*libusbDevHandle, uint8) error + getStringDesc(*libusbDevHandle, int) (string, error) + setAutoDetach(*libusbDevHandle, int) error + + // interface + claim(*libusbDevHandle, uint8) error + release(*libusbDevHandle, uint8) + setAlt(*libusbDevHandle, uint8, uint8) error + + // transfer + alloc(*libusbDevHandle, uint8, TransferType, time.Duration, int, []byte) (*libusbTransfer, error) + cancel(*libusbTransfer) error + submit(*libusbTransfer, chan struct{}) error + data(*libusbTransfer) (int, TransferStatus) + free(*libusbTransfer) + setIsoPacketLengths(*libusbTransfer, uint32) +} + +// libusbImpl is an implementation of libusbIntf using real CGo-wrapped libusb. +type libusbImpl struct{} + +func (libusbImpl) init() (*libusbContext, error) { + var ctx *C.libusb_context + if err := fromUSBError(C.libusb_init(&ctx)); err != nil { + return nil, err + } + return (*libusbContext)(ctx), nil +} + +func (libusbImpl) handleEvents(c *libusbContext, done <-chan struct{}) { + tv := C.struct_timeval{tv_usec: 100e3} + for { + select { + case <-done: + return + default: + } + if errno := C.libusb_handle_events_timeout_completed((*C.libusb_context)(c), &tv, nil); errno < 0 { + log.Printf("handle_events: error: %s", usbError(errno)) + } + } +} + +func (libusbImpl) getDevices(ctx *libusbContext) ([]*libusbDevice, error) { + var list **C.libusb_device + cnt := C.libusb_get_device_list((*C.libusb_context)(ctx), &list) + if cnt < 0 { + return nil, fromUSBError(C.int(cnt)) + } + var devs []*C.libusb_device + *(*reflect.SliceHeader)(unsafe.Pointer(&devs)) = reflect.SliceHeader{ + Data: uintptr(unsafe.Pointer(list)), + Len: int(cnt), + Cap: int(cnt), + } + var ret []*libusbDevice + for _, d := range devs { + ret = append(ret, (*libusbDevice)(d)) + } + // devices will be dereferenced later, during close. + C.libusb_free_device_list(list, 0) + return ret, nil +} + +func (libusbImpl) exit(c *libusbContext) { + C.libusb_exit((*C.libusb_context)(c)) +} + +func (libusbImpl) setDebug(c *libusbContext, lvl int) { + C.libusb_set_debug((*C.libusb_context)(c), C.int(lvl)) +} + +func (libusbImpl) getDeviceDesc(d *libusbDevice) (*Descriptor, error) { + var desc C.struct_libusb_device_descriptor + if err := fromUSBError(C.libusb_get_device_descriptor((*C.libusb_device)(d), &desc)); err != nil { + return nil, err + } + // Enumerate configurations + var cfgs []ConfigInfo + for i := 0; i < int(desc.bNumConfigurations); i++ { + var cfg *C.struct_libusb_config_descriptor + if err := fromUSBError(C.libusb_get_config_descriptor((*C.libusb_device)(d), C.uint8_t(i), &cfg)); err != nil { + return nil, err + } + c := ConfigInfo{ + Config: uint8(cfg.bConfigurationValue), + Attributes: uint8(cfg.bmAttributes), + MaxPower: uint8(cfg.MaxPower), + } + + var ifaces []C.struct_libusb_interface + *(*reflect.SliceHeader)(unsafe.Pointer(&ifaces)) = reflect.SliceHeader{ + Data: uintptr(unsafe.Pointer(cfg._interface)), + Len: int(cfg.bNumInterfaces), + Cap: int(cfg.bNumInterfaces), + } + c.Interfaces = make([]InterfaceInfo, 0, len(ifaces)) + for _, iface := range ifaces { + if iface.num_altsetting == 0 { + continue + } + + var alts []C.struct_libusb_interface_descriptor + *(*reflect.SliceHeader)(unsafe.Pointer(&alts)) = reflect.SliceHeader{ + Data: uintptr(unsafe.Pointer(iface.altsetting)), + Len: int(iface.num_altsetting), + Cap: int(iface.num_altsetting), + } + descs := make([]InterfaceSetup, 0, len(alts)) + for _, alt := range alts { + i := InterfaceSetup{ + Number: uint8(alt.bInterfaceNumber), + Alternate: uint8(alt.bAlternateSetting), + IfClass: uint8(alt.bInterfaceClass), + IfSubClass: uint8(alt.bInterfaceSubClass), + IfProtocol: uint8(alt.bInterfaceProtocol), + } + var ends []C.struct_libusb_endpoint_descriptor + *(*reflect.SliceHeader)(unsafe.Pointer(&ends)) = reflect.SliceHeader{ + Data: uintptr(unsafe.Pointer(alt.endpoint)), + Len: int(alt.bNumEndpoints), + Cap: int(alt.bNumEndpoints), + } + i.Endpoints = make([]EndpointInfo, 0, len(ends)) + for _, end := range ends { + ei := EndpointInfo{ + Address: uint8(end.bEndpointAddress), + Attributes: uint8(end.bmAttributes), + MaxPacketSize: uint16(end.wMaxPacketSize), + PollInterval: uint8(end.bInterval), + RefreshRate: uint8(end.bRefresh), + SynchAddress: uint8(end.bSynchAddress), + } + if ei.TransferType() == TRANSFER_TYPE_ISOCHRONOUS { + // 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) + } + c.Interfaces = append(c.Interfaces, InterfaceInfo{ + Number: descs[0].Number, + Setups: descs, + }) + } + C.libusb_free_config_descriptor(cfg) + cfgs = append(cfgs, c) + } + + return &Descriptor{ + Bus: uint8(C.libusb_get_bus_number((*C.libusb_device)(d))), + Address: uint8(C.libusb_get_device_address((*C.libusb_device)(d))), + Spec: BCD(desc.bcdUSB), + Device: BCD(desc.bcdDevice), + Vendor: ID(desc.idVendor), + Product: ID(desc.idProduct), + Class: uint8(desc.bDeviceClass), + SubClass: uint8(desc.bDeviceSubClass), + Protocol: uint8(desc.bDeviceProtocol), + Configs: cfgs, + }, nil +} + +func (libusbImpl) dereference(d *libusbDevice) { + C.libusb_unref_device((*C.libusb_device)(d)) +} + +func (libusbImpl) open(d *libusbDevice) (*libusbDevHandle, error) { + var handle *C.libusb_device_handle + if err := fromUSBError(C.libusb_open((*C.libusb_device)(d), &handle)); err != nil { + return nil, err + } + return (*libusbDevHandle)(handle), nil +} + +func (libusbImpl) close(d *libusbDevHandle) { + C.libusb_close((*C.libusb_device_handle)(d)) +} + +func (libusbImpl) reset(d *libusbDevHandle) error { + return fromUSBError(C.libusb_reset_device((*C.libusb_device_handle)(d))) +} + +func (libusbImpl) control(d *libusbDevHandle, timeout time.Duration, rType, request uint8, val, idx uint16, data []byte) (int, error) { + dataSlice := (*reflect.SliceHeader)(unsafe.Pointer(&data)) + n := C.libusb_control_transfer( + (*C.libusb_device_handle)(d), + C.uint8_t(rType), + C.uint8_t(request), + C.uint16_t(val), + C.uint16_t(idx), + (*C.uchar)(unsafe.Pointer(dataSlice.Data)), + C.uint16_t(len(data)), + C.uint(timeout/time.Millisecond)) + if n < 0 { + return int(n), fromUSBError(n) + } + return int(n), nil +} + +func (libusbImpl) getConfig(d *libusbDevHandle) (uint8, error) { + var cfg C.int + if errno := C.libusb_get_configuration((*C.libusb_device_handle)(d), &cfg); errno < 0 { + return 0, fromUSBError(errno) + } + return uint8(cfg), nil +} + +func (libusbImpl) setConfig(d *libusbDevHandle, cfg uint8) error { + return fromUSBError(C.libusb_set_configuration((*C.libusb_device_handle)(d), C.int(cfg))) +} + +func (libusbImpl) getStringDesc(d *libusbDevHandle, index int) (string, error) { + // allocate 200-byte array limited the length of string descriptor + buf := make([]byte, 200) + // get string descriptor from libusb. if errno < 0 then there are any errors. + // if errno >= 0; it is a length of result string descriptor + errno := C.libusb_get_string_descriptor_ascii( + (*C.libusb_device_handle)(d), + C.uint8_t(index), + (*C.uchar)(unsafe.Pointer(&buf[0])), + 200) + if errno < 0 { + return "", fmt.Errorf("usb: getstr: %s", fromUSBError(errno)) + } + return string(buf[:errno]), nil +} + +func (libusbImpl) setAutoDetach(d *libusbDevHandle, val int) error { + err := fromUSBError(C.libusb_set_auto_detach_kernel_driver((*C.libusb_device_handle)(d), C.int(val))) + if err != nil && err != ERROR_NOT_SUPPORTED { + return err + } + return nil +} + +func (libusbImpl) claim(d *libusbDevHandle, iface uint8) error { + return fromUSBError(C.libusb_claim_interface((*C.libusb_device_handle)(d), C.int(iface))) +} + +func (libusbImpl) release(d *libusbDevHandle, iface uint8) { + C.libusb_release_interface((*C.libusb_device_handle)(d), C.int(iface)) +} + +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))) +} + +func (libusbImpl) alloc(d *libusbDevHandle, addr uint8, tt TransferType, timeout time.Duration, isoPackets int, buf []byte) (*libusbTransfer, error) { + xfer := C.libusb_alloc_transfer(C.int(isoPackets)) + if xfer == nil { + return nil, fmt.Errorf("libusb_alloc_transfer(%d) failed", isoPackets) + } + xfer.dev_handle = (*C.libusb_device_handle)(d) + xfer.endpoint = C.uchar(addr) + xfer.timeout = C.uint(timeout / time.Millisecond) + xfer._type = C.uchar(tt) + xfer.num_iso_packets = C.int(isoPackets) + xfer.buffer = (*C.uchar)((unsafe.Pointer)(&buf[0])) + xfer.length = C.int(len(buf)) + return (*libusbTransfer)(xfer), nil +} + +func (libusbImpl) cancel(t *libusbTransfer) error { + return fromUSBError(C.libusb_cancel_transfer((*C.struct_libusb_transfer)(t))) +} + +func (libusbImpl) submit(t *libusbTransfer, done chan struct{}) error { + t.user_data = (unsafe.Pointer)(&done) + return fromUSBError(C.submit((*C.struct_libusb_transfer)(t))) +} + +func (libusbImpl) data(t *libusbTransfer) (int, TransferStatus) { + if TransferType(t._type) == TRANSFER_TYPE_ISOCHRONOUS { + var status TransferStatus + n := int(C.compact_iso_data((*C.struct_libusb_transfer)(t), (*C.uchar)(unsafe.Pointer(&status)))) + return n, status + } + return int(t.actual_length), TransferStatus(t.status) +} + +func (libusbImpl) free(t *libusbTransfer) { + C.libusb_free_transfer((*C.struct_libusb_transfer)(t)) +} + +func (libusbImpl) setIsoPacketLengths(t *libusbTransfer, length uint32) { + C.libusb_set_iso_packet_lengths((*C.struct_libusb_transfer)(t), C.uint(length)) +} + +// libusb is an injection point for tests +var libusb libusbIntf = libusbImpl{} + +//export xfer_callback +func xfer_callback(cptr unsafe.Pointer) { + ch := *(*chan struct{})(cptr) + close(ch) +} + +// for benchmarking +func libusbSetDebug(c *libusbContext, lvl int) { + C.libusb_set_debug((*C.libusb_context)(c), C.int(lvl)) +} diff --git a/vendor/github.com/kylelemons/gousb/usb/libusb_cgo_benchmark_test.go b/vendor/github.com/kylelemons/gousb/usb/libusb_cgo_benchmark_test.go new file mode 100644 index 0000000..da8acc9 --- /dev/null +++ b/vendor/github.com/kylelemons/gousb/usb/libusb_cgo_benchmark_test.go @@ -0,0 +1,46 @@ +package usb + +import "testing" + +func BenchmarkCGo(b *testing.B) { + for _, bc := range []struct { + name string + bfunc func(*libusbContext, int) + }{ + { + name: "simple function", + bfunc: func(ctx *libusbContext, N int) { + for i := 0; i < N; i++ { + libusbSetDebug(ctx, i&1) + } + }, + }, + { + name: "method", + bfunc: func(ctx *libusbContext, N int) { + impl := libusbImpl{} + for i := 0; i < N; i++ { + impl.setDebug(ctx, i&1) + } + }, + }, + { + name: "interface", + bfunc: func(ctx *libusbContext, N int) { + var intf libusbIntf = libusbImpl{} + for i := 0; i < N; i++ { + intf.setDebug(ctx, i&1) + } + }, + }, + } { + b.Run(bc.name, func(b *testing.B) { + ctx, err := libusbImpl{}.init() + if err != nil { + b.Fatalf("libusb_init() failed: %v", err) + } + b.ResetTimer() + bc.bfunc(ctx, b.N) + }) + } +} diff --git a/vendor/github.com/kylelemons/gousb/usb/misc.go b/vendor/github.com/kylelemons/gousb/usb/misc.go new file mode 100644 index 0000000..1650e10 --- /dev/null +++ b/vendor/github.com/kylelemons/gousb/usb/misc.go @@ -0,0 +1,48 @@ +// Copyright 2013 Google Inc. All rights reserved. +// Copyright 2016 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 ( + "fmt" +) + +type BCD uint16 + +const ( + USB_2_0 BCD = 0x0200 + USB_1_1 BCD = 0x0110 + USB_1_0 BCD = 0x0100 +) + +func (d BCD) Int() (i int) { + ten := 1 + for o := uint(0); o < 4; o++ { + n := ((0xF << (o * 4)) & d) >> (o * 4) + i += int(n) * ten + ten *= 10 + } + return +} + +func (d BCD) String() string { + return fmt.Sprintf("%02x.%02x", int(d>>8), int(d&0xFF)) +} + +type ID uint16 + +func (id ID) String() string { + return fmt.Sprintf("%04x", int(id)) +} diff --git a/vendor/github.com/kylelemons/gousb/usb/misc_test.go b/vendor/github.com/kylelemons/gousb/usb/misc_test.go new file mode 100644 index 0000000..106e5d5 --- /dev/null +++ b/vendor/github.com/kylelemons/gousb/usb/misc_test.go @@ -0,0 +1,39 @@ +// Copyright 2013 Google Inc. All rights reserved. +// Copyright 2016 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" +) + +func TestBCD(t *testing.T) { + tests := []struct { + BCD + Int int + Str string + }{ + {0x1234, 1234, "12.34"}, + } + + for _, test := range tests { + if got, want := test.BCD.Int(), test.Int; got != want { + t.Errorf("Int(%x) = %d, want %d", test.BCD, got, want) + } + if got, want := test.BCD.String(), test.Str; got != want { + t.Errorf("String(%x) = %q, want %q", test.BCD, got, want) + } + } +} diff --git a/vendor/github.com/kylelemons/gousb/usb/transfer.c b/vendor/github.com/kylelemons/gousb/usb/transfer.c new file mode 100644 index 0000000..9965019 --- /dev/null +++ b/vendor/github.com/kylelemons/gousb/usb/transfer.c @@ -0,0 +1,80 @@ +// Copyright 2013 Google Inc. All rights reserved. +// Copyright 2016 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. + +#include +#include +#include + +void print_xfer(struct libusb_transfer *xfer); +void xfer_callback(void *); + +void callback(struct libusb_transfer *xfer) { + xfer_callback(xfer->user_data); +} + +int submit(struct libusb_transfer *xfer) { + xfer->callback = (libusb_transfer_cb_fn)(&callback); + xfer->status = -1; + return libusb_submit_transfer(xfer); +} + +void print_xfer(struct libusb_transfer *xfer) { + int i; + + printf("Transfer:\n"); + printf(" dev_handle: %p\n", xfer->dev_handle); + printf(" flags: %08x\n", xfer->flags); + printf(" endpoint: %x\n", xfer->endpoint); + printf(" type: %x\n", xfer->type); + printf(" timeout: %dms\n", xfer->timeout); + printf(" status: %x\n", xfer->status); + printf(" length: %d (act: %d)\n", xfer->length, xfer->actual_length); + printf(" callback: %p\n", xfer->callback); + printf(" user_data: %p\n", xfer->user_data); + printf(" buffer: %p\n", xfer->buffer); + printf(" num_iso_pkts: %d\n", xfer->num_iso_packets); + printf(" packets:\n"); + for (i = 0; i < xfer->num_iso_packets; i++) { + printf(" [%04d] %d (act: %d) %x\n", i, + xfer->iso_packet_desc[i].length, + xfer->iso_packet_desc[i].actual_length, + xfer->iso_packet_desc[i].status); + } +} + +// compact the data in an isochronous transfer. The contents of individual +// iso packets are shifted left, so that no gaps are left between them. +// Status is set to the first non-zero status of an iso packet. +int compact_iso_data(struct libusb_transfer *xfer, unsigned char *status) { + int i; + int sum = 0; + unsigned char *in = xfer->buffer; + unsigned char *out = xfer->buffer; + for (i = 0; i < xfer->num_iso_packets; i++) { + struct libusb_iso_packet_descriptor pkt = xfer->iso_packet_desc[i]; + if (pkt.status != 0) { + *status = pkt.status; + break; + } + // Copy the data + int len = pkt.actual_length; + memmove(out, in, len); + // Increment offsets + sum += len; + in += pkt.length; + out += len; + } + return sum; +} diff --git a/vendor/github.com/kylelemons/gousb/usb/transfer.go b/vendor/github.com/kylelemons/gousb/usb/transfer.go new file mode 100644 index 0000000..e41713a --- /dev/null +++ b/vendor/github.com/kylelemons/gousb/usb/transfer.go @@ -0,0 +1,147 @@ +// Copyright 2016 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" + "fmt" + "runtime" + "sync" + "time" +) + +type usbTransfer struct { + // mu protects the transfer state. + mu sync.Mutex + // xfer is the allocated libusb_transfer. + xfer *libusbTransfer + // buf is the buffer allocated for the transfer. Both buf and xfer.buffer + // point to the same piece of memory. + buf []byte + // done is blocking until the transfer is complete and data and transfer + // status are available. + done chan struct{} + // submitted is true if this transfer was passed to libusb through submit() + submitted bool +} + +// submits the transfer. After submit() the transfer is in flight and is owned by libusb. +// It's not safe to access the contents of the transfer until wait() returns. +// Once wait() returns, it's ok to re-use the same transfer structure by calling submit() again. +func (t *usbTransfer) submit() error { + t.mu.Lock() + defer t.mu.Unlock() + if t.submitted { + return errors.New("transfer was already submitted and is not finished yet.") + } + t.done = make(chan struct{}) + if err := libusb.submit(t.xfer, t.done); err != nil { + return err + } + t.submitted = true + return nil +} + +// wait waits for libusb to signal the release of transfer data. +// After wait returns, the transfer contents are safe to access +// via t.buf. The number returned by wait indicates how many bytes +// of the buffer were read or written by libusb, and it can be +// smaller than the length of t.buf. +func (t *usbTransfer) wait() (n int, err error) { + t.mu.Lock() + defer t.mu.Unlock() + if !t.submitted { + return 0, nil + } + select { + case <-time.After(10 * time.Second): + return 0, fmt.Errorf("wait timed out after 10s") + case <-t.done: + } + t.submitted = false + n, status := libusb.data(t.xfer) + if status != LIBUSB_TRANSFER_COMPLETED { + return n, status + } + return n, err +} + +// cancel aborts a submitted transfer. The transfer is cancelled +// asynchronously and the user still needs to wait() to return. +func (t *usbTransfer) cancel() error { + t.mu.Lock() + defer t.mu.Unlock() + if !t.submitted { + return nil + } + err := libusb.cancel(t.xfer) + if err == ERROR_NOT_FOUND { + // transfer already completed + return nil + } + return err +} + +// free releases the memory allocated for the transfer. +// free should be called only if the transfer is not used by libusb, +// i.e. it should not be called after submit() and before wait() returns. +func (t *usbTransfer) free() error { + t.mu.Lock() + defer t.mu.Unlock() + if t.submitted { + return errors.New("free() cannot be called on a submitted transfer until wait() returns") + } + libusb.free(t.xfer) + t.xfer = nil + t.buf = nil + t.done = nil + return nil +} + +// newUSBTransfer allocates a new transfer structure for communication with a +// given device/endpoint, with buf as the underlying transfer buffer. +func newUSBTransfer(dev *libusbDevHandle, ei EndpointInfo, buf []byte, timeout time.Duration) (*usbTransfer, error) { + var isoPackets int + var isoPktSize uint32 + tt := ei.TransferType() + if tt == TRANSFER_TYPE_ISOCHRONOUS { + isoPktSize = ei.MaxIsoPacket + if len(buf) < int(isoPktSize) { + isoPktSize = uint32(len(buf)) + } + isoPackets = len(buf) / int(isoPktSize) + debug.Printf("New isochronous transfer - buffer length %d, using %d packets of %d bytes each", len(buf), isoPackets, isoPktSize) + } + + xfer, err := libusb.alloc(dev, ei.Address, tt, timeout, isoPackets, buf) + if err != nil { + return nil, err + } + + if tt == TRANSFER_TYPE_ISOCHRONOUS { + libusb.setIsoPacketLengths(xfer, isoPktSize) + } + + t := &usbTransfer{ + xfer: xfer, + buf: buf, + } + runtime.SetFinalizer(t, func(t *usbTransfer) { + t.cancel() + t.wait() + t.free() + }) + return t, nil +} diff --git a/vendor/github.com/kylelemons/gousb/usb/transfer_test.go b/vendor/github.com/kylelemons/gousb/usb/transfer_test.go new file mode 100644 index 0000000..4e4fcea --- /dev/null +++ b/vendor/github.com/kylelemons/gousb/usb/transfer_test.go @@ -0,0 +1,145 @@ +// 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" +) + +func TestNewTransfer(t *testing.T) { + defer func(i libusbIntf) { libusb = i }(libusb) + libusb = newFakeLibusb() + + for _, tc := range []struct { + desc string + dir EndpointDirection + tt TransferType + maxPkt uint16 + maxIso uint32 + buf int + timeout time.Duration + wantIso int + wantLength int + wantTimeout int + }{ + { + desc: "bulk in transfer, 512B packets", + dir: ENDPOINT_DIR_IN, + tt: TRANSFER_TYPE_BULK, + maxPkt: 512, + buf: 1024, + timeout: time.Second, + wantLength: 1024, + }, + { + desc: "iso out transfer, 3 * 1024B packets", + dir: ENDPOINT_DIR_OUT, + tt: TRANSFER_TYPE_ISOCHRONOUS, + maxPkt: 2<<11 + 1024, + maxIso: 3 * 1024, + buf: 10000, + wantLength: 10000, + }, + } { + xfer, err := newUSBTransfer(nil, EndpointInfo{ + Address: uint8(tc.dir) | 0x02, + Attributes: uint8(tc.tt), + MaxPacketSize: tc.maxPkt, + MaxIsoPacket: tc.maxIso, + PollInterval: 1, + }, make([]byte, tc.buf), tc.timeout) + + if err != nil { + t.Fatalf("newUSBTransfer(): %v", err) + } + if got, want := len(xfer.buf), tc.wantLength; got != want { + t.Errorf("xfer.buf: got %d bytes, want %d", got, want) + } + } +} + +func TestTransferProtocol(t *testing.T) { + defer func(i libusbIntf) { libusb = i }(libusb) + + f := newFakeLibusb() + libusb = f + + xfers := make([]*usbTransfer, 2) + var err error + for i := 0; i < 2; i++ { + xfers[i], err = newUSBTransfer(nil, EndpointInfo{ + Address: 0x86, + Attributes: uint8(TRANSFER_TYPE_BULK), + MaxPacketSize: 512, + PollInterval: 1, + }, make([]byte, 10240), time.Second) + if err != nil { + t.Fatalf("newUSBTransfer: %v", err) + } + } + + go func() { + ft := f.waitForSubmitted() + ft.length = 5 + ft.status = LIBUSB_TRANSFER_COMPLETED + copy(ft.buf, []byte{1, 2, 3, 4, 5}) + close(ft.done) + + ft = f.waitForSubmitted() + ft.length = 99 + ft.status = LIBUSB_TRANSFER_COMPLETED + copy(ft.buf, []byte{12, 12, 12, 12, 12}) + close(ft.done) + + ft = f.waitForSubmitted() + ft.length = 123 + ft.status = LIBUSB_TRANSFER_CANCELLED + close(ft.done) + }() + + xfers[0].submit() + xfers[1].submit() + got, err := xfers[0].wait() + if err != nil { + t.Errorf("xfer#0.wait returned error %v, want nil", err) + } + if want := 5; got != want { + t.Errorf("xfer#0.wait returned %d bytes, want %d", got, want) + } + got, err = xfers[1].wait() + if err != nil { + t.Errorf("xfer#0.wait returned error %v, want nil", err) + } + if want := 99; got != want { + t.Errorf("xfer#0.wait returned %d bytes, want %d", got, want) + } + + xfers[1].submit() + xfers[1].cancel() + got, err = xfers[1].wait() + if err == nil { + t.Error("xfer#1(resubmitted).wait returned error nil, want non-nil") + } + if want := 123; got != want { + t.Errorf("xfer#1(resubmitted).wait returned %d bytes, want %d", got, want) + } + + for _, x := range xfers { + x.cancel() + x.wait() + x.free() + } +} diff --git a/vendor/github.com/kylelemons/gousb/usb/usb.go b/vendor/github.com/kylelemons/gousb/usb/usb.go new file mode 100644 index 0000000..b4bb258 --- /dev/null +++ b/vendor/github.com/kylelemons/gousb/usb/usb.go @@ -0,0 +1,110 @@ +// Copyright 2013 Google Inc. All rights reserved. +// Copyright 2016 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 provides a wrapper around libusb-1.0. +// +// Deprecated: this package was deprecated in favor of github.com/google/gousb. +// Please use the new package when starting new projects. +package usb + +type Context struct { + ctx *libusbContext + done chan struct{} +} + +func (c *Context) Debug(level int) { + libusb.setDebug(c.ctx, level) +} + +func NewContext() *Context { + c, err := libusb.init() + if err != nil { + panic(err) + } + ctx := &Context{ + ctx: c, + done: make(chan struct{}), + } + go libusb.handleEvents(ctx.ctx, ctx.done) + return ctx +} + +// ListDevices calls each with each enumerated device. +// If the function returns true, the device is opened and a Device is returned if the operation succeeds. +// Every Device returned (whether an error is also returned or not) must be closed. +// If there are any errors enumerating the devices, +// the final one is returned along with any successfully opened devices. +func (c *Context) ListDevices(each func(desc *Descriptor) bool) ([]*Device, error) { + list, err := libusb.getDevices(c.ctx) + if err != nil { + return nil, err + } + + var reterr error + var ret []*Device + for _, dev := range list { + desc, err := libusb.getDeviceDesc(dev) + if err != nil { + libusb.dereference(dev) + reterr = err + continue + } + + if each(desc) { + handle, err := libusb.open(dev) + if err != nil { + reterr = err + continue + } + ret = append(ret, newDevice(handle, desc)) + } else { + libusb.dereference(dev) + } + } + return ret, reterr +} + +// OpenDeviceWithVidPid opens Device from specific VendorId and ProductId. +// If none is found, it returns nil and nil error. If there are multiple devices +// with the same VID/PID, it will return one of them, picked arbitrarily. +// If there were any errors during device list traversal, it is possible +// it will return a non-nil device and non-nil error. A Device.Close() must +// be called to release the device if the returned device wasn't nil. +func (c *Context) OpenDeviceWithVidPid(vid, pid int) (*Device, error) { + var found bool + devs, err := c.ListDevices(func(desc *Descriptor) bool { + if found { + return false + } + if desc.Vendor == ID(vid) && desc.Product == ID(pid) { + found = true + return true + } + return false + }) + if len(devs) == 0 { + return nil, err + } + return devs[0], nil +} + +func (c *Context) Close() error { + c.done <- struct{}{} + if c.ctx != nil { + libusb.exit(c.ctx) + } + c.ctx = nil + return nil +} diff --git a/vendor/github.com/kylelemons/gousb/usb/usb_test.go b/vendor/github.com/kylelemons/gousb/usb/usb_test.go new file mode 100644 index 0000000..cad8646 --- /dev/null +++ b/vendor/github.com/kylelemons/gousb/usb/usb_test.go @@ -0,0 +1,87 @@ +// Copyright 2013 Google Inc. All rights reserved. +// Copyright 2016 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" + +func TestListDevices(t *testing.T) { + orig := libusb + defer func() { libusb = orig }() + libusb = newFakeLibusb() + + c := NewContext() + defer c.Close() + c.Debug(0) + + descs := []*Descriptor{} + devs, err := c.ListDevices(func(desc *Descriptor) bool { + descs = append(descs, desc) + return true + }) + defer func() { + for _, d := range devs { + d.Close() + } + }() + if err != nil { + t.Fatalf("ListDevices(): %s", err) + } + + if got, want := len(devs), len(fakeDevices); got != want { + t.Fatalf("len(devs) = %d, want %d (based on num fake devs)", got, want) + } + if got, want := len(devs), len(descs); got != want { + t.Fatalf("len(devs) = %d, want %d (based on num opened devices)", got, want) + } + + for i := range devs { + if got, want := devs[i].Descriptor, descs[i]; got != want { + t.Errorf("dev[%d].Descriptor = %p, want %p", i, got, want) + } + } +} + +func TestOpenDeviceWithVidPid(t *testing.T) { + orig := libusb + defer func() { libusb = orig }() + libusb = newFakeLibusb() + + for _, d := range []struct { + vid, pid int + exists bool + }{ + {0x7777, 0x0003, false}, + {0x8888, 0x0001, false}, + {0x8888, 0x0002, true}, + {0x9999, 0x0001, true}, + {0x9999, 0x0002, false}, + } { + c := NewContext() + dev, err := c.OpenDeviceWithVidPid(d.vid, d.pid) + if (dev != nil) != d.exists { + t.Errorf("OpenDeviceWithVidPid(%s/%s): device != nil is %v, want %v", ID(d.vid), ID(d.pid), dev != nil, d.exists) + } + if err != nil { + t.Errorf("OpenDeviceWithVidPid(%s/%s): got error %v, want nil", ID(d.vid), ID(d.pid), err) + } + if dev != nil { + if dev.Descriptor.Vendor != ID(d.vid) || dev.Descriptor.Product != ID(d.pid) { + t.Errorf("OpenDeviceWithVidPid(%s/%s): the device returned has VID/PID %s/%s, different from specified in the arguments", ID(d.vid), ID(d.pid), dev.Descriptor.Vendor, dev.Descriptor.Product) + } + dev.Close() + } + } +} -- cgit v1.2.3