├── .github ├── test-coverage.sh └── workflows │ └── build-and-test.yaml ├── .gitignore ├── AUTHORS ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── config.go ├── constants.go ├── debug.go ├── device.go ├── device_test.go ├── endpoint.go ├── endpoint_stream.go ├── endpoint_stream_test.go ├── endpoint_test.go ├── error.go ├── example_test.go ├── fakelibusb_devices.go ├── fakelibusb_test.go ├── fixlibusb_darwin.sh ├── go.mod ├── interface.go ├── libusb.go ├── libusb_cgo_benchmark_test.go ├── lsusb ├── .gitignore └── main.go ├── misc.go ├── misc_test.go ├── rawread ├── .gitignore └── main.go ├── transfer.c ├── transfer.go ├── transfer_stream.go ├── transfer_stream_test.go ├── transfer_test.go ├── usb.c ├── usb.go ├── usb_test.go └── usbid ├── describe.go ├── load.go ├── load_data.go ├── load_test.go ├── parse.go ├── parse_test.go ├── regen ├── .gitignore ├── load_data.go.tpl └── regen.go ├── testdata └── testdb.txt └── testdata_test.go /.github/test-coverage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | echo 'mode: count' > coverage.merged &&\ 3 | go list -f '{{.Dir}}' ./... | xargs -I'{}' sh -c ': > coverage.tmp; go test -v -covermode=count -coverprofile=coverage.tmp {} && tail -n +2 coverage.tmp >> coverage.merged' &&\ 4 | rm coverage.tmp 5 | -------------------------------------------------------------------------------- /.github/workflows/build-and-test.yaml: -------------------------------------------------------------------------------- 1 | name: build-and-test 2 | on: [pull_request, push, workflow_dispatch] 3 | jobs: 4 | linux: 5 | runs-on: ubuntu-24.04 6 | steps: 7 | - uses: actions/checkout@v2 8 | - uses: actions/setup-go@v2 9 | with: 10 | go-version: 1.23 11 | - run: DIFF="$( find . -name '*.go' -print0 | xargs -0 gofmt -l )"; if [ -n "$DIFF" ]; then echo "Files not formatted, run gofmt:"; echo "$DIFF"; exit 1; fi 12 | - run: sudo apt-get install libusb-1.0-0-dev 13 | - run: go install golang.org/x/tools/cmd/cover@latest 14 | - run: go install golang.org/x/lint/golint@latest 15 | - run: $HOME/go/bin/golint -set_exit_status ./... 16 | - run: sh ./.github/test-coverage.sh 17 | - uses: shogo82148/actions-goveralls@v1 18 | with: 19 | path-to-profile: coverage.merged 20 | ignore: libusb.go,error.go 21 | windows: 22 | runs-on: windows-2022 23 | steps: 24 | - uses: actions/checkout@v2 25 | - uses: actions/setup-go@v2 26 | with: 27 | go-version: 1.22 28 | - uses: msys2/setup-msys2@v2 29 | with: 30 | install: |- 31 | mingw64/mingw-w64-x86_64-libusb 32 | mingw64/mingw-w64-x86_64-pkg-config 33 | - run: echo "D:\a\_temp\msys64\mingw64\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append 34 | - run: go test ./... 35 | - run: go run ./lsusb 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.sw[op] 2 | .idea/ 3 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | This is the list of gousb authors for copyright purposes. 2 | # 3 | # This does not necessarily list everyone who has contributed code, since in 4 | # some cases, their employer may be the copyright holder. To see the full list 5 | # of contributors, see the revision history in source control. 6 | Google Inc. 7 | Kyle Lemons 8 | Sebastian Zagrodzki 9 | Pieter Joost van de Sande 10 | Ivan Krasin 11 | Jirawat I. 12 | Thordur Bjornsson 13 | Vincent Serpoul 14 | Josef Filzmaier 15 | Nico MT 16 | Deomid "rojer" Ryabkov 17 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Want to contribute? Great! First, read this page (including the small print at 2 | the end). 3 | 4 | ### Before you contribute 5 | Before we can use your code, you must sign the 6 | [Google Individual Contributor License Agreement] 7 | (https://cla.developers.google.com/about/google-individual) 8 | (CLA), which you can do online. The CLA is necessary mainly because you own the 9 | copyright to your changes, even after your contribution becomes part of our 10 | codebase, so we need your permission to use and distribute your code. We also 11 | need to be sure of various other things—for instance that you'll tell us if you 12 | know that your code infringes on other people's patents. You don't have to sign 13 | the CLA until after you've submitted your code for review and a member has 14 | approved it, but you must do it before we can put your code into our codebase. 15 | Before you start working on a larger contribution, you should get in touch with 16 | us first through the issue tracker with your idea so that we can help out and 17 | possibly guide you. Coordinating up front makes it much easier to avoid 18 | frustration later on. 19 | 20 | ### Code reviews 21 | All submissions, including submissions by project members, require review. We 22 | use Github pull requests for this purpose. 23 | 24 | ### The small print 25 | Contributions made by corporations are covered by a different agreement than 26 | the one above, the 27 | [Software Grant and Corporate Contributor License Agreement] 28 | (https://cla.developers.google.com/about/google-corporate). 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Introduction 2 | ============ 3 | 4 | [![Build Status][ciimg]][ci] 5 | [![GoDoc][docimg]][doc] 6 | [![Coverage Status][coverimg]][cover] 7 | 8 | 9 | The gousb package is an attempt at wrapping the libusb library into a Go-like binding. 10 | 11 | Supported platforms include: 12 | 13 | - linux 14 | - darwin 15 | - windows 16 | 17 | This is the release 2.0 of the package [github.com/kylelemons/gousb](https://github.com/kylelemons/gousb). 18 | Its API is not backwards-compatible with version 1.0. 19 | As of 2017-07-13 the 2.0 API is considered stable and 1.0 is deprecated. 20 | 21 | [coverimg]: https://coveralls.io/repos/github/google/gousb/badge.svg 22 | [cover]: https://coveralls.io/github/google/gousb 23 | [ciimg]: https://github.com/google/gousb/actions/workflows/build-and-test.yaml/badge.svg 24 | [ci]: https://github.com/google/gousb/actions/workflows/build-and-test.yaml 25 | [docimg]: https://godoc.org/github.com/google/gousb?status.svg 26 | [doc]: https://godoc.org/github.com/google/gousb 27 | 28 | Documentation 29 | ============= 30 | The documentation can be viewed via local godoc or via the excellent [godoc.org](http://godoc.org/): 31 | 32 | - [usb](http://godoc.org/github.com/google/gousb) 33 | - [usbid](http://godoc.org/pkg/github.com/google/gousb/usbid) 34 | 35 | Installation 36 | ============ 37 | 38 | Dependencies 39 | ------------ 40 | You must first install [libusb-1.0](https://github.com/libusb/libusb/wiki). This is pretty straightforward on linux and darwin. The cgo package should be able to find it if you install it in the default manner or use your distribution's package manager. How to tell cgo how to find one installed in a non-default place is beyond the scope of this README. 41 | 42 | *Note*: If you are installing this on darwin, you will probably need to run `fixlibusb_darwin.sh /usr/local/lib/libusb-1.0/libusb.h` because of an LLVM incompatibility. It shouldn't break C programs, though I haven't tried it in anger. 43 | 44 | Example: lsusb 45 | -------------- 46 | The gousb project provides a simple but useful example: lsusb. This binary will list the USB devices connected to your system and various interesting tidbits about them, their configurations, endpoints, etc. To install it, run the following command: 47 | 48 | go get -v github.com/google/gousb/lsusb 49 | 50 | gousb 51 | ----- 52 | If you installed the lsusb example, both libraries below are already installed. 53 | 54 | Installing the primary gousb package is really easy: 55 | 56 | go get -v github.com/google/gousb 57 | 58 | There is also a `usbid` package that will not be installed by default by this command, but which provides useful information including the human-readable vendor and product codes for detected hardware. It's not installed by default and not linked into the `gousb` package by default because it adds ~400kb to the resulting binary. If you want both, they can be installed thus: 59 | 60 | go get -v github.com/google/gousb{,/usbid} 61 | 62 | Notes for installation on Windows 63 | --------------------------------- 64 | 65 | You'll need: 66 | 67 | - Gcc - tested on [Win-Builds](http://win-builds.org/) and MSYS/MINGW 68 | - pkg-config - see http://www.mingw.org/wiki/FAQ, "How do I get pkg-config installed?" 69 | - [libusb-1.0](http://sourceforge.net/projects/libusb/files/libusb-1.0/). 70 | 71 | Make sure the `libusb-1.0.pc` pkg-config file from libusb was installed 72 | and that the result of the `pkg-config --cflags libusb-1.0` command shows the 73 | correct include path for installed libusb. 74 | 75 | After that you can continue with instructions for lsusb/gousb above. 76 | 77 | Contributing 78 | ============ 79 | Contributing to this project will require signing the [Google CLA][cla]. 80 | This is the same agreement that is required for contributing to Go itself, so if you have 81 | already filled it out for that, you needn't fill it out again. 82 | 83 | [cla]: https://cla.developers.google.com/ 84 | 85 | -------------------------------------------------------------------------------- /config.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Copyright 2016 the gousb Authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package gousb 17 | 18 | import ( 19 | "fmt" 20 | "sync" 21 | ) 22 | 23 | // ConfigDesc contains the information about a USB device configuration, 24 | // extracted from the device descriptor. 25 | type ConfigDesc struct { 26 | // Number is the configuration number. 27 | Number int 28 | // SelfPowered is true if the device is powered externally, i.e. not 29 | // drawing power from the USB bus. 30 | SelfPowered bool 31 | // RemoteWakeup is true if the device supports remote wakeup, i.e. 32 | // an external signal that will wake up a suspended USB device. An example 33 | // might be a keyboard that can wake up through a keypress after 34 | // the host put it in suspend mode. Note that gousb does not support 35 | // device power management, RemoteWakeup only refers to the reported device 36 | // capability. 37 | RemoteWakeup bool 38 | // MaxPower is the maximum current the device draws from the USB bus 39 | // in this configuration. 40 | MaxPower Milliamperes 41 | // Interfaces has a list of USB interfaces available in this configuration. 42 | Interfaces []InterfaceDesc 43 | 44 | iConfiguration int // index of a string descriptor describing this configuration 45 | } 46 | 47 | // String returns the human-readable description of the configuration descriptor. 48 | func (c ConfigDesc) String() string { 49 | return fmt.Sprintf("Configuration %d", c.Number) 50 | } 51 | 52 | func (c ConfigDesc) intfDesc(num int) (*InterfaceDesc, error) { 53 | // In an ideal world, interfaces in the descriptor would be numbered 54 | // contiguously starting from 0, as required by the specification. In the 55 | // real world however the specification is sometimes ignored: 56 | // https://github.com/google/gousb/issues/65 57 | ifs := make([]int, len(c.Interfaces)) 58 | for i, desc := range c.Interfaces { 59 | if desc.Number == num { 60 | return &desc, nil 61 | } 62 | ifs[i] = desc.Number 63 | } 64 | return nil, fmt.Errorf("interface %d not found, available interface numbers: %v", num, ifs) 65 | } 66 | 67 | // Config represents a USB device set to use a particular configuration. 68 | // Only one Config of a particular device can be used at any one time. 69 | // To access device endpoints, claim an interface and it's alternate 70 | // setting number through a call to Interface(). 71 | type Config struct { 72 | Desc ConfigDesc 73 | 74 | dev *Device 75 | 76 | // Claimed interfaces 77 | mu sync.Mutex 78 | claimed map[int]bool 79 | } 80 | 81 | // Close releases the underlying device, allowing the caller to switch the device to a different configuration. 82 | func (c *Config) Close() error { 83 | if c.dev == nil { 84 | return nil 85 | } 86 | c.mu.Lock() 87 | defer c.mu.Unlock() 88 | if len(c.claimed) > 0 { 89 | var ifs []int 90 | for k := range c.claimed { 91 | ifs = append(ifs, k) 92 | } 93 | return fmt.Errorf("failed to release %s, interfaces %v are still open", c, ifs) 94 | } 95 | c.dev.mu.Lock() 96 | defer c.dev.mu.Unlock() 97 | c.dev.claimed = nil 98 | c.dev = nil 99 | return nil 100 | } 101 | 102 | // String returns the human-readable description of the configuration. 103 | func (c *Config) String() string { 104 | return fmt.Sprintf("%s,config=%d", c.dev.String(), c.Desc.Number) 105 | } 106 | 107 | // Interface claims and returns an interface on a USB device. 108 | // num specifies the number of an interface to claim, and alt specifies the 109 | // alternate setting number for that interface. 110 | func (c *Config) Interface(num, alt int) (*Interface, error) { 111 | if c.dev == nil { 112 | return nil, fmt.Errorf("Interface(%d, %d) called on %s after Close", num, alt, c) 113 | } 114 | 115 | intf, err := c.Desc.intfDesc(num) 116 | if err != nil { 117 | return nil, fmt.Errorf("descriptor of interface %d in %s: %v", num, c, err) 118 | } 119 | altInfo, err := intf.altSetting(alt) 120 | if err != nil { 121 | return nil, fmt.Errorf("descriptor of alternate setting %d of interface %d in %s: %v", alt, num, c, err) 122 | } 123 | 124 | c.mu.Lock() 125 | defer c.mu.Unlock() 126 | if c.claimed[num] { 127 | return nil, fmt.Errorf("interface %d on %s is already claimed", num, c) 128 | } 129 | 130 | // Claim the interface 131 | if err := c.dev.ctx.libusb.claim(c.dev.handle, uint8(num)); err != nil { 132 | return nil, fmt.Errorf("failed to claim interface %d on %s: %v", num, c, err) 133 | } 134 | 135 | // Select an alternate setting if needed (device has multiple alternate settings). 136 | if len(intf.AltSettings) > 1 { 137 | if err := c.dev.ctx.libusb.setAlt(c.dev.handle, uint8(num), uint8(alt)); err != nil { 138 | c.dev.ctx.libusb.release(c.dev.handle, uint8(num)) 139 | return nil, fmt.Errorf("failed to set alternate config %d on interface %d of %s: %v", alt, num, c, err) 140 | } 141 | } 142 | 143 | c.claimed[num] = true 144 | return &Interface{ 145 | Setting: *altInfo, 146 | config: c, 147 | }, nil 148 | } 149 | -------------------------------------------------------------------------------- /constants.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Copyright 2016 the gousb Authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package gousb 17 | 18 | // #include 19 | import "C" 20 | import "strconv" 21 | 22 | // Class represents a USB-IF (Implementers Forum) class or subclass code. 23 | type Class uint8 24 | 25 | // Standard classes defined by USB spec, see https://www.usb.org/defined-class-codes 26 | const ( 27 | ClassPerInterface Class = 0x00 28 | ClassAudio Class = 0x01 29 | ClassComm Class = 0x02 30 | ClassHID Class = 0x03 31 | ClassPhysical Class = 0x05 32 | ClassImage Class = 0x06 33 | ClassPTP Class = ClassImage // legacy name for image 34 | ClassPrinter Class = 0x07 35 | ClassMassStorage Class = 0x08 36 | ClassHub Class = 0x09 37 | ClassData Class = 0x0a 38 | ClassSmartCard Class = 0x0b 39 | ClassContentSecurity Class = 0x0d 40 | ClassVideo Class = 0x0e 41 | ClassPersonalHealthcare Class = 0x0f 42 | ClassAudioVideo Class = 0x10 43 | ClassBillboard Class = 0x11 44 | ClassUSBTypeCBridge Class = 0x12 45 | ClassDiagnosticDevice Class = 0xdc 46 | ClassWireless Class = 0xe0 47 | ClassMiscellaneous Class = 0xef 48 | ClassApplication Class = 0xfe 49 | ClassVendorSpec Class = 0xff 50 | ) 51 | 52 | var classDescription = map[Class]string{ 53 | ClassPerInterface: "per-interface", 54 | ClassAudio: "audio", 55 | ClassComm: "communications", 56 | ClassHID: "human interface device", 57 | ClassPhysical: "physical", 58 | ClassImage: "image", 59 | ClassPrinter: "printer", 60 | ClassMassStorage: "mass storage", 61 | ClassHub: "hub", 62 | ClassData: "data", 63 | ClassSmartCard: "smart card", 64 | ClassContentSecurity: "content security", 65 | ClassVideo: "video", 66 | ClassPersonalHealthcare: "personal healthcare", 67 | ClassAudioVideo: "audio/video", 68 | ClassBillboard: "billboard", 69 | ClassUSBTypeCBridge: "USB type-C bridge", 70 | ClassDiagnosticDevice: "diagnostic device", 71 | ClassWireless: "wireless", 72 | ClassMiscellaneous: "miscellaneous", 73 | ClassApplication: "application-specific", 74 | ClassVendorSpec: "vendor-specific", 75 | } 76 | 77 | func (c Class) String() string { 78 | if d, ok := classDescription[c]; ok { 79 | return d 80 | } 81 | return strconv.Itoa(int(c)) 82 | } 83 | 84 | // Protocol is the interface class protocol, qualified by the values 85 | // of interface class and subclass. 86 | type Protocol uint8 87 | 88 | func (p Protocol) String() string { 89 | return strconv.Itoa(int(p)) 90 | } 91 | 92 | // DescriptorType identifies the type of a USB descriptor. 93 | type DescriptorType uint8 94 | 95 | // Descriptor types defined by the USB spec. 96 | const ( 97 | DescriptorTypeDevice DescriptorType = C.LIBUSB_DT_DEVICE 98 | DescriptorTypeConfig DescriptorType = C.LIBUSB_DT_CONFIG 99 | DescriptorTypeString DescriptorType = C.LIBUSB_DT_STRING 100 | DescriptorTypeInterface DescriptorType = C.LIBUSB_DT_INTERFACE 101 | DescriptorTypeEndpoint DescriptorType = C.LIBUSB_DT_ENDPOINT 102 | DescriptorTypeHID DescriptorType = C.LIBUSB_DT_HID 103 | DescriptorTypeReport DescriptorType = C.LIBUSB_DT_REPORT 104 | DescriptorTypePhysical DescriptorType = C.LIBUSB_DT_PHYSICAL 105 | DescriptorTypeHub DescriptorType = C.LIBUSB_DT_HUB 106 | ) 107 | 108 | var descriptorTypeDescription = map[DescriptorType]string{ 109 | DescriptorTypeDevice: "device", 110 | DescriptorTypeConfig: "configuration", 111 | DescriptorTypeString: "string", 112 | DescriptorTypeInterface: "interface", 113 | DescriptorTypeEndpoint: "endpoint", 114 | DescriptorTypeHID: "HID", 115 | DescriptorTypeReport: "HID report", 116 | DescriptorTypePhysical: "physical", 117 | DescriptorTypeHub: "hub", 118 | } 119 | 120 | func (dt DescriptorType) String() string { 121 | return descriptorTypeDescription[dt] 122 | } 123 | 124 | // EndpointDirection defines the direction of data flow - IN (device to host) 125 | // or OUT (host to device). 126 | type EndpointDirection bool 127 | 128 | const ( 129 | endpointNumMask = 0x0f 130 | endpointDirectionMask = 0x80 131 | // EndpointDirectionIn marks data flowing from device to host. 132 | EndpointDirectionIn EndpointDirection = true 133 | // EndpointDirectionOut marks data flowing from host to device. 134 | EndpointDirectionOut EndpointDirection = false 135 | ) 136 | 137 | var endpointDirectionDescription = map[EndpointDirection]string{ 138 | EndpointDirectionIn: "IN", 139 | EndpointDirectionOut: "OUT", 140 | } 141 | 142 | func (ed EndpointDirection) String() string { 143 | return endpointDirectionDescription[ed] 144 | } 145 | 146 | // TransferType defines the endpoint transfer type. 147 | type TransferType uint8 148 | 149 | // Transfer types defined by the USB spec. 150 | const ( 151 | TransferTypeControl TransferType = C.LIBUSB_TRANSFER_TYPE_CONTROL 152 | TransferTypeIsochronous TransferType = C.LIBUSB_TRANSFER_TYPE_ISOCHRONOUS 153 | TransferTypeBulk TransferType = C.LIBUSB_TRANSFER_TYPE_BULK 154 | TransferTypeInterrupt TransferType = C.LIBUSB_TRANSFER_TYPE_INTERRUPT 155 | transferTypeMask = 0x03 156 | ) 157 | 158 | var transferTypeDescription = map[TransferType]string{ 159 | TransferTypeControl: "control", 160 | TransferTypeIsochronous: "isochronous", 161 | TransferTypeBulk: "bulk", 162 | TransferTypeInterrupt: "interrupt", 163 | } 164 | 165 | // String returns a human-readable name of the endpoint transfer type. 166 | func (tt TransferType) String() string { 167 | return transferTypeDescription[tt] 168 | } 169 | 170 | // IsoSyncType defines the isochronous transfer synchronization type. 171 | type IsoSyncType uint8 172 | 173 | // Synchronization types defined by the USB spec. 174 | const ( 175 | IsoSyncTypeNone IsoSyncType = C.LIBUSB_ISO_SYNC_TYPE_NONE << 2 176 | IsoSyncTypeAsync IsoSyncType = C.LIBUSB_ISO_SYNC_TYPE_ASYNC << 2 177 | IsoSyncTypeAdaptive IsoSyncType = C.LIBUSB_ISO_SYNC_TYPE_ADAPTIVE << 2 178 | IsoSyncTypeSync IsoSyncType = C.LIBUSB_ISO_SYNC_TYPE_SYNC << 2 179 | isoSyncTypeMask = 0x0C 180 | ) 181 | 182 | var isoSyncTypeDescription = map[IsoSyncType]string{ 183 | IsoSyncTypeNone: "unsynchronized", 184 | IsoSyncTypeAsync: "asynchronous", 185 | IsoSyncTypeAdaptive: "adaptive", 186 | IsoSyncTypeSync: "synchronous", 187 | } 188 | 189 | // String returns a human-readable description of the synchronization type. 190 | func (ist IsoSyncType) String() string { 191 | return isoSyncTypeDescription[ist] 192 | } 193 | 194 | // UsageType defines the transfer usage type for isochronous and interrupt 195 | // transfers. 196 | type UsageType uint8 197 | 198 | // Usage types for iso and interrupt transfers, defined by the USB spec. 199 | const ( 200 | // Note: USB3.0 defines usage type for both isochronous and interrupt 201 | // endpoints, with the same constants representing different usage types. 202 | // UsageType constants do not correspond to bmAttribute values. 203 | UsageTypeUndefined UsageType = iota 204 | IsoUsageTypeData 205 | IsoUsageTypeFeedback 206 | IsoUsageTypeImplicit 207 | InterruptUsageTypePeriodic 208 | InterruptUsageTypeNotification 209 | usageTypeMask = 0x30 210 | ) 211 | 212 | var usageTypeDescription = map[UsageType]string{ 213 | UsageTypeUndefined: "undefined usage", 214 | IsoUsageTypeData: "data", 215 | IsoUsageTypeFeedback: "feedback", 216 | IsoUsageTypeImplicit: "implicit data", 217 | InterruptUsageTypePeriodic: "periodic", 218 | InterruptUsageTypeNotification: "notification", 219 | } 220 | 221 | func (ut UsageType) String() string { 222 | return usageTypeDescription[ut] 223 | } 224 | 225 | // Control request type bit fields as defined in the USB spec. All values are 226 | // of uint8 type. These constants can be used with Device.Control() method to 227 | // specify the type and destination of the control request, e.g. 228 | // `dev.Control(ControlOut|ControlVendor|ControlDevice, ...)`. 229 | const ( 230 | ControlIn = C.LIBUSB_ENDPOINT_IN 231 | ControlOut = C.LIBUSB_ENDPOINT_OUT 232 | 233 | // "Standard" is explicitly omitted, as functionality of standard requests 234 | // is exposed through higher level operations of gousb. 235 | ControlClass = C.LIBUSB_REQUEST_TYPE_CLASS 236 | ControlVendor = C.LIBUSB_REQUEST_TYPE_VENDOR 237 | // "Reserved" is explicitly omitted, should not be used. 238 | 239 | ControlDevice = C.LIBUSB_RECIPIENT_DEVICE 240 | ControlInterface = C.LIBUSB_RECIPIENT_INTERFACE 241 | ControlEndpoint = C.LIBUSB_RECIPIENT_ENDPOINT 242 | ControlOther = C.LIBUSB_RECIPIENT_OTHER 243 | ) 244 | 245 | // Speed identifies the speed of the device. 246 | type Speed int 247 | 248 | // Device speeds as defined in the USB spec. 249 | const ( 250 | SpeedUnknown Speed = C.LIBUSB_SPEED_UNKNOWN 251 | SpeedLow Speed = C.LIBUSB_SPEED_LOW 252 | SpeedFull Speed = C.LIBUSB_SPEED_FULL 253 | SpeedHigh Speed = C.LIBUSB_SPEED_HIGH 254 | SpeedSuper Speed = C.LIBUSB_SPEED_SUPER 255 | ) 256 | 257 | var deviceSpeedDescription = map[Speed]string{ 258 | SpeedUnknown: "unknown", 259 | SpeedLow: "low", 260 | SpeedFull: "full", 261 | SpeedHigh: "high", 262 | SpeedSuper: "super", 263 | } 264 | 265 | // String returns a human-readable name of the device speed. 266 | func (s Speed) String() string { 267 | return deviceSpeedDescription[s] 268 | } 269 | 270 | const ( 271 | selfPoweredMask = 0x40 272 | remoteWakeupMask = 0x20 273 | ) 274 | 275 | // Milliamperes is a unit of electric current consumption. 276 | type Milliamperes uint 277 | -------------------------------------------------------------------------------- /debug.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Copyright 2016 the gousb Authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package gousb 17 | 18 | // To enable internal debugging, set the GOUSB_DEBUG environment variable. 19 | 20 | import ( 21 | "io" 22 | "io/ioutil" 23 | "log" // TODO(kevlar): make a logger 24 | "os" 25 | ) 26 | 27 | var debug *log.Logger 28 | 29 | const debugEnvVarName = "GOUSB_DEBUG" 30 | 31 | func init() { 32 | out := io.Writer(ioutil.Discard) 33 | if os.Getenv(debugEnvVarName) != "" { 34 | out = os.Stderr 35 | } 36 | debug = log.New(out, "gousb: ", log.LstdFlags|log.Lshortfile) 37 | } 38 | -------------------------------------------------------------------------------- /device.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Copyright 2016 the gousb Authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package gousb 17 | 18 | import ( 19 | "fmt" 20 | "sort" 21 | "sync" 22 | "time" 23 | ) 24 | 25 | // DeviceDesc is a representation of a USB device descriptor. 26 | type DeviceDesc struct { 27 | // The bus on which the device was detected 28 | Bus int 29 | // The address of the device on the bus 30 | Address int 31 | // The negotiated operating speed for the device 32 | Speed Speed 33 | // The pyhsical port on the parent hub on which the device is connected. 34 | // Ports are numbered from 1, excepting root hub devices which are always 0. 35 | Port int 36 | // Physical path of connected parent ports, starting at the root hub device. 37 | // A path length of 0 represents a root hub device, 38 | // a path length of 1 represents a device directly connected to a root hub, 39 | // a path length of 2 or more are connected to intermediate hub devices. 40 | // e.g. [1,2,3] represents a device connected to port 3 of a hub connected 41 | // to port 2 of a hub connected to port 1 of a root hub. 42 | Path []int 43 | 44 | // Version information 45 | Spec BCD // USB Specification Release Number 46 | Device BCD // The device version 47 | 48 | // Product information 49 | Vendor ID // The Vendor identifer 50 | Product ID // The Product identifier 51 | 52 | // Protocol information 53 | Class Class // The class of this device 54 | SubClass Class // The sub-class (within the class) of this device 55 | Protocol Protocol // The protocol (within the sub-class) of this device 56 | MaxControlPacketSize int // Maximum size of the control transfer 57 | 58 | // Configuration information 59 | Configs map[int]ConfigDesc 60 | 61 | iManufacturer int // The Manufacturer descriptor index 62 | iProduct int // The Product descriptor index 63 | iSerialNumber int // The SerialNumber descriptor index 64 | } 65 | 66 | // String returns a human-readable version of the device descriptor. 67 | func (d *DeviceDesc) String() string { 68 | return fmt.Sprintf("%d.%d: %s:%s (available configs: %v)", d.Bus, d.Address, d.Vendor, d.Product, d.sortedConfigIds()) 69 | } 70 | 71 | func (d *DeviceDesc) sortedConfigIds() []int { 72 | var cfgs []int 73 | for c := range d.Configs { 74 | cfgs = append(cfgs, c) 75 | } 76 | sort.Ints(cfgs) 77 | return cfgs 78 | } 79 | 80 | func (d *DeviceDesc) cfgDesc(cfgNum int) (*ConfigDesc, error) { 81 | desc, ok := d.Configs[cfgNum] 82 | if !ok { 83 | return nil, fmt.Errorf("configuration id %d not found in the descriptor of the device. Available config ids: %v", cfgNum, d.sortedConfigIds()) 84 | } 85 | return &desc, nil 86 | } 87 | 88 | // Device represents an opened USB device. 89 | // Device allows sending USB control commands through the Command() method. 90 | // For data transfers select a device configuration through a call to 91 | // Config(). 92 | // A Device must be Close()d after use. 93 | type Device struct { 94 | handle *libusbDevHandle 95 | ctx *Context 96 | 97 | // Embed the device information for easy access 98 | Desc *DeviceDesc 99 | // Timeout for control commands 100 | ControlTimeout time.Duration 101 | 102 | // Claimed config 103 | mu sync.Mutex 104 | claimed *Config 105 | 106 | // Handle AutoDetach in this library 107 | autodetach bool 108 | } 109 | 110 | // String represents a human readable representation of the device. 111 | func (d *Device) String() string { 112 | return fmt.Sprintf("vid=%s,pid=%s,bus=%d,addr=%d", d.Desc.Vendor, d.Desc.Product, d.Desc.Bus, d.Desc.Address) 113 | } 114 | 115 | // Reset performs a USB port reset to reinitialize a device. 116 | func (d *Device) Reset() error { 117 | if d.handle == nil { 118 | return fmt.Errorf("Reset() called on %s after Close", d) 119 | } 120 | d.mu.Lock() 121 | defer d.mu.Unlock() 122 | if d.claimed != nil { 123 | return fmt.Errorf("can't reset device %s while it has an active configuration %s", d, d.claimed) 124 | } 125 | return d.ctx.libusb.reset(d.handle) 126 | } 127 | 128 | // ActiveConfigNum returns the config id of the active configuration. 129 | // The value corresponds to the ConfigInfo.Config field of one of the 130 | // ConfigInfos of this Device. 131 | func (d *Device) ActiveConfigNum() (int, error) { 132 | if d.handle == nil { 133 | return 0, fmt.Errorf("ActiveConfig() called on %s after Close", d) 134 | } 135 | ret, err := d.ctx.libusb.getConfig(d.handle) 136 | return int(ret), err 137 | } 138 | 139 | // Config returns a USB device set to use a particular config. 140 | // The cfgNum provided is the config id (not the index) of the configuration to 141 | // set, which corresponds to the ConfigInfo.Config field. 142 | // USB supports only one active config per device at a time. Config claims the 143 | // device before setting the desired config and keeps it locked until Close is 144 | // called. 145 | // A claimed config needs to be Close()d after use. 146 | func (d *Device) Config(cfgNum int) (*Config, error) { 147 | if d.handle == nil { 148 | return nil, fmt.Errorf("Config(%d) called on %s after Close", cfgNum, d) 149 | } 150 | desc, err := d.Desc.cfgDesc(cfgNum) 151 | if err != nil { 152 | return nil, fmt.Errorf("device %s: %v", d, err) 153 | } 154 | cfg := &Config{ 155 | Desc: *desc, 156 | dev: d, 157 | claimed: make(map[int]bool), 158 | } 159 | 160 | if d.autodetach { 161 | for _, iface := range cfg.Desc.Interfaces { 162 | if err := d.ctx.libusb.detachKernelDriver(d.handle, uint8(iface.Number)); err != nil { 163 | return nil, fmt.Errorf("Can't detach kernel driver of the device %s and interface %d: %v", d, iface.Number, err) 164 | } 165 | } 166 | } 167 | 168 | if activeCfgNum, err := d.ActiveConfigNum(); err != nil { 169 | return nil, fmt.Errorf("failed to query active config of the device %s: %v", d, err) 170 | } else if cfgNum != activeCfgNum { 171 | if err := d.ctx.libusb.setConfig(d.handle, uint8(cfgNum)); err != nil { 172 | return nil, fmt.Errorf("failed to set active config %d for the device %s: %v", cfgNum, d, err) 173 | } 174 | } 175 | d.mu.Lock() 176 | defer d.mu.Unlock() 177 | d.claimed = cfg 178 | return cfg, nil 179 | } 180 | 181 | // DefaultInterface opens interface #0 with alternate setting #0 of the currently active 182 | // config. It's intended as a shortcut for devices that have the simplest 183 | // interface of a single config, interface and alternate setting. 184 | // The done func should be called to release the claimed interface and config. 185 | func (d *Device) DefaultInterface() (intf *Interface, done func(), err error) { 186 | cfgNum, err := d.ActiveConfigNum() 187 | if err != nil { 188 | return nil, nil, fmt.Errorf("failed to get active config number of device %s: %v", d, err) 189 | } 190 | cfg, err := d.Config(cfgNum) 191 | if err != nil { 192 | return nil, nil, fmt.Errorf("failed to claim config %d of device %s: %v", cfgNum, d, err) 193 | } 194 | i, err := cfg.Interface(0, 0) 195 | if err != nil { 196 | cfg.Close() 197 | return nil, nil, fmt.Errorf("failed to select interface #%d alternate setting %d of config %d of device %s: %v", 0, 0, cfgNum, d, err) 198 | } 199 | return i, func() { 200 | intf.Close() 201 | cfg.Close() 202 | }, nil 203 | } 204 | 205 | // Control sends a control request to the device. 206 | func (d *Device) Control(rType, request uint8, val, idx uint16, data []byte) (int, error) { 207 | if d.handle == nil { 208 | return 0, fmt.Errorf("Control() called on %s after Close", d) 209 | } 210 | return d.ctx.libusb.control(d.handle, d.ControlTimeout, rType, request, val, idx, data) 211 | } 212 | 213 | // Close closes the device. 214 | func (d *Device) Close() error { 215 | if d.handle == nil { 216 | return nil 217 | } 218 | d.mu.Lock() 219 | defer d.mu.Unlock() 220 | if d.claimed != nil { 221 | return fmt.Errorf("can't release the device %s, it has an open config %d", d, d.claimed.Desc.Number) 222 | } 223 | d.ctx.closeDev(d) 224 | d.handle = nil 225 | return nil 226 | } 227 | 228 | // GetStringDescriptor returns a device string descriptor with the given index 229 | // number. The first supported language is always used and the returned 230 | // descriptor string is converted to ASCII (non-ASCII characters are replaced 231 | // with "?"). 232 | func (d *Device) GetStringDescriptor(descIndex int) (string, error) { 233 | if d.handle == nil { 234 | return "", fmt.Errorf("GetStringDescriptor(%d) called on %s after Close", descIndex, d) 235 | } 236 | // string descriptor index value of 0 indicates no string descriptor. 237 | if descIndex == 0 { 238 | return "", nil 239 | } 240 | return d.ctx.libusb.getStringDesc(d.handle, descIndex) 241 | } 242 | 243 | // Manufacturer returns the device's manufacturer name. 244 | // GetStringDescriptor's string conversion rules apply. 245 | func (d *Device) Manufacturer() (string, error) { 246 | return d.GetStringDescriptor(d.Desc.iManufacturer) 247 | } 248 | 249 | // Product returns the device's product name. 250 | // GetStringDescriptor's string conversion rules apply. 251 | func (d *Device) Product() (string, error) { 252 | return d.GetStringDescriptor(d.Desc.iProduct) 253 | } 254 | 255 | // SerialNumber returns the device's serial number. 256 | // GetStringDescriptor's string conversion rules apply. 257 | func (d *Device) SerialNumber() (string, error) { 258 | return d.GetStringDescriptor(d.Desc.iSerialNumber) 259 | } 260 | 261 | // ConfigDescription returns the description of the selected device 262 | // configuration. GetStringDescriptor's string conversion rules apply. 263 | func (d *Device) ConfigDescription(cfg int) (string, error) { 264 | c, err := d.Desc.cfgDesc(cfg) 265 | if err != nil { 266 | return "", fmt.Errorf("%s: %v", d, err) 267 | } 268 | return d.GetStringDescriptor(c.iConfiguration) 269 | } 270 | 271 | // InterfaceDescription returns the description of the selected interface and 272 | // its alternate setting in a selected configuration. GetStringDescriptor's 273 | // string conversion rules apply. 274 | func (d *Device) InterfaceDescription(cfgNum, intfNum, altNum int) (string, error) { 275 | cfg, err := d.Desc.cfgDesc(cfgNum) 276 | if err != nil { 277 | return "", fmt.Errorf("%s: %v", d, err) 278 | } 279 | intf, err := cfg.intfDesc(intfNum) 280 | if err != nil { 281 | return "", fmt.Errorf("%s, configuration %d interface %d: %v", d, cfgNum, intfNum, err) 282 | } 283 | alt, err := intf.altSetting(altNum) 284 | if err != nil { 285 | return "", fmt.Errorf("%s, configuration %d interface %d alternate setting %d: %v", d, cfgNum, intfNum, altNum, err) 286 | } 287 | return d.GetStringDescriptor(alt.iInterface) 288 | } 289 | 290 | // SetAutoDetach enables/disables automatic kernel driver detachment. 291 | // When autodetach is enabled gousb will automatically detach the kernel driver 292 | // on the interface and reattach it when releasing the interface. 293 | // Automatic kernel driver detachment is disabled on newly opened device handles by default. 294 | func (d *Device) SetAutoDetach(autodetach bool) error { 295 | if d.handle == nil { 296 | return fmt.Errorf("SetAutoDetach(%v) called on %s after Close", autodetach, d) 297 | } 298 | d.autodetach = autodetach 299 | var autodetachInt int 300 | if autodetach { 301 | autodetachInt = 1 302 | } 303 | return d.ctx.libusb.setAutoDetach(d.handle, autodetachInt) 304 | } 305 | -------------------------------------------------------------------------------- /device_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 the gousb Authors. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package gousb 16 | 17 | import ( 18 | "errors" 19 | "reflect" 20 | "testing" 21 | ) 22 | 23 | func TestClaimAndRelease(t *testing.T) { 24 | t.Parallel() 25 | const ( 26 | devIdx = 1 27 | cfgNum = 1 28 | if1Num = 1 29 | alt1Num = 1 30 | ep1Addr = 0x86 31 | alt2Num = 0 32 | if2Num = 0 33 | ) 34 | 35 | c := newContextWithImpl(newFakeLibusb()) 36 | defer func() { 37 | if err := c.Close(); err != nil { 38 | t.Errorf("Context.Close: %v", err) 39 | } 40 | }() 41 | 42 | dev, err := c.OpenDeviceWithVIDPID(0x8888, 0x0002) 43 | if dev == nil { 44 | t.Fatal("OpenDeviceWithVIDPID(0x8888, 0x0002): got nil device, need non-nil") 45 | } 46 | defer dev.Close() 47 | if err != nil { 48 | t.Fatalf("OpenDeviceWithVIDPID(0x8888, 0x0002): %v", err) 49 | } 50 | 51 | if mfg, err := dev.Manufacturer(); err != nil { 52 | t.Errorf("%s.Manufacturer(): error %v", dev, err) 53 | } else if want := "ACME Industries"; mfg != want { 54 | t.Errorf("%s.Manufacturer(): %q, want %q", dev, mfg, want) 55 | } 56 | if prod, err := dev.Product(); err != nil { 57 | t.Errorf("%s.Product(): error %v", dev, err) 58 | } else if want := "Fidgety Gadget"; prod != want { 59 | t.Errorf("%s.Product(): %q, want %q", dev, prod, want) 60 | } 61 | if sn, err := dev.SerialNumber(); err != nil { 62 | t.Errorf("%s.SerialNumber(): error %v", dev, err) 63 | } else if want := "01234567"; sn != want { 64 | t.Errorf("%s.SerialNumber(): %q, want %q", dev, sn, want) 65 | } 66 | 67 | if got, err := dev.ConfigDescription(1); err != nil { 68 | t.Errorf("%s.ConfigDescription(1): %v", dev, err) 69 | } else if want := "Weird configuration"; got != want { 70 | t.Errorf("%s.ConfigDescription(1): %q, want %q", dev, got, want) 71 | } 72 | if got, err := dev.ConfigDescription(2); err == nil { 73 | t.Errorf("%s.ConfigDescription(2): %q, want error", dev, got) 74 | } 75 | 76 | for _, tc := range []struct { 77 | intf, alt int 78 | want string 79 | }{ 80 | {0, 0, "Boring setting"}, 81 | {1, 0, "Fast streaming"}, 82 | {1, 1, "Slower streaming"}, 83 | {1, 2, ""}, 84 | {3, 2, "Interface for https://github.com/google/gousb/issues/65"}, 85 | } { 86 | if got, err := dev.InterfaceDescription(1, tc.intf, tc.alt); err != nil { 87 | t.Errorf("%s.InterfaceDescription(1, %d, %d): %v", dev, tc.intf, tc.alt, err) 88 | } else if got != tc.want { 89 | t.Errorf("%s.InterfaceDescription(1, %d, %d): %q, want %q", dev, tc.intf, tc.alt, got, tc.want) 90 | } 91 | } 92 | 93 | if err = dev.SetAutoDetach(true); err != nil { 94 | t.Fatalf("%s.SetAutoDetach(true): %v", dev, err) 95 | } 96 | cfg, err := dev.Config(cfgNum) 97 | if err != nil { 98 | t.Fatalf("%s.Config(1): %v", dev, err) 99 | } 100 | defer cfg.Close() 101 | if got, err := dev.ActiveConfigNum(); err != nil { 102 | t.Errorf("%s.ActiveConfigNum(): got error %v, want nil", dev, err) 103 | } else if got != cfgNum { 104 | t.Errorf("%s.ActiveConfigNum(): got %d, want %d", dev, got, cfgNum) 105 | } 106 | 107 | intf, err := cfg.Interface(if1Num, alt1Num) 108 | if err != nil { 109 | t.Fatalf("%s.Interface(%d, %d): %v", cfg, if1Num, alt1Num, err) 110 | } 111 | defer intf.Close() 112 | got, err := intf.InEndpoint(ep1Addr) 113 | if err != nil { 114 | t.Fatalf("%s.InEndpoint(%d): got error %v, want nil", intf, ep1Addr, err) 115 | } 116 | if want := fakeDevices[devIdx].devDesc.Configs[cfgNum].Interfaces[if1Num].AltSettings[alt1Num].Endpoints[ep1Addr]; !reflect.DeepEqual(got.Desc, want) { 117 | t.Errorf("%s.InEndpoint(%d): got %+v, want %+v", intf, ep1Addr, got, want) 118 | } 119 | 120 | if _, err := cfg.Interface(if1Num, 0); err == nil { 121 | t.Fatalf("%s.Interface(1, 0): got nil, want non nil, because Interface 1 is already claimed.", cfg) 122 | } 123 | 124 | // intf2 is interface #0, not claimed yet. 125 | intf2, err := cfg.Interface(if2Num, alt2Num) 126 | if err != nil { 127 | t.Fatalf("%s.Interface(0, 0): got %v, want nil", cfg, err) 128 | } 129 | 130 | if err := cfg.Close(); err == nil { 131 | t.Fatalf("%s.Close(): got nil, want non nil, because the Interfaces #1/2 was not released.", cfg) 132 | } 133 | if err := dev.Close(); err == nil { 134 | t.Fatalf("%s.Close(): got nil, want non nil, because the Config was not released.", cfg) 135 | } 136 | 137 | intf.Close() 138 | if err := cfg.Close(); err == nil { 139 | t.Fatalf("%s.Close(): got nil, want non nil, because the Interface #2 was not released.", cfg) 140 | } 141 | if err := dev.Close(); err == nil { 142 | t.Fatalf("%s.Close(): got nil, want non nil, because the Config was not released.", dev) 143 | } 144 | 145 | intf2.Close() 146 | if err := dev.Close(); err == nil { 147 | t.Fatalf("%s.Close(): got nil, want non nil, because the Config was not released.", dev) 148 | } 149 | 150 | if err := dev.Reset(); err == nil { 151 | t.Fatalf("%s.Reset(): got nil, want non nil, because Device is still has an active Config.", dev) 152 | } 153 | 154 | if err := cfg.Close(); err != nil { 155 | t.Fatalf("%s.Close(): got error %v, want nil", cfg, err) 156 | } 157 | 158 | if err := dev.Reset(); err != nil { 159 | t.Fatalf("%s.Reset(): got error %v, want nil", dev, err) 160 | } 161 | 162 | if err := dev.Close(); err != nil { 163 | t.Fatalf("%s.Close(): got error %v, want nil", dev, err) 164 | } 165 | 166 | if _, err := dev.Manufacturer(); err == nil { 167 | t.Errorf("%s.Manufacturer(): expected an error after device is closed", dev) 168 | } 169 | 170 | if _, err := dev.Config(cfgNum); err == nil { 171 | t.Fatalf("%s.Config(1): got error nil, want no nil because it is closed", dev) 172 | } 173 | 174 | if err := dev.Reset(); err == nil { 175 | t.Fatalf("%s.Reset(): got error nil, want no nil because it is closed", dev) 176 | } 177 | 178 | if err := dev.SetAutoDetach(false); err == nil { 179 | t.Fatalf("%s.SetAutoDetach(false): got error nil, want no nil because it is closed", dev) 180 | } 181 | } 182 | 183 | func TestInterfaceDescriptionError(t *testing.T) { 184 | t.Parallel() 185 | for _, tc := range []struct { 186 | name string 187 | cfg, intf, alt int 188 | }{ 189 | {"no config", 2, 1, 1}, 190 | {"no interface", 1, 3, 1}, 191 | {"no alt setting", 1, 1, 5}, 192 | } { 193 | tc := tc 194 | t.Run(tc.name, func(t *testing.T) { 195 | t.Parallel() 196 | c := newContextWithImpl(newFakeLibusb()) 197 | defer func() { 198 | if err := c.Close(); err != nil { 199 | t.Errorf("Context.Close(): %v", err) 200 | } 201 | }() 202 | dev, err := c.OpenDeviceWithVIDPID(0x8888, 0x0002) 203 | if dev == nil { 204 | t.Fatal("OpenDeviceWithVIDPID(0x8888, 0x0002): got nil device, need non-nil") 205 | } 206 | defer dev.Close() 207 | if err != nil { 208 | t.Fatalf("OpenDeviceWithVIDPID(0x8888, 0x0002): %v", err) 209 | } 210 | if desc, err := dev.InterfaceDescription(tc.cfg, tc.intf, tc.alt); err == nil { 211 | t.Errorf("%s.InterfaceDescriptor(%d, %d, %d): %q, want error", dev, tc.cfg, tc.intf, tc.alt, desc) 212 | } 213 | }) 214 | } 215 | } 216 | 217 | type failDetachLib struct { 218 | *fakeLibusb 219 | } 220 | 221 | func (*failDetachLib) detachKernelDriver(h *libusbDevHandle, i uint8) error { 222 | if i == 1 { 223 | return errors.New("test detach fail") 224 | } 225 | return nil 226 | } 227 | 228 | func TestAutoDetachFailure(t *testing.T) { 229 | t.Parallel() 230 | fake := newFakeLibusb() 231 | c := newContextWithImpl(&failDetachLib{fake}) 232 | defer c.Close() 233 | dev, err := c.OpenDeviceWithVIDPID(0x8888, 0x0002) 234 | if dev == nil { 235 | t.Fatal("OpenDeviceWithVIDPID(0x8888, 0x0002): got nil device, need non-nil") 236 | } 237 | defer dev.Close() 238 | if err != nil { 239 | t.Fatalf("OpenDeviceWithVIDPID(0x8888, 0x0002): %v", err) 240 | } 241 | dev.SetAutoDetach(true) 242 | _, err = dev.Config(1) 243 | if err == nil { 244 | t.Fatalf("%s.Config(1) got nil, but want no nil because interface fails to detach", dev) 245 | } 246 | } 247 | 248 | func TestInterface(t *testing.T) { 249 | t.Parallel() 250 | c := newContextWithImpl(newFakeLibusb()) 251 | defer func() { 252 | if err := c.Close(); err != nil { 253 | t.Errorf("Context.Close: %v", err) 254 | } 255 | }() 256 | 257 | for _, tc := range []struct { 258 | name string 259 | vid, pid ID 260 | cfgNum, ifNum, altNum int 261 | }{{ 262 | name: "simple", 263 | vid: 0x8888, 264 | pid: 0x0002, 265 | cfgNum: 1, 266 | ifNum: 0, 267 | altNum: 0, 268 | }, { 269 | name: "alt_setting", 270 | vid: 0x8888, 271 | pid: 0x0002, 272 | cfgNum: 1, 273 | ifNum: 1, 274 | altNum: 1, 275 | }, { 276 | name: "noncontiguous_interfaces", 277 | vid: 0x8888, 278 | pid: 0x0002, 279 | cfgNum: 1, 280 | ifNum: 3, 281 | altNum: 2, 282 | }} { 283 | t.Run(tc.name, func(t *testing.T) { 284 | dev, err := c.OpenDeviceWithVIDPID(0x8888, 0x0002) 285 | if err != nil { 286 | t.Fatalf("OpenDeviceWithVIDPID(0x8888, 0x0002): %v", err) 287 | } 288 | if dev == nil { 289 | t.Fatal("OpenDeviceWithVIDPID(0x8888, 0x0002): got nil device, need non-nil") 290 | } 291 | defer dev.Close() 292 | cfg, err := dev.Config(tc.cfgNum) 293 | if err != nil { 294 | t.Fatalf("%s.Config(%d): %v", dev, tc.cfgNum, err) 295 | } 296 | defer cfg.Close() 297 | intf, err := cfg.Interface(tc.ifNum, tc.altNum) 298 | if err != nil { 299 | t.Fatalf("%s.Interface(%d, %d): %v", cfg, tc.ifNum, tc.altNum, err) 300 | } 301 | intf.Close() 302 | }) 303 | } 304 | } 305 | -------------------------------------------------------------------------------- /endpoint.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Copyright 2016 the gousb Authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package gousb 17 | 18 | import ( 19 | "context" 20 | "fmt" 21 | "strings" 22 | "time" 23 | ) 24 | 25 | // EndpointAddress is a unique identifier for the endpoint, combining the endpoint number and direction. 26 | type EndpointAddress uint8 27 | 28 | // String implements the Stringer interface. 29 | func (a EndpointAddress) String() string { 30 | return fmt.Sprintf("0x%02x", uint8(a)) 31 | } 32 | 33 | // EndpointDesc contains the information about an interface endpoint, extracted 34 | // from the descriptor. 35 | type EndpointDesc struct { 36 | // Address is the unique identifier of the endpoint within the interface. 37 | Address EndpointAddress 38 | // Number represents the endpoint number. Note that the endpoint number is different from the 39 | // address field in the descriptor - address 0x82 means endpoint number 2, 40 | // with endpoint direction IN. 41 | // The device can have up to two endpoints with the same number but with 42 | // different directions. 43 | Number int 44 | // Direction defines whether the data is flowing IN or OUT from the host perspective. 45 | Direction EndpointDirection 46 | // MaxPacketSize is the maximum USB packet size for a single frame/microframe. 47 | MaxPacketSize int 48 | // TransferType defines the endpoint type - bulk, interrupt, isochronous. 49 | TransferType TransferType 50 | // PollInterval is the maximum time between transfers for interrupt and isochronous transfer, 51 | // or the NAK interval for a control transfer. See endpoint descriptor bInterval documentation 52 | // in the USB spec for details. 53 | PollInterval time.Duration 54 | // IsoSyncType is the isochronous endpoint synchronization type, as defined by USB spec. 55 | IsoSyncType IsoSyncType 56 | // UsageType is the isochronous or interrupt endpoint usage type, as defined by USB spec. 57 | UsageType UsageType 58 | } 59 | 60 | // String returns the human-readable description of the endpoint. 61 | func (e EndpointDesc) String() string { 62 | ret := make([]string, 0, 3) 63 | ret = append(ret, fmt.Sprintf("ep #%d %s (address %s) %s", e.Number, e.Direction, e.Address, e.TransferType)) 64 | switch e.TransferType { 65 | case TransferTypeIsochronous: 66 | ret = append(ret, fmt.Sprintf("- %s %s", e.IsoSyncType, e.UsageType)) 67 | case TransferTypeInterrupt: 68 | ret = append(ret, fmt.Sprintf("- %s", e.UsageType)) 69 | } 70 | ret = append(ret, fmt.Sprintf("[%d bytes]", e.MaxPacketSize)) 71 | return strings.Join(ret, " ") 72 | } 73 | 74 | type endpoint struct { 75 | h *libusbDevHandle 76 | 77 | InterfaceSetting 78 | Desc EndpointDesc 79 | 80 | ctx *Context 81 | } 82 | 83 | // String returns a human-readable description of the endpoint. 84 | func (e *endpoint) String() string { 85 | return e.Desc.String() 86 | } 87 | 88 | func (e *endpoint) transfer(ctx context.Context, buf []byte) (int, error) { 89 | t, err := newUSBTransfer(e.ctx, e.h, &e.Desc, len(buf)) 90 | if err != nil { 91 | return 0, err 92 | } 93 | defer t.free() 94 | if e.Desc.Direction == EndpointDirectionOut { 95 | copy(t.data(), buf) 96 | } 97 | 98 | if err := t.submit(); err != nil { 99 | return 0, err 100 | } 101 | 102 | n, err := t.wait(ctx) 103 | if e.Desc.Direction == EndpointDirectionIn { 104 | copy(buf, t.data()) 105 | } 106 | if err != nil { 107 | return n, err 108 | } 109 | return n, nil 110 | } 111 | 112 | // InEndpoint represents an IN endpoint open for transfer. 113 | // InEndpoint implements the io.Reader interface. 114 | // For high-throughput transfers, consider creating a buffered read stream 115 | // through InEndpoint.ReadStream. 116 | type InEndpoint struct { 117 | *endpoint 118 | } 119 | 120 | // Read reads data from an IN endpoint. Read returns number of bytes obtained 121 | // from the endpoint. Read may return non-zero length even if 122 | // the returned error is not nil (partial read). 123 | // It's recommended to use buffer sizes that are multiples of 124 | // EndpointDesc.MaxPacketSize to avoid overflows. 125 | // When a USB device receives a read request, it doesn't know the size of the 126 | // buffer and may send too much data in one packet to fit in the buffer. 127 | // If that happens, Read will return an error signaling an overflow. 128 | // See http://libusb.sourceforge.net/api-1.0/libusb_packetoverflow.html 129 | // for more details. 130 | func (e *InEndpoint) Read(buf []byte) (int, error) { 131 | return e.transfer(context.Background(), buf) 132 | } 133 | 134 | // ReadContext reads data from an IN endpoint. ReadContext returns number of 135 | // bytes obtained from the endpoint. ReadContext may return non-zero length 136 | // even if the returned error is not nil (partial read). 137 | // The passed context can be used to control the cancellation of the read. If 138 | // the context is cancelled, ReadContext will cancel the underlying transfers, 139 | // resulting in TransferCancelled error. 140 | // It's recommended to use buffer sizes that are multiples of 141 | // EndpointDesc.MaxPacketSize to avoid overflows. 142 | // When a USB device receives a read request, it doesn't know the size of the 143 | // buffer and may send too much data in one packet to fit in the buffer. 144 | // If that happens, Read will return an error signaling an overflow. 145 | // See http://libusb.sourceforge.net/api-1.0/libusb_packetoverflow.html 146 | // for more details. 147 | func (e *InEndpoint) ReadContext(ctx context.Context, buf []byte) (int, error) { 148 | return e.transfer(ctx, buf) 149 | } 150 | 151 | // OutEndpoint represents an OUT endpoint open for transfer. 152 | type OutEndpoint struct { 153 | *endpoint 154 | } 155 | 156 | // Write writes data to an OUT endpoint. Write returns number of bytes comitted 157 | // to the endpoint. Write may return non-zero length even if the returned error 158 | // is not nil (partial write). 159 | func (e *OutEndpoint) Write(buf []byte) (int, error) { 160 | return e.transfer(context.Background(), buf) 161 | } 162 | 163 | // WriteContext writes data to an OUT endpoint. WriteContext returns number of 164 | // bytes comitted to the endpoint. WriteContext may return non-zero length even 165 | // if the returned error is not nil (partial write). 166 | // The passed context can be used to control the cancellation of the write. If 167 | // the context is cancelled, WriteContext will cancel the underlying transfers, 168 | // resulting in TransferCancelled error. 169 | func (e *OutEndpoint) WriteContext(ctx context.Context, buf []byte) (int, error) { 170 | return e.transfer(ctx, buf) 171 | } 172 | -------------------------------------------------------------------------------- /endpoint_stream.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 the gousb Authors. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package gousb 16 | 17 | func (e *endpoint) newStream(size, count int) (*stream, error) { 18 | var ts []transferIntf 19 | for i := 0; i < count; i++ { 20 | t, err := newUSBTransfer(e.ctx, e.h, &e.Desc, size) 21 | if err != nil { 22 | for _, t := range ts { 23 | t.free() 24 | } 25 | return nil, err 26 | } 27 | ts = append(ts, t) 28 | } 29 | return newStream(ts), nil 30 | } 31 | 32 | // NewStream prepares a new read stream that will keep reading data from 33 | // the endpoint until closed or until an error or timeout is encountered. 34 | // Size defines a buffer size for a single read transaction and count 35 | // defines how many transactions should be active at any time. 36 | // By keeping multiple transfers active at the same time, a Stream reduces 37 | // the latency between subsequent transfers and increases reading throughput. 38 | // Similarly to InEndpoint.Read, the size of the buffer should be a multiple 39 | // of EndpointDesc.MaxPacketSize to avoid overflows, see documentation 40 | // in InEndpoint.Read for more details. 41 | func (e *InEndpoint) NewStream(size, count int) (*ReadStream, error) { 42 | s, err := e.newStream(size, count) 43 | if err != nil { 44 | return nil, err 45 | } 46 | s.submitAll() 47 | return &ReadStream{s: s}, nil 48 | } 49 | 50 | // NewStream prepares a new write stream that will write data in the 51 | // background. Size defines a buffer size for a single write transaction and 52 | // count defines how many transactions may be active at any time. By buffering 53 | // the writes, a Stream reduces the latency between subsequent transfers and 54 | // increases writing throughput. 55 | func (e *OutEndpoint) NewStream(size, count int) (*WriteStream, error) { 56 | s, err := e.newStream(size, count) 57 | if err != nil { 58 | return nil, err 59 | } 60 | return &WriteStream{s: s}, nil 61 | } 62 | -------------------------------------------------------------------------------- /endpoint_stream_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 the gousb Authors. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package gousb 16 | 17 | import "testing" 18 | 19 | func TestEndpointReadStream(t *testing.T) { 20 | t.Parallel() 21 | lib := newFakeLibusb() 22 | ctx := newContextWithImpl(lib) 23 | defer func() { 24 | if err := ctx.Close(); err != nil { 25 | t.Errorf("Context.Close: %v", err) 26 | } 27 | }() 28 | 29 | goodTransfers := 7 30 | done := make(chan struct{}) 31 | go func() { 32 | var num int 33 | for { 34 | xfr := lib.waitForSubmitted(done) 35 | if xfr == nil { 36 | return 37 | } 38 | if num < goodTransfers { 39 | xfr.setData(make([]byte, len(xfr.buf))) 40 | xfr.setStatus(TransferCompleted) 41 | } else { 42 | xfr.setStatus(TransferError) 43 | } 44 | num++ 45 | } 46 | }() 47 | 48 | dev, err := ctx.OpenDeviceWithVIDPID(0x9999, 0x0001) 49 | if err != nil { 50 | t.Fatalf("OpenDeviceWithVIDPID(9999, 0001): %v", err) 51 | } 52 | defer dev.Close() 53 | cfg, err := dev.Config(1) 54 | if err != nil { 55 | t.Fatalf("%s.Config(1): %v", dev, err) 56 | } 57 | defer cfg.Close() 58 | intf, err := cfg.Interface(0, 0) 59 | if err != nil { 60 | t.Fatalf("%s.Interface(0, 0): %v", cfg, err) 61 | } 62 | defer intf.Close() 63 | ep, err := intf.InEndpoint(2) 64 | if err != nil { 65 | t.Fatalf("%s.Endpoint(2): %v", intf, err) 66 | } 67 | stream, err := ep.NewStream(1024, 5) 68 | if err != nil { 69 | t.Fatalf("%s.NewStream(1024, 5): %v", ep, err) 70 | } 71 | defer stream.Close() 72 | var got int 73 | want := goodTransfers * 512 // EP max packet size is 512 74 | buf := make([]byte, 1024) 75 | for got <= want { 76 | num, err := stream.Read(buf) 77 | if err != nil { 78 | break 79 | } 80 | got += num 81 | } 82 | if got != want { 83 | t.Errorf("stream.Read(): read %d bytes, want %d", got, want) 84 | } 85 | close(done) 86 | } 87 | 88 | func TestEndpointWriteStream(t *testing.T) { 89 | t.Parallel() 90 | lib := newFakeLibusb() 91 | ctx := newContextWithImpl(lib) 92 | defer func() { 93 | if err := ctx.Close(); err != nil { 94 | t.Errorf("Context.Close: %v", err) 95 | } 96 | }() 97 | 98 | done := make(chan struct{}) 99 | total := 0 100 | num := 0 101 | go func() { 102 | for { 103 | xfr := lib.waitForSubmitted(done) 104 | if xfr == nil { 105 | return 106 | } 107 | xfr.setData(make([]byte, len(xfr.buf))) 108 | xfr.setStatus(TransferCompleted) 109 | num++ 110 | total += xfr.length 111 | } 112 | }() 113 | 114 | dev, err := ctx.OpenDeviceWithVIDPID(0x9999, 0x0001) 115 | if err != nil { 116 | t.Fatalf("OpenDeviceWithVIDPID(9999, 0001): %v", err) 117 | } 118 | defer dev.Close() 119 | cfg, err := dev.Config(1) 120 | if err != nil { 121 | t.Fatalf("%s.Config(1): %v", dev, err) 122 | } 123 | defer cfg.Close() 124 | intf, err := cfg.Interface(0, 0) 125 | if err != nil { 126 | t.Fatalf("%s.Interface(0, 0): %v", cfg, err) 127 | } 128 | defer intf.Close() 129 | ep, err := intf.OutEndpoint(1) 130 | if err != nil { 131 | t.Fatalf("%s.Endpoint(1): %v", intf, err) 132 | } 133 | bufSize := 1024 134 | stream, err := ep.NewStream(bufSize, 5) 135 | if err != nil { 136 | t.Fatalf("%s.NewStream(%d, 5): %v", ep, bufSize, err) 137 | } 138 | defer stream.Close() 139 | for i := 0; i < 5; i++ { 140 | if n, err := stream.Write(make([]byte, bufSize*2)); err != nil { 141 | t.Fatalf("stream.Write: got error %v", err) 142 | } else if n != bufSize*2 { 143 | t.Fatalf("stream.Write: %d, want %d", n, bufSize*2) 144 | } 145 | } 146 | if err := stream.Close(); err != nil { 147 | t.Fatalf("stream.Close: got error %v", err) 148 | } 149 | if got, want := stream.Written(), 10240; got != want { // 5 stream.Writes, each with 2048 bytes 150 | t.Errorf("stream.Written: got %d, want %d", got, want) 151 | } 152 | done <- struct{}{} 153 | if w := stream.Written(); total != w { 154 | t.Errorf("received data: got %d, but stream.Written returned %d, results should be identical", total, w) 155 | } 156 | if wantXfers := 20; num != wantXfers { // transferred 10240 bytes, device max packet size is 512 157 | t.Errorf("received transfers: got %d, want %d", num, wantXfers) 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /endpoint_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 the gousb Authors. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package gousb 16 | 17 | import ( 18 | "context" 19 | "testing" 20 | "time" 21 | ) 22 | 23 | func TestEndpoint(t *testing.T) { 24 | t.Parallel() 25 | lib := newFakeLibusb() 26 | ctx := newContextWithImpl(lib) 27 | defer func() { 28 | if err := ctx.Close(); err != nil { 29 | t.Errorf("Context.Close(): %v", err) 30 | } 31 | }() 32 | 33 | for _, epData := range []struct { 34 | ei EndpointDesc 35 | intf InterfaceSetting 36 | }{ 37 | { 38 | ei: EndpointDesc{ 39 | Address: 0x82, 40 | Number: 2, 41 | Direction: EndpointDirectionIn, 42 | MaxPacketSize: 512, 43 | TransferType: TransferTypeBulk, 44 | }, 45 | intf: InterfaceSetting{ 46 | Number: 0, 47 | Alternate: 0, 48 | Class: ClassVendorSpec, 49 | }, 50 | }, 51 | { 52 | ei: EndpointDesc{ 53 | Address: 0x06, 54 | Number: 6, 55 | MaxPacketSize: 3 * 1024, 56 | TransferType: TransferTypeIsochronous, 57 | PollInterval: 125 * time.Microsecond, 58 | UsageType: IsoUsageTypeData, 59 | }, 60 | intf: InterfaceSetting{ 61 | Number: 0, 62 | Alternate: 0, 63 | Class: ClassVendorSpec, 64 | }, 65 | }, 66 | } { 67 | epData.intf.Endpoints = map[EndpointAddress]EndpointDesc{epData.ei.Address: epData.ei} 68 | for _, tc := range []struct { 69 | desc string 70 | buf []byte 71 | ret int 72 | wantSubmit bool 73 | status TransferStatus 74 | want int 75 | wantErr bool 76 | }{ 77 | { 78 | desc: "empty buffer", 79 | buf: []byte{}, 80 | ret: 0, 81 | wantSubmit: true, 82 | want: 0, 83 | }, 84 | { 85 | desc: "128B buffer, 60 transferred", 86 | buf: make([]byte, 128), 87 | ret: 60, 88 | wantSubmit: true, 89 | want: 60, 90 | }, 91 | { 92 | desc: "128B buffer, 10 transferred and then error", 93 | buf: make([]byte, 128), 94 | ret: 10, 95 | wantSubmit: true, 96 | status: TransferError, 97 | want: 10, 98 | wantErr: true, 99 | }, 100 | } { 101 | ep := &endpoint{h: nil, ctx: ctx, InterfaceSetting: epData.intf, Desc: epData.ei} 102 | if tc.wantSubmit { 103 | go func() { 104 | fakeT := lib.waitForSubmitted(nil) 105 | fakeT.setData(make([]byte, tc.ret)) 106 | fakeT.setStatus(tc.status) 107 | }() 108 | } 109 | got, err := ep.transfer(context.TODO(), tc.buf) 110 | if (err != nil) != tc.wantErr { 111 | t.Errorf("%s, %s: ep.transfer(...): got err: %v, err != nil is %v, want %v", epData.ei, tc.desc, err, err != nil, tc.wantErr) 112 | continue 113 | } 114 | if got != tc.want { 115 | t.Errorf("%s, %s: ep.transfer(...): got %d bytes, want %d", epData.ei, tc.desc, got, tc.want) 116 | } 117 | if !lib.empty() { 118 | t.Fatalf("%s, %s: transfers still pending when none were expected", epData.ei, tc.desc) 119 | } 120 | } 121 | } 122 | } 123 | 124 | func TestEndpointInfo(t *testing.T) { 125 | t.Parallel() 126 | for _, tc := range []struct { 127 | ep EndpointDesc 128 | want string 129 | }{ 130 | { 131 | ep: EndpointDesc{ 132 | Address: 0x86, 133 | Number: 6, 134 | Direction: EndpointDirectionIn, 135 | TransferType: TransferTypeBulk, 136 | MaxPacketSize: 512, 137 | }, 138 | want: "ep #6 IN (address 0x86) bulk [512 bytes]", 139 | }, 140 | { 141 | ep: EndpointDesc{ 142 | Address: 0x02, 143 | Number: 2, 144 | Direction: EndpointDirectionOut, 145 | TransferType: TransferTypeIsochronous, 146 | MaxPacketSize: 512, 147 | IsoSyncType: IsoSyncTypeAsync, 148 | UsageType: IsoUsageTypeData, 149 | }, 150 | want: "ep #2 OUT (address 0x02) isochronous - asynchronous data [512 bytes]", 151 | }, 152 | { 153 | ep: EndpointDesc{ 154 | Address: 0x83, 155 | Number: 3, 156 | Direction: EndpointDirectionIn, 157 | TransferType: TransferTypeInterrupt, 158 | MaxPacketSize: 16, 159 | UsageType: InterruptUsageTypePeriodic, 160 | }, 161 | want: "ep #3 IN (address 0x83) interrupt - periodic [16 bytes]", 162 | }, 163 | } { 164 | if got := tc.ep.String(); got != tc.want { 165 | t.Errorf("%#v.String(): got %q, want %q", tc.ep, got, tc.want) 166 | } 167 | } 168 | } 169 | 170 | func TestEndpointInOut(t *testing.T) { 171 | t.Parallel() 172 | lib := newFakeLibusb() 173 | ctx := newContextWithImpl(lib) 174 | defer func() { 175 | if err := ctx.Close(); err != nil { 176 | t.Errorf("Context.Close(): %v", err) 177 | } 178 | }() 179 | 180 | d, err := ctx.OpenDeviceWithVIDPID(0x9999, 0x0001) 181 | if err != nil { 182 | t.Fatalf("OpenDeviceWithVIDPID(0x9999, 0x0001): got error %v, want nil", err) 183 | } 184 | defer func() { 185 | if err := d.Close(); err != nil { 186 | t.Errorf("%s.Close(): %v", d, err) 187 | } 188 | }() 189 | cfg, err := d.Config(1) 190 | if err != nil { 191 | t.Fatalf("%s.Config(1): %v", d, err) 192 | } 193 | defer func() { 194 | if err := cfg.Close(); err != nil { 195 | t.Errorf("%s.Close(): %v", cfg, err) 196 | } 197 | }() 198 | intf, err := cfg.Interface(0, 0) 199 | if err != nil { 200 | t.Fatalf("%s.Interface(0, 0): %v", cfg, err) 201 | } 202 | defer intf.Close() 203 | 204 | // IN endpoint 2 205 | iep, err := intf.InEndpoint(2) 206 | if err != nil { 207 | t.Fatalf("%s.InEndpoint(2): got error %v, want nil", intf, err) 208 | } 209 | dataTransferred := 100 210 | go func() { 211 | fakeT := lib.waitForSubmitted(nil) 212 | fakeT.setData(make([]byte, dataTransferred)) 213 | fakeT.setStatus(TransferCompleted) 214 | }() 215 | buf := make([]byte, 512) 216 | got, err := iep.Read(buf) 217 | if err != nil { 218 | t.Errorf("%s.Read: got error %v, want nil", iep, err) 219 | } else if got != dataTransferred { 220 | t.Errorf("%s.Read: got %d, want %d", iep, got, dataTransferred) 221 | } 222 | 223 | _, err = intf.InEndpoint(1) 224 | if err == nil { 225 | t.Errorf("%s.InEndpoint(1): got nil, want error", intf) 226 | } 227 | 228 | // OUT endpoint 1 229 | oep, err := intf.OutEndpoint(1) 230 | if err != nil { 231 | t.Fatalf("%s.OutEndpoint(1): got error %v, want nil", intf, err) 232 | } 233 | go func() { 234 | fakeT := lib.waitForSubmitted(nil) 235 | fakeT.setData(make([]byte, dataTransferred)) 236 | fakeT.setStatus(TransferCompleted) 237 | }() 238 | got, err = oep.Write(buf) 239 | if err != nil { 240 | t.Errorf("%s.Write: got error %v, want nil", oep, err) 241 | } else if got != dataTransferred { 242 | t.Errorf("%s.Write: got %d, want %d", oep, got, dataTransferred) 243 | } 244 | 245 | _, err = intf.OutEndpoint(2) 246 | if err == nil { 247 | t.Errorf("%s.OutEndpoint(2): got nil, want error", intf) 248 | } 249 | } 250 | 251 | func TestSameEndpointNumberInOut(t *testing.T) { 252 | t.Parallel() 253 | ctx := newContextWithImpl(newFakeLibusb()) 254 | defer func() { 255 | if err := ctx.Close(); err != nil { 256 | t.Errorf("Context.Close(): %v", err) 257 | } 258 | }() 259 | 260 | d, err := ctx.OpenDeviceWithVIDPID(0x1111, 0x1111) 261 | if err != nil { 262 | t.Fatalf("OpenDeviceWithVIDPID(0x1111, 0x1111): got error %v, want nil", err) 263 | } 264 | defer func() { 265 | if err := d.Close(); err != nil { 266 | t.Errorf("%s.Close(): %v", d, err) 267 | } 268 | }() 269 | cfg, err := d.Config(1) 270 | if err != nil { 271 | t.Fatalf("%s.Config(1): %v", d, err) 272 | } 273 | defer func() { 274 | if err := cfg.Close(); err != nil { 275 | t.Errorf("%s.Close(): %v", cfg, err) 276 | } 277 | }() 278 | intf, err := cfg.Interface(0, 0) 279 | if err != nil { 280 | t.Fatalf("%s.Interface(0, 0): %v", cfg, err) 281 | } 282 | defer intf.Close() 283 | 284 | if _, err := intf.InEndpoint(1); err != nil { 285 | t.Errorf("%s.InEndpoint(1): got error %v, want nil", intf, err) 286 | } 287 | if _, err := intf.OutEndpoint(1); err != nil { 288 | t.Errorf("%s.OutEndpoint(1): got error %v, want nil", intf, err) 289 | } 290 | } 291 | 292 | func TestReadContext(t *testing.T) { 293 | t.Parallel() 294 | lib := newFakeLibusb() 295 | ctx := newContextWithImpl(lib) 296 | defer func() { 297 | if err := ctx.Close(); err != nil { 298 | t.Errorf("Context.Close(): %v", err) 299 | } 300 | }() 301 | 302 | d, err := ctx.OpenDeviceWithVIDPID(0x9999, 0x0001) 303 | if err != nil { 304 | t.Fatalf("OpenDeviceWithVIDPID(0x9999, 0x0001): got error %v, want nil", err) 305 | } 306 | defer func() { 307 | if err := d.Close(); err != nil { 308 | t.Errorf("%s.Close(): %v", d, err) 309 | } 310 | }() 311 | cfg, err := d.Config(1) 312 | if err != nil { 313 | t.Fatalf("%s.Config(1): %v", d, err) 314 | } 315 | defer func() { 316 | if err := cfg.Close(); err != nil { 317 | t.Errorf("%s.Close(): %v", cfg, err) 318 | } 319 | }() 320 | intf, err := cfg.Interface(0, 0) 321 | if err != nil { 322 | t.Fatalf("%s.Interface(0, 0): %v", cfg, err) 323 | } 324 | defer intf.Close() 325 | iep, err := intf.InEndpoint(2) 326 | if err != nil { 327 | t.Fatalf("%s.InEndpoint(2): got error %v, want nil", intf, err) 328 | } 329 | buf := make([]byte, 512) 330 | 331 | rCtx, done := context.WithCancel(context.Background()) 332 | go func() { 333 | ft := lib.waitForSubmitted(nil) 334 | ft.setData([]byte{1, 2, 3, 4, 5}) 335 | done() 336 | }() 337 | if got, err := iep.ReadContext(rCtx, buf); err != TransferCancelled { 338 | t.Errorf("%s.Read: got error %v, want %v", iep, err, TransferCancelled) 339 | } else if want := 5; got != want { 340 | t.Errorf("%s.Read: got %d bytes, want %d (partial read success)", iep, got, want) 341 | } 342 | 343 | oep, err := intf.OutEndpoint(1) 344 | if err != nil { 345 | t.Fatalf("%s.OutEndpoint(1): got error %v, want nil", intf, err) 346 | } 347 | wCtx, done := context.WithCancel(context.Background()) 348 | go func() { 349 | ft := lib.waitForSubmitted(nil) 350 | ft.setLength(5) 351 | done() 352 | }() 353 | if got, err := oep.WriteContext(wCtx, buf); err != TransferCancelled { 354 | t.Errorf("%s.Write: got error %v, want %v", oep, err, TransferCancelled) 355 | } else if want := 5; got != want { 356 | t.Errorf("%s.Write: got %d bytes, want %d (partial write success)", oep, got, want) 357 | } 358 | } 359 | -------------------------------------------------------------------------------- /error.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Copyright 2016 the gousb Authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package gousb 17 | 18 | import ( 19 | "fmt" 20 | ) 21 | 22 | // #include 23 | import "C" 24 | 25 | // Error is an error code from a USB operation. See the list of Error constants below. 26 | type Error C.int 27 | 28 | // Error implements the error interface. 29 | func (e Error) Error() string { 30 | return fmt.Sprintf("libusb: %s [code %d]", errorString[e], e) 31 | } 32 | 33 | func fromErrNo(errno C.int) error { 34 | err := Error(errno) 35 | if err == Success { 36 | return nil 37 | } 38 | return err 39 | } 40 | 41 | // Defined result codes. 42 | const ( 43 | Success Error = C.LIBUSB_SUCCESS 44 | ErrorIO Error = C.LIBUSB_ERROR_IO 45 | ErrorInvalidParam Error = C.LIBUSB_ERROR_INVALID_PARAM 46 | ErrorAccess Error = C.LIBUSB_ERROR_ACCESS 47 | ErrorNoDevice Error = C.LIBUSB_ERROR_NO_DEVICE 48 | ErrorNotFound Error = C.LIBUSB_ERROR_NOT_FOUND 49 | ErrorBusy Error = C.LIBUSB_ERROR_BUSY 50 | ErrorTimeout Error = C.LIBUSB_ERROR_TIMEOUT 51 | // ErrorOverflow indicates that the device tried to send more data than was 52 | // requested and that could fit in the packet buffer. 53 | ErrorOverflow Error = C.LIBUSB_ERROR_OVERFLOW 54 | ErrorPipe Error = C.LIBUSB_ERROR_PIPE 55 | ErrorInterrupted Error = C.LIBUSB_ERROR_INTERRUPTED 56 | ErrorNoMem Error = C.LIBUSB_ERROR_NO_MEM 57 | ErrorNotSupported Error = C.LIBUSB_ERROR_NOT_SUPPORTED 58 | ErrorOther Error = C.LIBUSB_ERROR_OTHER 59 | ) 60 | 61 | var errorString = map[Error]string{ 62 | Success: "success", 63 | ErrorIO: "i/o error", 64 | ErrorInvalidParam: "invalid param", 65 | ErrorAccess: "bad access", 66 | ErrorNoDevice: "no device", 67 | ErrorNotFound: "not found", 68 | ErrorBusy: "device or resource busy", 69 | ErrorTimeout: "timeout", 70 | ErrorOverflow: "overflow", 71 | ErrorPipe: "pipe error", 72 | ErrorInterrupted: "interrupted", 73 | ErrorNoMem: "out of memory", 74 | ErrorNotSupported: "not supported", 75 | ErrorOther: "unknown error", 76 | } 77 | 78 | // TransferStatus contains information about the result of a transfer. 79 | type TransferStatus uint8 80 | 81 | // Defined Transfer status values. 82 | const ( 83 | TransferCompleted TransferStatus = C.LIBUSB_TRANSFER_COMPLETED 84 | TransferError TransferStatus = C.LIBUSB_TRANSFER_ERROR 85 | TransferTimedOut TransferStatus = C.LIBUSB_TRANSFER_TIMED_OUT 86 | TransferCancelled TransferStatus = C.LIBUSB_TRANSFER_CANCELLED 87 | TransferStall TransferStatus = C.LIBUSB_TRANSFER_STALL 88 | TransferNoDevice TransferStatus = C.LIBUSB_TRANSFER_NO_DEVICE 89 | TransferOverflow TransferStatus = C.LIBUSB_TRANSFER_OVERFLOW 90 | ) 91 | 92 | var transferStatusDescription = map[TransferStatus]string{ 93 | TransferCompleted: "transfer completed without error", 94 | TransferError: "transfer failed", 95 | TransferTimedOut: "transfer timed out", 96 | TransferCancelled: "transfer was cancelled", 97 | TransferStall: "halt condition detected (endpoint stalled) or control request not supported", 98 | TransferNoDevice: "device was disconnected", 99 | TransferOverflow: "device sent more data than requested", 100 | } 101 | 102 | // String returns a human-readable transfer status. 103 | func (ts TransferStatus) String() string { 104 | return transferStatusDescription[ts] 105 | } 106 | 107 | // Error implements the error interface. 108 | func (ts TransferStatus) Error() string { 109 | return ts.String() 110 | } 111 | -------------------------------------------------------------------------------- /example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 the gousb Authors. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package gousb_test 16 | 17 | import ( 18 | "fmt" 19 | "log" 20 | 21 | "github.com/google/gousb" 22 | ) 23 | 24 | // This examples demonstrates the use of a few convenience functions that 25 | // can be used in simple situations and with simple devices. 26 | // It opens a device with a given VID/PID, 27 | // claims the default interface (use the same config as currently active, 28 | // interface 0, alternate setting 0) and tries to write 5 bytes of data 29 | // to endpoint number 7. 30 | func Example_simple() { 31 | // Initialize a new Context. 32 | ctx := gousb.NewContext() 33 | defer ctx.Close() 34 | 35 | // Open any device with a given VID/PID using a convenience function. 36 | dev, err := ctx.OpenDeviceWithVIDPID(0x046d, 0xc526) 37 | if err != nil { 38 | log.Fatalf("Could not open a device: %v", err) 39 | } 40 | defer dev.Close() 41 | 42 | // Claim the default interface using a convenience function. 43 | // The default interface is always #0 alt #0 in the currently active 44 | // config. 45 | intf, done, err := dev.DefaultInterface() 46 | if err != nil { 47 | log.Fatalf("%s.DefaultInterface(): %v", dev, err) 48 | } 49 | defer done() 50 | 51 | // Open an OUT endpoint. 52 | ep, err := intf.OutEndpoint(7) 53 | if err != nil { 54 | log.Fatalf("%s.OutEndpoint(7): %v", intf, err) 55 | } 56 | 57 | // Generate some data to write. 58 | data := make([]byte, 5) 59 | for i := range data { 60 | data[i] = byte(i) 61 | } 62 | 63 | // Write data to the USB device. 64 | numBytes, err := ep.Write(data) 65 | if numBytes != 5 { 66 | log.Fatalf("%s.Write([5]): only %d bytes written, returned error is %v", ep, numBytes, err) 67 | } 68 | fmt.Println("5 bytes successfully sent to the endpoint") 69 | } 70 | 71 | // This example demostrates the full API for accessing endpoints. 72 | // It opens a device with a known VID/PID, switches the device to 73 | // configuration #2, in that configuration it opens (claims) interface #3 with alternate setting #0. 74 | // Within that interface setting it opens an IN endpoint number 6 and an OUT endpoint number 5, then starts copying 75 | // data between them, 76 | func Example_complex() { 77 | // Initialize a new Context. 78 | ctx := gousb.NewContext() 79 | defer ctx.Close() 80 | 81 | // Iterate through available Devices, finding all that match a known VID/PID. 82 | vid, pid := gousb.ID(0x04f2), gousb.ID(0xb531) 83 | devs, err := ctx.OpenDevices(func(desc *gousb.DeviceDesc) bool { 84 | // this function is called for every device present. 85 | // Returning true means the device should be opened. 86 | return desc.Vendor == vid && desc.Product == pid 87 | }) 88 | // All returned devices are now open and will need to be closed. 89 | for _, d := range devs { 90 | defer d.Close() 91 | } 92 | if err != nil { 93 | log.Fatalf("OpenDevices(): %v", err) 94 | } 95 | if len(devs) == 0 { 96 | log.Fatalf("no devices found matching VID %s and PID %s", vid, pid) 97 | } 98 | 99 | // Pick the first device found. 100 | dev := devs[0] 101 | 102 | // Switch the configuration to #2. 103 | cfg, err := dev.Config(2) 104 | if err != nil { 105 | log.Fatalf("%s.Config(2): %v", dev, err) 106 | } 107 | defer cfg.Close() 108 | 109 | // In the config #2, claim interface #3 with alt setting #0. 110 | intf, err := cfg.Interface(3, 0) 111 | if err != nil { 112 | log.Fatalf("%s.Interface(3, 0): %v", cfg, err) 113 | } 114 | defer intf.Close() 115 | 116 | // In this interface open endpoint #6 for reading. 117 | epIn, err := intf.InEndpoint(6) 118 | if err != nil { 119 | log.Fatalf("%s.InEndpoint(6): %v", intf, err) 120 | } 121 | 122 | // And in the same interface open endpoint #5 for writing. 123 | epOut, err := intf.OutEndpoint(5) 124 | if err != nil { 125 | log.Fatalf("%s.OutEndpoint(5): %v", intf, err) 126 | } 127 | 128 | // Buffer large enough for 10 USB packets from endpoint 6. 129 | buf := make([]byte, 10*epIn.Desc.MaxPacketSize) 130 | total := 0 131 | // Repeat the read/write cycle 10 times. 132 | for i := 0; i < 10; i++ { 133 | // readBytes might be smaller than the buffer size. readBytes might be greater than zero even if err is not nil. 134 | readBytes, err := epIn.Read(buf) 135 | if err != nil { 136 | fmt.Println("Read returned an error:", err) 137 | } 138 | if readBytes == 0 { 139 | log.Fatalf("IN endpoint 6 returned 0 bytes of data.") 140 | } 141 | // writeBytes might be smaller than the buffer size if an error occurred. writeBytes might be greater than zero even if err is not nil. 142 | writeBytes, err := epOut.Write(buf[:readBytes]) 143 | if err != nil { 144 | fmt.Println("Write returned an error:", err) 145 | } 146 | if writeBytes != readBytes { 147 | log.Fatalf("IN endpoint 5 received only %d bytes of data out of %d sent", writeBytes, readBytes) 148 | } 149 | total += writeBytes 150 | } 151 | fmt.Printf("Total number of bytes copied: %d\n", total) 152 | } 153 | -------------------------------------------------------------------------------- /fakelibusb_devices.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 the gousb Authors. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package gousb 16 | 17 | // fake devices connected through the fakeLibusb stack. 18 | type fakeDevice struct { 19 | devDesc *DeviceDesc 20 | strDesc map[int]string 21 | alt uint8 22 | sysDevPtr uintptr 23 | } 24 | 25 | var fakeDevices = []fakeDevice{ 26 | // Bus 001 Device 001: ID 9999:0001 27 | // One config, one interface, one setup, 28 | // two endpoints: 0x01 OUT, 0x82 IN. 29 | { 30 | devDesc: &DeviceDesc{ 31 | Bus: 1, 32 | Address: 1, 33 | Port: 1, 34 | Spec: Version(2, 0), 35 | Device: Version(1, 0), 36 | Vendor: ID(0x9999), 37 | Product: ID(0x0001), 38 | Protocol: 255, 39 | Configs: map[int]ConfigDesc{1: { 40 | Number: 1, 41 | MaxPower: Milliamperes(100), 42 | Interfaces: []InterfaceDesc{{ 43 | Number: 0, 44 | AltSettings: []InterfaceSetting{{ 45 | Number: 0, 46 | Alternate: 0, 47 | Class: ClassVendorSpec, 48 | Endpoints: map[EndpointAddress]EndpointDesc{ 49 | 0x01: { 50 | Address: 0x01, 51 | Number: 1, 52 | Direction: EndpointDirectionOut, 53 | MaxPacketSize: 512, 54 | TransferType: TransferTypeBulk, 55 | }, 56 | 0x82: { 57 | Address: 0x82, 58 | Number: 2, 59 | Direction: EndpointDirectionIn, 60 | MaxPacketSize: 512, 61 | TransferType: TransferTypeBulk, 62 | }, 63 | }, 64 | }}, 65 | }}, 66 | }}, 67 | }, 68 | sysDevPtr: 78, 69 | }, 70 | // Bus 001 Device 002: ID 8888:0002 71 | // One config, two interfaces. interface #0 with no endpoints, 72 | // interface #1 with two alt setups with different packet sizes for 73 | // endpoints. Two isochronous endpoints, 0x05 OUT and 0x86 OUT. 74 | { 75 | devDesc: &DeviceDesc{ 76 | Bus: 1, 77 | Address: 2, 78 | Port: 2, 79 | Spec: Version(2, 0), 80 | Device: Version(1, 3), 81 | Vendor: ID(0x8888), 82 | Product: ID(0x0002), 83 | Protocol: 255, 84 | Configs: map[int]ConfigDesc{1: { 85 | Number: 1, 86 | MaxPower: Milliamperes(100), 87 | iConfiguration: 5, 88 | Interfaces: []InterfaceDesc{{ 89 | Number: 0, 90 | AltSettings: []InterfaceSetting{{ 91 | Number: 0, 92 | Alternate: 0, 93 | Class: ClassVendorSpec, 94 | iInterface: 6, 95 | }}, 96 | }, { 97 | Number: 1, 98 | AltSettings: []InterfaceSetting{{ 99 | Number: 1, 100 | Alternate: 0, 101 | Class: ClassVendorSpec, 102 | Endpoints: map[EndpointAddress]EndpointDesc{ 103 | 0x05: { 104 | Address: 0x05, 105 | Number: 5, 106 | Direction: EndpointDirectionOut, 107 | MaxPacketSize: 3 * 1024, 108 | TransferType: TransferTypeIsochronous, 109 | UsageType: IsoUsageTypeData, 110 | }, 111 | 0x86: { 112 | Address: 0x86, 113 | Number: 6, 114 | Direction: EndpointDirectionIn, 115 | MaxPacketSize: 3 * 1024, 116 | TransferType: TransferTypeIsochronous, 117 | UsageType: IsoUsageTypeData, 118 | }, 119 | }, 120 | iInterface: 7, 121 | }, { 122 | Number: 1, 123 | Alternate: 1, 124 | Class: ClassVendorSpec, 125 | Endpoints: map[EndpointAddress]EndpointDesc{ 126 | 0x05: { 127 | Address: 0x05, 128 | Number: 5, 129 | Direction: EndpointDirectionOut, 130 | MaxPacketSize: 2 * 1024, 131 | TransferType: TransferTypeIsochronous, 132 | }, 133 | 0x86: { 134 | Address: 0x86, 135 | Number: 6, 136 | Direction: EndpointDirectionIn, 137 | MaxPacketSize: 2 * 1024, 138 | TransferType: TransferTypeIsochronous, 139 | }, 140 | }, 141 | iInterface: 8, 142 | }, { 143 | Number: 1, 144 | Alternate: 2, 145 | Class: ClassVendorSpec, 146 | Endpoints: map[EndpointAddress]EndpointDesc{ 147 | 0x05: { 148 | Address: 0x05, 149 | Number: 5, 150 | Direction: EndpointDirectionIn, 151 | MaxPacketSize: 1024, 152 | TransferType: TransferTypeIsochronous, 153 | }, 154 | 0x86: { 155 | Address: 0x86, 156 | Number: 6, 157 | Direction: EndpointDirectionIn, 158 | MaxPacketSize: 1024, 159 | TransferType: TransferTypeIsochronous, 160 | }, 161 | }, 162 | }}, 163 | }, { 164 | // Malformed descriptior, non contiguous interface 165 | // number (previous interface is #1), alt settings 166 | // not starting from 0. 167 | // See https://github.com/google/gousb/issues/65 168 | Number: 3, 169 | AltSettings: []InterfaceSetting{{ 170 | Number: 3, 171 | Alternate: 2, 172 | Class: ClassVendorSpec, 173 | iInterface: 9, 174 | }}, 175 | }}, 176 | }}, 177 | iManufacturer: 1, 178 | iProduct: 2, 179 | iSerialNumber: 3, 180 | }, 181 | strDesc: map[int]string{ 182 | 1: "ACME Industries", 183 | 2: "Fidgety Gadget", 184 | 3: "01234567", 185 | 5: "Weird configuration", 186 | 6: "Boring setting", 187 | 7: "Fast streaming", 188 | 8: "Slower streaming", 189 | 9: "Interface for https://github.com/google/gousb/issues/65", 190 | }, 191 | sysDevPtr: 94, 192 | }, 193 | // Bus 001 Device 003: ID 9999:0002 194 | // One config, one interface, one setup, 195 | // two endpoints: 0x01 OUT, 0x81 IN. 196 | { 197 | devDesc: &DeviceDesc{ 198 | Bus: 1, 199 | Address: 3, 200 | Port: 3, 201 | Spec: Version(2, 0), 202 | Device: Version(1, 0), 203 | Vendor: ID(0x1111), 204 | Product: ID(0x1111), 205 | Protocol: 255, 206 | Configs: map[int]ConfigDesc{1: { 207 | Number: 1, 208 | MaxPower: Milliamperes(100), 209 | Interfaces: []InterfaceDesc{{ 210 | Number: 0, 211 | AltSettings: []InterfaceSetting{{ 212 | Number: 0, 213 | Alternate: 0, 214 | Class: ClassVendorSpec, 215 | Endpoints: map[EndpointAddress]EndpointDesc{ 216 | 0x01: { 217 | Address: 0x01, 218 | Number: 1, 219 | Direction: EndpointDirectionOut, 220 | MaxPacketSize: 512, 221 | TransferType: TransferTypeBulk, 222 | }, 223 | 0x81: { 224 | Address: 0x81, 225 | Number: 1, 226 | Direction: EndpointDirectionIn, 227 | MaxPacketSize: 512, 228 | TransferType: TransferTypeBulk, 229 | }, 230 | }, 231 | }}, 232 | }}, 233 | }}, 234 | }, 235 | }, 236 | } 237 | -------------------------------------------------------------------------------- /fakelibusb_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 the gousb Authors. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package gousb 16 | 17 | import ( 18 | "errors" 19 | "fmt" 20 | "sync" 21 | "time" 22 | ) 23 | 24 | type fakeTransfer struct { 25 | // done is the channel that needs to receive a signal when the transfer has 26 | // finished. 27 | // This is different from finished below - done is provided by the caller 28 | // and is used to signal the caller. 29 | done chan struct{} 30 | // mu protects transfer data and status. 31 | mu sync.Mutex 32 | // buf is the slice for reading/writing data between the submit() and wait() returning. 33 | buf []byte 34 | // finished is true after the transfer is no longer in flight 35 | finished bool 36 | // status will be returned by wait() on this transfer 37 | status TransferStatus 38 | // length is the number of bytes used from the buffer (write) or available 39 | // in the buffer (read). 40 | length int 41 | // ep is the endpoint that this transfer was created for. 42 | ep *EndpointDesc 43 | // isoPackets is the number of isochronous transfers performed in a single libusb transfer 44 | isoPackets int 45 | // maxLength is the maximum number of bytes this transfer could contain 46 | maxLength int 47 | } 48 | 49 | func (t *fakeTransfer) setData(d []byte) { 50 | t.mu.Lock() 51 | defer t.mu.Unlock() 52 | if t.finished { 53 | return 54 | } 55 | copy(t.buf, d) 56 | t.length = len(d) 57 | } 58 | 59 | func (t *fakeTransfer) setLength(n int) { 60 | t.mu.Lock() 61 | defer t.mu.Unlock() 62 | if t.finished { 63 | return 64 | } 65 | t.length = n 66 | } 67 | 68 | func (t *fakeTransfer) setStatus(st TransferStatus) { 69 | t.mu.Lock() 70 | defer t.mu.Unlock() 71 | if t.finished { 72 | return 73 | } 74 | t.status = st 75 | t.finished = true 76 | t.done <- struct{}{} 77 | } 78 | 79 | // fakeLibusb implements a fake libusb stack that pretends to have a number of 80 | // devices connected to it (see fakeDevices variable for a list of devices). 81 | // fakeLibusb is expected to implement all the functions related to device 82 | // enumeration, configuration etc. according to fakeDevices descriptors. 83 | // The fake devices endpoints don't have any particular behavior implemented, 84 | // instead fakeLibusb provides additional functions, like waitForSubmitted, 85 | // that allows the test to explicitly control individual transfer behavior. 86 | type fakeLibusb struct { 87 | mu sync.Mutex 88 | // devices has a map of devices and their descriptors. 89 | devices map[*libusbDevice]*fakeDevice 90 | // sysDevices keeps the order of devices to be accessd by wrapSysDevice 91 | sysDevices map[uintptr]*libusbDevice 92 | // ts has a map of all allocated transfers, indexed by the pointer of 93 | // underlying libusbTransfer. 94 | ts map[*libusbTransfer]*fakeTransfer 95 | // submitted receives a fakeTransfers when submit() is called. 96 | submitted chan *fakeTransfer 97 | // handles is a map of device handles pointing at opened devices. 98 | handles map[*libusbDevHandle]*libusbDevice 99 | // claims is a map of devices to a set of claimed interfaces 100 | claims map[*libusbDevice]map[uint8]bool 101 | } 102 | 103 | func (f *fakeLibusb) init() (*libusbContext, error) { return newContextPointer(), nil } 104 | func (f *fakeLibusb) handleEvents(c *libusbContext, done <-chan struct{}) { <-done } 105 | func (f *fakeLibusb) getDevices(*libusbContext) ([]*libusbDevice, error) { 106 | ret := make([]*libusbDevice, 0, len(fakeDevices)) 107 | for d := range f.devices { 108 | ret = append(ret, d) 109 | } 110 | return ret, nil 111 | } 112 | 113 | func (f *fakeLibusb) wrapSysDevice(ctx *libusbContext, systemDeviceHandle uintptr) (*libusbDevHandle, error) { 114 | dev, ok := f.sysDevices[systemDeviceHandle] 115 | if !ok { 116 | return nil, fmt.Errorf("the passed file descriptor %d does not point to a valid device", systemDeviceHandle) 117 | } 118 | h := newDevHandlePointer() 119 | f.mu.Lock() 120 | defer f.mu.Unlock() 121 | f.handles[h] = dev 122 | return h, nil 123 | } 124 | 125 | func (f *fakeLibusb) getDevice(handle *libusbDevHandle) *libusbDevice { 126 | return f.handles[handle] 127 | } 128 | 129 | func (f *fakeLibusb) exit(*libusbContext) error { 130 | close(f.submitted) 131 | if got := len(f.ts); got > 0 { 132 | for t := range f.ts { 133 | f.free(t) 134 | } 135 | return fmt.Errorf("fakeLibusb has %d remaining transfers that should have been freed", got) 136 | } 137 | return nil 138 | } 139 | 140 | func (f *fakeLibusb) setDebug(*libusbContext, int) {} 141 | func (f *fakeLibusb) dereference(d *libusbDevice) {} 142 | func (f *fakeLibusb) getDeviceDesc(d *libusbDevice) (*DeviceDesc, error) { 143 | if dev, ok := f.devices[d]; ok { 144 | return dev.devDesc, nil 145 | } 146 | return nil, fmt.Errorf("invalid USB device %p", d) 147 | } 148 | func (f *fakeLibusb) open(d *libusbDevice) (*libusbDevHandle, error) { 149 | h := newDevHandlePointer() 150 | f.mu.Lock() 151 | defer f.mu.Unlock() 152 | f.handles[h] = d 153 | return h, nil 154 | } 155 | 156 | func (f *fakeLibusb) close(h *libusbDevHandle) { 157 | f.mu.Lock() 158 | defer f.mu.Unlock() 159 | delete(f.handles, h) 160 | } 161 | func (f *fakeLibusb) reset(*libusbDevHandle) error { return nil } 162 | func (f *fakeLibusb) control(*libusbDevHandle, time.Duration, uint8, uint8, uint16, uint16, []byte) (int, error) { 163 | return 0, errors.New("not implemented") 164 | } 165 | func (f *fakeLibusb) getConfig(*libusbDevHandle) (uint8, error) { return 1, nil } 166 | func (f *fakeLibusb) setConfig(d *libusbDevHandle, cfg uint8) error { 167 | debug.Printf("setConfig(%p, %d)\n", d, cfg) 168 | f.mu.Lock() 169 | defer f.mu.Unlock() 170 | if len(f.claims[f.handles[d]]) != 0 { 171 | return fmt.Errorf("can't set device config while interfaces are claimed: %v", f.claims[f.handles[d]]) 172 | } 173 | if cfg != 1 { 174 | return fmt.Errorf("device doesn't have config number %d", cfg) 175 | } 176 | return nil 177 | } 178 | func (f *fakeLibusb) getStringDesc(d *libusbDevHandle, index int) (string, error) { 179 | dev, ok := f.devices[f.handles[d]] 180 | if !ok { 181 | return "", fmt.Errorf("invalid USB device %p", d) 182 | } 183 | str, ok := dev.strDesc[index] 184 | if !ok { 185 | return "", fmt.Errorf("invalid string descriptor index %d", index) 186 | } 187 | return str, nil 188 | } 189 | func (f *fakeLibusb) setAutoDetach(*libusbDevHandle, int) error { return nil } 190 | 191 | func (f *fakeLibusb) detachKernelDriver(*libusbDevHandle, uint8) error { return nil } 192 | 193 | func (f *fakeLibusb) claim(d *libusbDevHandle, intf uint8) error { 194 | debug.Printf("claim(%p, %d)\n", d, intf) 195 | f.mu.Lock() 196 | defer f.mu.Unlock() 197 | c := f.claims[f.handles[d]] 198 | if c == nil { 199 | c = make(map[uint8]bool) 200 | f.claims[f.handles[d]] = c 201 | } 202 | c[intf] = true 203 | return nil 204 | } 205 | func (f *fakeLibusb) release(d *libusbDevHandle, intf uint8) { 206 | debug.Printf("release(%p, %d)\n", d, intf) 207 | f.mu.Lock() 208 | defer f.mu.Unlock() 209 | c := f.claims[f.handles[d]] 210 | if c == nil { 211 | return 212 | } 213 | c[intf] = false 214 | } 215 | func (f *fakeLibusb) setAlt(d *libusbDevHandle, intf, alt uint8) error { 216 | debug.Printf("setAlt(%p, %d, %d)\n", d, intf, alt) 217 | f.mu.Lock() 218 | defer f.mu.Unlock() 219 | if !f.claims[f.handles[d]][intf] { 220 | return fmt.Errorf("interface %d must be claimed before alt setup can be set", intf) 221 | } 222 | f.devices[f.handles[d]].alt = alt 223 | return nil 224 | } 225 | 226 | func (f *fakeLibusb) alloc(_ *libusbDevHandle, ep *EndpointDesc, isoPackets int, bufLen int, done chan struct{}) (*libusbTransfer, error) { 227 | f.mu.Lock() 228 | defer f.mu.Unlock() 229 | maxLen := ep.MaxPacketSize 230 | if isoPackets > 0 { 231 | if ep.TransferType != TransferTypeIsochronous { 232 | return nil, fmt.Errorf("alloc(..., ep: %s, isoPackets: %d, ...): endpoint is not an isochronous type endpoint, iso packets must be 0", ep, isoPackets) 233 | } 234 | maxLen = isoPackets * ep.MaxPacketSize 235 | } 236 | if bufLen > maxLen { 237 | bufLen = maxLen 238 | } 239 | t := newFakeTransferPointer() 240 | f.ts[t] = &fakeTransfer{ 241 | buf: make([]byte, bufLen), 242 | ep: ep, 243 | isoPackets: isoPackets, 244 | maxLength: maxLen, 245 | done: done, 246 | } 247 | return t, nil 248 | } 249 | func (f *fakeLibusb) cancel(t *libusbTransfer) error { 250 | f.mu.Lock() 251 | ft := f.ts[t] 252 | f.mu.Unlock() 253 | ft.setStatus(TransferCancelled) 254 | return nil 255 | } 256 | func (f *fakeLibusb) submit(t *libusbTransfer) error { 257 | f.mu.Lock() 258 | ft := f.ts[t] 259 | f.mu.Unlock() 260 | ft.finished = false 261 | f.submitted <- ft 262 | return nil 263 | } 264 | func (f *fakeLibusb) buffer(t *libusbTransfer) []byte { return f.ts[t].buf } 265 | func (f *fakeLibusb) data(t *libusbTransfer) (int, TransferStatus) { 266 | f.mu.Lock() 267 | defer f.mu.Unlock() 268 | ret := f.ts[t].length 269 | if maxRet := f.ts[t].maxLength; ret > maxRet { 270 | ret = maxRet 271 | } 272 | return ret, f.ts[t].status 273 | } 274 | func (f *fakeLibusb) free(t *libusbTransfer) { 275 | f.mu.Lock() 276 | defer f.mu.Unlock() 277 | delete(f.ts, t) 278 | } 279 | func (f *fakeLibusb) setIsoPacketLengths(t *libusbTransfer, length uint32) { 280 | f.mu.Lock() 281 | defer f.mu.Unlock() 282 | maxLen := f.ts[t].isoPackets * int(length) 283 | if bufLen := len(f.ts[t].buf); maxLen > bufLen { 284 | maxLen = bufLen 285 | } 286 | f.ts[t].maxLength = maxLen 287 | } 288 | 289 | // waitForSubmitted can be used by tests to define custom behavior of the transfers submitted on the USB bus. 290 | func (f *fakeLibusb) waitForSubmitted(done <-chan struct{}) *fakeTransfer { 291 | select { 292 | case t, ok := <-f.submitted: 293 | if !ok { 294 | return nil 295 | } 296 | return t 297 | case <-done: 298 | return nil 299 | } 300 | } 301 | 302 | // empty can be used to confirm that all transfers were cleaned up. 303 | func (f *fakeLibusb) empty() bool { 304 | return len(f.submitted) == 0 305 | } 306 | 307 | func newFakeLibusb() *fakeLibusb { 308 | fl := &fakeLibusb{ 309 | devices: make(map[*libusbDevice]*fakeDevice), 310 | sysDevices: make(map[uintptr]*libusbDevice), 311 | ts: make(map[*libusbTransfer]*fakeTransfer), 312 | submitted: make(chan *fakeTransfer, 10), 313 | handles: make(map[*libusbDevHandle]*libusbDevice), 314 | claims: make(map[*libusbDevice]map[uint8]bool), 315 | } 316 | for _, d := range fakeDevices { 317 | // libusb does not export a way to allocate a new libusb_device struct 318 | // without using the full USB stack. Since the fake library uses the 319 | // libusbDevice only as an identifier, use an arbitrary unique pointer. 320 | // The contents of these pointers is never accessed. 321 | fd := new(fakeDevice) 322 | *fd = d 323 | devPointer := newDevicePointer() 324 | fl.devices[devPointer] = fd 325 | if fd.sysDevPtr != 0 { // the sysDevPtr not being set in the fakeDevices list 326 | fl.sysDevices[fd.sysDevPtr] = devPointer 327 | } 328 | } 329 | return fl 330 | } 331 | -------------------------------------------------------------------------------- /fixlibusb_darwin.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function die { 4 | echo "$@" 5 | exit 1 6 | } 7 | 8 | FILE="$1" 9 | if [[ -z "$FILE" ]]; then 10 | die "Usage: $0 " 11 | fi 12 | 13 | if [[ $(gcc --version | grep -i "llvm") == "" ]]; then 14 | die "Error: This change is unnecessary unless your gcc uses llvm" 15 | fi 16 | 17 | BACKUP="${FILE}.orig" 18 | if [[ -f "$BACKUP" ]]; then 19 | die "It looks like you've already run this script ($BACKUP exists)" 20 | fi 21 | 22 | cp $FILE $BACKUP || die "Could not create backup" 23 | 24 | { 25 | echo 'H' # Turn on error printing 26 | echo 'g/\[0\].*non-standard/s/\[0\]/[1]/' # Use [1] instead of [0] so the size is unambiguous 27 | echo 'g/\[.\].*non-standard/p' # Print the lines changed 28 | echo 'w' # Write output 29 | } | ed $FILE 30 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/google/gousb 2 | 3 | go 1.16 4 | 5 | // Released in error, v1.* remains the current version. 6 | retract v2.1.0+incompatible 7 | -------------------------------------------------------------------------------- /interface.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Copyright 2016 the gousb Authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package gousb 17 | 18 | import ( 19 | "fmt" 20 | "sort" 21 | ) 22 | 23 | // InterfaceDesc contains information about a USB interface, extracted from 24 | // the descriptor. 25 | type InterfaceDesc struct { 26 | // Number is the number of this interface. 27 | Number int 28 | // AltSettings is a list of alternate settings supported by the interface. 29 | AltSettings []InterfaceSetting 30 | } 31 | 32 | func (i *InterfaceDesc) altSetting(alt int) (*InterfaceSetting, error) { 33 | alts := make([]int, len(i.AltSettings)) 34 | for a, s := range i.AltSettings { 35 | if s.Alternate == alt { 36 | return &s, nil 37 | } 38 | alts[a] = s.Alternate 39 | } 40 | return nil, fmt.Errorf("alternate setting %d not found for %s, available alt settings: %v", alt, i, alts) 41 | } 42 | 43 | // String returns a human-readable description of the interface descriptor and 44 | // its alternate settings. 45 | func (i InterfaceDesc) String() string { 46 | return fmt.Sprintf("Interface %d (%d alternate settings)", i.Number, len(i.AltSettings)) 47 | } 48 | 49 | // InterfaceSetting contains information about a USB interface with a particular 50 | // alternate setting, extracted from the descriptor. 51 | type InterfaceSetting struct { 52 | // Number is the number of this interface, the same as in InterfaceDesc. 53 | Number int 54 | // Alternate is the number of this alternate setting. 55 | Alternate int 56 | // Class is the USB-IF (Implementers Forum) class code, as defined by the USB spec. 57 | Class Class 58 | // SubClass is the USB-IF (Implementers Forum) subclass code, as defined by the USB spec. 59 | SubClass Class 60 | // Protocol is USB protocol code, as defined by the USB spe.c 61 | Protocol Protocol 62 | // Endpoints enumerates the endpoints available on this interface with 63 | // this alternate setting. 64 | Endpoints map[EndpointAddress]EndpointDesc 65 | 66 | iInterface int // index of a string descriptor describing this interface. 67 | } 68 | 69 | func (a InterfaceSetting) sortedEndpointIds() []string { 70 | var eps []string 71 | for _, ei := range a.Endpoints { 72 | eps = append(eps, fmt.Sprintf("%s(%d,%s)", ei.Address, ei.Number, ei.Direction)) 73 | } 74 | sort.Strings(eps) 75 | return eps 76 | } 77 | 78 | // String returns a human-readable description of the particular 79 | // alternate setting of an interface. 80 | func (a InterfaceSetting) String() string { 81 | return fmt.Sprintf("Interface %d alternate setting %d (available endpoints: %v)", a.Number, a.Alternate, a.sortedEndpointIds()) 82 | } 83 | 84 | // Interface is a representation of a claimed interface with a particular setting. 85 | // To access device endpoints use InEndpoint() and OutEndpoint() methods. 86 | // The interface should be Close()d after use. 87 | type Interface struct { 88 | Setting InterfaceSetting 89 | 90 | config *Config 91 | } 92 | 93 | func (i *Interface) String() string { 94 | return fmt.Sprintf("%s,if=%d,alt=%d", i.config, i.Setting.Number, i.Setting.Alternate) 95 | } 96 | 97 | // Close releases the interface. 98 | func (i *Interface) Close() { 99 | if i.config == nil { 100 | return 101 | } 102 | i.config.dev.ctx.libusb.release(i.config.dev.handle, uint8(i.Setting.Number)) 103 | i.config.mu.Lock() 104 | defer i.config.mu.Unlock() 105 | delete(i.config.claimed, i.Setting.Number) 106 | i.config = nil 107 | } 108 | 109 | func (i *Interface) openEndpoint(epAddr EndpointAddress) (*endpoint, error) { 110 | var ep EndpointDesc 111 | ep, ok := i.Setting.Endpoints[epAddr] 112 | if !ok { 113 | return nil, fmt.Errorf("%s does not have endpoint with address %s. Available endpoints: %v", i, epAddr, i.Setting.sortedEndpointIds()) 114 | } 115 | return &endpoint{ 116 | InterfaceSetting: i.Setting, 117 | Desc: ep, 118 | h: i.config.dev.handle, 119 | ctx: i.config.dev.ctx, 120 | }, nil 121 | } 122 | 123 | // InEndpoint prepares an IN endpoint for transfer. 124 | func (i *Interface) InEndpoint(epNum int) (*InEndpoint, error) { 125 | if i.config == nil { 126 | return nil, fmt.Errorf("InEndpoint(%d) called on %s after Close", epNum, i) 127 | } 128 | ep, err := i.openEndpoint(EndpointAddress(0x80 | epNum)) 129 | if err != nil { 130 | return nil, err 131 | } 132 | return &InEndpoint{ 133 | endpoint: ep, 134 | }, nil 135 | } 136 | 137 | // OutEndpoint prepares an OUT endpoint for transfer. 138 | func (i *Interface) OutEndpoint(epNum int) (*OutEndpoint, error) { 139 | if i.config == nil { 140 | return nil, fmt.Errorf("OutEndpoint(%d) called on %s after Close", epNum, i) 141 | } 142 | ep, err := i.openEndpoint(EndpointAddress(epNum)) 143 | if err != nil { 144 | return nil, err 145 | } 146 | return &OutEndpoint{ 147 | endpoint: ep, 148 | }, nil 149 | } 150 | -------------------------------------------------------------------------------- /libusb_cgo_benchmark_test.go: -------------------------------------------------------------------------------- 1 | package gousb 2 | 3 | import "testing" 4 | 5 | func BenchmarkCGo(b *testing.B) { 6 | for _, bc := range []struct { 7 | name string 8 | bfunc func(*libusbContext, int) 9 | }{ 10 | { 11 | name: "simple function", 12 | bfunc: func(ctx *libusbContext, N int) { 13 | for i := 0; i < N; i++ { 14 | libusbSetDebug(ctx, i&1) 15 | } 16 | }, 17 | }, 18 | { 19 | name: "method", 20 | bfunc: func(ctx *libusbContext, N int) { 21 | impl := libusbImpl{} 22 | for i := 0; i < N; i++ { 23 | impl.setDebug(ctx, i&1) 24 | } 25 | }, 26 | }, 27 | { 28 | name: "interface", 29 | bfunc: func(ctx *libusbContext, N int) { 30 | var intf libusbIntf = libusbImpl{} 31 | for i := 0; i < N; i++ { 32 | intf.setDebug(ctx, i&1) 33 | } 34 | }, 35 | }, 36 | } { 37 | b.Run(bc.name, func(b *testing.B) { 38 | ctx, err := libusbImpl{}.init() 39 | if err != nil { 40 | b.Fatalf("libusb_init() failed: %v", err) 41 | } 42 | defer libusbImpl{}.exit(ctx) 43 | b.ResetTimer() 44 | bc.bfunc(ctx, b.N) 45 | }) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lsusb/.gitignore: -------------------------------------------------------------------------------- 1 | lsusb 2 | -------------------------------------------------------------------------------- /lsusb/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Copyright 2016 the gousb Authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | // lsusb lists attached USB devices. 17 | package main 18 | 19 | import ( 20 | "flag" 21 | "fmt" 22 | "log" 23 | 24 | "github.com/google/gousb" 25 | "github.com/google/gousb/usbid" 26 | ) 27 | 28 | var ( 29 | debug = flag.Int("debug", 0, "libusb debug level (0..3)") 30 | ) 31 | 32 | func main() { 33 | flag.Parse() 34 | 35 | // Only one context should be needed for an application. It should always be closed. 36 | ctx := gousb.NewContext() 37 | defer ctx.Close() 38 | 39 | // Debugging can be turned on; this shows some of the inner workings of the libusb package. 40 | ctx.Debug(*debug) 41 | 42 | // OpenDevices is used to find the devices to open. 43 | devs, err := ctx.OpenDevices(func(desc *gousb.DeviceDesc) bool { 44 | // The usbid package can be used to print out human readable information. 45 | fmt.Printf("%03d.%03d %s:%s %s\n", desc.Bus, desc.Address, desc.Vendor, desc.Product, usbid.Describe(desc)) 46 | fmt.Printf(" Protocol: %s\n", usbid.Classify(desc)) 47 | 48 | // The configurations can be examined from the DeviceDesc, though they can only 49 | // be set once the device is opened. All configuration references must be closed, 50 | // to free up the memory in libusb. 51 | for _, cfg := range desc.Configs { 52 | // This loop just uses more of the built-in and usbid pretty printing to list 53 | // the USB devices. 54 | fmt.Printf(" %s:\n", cfg) 55 | for _, intf := range cfg.Interfaces { 56 | fmt.Printf(" --------------\n") 57 | for _, ifSetting := range intf.AltSettings { 58 | fmt.Printf(" %s\n", ifSetting) 59 | fmt.Printf(" %s\n", usbid.Classify(ifSetting)) 60 | for _, end := range ifSetting.Endpoints { 61 | fmt.Printf(" %s\n", end) 62 | } 63 | } 64 | } 65 | fmt.Printf(" --------------\n") 66 | } 67 | 68 | // After inspecting the descriptor, return true or false depending on whether 69 | // the device is "interesting" or not. Any descriptor for which true is returned 70 | // opens a Device which is retuned in a slice (and must be subsequently closed). 71 | return false 72 | }) 73 | 74 | // All Devices returned from OpenDevices must be closed. 75 | defer func() { 76 | for _, d := range devs { 77 | d.Close() 78 | } 79 | }() 80 | 81 | // OpenDevices can occasionally fail, so be sure to check its return value. 82 | if err != nil { 83 | log.Fatalf("list: %s", err) 84 | } 85 | 86 | for _, dev := range devs { 87 | // Once the device has been selected from OpenDevices, it is opened 88 | // and can be interacted with. 89 | _ = dev 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /misc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Copyright 2016 the gousb Authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package gousb 17 | 18 | import ( 19 | "fmt" 20 | ) 21 | 22 | // BCD is a binary-coded decimal version number. Its first 8 bits represent 23 | // the major version number, its last 8 bits represent the minor version number. 24 | // Major and minor are composed of 4+4 bits, where each 4 bits represents 25 | // a decimal digit. 26 | // Example: BCD(0x1234) means major 12 (decimal) and minor 34 (decimal). 27 | type BCD uint16 28 | 29 | // Major is the major number of the BCD. 30 | func (s BCD) Major() uint8 { 31 | maj := uint8(s >> 8) 32 | return 10*(maj>>4) + maj&0x0f 33 | } 34 | 35 | // Minor is the minor number of the BCD. 36 | func (s BCD) Minor() uint8 { 37 | min := uint8(s & 0xff) 38 | return 10*(min>>4) + min&0x0f 39 | } 40 | 41 | // String returns a dotted representation of the BCD (major.minor). 42 | func (s BCD) String() string { 43 | return fmt.Sprintf("%d.%02d", s.Major(), s.Minor()) 44 | } 45 | 46 | // Version returns a BCD version number with given major/minor. 47 | func Version(major, minor uint8) BCD { 48 | return (BCD(major)/10)<<12 | (BCD(major)%10)<<8 | (BCD(minor)/10)<<4 | BCD(minor)%10 49 | } 50 | 51 | // ID represents a vendor or product ID. 52 | type ID uint16 53 | 54 | // String returns a hexadecimal ID. 55 | func (id ID) String() string { 56 | return fmt.Sprintf("%04x", int(id)) 57 | } 58 | -------------------------------------------------------------------------------- /misc_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Copyright 2016 the gousb Authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package gousb 17 | 18 | import ( 19 | "testing" 20 | ) 21 | 22 | func TestBCD(t *testing.T) { 23 | t.Parallel() 24 | tests := []struct { 25 | major, minor uint8 26 | bcd BCD 27 | str string 28 | }{ 29 | {1, 1, 0x0101, "1.01"}, 30 | {12, 34, 0x1234, "12.34"}, 31 | } 32 | 33 | for _, test := range tests { 34 | bcd := Version(test.major, test.minor) 35 | if bcd != test.bcd { 36 | t.Errorf("Version(%d, %d): got BCD %04x, want %04x", test.major, test.minor, uint16(bcd), uint16(test.bcd)) 37 | continue 38 | } 39 | if got, want := bcd.String(), test.str; got != want { 40 | t.Errorf("String(%04x) = %q, want %q", uint16(test.bcd), got, want) 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /rawread/.gitignore: -------------------------------------------------------------------------------- 1 | rawread 2 | -------------------------------------------------------------------------------- /rawread/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Copyright 2016 the gousb Authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | // rawread attempts to read from the specified USB device. 17 | package main 18 | 19 | import ( 20 | "context" 21 | "flag" 22 | "fmt" 23 | "log" 24 | "os" 25 | "strconv" 26 | "strings" 27 | 28 | "github.com/google/gousb" 29 | ) 30 | 31 | var ( 32 | vidPID = flag.String("vidpid", "", "VID:PID of the device to which to connect. Exclusive with busaddr flag.") 33 | busAddr = flag.String("busaddr", "", "Bus:address of the device to which to connect. Exclusive with vidpid flag.") 34 | config = flag.Int("config", 1, "Configuration number to use with the device.") 35 | iface = flag.Int("interface", 0, "Interface to use on the device.") 36 | alternate = flag.Int("alternate", 0, "Alternate setting to use on the interface.") 37 | endpoint = flag.Int("endpoint", 1, "Endpoint number to which to connect (without the leading 0x8).") 38 | debug = flag.Int("debug", 3, "Debug level for libusb.") 39 | size = flag.Int("read_size", 1024, "Number of bytes of data to read in a single transaction.") 40 | bufSize = flag.Int("buffer_size", 0, "Number of buffer transfers, for data prefetching.") 41 | num = flag.Int("read_num", 0, "Number of read transactions to perform. 0 means infinite.") 42 | timeout = flag.Duration("timeout", 0, "Timeout for the command. 0 means infinite.") 43 | ) 44 | 45 | func parseVIDPID(vidPid string) (gousb.ID, gousb.ID, error) { 46 | s := strings.Split(vidPid, ":") 47 | if len(s) != 2 { 48 | return 0, 0, fmt.Errorf("want VID:PID, two 32-bit hex numbers separated by colon, e.g. 1d6b:0002") 49 | } 50 | vid, err := strconv.ParseUint(s[0], 16, 32) 51 | if err != nil { 52 | return 0, 0, fmt.Errorf("VID must be a hexadecimal 32-bit number, e.g. 1d6b") 53 | } 54 | pid, err := strconv.ParseUint(s[1], 16, 32) 55 | if err != nil { 56 | return 0, 0, fmt.Errorf("PID must be a hexadecimal 32-bit number, e.g. 1d6b") 57 | } 58 | return gousb.ID(vid), gousb.ID(pid), nil 59 | } 60 | 61 | func parseBusAddr(busAddr string) (int, int, error) { 62 | s := strings.Split(busAddr, ":") 63 | if len(s) != 2 { 64 | return 0, 0, fmt.Errorf("want bus:addr, two 8-bit decimal unsigned integers separated by colon, e.g. 1:1") 65 | } 66 | bus, err := strconv.ParseUint(s[0], 10, 8) 67 | if err != nil { 68 | return 0, 0, fmt.Errorf("bus number must be an 8-bit decimal unsigned integer") 69 | } 70 | addr, err := strconv.ParseUint(s[1], 10, 8) 71 | if err != nil { 72 | return 0, 0, fmt.Errorf("device address must be an 8-bit decimal unsigned integer") 73 | } 74 | return int(bus), int(addr), nil 75 | } 76 | 77 | type contextReader interface { 78 | ReadContext(context.Context, []byte) (int, error) 79 | } 80 | 81 | func main() { 82 | flag.Parse() 83 | 84 | // Only one context should be needed for an application. It should always be closed. 85 | ctx := gousb.NewContext() 86 | defer ctx.Close() 87 | 88 | ctx.Debug(*debug) 89 | 90 | var devName string 91 | var vid, pid gousb.ID 92 | var bus, addr int 93 | switch { 94 | case *vidPID == "" && *busAddr == "": 95 | log.Fatal("You need to specify the device through a --vidpid flag or through a --busaddr flag.") 96 | case *vidPID != "" && *busAddr != "": 97 | log.Fatal("You can't use --vidpid flag together with --busaddr. Pick one.") 98 | case *vidPID != "": 99 | var err error 100 | vid, pid, err = parseVIDPID(*vidPID) 101 | if err != nil { 102 | log.Fatalf("Invalid value for --vidpid (%q): %v", *vidPID, err) 103 | } 104 | devName = fmt.Sprintf("VID:PID %s:%s", vid, pid) 105 | default: 106 | var err error 107 | bus, addr, err = parseBusAddr(*busAddr) 108 | if err != nil { 109 | log.Fatalf("Invalid value for --busaddr (%q): %v", *busAddr, err) 110 | } 111 | devName = fmt.Sprintf("bus:addr %d:%d", bus, addr) 112 | } 113 | 114 | log.Printf("Scanning for device %q...", devName) 115 | // OpenDevices is used to find the devices to open. 116 | devs, err := ctx.OpenDevices(func(desc *gousb.DeviceDesc) bool { 117 | switch { 118 | case vid == desc.Vendor && pid == desc.Product: 119 | return true 120 | case bus == desc.Bus && addr == desc.Address: 121 | return true 122 | } 123 | return false 124 | }) 125 | // All Devices returned from OpenDevices must be closed. 126 | defer func() { 127 | for _, d := range devs { 128 | d.Close() 129 | } 130 | }() 131 | 132 | // OpenDevices can occasionally fail, so be sure to check its return value. 133 | if err != nil { 134 | log.Printf("Warning: OpenDevices: %s.", err) 135 | } 136 | switch { 137 | case len(devs) == 0: 138 | log.Fatal("No matching devices found.") 139 | case len(devs) > 1: 140 | log.Printf("Warning: multiple devices found. Using bus %d, addr %d.", devs[0].Desc.Bus, devs[0].Desc.Address) 141 | for _, d := range devs[1:] { 142 | d.Close() 143 | } 144 | devs = devs[:1] 145 | } 146 | dev := devs[0] 147 | 148 | log.Print("Enabling autodetach") 149 | dev.SetAutoDetach(true) 150 | 151 | log.Printf("Setting configuration %d...", *config) 152 | cfg, err := dev.Config(*config) 153 | if err != nil { 154 | log.Fatalf("dev.Config(%d): %v", *config, err) 155 | } 156 | log.Printf("Claiming interface %d (alt setting %d)...", *iface, *alternate) 157 | intf, err := cfg.Interface(*iface, *alternate) 158 | if err != nil { 159 | log.Fatalf("cfg.Interface(%d, %d): %v", *iface, *alternate, err) 160 | } 161 | 162 | log.Printf("Using endpoint %d...", *endpoint) 163 | ep, err := intf.InEndpoint(*endpoint) 164 | if err != nil { 165 | log.Fatalf("dev.InEndpoint(): %s", err) 166 | } 167 | log.Printf("Found endpoint: %s", ep) 168 | var rdr contextReader = ep 169 | if *bufSize > 1 { 170 | log.Print("Creating buffer...") 171 | s, err := ep.NewStream(*size, *bufSize) 172 | if err != nil { 173 | log.Fatalf("ep.NewStream(): %v", err) 174 | } 175 | defer s.Close() 176 | rdr = s 177 | } 178 | 179 | opCtx := context.Background() 180 | if *timeout > 0 { 181 | var done func() 182 | opCtx, done = context.WithTimeout(opCtx, *timeout) 183 | defer done() 184 | } 185 | buf := make([]byte, *size) 186 | log.Print("Reading...") 187 | for i := 0; *num == 0 || i < *num; i++ { 188 | num, err := rdr.ReadContext(opCtx, buf) 189 | if err != nil { 190 | log.Fatalf("Reading from device failed: %v", err) 191 | } 192 | os.Stdout.Write(buf[:num]) 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /transfer.c: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Copyright 2016 the gousb Authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | void print_xfer(struct libusb_transfer *xfer); 22 | void xferCallback(struct libusb_transfer*); 23 | 24 | int submit(struct libusb_transfer *xfer) { 25 | xfer->callback = (libusb_transfer_cb_fn)(&xferCallback); 26 | xfer->status = -1; 27 | return libusb_submit_transfer(xfer); 28 | } 29 | 30 | void print_xfer(struct libusb_transfer *xfer) { 31 | int i; 32 | 33 | printf("Transfer:\n"); 34 | printf(" dev_handle: %p\n", xfer->dev_handle); 35 | printf(" flags: %08x\n", xfer->flags); 36 | printf(" endpoint: %x\n", xfer->endpoint); 37 | printf(" type: %x\n", xfer->type); 38 | printf(" timeout: %dms\n", xfer->timeout); 39 | printf(" status: %x\n", xfer->status); 40 | printf(" length: %d (act: %d)\n", xfer->length, xfer->actual_length); 41 | printf(" callback: %p\n", xfer->callback); 42 | printf(" user_data: %p\n", xfer->user_data); 43 | printf(" buffer: %p\n", xfer->buffer); 44 | printf(" num_iso_pkts: %d\n", xfer->num_iso_packets); 45 | printf(" packets:\n"); 46 | for (i = 0; i < xfer->num_iso_packets; i++) { 47 | printf(" [%04d] %d (act: %d) %x\n", i, 48 | xfer->iso_packet_desc[i].length, 49 | xfer->iso_packet_desc[i].actual_length, 50 | xfer->iso_packet_desc[i].status); 51 | } 52 | } 53 | 54 | // compact the data in an isochronous transfer. The contents of individual 55 | // iso packets are shifted left, so that no gaps are left between them. 56 | // Status is set to the first non-zero status of an iso packet. 57 | int gousb_compact_iso_data(struct libusb_transfer *xfer, unsigned char *status) { 58 | int i; 59 | int sum = 0; 60 | unsigned char *in = xfer->buffer; 61 | unsigned char *out = xfer->buffer; 62 | for (i = 0; i < xfer->num_iso_packets; i++) { 63 | struct libusb_iso_packet_descriptor pkt = xfer->iso_packet_desc[i]; 64 | if (pkt.status != 0) { 65 | *status = pkt.status; 66 | break; 67 | } 68 | // Copy the data 69 | int len = pkt.actual_length; 70 | memmove(out, in, len); 71 | // Increment offsets 72 | sum += len; 73 | in += pkt.length; 74 | out += len; 75 | } 76 | return sum; 77 | } 78 | 79 | // allocates a libusb transfer and a buffer for packet data. 80 | struct libusb_transfer *gousb_alloc_transfer_and_buffer(int bufLen, int isoPackets) { 81 | struct libusb_transfer *xfer = libusb_alloc_transfer(isoPackets); 82 | if (xfer == NULL) { 83 | return NULL; 84 | } 85 | xfer->buffer = (unsigned char*)malloc(bufLen); 86 | if (xfer->buffer == NULL) { 87 | libusb_free_transfer(xfer); 88 | return NULL; 89 | } 90 | xfer->length = bufLen; 91 | return xfer; 92 | } 93 | 94 | // frees a libusb transfer and its buffer. The buffer of the given 95 | // libusb_transfer must have been allocated with alloc_transfer_and_buffer. 96 | void gousb_free_transfer_and_buffer(struct libusb_transfer *xfer) { 97 | free(xfer->buffer); 98 | xfer->length = 0; 99 | libusb_free_transfer(xfer); 100 | } 101 | -------------------------------------------------------------------------------- /transfer.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 the gousb Authors. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package gousb 16 | 17 | import ( 18 | "context" 19 | "errors" 20 | "runtime" 21 | "sync" 22 | ) 23 | 24 | type usbTransfer struct { 25 | // mu protects the transfer state. 26 | mu sync.Mutex 27 | // xfer is the allocated libusb_transfer. 28 | xfer *libusbTransfer 29 | // buf is the buffer allocated for the transfer. The underlying memory 30 | // is allocated by the C code, both buf and xfer.buffer point to the same 31 | // memory. 32 | buf []byte 33 | // done is blocking until the transfer is complete and data and transfer 34 | // status are available. 35 | done chan struct{} 36 | // submitted is true if submit() was called on this transfer. 37 | submitted bool 38 | // ctx is the Context that created this transfer. 39 | ctx *Context 40 | } 41 | 42 | // submits the transfer. After submit() the transfer is in flight and is owned by libusb. 43 | // It's not safe to access the contents of the transfer until wait() returns. 44 | // Once wait() returns, it's ok to re-use the same transfer structure by calling submit() again. 45 | func (t *usbTransfer) submit() error { 46 | t.mu.Lock() 47 | defer t.mu.Unlock() 48 | if t.submitted { 49 | return errors.New("transfer was already submitted and is not finished yet") 50 | } 51 | if err := t.ctx.libusb.submit(t.xfer); err != nil { 52 | return err 53 | } 54 | t.submitted = true 55 | return nil 56 | } 57 | 58 | // waits for libusb to signal the release of transfer data. 59 | // After wait returns, the transfer contents are safe to access 60 | // via t.buf. The number returned by wait indicates how many bytes 61 | // of the buffer were read or written by libusb, and it can be 62 | // smaller than the length of t.buf. 63 | func (t *usbTransfer) wait(ctx context.Context) (n int, err error) { 64 | t.mu.Lock() 65 | defer t.mu.Unlock() 66 | if !t.submitted { 67 | return 0, nil 68 | } 69 | select { 70 | case <-ctx.Done(): 71 | t.ctx.libusb.cancel(t.xfer) 72 | // after the transfer is cancelled, it will run a callback 73 | // that triggers the activation of t.done. 74 | <-t.done 75 | case <-t.done: 76 | } 77 | t.submitted = false 78 | n, status := t.ctx.libusb.data(t.xfer) 79 | if status != TransferCompleted { 80 | return n, status 81 | } 82 | return n, err 83 | } 84 | 85 | // cancel aborts a submitted transfer. The transfer is cancelled 86 | // asynchronously and the user still needs to wait() to return. 87 | func (t *usbTransfer) cancel() error { 88 | t.mu.Lock() 89 | defer t.mu.Unlock() 90 | if !t.submitted { 91 | return nil 92 | } 93 | err := t.ctx.libusb.cancel(t.xfer) 94 | if err == ErrorNotFound { 95 | // transfer already completed 96 | return nil 97 | } 98 | return err 99 | } 100 | 101 | // free releases the memory allocated for the transfer. 102 | // free should be called only if the transfer is not used by libusb, 103 | // i.e. it should not be called after submit() and before wait() returns. 104 | func (t *usbTransfer) free() error { 105 | t.mu.Lock() 106 | defer t.mu.Unlock() 107 | if t.submitted { 108 | return errors.New("free() cannot be called on a submitted transfer until wait() returns") 109 | } 110 | if t.xfer == nil { 111 | return nil 112 | } 113 | t.ctx.libusb.free(t.xfer) 114 | t.xfer = nil 115 | t.buf = nil 116 | t.done = nil 117 | return nil 118 | } 119 | 120 | // data returns the slice containing transfer buffer. 121 | func (t *usbTransfer) data() []byte { 122 | return t.buf 123 | } 124 | 125 | // newUSBTransfer allocates a new transfer structure and a new buffer for 126 | // communication with a given device/endpoint. 127 | func newUSBTransfer(ctx *Context, dev *libusbDevHandle, ei *EndpointDesc, bufLen int) (*usbTransfer, error) { 128 | var isoPackets, isoPktSize int 129 | if ei.TransferType == TransferTypeIsochronous { 130 | isoPktSize = ei.MaxPacketSize 131 | if bufLen < isoPktSize { 132 | isoPktSize = bufLen 133 | } 134 | if isoPktSize > 0 { 135 | isoPackets = bufLen / isoPktSize 136 | } else { 137 | isoPackets = 1 138 | } 139 | debug.Printf("New isochronous transfer - buffer length %d, using %d packets of %d bytes each", bufLen, isoPackets, isoPktSize) 140 | } 141 | 142 | done := make(chan struct{}, 1) 143 | xfer, err := ctx.libusb.alloc(dev, ei, isoPackets, bufLen, done) 144 | if err != nil { 145 | return nil, err 146 | } 147 | 148 | if ei.TransferType == TransferTypeIsochronous { 149 | ctx.libusb.setIsoPacketLengths(xfer, uint32(isoPktSize)) 150 | } 151 | 152 | t := &usbTransfer{ 153 | xfer: xfer, 154 | buf: ctx.libusb.buffer(xfer), 155 | done: done, 156 | ctx: ctx, 157 | } 158 | runtime.SetFinalizer(t, func(t *usbTransfer) { 159 | t.cancel() 160 | t.wait(context.Background()) 161 | t.free() 162 | }) 163 | return t, nil 164 | } 165 | -------------------------------------------------------------------------------- /transfer_stream.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 the gousb Authors. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package gousb 16 | 17 | import ( 18 | "context" 19 | "io" 20 | ) 21 | 22 | type transferIntf interface { 23 | submit() error 24 | cancel() error 25 | wait(context.Context) (int, error) 26 | free() error 27 | data() []byte 28 | } 29 | 30 | type stream struct { 31 | // a fifo of USB transfers. 32 | transfers chan transferIntf 33 | // err is the first encountered error, returned to the user. 34 | err error 35 | // finished is true if transfers has been already closed. 36 | finished bool 37 | } 38 | 39 | func (s *stream) gotError(err error) { 40 | if s.err == nil { 41 | s.err = err 42 | } 43 | } 44 | 45 | func (s *stream) noMore() { 46 | if !s.finished { 47 | close(s.transfers) 48 | s.finished = true 49 | } 50 | } 51 | 52 | func (s *stream) submitAll() { 53 | count := len(s.transfers) 54 | var all []transferIntf 55 | for i := 0; i < count; i++ { 56 | all = append(all, <-s.transfers) 57 | } 58 | for _, t := range all { 59 | if err := t.submit(); err != nil { 60 | t.free() 61 | s.gotError(err) 62 | s.noMore() 63 | return 64 | } 65 | s.transfers <- t 66 | } 67 | return 68 | } 69 | 70 | func (s *stream) flushRemaining() { 71 | s.noMore() 72 | for t := range s.transfers { 73 | t.cancel() 74 | t.wait(context.Background()) 75 | t.free() 76 | } 77 | } 78 | 79 | func (s *stream) done() { 80 | if s.err == nil { 81 | close(s.transfers) 82 | } 83 | } 84 | 85 | // ReadStream is a buffer that tries to prefetch data from the IN endpoint, 86 | // reducing the latency between subsequent Read()s. 87 | // ReadStream keeps prefetching data until Close() is called or until 88 | // an error is encountered. After Close(), the buffer might still have 89 | // data left from transfers that were initiated before Close. Read()ing 90 | // from the ReadStream will keep returning available data. When no more 91 | // data is left, io.EOF is returned. 92 | type ReadStream struct { 93 | s *stream 94 | // current holds the last transfer to return. 95 | current transferIntf 96 | // total/used are the number of all/used bytes in the current transfer. 97 | total, used int 98 | } 99 | 100 | // Read reads data from the transfer stream. 101 | // The data will come from at most a single transfer, so the returned number 102 | // might be smaller than the length of p. 103 | // After a non-nil error is returned, all subsequent attempts to read will 104 | // return io.ErrClosedPipe. 105 | // Read cannot be called concurrently with other Read, ReadContext 106 | // or Close. 107 | func (r *ReadStream) Read(p []byte) (int, error) { 108 | return r.ReadContext(context.Background(), p) 109 | } 110 | 111 | // ReadContext reads data from the transfer stream. 112 | // The data will come from at most a single transfer, so the returned number 113 | // might be smaller than the length of p. 114 | // After a non-nil error is returned, all subsequent attempts to read will 115 | // return io.ErrClosedPipe. 116 | // ReadContext cannot be called concurrently with other Read, ReadContext 117 | // or Close. 118 | // The context passed controls the cancellation of this particular read 119 | // operation within the stream. The semantics is identical to 120 | // Endpoint.ReadContext. 121 | func (r *ReadStream) ReadContext(ctx context.Context, p []byte) (int, error) { 122 | if r.s.transfers == nil { 123 | return 0, io.ErrClosedPipe 124 | } 125 | if r.current == nil { 126 | t, ok := <-r.s.transfers 127 | if !ok { 128 | // no more transfers in flight 129 | r.s.transfers = nil 130 | return 0, r.s.err 131 | } 132 | n, err := t.wait(ctx) 133 | if err != nil { 134 | // wait error aborts immediately, all remaining data is invalid. 135 | t.free() 136 | r.s.flushRemaining() 137 | r.s.transfers = nil 138 | return n, err 139 | } 140 | r.current = t 141 | r.total = n 142 | r.used = 0 143 | } 144 | use := r.total - r.used 145 | if use > len(p) { 146 | use = len(p) 147 | } 148 | copy(p, r.current.data()[r.used:r.used+use]) 149 | r.used += use 150 | if r.used == r.total { 151 | if r.s.err == nil { 152 | if err := r.current.submit(); err == nil { 153 | // guaranteed to not block, len(transfers) == number of allocated transfers 154 | r.s.transfers <- r.current 155 | } else { 156 | r.s.gotError(err) 157 | r.s.noMore() 158 | } 159 | } 160 | if r.s.err != nil { 161 | r.current.free() 162 | } 163 | r.current = nil 164 | } 165 | return use, nil 166 | } 167 | 168 | // Close signals that the transfer should stop. After Close is called, 169 | // subsequent Read()s will return data from all transfers that were already 170 | // in progress before returning an io.EOF error, unless another error 171 | // was encountered earlier. 172 | // Close cannot be called concurrently with Read. 173 | func (r *ReadStream) Close() error { 174 | if r.s.transfers == nil { 175 | return nil 176 | } 177 | r.s.gotError(io.EOF) 178 | r.s.noMore() 179 | return nil 180 | } 181 | 182 | // WriteStream is a buffer that will send data asynchronously, reducing 183 | // the latency between subsequent Write()s. 184 | type WriteStream struct { 185 | s *stream 186 | total int 187 | } 188 | 189 | // Write sends the data to the endpoint. Write returning a nil error doesn't 190 | // mean that data was written to the device, only that it was written to the 191 | // buffer. Only a call to Close() that returns nil error guarantees that 192 | // all transfers have succeeded. 193 | // If the slice passed to Write does not align exactly with the transfer 194 | // buffer size (as declared in a call to NewStream), the last USB transfer 195 | // of this Write will be sent with less data than the full buffer. 196 | // After a non-nil error is returned, all subsequent attempts to write will 197 | // return io.ErrClosedPipe. 198 | // If Write encounters an error when preparing the transfer, the stream 199 | // will still try to complete any pending transfers. The total number 200 | // of bytes successfully written can be retrieved through a Written() 201 | // call after Close() has returned. 202 | // Write cannot be called concurrently with another Write, Written or Close. 203 | func (w *WriteStream) Write(p []byte) (int, error) { 204 | return w.WriteContext(context.Background(), p) 205 | } 206 | 207 | // WriteContext sends the data to the endpoint. Write returning a nil error doesn't 208 | // mean that data was written to the device, only that it was written to the 209 | // buffer. Only a call to Close() that returns nil error guarantees that 210 | // all transfers have succeeded. 211 | // If the slice passed to WriteContext does not align exactly with the transfer 212 | // buffer size (as declared in a call to NewStream), the last USB transfer 213 | // of this Write will be sent with less data than the full buffer. 214 | // After a non-nil error is returned, all subsequent attempts to write will 215 | // return io.ErrClosedPipe. 216 | // If WriteContext encounters an error when preparing the transfer, the stream 217 | // will still try to complete any pending transfers. The total number 218 | // of bytes successfully written can be retrieved through a Written() 219 | // call after Close() has returned. 220 | // WriteContext cannot be called concurrently with another Write, WriteContext, 221 | // Written, Close or CloseContext. 222 | func (w *WriteStream) WriteContext(ctx context.Context, p []byte) (int, error) { 223 | if w.s.transfers == nil || w.s.err != nil { 224 | return 0, io.ErrClosedPipe 225 | } 226 | written := 0 227 | all := len(p) 228 | for written < all { 229 | t := <-w.s.transfers 230 | n, err := t.wait(ctx) // unsubmitted transfers will return 0 bytes and no error 231 | w.total += n 232 | if err != nil { 233 | t.free() 234 | w.s.gotError(err) 235 | // This branch is used only after all the transfers were set in flight. 236 | // That means all transfers left in the queue are in flight. 237 | // They must be ignored, since this wait() failed. 238 | w.s.flushRemaining() 239 | return written, err 240 | } 241 | use := all - written 242 | if max := len(t.data()); use > max { 243 | use = max 244 | } 245 | copy(t.data(), p[written:written+use]) 246 | if err := t.submit(); err != nil { 247 | t.free() 248 | w.s.gotError(err) 249 | // Even though this submit failed, all the transfers in flight are still valid. 250 | // Don't flush remaining transfers. 251 | // We won't submit any more transfers. 252 | w.s.noMore() 253 | return written, err 254 | } 255 | written += use 256 | w.s.transfers <- t // guaranteed non blocking 257 | } 258 | return written, nil 259 | } 260 | 261 | // Close signals end of data to write. Close blocks until all transfers 262 | // that were sent are finished. The error returned by Close is the first 263 | // error encountered during writing the entire stream (if any). 264 | // Close returning nil indicates all transfers completed successfully. 265 | // After Close, the total number of bytes successfully written can be 266 | // retrieved using Written(). 267 | // Close may not be called concurrently with Write, Close or Written. 268 | func (w *WriteStream) Close() error { 269 | return w.CloseContext(context.Background()) 270 | } 271 | 272 | // CloseContext signals end of data to write. CloseContext blocks until all 273 | // transfers that were sent are finished or until the context is canceled. The 274 | // error returned by CloseContext is the first error encountered during writing 275 | // the entire stream (if any). 276 | // CloseContext returning nil indicates all transfers completed successfully. 277 | // After CloseContext, the total number of bytes successfully written can be 278 | // retrieved using Written(). 279 | // CloseContext may not be called concurrently with Write, WriteContext, Close, 280 | // CloseContext or Written. 281 | func (w *WriteStream) CloseContext(ctx context.Context) error { 282 | if w.s.transfers == nil { 283 | return io.ErrClosedPipe 284 | } 285 | w.s.noMore() 286 | for t := range w.s.transfers { 287 | n, err := t.wait(ctx) 288 | w.total += n 289 | t.free() 290 | if err != nil { 291 | w.s.gotError(err) 292 | w.s.flushRemaining() 293 | } 294 | t.free() 295 | } 296 | w.s.transfers = nil 297 | return w.s.err 298 | } 299 | 300 | // Written returns the number of bytes successfully written by the stream. 301 | // Written may be called only after Close() or CloseContext() 302 | // has been called and returned. 303 | func (w *WriteStream) Written() int { 304 | return w.total 305 | } 306 | 307 | func newStream(tt []transferIntf) *stream { 308 | s := &stream{ 309 | transfers: make(chan transferIntf, len(tt)), 310 | } 311 | for _, t := range tt { 312 | s.transfers <- t 313 | } 314 | return s 315 | } 316 | -------------------------------------------------------------------------------- /transfer_stream_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 the gousb Authors. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package gousb 16 | 17 | import ( 18 | "bytes" 19 | "context" 20 | "errors" 21 | "fmt" 22 | "io" 23 | "reflect" 24 | "strconv" 25 | "testing" 26 | ) 27 | 28 | var fakeTransferBuf = make([]byte, 1500) 29 | 30 | type fakeStreamResult struct { 31 | n int 32 | waitErr error 33 | submitErr error 34 | } 35 | 36 | type fakeStreamTransfer struct { 37 | res []fakeStreamResult 38 | inFlight bool 39 | released bool 40 | } 41 | 42 | func (f *fakeStreamTransfer) submit() error { 43 | if f.released { 44 | return errors.New("submit() called on a free()d transfer") 45 | } 46 | if f.inFlight { 47 | return errors.New("submit() called twice") 48 | } 49 | if len(f.res) == 0 { 50 | return errors.New("submit() called but fake result missing") 51 | } 52 | f.inFlight = true 53 | res := f.res[0] 54 | if res.submitErr != nil { 55 | f.res = nil 56 | return res.submitErr 57 | } 58 | return nil 59 | } 60 | 61 | func (f *fakeStreamTransfer) wait(ctx context.Context) (int, error) { 62 | if f.released { 63 | return 0, errors.New("wait() called on a free()d transfer") 64 | } 65 | if !f.inFlight { 66 | return 0, nil 67 | } 68 | if len(f.res) == 0 { 69 | return 0, errors.New("wait() called but fake result missing") 70 | } 71 | f.inFlight = false 72 | res := f.res[0] 73 | if res.waitErr == nil { 74 | f.res = f.res[1:] 75 | } else { 76 | f.res = nil 77 | } 78 | return res.n, res.waitErr 79 | } 80 | 81 | func (f *fakeStreamTransfer) free() error { 82 | if f.released { 83 | return errors.New("free() called twice") 84 | } 85 | f.released = true 86 | return nil 87 | } 88 | 89 | func (f *fakeStreamTransfer) cancel() error { return nil } 90 | func (f *fakeStreamTransfer) data() []byte { return fakeTransferBuf } 91 | 92 | var errSentinel = errors.New("sentinel error") 93 | 94 | type readRes struct { 95 | n int 96 | err error 97 | } 98 | 99 | func (r readRes) String() string { 100 | var buf bytes.Buffer 101 | fmt.Fprintf(&buf, "<%d bytes", r.n) 102 | if r.err != nil { 103 | fmt.Fprintf(&buf, ", error: %s", r.err.Error()) 104 | } 105 | buf.WriteString(">") 106 | return buf.String() 107 | } 108 | 109 | func TestTransferReadStream(t *testing.T) { 110 | t.Parallel() 111 | for tcNum, tc := range []struct { 112 | desc string 113 | closeBefore int 114 | // transfers is a list of allocated transfers, each transfers 115 | // carries a list of results for subsequent submits/waits. 116 | transfers [][]fakeStreamResult 117 | want []readRes 118 | }{ 119 | { 120 | desc: "two transfers submitted, close, read returns both and EOF", 121 | closeBefore: 1, 122 | transfers: [][]fakeStreamResult{ 123 | {{n: 400}}, 124 | {{n: 400}}, 125 | }, 126 | want: []readRes{ 127 | {n: 400}, 128 | {n: 400}, 129 | {err: io.EOF}, 130 | {err: io.ErrClosedPipe}, 131 | }, 132 | }, 133 | { 134 | desc: "two transfers, two and a half cycles through transfer queue", 135 | closeBefore: 4, 136 | transfers: [][]fakeStreamResult{ 137 | {{n: 400}, {n: 400}, {n: 400}, {waitErr: errors.New("fake wait error")}}, 138 | {{n: 400}, {n: 400}, {waitErr: errors.New("fake wait error")}}, 139 | }, 140 | want: []readRes{ 141 | {n: 400}, 142 | {n: 400}, 143 | {n: 400}, 144 | {n: 400}, 145 | {n: 400}, 146 | {err: io.EOF}, 147 | {err: io.ErrClosedPipe}, 148 | }, 149 | }, 150 | { 151 | desc: "4 transfers submitted, two return, third fails on wait", 152 | transfers: [][]fakeStreamResult{ 153 | {{n: 500}}, 154 | {{n: 500}}, 155 | {{n: 123, waitErr: errSentinel}}, 156 | {{n: 500}}, 157 | }, 158 | want: []readRes{ 159 | {n: 400}, 160 | {n: 100}, 161 | {n: 400}, 162 | {n: 100}, 163 | {n: 123, err: errSentinel}, 164 | {err: io.ErrClosedPipe}, 165 | }, 166 | }, 167 | { 168 | desc: "2 transfers, second submit fails initialization but error overshadowed by wait error", 169 | transfers: [][]fakeStreamResult{ 170 | {{n: 123, waitErr: errSentinel}}, 171 | {{submitErr: errors.New("fake submit error")}}, 172 | }, 173 | want: []readRes{ 174 | {n: 123, err: errSentinel}, 175 | {err: io.ErrClosedPipe}, 176 | }, 177 | }, 178 | { 179 | desc: "2 transfers, second submit fails during initialization", 180 | transfers: [][]fakeStreamResult{ 181 | {{n: 400}}, 182 | {{submitErr: errSentinel}}, 183 | }, 184 | want: []readRes{ 185 | {n: 400}, 186 | {err: errSentinel}, 187 | {err: io.ErrClosedPipe}, 188 | }, 189 | }, 190 | { 191 | desc: "2 transfers, 3rd submit fails during second round", 192 | transfers: [][]fakeStreamResult{ 193 | {{n: 400}, {submitErr: errSentinel}}, 194 | {{n: 400}}, 195 | }, 196 | want: []readRes{ 197 | {n: 400}, 198 | {n: 400}, 199 | {err: errSentinel}, 200 | {err: io.ErrClosedPipe}, 201 | }, 202 | }, 203 | { 204 | desc: "fail quickly", 205 | transfers: [][]fakeStreamResult{ 206 | {{waitErr: errSentinel}}, 207 | {{n: 500}}, 208 | {{n: 500}}, 209 | }, 210 | want: []readRes{ 211 | {err: errSentinel}, 212 | {err: io.ErrClosedPipe}, 213 | }, 214 | }, 215 | } { 216 | tcNum, tc := tcNum, tc // t.Parallel will delay the execution of the test, save the iteration values. 217 | t.Run(strconv.Itoa(tcNum), func(t *testing.T) { 218 | t.Parallel() 219 | t.Logf("Case %d: %s", tcNum, tc.desc) 220 | ftt := make([]*fakeStreamTransfer, len(tc.transfers)) 221 | tt := make([]transferIntf, len(tc.transfers)) 222 | for i := range tc.transfers { 223 | ftt[i] = &fakeStreamTransfer{ 224 | res: tc.transfers[i], 225 | } 226 | tt[i] = ftt[i] 227 | } 228 | s := ReadStream{s: newStream(tt)} 229 | s.s.submitAll() 230 | buf := make([]byte, 400) 231 | got := make([]readRes, len(tc.want)) 232 | for i := range tc.want { 233 | if i == tc.closeBefore-1 { 234 | t.Log("Close()") 235 | s.Close() 236 | } 237 | n, err := s.Read(buf) 238 | t.Logf("Read(): got %d, %v", n, err) 239 | got[i] = readRes{ 240 | n: n, 241 | err: err, 242 | } 243 | } 244 | if !reflect.DeepEqual(got, tc.want) { 245 | t.Errorf("Got Read() results:\n%v\nwant Read() results:\n%v\n", got, tc.want) 246 | } 247 | for i := range ftt { 248 | if !ftt[i].released { 249 | t.Errorf("Transfer #%d was not freed after stream completed", i) 250 | } 251 | } 252 | }) 253 | } 254 | } 255 | 256 | func TestTransferWriteStream(t *testing.T) { 257 | t.Parallel() 258 | for _, tc := range []struct { 259 | desc string 260 | transfers [][]fakeStreamResult 261 | writes []int 262 | want []int 263 | total int 264 | err error 265 | }{ 266 | { 267 | desc: "successful two transfers", 268 | transfers: [][]fakeStreamResult{ 269 | {{n: 1500}}, 270 | {{n: 1500}}, 271 | {{n: 1500}}, 272 | }, 273 | writes: []int{3000}, 274 | want: []int{3000}, 275 | total: 3000, 276 | }, 277 | { 278 | desc: "submit failed on second transfer", 279 | transfers: [][]fakeStreamResult{ 280 | {{n: 1500}}, 281 | {{submitErr: errSentinel}}, 282 | {{n: 1500}}, 283 | }, 284 | writes: []int{3000}, 285 | want: []int{1500}, 286 | total: 1500, 287 | err: errSentinel, 288 | }, 289 | { 290 | desc: "wait failed on second transfer", 291 | transfers: [][]fakeStreamResult{ 292 | {{n: 1500}}, 293 | {{waitErr: errSentinel}}, 294 | {{n: 1500}}, 295 | }, 296 | writes: []int{3000, 1500}, 297 | want: []int{3000, 1500}, 298 | total: 1500, 299 | err: errSentinel, 300 | }, 301 | { 302 | desc: "reused transfer", 303 | transfers: [][]fakeStreamResult{ 304 | {{n: 1500}, {n: 1500}}, 305 | {{n: 1500}, {n: 1500}}, 306 | {{n: 1500}, {n: 500}}, 307 | }, 308 | writes: []int{3000, 3000, 2000}, 309 | want: []int{3000, 3000, 2000}, 310 | total: 8000, 311 | }, 312 | { 313 | desc: "wait failed on reused transfer", 314 | transfers: [][]fakeStreamResult{ 315 | {{n: 1500}, {n: 1500}}, 316 | {{waitErr: errSentinel}, {n: 1500}}, 317 | {{n: 1500}, {n: 1500}}, 318 | }, 319 | writes: []int{1500, 1500, 1500, 1500, 1500}, 320 | want: []int{1500, 1500, 1500, 1500, 0}, 321 | total: 1500, 322 | err: errSentinel, 323 | }, 324 | } { 325 | tc := tc 326 | t.Run(tc.desc, func(t *testing.T) { 327 | t.Parallel() 328 | ftt := make([]*fakeStreamTransfer, len(tc.transfers)) 329 | tt := make([]transferIntf, len(tc.transfers)) 330 | for i := range tc.transfers { 331 | ftt[i] = &fakeStreamTransfer{ 332 | res: tc.transfers[i], 333 | } 334 | tt[i] = ftt[i] 335 | } 336 | s := WriteStream{s: newStream(tt)} 337 | for i, w := range tc.writes { 338 | got, err := s.Write(make([]byte, w)) 339 | if want := tc.want[i]; got != want { 340 | t.Errorf("WriteStream.Write #%d: got %d, want %d", i, got, want) 341 | } 342 | if err != nil && err != tc.err { 343 | t.Errorf("WriteStream.Write: got error %v, want %v", err, tc.err) 344 | } 345 | } 346 | if err := s.Close(); err != tc.err { 347 | t.Fatalf("WriteStream.Close: got %v, want %v", err, tc.err) 348 | } 349 | if err := s.Close(); err != io.ErrClosedPipe { 350 | t.Fatalf("second WriteStream.Close: got %v, want %v", err, io.ErrClosedPipe) 351 | } 352 | if got := s.Written(); got != tc.total { 353 | t.Fatalf("WriteStream.Written: got %d, want %d", got, tc.total) 354 | } 355 | }) 356 | } 357 | } 358 | -------------------------------------------------------------------------------- /transfer_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 the gousb Authors. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package gousb 16 | 17 | import ( 18 | "context" 19 | "testing" 20 | ) 21 | 22 | func TestNewTransfer(t *testing.T) { 23 | t.Parallel() 24 | ctx := newContextWithImpl(newFakeLibusb()) 25 | defer func() { 26 | if err := ctx.Close(); err != nil { 27 | t.Errorf("Context.Close(): %v", err) 28 | } 29 | }() 30 | 31 | for _, tc := range []struct { 32 | desc string 33 | dir EndpointDirection 34 | tt TransferType 35 | maxPkt int 36 | buf int 37 | wantIso int 38 | wantLength int 39 | wantTimeout int 40 | }{ 41 | { 42 | desc: "bulk in transfer, 512B packets", 43 | dir: EndpointDirectionIn, 44 | tt: TransferTypeBulk, 45 | maxPkt: 512, 46 | buf: 1024, 47 | wantLength: 512, 48 | }, 49 | { 50 | desc: "iso out transfer, 3 * 1024B packets", 51 | dir: EndpointDirectionOut, 52 | tt: TransferTypeIsochronous, 53 | maxPkt: 3 * 1024, 54 | buf: 10000, 55 | wantLength: 9216, 56 | }, 57 | { 58 | desc: "iso out transfer, 512B packets", 59 | dir: EndpointDirectionOut, 60 | tt: TransferTypeIsochronous, 61 | maxPkt: 512, 62 | buf: 2048, 63 | wantLength: 2048, 64 | }, 65 | } { 66 | xfer, err := newUSBTransfer(ctx, nil, &EndpointDesc{ 67 | Number: 2, 68 | Direction: tc.dir, 69 | TransferType: tc.tt, 70 | MaxPacketSize: tc.maxPkt, 71 | }, tc.buf) 72 | 73 | if err != nil { 74 | t.Fatalf("newUSBTransfer(): %v", err) 75 | } 76 | defer xfer.free() 77 | if got, want := len(xfer.data()), tc.wantLength; got != want { 78 | t.Errorf("xfer.buf: got %d bytes, want %d", got, want) 79 | } 80 | } 81 | } 82 | 83 | func TestTransferProtocol(t *testing.T) { 84 | t.Parallel() 85 | f := newFakeLibusb() 86 | ctx := newContextWithImpl(f) 87 | defer func() { 88 | if err := ctx.Close(); err != nil { 89 | t.Errorf("Context.Close(): %v", err) 90 | } 91 | }() 92 | 93 | xfers := make([]*usbTransfer, 2) 94 | var err error 95 | for i := 0; i < 2; i++ { 96 | xfers[i], err = newUSBTransfer(ctx, nil, &EndpointDesc{ 97 | Number: 6, 98 | Direction: EndpointDirectionIn, 99 | TransferType: TransferTypeBulk, 100 | MaxPacketSize: 512, 101 | }, 10240) 102 | if err != nil { 103 | t.Fatalf("newUSBTransfer: %v", err) 104 | } 105 | } 106 | 107 | partial := make(chan struct{}) 108 | go func() { 109 | ft := f.waitForSubmitted(nil) 110 | ft.setData([]byte{1, 2, 3, 4, 5}) 111 | ft.setStatus(TransferCompleted) 112 | 113 | ft = f.waitForSubmitted(nil) 114 | ft.setData(make([]byte, 99)) 115 | ft.setStatus(TransferCompleted) 116 | 117 | ft = f.waitForSubmitted(nil) 118 | ft.setData(make([]byte, 123)) 119 | close(partial) 120 | }() 121 | 122 | xfers[0].submit() 123 | xfers[1].submit() 124 | got, err := xfers[0].wait(context.Background()) 125 | if err != nil { 126 | t.Errorf("xfer#0.wait returned error %v, want nil", err) 127 | } 128 | if want := 5; got != want { 129 | t.Errorf("xfer#0.wait returned %d bytes, want %d", got, want) 130 | } 131 | got, err = xfers[1].wait(context.Background()) 132 | if err != nil { 133 | t.Errorf("xfer#0.wait returned error %v, want nil", err) 134 | } 135 | if want := 99; got != want { 136 | t.Errorf("xfer#0.wait returned %d bytes, want %d", got, want) 137 | } 138 | 139 | xfers[1].submit() 140 | <-partial 141 | xfers[1].cancel() 142 | got, err = xfers[1].wait(context.Background()) 143 | if err == nil { 144 | t.Error("xfer#1(resubmitted).wait returned error nil, want non-nil") 145 | } 146 | if want := 123; got != want { 147 | t.Errorf("xfer#1(resubmitted).wait returned %d bytes, want %d", got, want) 148 | } 149 | 150 | for _, x := range xfers { 151 | x.cancel() 152 | x.wait(context.Background()) 153 | x.free() 154 | } 155 | } 156 | 157 | func BenchmarkSubSlice(b *testing.B) { 158 | x := make([]byte, 512) 159 | start, len := 50, 50 160 | b.Run("start:start+len", func(b *testing.B) { 161 | for i := 0; i < b.N; i++ { 162 | y := x 163 | y = y[start : start+len] 164 | } 165 | }) 166 | b.Run("[start:][:len]", func(b *testing.B) { 167 | for i := 0; i < b.N; i++ { 168 | y := x 169 | y = y[start:][:len] 170 | } 171 | }) 172 | } 173 | -------------------------------------------------------------------------------- /usb.c: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Copyright 2018 the gousb Authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | #include 17 | 18 | void gousb_set_debug(libusb_context *ctx, int lvl) { 19 | // TODO(sebek): remove libusb_debug entirely in 2.1 or 3.0, 20 | // require libusb >= 1.0.22. libusb 1.0.22 sets API version 0x01000106. 21 | #if LIBUSB_API_VERSION >= 0x01000106 22 | libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, lvl); 23 | #else 24 | libusb_set_debug(ctx, lvl); 25 | #endif 26 | } 27 | -------------------------------------------------------------------------------- /usb.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Copyright 2016 the gousb Authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | /* 17 | Package gousb provides an low-level interface to attached USB devices. 18 | 19 | # A Short Tutorial 20 | 21 | A Context manages all resources necessary for communicating with USB 22 | devices. 23 | Through the Context users can iterate over available USB devices. 24 | 25 | The USB standard defines a mechanism of discovering USB device functionality 26 | through descriptors. After the device is attached and 27 | initialized by the host stack, it's possible to retrieve its descriptor 28 | (the device descriptor). It contains elements such as product and vendor IDs, 29 | bus number and device number (address) on the bus. 30 | 31 | In gousb, the Device struct represents a USB device. The Device struct’s Desc 32 | field contains all known information about the device. 33 | 34 | Among other information in the device descriptor is a list of configuration 35 | descriptors, accessible through Device.Desc.Configs. 36 | 37 | The USB standard allows one physical USB device to switch between different 38 | sets of behaviors, or working modes, by selecting one of the offered configs 39 | (each device has at least one). 40 | This allows the same device to sometimes present itself as e.g. a 3G modem, 41 | and sometimes as a flash drive with the drivers for that 3G modem. 42 | Configs are mutually exclusive, each device 43 | can have only one active config at a time. Switching the active config performs 44 | a light-weight device reset. Each config in the device descriptor has 45 | a unique identification number. 46 | 47 | In gousb a device config needs to be selected through Device.Config(num). 48 | It returns a Config struct that represents the device in this particular configuration. 49 | The configuration descriptor is accessible through Config.Desc. 50 | 51 | A config descriptor determines the list of available USB interfaces on the device. 52 | Each interface is a virtual device within the physical USB device and its active 53 | config. There can be many interfaces active concurrently. Interfaces are 54 | enumerated sequentially starting from zero. 55 | 56 | Additionally, each interface comes with a number of alternate settings for 57 | the interface, which are somewhat similar to device configs, but on the 58 | interface level. Each interface can have only a single alternate setting 59 | active at any time. Alternate settings are enumerated sequentially starting from 60 | zero. 61 | 62 | In gousb an interface and its alternate setting can be selected through 63 | Config.Interface(num, altNum). The Interface struct is the representation 64 | of the claimed interface with a particular alternate setting. 65 | The descriptor of the interface is available through Interface.Setting. 66 | 67 | An interface with a particular alternate setting defines up to 30 data 68 | endpoints, each identified by a unique address. The endpoint address is a combination 69 | of endpoint number (1..15) and endpoint directionality (IN/OUT). 70 | IN endpoints have addresses 0x81..0x8f, while OUT endpoints 0x01..0x0f. 71 | 72 | An endpoint can be considered similar to a UDP/IP port, 73 | except the data transfers are unidirectional. 74 | 75 | Endpoints are represented by the Endpoint struct, and all defined endpoints 76 | can be obtained through the Endpoints field of the Interface.Setting. 77 | 78 | Each endpoint descriptor (EndpointDesc) defined in the interface's endpoint 79 | map includes information about the type of the endpoint: 80 | 81 | - endpoint address 82 | 83 | - endpoint number 84 | 85 | - direction: IN (device-to-host) or OUT (host-to-device) 86 | 87 | - transfer type: USB standard defines a few distinct data transfer types: 88 | 89 | --- bulk - high throughput, but no guaranteed bandwidth and no latency guarantees, 90 | 91 | --- isochronous - medium throughput, guaranteed bandwidth, some latency guarantees, 92 | 93 | --- interrupt - low throughput, high latency guarantees. 94 | 95 | The endpoint descriptor determines the type of the transfer that will be used. 96 | 97 | - maximum packet size: maximum number of bytes that can be sent or received by the device in a single USB transaction. 98 | and a few other less frequently used pieces of endpoint information. 99 | 100 | An IN Endpoint can be opened for reading through Interface.InEndpoint(epNum), 101 | while an OUT Endpoint can be opened for writing through Interface.OutEndpoint(epNum). 102 | 103 | An InEndpoint implements the io.Reader interface, an OutEndpoint implements 104 | the io.Writer interface. Both Reads and Writes will accept larger slices 105 | of data than the endpoint's maximum packet size, the transfer will be split 106 | into smaller USB transactions as needed. But using Read/Write size equal 107 | to an integer multiple of maximum packet size helps with improving the transfer 108 | performance. 109 | 110 | Apart from 15 possible data endpoints, each USB device also has a control endpoint. 111 | The control endpoint is present regardless of the current device config, claimed 112 | interfaces and their alternate settings. It makes a lot of sense, as the control endpoint is actually used, among others, 113 | to issue commands to switch the active config or select an alternate setting for an interface. 114 | 115 | Control commands are also often used to control the behavior of the device. There is no single 116 | standard for control commands though, and many devices implement their custom control command schema. 117 | 118 | Control commands can be issued through Device.Control(). 119 | 120 | # See Also 121 | 122 | For more information about USB protocol and handling USB devices, 123 | see the excellent "USB in a nutshell" guide: http://www.beyondlogic.org/usbnutshell/ 124 | */ 125 | package gousb 126 | 127 | import ( 128 | "errors" 129 | "fmt" 130 | "sync" 131 | ) 132 | 133 | // Context manages all resources related to USB device handling. 134 | type Context struct { 135 | ctx *libusbContext 136 | done chan struct{} 137 | libusb libusbIntf 138 | 139 | mu sync.Mutex 140 | devices map[*Device]bool 141 | } 142 | 143 | // Debug changes the debug level. Level 0 means no debug, higher levels 144 | // will print out more debugging information. 145 | // TODO(sebek): in the next major release, replace int levels with 146 | // Go-typed constants. 147 | func (c *Context) Debug(level int) { 148 | c.libusb.setDebug(c.ctx, level) 149 | } 150 | 151 | func newContextWithImpl(impl libusbIntf) *Context { 152 | c, err := impl.init() 153 | if err != nil { 154 | panic(err) 155 | } 156 | ctx := &Context{ 157 | ctx: c, 158 | done: make(chan struct{}), 159 | libusb: impl, 160 | devices: make(map[*Device]bool), 161 | } 162 | go impl.handleEvents(ctx.ctx, ctx.done) 163 | return ctx 164 | } 165 | 166 | // NewContext returns a new Context instance with default ContextOptions. 167 | func NewContext() *Context { 168 | return ContextOptions{}.New() 169 | } 170 | 171 | // DeviceDiscovery controls USB device discovery. 172 | type DeviceDiscovery int 173 | 174 | const ( 175 | // EnableDeviceDiscovery means the connected USB devices will be enumerated 176 | // on Context initialization. This enables the use of OpenDevices and 177 | // OpenWithVIDPID. This is the default. 178 | EnableDeviceDiscovery = iota 179 | // DisableDeviceDiscovery means the USB devices are not enumerated and 180 | // OpenDevices will not return any devices. 181 | // Without device discovery, OpenDeviceWithFileDescriptor can be used 182 | // to open devices. 183 | DisableDeviceDiscovery 184 | ) 185 | 186 | // ContextOptions holds parameters for Context initialization. 187 | type ContextOptions struct { 188 | DeviceDiscovery DeviceDiscovery 189 | } 190 | 191 | // New creates a Context, taking into account the optional flags contained in ContextOptions 192 | func (o ContextOptions) New() *Context { 193 | return newContextWithImpl(libusbImpl{ 194 | discovery: o.DeviceDiscovery, 195 | }) 196 | } 197 | 198 | // OpenDevices calls opener with each enumerated device. 199 | // If the opener returns true, the device is opened and a Device is returned if the operation succeeds. 200 | // Every Device returned (whether an error is also returned or not) must be closed. 201 | // If there are any errors enumerating the devices, 202 | // the final one is returned along with any successfully opened devices. 203 | func (c *Context) OpenDevices(opener func(desc *DeviceDesc) bool) ([]*Device, error) { 204 | if c.ctx == nil { 205 | return nil, errors.New("OpenDevices called on a closed or uninitialized Context") 206 | } 207 | list, err := c.libusb.getDevices(c.ctx) 208 | if err != nil { 209 | return nil, err 210 | } 211 | 212 | var reterr error 213 | var ret []*Device 214 | for _, dev := range list { 215 | desc, err := c.libusb.getDeviceDesc(dev) 216 | defer c.libusb.dereference(dev) 217 | if err != nil { 218 | reterr = err 219 | continue 220 | } 221 | 222 | if !opener(desc) { 223 | continue 224 | } 225 | handle, err := c.libusb.open(dev) 226 | if err != nil { 227 | reterr = err 228 | continue 229 | } 230 | o := &Device{handle: handle, ctx: c, Desc: desc} 231 | ret = append(ret, o) 232 | c.mu.Lock() 233 | c.devices[o] = true 234 | c.mu.Unlock() 235 | 236 | } 237 | return ret, reterr 238 | } 239 | 240 | // OpenDeviceWithFileDescriptor takes a (Unix) file descriptor of an opened USB 241 | // device and wraps the library around it. 242 | // This is particularly useful when working on Android, where the USB device can be 243 | // opened by the SDK (Java), giving access to the device through the file descriptor 244 | // (https://developer.android.com/reference/android/hardware/usb/UsbDeviceConnection#getFileDescriptor()). 245 | // 246 | // Do note that for this to work the automatic device discovery must be disabled 247 | // at the time when the new Context is created, through the use of 248 | // ContextOptions.DeviceDiscovery. 249 | // 250 | // Example: 251 | // 252 | // ctx := ContextOptions{DeviceDiscovery: DisableDeviceDiscovery}.New() 253 | // device, err := ctx.OpenDeviceWithFileDescriptor(fd) 254 | // 255 | // An error is returned in case the file descriptor is not valid. 256 | func (c *Context) OpenDeviceWithFileDescriptor(fd uintptr) (*Device, error) { 257 | handle, err := c.libusb.wrapSysDevice(c.ctx, fd) 258 | if err != nil { 259 | return nil, err 260 | } 261 | dev := c.libusb.getDevice(handle) 262 | desc, err := c.libusb.getDeviceDesc(dev) 263 | if err != nil { 264 | return nil, fmt.Errorf("device was opened, but getting device descriptor failed: %v", err) 265 | } 266 | 267 | o := &Device{handle: handle, ctx: c, Desc: desc} 268 | c.mu.Lock() 269 | c.devices[o] = true 270 | c.mu.Unlock() 271 | 272 | return o, nil 273 | } 274 | 275 | // OpenDeviceWithVIDPID opens Device from specific VendorId and ProductId. 276 | // If none is found, it returns nil and nil error. If there are multiple devices 277 | // with the same VID/PID, it will return one of them, picked arbitrarily. 278 | // If there were any errors during device list traversal, it is possible 279 | // it will return a non-nil device and non-nil error. A Device.Close() must 280 | // be called to release the device if the returned device wasn't nil. 281 | func (c *Context) OpenDeviceWithVIDPID(vid, pid ID) (*Device, error) { 282 | var found bool 283 | devs, err := c.OpenDevices(func(desc *DeviceDesc) bool { 284 | if found { 285 | return false 286 | } 287 | if desc.Vendor == ID(vid) && desc.Product == ID(pid) { 288 | found = true 289 | return true 290 | } 291 | return false 292 | }) 293 | if len(devs) == 0 { 294 | return nil, err 295 | } 296 | return devs[0], nil 297 | } 298 | 299 | func (c *Context) closeDev(d *Device) { 300 | c.mu.Lock() 301 | defer c.mu.Unlock() 302 | c.libusb.close(d.handle) 303 | delete(c.devices, d) 304 | } 305 | 306 | func (c *Context) checkOpenDevs() error { 307 | c.mu.Lock() 308 | defer c.mu.Unlock() 309 | if l := len(c.devices); l > 0 { 310 | return fmt.Errorf("Context.Close called while %d Devices are still open, Close may be called only after all previously opened devices were successfuly closed", l) 311 | } 312 | return nil 313 | } 314 | 315 | // Close releases the Context and all associated resources. 316 | func (c *Context) Close() error { 317 | if c.ctx == nil { 318 | return nil 319 | } 320 | if err := c.checkOpenDevs(); err != nil { 321 | return err 322 | } 323 | c.done <- struct{}{} 324 | err := c.libusb.exit(c.ctx) 325 | c.ctx = nil 326 | return err 327 | } 328 | -------------------------------------------------------------------------------- /usb_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Copyright 2016 the gousb Authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package gousb 17 | 18 | import "testing" 19 | 20 | func TestOPenDevices(t *testing.T) { 21 | t.Parallel() 22 | c := newContextWithImpl(newFakeLibusb()) 23 | defer func() { 24 | if err := c.Close(); err != nil { 25 | t.Errorf("Context.Close(): %v", err) 26 | } 27 | }() 28 | c.Debug(0) 29 | 30 | descs := []*DeviceDesc{} 31 | devs, err := c.OpenDevices(func(desc *DeviceDesc) bool { 32 | descs = append(descs, desc) 33 | return true 34 | }) 35 | defer func() { 36 | for _, d := range devs { 37 | d.Close() 38 | } 39 | }() 40 | if err != nil { 41 | t.Fatalf("OpenDevices(): %s", err) 42 | } 43 | 44 | // attempt to Close() should fail because of open devices 45 | if err := c.Close(); err == nil { 46 | t.Fatal("Context.Close succeeded while some devices were still open") 47 | } 48 | 49 | if got, want := len(devs), len(fakeDevices); got != want { 50 | t.Fatalf("len(devs) = %d, want %d (based on num fake devs)", got, want) 51 | } 52 | if got, want := len(devs), len(descs); got != want { 53 | t.Fatalf("len(devs) = %d, want %d (based on num opened devices)", got, want) 54 | } 55 | 56 | for i := range devs { 57 | if got, want := devs[i].Desc, descs[i]; got != want { 58 | t.Errorf("dev[%d].Desc = %p, want %p", i, got, want) 59 | } 60 | } 61 | } 62 | 63 | func TestOpenDeviceWithVIDPID(t *testing.T) { 64 | t.Parallel() 65 | ctx := newContextWithImpl(newFakeLibusb()) 66 | defer func() { 67 | if err := ctx.Close(); err != nil { 68 | t.Errorf("Context.Close(): %v", err) 69 | } 70 | }() 71 | 72 | for _, d := range []struct { 73 | vid, pid ID 74 | exists bool 75 | }{ 76 | {0x7777, 0x0003, false}, 77 | {0x8888, 0x0001, false}, 78 | {0x8888, 0x0002, true}, 79 | {0x9999, 0x0001, true}, 80 | {0x9999, 0x0002, false}, 81 | } { 82 | dev, err := ctx.OpenDeviceWithVIDPID(d.vid, d.pid) 83 | if (dev != nil) != d.exists { 84 | t.Errorf("OpenDeviceWithVIDPID(%s/%s): device != nil is %v, want %v", ID(d.vid), ID(d.pid), dev != nil, d.exists) 85 | } 86 | if err != nil { 87 | t.Errorf("OpenDeviceWithVIDPID(%s/%s): got error %v, want nil", ID(d.vid), ID(d.pid), err) 88 | } 89 | if dev != nil { 90 | if dev.Desc.Vendor != ID(d.vid) || dev.Desc.Product != ID(d.pid) { 91 | 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.Desc.Vendor, dev.Desc.Product) 92 | } 93 | dev.Close() 94 | } 95 | } 96 | } 97 | 98 | func TestOpenDeviceWithFileDescriptor(t *testing.T) { 99 | ctx := newContextWithImpl(newFakeLibusb()) 100 | defer ctx.Close() 101 | 102 | // file descriptor is an index to the FakeDevices array 103 | for _, d := range []struct { 104 | vid, pid ID 105 | sysDevPtr uintptr 106 | }{ 107 | {0x9999, 0x0001, 78}, 108 | {0x8888, 0x0002, 94}, 109 | } { 110 | dev, err := ctx.OpenDeviceWithFileDescriptor(d.sysDevPtr) 111 | if err != nil { 112 | t.Fatalf("OpenDeviceWithFileDescriptor(%d): err != nil for a valid device: %v", d.sysDevPtr, err) 113 | } 114 | if dev == nil { 115 | t.Fatalf("OpenDeviceWithFileDescriptor(%d): device == nil for a valid device", d.sysDevPtr) 116 | } 117 | if dev != nil && (dev.Desc.Vendor != ID(d.vid) || dev.Desc.Product != ID(d.pid)) { 118 | t.Errorf("OpenDeviceWithFileDescriptor(%d): device's VID/PID %s/%s don't match expected: %s/%s", d.sysDevPtr, dev.Desc.Vendor, dev.Desc.Product, ID(d.vid), ID(d.pid)) 119 | } 120 | } 121 | 122 | } 123 | 124 | func TestOpenDeviceWithFileDescriptorOnMissingDevice(t *testing.T) { 125 | ctx := newContextWithImpl(newFakeLibusb()) 126 | defer ctx.Close() 127 | 128 | for _, sysDevPtr := range []uintptr{ 129 | 7, // set, but does not exist in the fakeDevices array 130 | 0, // unset 131 | } { 132 | if _, err := ctx.OpenDeviceWithFileDescriptor(sysDevPtr); err == nil { 133 | t.Errorf("OpenDeviceWithFileDescriptor(%d): got nil error for invalid device", sysDevPtr) 134 | } 135 | } 136 | 137 | } 138 | -------------------------------------------------------------------------------- /usbid/describe.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Copyright 2016 the gousb Authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | // Package usbid provides human-readable text output for the usb package. 17 | // 18 | // On load, the usbid package parses an embedded mapping of vendors/products 19 | // and class/subclass/protocols. They can also be loaded from a URL or from 20 | // a reader. 21 | // 22 | // The bread and butter of this package are the following two functions: 23 | // 24 | // Describe - Pretty-print the vendor and product of a device descriptor 25 | // Classify - Pretty-print the class/protocol info for a device/interface 26 | package usbid 27 | 28 | import ( 29 | "fmt" 30 | 31 | "github.com/google/gousb" 32 | ) 33 | 34 | // Describe returns a human readable string describing the vendor and product 35 | // of the given device. 36 | // 37 | // The given val must be one of the following: 38 | // - *gousb.DeviceDesc "Product (Vendor)" 39 | func Describe(val interface{}) string { 40 | switch val := val.(type) { 41 | case *gousb.DeviceDesc: 42 | if v, ok := Vendors[val.Vendor]; ok { 43 | if d, ok := v.Product[val.Product]; ok { 44 | return fmt.Sprintf("%s (%s)", d, v) 45 | } 46 | return fmt.Sprintf("Unknown (%s)", v) 47 | } 48 | return fmt.Sprintf("Unknown %s:%s", val.Vendor, val.Product) 49 | } 50 | return fmt.Sprintf("Unknown (%T)", val) 51 | } 52 | 53 | // Classify returns a human-readable string describing the class, subclass, 54 | // and protocol associated with a device or interface. 55 | // 56 | // The given val must be one of the following: 57 | // - *gousb.DeviceDesc "Class (SubClass) Protocol" 58 | // - gousb.InterfaceSetup "IfClass (IfSubClass) IfProtocol" 59 | func Classify(val interface{}) string { 60 | var ( 61 | class, sub gousb.Class 62 | proto gousb.Protocol 63 | ) 64 | switch val := val.(type) { 65 | case *gousb.DeviceDesc: 66 | class, sub, proto = val.Class, val.SubClass, val.Protocol 67 | case gousb.InterfaceSetting: 68 | class, sub, proto = val.Class, val.SubClass, val.Protocol 69 | default: 70 | return fmt.Sprintf("Unknown (%T)", val) 71 | } 72 | 73 | if c, ok := Classes[class]; ok { 74 | if s, ok := c.SubClass[sub]; ok { 75 | if p, ok := s.Protocol[proto]; ok { 76 | return fmt.Sprintf("%s (%s) %s", c, s, p) 77 | } 78 | return fmt.Sprintf("%s (%s)", c, s) 79 | } 80 | return fmt.Sprintf("%s", c) 81 | } 82 | return fmt.Sprintf("Unknown %s.%s.%s", class, sub, proto) 83 | } 84 | -------------------------------------------------------------------------------- /usbid/load.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Copyright 2016 the gousb Authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package usbid 17 | 18 | import ( 19 | "log" 20 | "net/http" 21 | "strings" 22 | "time" 23 | 24 | "github.com/google/gousb" 25 | ) 26 | 27 | const ( 28 | // LinuxUsbDotOrg is one source of files in the format used by this package. 29 | LinuxUsbDotOrg = "http://www.linux-usb.org/usb.ids" 30 | ) 31 | 32 | var ( 33 | // Vendors stores the vendor and product ID mappings. 34 | Vendors map[gousb.ID]*Vendor 35 | 36 | // Classes stores the class, subclass and protocol mappings. 37 | Classes map[gousb.Class]*Class 38 | ) 39 | 40 | // LoadFromURL replaces the built-in vendor and class mappings with ones loaded 41 | // from the given URL. 42 | // 43 | // This should usually only be necessary if the mappings in the library are 44 | // stale. The contents of this file as of February 2012 are embedded in the 45 | // library itself. 46 | func LoadFromURL(url string) error { 47 | resp, err := http.Get(url) 48 | if err != nil { 49 | return err 50 | } 51 | defer resp.Body.Close() 52 | 53 | ids, cls, err := ParseIDs(resp.Body) 54 | if err != nil { 55 | return err 56 | } 57 | 58 | Vendors = ids 59 | Classes = cls 60 | LastUpdate = time.Now() 61 | return nil 62 | } 63 | 64 | //go:generate go run regen/regen.go --template regen/load_data.go.tpl -o load_data.go 65 | 66 | func init() { 67 | ids, cls, err := ParseIDs(strings.NewReader(usbIDListData)) 68 | if err != nil { 69 | log.Printf("usbid: failed to parse: %s", err) 70 | return 71 | } 72 | 73 | Vendors = ids 74 | Classes = cls 75 | } 76 | -------------------------------------------------------------------------------- /usbid/load_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Copyright 2016 the gousb Authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package usbid 17 | 18 | import ( 19 | "fmt" 20 | "io" 21 | "net/http" 22 | "net/http/httptest" 23 | "os" 24 | "reflect" 25 | "testing" 26 | ) 27 | 28 | func TestLoaded(t *testing.T) { 29 | if got, min := len(Vendors), 1000; got < min { 30 | t.Errorf("%d vendors loaded, want at least %d", got, min) 31 | } 32 | if got, min := len(Classes), 10; got < min { 33 | t.Errorf("%d classes loaded, want at least %d", got, min) 34 | } 35 | } 36 | 37 | type handler struct{} 38 | 39 | func (handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 40 | f, err := os.Open(testDBPath) 41 | if err != nil { 42 | w.WriteHeader(http.StatusInternalServerError) 43 | fmt.Fprintf(w, "Open(%q): %v", testDBPath, err) 44 | return 45 | } 46 | defer f.Close() 47 | w.Header().Set("content-type", "text/plain") 48 | io.Copy(w, f) 49 | } 50 | 51 | func TestLoadFromURL(t *testing.T) { 52 | origV, origC := Vendors, Classes 53 | Vendors, Classes = nil, nil 54 | defer func() { Vendors, Classes = origV, origC }() 55 | 56 | s := httptest.NewServer(handler{}) 57 | defer s.Close() 58 | 59 | err := LoadFromURL(s.URL) 60 | if err != nil { 61 | t.Fatalf("LoadFromURL(%q): got unexpected error: %v", s.URL, err) 62 | } 63 | if !reflect.DeepEqual(Vendors, testDBVendors) { 64 | t.Errorf("LoadFromURL Vendors:\ngot: %v\nwant: %v", Vendors, testDBVendors) 65 | } 66 | } 67 | 68 | func TestLoadFromURLError(t *testing.T) { 69 | origV, origC := Vendors, Classes 70 | defer func() { Vendors, Classes = origV, origC }() 71 | 72 | s := httptest.NewServer(http.NotFoundHandler()) 73 | defer s.Close() 74 | 75 | ts := LastUpdate 76 | err := LoadFromURL(s.URL) 77 | if err == nil { 78 | t.Fatalf("LoadFromURL(%q): err is nil, want not nil", s.URL) 79 | } 80 | if LastUpdate != ts { 81 | t.Errorf("LastUpdate was unexpectedly updated when LoadFromURL failed") 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /usbid/parse.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Copyright 2016 the gousb Authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package usbid 17 | 18 | import ( 19 | "bufio" 20 | "fmt" 21 | "io" 22 | "strconv" 23 | "strings" 24 | 25 | "github.com/google/gousb" 26 | ) 27 | 28 | // A Vendor contains the name of the vendor and mappings corresponding to all 29 | // known products by their ID. 30 | type Vendor struct { 31 | Name string 32 | Product map[gousb.ID]*Product 33 | } 34 | 35 | // String returns the name of the vendor. 36 | func (v Vendor) String() string { 37 | return v.Name 38 | } 39 | 40 | // A Product contains the name of the product (from a particular vendor) and 41 | // the names of any interfaces that were specified. 42 | type Product struct { 43 | Name string 44 | Interface map[gousb.ID]string 45 | } 46 | 47 | // String returns the name of the product. 48 | func (p Product) String() string { 49 | return p.Name 50 | } 51 | 52 | // A Class contains the name of the class and mappings for each subclass. 53 | type Class struct { 54 | Name string 55 | SubClass map[gousb.Class]*SubClass 56 | } 57 | 58 | // String returns the name of the class. 59 | func (c Class) String() string { 60 | return c.Name 61 | } 62 | 63 | // A SubClass contains the name of the subclass and any associated protocols. 64 | type SubClass struct { 65 | Name string 66 | Protocol map[gousb.Protocol]string 67 | } 68 | 69 | // String returns the name of the SubClass. 70 | func (s SubClass) String() string { 71 | return s.Name 72 | } 73 | 74 | // ParseIDs parses and returns mappings from the given reader. In general, this 75 | // should not be necessary, as a set of mappings is already embedded in the library. 76 | // If a new or specialized file is obtained, this can be used to retrieve the mappings, 77 | // which can be stored in the global Vendors and Classes map. 78 | func ParseIDs(r io.Reader) (map[gousb.ID]*Vendor, map[gousb.Class]*Class, error) { 79 | vendors := make(map[gousb.ID]*Vendor, 2800) 80 | classes := make(map[gousb.Class]*Class) // TODO(kevlar): count 81 | 82 | split := func(s string) (kind string, level int, id uint64, name string, err error) { 83 | pieces := strings.SplitN(s, " ", 2) 84 | if len(pieces) != 2 { 85 | err = fmt.Errorf("malformatted line %q", s) 86 | return 87 | } 88 | 89 | // Save the name 90 | name = pieces[1] 91 | 92 | // Parse out the level 93 | for len(pieces[0]) > 0 && pieces[0][0] == '\t' { 94 | level, pieces[0] = level+1, pieces[0][1:] 95 | } 96 | 97 | // Parse the first piece to see if it has a kind 98 | first := strings.SplitN(pieces[0], " ", 2) 99 | if len(first) == 2 { 100 | kind, pieces[0] = first[0], first[1] 101 | } 102 | 103 | // Parse the ID 104 | i, err := strconv.ParseUint(pieces[0], 16, 16) 105 | if err != nil { 106 | err = fmt.Errorf("malformatted id %q: %s", pieces[0], err) 107 | return 108 | } 109 | id = i 110 | 111 | return 112 | } 113 | 114 | // Hold the interim values 115 | var vendor *Vendor 116 | var device *Product 117 | 118 | parseVendor := func(level int, raw uint64, name string) error { 119 | id := gousb.ID(raw) 120 | 121 | switch level { 122 | case 0: 123 | vendor = &Vendor{ 124 | Name: name, 125 | } 126 | vendors[id] = vendor 127 | 128 | case 1: 129 | if vendor == nil { 130 | return fmt.Errorf("product line without vendor line") 131 | } 132 | 133 | device = &Product{ 134 | Name: name, 135 | } 136 | if vendor.Product == nil { 137 | vendor.Product = make(map[gousb.ID]*Product) 138 | } 139 | vendor.Product[id] = device 140 | 141 | case 2: 142 | if device == nil { 143 | return fmt.Errorf("interface line without device line") 144 | } 145 | 146 | if device.Interface == nil { 147 | device.Interface = make(map[gousb.ID]string) 148 | } 149 | device.Interface[id] = name 150 | 151 | default: 152 | return fmt.Errorf("too many levels of nesting for vendor block") 153 | } 154 | 155 | return nil 156 | } 157 | 158 | // Hold the interim values 159 | var class *Class 160 | var subclass *SubClass 161 | 162 | parseClass := func(level int, id uint64, name string) error { 163 | switch level { 164 | case 0: 165 | class = &Class{ 166 | Name: name, 167 | } 168 | classes[gousb.Class(id)] = class 169 | 170 | case 1: 171 | if class == nil { 172 | return fmt.Errorf("subclass line without class line") 173 | } 174 | 175 | subclass = &SubClass{ 176 | Name: name, 177 | } 178 | if class.SubClass == nil { 179 | class.SubClass = make(map[gousb.Class]*SubClass) 180 | } 181 | class.SubClass[gousb.Class(id)] = subclass 182 | 183 | case 2: 184 | if subclass == nil { 185 | return fmt.Errorf("protocol line without subclass line") 186 | } 187 | 188 | if subclass.Protocol == nil { 189 | subclass.Protocol = make(map[gousb.Protocol]string) 190 | } 191 | subclass.Protocol[gousb.Protocol(id)] = name 192 | 193 | default: 194 | return fmt.Errorf("too many levels of nesting for class") 195 | } 196 | 197 | return nil 198 | } 199 | 200 | // TODO(kevlar): Parse class information, etc 201 | //var class *Class 202 | //var subclass *SubClass 203 | 204 | var kind string 205 | 206 | lines := bufio.NewReaderSize(r, 512) 207 | parseLines: 208 | for lineno := 0; ; lineno++ { 209 | b, isPrefix, err := lines.ReadLine() 210 | switch { 211 | case err == io.EOF: 212 | break parseLines 213 | case err != nil: 214 | return nil, nil, err 215 | case isPrefix: 216 | return nil, nil, fmt.Errorf("line %d: line too long", lineno) 217 | } 218 | line := string(b) 219 | 220 | if len(line) == 0 || line[0] == '#' { 221 | continue 222 | } 223 | 224 | k, level, id, name, err := split(line) 225 | if err != nil { 226 | return nil, nil, fmt.Errorf("line %d: %s", lineno, err) 227 | } 228 | if k != "" { 229 | kind = k 230 | } 231 | 232 | switch kind { 233 | case "": 234 | err = parseVendor(level, id, name) 235 | case "C": 236 | err = parseClass(level, id, name) 237 | } 238 | if err != nil { 239 | return nil, nil, fmt.Errorf("line %d: %s", lineno, err) 240 | } 241 | } 242 | 243 | return vendors, classes, nil 244 | } 245 | -------------------------------------------------------------------------------- /usbid/parse_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Copyright 2016 the gousb Authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package usbid 17 | 18 | import ( 19 | "io/ioutil" 20 | "reflect" 21 | "strings" 22 | "testing" 23 | ) 24 | 25 | func mustRead(fname string) string { 26 | b, err := ioutil.ReadFile(fname) 27 | if err != nil { 28 | panic(err) 29 | } 30 | return string(b) 31 | } 32 | 33 | func TestParse(t *testing.T) { 34 | vendors, classes, err := ParseIDs(strings.NewReader(mustRead(testDBPath))) 35 | if err != nil { 36 | t.Fatalf("ParseIDs(%q): %s", testDBPath, err) 37 | } 38 | if got, want := vendors, testDBVendors; !reflect.DeepEqual(got, want) { 39 | t.Errorf("Vendors from %q", testDBPath) 40 | t.Errorf(" - got: %+v", got) 41 | t.Errorf(" - want: %+v", want) 42 | } 43 | if got, want := classes, testDBClasses; !reflect.DeepEqual(got, want) { 44 | t.Errorf("Classes from %q", testDBPath) 45 | t.Errorf(" - got: %+v", got) 46 | t.Errorf(" - want: %+v", want) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /usbid/regen/.gitignore: -------------------------------------------------------------------------------- 1 | regen 2 | -------------------------------------------------------------------------------- /usbid/regen/load_data.go.tpl: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Copyright 2016 the gousb Authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package usbid 17 | 18 | import "time" 19 | 20 | // LastUpdate stores the latest time that the library was updated. 21 | // 22 | // The baked-in data was last generated: 23 | // {{.Generated}} 24 | var LastUpdate = time.Unix(0, {{.Generated.UnixNano}}) 25 | 26 | const usbIdListData = `{{printf "%s" .Data}}` 27 | -------------------------------------------------------------------------------- /usbid/regen/regen.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Copyright 2016 the gousb Authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package main 17 | 18 | import ( 19 | "bytes" 20 | "flag" 21 | "io/ioutil" 22 | "log" 23 | "net/http" 24 | "os" 25 | "text/template" 26 | "time" 27 | 28 | "github.com/google/gousb/usbid" 29 | ) 30 | 31 | var ( 32 | remote = flag.String("url", usbid.LinuxUsbDotOrg, "URL from which to download new vendor data") 33 | dataFile = flag.String("template", "load_data.go.tpl", "Template filename") 34 | outFile = flag.String("o", "load_data.go", "Output filename") 35 | ) 36 | 37 | func main() { 38 | flag.Parse() 39 | 40 | log.Printf("Fetching %q...", *remote) 41 | resp, err := http.Get(*remote) 42 | if err != nil { 43 | log.Fatalf("failed to download from %q: %s", *remote, err) 44 | } 45 | defer resp.Body.Close() 46 | 47 | data, err := ioutil.ReadAll(resp.Body) 48 | if err != nil { 49 | log.Fatalf("failed to read %q: %s", *remote, err) 50 | } 51 | 52 | ids, cls, err := usbid.ParseIDs(bytes.NewReader(data)) 53 | if err != nil { 54 | log.Fatalf("failed to parse %q: %s", *remote, err) 55 | } 56 | 57 | log.Printf("Successfully fetched %q:", *remote) 58 | log.Printf(" Loaded %d Vendor IDs", len(ids)) 59 | log.Printf(" Loaded %d Class IDs", len(cls)) 60 | 61 | rawTemplate, err := ioutil.ReadFile(*dataFile) 62 | if err != nil { 63 | log.Fatalf("failed to read template %q: %s", *dataFile, err) 64 | } 65 | 66 | template, err := template.New("").Parse(string(rawTemplate)) 67 | if err != nil { 68 | log.Fatalf("failed to parse template %q: %s", *dataFile, err) 69 | } 70 | 71 | out, err := os.Create(*outFile) 72 | if err != nil { 73 | log.Fatalf("failed to open output file %q: %s", *outFile, err) 74 | } 75 | defer out.Close() 76 | 77 | templateData := struct { 78 | Data []byte 79 | Generated time.Time 80 | RFC3339 string 81 | }{ 82 | Data: bytes.Map(sanitize, data), 83 | Generated: time.Now(), 84 | } 85 | if err := template.Execute(out, templateData); err != nil { 86 | log.Fatalf("failed to execute template: %s", err) 87 | } 88 | 89 | log.Printf("Successfully wrote %q", *outFile) 90 | } 91 | 92 | // sanitize strips characters that can't be `-quoted 93 | func sanitize(r rune) rune { 94 | switch { 95 | case r == '`': 96 | return -1 97 | case r == '\t', r == '\n': 98 | return r 99 | case r >= ' ' && r <= '~': 100 | return r 101 | } 102 | return -1 103 | } 104 | -------------------------------------------------------------------------------- /usbid/testdata/testdb.txt: -------------------------------------------------------------------------------- 1 | # Skip comment 2 | abcd Vendor One 3 | 0123 Product One 4 | 0124 Product Two 5 | efef Vendor Two 6 | 0aba Product 7 | 12 Interface One 8 | 24 Interface Two 9 | 0abb Product 10 | 12 Interface 11 | 12 | C 00 (Defined at Interface level) 13 | C 01 Audio 14 | 01 Control Device 15 | 02 Streaming 16 | 03 MIDI Streaming 17 | C 02 Communications 18 | 01 Direct Line 19 | 02 Abstract (modem) 20 | 00 None 21 | 01 AT-commands (v.25ter) 22 | 02 AT-commands (PCCA101) 23 | 03 AT-commands (PCCA101 + wakeup) 24 | 04 AT-commands (GSM) 25 | 05 AT-commands (3G) 26 | 06 AT-commands (CDMA) 27 | fe Defined by command set descriptor 28 | ff Vendor Specific (MSFT RNDIS?) 29 | -------------------------------------------------------------------------------- /usbid/testdata_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 the gousb Authors. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package usbid 16 | 17 | import "github.com/google/gousb" 18 | 19 | const testDBPath = "testdata/testdb.txt" 20 | 21 | var ( 22 | testDBVendors = map[gousb.ID]*Vendor{ 23 | 0xabcd: { 24 | Name: "Vendor One", 25 | Product: map[gousb.ID]*Product{ 26 | 0x0123: {Name: "Product One"}, 27 | 0x0124: {Name: "Product Two"}, 28 | }, 29 | }, 30 | 0xefef: { 31 | Name: "Vendor Two", 32 | Product: map[gousb.ID]*Product{ 33 | 0x0aba: { 34 | Name: "Product", 35 | Interface: map[gousb.ID]string{ 36 | 0x12: "Interface One", 37 | 0x24: "Interface Two", 38 | }, 39 | }, 40 | 0x0abb: { 41 | Name: "Product", 42 | Interface: map[gousb.ID]string{ 43 | 0x12: "Interface", 44 | }, 45 | }, 46 | }, 47 | }, 48 | } 49 | testDBClasses = map[gousb.Class]*Class{ 50 | 0x00: { 51 | Name: "(Defined at Interface level)", 52 | }, 53 | 0x01: { 54 | Name: "Audio", 55 | SubClass: map[gousb.Class]*SubClass{ 56 | 0x01: {Name: "Control Device"}, 57 | 0x02: {Name: "Streaming"}, 58 | 0x03: {Name: "MIDI Streaming"}, 59 | }, 60 | }, 61 | 0x02: { 62 | Name: "Communications", 63 | SubClass: map[gousb.Class]*SubClass{ 64 | 0x01: {Name: "Direct Line"}, 65 | 0x02: { 66 | Name: "Abstract (modem)", 67 | Protocol: map[gousb.Protocol]string{ 68 | 0x00: "None", 69 | 0x01: "AT-commands (v.25ter)", 70 | 0x02: "AT-commands (PCCA101)", 71 | 0x03: "AT-commands (PCCA101 + wakeup)", 72 | 0x04: "AT-commands (GSM)", 73 | 0x05: "AT-commands (3G)", 74 | 0x06: "AT-commands (CDMA)", 75 | 0xfe: "Defined by command set descriptor", 76 | 0xff: "Vendor Specific (MSFT RNDIS?)", 77 | }, 78 | }, 79 | }, 80 | }, 81 | } 82 | ) 83 | --------------------------------------------------------------------------------