18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* $(CROSS_COMPILE)cc -Wall -Wextra -g -lpthread -o testusb testusb.c */ 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci/* 58c2ecf20Sopenharmony_ci * Copyright (c) 2002 by David Brownell 68c2ecf20Sopenharmony_ci * Copyright (c) 2010 by Samsung Electronics 78c2ecf20Sopenharmony_ci * Author: Michal Nazarewicz <mina86@mina86.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci/* 118c2ecf20Sopenharmony_ci * This program issues ioctls to perform the tests implemented by the 128c2ecf20Sopenharmony_ci * kernel driver. It can generate a variety of transfer patterns; you 138c2ecf20Sopenharmony_ci * should make sure to test both regular streaming and mixes of 148c2ecf20Sopenharmony_ci * transfer sizes (including short transfers). 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * For more information on how this can be used and on USB testing 178c2ecf20Sopenharmony_ci * refer to <URL:http://www.linux-usb.org/usbtest/>. 188c2ecf20Sopenharmony_ci */ 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <stdio.h> 218c2ecf20Sopenharmony_ci#include <string.h> 228c2ecf20Sopenharmony_ci#include <ftw.h> 238c2ecf20Sopenharmony_ci#include <stdlib.h> 248c2ecf20Sopenharmony_ci#include <pthread.h> 258c2ecf20Sopenharmony_ci#include <unistd.h> 268c2ecf20Sopenharmony_ci#include <errno.h> 278c2ecf20Sopenharmony_ci#include <limits.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#include <sys/types.h> 308c2ecf20Sopenharmony_ci#include <sys/stat.h> 318c2ecf20Sopenharmony_ci#include <fcntl.h> 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#include <sys/ioctl.h> 348c2ecf20Sopenharmony_ci#include <linux/usbdevice_fs.h> 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/ 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define TEST_CASES 30 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci// FIXME make these public somewhere; usbdevfs.h? 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistruct usbtest_param { 438c2ecf20Sopenharmony_ci // inputs 448c2ecf20Sopenharmony_ci unsigned test_num; /* 0..(TEST_CASES-1) */ 458c2ecf20Sopenharmony_ci unsigned iterations; 468c2ecf20Sopenharmony_ci unsigned length; 478c2ecf20Sopenharmony_ci unsigned vary; 488c2ecf20Sopenharmony_ci unsigned sglen; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci // outputs 518c2ecf20Sopenharmony_ci struct timeval duration; 528c2ecf20Sopenharmony_ci}; 538c2ecf20Sopenharmony_ci#define USBTEST_REQUEST _IOWR('U', 100, struct usbtest_param) 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/ 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci/* #include <linux/usb_ch9.h> */ 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci#define USB_DT_DEVICE 0x01 608c2ecf20Sopenharmony_ci#define USB_DT_INTERFACE 0x04 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci#define USB_CLASS_PER_INTERFACE 0 /* for DeviceClass */ 638c2ecf20Sopenharmony_ci#define USB_CLASS_VENDOR_SPEC 0xff 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistruct usb_device_descriptor { 678c2ecf20Sopenharmony_ci __u8 bLength; 688c2ecf20Sopenharmony_ci __u8 bDescriptorType; 698c2ecf20Sopenharmony_ci __u16 bcdUSB; 708c2ecf20Sopenharmony_ci __u8 bDeviceClass; 718c2ecf20Sopenharmony_ci __u8 bDeviceSubClass; 728c2ecf20Sopenharmony_ci __u8 bDeviceProtocol; 738c2ecf20Sopenharmony_ci __u8 bMaxPacketSize0; 748c2ecf20Sopenharmony_ci __u16 idVendor; 758c2ecf20Sopenharmony_ci __u16 idProduct; 768c2ecf20Sopenharmony_ci __u16 bcdDevice; 778c2ecf20Sopenharmony_ci __u8 iManufacturer; 788c2ecf20Sopenharmony_ci __u8 iProduct; 798c2ecf20Sopenharmony_ci __u8 iSerialNumber; 808c2ecf20Sopenharmony_ci __u8 bNumConfigurations; 818c2ecf20Sopenharmony_ci} __attribute__ ((packed)); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistruct usb_interface_descriptor { 848c2ecf20Sopenharmony_ci __u8 bLength; 858c2ecf20Sopenharmony_ci __u8 bDescriptorType; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci __u8 bInterfaceNumber; 888c2ecf20Sopenharmony_ci __u8 bAlternateSetting; 898c2ecf20Sopenharmony_ci __u8 bNumEndpoints; 908c2ecf20Sopenharmony_ci __u8 bInterfaceClass; 918c2ecf20Sopenharmony_ci __u8 bInterfaceSubClass; 928c2ecf20Sopenharmony_ci __u8 bInterfaceProtocol; 938c2ecf20Sopenharmony_ci __u8 iInterface; 948c2ecf20Sopenharmony_ci} __attribute__ ((packed)); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cienum usb_device_speed { 978c2ecf20Sopenharmony_ci USB_SPEED_UNKNOWN = 0, /* enumerating */ 988c2ecf20Sopenharmony_ci USB_SPEED_LOW, USB_SPEED_FULL, /* usb 1.1 */ 998c2ecf20Sopenharmony_ci USB_SPEED_HIGH /* usb 2.0 */ 1008c2ecf20Sopenharmony_ci}; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/ 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic char *speed (enum usb_device_speed s) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci switch (s) { 1078c2ecf20Sopenharmony_ci case USB_SPEED_UNKNOWN: return "unknown"; 1088c2ecf20Sopenharmony_ci case USB_SPEED_LOW: return "low"; 1098c2ecf20Sopenharmony_ci case USB_SPEED_FULL: return "full"; 1108c2ecf20Sopenharmony_ci case USB_SPEED_HIGH: return "high"; 1118c2ecf20Sopenharmony_ci default: return "??"; 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistruct testdev { 1168c2ecf20Sopenharmony_ci struct testdev *next; 1178c2ecf20Sopenharmony_ci char *name; 1188c2ecf20Sopenharmony_ci pthread_t thread; 1198c2ecf20Sopenharmony_ci enum usb_device_speed speed; 1208c2ecf20Sopenharmony_ci unsigned ifnum : 8; 1218c2ecf20Sopenharmony_ci unsigned forever : 1; 1228c2ecf20Sopenharmony_ci int test; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci struct usbtest_param param; 1258c2ecf20Sopenharmony_ci}; 1268c2ecf20Sopenharmony_cistatic struct testdev *testdevs; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic int testdev_ffs_ifnum(FILE *fd) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci union { 1318c2ecf20Sopenharmony_ci char buf[255]; 1328c2ecf20Sopenharmony_ci struct usb_interface_descriptor intf; 1338c2ecf20Sopenharmony_ci } u; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci for (;;) { 1368c2ecf20Sopenharmony_ci if (fread(u.buf, 1, 1, fd) != 1) 1378c2ecf20Sopenharmony_ci return -1; 1388c2ecf20Sopenharmony_ci if (fread(u.buf + 1, (unsigned char)u.buf[0] - 1, 1, fd) != 1) 1398c2ecf20Sopenharmony_ci return -1; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci if (u.intf.bLength == sizeof u.intf 1428c2ecf20Sopenharmony_ci && u.intf.bDescriptorType == USB_DT_INTERFACE 1438c2ecf20Sopenharmony_ci && u.intf.bNumEndpoints == 2 1448c2ecf20Sopenharmony_ci && u.intf.bInterfaceClass == USB_CLASS_VENDOR_SPEC 1458c2ecf20Sopenharmony_ci && u.intf.bInterfaceSubClass == 0 1468c2ecf20Sopenharmony_ci && u.intf.bInterfaceProtocol == 0) 1478c2ecf20Sopenharmony_ci return (unsigned char)u.intf.bInterfaceNumber; 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic int testdev_ifnum(FILE *fd) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci struct usb_device_descriptor dev; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci if (fread(&dev, sizeof dev, 1, fd) != 1) 1568c2ecf20Sopenharmony_ci return -1; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci if (dev.bLength != sizeof dev || dev.bDescriptorType != USB_DT_DEVICE) 1598c2ecf20Sopenharmony_ci return -1; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci /* FX2 with (tweaked) bulksrc firmware */ 1628c2ecf20Sopenharmony_ci if (dev.idVendor == 0x0547 && dev.idProduct == 0x1002) 1638c2ecf20Sopenharmony_ci return 0; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci /*----------------------------------------------------*/ 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci /* devices that start up using the EZ-USB default device and 1688c2ecf20Sopenharmony_ci * which we can use after loading simple firmware. hotplug 1698c2ecf20Sopenharmony_ci * can fxload it, and then run this test driver. 1708c2ecf20Sopenharmony_ci * 1718c2ecf20Sopenharmony_ci * we return false positives in two cases: 1728c2ecf20Sopenharmony_ci * - the device has a "real" driver (maybe usb-serial) that 1738c2ecf20Sopenharmony_ci * renumerates. the device should vanish quickly. 1748c2ecf20Sopenharmony_ci * - the device doesn't have the test firmware installed. 1758c2ecf20Sopenharmony_ci */ 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci /* generic EZ-USB FX controller */ 1788c2ecf20Sopenharmony_ci if (dev.idVendor == 0x0547 && dev.idProduct == 0x2235) 1798c2ecf20Sopenharmony_ci return 0; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci /* generic EZ-USB FX2 controller */ 1828c2ecf20Sopenharmony_ci if (dev.idVendor == 0x04b4 && dev.idProduct == 0x8613) 1838c2ecf20Sopenharmony_ci return 0; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci /* CY3671 development board with EZ-USB FX */ 1868c2ecf20Sopenharmony_ci if (dev.idVendor == 0x0547 && dev.idProduct == 0x0080) 1878c2ecf20Sopenharmony_ci return 0; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci /* Keyspan 19Qi uses an21xx (original EZ-USB) */ 1908c2ecf20Sopenharmony_ci if (dev.idVendor == 0x06cd && dev.idProduct == 0x010b) 1918c2ecf20Sopenharmony_ci return 0; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci /*----------------------------------------------------*/ 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci /* "gadget zero", Linux-USB test software */ 1968c2ecf20Sopenharmony_ci if (dev.idVendor == 0x0525 && dev.idProduct == 0xa4a0) 1978c2ecf20Sopenharmony_ci return 0; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci /* user mode subset of that */ 2008c2ecf20Sopenharmony_ci if (dev.idVendor == 0x0525 && dev.idProduct == 0xa4a4) 2018c2ecf20Sopenharmony_ci return testdev_ffs_ifnum(fd); 2028c2ecf20Sopenharmony_ci /* return 0; */ 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci /* iso version of usermode code */ 2058c2ecf20Sopenharmony_ci if (dev.idVendor == 0x0525 && dev.idProduct == 0xa4a3) 2068c2ecf20Sopenharmony_ci return 0; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci /* some GPL'd test firmware uses these IDs */ 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci if (dev.idVendor == 0xfff0 && dev.idProduct == 0xfff0) 2118c2ecf20Sopenharmony_ci return 0; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci /*----------------------------------------------------*/ 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci /* iBOT2 high speed webcam */ 2168c2ecf20Sopenharmony_ci if (dev.idVendor == 0x0b62 && dev.idProduct == 0x0059) 2178c2ecf20Sopenharmony_ci return 0; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci /*----------------------------------------------------*/ 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci /* the FunctionFS gadget can have the source/sink interface 2228c2ecf20Sopenharmony_ci * anywhere. We look for an interface descriptor that match 2238c2ecf20Sopenharmony_ci * what we expect. We ignore configuratiens thou. */ 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci if (dev.idVendor == 0x0525 && dev.idProduct == 0xa4ac 2268c2ecf20Sopenharmony_ci && (dev.bDeviceClass == USB_CLASS_PER_INTERFACE 2278c2ecf20Sopenharmony_ci || dev.bDeviceClass == USB_CLASS_VENDOR_SPEC)) 2288c2ecf20Sopenharmony_ci return testdev_ffs_ifnum(fd); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci return -1; 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_cistatic int find_testdev(const char *name, const struct stat *sb, int flag) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci FILE *fd; 2368c2ecf20Sopenharmony_ci int ifnum; 2378c2ecf20Sopenharmony_ci struct testdev *entry; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci (void)sb; /* unused */ 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci if (flag != FTW_F) 2428c2ecf20Sopenharmony_ci return 0; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci fd = fopen(name, "rb"); 2458c2ecf20Sopenharmony_ci if (!fd) { 2468c2ecf20Sopenharmony_ci perror(name); 2478c2ecf20Sopenharmony_ci return 0; 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci ifnum = testdev_ifnum(fd); 2518c2ecf20Sopenharmony_ci fclose(fd); 2528c2ecf20Sopenharmony_ci if (ifnum < 0) 2538c2ecf20Sopenharmony_ci return 0; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci entry = calloc(1, sizeof *entry); 2568c2ecf20Sopenharmony_ci if (!entry) 2578c2ecf20Sopenharmony_ci goto nomem; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci entry->name = strdup(name); 2608c2ecf20Sopenharmony_ci if (!entry->name) { 2618c2ecf20Sopenharmony_ci free(entry); 2628c2ecf20Sopenharmony_cinomem: 2638c2ecf20Sopenharmony_ci perror("malloc"); 2648c2ecf20Sopenharmony_ci return 0; 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci entry->ifnum = ifnum; 2688c2ecf20Sopenharmony_ci entry->next = testdevs; 2698c2ecf20Sopenharmony_ci testdevs = entry; 2708c2ecf20Sopenharmony_ci return 0; 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_cistatic int 2748c2ecf20Sopenharmony_ciusbdev_ioctl (int fd, int ifno, unsigned request, void *param) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci struct usbdevfs_ioctl wrapper; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci wrapper.ifno = ifno; 2798c2ecf20Sopenharmony_ci wrapper.ioctl_code = request; 2808c2ecf20Sopenharmony_ci wrapper.data = param; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci return ioctl (fd, USBDEVFS_IOCTL, &wrapper); 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_cistatic void *handle_testdev (void *arg) 2868c2ecf20Sopenharmony_ci{ 2878c2ecf20Sopenharmony_ci struct testdev *dev = arg; 2888c2ecf20Sopenharmony_ci int fd, i; 2898c2ecf20Sopenharmony_ci int status; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci if ((fd = open (dev->name, O_RDWR)) < 0) { 2928c2ecf20Sopenharmony_ci perror ("can't open dev file r/w"); 2938c2ecf20Sopenharmony_ci return 0; 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci status = ioctl(fd, USBDEVFS_GET_SPEED, NULL); 2978c2ecf20Sopenharmony_ci if (status < 0) 2988c2ecf20Sopenharmony_ci fprintf(stderr, "USBDEVFS_GET_SPEED failed %d\n", status); 2998c2ecf20Sopenharmony_ci else 3008c2ecf20Sopenharmony_ci dev->speed = status; 3018c2ecf20Sopenharmony_ci fprintf(stderr, "%s speed\t%s\t%u\n", 3028c2ecf20Sopenharmony_ci speed(dev->speed), dev->name, dev->ifnum); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_cirestart: 3058c2ecf20Sopenharmony_ci for (i = 0; i < TEST_CASES; i++) { 3068c2ecf20Sopenharmony_ci if (dev->test != -1 && dev->test != i) 3078c2ecf20Sopenharmony_ci continue; 3088c2ecf20Sopenharmony_ci dev->param.test_num = i; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci status = usbdev_ioctl (fd, dev->ifnum, 3118c2ecf20Sopenharmony_ci USBTEST_REQUEST, &dev->param); 3128c2ecf20Sopenharmony_ci if (status < 0 && errno == EOPNOTSUPP) 3138c2ecf20Sopenharmony_ci continue; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci /* FIXME need a "syslog it" option for background testing */ 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci /* NOTE: each thread emits complete lines; no fragments! */ 3188c2ecf20Sopenharmony_ci if (status < 0) { 3198c2ecf20Sopenharmony_ci char buf [80]; 3208c2ecf20Sopenharmony_ci int err = errno; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci if (strerror_r (errno, buf, sizeof buf)) { 3238c2ecf20Sopenharmony_ci snprintf (buf, sizeof buf, "error %d", err); 3248c2ecf20Sopenharmony_ci errno = err; 3258c2ecf20Sopenharmony_ci } 3268c2ecf20Sopenharmony_ci printf ("%s test %d --> %d (%s)\n", 3278c2ecf20Sopenharmony_ci dev->name, i, errno, buf); 3288c2ecf20Sopenharmony_ci } else 3298c2ecf20Sopenharmony_ci printf ("%s test %d, %4d.%.06d secs\n", dev->name, i, 3308c2ecf20Sopenharmony_ci (int) dev->param.duration.tv_sec, 3318c2ecf20Sopenharmony_ci (int) dev->param.duration.tv_usec); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci fflush (stdout); 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci if (dev->forever) 3368c2ecf20Sopenharmony_ci goto restart; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci close (fd); 3398c2ecf20Sopenharmony_ci return arg; 3408c2ecf20Sopenharmony_ci} 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_cistatic const char *usb_dir_find(void) 3438c2ecf20Sopenharmony_ci{ 3448c2ecf20Sopenharmony_ci static char udev_usb_path[] = "/dev/bus/usb"; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci if (access(udev_usb_path, F_OK) == 0) 3478c2ecf20Sopenharmony_ci return udev_usb_path; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci return NULL; 3508c2ecf20Sopenharmony_ci} 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_cistatic int parse_num(unsigned *num, const char *str) 3538c2ecf20Sopenharmony_ci{ 3548c2ecf20Sopenharmony_ci unsigned long val; 3558c2ecf20Sopenharmony_ci char *end; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci errno = 0; 3588c2ecf20Sopenharmony_ci val = strtoul(str, &end, 0); 3598c2ecf20Sopenharmony_ci if (errno || *end || val > UINT_MAX) 3608c2ecf20Sopenharmony_ci return -1; 3618c2ecf20Sopenharmony_ci *num = val; 3628c2ecf20Sopenharmony_ci return 0; 3638c2ecf20Sopenharmony_ci} 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ciint main (int argc, char **argv) 3668c2ecf20Sopenharmony_ci{ 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci int c; 3698c2ecf20Sopenharmony_ci struct testdev *entry; 3708c2ecf20Sopenharmony_ci char *device; 3718c2ecf20Sopenharmony_ci const char *usb_dir = NULL; 3728c2ecf20Sopenharmony_ci int all = 0, forever = 0, not = 0; 3738c2ecf20Sopenharmony_ci int test = -1 /* all */; 3748c2ecf20Sopenharmony_ci struct usbtest_param param; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci /* pick defaults that works with all speeds, without short packets. 3778c2ecf20Sopenharmony_ci * 3788c2ecf20Sopenharmony_ci * Best per-frame data rates: 3798c2ecf20Sopenharmony_ci * super speed,bulk 1024 * 16 * 8 = 131072 3808c2ecf20Sopenharmony_ci * interrupt 1024 * 3 * 8 = 24576 3818c2ecf20Sopenharmony_ci * high speed, bulk 512 * 13 * 8 = 53248 3828c2ecf20Sopenharmony_ci * interrupt 1024 * 3 * 8 = 24576 3838c2ecf20Sopenharmony_ci * full speed, bulk/intr 64 * 19 = 1216 3848c2ecf20Sopenharmony_ci * interrupt 64 * 1 = 64 3858c2ecf20Sopenharmony_ci * low speed, interrupt 8 * 1 = 8 3868c2ecf20Sopenharmony_ci */ 3878c2ecf20Sopenharmony_ci param.iterations = 1000; 3888c2ecf20Sopenharmony_ci param.length = 1024; 3898c2ecf20Sopenharmony_ci param.vary = 1024; 3908c2ecf20Sopenharmony_ci param.sglen = 32; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci /* for easy use when hotplugging */ 3938c2ecf20Sopenharmony_ci device = getenv ("DEVICE"); 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci while ((c = getopt (argc, argv, "D:aA:c:g:hlns:t:v:")) != EOF) 3968c2ecf20Sopenharmony_ci switch (c) { 3978c2ecf20Sopenharmony_ci case 'D': /* device, if only one */ 3988c2ecf20Sopenharmony_ci device = optarg; 3998c2ecf20Sopenharmony_ci continue; 4008c2ecf20Sopenharmony_ci case 'A': /* use all devices with specified USB dir */ 4018c2ecf20Sopenharmony_ci usb_dir = optarg; 4028c2ecf20Sopenharmony_ci /* FALL THROUGH */ 4038c2ecf20Sopenharmony_ci case 'a': /* use all devices */ 4048c2ecf20Sopenharmony_ci device = NULL; 4058c2ecf20Sopenharmony_ci all = 1; 4068c2ecf20Sopenharmony_ci continue; 4078c2ecf20Sopenharmony_ci case 'c': /* count iterations */ 4088c2ecf20Sopenharmony_ci if (parse_num(¶m.iterations, optarg)) 4098c2ecf20Sopenharmony_ci goto usage; 4108c2ecf20Sopenharmony_ci continue; 4118c2ecf20Sopenharmony_ci case 'g': /* scatter/gather entries */ 4128c2ecf20Sopenharmony_ci if (parse_num(¶m.sglen, optarg)) 4138c2ecf20Sopenharmony_ci goto usage; 4148c2ecf20Sopenharmony_ci continue; 4158c2ecf20Sopenharmony_ci case 'l': /* loop forever */ 4168c2ecf20Sopenharmony_ci forever = 1; 4178c2ecf20Sopenharmony_ci continue; 4188c2ecf20Sopenharmony_ci case 'n': /* no test running! */ 4198c2ecf20Sopenharmony_ci not = 1; 4208c2ecf20Sopenharmony_ci continue; 4218c2ecf20Sopenharmony_ci case 's': /* size of packet */ 4228c2ecf20Sopenharmony_ci if (parse_num(¶m.length, optarg)) 4238c2ecf20Sopenharmony_ci goto usage; 4248c2ecf20Sopenharmony_ci continue; 4258c2ecf20Sopenharmony_ci case 't': /* run just one test */ 4268c2ecf20Sopenharmony_ci test = atoi (optarg); 4278c2ecf20Sopenharmony_ci if (test < 0) 4288c2ecf20Sopenharmony_ci goto usage; 4298c2ecf20Sopenharmony_ci continue; 4308c2ecf20Sopenharmony_ci case 'v': /* vary packet size by ... */ 4318c2ecf20Sopenharmony_ci if (parse_num(¶m.vary, optarg)) 4328c2ecf20Sopenharmony_ci goto usage; 4338c2ecf20Sopenharmony_ci continue; 4348c2ecf20Sopenharmony_ci case '?': 4358c2ecf20Sopenharmony_ci case 'h': 4368c2ecf20Sopenharmony_ci default: 4378c2ecf20Sopenharmony_ciusage: 4388c2ecf20Sopenharmony_ci fprintf (stderr, 4398c2ecf20Sopenharmony_ci "usage: %s [options]\n" 4408c2ecf20Sopenharmony_ci "Options:\n" 4418c2ecf20Sopenharmony_ci "\t-D dev only test specific device\n" 4428c2ecf20Sopenharmony_ci "\t-A usb-dir\n" 4438c2ecf20Sopenharmony_ci "\t-a test all recognized devices\n" 4448c2ecf20Sopenharmony_ci "\t-l loop forever(for stress test)\n" 4458c2ecf20Sopenharmony_ci "\t-t testnum only run specified case\n" 4468c2ecf20Sopenharmony_ci "\t-n no test running, show devices to be tested\n" 4478c2ecf20Sopenharmony_ci "Case arguments:\n" 4488c2ecf20Sopenharmony_ci "\t-c iterations default 1000\n" 4498c2ecf20Sopenharmony_ci "\t-s transfer length default 1024\n" 4508c2ecf20Sopenharmony_ci "\t-g sglen default 32\n" 4518c2ecf20Sopenharmony_ci "\t-v vary default 1024\n", 4528c2ecf20Sopenharmony_ci argv[0]); 4538c2ecf20Sopenharmony_ci return 1; 4548c2ecf20Sopenharmony_ci } 4558c2ecf20Sopenharmony_ci if (optind != argc) 4568c2ecf20Sopenharmony_ci goto usage; 4578c2ecf20Sopenharmony_ci if (!all && !device) { 4588c2ecf20Sopenharmony_ci fprintf (stderr, "must specify '-a' or '-D dev', " 4598c2ecf20Sopenharmony_ci "or DEVICE=/dev/bus/usb/BBB/DDD in env\n"); 4608c2ecf20Sopenharmony_ci goto usage; 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci /* Find usb device subdirectory */ 4648c2ecf20Sopenharmony_ci if (!usb_dir) { 4658c2ecf20Sopenharmony_ci usb_dir = usb_dir_find(); 4668c2ecf20Sopenharmony_ci if (!usb_dir) { 4678c2ecf20Sopenharmony_ci fputs ("USB device files are missing\n", stderr); 4688c2ecf20Sopenharmony_ci return -1; 4698c2ecf20Sopenharmony_ci } 4708c2ecf20Sopenharmony_ci } 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci /* collect and list the test devices */ 4738c2ecf20Sopenharmony_ci if (ftw (usb_dir, find_testdev, 3) != 0) { 4748c2ecf20Sopenharmony_ci fputs ("ftw failed; are USB device files missing?\n", stderr); 4758c2ecf20Sopenharmony_ci return -1; 4768c2ecf20Sopenharmony_ci } 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci /* quit, run single test, or create test threads */ 4798c2ecf20Sopenharmony_ci if (!testdevs && !device) { 4808c2ecf20Sopenharmony_ci fputs ("no test devices recognized\n", stderr); 4818c2ecf20Sopenharmony_ci return -1; 4828c2ecf20Sopenharmony_ci } 4838c2ecf20Sopenharmony_ci if (not) 4848c2ecf20Sopenharmony_ci return 0; 4858c2ecf20Sopenharmony_ci if (testdevs && testdevs->next == 0 && !device) 4868c2ecf20Sopenharmony_ci device = testdevs->name; 4878c2ecf20Sopenharmony_ci for (entry = testdevs; entry; entry = entry->next) { 4888c2ecf20Sopenharmony_ci int status; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci entry->param = param; 4918c2ecf20Sopenharmony_ci entry->forever = forever; 4928c2ecf20Sopenharmony_ci entry->test = test; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci if (device) { 4958c2ecf20Sopenharmony_ci if (strcmp (entry->name, device)) 4968c2ecf20Sopenharmony_ci continue; 4978c2ecf20Sopenharmony_ci return handle_testdev (entry) != entry; 4988c2ecf20Sopenharmony_ci } 4998c2ecf20Sopenharmony_ci status = pthread_create (&entry->thread, 0, handle_testdev, entry); 5008c2ecf20Sopenharmony_ci if (status) 5018c2ecf20Sopenharmony_ci perror ("pthread_create"); 5028c2ecf20Sopenharmony_ci } 5038c2ecf20Sopenharmony_ci if (device) { 5048c2ecf20Sopenharmony_ci struct testdev dev; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci /* kernel can recognize test devices we don't */ 5078c2ecf20Sopenharmony_ci fprintf (stderr, "%s: %s may see only control tests\n", 5088c2ecf20Sopenharmony_ci argv [0], device); 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci memset (&dev, 0, sizeof dev); 5118c2ecf20Sopenharmony_ci dev.name = device; 5128c2ecf20Sopenharmony_ci dev.param = param; 5138c2ecf20Sopenharmony_ci dev.forever = forever; 5148c2ecf20Sopenharmony_ci dev.test = test; 5158c2ecf20Sopenharmony_ci return handle_testdev (&dev) != &dev; 5168c2ecf20Sopenharmony_ci } 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci /* wait for tests to complete */ 5198c2ecf20Sopenharmony_ci for (entry = testdevs; entry; entry = entry->next) { 5208c2ecf20Sopenharmony_ci void *retval; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci if (pthread_join (entry->thread, &retval)) 5238c2ecf20Sopenharmony_ci perror ("pthread_join"); 5248c2ecf20Sopenharmony_ci /* testing errors discarded! */ 5258c2ecf20Sopenharmony_ci } 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci return 0; 5288c2ecf20Sopenharmony_ci} 529