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
|
||||
}
|
||||
|
||||
t, err := newUSBTransfer(e.h, &e.Desc, buf, e.Timeout)
|
||||
t, err := newUSBTransfer(e.h, &e.Desc, len(buf), e.Timeout)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer t.free()
|
||||
if e.Desc.Direction == EndpointDirectionOut {
|
||||
copy(t.data(), buf)
|
||||
}
|
||||
|
||||
if err := t.submit(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
n, err := t.wait()
|
||||
if e.Desc.Direction == EndpointDirectionIn {
|
||||
copy(buf, t.data())
|
||||
}
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
|
@@ -17,7 +17,7 @@ package gousb
|
||||
func (e *endpoint) newStream(size, count int, submit bool) (*stream, error) {
|
||||
var ts []transferIntf
|
||||
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 {
|
||||
for _, t := range ts {
|
||||
t.free()
|
||||
|
@@ -149,11 +149,11 @@ func (f *fakeLibusb) setAlt(d *libusbDevHandle, intf, alt uint8) error {
|
||||
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()
|
||||
defer f.mu.Unlock()
|
||||
t := newFakeTransferPointer()
|
||||
f.ts[t] = &fakeTransfer{buf: buf, done: done}
|
||||
f.ts[t] = &fakeTransfer{buf: make([]byte, bufLen), done: done}
|
||||
return t, nil
|
||||
}
|
||||
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
|
||||
return nil
|
||||
}
|
||||
func (f *fakeLibusb) buffer(t *libusbTransfer) []byte { return f.ts[t].buf }
|
||||
func (f *fakeLibusb) data(t *libusbTransfer) (int, TransferStatus) {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
|
34
libusb.go
34
libusb.go
@@ -27,7 +27,9 @@ import (
|
||||
#cgo pkg-config: libusb-1.0
|
||||
#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);
|
||||
*/
|
||||
import "C"
|
||||
@@ -156,9 +158,10 @@ type libusbIntf interface {
|
||||
setAlt(*libusbDevHandle, uint8, uint8) error
|
||||
|
||||
// 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
|
||||
submit(*libusbTransfer) error
|
||||
buffer(*libusbTransfer) []byte
|
||||
data(*libusbTransfer) (int, TransferStatus)
|
||||
free(*libusbTransfer)
|
||||
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)))
|
||||
}
|
||||
|
||||
func (libusbImpl) alloc(d *libusbDevHandle, ep *EndpointDesc, timeout time.Duration, isoPackets int, buf []byte, done chan struct{}) (*libusbTransfer, error) {
|
||||
xfer := C.libusb_alloc_transfer(C.int(isoPackets))
|
||||
func (libusbImpl) alloc(d *libusbDevHandle, ep *EndpointDesc, timeout time.Duration, isoPackets int, bufLen int, done chan struct{}) (*libusbTransfer, error) {
|
||||
xfer := C.gousb_alloc_transfer_and_buffer(C.int(bufLen), C.int(isoPackets))
|
||||
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.endpoint = C.uchar(ep.Address)
|
||||
xfer.timeout = C.uint(timeout / time.Millisecond)
|
||||
xfer._type = C.uchar(ep.TransferType)
|
||||
xfer.num_iso_packets = C.int(isoPackets)
|
||||
xfer.buffer = (*C.uchar)(unsafe.Pointer(&buf[0]))
|
||||
xfer.length = C.int(len(buf))
|
||||
ret := (*libusbTransfer)(xfer)
|
||||
xferDoneMap.Lock()
|
||||
xferDoneMap.m[ret] = done
|
||||
@@ -438,17 +442,29 @@ func (libusbImpl) submit(t *libusbTransfer) error {
|
||||
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) {
|
||||
if TransferType(t._type) == TransferTypeIsochronous {
|
||||
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 int(t.actual_length), TransferStatus(t.status)
|
||||
}
|
||||
|
||||
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()
|
||||
delete(xferDoneMap.m, t)
|
||||
xferDoneMap.Unlock()
|
||||
|
@@ -140,6 +140,9 @@ func main() {
|
||||
}
|
||||
dev := devs[0]
|
||||
|
||||
log.Print("Enabling autodetach")
|
||||
dev.SetAutoDetach(true)
|
||||
|
||||
log.Printf("Setting configuration %d...", *config)
|
||||
cfg, err := dev.Config(*config)
|
||||
if err != nil {
|
||||
|
26
transfer.c
26
transfer.c
@@ -15,6 +15,7 @@
|
||||
|
||||
#include <libusb.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
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
|
||||
// iso packets are shifted left, so that no gaps are left between them.
|
||||
// Status is set to the first non-zero status of an iso packet.
|
||||
int compact_iso_data(struct libusb_transfer *xfer, unsigned char *status) {
|
||||
int gousb_compact_iso_data(struct libusb_transfer *xfer, unsigned char *status) {
|
||||
int i;
|
||||
int sum = 0;
|
||||
unsigned char *in = xfer->buffer;
|
||||
@@ -74,3 +75,26 @@ int compact_iso_data(struct libusb_transfer *xfer, unsigned char *status) {
|
||||
}
|
||||
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
|
||||
// xfer is the allocated libusb_transfer.
|
||||
xfer *libusbTransfer
|
||||
// buf is the buffer allocated for the transfer. Both buf and xfer.buffer
|
||||
// point to the same piece of memory.
|
||||
// buf is the buffer allocated for the transfer. The underlying memory
|
||||
// is allocated by the C code, both buf and xfer.buffer point to the same
|
||||
// memory.
|
||||
buf []byte
|
||||
// done is blocking until the transfer is complete and data and transfer
|
||||
// status are available.
|
||||
@@ -97,6 +98,9 @@ func (t *usbTransfer) free() error {
|
||||
if t.submitted {
|
||||
return errors.New("free() cannot be called on a submitted transfer until wait() returns")
|
||||
}
|
||||
if t.xfer == nil {
|
||||
return nil
|
||||
}
|
||||
libusb.free(t.xfer)
|
||||
t.xfer = nil
|
||||
t.buf = nil
|
||||
@@ -109,21 +113,21 @@ func (t *usbTransfer) data() []byte {
|
||||
return t.buf
|
||||
}
|
||||
|
||||
// newUSBTransfer allocates a new transfer structure for communication with a
|
||||
// given device/endpoint, with buf as the underlying transfer buffer.
|
||||
func newUSBTransfer(dev *libusbDevHandle, ei *EndpointDesc, buf []byte, timeout time.Duration) (*usbTransfer, error) {
|
||||
// newUSBTransfer allocates a new transfer structure and a new buffer for
|
||||
// communication with a given device/endpoint.
|
||||
func newUSBTransfer(dev *libusbDevHandle, ei *EndpointDesc, bufLen int, timeout time.Duration) (*usbTransfer, error) {
|
||||
var isoPackets, isoPktSize int
|
||||
if ei.TransferType == TransferTypeIsochronous {
|
||||
isoPktSize = ei.MaxPacketSize
|
||||
if len(buf) < isoPktSize {
|
||||
isoPktSize = len(buf)
|
||||
if bufLen < isoPktSize {
|
||||
isoPktSize = bufLen
|
||||
}
|
||||
isoPackets = len(buf) / isoPktSize
|
||||
debug.Printf("New isochronous transfer - buffer length %d, using %d packets of %d bytes each", len(buf), isoPackets, isoPktSize)
|
||||
isoPackets = bufLen / isoPktSize
|
||||
debug.Printf("New isochronous transfer - buffer length %d, using %d packets of %d bytes each", bufLen, isoPackets, isoPktSize)
|
||||
}
|
||||
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
@@ -134,7 +138,7 @@ func newUSBTransfer(dev *libusbDevHandle, ei *EndpointDesc, buf []byte, timeout
|
||||
|
||||
t := &usbTransfer{
|
||||
xfer: xfer,
|
||||
buf: buf,
|
||||
buf: libusb.buffer(xfer),
|
||||
done: done,
|
||||
}
|
||||
runtime.SetFinalizer(t, func(t *usbTransfer) {
|
||||
|
@@ -57,13 +57,13 @@ func TestNewTransfer(t *testing.T) {
|
||||
Direction: tc.dir,
|
||||
TransferType: tc.tt,
|
||||
MaxPacketSize: tc.maxPkt,
|
||||
}, make([]byte, tc.buf), tc.timeout)
|
||||
}, tc.buf, tc.timeout)
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("newUSBTransfer(): %v", err)
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -81,7 +81,7 @@ func TestTransferProtocol(t *testing.T) {
|
||||
Direction: EndpointDirectionIn,
|
||||
TransferType: TransferTypeBulk,
|
||||
MaxPacketSize: 512,
|
||||
}, make([]byte, 10240), time.Second)
|
||||
}, 10240, time.Second)
|
||||
if err != nil {
|
||||
t.Fatalf("newUSBTransfer: %v", err)
|
||||
}
|
||||
|
Reference in New Issue
Block a user