174ba9207SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
25bc9661cSMichal Nazarewicz /* $(CROSS_COMPILE)cc -Wall -Wextra -g -lpthread -o testusb testusb.c */
32201d6b1SDavid Brownell
42201d6b1SDavid Brownell /*
52201d6b1SDavid Brownell * Copyright (c) 2002 by David Brownell
65bc9661cSMichal Nazarewicz * Copyright (c) 2010 by Samsung Electronics
754b8360fSMichal Nazarewicz * Author: Michal Nazarewicz <mina86@mina86.com>
82201d6b1SDavid Brownell */
92201d6b1SDavid Brownell
105bc9661cSMichal Nazarewicz /*
115bc9661cSMichal Nazarewicz * This program issues ioctls to perform the tests implemented by the
125bc9661cSMichal Nazarewicz * kernel driver. It can generate a variety of transfer patterns; you
135bc9661cSMichal Nazarewicz * should make sure to test both regular streaming and mixes of
145bc9661cSMichal Nazarewicz * transfer sizes (including short transfers).
155bc9661cSMichal Nazarewicz *
165bc9661cSMichal Nazarewicz * For more information on how this can be used and on USB testing
175bc9661cSMichal Nazarewicz * refer to <URL:http://www.linux-usb.org/usbtest/>.
185bc9661cSMichal Nazarewicz */
195bc9661cSMichal Nazarewicz
202201d6b1SDavid Brownell #include <stdio.h>
212201d6b1SDavid Brownell #include <string.h>
222201d6b1SDavid Brownell #include <ftw.h>
232201d6b1SDavid Brownell #include <stdlib.h>
242201d6b1SDavid Brownell #include <pthread.h>
252201d6b1SDavid Brownell #include <unistd.h>
262201d6b1SDavid Brownell #include <errno.h>
275bc9661cSMichal Nazarewicz #include <limits.h>
282201d6b1SDavid Brownell
292201d6b1SDavid Brownell #include <sys/types.h>
302201d6b1SDavid Brownell #include <sys/stat.h>
312201d6b1SDavid Brownell #include <fcntl.h>
322201d6b1SDavid Brownell
332201d6b1SDavid Brownell #include <sys/ioctl.h>
342201d6b1SDavid Brownell #include <linux/usbdevice_fs.h>
352201d6b1SDavid Brownell
362201d6b1SDavid Brownell /*-------------------------------------------------------------------------*/
372201d6b1SDavid Brownell
382201d6b1SDavid Brownell #define TEST_CASES 30
392201d6b1SDavid Brownell
402201d6b1SDavid Brownell // FIXME make these public somewhere; usbdevfs.h?
412201d6b1SDavid Brownell
422201d6b1SDavid Brownell struct usbtest_param {
432201d6b1SDavid Brownell // inputs
442201d6b1SDavid Brownell unsigned test_num; /* 0..(TEST_CASES-1) */
452201d6b1SDavid Brownell unsigned iterations;
462201d6b1SDavid Brownell unsigned length;
472201d6b1SDavid Brownell unsigned vary;
482201d6b1SDavid Brownell unsigned sglen;
492201d6b1SDavid Brownell
502201d6b1SDavid Brownell // outputs
512201d6b1SDavid Brownell struct timeval duration;
522201d6b1SDavid Brownell };
532201d6b1SDavid Brownell #define USBTEST_REQUEST _IOWR('U', 100, struct usbtest_param)
542201d6b1SDavid Brownell
552201d6b1SDavid Brownell /*-------------------------------------------------------------------------*/
562201d6b1SDavid Brownell
572201d6b1SDavid Brownell /* #include <linux/usb_ch9.h> */
582201d6b1SDavid Brownell
595bc9661cSMichal Nazarewicz #define USB_DT_DEVICE 0x01
605bc9661cSMichal Nazarewicz #define USB_DT_INTERFACE 0x04
615bc9661cSMichal Nazarewicz
625bc9661cSMichal Nazarewicz #define USB_CLASS_PER_INTERFACE 0 /* for DeviceClass */
635bc9661cSMichal Nazarewicz #define USB_CLASS_VENDOR_SPEC 0xff
645bc9661cSMichal Nazarewicz
655bc9661cSMichal Nazarewicz
662201d6b1SDavid Brownell struct usb_device_descriptor {
672201d6b1SDavid Brownell __u8 bLength;
682201d6b1SDavid Brownell __u8 bDescriptorType;
692201d6b1SDavid Brownell __u16 bcdUSB;
702201d6b1SDavid Brownell __u8 bDeviceClass;
712201d6b1SDavid Brownell __u8 bDeviceSubClass;
722201d6b1SDavid Brownell __u8 bDeviceProtocol;
732201d6b1SDavid Brownell __u8 bMaxPacketSize0;
742201d6b1SDavid Brownell __u16 idVendor;
752201d6b1SDavid Brownell __u16 idProduct;
762201d6b1SDavid Brownell __u16 bcdDevice;
772201d6b1SDavid Brownell __u8 iManufacturer;
782201d6b1SDavid Brownell __u8 iProduct;
792201d6b1SDavid Brownell __u8 iSerialNumber;
802201d6b1SDavid Brownell __u8 bNumConfigurations;
812201d6b1SDavid Brownell } __attribute__ ((packed));
822201d6b1SDavid Brownell
835bc9661cSMichal Nazarewicz struct usb_interface_descriptor {
845bc9661cSMichal Nazarewicz __u8 bLength;
855bc9661cSMichal Nazarewicz __u8 bDescriptorType;
865bc9661cSMichal Nazarewicz
875bc9661cSMichal Nazarewicz __u8 bInterfaceNumber;
885bc9661cSMichal Nazarewicz __u8 bAlternateSetting;
895bc9661cSMichal Nazarewicz __u8 bNumEndpoints;
905bc9661cSMichal Nazarewicz __u8 bInterfaceClass;
915bc9661cSMichal Nazarewicz __u8 bInterfaceSubClass;
925bc9661cSMichal Nazarewicz __u8 bInterfaceProtocol;
935bc9661cSMichal Nazarewicz __u8 iInterface;
945bc9661cSMichal Nazarewicz } __attribute__ ((packed));
955bc9661cSMichal Nazarewicz
962201d6b1SDavid Brownell enum usb_device_speed {
972201d6b1SDavid Brownell USB_SPEED_UNKNOWN = 0, /* enumerating */
982201d6b1SDavid Brownell USB_SPEED_LOW, USB_SPEED_FULL, /* usb 1.1 */
99b067fc28SBryan O'Donoghue USB_SPEED_HIGH, /* usb 2.0 */
100b067fc28SBryan O'Donoghue USB_SPEED_WIRELESS, /* wireless (usb 2.5) */
1017fbcd99eSBryan O'Donoghue USB_SPEED_SUPER, /* usb 3.0 */
102*5ea5746dSBryan O'Donoghue USB_SPEED_SUPER_PLUS, /* usb 3.1 */
1032201d6b1SDavid Brownell };
1042201d6b1SDavid Brownell
1052201d6b1SDavid Brownell /*-------------------------------------------------------------------------*/
1062201d6b1SDavid Brownell
speed(enum usb_device_speed s)1072201d6b1SDavid Brownell static char *speed (enum usb_device_speed s)
1082201d6b1SDavid Brownell {
1092201d6b1SDavid Brownell switch (s) {
1102201d6b1SDavid Brownell case USB_SPEED_UNKNOWN: return "unknown";
1112201d6b1SDavid Brownell case USB_SPEED_LOW: return "low";
1122201d6b1SDavid Brownell case USB_SPEED_FULL: return "full";
1132201d6b1SDavid Brownell case USB_SPEED_HIGH: return "high";
114b067fc28SBryan O'Donoghue case USB_SPEED_WIRELESS: return "wireless";
1157fbcd99eSBryan O'Donoghue case USB_SPEED_SUPER: return "super";
116*5ea5746dSBryan O'Donoghue case USB_SPEED_SUPER_PLUS: return "super-plus";
1172201d6b1SDavid Brownell default: return "??";
1182201d6b1SDavid Brownell }
1192201d6b1SDavid Brownell }
1202201d6b1SDavid Brownell
1212201d6b1SDavid Brownell struct testdev {
1222201d6b1SDavid Brownell struct testdev *next;
1232201d6b1SDavid Brownell char *name;
1242201d6b1SDavid Brownell pthread_t thread;
1252201d6b1SDavid Brownell enum usb_device_speed speed;
1262201d6b1SDavid Brownell unsigned ifnum : 8;
1272201d6b1SDavid Brownell unsigned forever : 1;
1282201d6b1SDavid Brownell int test;
1292201d6b1SDavid Brownell
1302201d6b1SDavid Brownell struct usbtest_param param;
1312201d6b1SDavid Brownell };
1322201d6b1SDavid Brownell static struct testdev *testdevs;
1332201d6b1SDavid Brownell
testdev_ffs_ifnum(FILE * fd)1345bc9661cSMichal Nazarewicz static int testdev_ffs_ifnum(FILE *fd)
1352201d6b1SDavid Brownell {
1365bc9661cSMichal Nazarewicz union {
1375bc9661cSMichal Nazarewicz char buf[255];
1385bc9661cSMichal Nazarewicz struct usb_interface_descriptor intf;
1395bc9661cSMichal Nazarewicz } u;
1405bc9661cSMichal Nazarewicz
1415bc9661cSMichal Nazarewicz for (;;) {
1425bc9661cSMichal Nazarewicz if (fread(u.buf, 1, 1, fd) != 1)
1435bc9661cSMichal Nazarewicz return -1;
1445bc9661cSMichal Nazarewicz if (fread(u.buf + 1, (unsigned char)u.buf[0] - 1, 1, fd) != 1)
1455bc9661cSMichal Nazarewicz return -1;
1465bc9661cSMichal Nazarewicz
1475bc9661cSMichal Nazarewicz if (u.intf.bLength == sizeof u.intf
1485bc9661cSMichal Nazarewicz && u.intf.bDescriptorType == USB_DT_INTERFACE
1495bc9661cSMichal Nazarewicz && u.intf.bNumEndpoints == 2
1505bc9661cSMichal Nazarewicz && u.intf.bInterfaceClass == USB_CLASS_VENDOR_SPEC
1515bc9661cSMichal Nazarewicz && u.intf.bInterfaceSubClass == 0
1525bc9661cSMichal Nazarewicz && u.intf.bInterfaceProtocol == 0)
1535bc9661cSMichal Nazarewicz return (unsigned char)u.intf.bInterfaceNumber;
1545bc9661cSMichal Nazarewicz }
1555bc9661cSMichal Nazarewicz }
1565bc9661cSMichal Nazarewicz
testdev_ifnum(FILE * fd)1575bc9661cSMichal Nazarewicz static int testdev_ifnum(FILE *fd)
1585bc9661cSMichal Nazarewicz {
1595bc9661cSMichal Nazarewicz struct usb_device_descriptor dev;
1605bc9661cSMichal Nazarewicz
1615bc9661cSMichal Nazarewicz if (fread(&dev, sizeof dev, 1, fd) != 1)
1625bc9661cSMichal Nazarewicz return -1;
1635bc9661cSMichal Nazarewicz
1645bc9661cSMichal Nazarewicz if (dev.bLength != sizeof dev || dev.bDescriptorType != USB_DT_DEVICE)
1655bc9661cSMichal Nazarewicz return -1;
1665bc9661cSMichal Nazarewicz
1672201d6b1SDavid Brownell /* FX2 with (tweaked) bulksrc firmware */
1685bc9661cSMichal Nazarewicz if (dev.idVendor == 0x0547 && dev.idProduct == 0x1002)
1695bc9661cSMichal Nazarewicz return 0;
1702201d6b1SDavid Brownell
1712201d6b1SDavid Brownell /*----------------------------------------------------*/
1722201d6b1SDavid Brownell
1732201d6b1SDavid Brownell /* devices that start up using the EZ-USB default device and
1742201d6b1SDavid Brownell * which we can use after loading simple firmware. hotplug
1752201d6b1SDavid Brownell * can fxload it, and then run this test driver.
1762201d6b1SDavid Brownell *
1772201d6b1SDavid Brownell * we return false positives in two cases:
1782201d6b1SDavid Brownell * - the device has a "real" driver (maybe usb-serial) that
1792201d6b1SDavid Brownell * renumerates. the device should vanish quickly.
1802201d6b1SDavid Brownell * - the device doesn't have the test firmware installed.
1812201d6b1SDavid Brownell */
1822201d6b1SDavid Brownell
1832201d6b1SDavid Brownell /* generic EZ-USB FX controller */
1845bc9661cSMichal Nazarewicz if (dev.idVendor == 0x0547 && dev.idProduct == 0x2235)
1855bc9661cSMichal Nazarewicz return 0;
1862201d6b1SDavid Brownell
1872201d6b1SDavid Brownell /* generic EZ-USB FX2 controller */
1885bc9661cSMichal Nazarewicz if (dev.idVendor == 0x04b4 && dev.idProduct == 0x8613)
1895bc9661cSMichal Nazarewicz return 0;
1902201d6b1SDavid Brownell
1912201d6b1SDavid Brownell /* CY3671 development board with EZ-USB FX */
1925bc9661cSMichal Nazarewicz if (dev.idVendor == 0x0547 && dev.idProduct == 0x0080)
1935bc9661cSMichal Nazarewicz return 0;
1942201d6b1SDavid Brownell
1952201d6b1SDavid Brownell /* Keyspan 19Qi uses an21xx (original EZ-USB) */
1965bc9661cSMichal Nazarewicz if (dev.idVendor == 0x06cd && dev.idProduct == 0x010b)
1975bc9661cSMichal Nazarewicz return 0;
1982201d6b1SDavid Brownell
1992201d6b1SDavid Brownell /*----------------------------------------------------*/
2002201d6b1SDavid Brownell
2012201d6b1SDavid Brownell /* "gadget zero", Linux-USB test software */
2025bc9661cSMichal Nazarewicz if (dev.idVendor == 0x0525 && dev.idProduct == 0xa4a0)
2035bc9661cSMichal Nazarewicz return 0;
2042201d6b1SDavid Brownell
2052201d6b1SDavid Brownell /* user mode subset of that */
2065bc9661cSMichal Nazarewicz if (dev.idVendor == 0x0525 && dev.idProduct == 0xa4a4)
2075bc9661cSMichal Nazarewicz return testdev_ffs_ifnum(fd);
2085bc9661cSMichal Nazarewicz /* return 0; */
2092201d6b1SDavid Brownell
2102201d6b1SDavid Brownell /* iso version of usermode code */
2115bc9661cSMichal Nazarewicz if (dev.idVendor == 0x0525 && dev.idProduct == 0xa4a3)
2125bc9661cSMichal Nazarewicz return 0;
2132201d6b1SDavid Brownell
2142201d6b1SDavid Brownell /* some GPL'd test firmware uses these IDs */
2152201d6b1SDavid Brownell
2165bc9661cSMichal Nazarewicz if (dev.idVendor == 0xfff0 && dev.idProduct == 0xfff0)
2175bc9661cSMichal Nazarewicz return 0;
2182201d6b1SDavid Brownell
2192201d6b1SDavid Brownell /*----------------------------------------------------*/
2202201d6b1SDavid Brownell
2212201d6b1SDavid Brownell /* iBOT2 high speed webcam */
2225bc9661cSMichal Nazarewicz if (dev.idVendor == 0x0b62 && dev.idProduct == 0x0059)
2232201d6b1SDavid Brownell return 0;
2245bc9661cSMichal Nazarewicz
2255bc9661cSMichal Nazarewicz /*----------------------------------------------------*/
2265bc9661cSMichal Nazarewicz
2275bc9661cSMichal Nazarewicz /* the FunctionFS gadget can have the source/sink interface
2285bc9661cSMichal Nazarewicz * anywhere. We look for an interface descriptor that match
2295bc9661cSMichal Nazarewicz * what we expect. We ignore configuratiens thou. */
2305bc9661cSMichal Nazarewicz
2315bc9661cSMichal Nazarewicz if (dev.idVendor == 0x0525 && dev.idProduct == 0xa4ac
2325bc9661cSMichal Nazarewicz && (dev.bDeviceClass == USB_CLASS_PER_INTERFACE
2335bc9661cSMichal Nazarewicz || dev.bDeviceClass == USB_CLASS_VENDOR_SPEC))
2345bc9661cSMichal Nazarewicz return testdev_ffs_ifnum(fd);
2355bc9661cSMichal Nazarewicz
2365bc9661cSMichal Nazarewicz return -1;
2372201d6b1SDavid Brownell }
2382201d6b1SDavid Brownell
find_testdev(const char * name,const struct stat * sb,int flag)2392201d6b1SDavid Brownell static int find_testdev(const char *name, const struct stat *sb, int flag)
2402201d6b1SDavid Brownell {
2415bc9661cSMichal Nazarewicz FILE *fd;
2425bc9661cSMichal Nazarewicz int ifnum;
2435bc9661cSMichal Nazarewicz struct testdev *entry;
2445bc9661cSMichal Nazarewicz
2455bc9661cSMichal Nazarewicz (void)sb; /* unused */
2462201d6b1SDavid Brownell
2472201d6b1SDavid Brownell if (flag != FTW_F)
2482201d6b1SDavid Brownell return 0;
2492201d6b1SDavid Brownell
2505bc9661cSMichal Nazarewicz fd = fopen(name, "rb");
2515bc9661cSMichal Nazarewicz if (!fd) {
2525bc9661cSMichal Nazarewicz perror(name);
2532201d6b1SDavid Brownell return 0;
2542201d6b1SDavid Brownell }
2552201d6b1SDavid Brownell
2565bc9661cSMichal Nazarewicz ifnum = testdev_ifnum(fd);
2575bc9661cSMichal Nazarewicz fclose(fd);
2585bc9661cSMichal Nazarewicz if (ifnum < 0)
2595bc9661cSMichal Nazarewicz return 0;
2605bc9661cSMichal Nazarewicz
2615bc9661cSMichal Nazarewicz entry = calloc(1, sizeof *entry);
2625bc9661cSMichal Nazarewicz if (!entry)
2635bc9661cSMichal Nazarewicz goto nomem;
2645bc9661cSMichal Nazarewicz
2652201d6b1SDavid Brownell entry->name = strdup(name);
2662201d6b1SDavid Brownell if (!entry->name) {
2672201d6b1SDavid Brownell free(entry);
2685bc9661cSMichal Nazarewicz nomem:
2695bc9661cSMichal Nazarewicz perror("malloc");
2705bc9661cSMichal Nazarewicz return 0;
2712201d6b1SDavid Brownell }
2722201d6b1SDavid Brownell
2735bc9661cSMichal Nazarewicz entry->ifnum = ifnum;
2742201d6b1SDavid Brownell entry->next = testdevs;
2752201d6b1SDavid Brownell testdevs = entry;
2762201d6b1SDavid Brownell return 0;
2772201d6b1SDavid Brownell }
2782201d6b1SDavid Brownell
2792201d6b1SDavid Brownell static int
usbdev_ioctl(int fd,int ifno,unsigned request,void * param)2802201d6b1SDavid Brownell usbdev_ioctl (int fd, int ifno, unsigned request, void *param)
2812201d6b1SDavid Brownell {
2822201d6b1SDavid Brownell struct usbdevfs_ioctl wrapper;
2832201d6b1SDavid Brownell
2842201d6b1SDavid Brownell wrapper.ifno = ifno;
2852201d6b1SDavid Brownell wrapper.ioctl_code = request;
2862201d6b1SDavid Brownell wrapper.data = param;
2872201d6b1SDavid Brownell
2882201d6b1SDavid Brownell return ioctl (fd, USBDEVFS_IOCTL, &wrapper);
2892201d6b1SDavid Brownell }
2902201d6b1SDavid Brownell
handle_testdev(void * arg)2912201d6b1SDavid Brownell static void *handle_testdev (void *arg)
2922201d6b1SDavid Brownell {
2932201d6b1SDavid Brownell struct testdev *dev = arg;
2942201d6b1SDavid Brownell int fd, i;
2952201d6b1SDavid Brownell int status;
2962201d6b1SDavid Brownell
2972201d6b1SDavid Brownell if ((fd = open (dev->name, O_RDWR)) < 0) {
2982201d6b1SDavid Brownell perror ("can't open dev file r/w");
2992201d6b1SDavid Brownell return 0;
3002201d6b1SDavid Brownell }
3012201d6b1SDavid Brownell
302f81c08f8SFaizel K B status = ioctl(fd, USBDEVFS_GET_SPEED, NULL);
303f81c08f8SFaizel K B if (status < 0)
304f81c08f8SFaizel K B fprintf(stderr, "USBDEVFS_GET_SPEED failed %d\n", status);
305f81c08f8SFaizel K B else
306f81c08f8SFaizel K B dev->speed = status;
307f81c08f8SFaizel K B fprintf(stderr, "%s speed\t%s\t%u\n",
308f81c08f8SFaizel K B speed(dev->speed), dev->name, dev->ifnum);
309f81c08f8SFaizel K B
3102201d6b1SDavid Brownell restart:
3112201d6b1SDavid Brownell for (i = 0; i < TEST_CASES; i++) {
3122201d6b1SDavid Brownell if (dev->test != -1 && dev->test != i)
3132201d6b1SDavid Brownell continue;
3142201d6b1SDavid Brownell dev->param.test_num = i;
3152201d6b1SDavid Brownell
3162201d6b1SDavid Brownell status = usbdev_ioctl (fd, dev->ifnum,
3172201d6b1SDavid Brownell USBTEST_REQUEST, &dev->param);
3182201d6b1SDavid Brownell if (status < 0 && errno == EOPNOTSUPP)
3192201d6b1SDavid Brownell continue;
3202201d6b1SDavid Brownell
3212201d6b1SDavid Brownell /* FIXME need a "syslog it" option for background testing */
3222201d6b1SDavid Brownell
3232201d6b1SDavid Brownell /* NOTE: each thread emits complete lines; no fragments! */
3242201d6b1SDavid Brownell if (status < 0) {
3252201d6b1SDavid Brownell char buf [80];
3262201d6b1SDavid Brownell int err = errno;
3272201d6b1SDavid Brownell
3282201d6b1SDavid Brownell if (strerror_r (errno, buf, sizeof buf)) {
3292201d6b1SDavid Brownell snprintf (buf, sizeof buf, "error %d", err);
3302201d6b1SDavid Brownell errno = err;
3312201d6b1SDavid Brownell }
3322201d6b1SDavid Brownell printf ("%s test %d --> %d (%s)\n",
3332201d6b1SDavid Brownell dev->name, i, errno, buf);
3342201d6b1SDavid Brownell } else
3352201d6b1SDavid Brownell printf ("%s test %d, %4d.%.06d secs\n", dev->name, i,
3362201d6b1SDavid Brownell (int) dev->param.duration.tv_sec,
3372201d6b1SDavid Brownell (int) dev->param.duration.tv_usec);
3382201d6b1SDavid Brownell
3392201d6b1SDavid Brownell fflush (stdout);
3402201d6b1SDavid Brownell }
3412201d6b1SDavid Brownell if (dev->forever)
3422201d6b1SDavid Brownell goto restart;
3432201d6b1SDavid Brownell
3442201d6b1SDavid Brownell close (fd);
3452201d6b1SDavid Brownell return arg;
3462201d6b1SDavid Brownell }
3472201d6b1SDavid Brownell
usb_dir_find(void)3489742aecdSSergei Shtylyov static const char *usb_dir_find(void)
3495bc9661cSMichal Nazarewicz {
350f6fe916eSDu, ChangbinX static char udev_usb_path[] = "/dev/bus/usb";
3515bc9661cSMichal Nazarewicz
352f6fe916eSDu, ChangbinX if (access(udev_usb_path, F_OK) == 0)
353f6fe916eSDu, ChangbinX return udev_usb_path;
354f6fe916eSDu, ChangbinX
3555bc9661cSMichal Nazarewicz return NULL;
3565bc9661cSMichal Nazarewicz }
3575bc9661cSMichal Nazarewicz
parse_num(unsigned * num,const char * str)3585bc9661cSMichal Nazarewicz static int parse_num(unsigned *num, const char *str)
3595bc9661cSMichal Nazarewicz {
3605bc9661cSMichal Nazarewicz unsigned long val;
3615bc9661cSMichal Nazarewicz char *end;
3625bc9661cSMichal Nazarewicz
3635bc9661cSMichal Nazarewicz errno = 0;
3645bc9661cSMichal Nazarewicz val = strtoul(str, &end, 0);
3655bc9661cSMichal Nazarewicz if (errno || *end || val > UINT_MAX)
3665bc9661cSMichal Nazarewicz return -1;
3675bc9661cSMichal Nazarewicz *num = val;
3685bc9661cSMichal Nazarewicz return 0;
3695bc9661cSMichal Nazarewicz }
3705bc9661cSMichal Nazarewicz
main(int argc,char ** argv)3712201d6b1SDavid Brownell int main (int argc, char **argv)
3722201d6b1SDavid Brownell {
3735bc9661cSMichal Nazarewicz
3742201d6b1SDavid Brownell int c;
3752201d6b1SDavid Brownell struct testdev *entry;
3762201d6b1SDavid Brownell char *device;
3779742aecdSSergei Shtylyov const char *usb_dir = NULL;
3782201d6b1SDavid Brownell int all = 0, forever = 0, not = 0;
3792201d6b1SDavid Brownell int test = -1 /* all */;
3802201d6b1SDavid Brownell struct usbtest_param param;
3812201d6b1SDavid Brownell
3822201d6b1SDavid Brownell /* pick defaults that works with all speeds, without short packets.
3832201d6b1SDavid Brownell *
3842201d6b1SDavid Brownell * Best per-frame data rates:
385a7ea58f3SFelipe Balbi * super speed,bulk 1024 * 16 * 8 = 131072
386a7ea58f3SFelipe Balbi * interrupt 1024 * 3 * 8 = 24576
3872201d6b1SDavid Brownell * high speed, bulk 512 * 13 * 8 = 53248
3882201d6b1SDavid Brownell * interrupt 1024 * 3 * 8 = 24576
3892201d6b1SDavid Brownell * full speed, bulk/intr 64 * 19 = 1216
3902201d6b1SDavid Brownell * interrupt 64 * 1 = 64
3912201d6b1SDavid Brownell * low speed, interrupt 8 * 1 = 8
3922201d6b1SDavid Brownell */
3932201d6b1SDavid Brownell param.iterations = 1000;
3949e44d194SPeter Chen param.length = 1024;
395a7ea58f3SFelipe Balbi param.vary = 1024;
3962201d6b1SDavid Brownell param.sglen = 32;
3972201d6b1SDavid Brownell
3982201d6b1SDavid Brownell /* for easy use when hotplugging */
3992201d6b1SDavid Brownell device = getenv ("DEVICE");
4002201d6b1SDavid Brownell
4017e54e978SDu, ChangbinX while ((c = getopt (argc, argv, "D:aA:c:g:hlns:t:v:")) != EOF)
4022201d6b1SDavid Brownell switch (c) {
4032201d6b1SDavid Brownell case 'D': /* device, if only one */
4042201d6b1SDavid Brownell device = optarg;
4052201d6b1SDavid Brownell continue;
4069742aecdSSergei Shtylyov case 'A': /* use all devices with specified USB dir */
4079742aecdSSergei Shtylyov usb_dir = optarg;
4085bc9661cSMichal Nazarewicz /* FALL THROUGH */
4092201d6b1SDavid Brownell case 'a': /* use all devices */
4105bc9661cSMichal Nazarewicz device = NULL;
4112201d6b1SDavid Brownell all = 1;
4122201d6b1SDavid Brownell continue;
4132201d6b1SDavid Brownell case 'c': /* count iterations */
4145bc9661cSMichal Nazarewicz if (parse_num(¶m.iterations, optarg))
4152201d6b1SDavid Brownell goto usage;
4162201d6b1SDavid Brownell continue;
4172201d6b1SDavid Brownell case 'g': /* scatter/gather entries */
4185bc9661cSMichal Nazarewicz if (parse_num(¶m.sglen, optarg))
4192201d6b1SDavid Brownell goto usage;
4202201d6b1SDavid Brownell continue;
4212201d6b1SDavid Brownell case 'l': /* loop forever */
4222201d6b1SDavid Brownell forever = 1;
4232201d6b1SDavid Brownell continue;
4242201d6b1SDavid Brownell case 'n': /* no test running! */
4252201d6b1SDavid Brownell not = 1;
4262201d6b1SDavid Brownell continue;
4272201d6b1SDavid Brownell case 's': /* size of packet */
4285bc9661cSMichal Nazarewicz if (parse_num(¶m.length, optarg))
4292201d6b1SDavid Brownell goto usage;
4302201d6b1SDavid Brownell continue;
4312201d6b1SDavid Brownell case 't': /* run just one test */
4322201d6b1SDavid Brownell test = atoi (optarg);
4332201d6b1SDavid Brownell if (test < 0)
4342201d6b1SDavid Brownell goto usage;
4352201d6b1SDavid Brownell continue;
4362201d6b1SDavid Brownell case 'v': /* vary packet size by ... */
4375bc9661cSMichal Nazarewicz if (parse_num(¶m.vary, optarg))
4382201d6b1SDavid Brownell goto usage;
4392201d6b1SDavid Brownell continue;
4402201d6b1SDavid Brownell case '?':
4412201d6b1SDavid Brownell case 'h':
4422201d6b1SDavid Brownell default:
4432201d6b1SDavid Brownell usage:
4447e54e978SDu, ChangbinX fprintf (stderr,
4457e54e978SDu, ChangbinX "usage: %s [options]\n"
4467e54e978SDu, ChangbinX "Options:\n"
4477e54e978SDu, ChangbinX "\t-D dev only test specific device\n"
4489742aecdSSergei Shtylyov "\t-A usb-dir\n"
4497e54e978SDu, ChangbinX "\t-a test all recognized devices\n"
4507e54e978SDu, ChangbinX "\t-l loop forever(for stress test)\n"
4517e54e978SDu, ChangbinX "\t-t testnum only run specified case\n"
4527e54e978SDu, ChangbinX "\t-n no test running, show devices to be tested\n"
4537e54e978SDu, ChangbinX "Case arguments:\n"
4547e54e978SDu, ChangbinX "\t-c iterations default 1000\n"
4559e44d194SPeter Chen "\t-s transfer length default 1024\n"
4567e54e978SDu, ChangbinX "\t-g sglen default 32\n"
457a7ea58f3SFelipe Balbi "\t-v vary default 1024\n",
4582201d6b1SDavid Brownell argv[0]);
4592201d6b1SDavid Brownell return 1;
4602201d6b1SDavid Brownell }
4612201d6b1SDavid Brownell if (optind != argc)
4622201d6b1SDavid Brownell goto usage;
4632201d6b1SDavid Brownell if (!all && !device) {
4642201d6b1SDavid Brownell fprintf (stderr, "must specify '-a' or '-D dev', "
4658a424bf4SSebastian Andrzej Siewior "or DEVICE=/dev/bus/usb/BBB/DDD in env\n");
4662201d6b1SDavid Brownell goto usage;
4672201d6b1SDavid Brownell }
4682201d6b1SDavid Brownell
4699742aecdSSergei Shtylyov /* Find usb device subdirectory */
4709742aecdSSergei Shtylyov if (!usb_dir) {
4719742aecdSSergei Shtylyov usb_dir = usb_dir_find();
4729742aecdSSergei Shtylyov if (!usb_dir) {
4739742aecdSSergei Shtylyov fputs ("USB device files are missing\n", stderr);
4742201d6b1SDavid Brownell return -1;
4752201d6b1SDavid Brownell }
4765bc9661cSMichal Nazarewicz }
4772201d6b1SDavid Brownell
4782201d6b1SDavid Brownell /* collect and list the test devices */
4799742aecdSSergei Shtylyov if (ftw (usb_dir, find_testdev, 3) != 0) {
4809742aecdSSergei Shtylyov fputs ("ftw failed; are USB device files missing?\n", stderr);
4812201d6b1SDavid Brownell return -1;
4822201d6b1SDavid Brownell }
4832201d6b1SDavid Brownell
4842201d6b1SDavid Brownell /* quit, run single test, or create test threads */
4852201d6b1SDavid Brownell if (!testdevs && !device) {
4862201d6b1SDavid Brownell fputs ("no test devices recognized\n", stderr);
4872201d6b1SDavid Brownell return -1;
4882201d6b1SDavid Brownell }
4892201d6b1SDavid Brownell if (not)
4902201d6b1SDavid Brownell return 0;
491ef94b266SHaowen Bai if (testdevs && !testdevs->next && !device)
4922201d6b1SDavid Brownell device = testdevs->name;
4932201d6b1SDavid Brownell for (entry = testdevs; entry; entry = entry->next) {
4942201d6b1SDavid Brownell int status;
4952201d6b1SDavid Brownell
4962201d6b1SDavid Brownell entry->param = param;
4972201d6b1SDavid Brownell entry->forever = forever;
4982201d6b1SDavid Brownell entry->test = test;
4992201d6b1SDavid Brownell
5002201d6b1SDavid Brownell if (device) {
5012201d6b1SDavid Brownell if (strcmp (entry->name, device))
5022201d6b1SDavid Brownell continue;
5032201d6b1SDavid Brownell return handle_testdev (entry) != entry;
5042201d6b1SDavid Brownell }
5052201d6b1SDavid Brownell status = pthread_create (&entry->thread, 0, handle_testdev, entry);
5062c449e38SSasha Levin if (status)
5072201d6b1SDavid Brownell perror ("pthread_create");
5082201d6b1SDavid Brownell }
5092201d6b1SDavid Brownell if (device) {
5102201d6b1SDavid Brownell struct testdev dev;
5112201d6b1SDavid Brownell
5122201d6b1SDavid Brownell /* kernel can recognize test devices we don't */
5132201d6b1SDavid Brownell fprintf (stderr, "%s: %s may see only control tests\n",
5142201d6b1SDavid Brownell argv [0], device);
5152201d6b1SDavid Brownell
5162201d6b1SDavid Brownell memset (&dev, 0, sizeof dev);
5172201d6b1SDavid Brownell dev.name = device;
5182201d6b1SDavid Brownell dev.param = param;
5192201d6b1SDavid Brownell dev.forever = forever;
5202201d6b1SDavid Brownell dev.test = test;
5212201d6b1SDavid Brownell return handle_testdev (&dev) != &dev;
5222201d6b1SDavid Brownell }
5232201d6b1SDavid Brownell
5242201d6b1SDavid Brownell /* wait for tests to complete */
5252201d6b1SDavid Brownell for (entry = testdevs; entry; entry = entry->next) {
5262201d6b1SDavid Brownell void *retval;
5272201d6b1SDavid Brownell
5282201d6b1SDavid Brownell if (pthread_join (entry->thread, &retval))
5292201d6b1SDavid Brownell perror ("pthread_join");
5302201d6b1SDavid Brownell /* testing errors discarded! */
5312201d6b1SDavid Brownell }
5322201d6b1SDavid Brownell
5332201d6b1SDavid Brownell return 0;
5342201d6b1SDavid Brownell }
535