1 /* 2 * libusb example program to measure Atmel SAM3U isochronous performance 3 * Copyright (C) 2012 Harald Welte <laforge (at) gnumonks.org> 4 * 5 * Copied with the author's permission under LGPL-2.1 from 6 * http://git.gnumonks.org/cgi-bin/gitweb.cgi?p=sam3u-tests.git;a=blob;f=usb-benchmark-project/host/benchmark.c;h=74959f7ee88f1597286cd435f312a8ff52c56b7e 7 * 8 * An Atmel SAM3U test firmware is also available in the above repository. 9 * 10 * This library is free software; you can redistribute it and/or 11 * modify it under the terms of the GNU Lesser General Public 12 * License as published by the Free Software Foundation; either 13 * version 2.1 of the License, or (at your option) any later version. 14 * 15 * This library is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 * Lesser General Public License for more details. 19 * 20 * You should have received a copy of the GNU Lesser General Public 21 * License along with this library; if not, write to the Free Software 22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 23 */ 24 25 #include <unistd.h> 26 #include <stdlib.h> 27 #include <stdio.h> 28 #include <errno.h> 29 #include <signal.h> 30 31 #include <libusb.h> 32 33 34 #define EP_DATA_IN 0x82 35 #define EP_ISO_IN 0x86 36 37 static int do_exit = 0; 38 static struct libusb_device_handle *devh = NULL; 39 40 static unsigned long num_bytes = 0, num_xfer = 0; 41 static struct timeval tv_start; 42 43 static void LIBUSB_CALL cb_xfr(struct libusb_transfer *xfr) 44 { 45 int i; 46 47 if (xfr->status != LIBUSB_TRANSFER_COMPLETED) { 48 fprintf(stderr, "transfer status %d\n", xfr->status); 49 libusb_free_transfer(xfr); 50 exit(3); 51 } 52 53 if (xfr->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS) { 54 for (i = 0; i < xfr->num_iso_packets; i++) { 55 struct libusb_iso_packet_descriptor *pack = &xfr->iso_packet_desc[i]; 56 57 if (pack->status != LIBUSB_TRANSFER_COMPLETED) { 58 fprintf(stderr, "Error: pack %u status %d\n", i, pack->status); 59 exit(5); 60 } 61 62 printf("pack%u length:%u, actual_length:%u\n", i, pack->length, pack->actual_length); 63 } 64 } 65 66 printf("length:%u, actual_length:%u\n", xfr->length, xfr->actual_length); 67 for (i = 0; i < xfr->actual_length; i++) { 68 printf("%02x", xfr->buffer[i]); 69 if (i % 16) 70 printf("\n"); 71 else if (i % 8) 72 printf(" "); 73 else 74 printf(" "); 75 } 76 num_bytes += xfr->actual_length; 77 num_xfer++; 78 79 if (libusb_submit_transfer(xfr) < 0) { 80 fprintf(stderr, "error re-submitting URB\n"); 81 exit(1); 82 } 83 } 84 85 static int benchmark_in(uint8_t ep) 86 { 87 static uint8_t buf[2048]; 88 static struct libusb_transfer *xfr; 89 int num_iso_pack = 0; 90 91 if (ep == EP_ISO_IN) 92 num_iso_pack = 16; 93 94 xfr = libusb_alloc_transfer(num_iso_pack); 95 if (!xfr) 96 return -ENOMEM; 97 98 if (ep == EP_ISO_IN) { 99 libusb_fill_iso_transfer(xfr, devh, ep, buf, 100 sizeof(buf), num_iso_pack, cb_xfr, NULL, 0); 101 libusb_set_iso_packet_lengths(xfr, sizeof(buf)/num_iso_pack); 102 } else 103 libusb_fill_bulk_transfer(xfr, devh, ep, buf, 104 sizeof(buf), cb_xfr, NULL, 0); 105 106 gettimeofday(&tv_start, NULL); 107 108 /* NOTE: To reach maximum possible performance the program must 109 * submit *multiple* transfers here, not just one. 110 * 111 * When only one transfer is submitted there is a gap in the bus 112 * schedule from when the transfer completes until a new transfer 113 * is submitted by the callback. This causes some jitter for 114 * isochronous transfers and loss of throughput for bulk transfers. 115 * 116 * This is avoided by queueing multiple transfers in advance, so 117 * that the host controller is always kept busy, and will schedule 118 * more transfers on the bus while the callback is running for 119 * transfers which have completed on the bus. 120 */ 121 122 return libusb_submit_transfer(xfr); 123 } 124 125 static void measure(void) 126 { 127 struct timeval tv_stop; 128 unsigned int diff_msec; 129 130 gettimeofday(&tv_stop, NULL); 131 132 diff_msec = (tv_stop.tv_sec - tv_start.tv_sec)*1000; 133 diff_msec += (tv_stop.tv_usec - tv_start.tv_usec)/1000; 134 135 printf("%lu transfers (total %lu bytes) in %u miliseconds => %lu bytes/sec\n", 136 num_xfer, num_bytes, diff_msec, (num_bytes*1000)/diff_msec); 137 } 138 139 static void sig_hdlr(int signum) 140 { 141 switch (signum) { 142 case SIGINT: 143 measure(); 144 do_exit = 1; 145 break; 146 } 147 } 148 149 int main(int argc, char **argv) 150 { 151 int rc; 152 struct sigaction sigact; 153 154 sigact.sa_handler = sig_hdlr; 155 sigemptyset(&sigact.sa_mask); 156 sigact.sa_flags = 0; 157 sigaction(SIGINT, &sigact, NULL); 158 159 rc = libusb_init(NULL); 160 if (rc < 0) { 161 fprintf(stderr, "Error initializing libusb: %s\n", libusb_error_name(rc)); 162 exit(1); 163 } 164 165 devh = libusb_open_device_with_vid_pid(NULL, 0x16c0, 0x0763); 166 if (!devh) { 167 fprintf(stderr, "Error finding USB device\n"); 168 goto out; 169 } 170 171 rc = libusb_claim_interface(devh, 2); 172 if (rc < 0) { 173 fprintf(stderr, "Error claiming interface: %s\n", libusb_error_name(rc)); 174 goto out; 175 } 176 177 benchmark_in(EP_ISO_IN); 178 179 while (!do_exit) { 180 rc = libusb_handle_events(NULL); 181 if (rc != LIBUSB_SUCCESS) 182 break; 183 } 184 185 /* Measurement has already been done by the signal handler. */ 186 187 libusb_release_interface(devh, 0); 188 out: 189 if (devh) 190 libusb_close(devh); 191 libusb_exit(NULL); 192 return rc; 193 } 194