diff --git a/rawread/main.go b/rawread/main.go index 8a2efac..4e56e1e 100644 --- a/rawread/main.go +++ b/rawread/main.go @@ -20,18 +20,26 @@ import ( "flag" "fmt" "log" + "os" + "sync/atomic" + "time" "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") + 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.") + 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.") + 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 main() { @@ -43,40 +51,29 @@ func main() { ctx.Debug(*debug) - log.Printf("Scanning for device %q...", *device) + var devName string + 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) + default: + 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 usb.ID(*vid) == desc.Vendor && usb.ID(*pid) == desc.Product: + return true + case uint8(*bus) == desc.Bus && uint8(*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 +81,80 @@ 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.Print("Connecting to 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...") + + var total uint64 + if *bench { + go func() { + var last uint64 + var before = time.Now() + for { + time.Sleep(4 * time.Second) + cur := atomic.LoadUint64(&total) + now := time.Now() + dur := now.Sub(before) + log.Printf("%.2f B/s\n", float64(cur-last)/(float64(dur)/float64(time.Second))) + before = now + last = cur + } + }() + } + + for { + buf := make([]byte, *size) + num, err := ep.Read(buf) + if err != nil { + log.Fatalf("Reading from device failed: %v", err) + } + if !*bench { + log.Printf("Read %d bytes of data", num) + os.Stdout.Write(buf[:num]) + return + } + atomic.AddUint64(&total, uint64(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;