From 80b2741a2c64a406f92d5fa84124e07153853efb Mon Sep 17 00:00:00 2001 From: Sebastian Zagrodzki Date: Thu, 9 Feb 2017 22:36:51 +0100 Subject: [PATCH 01/18] Rename Transfer to usbTransfer, it's internal to gousb. Move usbTransfer and it's methods to transfer.go, it's not only used by iso transfers. --- usb/iso.go | 60 ++-------------------------------------------- usb/transfer.go | 63 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 58 deletions(-) create mode 100644 usb/transfer.go diff --git a/usb/iso.go b/usb/iso.go index 517a126..cefaa7d 100644 --- a/usb/iso.go +++ b/usb/iso.go @@ -17,15 +17,10 @@ package usb /* #include - -int submit(struct libusb_transfer *xfer); -void print_xfer(struct libusb_transfer *xfer); -int extract_data(struct libusb_transfer *xfer, void *data, int max, unsigned char *status); */ import "C" import ( - "fmt" "log" "time" "unsafe" @@ -37,7 +32,7 @@ func iso_callback(cptr unsafe.Pointer) { close(ch) } -func (end *endpoint) allocTransfer() *Transfer { +func (end *endpoint) allocTransfer() *usbTransfer { const ( iso_packets = 8 // 128 // 242 ) @@ -68,7 +63,7 @@ func (end *endpoint) allocTransfer() *Transfer { })) */ - t := &Transfer{ + t := &usbTransfer{ xfer: xfer, done: done, buf: buf, @@ -78,57 +73,6 @@ func (end *endpoint) allocTransfer() *Transfer { return t } -type Transfer struct { - xfer *C.struct_libusb_transfer - pkts []*C.struct_libusb_packet_descriptor - done chan struct{} - buf []byte -} - -func (t *Transfer) Submit(timeout time.Duration) error { - //log.Printf("iso: submitting %#v", t.xfer) - t.xfer.timeout = C.uint(timeout / time.Millisecond) - if errno := C.submit(t.xfer); errno < 0 { - return usbError(errno) - } - return nil -} - -func (t *Transfer) Wait(b []byte) (n int, err error) { - select { - case <-time.After(10 * time.Second): - return 0, fmt.Errorf("wait timed out after 10s") - case <-t.done: - } - // Non-iso transfers: - //n = int(t.xfer.actual_length) - //copy(b, ((*[1 << 16]byte)(unsafe.Pointer(t.xfer.buffer)))[:n]) - - //C.print_xfer(t.xfer) - /* - buf, offset := ((*[1 << 16]byte)(unsafe.Pointer(t.xfer.buffer))), 0 - for i, pkt := range *t.pkts { - log.Printf("Type is %T", t.pkts) - n += copy(b[n:], buf[offset:][:pkt.actual_length]) - offset += pkt.Length - if pkt.status != 0 && err == nil { - err = error(TransferStatus(pkt.status)) - } - } - */ - var status uint8 - n = int(C.extract_data(t.xfer, unsafe.Pointer(&b[0]), C.int(len(b)), (*C.uchar)(unsafe.Pointer(&status)))) - if status != 0 { - err = TransferStatus(status) - } - return n, err -} - -func (t *Transfer) Close() error { - C.libusb_free_transfer(t.xfer) - return nil -} - func isochronous_xfer(e *endpoint, buf []byte, timeout time.Duration) (int, error) { t := e.allocTransfer() defer t.Close() diff --git a/usb/transfer.go b/usb/transfer.go new file mode 100644 index 0000000..d4714af --- /dev/null +++ b/usb/transfer.go @@ -0,0 +1,63 @@ +// Copyright 2016 the gousb Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package usb + +/* +#include + +int extract_data(struct libusb_transfer *xfer, void *data, int max, unsigned char *status); +int submit(struct libusb_transfer *xfer); +*/ +import "C" + +import ( + "fmt" + "time" + "unsafe" +) + +type usbTransfer struct { + xfer *C.struct_libusb_transfer + pkts []*C.struct_libusb_packet_descriptor + done chan struct{} + buf []byte +} + +func (t *usbTransfer) Submit(timeout time.Duration) error { + t.xfer.timeout = C.uint(timeout / time.Millisecond) + if errno := C.submit(t.xfer); errno < 0 { + return usbError(errno) + } + return nil +} + +func (t *usbTransfer) Wait(b []byte) (n int, err error) { + select { + case <-time.After(10 * time.Second): + return 0, fmt.Errorf("wait timed out after 10s") + case <-t.done: + } + var status uint8 + n = int(C.extract_data(t.xfer, unsafe.Pointer(&b[0]), C.int(len(b)), (*C.uchar)(unsafe.Pointer(&status)))) + if status != 0 { + err = TransferStatus(status) + } + return n, err +} + +func (t *usbTransfer) Close() error { + C.libusb_free_transfer(t.xfer) + return nil +} From de92c7047b33a5f0d90e888e138d139ed0d1281a Mon Sep 17 00:00:00 2001 From: Sebastian Zagrodzki Date: Thu, 9 Feb 2017 22:38:16 +0100 Subject: [PATCH 02/18] make methods of usbTransfer private --- usb/iso.go | 6 +++--- usb/transfer.go | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/usb/iso.go b/usb/iso.go index cefaa7d..732a5e5 100644 --- a/usb/iso.go +++ b/usb/iso.go @@ -75,14 +75,14 @@ func (end *endpoint) allocTransfer() *usbTransfer { func isochronous_xfer(e *endpoint, buf []byte, timeout time.Duration) (int, error) { t := e.allocTransfer() - defer t.Close() + defer t.free() - if err := t.Submit(timeout); err != nil { + if err := t.submit(timeout); err != nil { log.Printf("iso: xfer failed to submit: %s", err) return 0, err } - n, err := t.Wait(buf) + n, err := t.wait(buf) if err != nil { log.Printf("iso: xfer failed: %s", err) return 0, err diff --git a/usb/transfer.go b/usb/transfer.go index d4714af..becfbf5 100644 --- a/usb/transfer.go +++ b/usb/transfer.go @@ -35,7 +35,7 @@ type usbTransfer struct { buf []byte } -func (t *usbTransfer) Submit(timeout time.Duration) error { +func (t *usbTransfer) submit(timeout time.Duration) error { t.xfer.timeout = C.uint(timeout / time.Millisecond) if errno := C.submit(t.xfer); errno < 0 { return usbError(errno) @@ -43,7 +43,7 @@ func (t *usbTransfer) Submit(timeout time.Duration) error { return nil } -func (t *usbTransfer) Wait(b []byte) (n int, err error) { +func (t *usbTransfer) wait(b []byte) (n int, err error) { select { case <-time.After(10 * time.Second): return 0, fmt.Errorf("wait timed out after 10s") @@ -57,7 +57,7 @@ func (t *usbTransfer) Wait(b []byte) (n int, err error) { return n, err } -func (t *usbTransfer) Close() error { +func (t *usbTransfer) free() error { C.libusb_free_transfer(t.xfer) return nil } From c792f8e0283caa76432b99857cce5b269d95c34c Mon Sep 17 00:00:00 2001 From: Sebastian Zagrodzki Date: Thu, 9 Feb 2017 22:51:28 +0100 Subject: [PATCH 03/18] replace allocTransfer with a new more generic newUSBTransfer --- usb/endpoint.go | 33 +++++++++++++++++++++++++++++++++ usb/iso.go | 46 ++++------------------------------------------ 2 files changed, 37 insertions(+), 42 deletions(-) diff --git a/usb/endpoint.go b/usb/endpoint.go index 3baa10a..6c9c3cd 100644 --- a/usb/endpoint.go +++ b/usb/endpoint.go @@ -99,3 +99,36 @@ func interrupt_xfer(e *endpoint, buf []byte, timeout time.Duration) (int, error) } return int(cnt), nil } + +func (e *endpoint) newUSBTransfer(tt TransferType, buf []byte) (*usbTransfer, error) { + var isoPackets int + if tt == TRANSFER_TYPE_ISOCHRONOUS { + isoPackets = len(buf) / int(e.EndpointInfo.MaxIsoPacket) + } + + xfer := C.libusb_alloc_transfer(C.int(isoPackets)) + if xfer == nil { + return nil, fmt.Errorf("libusb_alloc_transfer(%d) failed", isoPackets) + } + + done := make(chan struct{}, 1) + xfer.user_data = (unsafe.Pointer)(&done) + + xfer.dev_handle = e.Device.handle + xfer.endpoint = C.uchar(e.Address) + xfer._type = C.uchar(tt) + + xfer.buffer = (*C.uchar)((unsafe.Pointer)(&buf[0])) + xfer.length = C.int(len(buf)) + + if tt == TRANSFER_TYPE_ISOCHRONOUS { + xfer.num_iso_packets = C.int(isoPackets) + C.libusb_set_iso_packet_lengths(xfer, C.uint(e.EndpointInfo.MaxIsoPacket)) + } + + return &usbTransfer{ + xfer: xfer, + done: done, + buf: buf, + }, nil +} diff --git a/usb/iso.go b/usb/iso.go index 732a5e5..ef410e9 100644 --- a/usb/iso.go +++ b/usb/iso.go @@ -32,49 +32,11 @@ func iso_callback(cptr unsafe.Pointer) { close(ch) } -func (end *endpoint) allocTransfer() *usbTransfer { - const ( - iso_packets = 8 // 128 // 242 - ) - - xfer := C.libusb_alloc_transfer(C.int(iso_packets)) - if xfer == nil { - log.Printf("usb: transfer allocation failed?!") - return nil - } - - buf := make([]byte, iso_packets*end.EndpointInfo.MaxIsoPacket) - done := make(chan struct{}, 1) - - xfer.dev_handle = end.Device.handle - xfer.endpoint = C.uchar(end.Address) - xfer._type = C.LIBUSB_TRANSFER_TYPE_ISOCHRONOUS - - xfer.buffer = (*C.uchar)((unsafe.Pointer)(&buf[0])) - xfer.length = C.int(len(buf)) - xfer.num_iso_packets = iso_packets - - C.libusb_set_iso_packet_lengths(xfer, C.uint(end.EndpointInfo.MaxIsoPacket)) - /* - pkts := *(*[]C.struct_libusb_packet_descriptor)(unsafe.Pointer(&reflect.SliceHeader{ - Data: uintptr(unsafe.Pointer(&xfer.iso_packet_desc)), - Len: iso_packets, - Cap: iso_packets, - })) - */ - - t := &usbTransfer{ - xfer: xfer, - done: done, - buf: buf, - } - xfer.user_data = (unsafe.Pointer)(&t.done) - - return t -} - func isochronous_xfer(e *endpoint, buf []byte, timeout time.Duration) (int, error) { - t := e.allocTransfer() + t, err := e.newUSBTransfer(TRANSFER_TYPE_ISOCHRONOUS, buf) + if err != nil { + return 0, err + } defer t.free() if err := t.submit(timeout); err != nil { From a5290248dede170ac6a20de34deb73ffcadc54f4 Mon Sep 17 00:00:00 2001 From: Sebastian Zagrodzki Date: Fri, 10 Feb 2017 12:43:56 +0100 Subject: [PATCH 04/18] Merge vid/pid and bus/addr into single flags vidpid and busaddr. --- rawread/main.go | 68 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 55 insertions(+), 13 deletions(-) diff --git a/rawread/main.go b/rawread/main.go index 4e56e1e..b885294 100644 --- a/rawread/main.go +++ b/rawread/main.go @@ -21,6 +21,8 @@ import ( "fmt" "log" "os" + "strconv" + "strings" "sync/atomic" "time" @@ -29,10 +31,8 @@ import ( ) var ( - vid = flag.Uint("vid", 0, "VID of the device to which to connect. Exclusive with bus/addr flags.") - pid = flag.Uint("pid", 0, "PID of the device to which to connect. Exclusive with bus/addr flags.") - bus = flag.Uint("bus", 0, "Bus number for the device to which to connect. Exclusive with vid/pid flags.") - addr = flag.Uint("addr", 0, "Address of the device to which to connect. Exclusive with vid/pid flags.") + vidPID = flag.String("vidpid", "", "VID:PID of the device to which to connect. Exclusive with busaddr flag.") + busAddr = flag.String("busaddr", "", "Bus:address of the device to which to connect. Exclusive with vidpid flag.") config = flag.Uint("config", 1, "Endpoint to which to connect") iface = flag.Uint("interface", 0, "Endpoint to which to connect") setup = flag.Uint("setup", 0, "Endpoint to which to connect") @@ -42,6 +42,38 @@ var ( bench = flag.Bool("benchmark", false, "When true, keep reading from the endpoint and periodically report the measured throughput. If false, only one read operation is performed and obtained data is printed to STDOUT.") ) +func parseVIDPID(vidPid string) (usb.ID, usb.ID, error) { + s := strings.Split(vidPid, ":") + if len(s) != 2 { + return 0, 0, fmt.Errorf("want VID:PID, two 32-bit hex numbers separated by colon, e.g. 1d6b:0002") + } + vid, err := strconv.ParseUint(s[0], 16, 32) + if err != nil { + return 0, 0, fmt.Errorf("VID must be a hexadecimal 32-bit number, e.g. 1d6b") + } + pid, err := strconv.ParseUint(s[1], 16, 32) + if err != nil { + return 0, 0, fmt.Errorf("PID must be a hexadecimal 32-bit number, e.g. 1d6b") + } + return usb.ID(vid), usb.ID(pid), nil +} + +func parseBusAddr(busAddr string) (uint8, uint8, error) { + s := strings.Split(busAddr, ":") + if len(s) != 2 { + return 0, 0, fmt.Errorf("want bus:addr, two 8-bit decimal unsigned integers separated by colon, e.g. 1:1") + } + bus, err := strconv.ParseUint(s[0], 10, 8) + if err != nil { + return 0, 0, fmt.Errorf("bus number must be an 8-bit decimal unsigned integer") + } + addr, err := strconv.ParseUint(s[1], 10, 8) + if err != nil { + return 0, 0, fmt.Errorf("device address must be an 8-bit decimal unsigned integer") + } + return uint8(bus), uint8(addr), nil +} + func main() { flag.Parse() @@ -52,24 +84,34 @@ func main() { ctx.Debug(*debug) var devName string + var vid, pid usb.ID + var bus, addr uint8 switch { - case *vid == 0 && *pid == 0 && *bus == 0 && *addr == 0: - log.Fatal("You need to specify the device, either through --vid/--pid flags or through --bus/--addr flags.") - case (*vid > 0 || *pid > 0) && (*bus > 0 || *addr > 0): - log.Fatal("You can't use --vid/--pid flags at the same time as --bus/--addr.") - case *vid > 0 || *pid > 0: - devName = fmt.Sprintf("VID:PID %04x:%04x", *vid, *pid) + case *vidPID == "" && *busAddr == "": + log.Fatal("You need to specify the device through a --vidpid flag or through a --busaddr flag.") + case *vidPID != "" && *busAddr != "": + log.Fatal("You can't use --vidpid flag together with --busaddr. Pick one.") + case *vidPID != "": + vid, pid, err := parseVIDPID(*vidPID) + if err != nil { + log.Fatalf("Invalid value for --vidpid (%q): %v", *vidPID, err) + } + devName = fmt.Sprintf("VID:PID %s:%s", vid, pid) default: - devName = fmt.Sprintf("bus:addr %d:%d", *bus, *addr) + bus, addr, err := parseBusAddr(*busAddr) + if err != nil { + log.Fatalf("Invalid value for --busaddr (%q): %v", *busAddr, err) + } + devName = fmt.Sprintf("bus:addr %d:%d", bus, addr) } log.Printf("Scanning for device %q...", devName) // ListDevices is used to find the devices to open. devs, err := ctx.ListDevices(func(desc *usb.Descriptor) bool { switch { - case usb.ID(*vid) == desc.Vendor && usb.ID(*pid) == desc.Product: + case vid == desc.Vendor && pid == desc.Product: return true - case uint8(*bus) == desc.Bus && uint8(*addr) == desc.Address: + case bus == desc.Bus && addr == desc.Address: return true } return false From 9e50cd8c1c2ba2d5979ca89ba5f5652b5c5f9175 Mon Sep 17 00:00:00 2001 From: Sebastian Zagrodzki Date: Fri, 10 Feb 2017 21:45:26 +0100 Subject: [PATCH 05/18] Rename extract_data to extract_iso_data, add extract_data for use by other transfer types. --- usb/iso.c | 19 +++++++++++++++++++ usb/transfer.go | 8 +++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/usb/iso.c b/usb/iso.c index 7cd4dd0..05c1106 100644 --- a/usb/iso.c +++ b/usb/iso.c @@ -64,7 +64,26 @@ void print_xfer(struct libusb_transfer *xfer) { } } +// extract data from a non-isochronous transfer. int extract_data(struct libusb_transfer *xfer, void *raw, int max, unsigned char *status) { + int i; + unsigned char *in = xfer->buffer; + unsigned char *out = raw; + + int len = xfer->actual_length; + if (len > max) { + len = max; + } + memcpy(out, in, len); + if (xfer->status != 0 && *status == 0) { + *status = xfer->status; + } + return len; +} + +// extract data from an isochronous transfer. Very similar to extract_data, but +// acquires data from xfer->iso_packet_desc array instead of xfer attributes. +int extract_iso_data(struct libusb_transfer *xfer, void *raw, int max, unsigned char *status) { int i; int copied = 0; unsigned char *in = xfer->buffer; diff --git a/usb/transfer.go b/usb/transfer.go index becfbf5..8e447d3 100644 --- a/usb/transfer.go +++ b/usb/transfer.go @@ -18,6 +18,7 @@ package usb #include int extract_data(struct libusb_transfer *xfer, void *data, int max, unsigned char *status); +int extract_iso_data(struct libusb_transfer *xfer, void *data, int max, unsigned char *status); int submit(struct libusb_transfer *xfer); */ import "C" @@ -50,7 +51,12 @@ func (t *usbTransfer) wait(b []byte) (n int, err error) { case <-t.done: } var status uint8 - n = int(C.extract_data(t.xfer, unsafe.Pointer(&b[0]), C.int(len(b)), (*C.uchar)(unsafe.Pointer(&status)))) + switch TransferType(t.xfer._type) { + case TRANSFER_TYPE_ISOCHRONOUS: + n = int(C.extract_iso_data(t.xfer, unsafe.Pointer(&b[0]), C.int(len(b)), (*C.uchar)(unsafe.Pointer(&status)))) + default: + n = int(C.extract_data(t.xfer, unsafe.Pointer(&b[0]), C.int(len(b)), (*C.uchar)(unsafe.Pointer(&status)))) + } if status != 0 { err = TransferStatus(status) } From c9c2757fe63fab7c910f3f7488aa21772ce6afa4 Mon Sep 17 00:00:00 2001 From: Sebastian Zagrodzki Date: Fri, 10 Feb 2017 21:46:12 +0100 Subject: [PATCH 06/18] Replace the libusb_bulk_transfer with the same prepare-submit-wait routine iso transfers use. --- usb/endpoint.go | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/usb/endpoint.go b/usb/endpoint.go index 6c9c3cd..a6d30e2 100644 --- a/usb/endpoint.go +++ b/usb/endpoint.go @@ -20,6 +20,7 @@ import "C" import ( "fmt" + "log" "reflect" "time" "unsafe" @@ -65,19 +66,23 @@ func bulk_xfer(e *endpoint, buf []byte, timeout time.Duration) (int, error) { return 0, nil } - data := (*reflect.SliceHeader)(unsafe.Pointer(&buf)).Data - - var cnt C.int - if errno := C.libusb_bulk_transfer( - e.handle, - C.uchar(e.Address), - (*C.uchar)(unsafe.Pointer(data)), - C.int(len(buf)), - &cnt, - C.uint(timeout/time.Millisecond)); errno < 0 { - return 0, usbError(errno) + t, err := e.newUSBTransfer(TRANSFER_TYPE_BULK, buf) + if err != nil { + return 0, err } - return int(cnt), nil + defer t.free() + + if err := t.submit(timeout); err != nil { + log.Printf("bulk: xfer failed to submit: %s", err) + return 0, err + } + + n, err := t.wait(buf) + if err != nil { + log.Printf("bulk: xfer failed: %s", err) + return 0, err + } + return n, err } func interrupt_xfer(e *endpoint, buf []byte, timeout time.Duration) (int, error) { From 87abb704d7345e3dc7aced8d25a1973ced8f449d Mon Sep 17 00:00:00 2001 From: Sebastian Zagrodzki Date: Fri, 10 Feb 2017 22:06:57 +0100 Subject: [PATCH 07/18] Make all transfer types use a generic endpoint.transfer() function that uses libusb's asynchronous interface. --- usb/config.go | 9 ++++++--- usb/device.go | 10 ---------- usb/endpoint.go | 37 +++++++------------------------------ 3 files changed, 13 insertions(+), 43 deletions(-) diff --git a/usb/config.go b/usb/config.go index 53c5a97..d60a703 100644 --- a/usb/config.go +++ b/usb/config.go @@ -38,14 +38,17 @@ func (e EndpointInfo) Number() int { return int(e.Address) & ENDPOINT_NUM_MASK } +func (e EndpointInfo) TransferType() TransferType { + return TransferType(e.Attributes) & TRANSFER_TYPE_MASK +} + func (e EndpointInfo) Direction() EndpointDirection { return EndpointDirection(e.Address) & ENDPOINT_DIR_MASK } func (e EndpointInfo) String() string { return fmt.Sprintf("Endpoint %d %-3s %s - %s %s [%d %d]", - e.Number(), e.Direction(), - TransferType(e.Attributes)&TRANSFER_TYPE_MASK, + e.Number(), e.Direction(), e.TransferType(), IsoSyncType(e.Attributes)&ISO_SYNC_TYPE_MASK, IsoUsageType(e.Attributes)&ISO_USAGE_TYPE_MASK, e.MaxPacketSize, e.MaxIsoPacket, @@ -135,7 +138,7 @@ func newConfig(dev *C.libusb_device, cfg *C.struct_libusb_config_descriptor) Con RefreshRate: uint8(end.bRefresh), SynchAddress: uint8(end.bSynchAddress), } - if TransferType(ei.Attributes)&TRANSFER_TYPE_MASK == TRANSFER_TYPE_ISOCHRONOUS { + if ei.TransferType() == TRANSFER_TYPE_ISOCHRONOUS { // bits 0-10 identify the packet size, bits 11-12 are the number of additional transactions per microframe. // Don't use libusb_get_max_iso_packet_size, as it has a bug where it returns the same value // regardless of alternative setting used, where different alternative settings might define different diff --git a/usb/device.go b/usb/device.go index 309fd4c..e065a46 100644 --- a/usb/device.go +++ b/usb/device.go @@ -151,16 +151,6 @@ func (d *Device) OpenEndpoint(conf, iface, setup, epoint uint8) (Endpoint, error } end.InterfaceSetup = s end.EndpointInfo = e - switch tt := TransferType(e.Attributes) & TRANSFER_TYPE_MASK; tt { - case TRANSFER_TYPE_BULK: - end.xfer = bulk_xfer - case TRANSFER_TYPE_INTERRUPT: - end.xfer = interrupt_xfer - case TRANSFER_TYPE_ISOCHRONOUS: - end.xfer = isochronous_xfer - default: - return nil, fmt.Errorf("usb: %s transfer is unsupported", tt) - } goto found } return nil, fmt.Errorf("usb: unknown endpoint %02x", epoint) diff --git a/usb/endpoint.go b/usb/endpoint.go index a6d30e2..68fbcdf 100644 --- a/usb/endpoint.go +++ b/usb/endpoint.go @@ -21,7 +21,6 @@ import "C" import ( "fmt" "log" - "reflect" "time" "unsafe" ) @@ -37,7 +36,6 @@ type endpoint struct { *Device InterfaceSetup EndpointInfo - xfer func(*endpoint, []byte, time.Duration) (int, error) } func (e *endpoint) Read(buf []byte) (int, error) { @@ -45,7 +43,7 @@ func (e *endpoint) Read(buf []byte) (int, error) { return 0, fmt.Errorf("usb: read: not an IN endpoint") } - return e.xfer(e, buf, e.ReadTimeout) + return e.transfer(buf, e.ReadTimeout) } func (e *endpoint) Write(buf []byte) (int, error) { @@ -53,58 +51,37 @@ func (e *endpoint) Write(buf []byte) (int, error) { return 0, fmt.Errorf("usb: write: not an OUT endpoint") } - return e.xfer(e, buf, e.WriteTimeout) + return e.transfer(buf, e.WriteTimeout) } func (e *endpoint) Interface() InterfaceSetup { return e.InterfaceSetup } func (e *endpoint) Info() EndpointInfo { return e.EndpointInfo } -// TODO(kevlar): (*Endpoint).Close - -func bulk_xfer(e *endpoint, buf []byte, timeout time.Duration) (int, error) { +func (e *endpoint) transfer(buf []byte, timeout time.Duration) (int, error) { if len(buf) == 0 { return 0, nil } - t, err := e.newUSBTransfer(TRANSFER_TYPE_BULK, buf) + tt := e.TransferType() + t, err := e.newUSBTransfer(tt, buf) if err != nil { return 0, err } defer t.free() if err := t.submit(timeout); err != nil { - log.Printf("bulk: xfer failed to submit: %s", err) + log.Printf("bulk: %s failed to submit: %s", tt, err) return 0, err } n, err := t.wait(buf) if err != nil { - log.Printf("bulk: xfer failed: %s", err) + log.Printf("bulk: %s failed: %s", tt, err) return 0, err } return n, err } -func interrupt_xfer(e *endpoint, buf []byte, timeout time.Duration) (int, error) { - if len(buf) == 0 { - return 0, nil - } - - data := (*reflect.SliceHeader)(unsafe.Pointer(&buf)).Data - - var cnt C.int - if errno := C.libusb_interrupt_transfer( - e.handle, - C.uchar(e.Address), - (*C.uchar)(unsafe.Pointer(data)), - C.int(len(buf)), - &cnt, - C.uint(timeout/time.Millisecond)); errno < 0 { - return 0, usbError(errno) - } - return int(cnt), nil -} - func (e *endpoint) newUSBTransfer(tt TransferType, buf []byte) (*usbTransfer, error) { var isoPackets int if tt == TRANSFER_TYPE_ISOCHRONOUS { From d7ed6d67e6438828e9c6eafa84ad07e95d6a91a8 Mon Sep 17 00:00:00 2001 From: Sebastian Zagrodzki Date: Fri, 10 Feb 2017 22:08:39 +0100 Subject: [PATCH 08/18] Rename iso_callback to transfer_callback, it's used for all transfer types. --- usb/iso.c | 4 ++-- usb/iso.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/usb/iso.c b/usb/iso.c index 05c1106..6aac81b 100644 --- a/usb/iso.c +++ b/usb/iso.c @@ -18,12 +18,12 @@ #include void print_xfer(struct libusb_transfer *xfer); -void iso_callback(void *); +void transfer_callback(void *); void callback(struct libusb_transfer *xfer) { //printf("Callback!\n"); //print_xfer(xfer); - iso_callback(xfer->user_data); + transfer_callback(xfer->user_data); } int submit(struct libusb_transfer *xfer) { diff --git a/usb/iso.go b/usb/iso.go index ef410e9..e29d4a6 100644 --- a/usb/iso.go +++ b/usb/iso.go @@ -26,8 +26,8 @@ import ( "unsafe" ) -//export iso_callback -func iso_callback(cptr unsafe.Pointer) { +//export transfer_callback +func transfer_callback(cptr unsafe.Pointer) { ch := *(*chan struct{})(cptr) close(ch) } From 31b3ac1c67ff7654d294e9dce0a3c3bbc9514774 Mon Sep 17 00:00:00 2001 From: Sebastian Zagrodzki Date: Fri, 10 Feb 2017 22:11:54 +0100 Subject: [PATCH 09/18] move iso.c to transfer.c, as it's used for all transfers. move transfer callback to transfer.go. --- usb/iso.go | 7 ------- usb/{iso.c => transfer.c} | 14 ++------------ usb/transfer.go | 6 ++++++ 3 files changed, 8 insertions(+), 19 deletions(-) rename usb/{iso.c => transfer.c} (92%) diff --git a/usb/iso.go b/usb/iso.go index e29d4a6..01a9062 100644 --- a/usb/iso.go +++ b/usb/iso.go @@ -23,15 +23,8 @@ import "C" import ( "log" "time" - "unsafe" ) -//export transfer_callback -func transfer_callback(cptr unsafe.Pointer) { - ch := *(*chan struct{})(cptr) - close(ch) -} - func isochronous_xfer(e *endpoint, buf []byte, timeout time.Duration) (int, error) { t, err := e.newUSBTransfer(TRANSFER_TYPE_ISOCHRONOUS, buf) if err != nil { diff --git a/usb/iso.c b/usb/transfer.c similarity index 92% rename from usb/iso.c rename to usb/transfer.c index 6aac81b..14585e6 100644 --- a/usb/iso.c +++ b/usb/transfer.c @@ -18,25 +18,15 @@ #include void print_xfer(struct libusb_transfer *xfer); -void transfer_callback(void *); +void xfer_callback(void *); void callback(struct libusb_transfer *xfer) { - //printf("Callback!\n"); - //print_xfer(xfer); - transfer_callback(xfer->user_data); + xfer_callback(xfer->user_data); } int submit(struct libusb_transfer *xfer) { xfer->callback = &callback; xfer->status = -1; - //print_xfer(xfer); - //printf("Transfer submitted\n"); - - /* fake - strcpy(xfer->buffer, "hello"); - xfer->actual_length = 5; - callback(xfer); - return 0; */ return libusb_submit_transfer(xfer); } diff --git a/usb/transfer.go b/usb/transfer.go index 8e447d3..4018ba2 100644 --- a/usb/transfer.go +++ b/usb/transfer.go @@ -29,6 +29,12 @@ import ( "unsafe" ) +//export xfer_callback +func xfer_callback(cptr unsafe.Pointer) { + ch := *(*chan struct{})(cptr) + close(ch) +} + type usbTransfer struct { xfer *C.struct_libusb_transfer pkts []*C.struct_libusb_packet_descriptor From 1860441a7bd165cec7094a4a3459ecf922df259c Mon Sep 17 00:00:00 2001 From: Sebastian Zagrodzki Date: Fri, 10 Feb 2017 22:12:21 +0100 Subject: [PATCH 10/18] iso.go is no longer needed, the only iso-specific part lives in transfer.c --- usb/iso.go | 46 ---------------------------------------------- 1 file changed, 46 deletions(-) delete mode 100644 usb/iso.go diff --git a/usb/iso.go b/usb/iso.go deleted file mode 100644 index 01a9062..0000000 --- a/usb/iso.go +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2013 Google Inc. All rights reserved. -// Copyright 2016 the gousb Authors. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package usb - -/* -#include -*/ -import "C" - -import ( - "log" - "time" -) - -func isochronous_xfer(e *endpoint, buf []byte, timeout time.Duration) (int, error) { - t, err := e.newUSBTransfer(TRANSFER_TYPE_ISOCHRONOUS, buf) - if err != nil { - return 0, err - } - defer t.free() - - if err := t.submit(timeout); err != nil { - log.Printf("iso: xfer failed to submit: %s", err) - return 0, err - } - - n, err := t.wait(buf) - if err != nil { - log.Printf("iso: xfer failed: %s", err) - return 0, err - } - return n, err -} From 83a4778988e9ec4068afa94f999c2d109239d6f7 Mon Sep 17 00:00:00 2001 From: Sebastian Zagrodzki Date: Fri, 10 Feb 2017 22:32:16 +0100 Subject: [PATCH 11/18] wait() does not need the buffer passed explicitly, it's already stored in t.buf. --- usb/endpoint.go | 2 +- usb/transfer.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/usb/endpoint.go b/usb/endpoint.go index 68fbcdf..52bff35 100644 --- a/usb/endpoint.go +++ b/usb/endpoint.go @@ -74,7 +74,7 @@ func (e *endpoint) transfer(buf []byte, timeout time.Duration) (int, error) { return 0, err } - n, err := t.wait(buf) + n, err := t.wait() if err != nil { log.Printf("bulk: %s failed: %s", tt, err) return 0, err diff --git a/usb/transfer.go b/usb/transfer.go index 4018ba2..0e22080 100644 --- a/usb/transfer.go +++ b/usb/transfer.go @@ -50,7 +50,7 @@ func (t *usbTransfer) submit(timeout time.Duration) error { return nil } -func (t *usbTransfer) wait(b []byte) (n int, err error) { +func (t *usbTransfer) wait() (n int, err error) { select { case <-time.After(10 * time.Second): return 0, fmt.Errorf("wait timed out after 10s") @@ -59,9 +59,9 @@ func (t *usbTransfer) wait(b []byte) (n int, err error) { var status uint8 switch TransferType(t.xfer._type) { case TRANSFER_TYPE_ISOCHRONOUS: - n = int(C.extract_iso_data(t.xfer, unsafe.Pointer(&b[0]), C.int(len(b)), (*C.uchar)(unsafe.Pointer(&status)))) + n = int(C.extract_iso_data(t.xfer, unsafe.Pointer(&t.buf[0]), C.int(len(t.buf)), (*C.uchar)(unsafe.Pointer(&status)))) default: - n = int(C.extract_data(t.xfer, unsafe.Pointer(&b[0]), C.int(len(b)), (*C.uchar)(unsafe.Pointer(&status)))) + n = int(C.extract_data(t.xfer, unsafe.Pointer(&t.buf[0]), C.int(len(t.buf)), (*C.uchar)(unsafe.Pointer(&status)))) } if status != 0 { err = TransferStatus(status) From a47809fda80200bc054fd30011b41fded2c9994c Mon Sep 17 00:00:00 2001 From: Sebastian Zagrodzki Date: Mon, 13 Feb 2017 02:50:47 +0100 Subject: [PATCH 12/18] Simplify the C part even more - the buffer that xfer uses for transferring data is the same as the one that was created in newUSBTransfer. --- usb/transfer.c | 53 ++++++++++++------------------------------------- usb/transfer.go | 14 ++++++------- 2 files changed, 20 insertions(+), 47 deletions(-) diff --git a/usb/transfer.c b/usb/transfer.c index 14585e6..2fa3497 100644 --- a/usb/transfer.c +++ b/usb/transfer.c @@ -54,54 +54,27 @@ void print_xfer(struct libusb_transfer *xfer) { } } -// extract data from a non-isochronous transfer. -int extract_data(struct libusb_transfer *xfer, void *raw, int max, unsigned char *status) { +// compact the data in an isochronous transfer. The contents of individual +// iso packets are shifted left, so that no gaps are left between them. +// Status is set to the first non-zero status of an iso packet. +int compact_iso_data(struct libusb_transfer *xfer, unsigned char *status) { int i; + int sum = 0; unsigned char *in = xfer->buffer; - unsigned char *out = raw; - - int len = xfer->actual_length; - if (len > max) { - len = max; - } - memcpy(out, in, len); - if (xfer->status != 0 && *status == 0) { - *status = xfer->status; - } - return len; -} - -// extract data from an isochronous transfer. Very similar to extract_data, but -// acquires data from xfer->iso_packet_desc array instead of xfer attributes. -int extract_iso_data(struct libusb_transfer *xfer, void *raw, int max, unsigned char *status) { - int i; - int copied = 0; - unsigned char *in = xfer->buffer; - unsigned char *out = raw; + unsigned char *out = xfer->buffer; for (i = 0; i < xfer->num_iso_packets; i++) { struct libusb_iso_packet_descriptor pkt = xfer->iso_packet_desc[i]; - + if (pkt.status != 0) { + *status = pkt.status; + break; + } // Copy the data int len = pkt.actual_length; - if (copied + len > max) { - len = max - copied; - } - memcpy(out, in, len); - copied += len; - + memmove(out, in, len); // Increment offsets + sum += len; in += pkt.length; out += len; - - if (copied == max) { - break; - } - - // Extract first error - if (pkt.status == 0 || *status != 0) { - continue; - } - *status = pkt.status; } - return copied; + return sum; } diff --git a/usb/transfer.go b/usb/transfer.go index 0e22080..f5c9129 100644 --- a/usb/transfer.go +++ b/usb/transfer.go @@ -17,8 +17,7 @@ package usb /* #include -int extract_data(struct libusb_transfer *xfer, void *data, int max, unsigned char *status); -int extract_iso_data(struct libusb_transfer *xfer, void *data, int max, unsigned char *status); +int compact_iso_data(struct libusb_transfer *xfer, unsigned char *status); int submit(struct libusb_transfer *xfer); */ import "C" @@ -56,15 +55,16 @@ func (t *usbTransfer) wait() (n int, err error) { return 0, fmt.Errorf("wait timed out after 10s") case <-t.done: } - var status uint8 + var status TransferStatus switch TransferType(t.xfer._type) { case TRANSFER_TYPE_ISOCHRONOUS: - n = int(C.extract_iso_data(t.xfer, unsafe.Pointer(&t.buf[0]), C.int(len(t.buf)), (*C.uchar)(unsafe.Pointer(&status)))) + n = int(C.compact_iso_data(t.xfer, (*C.uchar)(unsafe.Pointer(&status)))) default: - n = int(C.extract_data(t.xfer, unsafe.Pointer(&t.buf[0]), C.int(len(t.buf)), (*C.uchar)(unsafe.Pointer(&status)))) + n = int(t.xfer.length) + status = TransferStatus(t.xfer.status) } - if status != 0 { - err = TransferStatus(status) + if status != LIBUSB_TRANSFER_COMPLETED { + return 0, status } return n, err } From 4a64c18350e26bbe9855f9863e9cb9ef0e0bd990 Mon Sep 17 00:00:00 2001 From: Sebastian Zagrodzki Date: Mon, 13 Feb 2017 03:01:30 +0100 Subject: [PATCH 13/18] move newUSBTransfer as a vanilla function to transfer.go. Initialize all static params at new. --- usb/transfer.go | 41 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/usb/transfer.go b/usb/transfer.go index f5c9129..1fc74c6 100644 --- a/usb/transfer.go +++ b/usb/transfer.go @@ -41,8 +41,15 @@ type usbTransfer struct { buf []byte } -func (t *usbTransfer) submit(timeout time.Duration) error { - t.xfer.timeout = C.uint(timeout / time.Millisecond) +type deviceHandle *C.libusb_device_handle + +func (t *usbTransfer) attach(dev deviceHandle) { + t.xfer.dev_handle = dev +} + +func (t *usbTransfer) submit() error { + done := make(chan struct{}, 1) + t.xfer.user_data = (unsafe.Pointer)(&done) if errno := C.submit(t.xfer); errno < 0 { return usbError(errno) } @@ -73,3 +80,33 @@ func (t *usbTransfer) free() error { C.libusb_free_transfer(t.xfer) return nil } + +func newUSBTransfer(ei EndpointInfo, buf []byte, timeout time.Duration) (*usbTransfer, error) { + var isoPackets int + tt := ei.TransferType() + if tt == TRANSFER_TYPE_ISOCHRONOUS { + isoPackets = len(buf) / int(ei.MaxIsoPacket) + } + + xfer := C.libusb_alloc_transfer(C.int(isoPackets)) + if xfer == nil { + return nil, fmt.Errorf("libusb_alloc_transfer(%d) failed", isoPackets) + } + + xfer.timeout = C.uint(timeout / time.Millisecond) + xfer.endpoint = C.uchar(ei.Address) + xfer._type = C.uchar(tt) + + xfer.buffer = (*C.uchar)((unsafe.Pointer)(&buf[0])) + xfer.length = C.int(len(buf)) + + if tt == TRANSFER_TYPE_ISOCHRONOUS { + xfer.num_iso_packets = C.int(isoPackets) + C.libusb_set_iso_packet_lengths(xfer, C.uint(ei.MaxIsoPacket)) + } + + return &usbTransfer{ + xfer: xfer, + buf: buf, + }, nil +} From ea1db2d56a7883dde7b1890552e851e8a7dfe2ba Mon Sep 17 00:00:00 2001 From: Sebastian Zagrodzki Date: Mon, 13 Feb 2017 03:02:32 +0100 Subject: [PATCH 14/18] move to updated transfer init protocol --- usb/endpoint.go | 38 ++------------------------------------ 1 file changed, 2 insertions(+), 36 deletions(-) diff --git a/usb/endpoint.go b/usb/endpoint.go index 52bff35..6247ffa 100644 --- a/usb/endpoint.go +++ b/usb/endpoint.go @@ -22,7 +22,6 @@ import ( "fmt" "log" "time" - "unsafe" ) type Endpoint interface { @@ -63,13 +62,13 @@ func (e *endpoint) transfer(buf []byte, timeout time.Duration) (int, error) { } tt := e.TransferType() - t, err := e.newUSBTransfer(tt, buf) + t, err := newUSBTransfer(e.EndpointInfo, buf, timeout) if err != nil { return 0, err } defer t.free() - if err := t.submit(timeout); err != nil { + if err := t.submit(); err != nil { log.Printf("bulk: %s failed to submit: %s", tt, err) return 0, err } @@ -81,36 +80,3 @@ func (e *endpoint) transfer(buf []byte, timeout time.Duration) (int, error) { } return n, err } - -func (e *endpoint) newUSBTransfer(tt TransferType, buf []byte) (*usbTransfer, error) { - var isoPackets int - if tt == TRANSFER_TYPE_ISOCHRONOUS { - isoPackets = len(buf) / int(e.EndpointInfo.MaxIsoPacket) - } - - xfer := C.libusb_alloc_transfer(C.int(isoPackets)) - if xfer == nil { - return nil, fmt.Errorf("libusb_alloc_transfer(%d) failed", isoPackets) - } - - done := make(chan struct{}, 1) - xfer.user_data = (unsafe.Pointer)(&done) - - xfer.dev_handle = e.Device.handle - xfer.endpoint = C.uchar(e.Address) - xfer._type = C.uchar(tt) - - xfer.buffer = (*C.uchar)((unsafe.Pointer)(&buf[0])) - xfer.length = C.int(len(buf)) - - if tt == TRANSFER_TYPE_ISOCHRONOUS { - xfer.num_iso_packets = C.int(isoPackets) - C.libusb_set_iso_packet_lengths(xfer, C.uint(e.EndpointInfo.MaxIsoPacket)) - } - - return &usbTransfer{ - xfer: xfer, - done: done, - buf: buf, - }, nil -} From 931a15849b00a5d96d14ea4c9f9053fce02437ae Mon Sep 17 00:00:00 2001 From: Sebastian Zagrodzki Date: Mon, 13 Feb 2017 03:14:38 +0100 Subject: [PATCH 15/18] set the same channel in t.xfer.user_data as in t.done. --- usb/transfer.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/usb/transfer.go b/usb/transfer.go index 1fc74c6..bbb0792 100644 --- a/usb/transfer.go +++ b/usb/transfer.go @@ -36,9 +36,8 @@ func xfer_callback(cptr unsafe.Pointer) { type usbTransfer struct { xfer *C.struct_libusb_transfer - pkts []*C.struct_libusb_packet_descriptor - done chan struct{} buf []byte + done chan struct{} } type deviceHandle *C.libusb_device_handle @@ -48,8 +47,8 @@ func (t *usbTransfer) attach(dev deviceHandle) { } func (t *usbTransfer) submit() error { - done := make(chan struct{}, 1) - t.xfer.user_data = (unsafe.Pointer)(&done) + t.done = make(chan struct{}) + t.xfer.user_data = (unsafe.Pointer)(&t.done) if errno := C.submit(t.xfer); errno < 0 { return usbError(errno) } From e9d2ce49f0e8c2b0e2a58e206c48dc941bfa7763 Mon Sep 17 00:00:00 2001 From: Sebastian Zagrodzki Date: Mon, 13 Feb 2017 03:17:29 +0100 Subject: [PATCH 16/18] Add missing attach. --- usb/endpoint.go | 1 + 1 file changed, 1 insertion(+) diff --git a/usb/endpoint.go b/usb/endpoint.go index 6247ffa..9ae4005 100644 --- a/usb/endpoint.go +++ b/usb/endpoint.go @@ -66,6 +66,7 @@ func (e *endpoint) transfer(buf []byte, timeout time.Duration) (int, error) { if err != nil { return 0, err } + t.attach(e.Device.handle) defer t.free() if err := t.submit(); err != nil { From 0ba09a9942105b3830f28b6d59d50e47e70ac482 Mon Sep 17 00:00:00 2001 From: Sebastian Zagrodzki Date: Tue, 14 Feb 2017 01:24:20 +0100 Subject: [PATCH 17/18] Move device handle to newUSBTransfer args, remove separate attach() --- usb/endpoint.go | 3 +-- usb/transfer.go | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/usb/endpoint.go b/usb/endpoint.go index 095158d..fd7bb2d 100644 --- a/usb/endpoint.go +++ b/usb/endpoint.go @@ -62,11 +62,10 @@ func (e *endpoint) transfer(buf []byte, timeout time.Duration) (int, error) { } tt := e.TransferType() - t, err := newUSBTransfer(e.EndpointInfo, buf, timeout) + t, err := newUSBTransfer(e.Device.handle, e.EndpointInfo, buf, timeout) if err != nil { return 0, err } - t.attach(e.Device.handle) defer t.free() if err := t.submit(); err != nil { diff --git a/usb/transfer.go b/usb/transfer.go index 104c193..7d81a92 100644 --- a/usb/transfer.go +++ b/usb/transfer.go @@ -35,17 +35,16 @@ func xfer_callback(cptr unsafe.Pointer) { } type usbTransfer struct { + // xfer is the allocated libusb_transfer. xfer *C.struct_libusb_transfer - buf []byte + // buf is the buffer allocated for the transfer. Both buf and xfer.buffer + // point to the same piece of memory. + buf []byte + // done is blocking until the transfer is complete and data and transfer + // status are available. done chan struct{} } -type deviceHandle *C.libusb_device_handle - -func (t *usbTransfer) attach(dev deviceHandle) { - t.xfer.dev_handle = dev -} - func (t *usbTransfer) submit() error { t.done = make(chan struct{}) t.xfer.user_data = (unsafe.Pointer)(&t.done) @@ -80,7 +79,9 @@ func (t *usbTransfer) free() error { return nil } -func newUSBTransfer(ei EndpointInfo, buf []byte, timeout time.Duration) (*usbTransfer, error) { +type deviceHandle *C.libusb_device_handle + +func newUSBTransfer(dev deviceHandle, ei EndpointInfo, buf []byte, timeout time.Duration) (*usbTransfer, error) { var isoPackets int tt := ei.TransferType() if tt == TRANSFER_TYPE_ISOCHRONOUS { @@ -92,6 +93,7 @@ func newUSBTransfer(ei EndpointInfo, buf []byte, timeout time.Duration) (*usbTra return nil, fmt.Errorf("libusb_alloc_transfer(%d) failed", isoPackets) } + xfer.dev_handle = dev xfer.timeout = C.uint(timeout / time.Millisecond) xfer.endpoint = C.uchar(ei.Address) xfer._type = C.uchar(tt) From ef932806ed865feaeaf8d5a9534eccca029abdca Mon Sep 17 00:00:00 2001 From: Sebastian Zagrodzki Date: Wed, 15 Feb 2017 18:20:32 +0100 Subject: [PATCH 18/18] more comments about transfer protocol. --- usb/transfer.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/usb/transfer.go b/usb/transfer.go index 7d81a92..ee3b830 100644 --- a/usb/transfer.go +++ b/usb/transfer.go @@ -45,6 +45,9 @@ type usbTransfer struct { done chan struct{} } +// submits the transfer. After submit() the transfer is in flight and is owned by libusb. +// It's not safe to access the contents of the transfer until wait() returns. +// Once wait() returns, it's ok to re-use the same transfer structure by calling submit() again. func (t *usbTransfer) submit() error { t.done = make(chan struct{}) t.xfer.user_data = (unsafe.Pointer)(&t.done) @@ -54,6 +57,11 @@ func (t *usbTransfer) submit() error { return nil } +// wait waits for libusb to signal the release of transfer data. +// After wait returns, the transfer contents are safe to access +// via t.buf. The number returned by wait indicates how many bytes +// of the buffer were read or written by libusb, and it can be +// smaller than the length of t.buf. func (t *usbTransfer) wait() (n int, err error) { select { case <-time.After(10 * time.Second): @@ -74,13 +82,21 @@ func (t *usbTransfer) wait() (n int, err error) { return n, err } +// free releases the memory allocated for the transfer. +// free should be called only if the transfer is not used by libusb, +// i.e. it should not be called after submit() and before wait() returns. func (t *usbTransfer) free() error { C.libusb_free_transfer(t.xfer) + t.xfer = nil + t.buf = nil + t.done = nil return nil } type deviceHandle *C.libusb_device_handle +// newUSBTransfer allocates a new transfer structure for communication with a +// given device/endpoint, with buf as the underlying transfer buffer. func newUSBTransfer(dev deviceHandle, ei EndpointInfo, buf []byte, timeout time.Duration) (*usbTransfer, error) { var isoPackets int tt := ei.TransferType()