diff --git a/rawread/main.go b/rawread/main.go index 8a2efac..c389c4f 100644 --- a/rawread/main.go +++ b/rawread/main.go @@ -20,20 +20,58 @@ import ( "flag" "fmt" "log" + "os" + "strconv" + "strings" "github.com/kylelemons/gousb/usb" "github.com/kylelemons/gousb/usbid" ) var ( - device = flag.String("device", "vend:prod", "Device to which to connect") - config = flag.Int("config", 1, "Endpoint to which to connect") - iface = flag.Int("interface", 0, "Endpoint to which to connect") - setup = flag.Int("setup", 0, "Endpoint to which to connect") - endpoint = flag.Int("endpoint", 1, "Endpoint to which to connect") + 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") + endpoint = flag.Uint("endpoint", 1, "Endpoint to which to connect") debug = flag.Int("debug", 3, "Debug level for libusb") + size = flag.Uint("read_size", 1024, "Number of bytes of data to read in a single transaction.") + num = flag.Uint("read_num", 0, "Number of read transactions to perform. 0 means infinite.") ) +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() @@ -43,40 +81,41 @@ func main() { ctx.Debug(*debug) - log.Printf("Scanning for device %q...", *device) + var devName string + var vid, pid usb.ID + var bus, addr uint8 + switch { + 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 != "": + var err error + 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: + var err error + 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 { - if fmt.Sprintf("%s:%s", desc.Vendor, desc.Product) != *device { - return false + switch { + case vid == desc.Vendor && pid == desc.Product: + return true + case bus == desc.Bus && addr == desc.Address: + return true } - - // The usbid package can be used to print out human readable information. - fmt.Printf(" Protocol: %s\n", usbid.Classify(desc)) - - // The configurations can be examined from the Descriptor, though they can only - // be set once the device is opened. All configuration references must be closed, - // to free up the memory in libusb. - for _, cfg := range desc.Configs { - // This loop just uses more of the built-in and usbid pretty printing to list - // the USB devices. - fmt.Printf(" %s:\n", cfg) - for _, alt := range cfg.Interfaces { - fmt.Printf(" --------------\n") - for _, iface := range alt.Setups { - fmt.Printf(" %s\n", iface) - fmt.Printf(" %s\n", usbid.Classify(iface)) - for _, end := range iface.Endpoints { - fmt.Printf(" %s\n", end) - } - } - } - fmt.Printf(" --------------\n") - } - - return true + return false }) - // All Devices returned from ListDevices must be closed. defer func() { for _, d := range devs { @@ -84,22 +123,58 @@ func main() { } }() - // ListDevices can occaionally fail, so be sure to check its return value. + // ListDevices can occasionally fail, so be sure to check its return value. if err != nil { - log.Fatalf("list: %s", err) + log.Printf("Warning: ListDevices: %s.", err) } - - if len(devs) == 0 { - log.Fatalf("no devices found") + switch { + case len(devs) == 0: + log.Fatal("No matching devices found.") + case len(devs) > 1: + log.Printf("Warning: multiple devices found. Using bus %d, addr %d.", devs[0].Bus, devs[0].Address) + for _, d := range devs[1:] { + d.Close() + } + devs = devs[:1] } - dev := devs[0] - log.Printf("Connecting to endpoint...") - log.Printf("- %#v", dev.Descriptor) + // The usbid package can be used to print out human readable information. + log.Printf(" Protocol: %s\n", usbid.Classify(dev.Descriptor)) + + // The configurations can be examined from the Descriptor, though they can only + // be set once the device is opened. All configuration references must be closed, + // to free up the memory in libusb. + for _, cfg := range dev.Configs { + // This loop just uses more of the built-in and usbid pretty printing to list + // the USB devices. + log.Printf(" %s:\n", cfg) + for _, alt := range cfg.Interfaces { + log.Printf(" --------------\n") + for _, iface := range alt.Setups { + log.Printf(" %s\n", iface) + log.Printf(" %s\n", usbid.Classify(iface)) + for _, end := range iface.Endpoints { + log.Printf(" %s\n", end) + } + } + } + log.Printf(" --------------\n") + } + + log.Printf("Connecting to endpoint %d...", *endpoint) ep, err := dev.OpenEndpoint(uint8(*config), uint8(*iface), uint8(*setup), uint8(*endpoint)|uint8(usb.ENDPOINT_DIR_IN)) if err != nil { log.Fatalf("open: %s", err) } - _ = ep + log.Print("Reading...") + + buf := make([]byte, *size) + for i := uint(0); *num == 0 || i < *num; i++ { + num, err := ep.Read(buf) + if err != nil { + log.Fatalf("Reading from device failed: %v", err) + } + os.Stdout.Write(buf[:num]) + } } diff --git a/usb/constants.go b/usb/constants.go index b4dc590..88a28b7 100644 --- a/usb/constants.go +++ b/usb/constants.go @@ -87,7 +87,7 @@ func (dt DescriptorType) String() string { type EndpointDirection uint8 const ( - ENDPOINT_NUM_MASK = 0x03 + ENDPOINT_NUM_MASK = 0x0f ENDPOINT_DIR_IN EndpointDirection = C.LIBUSB_ENDPOINT_IN ENDPOINT_DIR_OUT EndpointDirection = C.LIBUSB_ENDPOINT_OUT ENDPOINT_DIR_MASK EndpointDirection = 0x80 diff --git a/usb/iso.c b/usb/iso.c index 1204eb7..7cd4dd0 100644 --- a/usb/iso.c +++ b/usb/iso.c @@ -74,8 +74,8 @@ int extract_data(struct libusb_transfer *xfer, void *raw, int max, unsigned char // Copy the data int len = pkt.actual_length; - if (len > max) { - len = max; + if (copied + len > max) { + len = max - copied; } memcpy(out, in, len); copied += len; @@ -84,10 +84,14 @@ int extract_data(struct libusb_transfer *xfer, void *raw, int max, unsigned char in += pkt.length; out += len; + if (copied == max) { + break; + } + // Extract first error if (pkt.status == 0 || *status != 0) { continue; - } + } *status = pkt.status; } return copied; diff --git a/usb/iso.go b/usb/iso.go index 517a126..20a6da6 100644 --- a/usb/iso.go +++ b/usb/iso.go @@ -37,18 +37,21 @@ func iso_callback(cptr unsafe.Pointer) { close(ch) } -func (end *endpoint) allocTransfer() *Transfer { - const ( - iso_packets = 8 // 128 // 242 - ) - - xfer := C.libusb_alloc_transfer(C.int(iso_packets)) +func (end *endpoint) allocTransfer(maxLen int) *Transfer { + isoPacketSize := end.EndpointInfo.MaxIsoPacket + // the larget the input buffer, the more packets we use in a single + // transfer. + numIsoPackets := maxLen / int(isoPacketSize) + if numIsoPackets*int(isoPacketSize) < maxLen { + numIsoPackets++ + } + xfer := C.libusb_alloc_transfer(C.int(numIsoPackets)) if xfer == nil { log.Printf("usb: transfer allocation failed?!") return nil } - buf := make([]byte, iso_packets*end.EndpointInfo.MaxIsoPacket) + buf := make([]byte, numIsoPackets*int(end.EndpointInfo.MaxIsoPacket)) done := make(chan struct{}, 1) xfer.dev_handle = end.Device.handle @@ -57,7 +60,7 @@ func (end *endpoint) allocTransfer() *Transfer { xfer.buffer = (*C.uchar)((unsafe.Pointer)(&buf[0])) xfer.length = C.int(len(buf)) - xfer.num_iso_packets = iso_packets + xfer.num_iso_packets = C.int(numIsoPackets) C.libusb_set_iso_packet_lengths(xfer, C.uint(end.EndpointInfo.MaxIsoPacket)) /* @@ -130,7 +133,7 @@ func (t *Transfer) Close() error { } func isochronous_xfer(e *endpoint, buf []byte, timeout time.Duration) (int, error) { - t := e.allocTransfer() + t := e.allocTransfer(len(buf)) defer t.Close() if err := t.Submit(timeout); err != nil {