Allocate libusb buffers in C (#11)
* add alloc/free_transfer_and_buffer. Manages the buffer memory on the C side. * switch libusb.go to use the new alloc/free_transfer_and_buffer. Add a new buffer() call to get access to the allocated buffer as a Go slice. * Fake USB transfer uses the new alloc/free/buffer interface. * Switch to the new libusb.alloc signature, where libusb owns the buffer. * newUSBTransfer now allocates a separate buffer, do a copy on endpoint.transfer. * newUSBTransfer will now allocate it's own buffer. * Enable autodetach in rawread.
This commit is contained in:
@@ -89,17 +89,23 @@ func (e *endpoint) transfer(buf []byte) (int, error) {
|
|||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
t, err := newUSBTransfer(e.h, &e.Desc, buf, e.Timeout)
|
t, err := newUSBTransfer(e.h, &e.Desc, len(buf), e.Timeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
defer t.free()
|
defer t.free()
|
||||||
|
if e.Desc.Direction == EndpointDirectionOut {
|
||||||
|
copy(t.data(), buf)
|
||||||
|
}
|
||||||
|
|
||||||
if err := t.submit(); err != nil {
|
if err := t.submit(); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
n, err := t.wait()
|
n, err := t.wait()
|
||||||
|
if e.Desc.Direction == EndpointDirectionIn {
|
||||||
|
copy(buf, t.data())
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
|
@@ -17,7 +17,7 @@ package gousb
|
|||||||
func (e *endpoint) newStream(size, count int, submit bool) (*stream, error) {
|
func (e *endpoint) newStream(size, count int, submit bool) (*stream, error) {
|
||||||
var ts []transferIntf
|
var ts []transferIntf
|
||||||
for i := 0; i < count; i++ {
|
for i := 0; i < count; i++ {
|
||||||
t, err := newUSBTransfer(e.h, &e.Desc, make([]byte, size), e.Timeout)
|
t, err := newUSBTransfer(e.h, &e.Desc, size, e.Timeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
for _, t := range ts {
|
for _, t := range ts {
|
||||||
t.free()
|
t.free()
|
||||||
|
@@ -149,11 +149,11 @@ func (f *fakeLibusb) setAlt(d *libusbDevHandle, intf, alt uint8) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *fakeLibusb) alloc(_ *libusbDevHandle, _ *EndpointDesc, _ time.Duration, _ int, buf []byte, done chan struct{}) (*libusbTransfer, error) {
|
func (f *fakeLibusb) alloc(_ *libusbDevHandle, _ *EndpointDesc, _ time.Duration, _ int, bufLen int, done chan struct{}) (*libusbTransfer, error) {
|
||||||
f.mu.Lock()
|
f.mu.Lock()
|
||||||
defer f.mu.Unlock()
|
defer f.mu.Unlock()
|
||||||
t := newFakeTransferPointer()
|
t := newFakeTransferPointer()
|
||||||
f.ts[t] = &fakeTransfer{buf: buf, done: done}
|
f.ts[t] = &fakeTransfer{buf: make([]byte, bufLen), done: done}
|
||||||
return t, nil
|
return t, nil
|
||||||
}
|
}
|
||||||
func (f *fakeLibusb) cancel(t *libusbTransfer) error { return errors.New("not implemented") }
|
func (f *fakeLibusb) cancel(t *libusbTransfer) error { return errors.New("not implemented") }
|
||||||
@@ -164,6 +164,7 @@ func (f *fakeLibusb) submit(t *libusbTransfer) error {
|
|||||||
f.submitted <- ft
|
f.submitted <- ft
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
func (f *fakeLibusb) buffer(t *libusbTransfer) []byte { return f.ts[t].buf }
|
||||||
func (f *fakeLibusb) data(t *libusbTransfer) (int, TransferStatus) {
|
func (f *fakeLibusb) data(t *libusbTransfer) (int, TransferStatus) {
|
||||||
f.mu.Lock()
|
f.mu.Lock()
|
||||||
defer f.mu.Unlock()
|
defer f.mu.Unlock()
|
||||||
|
34
libusb.go
34
libusb.go
@@ -27,7 +27,9 @@ import (
|
|||||||
#cgo pkg-config: libusb-1.0
|
#cgo pkg-config: libusb-1.0
|
||||||
#include <libusb.h>
|
#include <libusb.h>
|
||||||
|
|
||||||
int compact_iso_data(struct libusb_transfer *xfer, unsigned char *status);
|
int gousb_compact_iso_data(struct libusb_transfer *xfer, unsigned char *status);
|
||||||
|
struct libusb_transfer *gousb_alloc_transfer_and_buffer(int bufLen, int numIsoPackets);
|
||||||
|
void gousb_free_transfer_and_buffer(struct libusb_transfer *xfer);
|
||||||
int submit(struct libusb_transfer *xfer);
|
int submit(struct libusb_transfer *xfer);
|
||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
@@ -156,9 +158,10 @@ type libusbIntf interface {
|
|||||||
setAlt(*libusbDevHandle, uint8, uint8) error
|
setAlt(*libusbDevHandle, uint8, uint8) error
|
||||||
|
|
||||||
// transfer
|
// transfer
|
||||||
alloc(*libusbDevHandle, *EndpointDesc, time.Duration, int, []byte, chan struct{}) (*libusbTransfer, error)
|
alloc(*libusbDevHandle, *EndpointDesc, time.Duration, int, int, chan struct{}) (*libusbTransfer, error)
|
||||||
cancel(*libusbTransfer) error
|
cancel(*libusbTransfer) error
|
||||||
submit(*libusbTransfer) error
|
submit(*libusbTransfer) error
|
||||||
|
buffer(*libusbTransfer) []byte
|
||||||
data(*libusbTransfer) (int, TransferStatus)
|
data(*libusbTransfer) (int, TransferStatus)
|
||||||
free(*libusbTransfer)
|
free(*libusbTransfer)
|
||||||
setIsoPacketLengths(*libusbTransfer, uint32)
|
setIsoPacketLengths(*libusbTransfer, uint32)
|
||||||
@@ -411,18 +414,19 @@ func (libusbImpl) setAlt(d *libusbDevHandle, iface, setup uint8) error {
|
|||||||
return fromErrNo(C.libusb_set_interface_alt_setting((*C.libusb_device_handle)(d), C.int(iface), C.int(setup)))
|
return fromErrNo(C.libusb_set_interface_alt_setting((*C.libusb_device_handle)(d), C.int(iface), C.int(setup)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (libusbImpl) alloc(d *libusbDevHandle, ep *EndpointDesc, timeout time.Duration, isoPackets int, buf []byte, done chan struct{}) (*libusbTransfer, error) {
|
func (libusbImpl) alloc(d *libusbDevHandle, ep *EndpointDesc, timeout time.Duration, isoPackets int, bufLen int, done chan struct{}) (*libusbTransfer, error) {
|
||||||
xfer := C.libusb_alloc_transfer(C.int(isoPackets))
|
xfer := C.gousb_alloc_transfer_and_buffer(C.int(bufLen), C.int(isoPackets))
|
||||||
if xfer == nil {
|
if xfer == nil {
|
||||||
return nil, fmt.Errorf("libusb_alloc_transfer(%d) failed", isoPackets)
|
return nil, fmt.Errorf("gousb_alloc_transfer_and_buffer(%d, %d) failed", bufLen, isoPackets)
|
||||||
|
}
|
||||||
|
if int(xfer.length) != bufLen {
|
||||||
|
return nil, fmt.Errorf("gousb_alloc_transfer_and_buffer(%d, %d): length = %d, want %d", bufLen, isoPackets, xfer.length, bufLen)
|
||||||
}
|
}
|
||||||
xfer.dev_handle = (*C.libusb_device_handle)(d)
|
xfer.dev_handle = (*C.libusb_device_handle)(d)
|
||||||
xfer.endpoint = C.uchar(ep.Address)
|
xfer.endpoint = C.uchar(ep.Address)
|
||||||
xfer.timeout = C.uint(timeout / time.Millisecond)
|
xfer.timeout = C.uint(timeout / time.Millisecond)
|
||||||
xfer._type = C.uchar(ep.TransferType)
|
xfer._type = C.uchar(ep.TransferType)
|
||||||
xfer.num_iso_packets = C.int(isoPackets)
|
xfer.num_iso_packets = C.int(isoPackets)
|
||||||
xfer.buffer = (*C.uchar)(unsafe.Pointer(&buf[0]))
|
|
||||||
xfer.length = C.int(len(buf))
|
|
||||||
ret := (*libusbTransfer)(xfer)
|
ret := (*libusbTransfer)(xfer)
|
||||||
xferDoneMap.Lock()
|
xferDoneMap.Lock()
|
||||||
xferDoneMap.m[ret] = done
|
xferDoneMap.m[ret] = done
|
||||||
@@ -438,17 +442,29 @@ func (libusbImpl) submit(t *libusbTransfer) error {
|
|||||||
return fromErrNo(C.submit((*C.struct_libusb_transfer)(t)))
|
return fromErrNo(C.submit((*C.struct_libusb_transfer)(t)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (libusbImpl) buffer(t *libusbTransfer) []byte {
|
||||||
|
// TODO(go1.10?): replace with more user-friendly construct once
|
||||||
|
// one exists. https://github.com/golang/go/issues/13656
|
||||||
|
var ret []byte
|
||||||
|
*(*reflect.SliceHeader)(unsafe.Pointer(&ret)) = reflect.SliceHeader{
|
||||||
|
Data: uintptr(unsafe.Pointer(t.buffer)),
|
||||||
|
Len: int(t.length),
|
||||||
|
Cap: int(t.length),
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
func (libusbImpl) data(t *libusbTransfer) (int, TransferStatus) {
|
func (libusbImpl) data(t *libusbTransfer) (int, TransferStatus) {
|
||||||
if TransferType(t._type) == TransferTypeIsochronous {
|
if TransferType(t._type) == TransferTypeIsochronous {
|
||||||
var status TransferStatus
|
var status TransferStatus
|
||||||
n := int(C.compact_iso_data((*C.struct_libusb_transfer)(t), (*C.uchar)(unsafe.Pointer(&status))))
|
n := int(C.gousb_compact_iso_data((*C.struct_libusb_transfer)(t), (*C.uchar)(unsafe.Pointer(&status))))
|
||||||
return n, status
|
return n, status
|
||||||
}
|
}
|
||||||
return int(t.actual_length), TransferStatus(t.status)
|
return int(t.actual_length), TransferStatus(t.status)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (libusbImpl) free(t *libusbTransfer) {
|
func (libusbImpl) free(t *libusbTransfer) {
|
||||||
C.libusb_free_transfer((*C.struct_libusb_transfer)(t))
|
C.gousb_free_transfer_and_buffer((*C.struct_libusb_transfer)(t))
|
||||||
xferDoneMap.Lock()
|
xferDoneMap.Lock()
|
||||||
delete(xferDoneMap.m, t)
|
delete(xferDoneMap.m, t)
|
||||||
xferDoneMap.Unlock()
|
xferDoneMap.Unlock()
|
||||||
|
@@ -140,6 +140,9 @@ func main() {
|
|||||||
}
|
}
|
||||||
dev := devs[0]
|
dev := devs[0]
|
||||||
|
|
||||||
|
log.Print("Enabling autodetach")
|
||||||
|
dev.SetAutoDetach(true)
|
||||||
|
|
||||||
log.Printf("Setting configuration %d...", *config)
|
log.Printf("Setting configuration %d...", *config)
|
||||||
cfg, err := dev.Config(*config)
|
cfg, err := dev.Config(*config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
26
transfer.c
26
transfer.c
@@ -15,6 +15,7 @@
|
|||||||
|
|
||||||
#include <libusb.h>
|
#include <libusb.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
void print_xfer(struct libusb_transfer *xfer);
|
void print_xfer(struct libusb_transfer *xfer);
|
||||||
@@ -53,7 +54,7 @@ void print_xfer(struct libusb_transfer *xfer) {
|
|||||||
// compact the data in an isochronous transfer. The contents of individual
|
// compact the data in an isochronous transfer. The contents of individual
|
||||||
// iso packets are shifted left, so that no gaps are left between them.
|
// iso packets are shifted left, so that no gaps are left between them.
|
||||||
// Status is set to the first non-zero status of an iso packet.
|
// Status is set to the first non-zero status of an iso packet.
|
||||||
int compact_iso_data(struct libusb_transfer *xfer, unsigned char *status) {
|
int gousb_compact_iso_data(struct libusb_transfer *xfer, unsigned char *status) {
|
||||||
int i;
|
int i;
|
||||||
int sum = 0;
|
int sum = 0;
|
||||||
unsigned char *in = xfer->buffer;
|
unsigned char *in = xfer->buffer;
|
||||||
@@ -74,3 +75,26 @@ int compact_iso_data(struct libusb_transfer *xfer, unsigned char *status) {
|
|||||||
}
|
}
|
||||||
return sum;
|
return sum;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// allocates a libusb transfer and a buffer for packet data.
|
||||||
|
struct libusb_transfer *gousb_alloc_transfer_and_buffer(int bufLen, int isoPackets) {
|
||||||
|
struct libusb_transfer *xfer = libusb_alloc_transfer(isoPackets);
|
||||||
|
if (xfer == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
xfer->buffer = (unsigned char*)malloc(bufLen);
|
||||||
|
if (xfer->buffer == NULL) {
|
||||||
|
libusb_free_transfer(xfer);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
xfer->length = bufLen;
|
||||||
|
return xfer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// frees a libusb transfer and its buffer. The buffer of the given
|
||||||
|
// libusb_transfer must have been allocated with alloc_transfer_and_buffer.
|
||||||
|
void gousb_free_transfer_and_buffer(struct libusb_transfer *xfer) {
|
||||||
|
free(xfer->buffer);
|
||||||
|
xfer->length = 0;
|
||||||
|
libusb_free_transfer(xfer);
|
||||||
|
}
|
||||||
|
26
transfer.go
26
transfer.go
@@ -26,8 +26,9 @@ type usbTransfer struct {
|
|||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
// xfer is the allocated libusb_transfer.
|
// xfer is the allocated libusb_transfer.
|
||||||
xfer *libusbTransfer
|
xfer *libusbTransfer
|
||||||
// buf is the buffer allocated for the transfer. Both buf and xfer.buffer
|
// buf is the buffer allocated for the transfer. The underlying memory
|
||||||
// point to the same piece of memory.
|
// is allocated by the C code, both buf and xfer.buffer point to the same
|
||||||
|
// memory.
|
||||||
buf []byte
|
buf []byte
|
||||||
// done is blocking until the transfer is complete and data and transfer
|
// done is blocking until the transfer is complete and data and transfer
|
||||||
// status are available.
|
// status are available.
|
||||||
@@ -97,6 +98,9 @@ func (t *usbTransfer) free() error {
|
|||||||
if t.submitted {
|
if t.submitted {
|
||||||
return errors.New("free() cannot be called on a submitted transfer until wait() returns")
|
return errors.New("free() cannot be called on a submitted transfer until wait() returns")
|
||||||
}
|
}
|
||||||
|
if t.xfer == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
libusb.free(t.xfer)
|
libusb.free(t.xfer)
|
||||||
t.xfer = nil
|
t.xfer = nil
|
||||||
t.buf = nil
|
t.buf = nil
|
||||||
@@ -109,21 +113,21 @@ func (t *usbTransfer) data() []byte {
|
|||||||
return t.buf
|
return t.buf
|
||||||
}
|
}
|
||||||
|
|
||||||
// newUSBTransfer allocates a new transfer structure for communication with a
|
// newUSBTransfer allocates a new transfer structure and a new buffer for
|
||||||
// given device/endpoint, with buf as the underlying transfer buffer.
|
// communication with a given device/endpoint.
|
||||||
func newUSBTransfer(dev *libusbDevHandle, ei *EndpointDesc, buf []byte, timeout time.Duration) (*usbTransfer, error) {
|
func newUSBTransfer(dev *libusbDevHandle, ei *EndpointDesc, bufLen int, timeout time.Duration) (*usbTransfer, error) {
|
||||||
var isoPackets, isoPktSize int
|
var isoPackets, isoPktSize int
|
||||||
if ei.TransferType == TransferTypeIsochronous {
|
if ei.TransferType == TransferTypeIsochronous {
|
||||||
isoPktSize = ei.MaxPacketSize
|
isoPktSize = ei.MaxPacketSize
|
||||||
if len(buf) < isoPktSize {
|
if bufLen < isoPktSize {
|
||||||
isoPktSize = len(buf)
|
isoPktSize = bufLen
|
||||||
}
|
}
|
||||||
isoPackets = len(buf) / isoPktSize
|
isoPackets = bufLen / isoPktSize
|
||||||
debug.Printf("New isochronous transfer - buffer length %d, using %d packets of %d bytes each", len(buf), isoPackets, isoPktSize)
|
debug.Printf("New isochronous transfer - buffer length %d, using %d packets of %d bytes each", bufLen, isoPackets, isoPktSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
done := make(chan struct{}, 1)
|
done := make(chan struct{}, 1)
|
||||||
xfer, err := libusb.alloc(dev, ei, timeout, isoPackets, buf, done)
|
xfer, err := libusb.alloc(dev, ei, timeout, isoPackets, bufLen, done)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -134,7 +138,7 @@ func newUSBTransfer(dev *libusbDevHandle, ei *EndpointDesc, buf []byte, timeout
|
|||||||
|
|
||||||
t := &usbTransfer{
|
t := &usbTransfer{
|
||||||
xfer: xfer,
|
xfer: xfer,
|
||||||
buf: buf,
|
buf: libusb.buffer(xfer),
|
||||||
done: done,
|
done: done,
|
||||||
}
|
}
|
||||||
runtime.SetFinalizer(t, func(t *usbTransfer) {
|
runtime.SetFinalizer(t, func(t *usbTransfer) {
|
||||||
|
@@ -57,13 +57,13 @@ func TestNewTransfer(t *testing.T) {
|
|||||||
Direction: tc.dir,
|
Direction: tc.dir,
|
||||||
TransferType: tc.tt,
|
TransferType: tc.tt,
|
||||||
MaxPacketSize: tc.maxPkt,
|
MaxPacketSize: tc.maxPkt,
|
||||||
}, make([]byte, tc.buf), tc.timeout)
|
}, tc.buf, tc.timeout)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("newUSBTransfer(): %v", err)
|
t.Fatalf("newUSBTransfer(): %v", err)
|
||||||
}
|
}
|
||||||
defer xfer.free()
|
defer xfer.free()
|
||||||
if got, want := len(xfer.buf), tc.wantLength; got != want {
|
if got, want := len(xfer.data()), tc.wantLength; got != want {
|
||||||
t.Errorf("xfer.buf: got %d bytes, want %d", got, want)
|
t.Errorf("xfer.buf: got %d bytes, want %d", got, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -81,7 +81,7 @@ func TestTransferProtocol(t *testing.T) {
|
|||||||
Direction: EndpointDirectionIn,
|
Direction: EndpointDirectionIn,
|
||||||
TransferType: TransferTypeBulk,
|
TransferType: TransferTypeBulk,
|
||||||
MaxPacketSize: 512,
|
MaxPacketSize: 512,
|
||||||
}, make([]byte, 10240), time.Second)
|
}, 10240, time.Second)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("newUSBTransfer: %v", err)
|
t.Fatalf("newUSBTransfer: %v", err)
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user