Compare commits
12 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
844d4b8a71 | ||
![]() |
6ad122da6a | ||
![]() |
1f6abc1f14 | ||
![]() |
bdb184b25c | ||
![]() |
d3bdbe53c8 | ||
![]() |
0ce3a07f8e | ||
![]() |
0eba1b1264 | ||
![]() |
c9efe54672 | ||
![]() |
dd9486a141 | ||
![]() |
cb06b9fb0d | ||
![]() |
76f12f69ac | ||
![]() |
0995919da4 |
@@ -1,15 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
export PATH="/mingw64/bin:${PATH}"
|
||||
export PKG_CONFIG_PATH="/mingw64/lib/pkgconfig:${PKG_CONFIG_PATH}"
|
||||
export GOROOT=/mingw64/lib/go
|
||||
export GOPATH=/go
|
||||
export CGO_ENABLED=1
|
||||
|
||||
pacman --noconfirm -S \
|
||||
mingw64/mingw-w64-x86_64-go \
|
||||
mingw64/mingw-w64-x86_64-libusb
|
||||
|
||||
go version
|
||||
go get -t github.com/google/gousb/...
|
||||
go test github.com/google/gousb/...
|
4
.github/test-coverage.sh
vendored
Normal file
4
.github/test-coverage.sh
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
echo 'mode: count' > coverage.merged &&\
|
||||
go list -f '{{.Dir}}' ./... | xargs -n1 -I'{}' sh -c ': > coverage.tmp; go test -v -covermode=count -coverprofile=coverage.tmp {} && tail -n +2 coverage.tmp >> coverage.merged' &&\
|
||||
rm coverage.tmp
|
35
.github/workflows/build-and-test.yaml
vendored
Normal file
35
.github/workflows/build-and-test.yaml
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
name: build-and-test
|
||||
on: [pull_request, push]
|
||||
jobs:
|
||||
linux:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.17
|
||||
- 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
|
||||
- run: sudo apt-get install libusb-1.0-0-dev
|
||||
- run: go install golang.org/x/tools/cmd/cover@latest
|
||||
- run: go install golang.org/x/lint/golint@latest
|
||||
- run: $HOME/go/bin/golint -set_exit_status ./...
|
||||
- run: sh ./.github/test-coverage.sh
|
||||
- uses: shogo82148/actions-goveralls@v1
|
||||
with:
|
||||
path-to-profile: coverage.merged
|
||||
ignore: libusb.go,error.go
|
||||
windows:
|
||||
runs-on: windows-2022
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.17
|
||||
- uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
install: |-
|
||||
mingw64/mingw-w64-x86_64-libusb
|
||||
mingw64/mingw-w64-x86_64-pkg-config
|
||||
- run: echo "D:\a\_temp\msys64\mingw64\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
||||
- run: go test ./...
|
||||
- run: go run ./lsusb
|
30
.travis.yml
30
.travis.yml
@@ -1,30 +0,0 @@
|
||||
language: go
|
||||
dist: trusty
|
||||
|
||||
go:
|
||||
- 1.7.x
|
||||
- 1.8.x
|
||||
- 1.9.x
|
||||
- 1.10.x
|
||||
- tip
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
libusb-1.0-0-dev
|
||||
|
||||
install:
|
||||
- go get golang.org/x/tools/cmd/cover
|
||||
# Golint requires Go 1.9 or later.
|
||||
- if ! [[ "$TRAVIS_GO_VERSION" =~ ^1\.7\.([0-9]+|x)$ || "$TRAVIS_GO_VERSION" =~ ^1\.8\.([0-9]+|x)$ ]]; then go get golang.org/x/lint/golint; fi
|
||||
- go get github.com/mattn/goveralls
|
||||
|
||||
script:
|
||||
# a workaround for go test not supporting coverage for multiple packages in a single invocation.
|
||||
# If goveralls upload fails, ignore the result.
|
||||
# Golint requires Go 1.9 or later.
|
||||
- if ! [[ "$TRAVIS_GO_VERSION" =~ ^1\.7\.([0-9]+|x)+$ || "$TRAVIS_GO_VERSION" =~ ^1\.8\.([0-9]+|x)$ ]]; then $HOME/gopath/bin/golint ./...; fi
|
||||
- |-
|
||||
echo 'mode: count' > coverage.merged && go list ./... | xargs -n1 -I{} sh -c ': > coverage.tmp; go test -v -covermode=count -coverprofile=coverage.tmp {} && tail -n +2 coverage.tmp >> coverage.merged' && rm coverage.tmp
|
||||
- |-
|
||||
$HOME/gopath/bin/goveralls -coverprofile=coverage.merged -service=travis-ci -ignore libusb.go,error.go || true
|
@@ -4,7 +4,6 @@ Introduction
|
||||
[![Build Status][ciimg]][ci]
|
||||
[![GoDoc][docimg]][doc]
|
||||
[![Coverage Status][coverimg]][cover]
|
||||
[![Build status][appveimg]][appveyor]
|
||||
|
||||
|
||||
The gousb package is an attempt at wrapping the libusb library into a Go-like binding.
|
||||
@@ -21,12 +20,10 @@ As of 2017-07-13 the 2.0 API is considered stable and 1.0 is deprecated.
|
||||
|
||||
[coverimg]: https://coveralls.io/repos/github/google/gousb/badge.svg
|
||||
[cover]: https://coveralls.io/github/google/gousb
|
||||
[ciimg]: https://travis-ci.org/google/gousb.svg
|
||||
[ci]: https://travis-ci.org/google/gousb
|
||||
[ciimg]: https://github.com/google/gousb/actions/workflows/build-and-test.yaml/badge.svg
|
||||
[ci]: https://github.com/google/gousb/actions/workflows/build-and-test.yaml
|
||||
[docimg]: https://godoc.org/github.com/google/gousb?status.svg
|
||||
[doc]: https://godoc.org/github.com/google/gousb
|
||||
[appveimg]: https://ci.appveyor.com/api/projects/status/661qp7x33o3wqe4o?svg=true
|
||||
[appveyor]: https://ci.appveyor.com/project/zagrodzki/gousb
|
||||
|
||||
Documentation
|
||||
=============
|
||||
|
@@ -1,8 +0,0 @@
|
||||
platform: x64
|
||||
|
||||
clone_folder: C:\msys64\go\src\github.com\google\gousb
|
||||
|
||||
install:
|
||||
- C:\msys64\usr\bin\bash.exe -lc "cd /go/src/github.com/google/gousb/ && .appveyor/./install.sh"
|
||||
|
||||
build: off
|
23
device.go
23
device.go
@@ -24,11 +24,22 @@ import (
|
||||
|
||||
// DeviceDesc is a representation of a USB device descriptor.
|
||||
type DeviceDesc struct {
|
||||
// Bus information
|
||||
Bus int // The bus on which the device was detected
|
||||
Address int // The address of the device on the bus
|
||||
Speed Speed // The negotiated operating speed for the device
|
||||
Port int // The usb port on which the device was detected
|
||||
// The bus on which the device was detected
|
||||
Bus int
|
||||
// The address of the device on the bus
|
||||
Address int
|
||||
// The negotiated operating speed for the device
|
||||
Speed Speed
|
||||
// The pyhsical port on the parent hub on which the device is connected.
|
||||
// Ports are numbered from 1, excepting root hub devices which are always 0.
|
||||
Port int
|
||||
// Physical path of connected parent ports, starting at the root hub device.
|
||||
// A path length of 0 represents a root hub device,
|
||||
// a path length of 1 represents a device directly connected to a root hub,
|
||||
// a path length of 2 or more are connected to intermediate hub devices.
|
||||
// e.g. [1,2,3] represents a device connected to port 3 of a hub connected
|
||||
// to port 2 of a hub connected to port 1 of a root hub.
|
||||
Path []int
|
||||
|
||||
// Version information
|
||||
Spec BCD // USB Specification Release Number
|
||||
@@ -209,7 +220,7 @@ func (d *Device) Close() error {
|
||||
if d.claimed != nil {
|
||||
return fmt.Errorf("can't release the device %s, it has an open config %d", d, d.claimed.Desc.Number)
|
||||
}
|
||||
d.ctx.libusb.close(d.handle)
|
||||
d.ctx.closeDev(d)
|
||||
d.handle = nil
|
||||
return nil
|
||||
}
|
||||
|
@@ -98,7 +98,7 @@ type fakeLibusb struct {
|
||||
claims map[*libusbDevice]map[uint8]bool
|
||||
}
|
||||
|
||||
func (f *fakeLibusb) init() (*libusbContext, error) { return new(libusbContext), nil }
|
||||
func (f *fakeLibusb) init() (*libusbContext, error) { return newContextPointer(), nil }
|
||||
func (f *fakeLibusb) handleEvents(c *libusbContext, done <-chan struct{}) { <-done }
|
||||
func (f *fakeLibusb) getDevices(*libusbContext) ([]*libusbDevice, error) {
|
||||
ret := make([]*libusbDevice, 0, len(fakeDevices))
|
||||
@@ -128,7 +128,7 @@ func (f *fakeLibusb) getDeviceDesc(d *libusbDevice) (*DeviceDesc, error) {
|
||||
return nil, fmt.Errorf("invalid USB device %p", d)
|
||||
}
|
||||
func (f *fakeLibusb) open(d *libusbDevice) (*libusbDevHandle, error) {
|
||||
h := new(libusbDevHandle)
|
||||
h := newDevHandlePointer()
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
f.handles[h] = d
|
||||
|
6
go.mod
Normal file
6
go.mod
Normal file
@@ -0,0 +1,6 @@
|
||||
module github.com/google/gousb
|
||||
|
||||
go 1.16
|
||||
|
||||
// Released in error, v1.* remains the current version.
|
||||
retract v2.1.0+incompatible
|
27
libusb.go
27
libusb.go
@@ -209,7 +209,7 @@ func (libusbImpl) getDevices(ctx *libusbContext) ([]*libusbDevice, error) {
|
||||
for _, d := range devs {
|
||||
ret = append(ret, (*libusbDevice)(d))
|
||||
}
|
||||
// devices will be dereferenced later, during close.
|
||||
// devices must be dereferenced by the caller to prevent memory leaks.
|
||||
C.libusb_free_device_list(list, 0)
|
||||
return ret, nil
|
||||
}
|
||||
@@ -224,14 +224,26 @@ func (libusbImpl) setDebug(c *libusbContext, lvl int) {
|
||||
}
|
||||
|
||||
func (libusbImpl) getDeviceDesc(d *libusbDevice) (*DeviceDesc, error) {
|
||||
var desc C.struct_libusb_device_descriptor
|
||||
var (
|
||||
desc C.struct_libusb_device_descriptor
|
||||
pathData [8]uint8
|
||||
path []int
|
||||
port int
|
||||
)
|
||||
if err := fromErrNo(C.libusb_get_device_descriptor((*C.libusb_device)(d), &desc)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pathLen := int(C.libusb_get_port_numbers((*C.libusb_device)(d), (*C.uint8_t)(&pathData[0]), 8))
|
||||
for _, nPort := range pathData[:pathLen] {
|
||||
port = int(nPort)
|
||||
path = append(path, port)
|
||||
}
|
||||
// Defaults to port = 0, path = [] for root device
|
||||
dev := &DeviceDesc{
|
||||
Bus: int(C.libusb_get_bus_number((*C.libusb_device)(d))),
|
||||
Address: int(C.libusb_get_device_address((*C.libusb_device)(d))),
|
||||
Port: int(C.libusb_get_port_number((*C.libusb_device)(d))),
|
||||
Port: port,
|
||||
Path: path,
|
||||
Speed: Speed(C.libusb_get_device_speed((*C.libusb_device)(d))),
|
||||
Spec: BCD(desc.bcdUSB),
|
||||
Device: BCD(desc.bcdDevice),
|
||||
@@ -508,6 +520,7 @@ func libusbSetDebug(c *libusbContext, lvl int) {
|
||||
C.gousb_set_debug((*C.libusb_context)(c), C.int(lvl))
|
||||
}
|
||||
|
||||
// for obtaining unique CGo pointers.
|
||||
func newDevicePointer() *libusbDevice {
|
||||
return (*libusbDevice)(unsafe.Pointer(C.malloc(1)))
|
||||
}
|
||||
@@ -515,3 +528,11 @@ func newDevicePointer() *libusbDevice {
|
||||
func newFakeTransferPointer() *libusbTransfer {
|
||||
return (*libusbTransfer)(unsafe.Pointer(C.malloc(1)))
|
||||
}
|
||||
|
||||
func newContextPointer() *libusbContext {
|
||||
return (*libusbContext)(unsafe.Pointer(C.malloc(1)))
|
||||
}
|
||||
|
||||
func newDevHandlePointer() *libusbDevHandle {
|
||||
return (*libusbDevHandle)(unsafe.Pointer(C.malloc(1)))
|
||||
}
|
||||
|
@@ -269,14 +269,15 @@ func (w *WriteStream) Close() error {
|
||||
return w.CloseContext(context.Background())
|
||||
}
|
||||
|
||||
// Close signals end of data to write. Close blocks until all transfers
|
||||
// that were sent are finished. The error returned by Close is the first
|
||||
// error encountered during writing the entire stream (if any).
|
||||
// Close returning nil indicates all transfers completed successfully.
|
||||
// After Close, the total number of bytes successfully written can be
|
||||
// CloseContext signals end of data to write. CloseContext blocks until all
|
||||
// transfers that were sent are finished or until the context is canceled. The
|
||||
// error returned by CloseContext is the first error encountered during writing
|
||||
// the entire stream (if any).
|
||||
// CloseContext returning nil indicates all transfers completed successfully.
|
||||
// After CloseContext, the total number of bytes successfully written can be
|
||||
// retrieved using Written().
|
||||
// Close may not be called concurrently with Write, Close or Written.
|
||||
// CloseContext
|
||||
// CloseContext may not be called concurrently with Write, WriteContext, Close,
|
||||
// CloseContext or Written.
|
||||
func (w *WriteStream) CloseContext(ctx context.Context) error {
|
||||
if w.s.transfers == nil {
|
||||
return io.ErrClosedPipe
|
||||
|
73
usb.go
73
usb.go
@@ -125,11 +125,20 @@ see the excellent "USB in a nutshell" guide: http://www.beyondlogic.org/usbnutsh
|
||||
*/
|
||||
package gousb
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Context manages all resources related to USB device handling.
|
||||
type Context struct {
|
||||
ctx *libusbContext
|
||||
done chan struct{}
|
||||
libusb libusbIntf
|
||||
|
||||
mu sync.Mutex
|
||||
devices map[*Device]bool
|
||||
}
|
||||
|
||||
// Debug changes the debug level. Level 0 means no debug, higher levels
|
||||
@@ -146,9 +155,10 @@ func newContextWithImpl(impl libusbIntf) *Context {
|
||||
panic(err)
|
||||
}
|
||||
ctx := &Context{
|
||||
ctx: c,
|
||||
done: make(chan struct{}),
|
||||
libusb: impl,
|
||||
ctx: c,
|
||||
done: make(chan struct{}),
|
||||
libusb: impl,
|
||||
devices: make(map[*Device]bool),
|
||||
}
|
||||
go impl.handleEvents(ctx.ctx, ctx.done)
|
||||
return ctx
|
||||
@@ -165,6 +175,9 @@ func NewContext() *Context {
|
||||
// If there are any errors enumerating the devices,
|
||||
// the final one is returned along with any successfully opened devices.
|
||||
func (c *Context) OpenDevices(opener func(desc *DeviceDesc) bool) ([]*Device, error) {
|
||||
if c.ctx == nil {
|
||||
return nil, errors.New("OpenDevices called on a closed or uninitialized Context")
|
||||
}
|
||||
list, err := c.libusb.getDevices(c.ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -174,23 +187,26 @@ func (c *Context) OpenDevices(opener func(desc *DeviceDesc) bool) ([]*Device, er
|
||||
var ret []*Device
|
||||
for _, dev := range list {
|
||||
desc, err := c.libusb.getDeviceDesc(dev)
|
||||
defer c.libusb.dereference(dev)
|
||||
if err != nil {
|
||||
c.libusb.dereference(dev)
|
||||
reterr = err
|
||||
continue
|
||||
}
|
||||
|
||||
if opener(desc) {
|
||||
handle, err := c.libusb.open(dev)
|
||||
if err != nil {
|
||||
c.libusb.dereference(dev)
|
||||
reterr = err
|
||||
continue
|
||||
}
|
||||
ret = append(ret, &Device{handle: handle, ctx: c, Desc: desc})
|
||||
} else {
|
||||
c.libusb.dereference(dev)
|
||||
if !opener(desc) {
|
||||
continue
|
||||
}
|
||||
handle, err := c.libusb.open(dev)
|
||||
if err != nil {
|
||||
reterr = err
|
||||
continue
|
||||
}
|
||||
o := &Device{handle: handle, ctx: c, Desc: desc}
|
||||
ret = append(ret, o)
|
||||
c.mu.Lock()
|
||||
c.devices[o] = true
|
||||
c.mu.Unlock()
|
||||
|
||||
}
|
||||
return ret, reterr
|
||||
}
|
||||
@@ -219,13 +235,32 @@ func (c *Context) OpenDeviceWithVIDPID(vid, pid ID) (*Device, error) {
|
||||
return devs[0], nil
|
||||
}
|
||||
|
||||
func (c *Context) closeDev(d *Device) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
c.libusb.close(d.handle)
|
||||
delete(c.devices, d)
|
||||
}
|
||||
|
||||
func (c *Context) checkOpenDevs() error {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
if l := len(c.devices); l > 0 {
|
||||
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)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close releases the Context and all associated resources.
|
||||
func (c *Context) Close() error {
|
||||
var ret error
|
||||
c.done <- struct{}{}
|
||||
if c.ctx != nil {
|
||||
ret = c.libusb.exit(c.ctx)
|
||||
if c.ctx == nil {
|
||||
return nil
|
||||
}
|
||||
if err := c.checkOpenDevs(); err != nil {
|
||||
return err
|
||||
}
|
||||
c.done <- struct{}{}
|
||||
err := c.libusb.exit(c.ctx)
|
||||
c.ctx = nil
|
||||
return ret
|
||||
return err
|
||||
}
|
||||
|
@@ -41,6 +41,11 @@ func TestOPenDevices(t *testing.T) {
|
||||
t.Fatalf("OpenDevices(): %s", err)
|
||||
}
|
||||
|
||||
// attempt to Close() should fail because of open devices
|
||||
if err := c.Close(); err == nil {
|
||||
t.Fatal("Context.Close succeeded while some devices were still open")
|
||||
}
|
||||
|
||||
if got, want := len(devs), len(fakeDevices); got != want {
|
||||
t.Fatalf("len(devs) = %d, want %d (based on num fake devs)", got, want)
|
||||
}
|
||||
|
Reference in New Issue
Block a user