diff options
author | Michal Nazarewicz <m.nazarewicz@samsung.com> | 2010-05-05 06:53:18 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2010-05-20 16:21:43 -0400 |
commit | 5bc9661cba04ff3704e704a06367d4fe96d8dd33 (patch) | |
tree | c8c3103bbe215607985ae10fb3832d4605cb33f2 /tools/usb | |
parent | 2201d6b1620a1d9feac78e9ff12b7246227c8b17 (diff) |
USB: testusb: testusb compatibility with FunctionFS gadget
The FunctionFS gadget may provide the source/sink interface
not as the first interface (with id == 0) but some different
interface hence a code to find the interface number is
required.
(Note that you will still configure the gadget to report
idProduct == 0xa4a4 (an "echo 0xa4a4
>/sys/module/g_ffs/parameters/usb_product" should suffice) or
configure host to handle 0x0525:0xa4ac devices using the
usbtest driver.)
Signed-off-by: Michal Nazarewicz <m.nazarewicz@samsung.com>
Cc: Kyungmin Park <kyungmin.park@samsung.com>
Cc: Marek Szyprowski <m.szyprowski@samsung.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'tools/usb')
-rw-r--r-- | tools/usb/testusb.c | 260 |
1 files changed, 190 insertions, 70 deletions
diff --git a/tools/usb/testusb.c b/tools/usb/testusb.c index 1f372983be6..f08e8946384 100644 --- a/tools/usb/testusb.c +++ b/tools/usb/testusb.c | |||
@@ -1,7 +1,9 @@ | |||
1 | /* $(CROSS_COMPILE)cc -Wall -g -lpthread -o testusb testusb.c */ | 1 | /* $(CROSS_COMPILE)cc -Wall -Wextra -g -lpthread -o testusb testusb.c */ |
2 | 2 | ||
3 | /* | 3 | /* |
4 | * Copyright (c) 2002 by David Brownell | 4 | * Copyright (c) 2002 by David Brownell |
5 | * Copyright (c) 2010 by Samsung Electronics | ||
6 | * Author: Michal Nazarewicz <m.nazarewicz@samsung.com> | ||
5 | * | 7 | * |
6 | * This program is free software; you can redistribute it and/or modify it | 8 | * This program is free software; you can redistribute it and/or modify it |
7 | * under the terms of the GNU General Public License as published by the | 9 | * under the terms of the GNU General Public License as published by the |
@@ -18,6 +20,16 @@ | |||
18 | * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | 20 | * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
19 | */ | 21 | */ |
20 | 22 | ||
23 | /* | ||
24 | * This program issues ioctls to perform the tests implemented by the | ||
25 | * kernel driver. It can generate a variety of transfer patterns; you | ||
26 | * should make sure to test both regular streaming and mixes of | ||
27 | * transfer sizes (including short transfers). | ||
28 | * | ||
29 | * For more information on how this can be used and on USB testing | ||
30 | * refer to <URL:http://www.linux-usb.org/usbtest/>. | ||
31 | */ | ||
32 | |||
21 | #include <stdio.h> | 33 | #include <stdio.h> |
22 | #include <string.h> | 34 | #include <string.h> |
23 | #include <ftw.h> | 35 | #include <ftw.h> |
@@ -25,6 +37,7 @@ | |||
25 | #include <pthread.h> | 37 | #include <pthread.h> |
26 | #include <unistd.h> | 38 | #include <unistd.h> |
27 | #include <errno.h> | 39 | #include <errno.h> |
40 | #include <limits.h> | ||
28 | 41 | ||
29 | #include <sys/types.h> | 42 | #include <sys/types.h> |
30 | #include <sys/stat.h> | 43 | #include <sys/stat.h> |
@@ -56,6 +69,13 @@ struct usbtest_param { | |||
56 | 69 | ||
57 | /* #include <linux/usb_ch9.h> */ | 70 | /* #include <linux/usb_ch9.h> */ |
58 | 71 | ||
72 | #define USB_DT_DEVICE 0x01 | ||
73 | #define USB_DT_INTERFACE 0x04 | ||
74 | |||
75 | #define USB_CLASS_PER_INTERFACE 0 /* for DeviceClass */ | ||
76 | #define USB_CLASS_VENDOR_SPEC 0xff | ||
77 | |||
78 | |||
59 | struct usb_device_descriptor { | 79 | struct usb_device_descriptor { |
60 | __u8 bLength; | 80 | __u8 bLength; |
61 | __u8 bDescriptorType; | 81 | __u8 bDescriptorType; |
@@ -73,6 +93,19 @@ struct usb_device_descriptor { | |||
73 | __u8 bNumConfigurations; | 93 | __u8 bNumConfigurations; |
74 | } __attribute__ ((packed)); | 94 | } __attribute__ ((packed)); |
75 | 95 | ||
96 | struct usb_interface_descriptor { | ||
97 | __u8 bLength; | ||
98 | __u8 bDescriptorType; | ||
99 | |||
100 | __u8 bInterfaceNumber; | ||
101 | __u8 bAlternateSetting; | ||
102 | __u8 bNumEndpoints; | ||
103 | __u8 bInterfaceClass; | ||
104 | __u8 bInterfaceSubClass; | ||
105 | __u8 bInterfaceProtocol; | ||
106 | __u8 iInterface; | ||
107 | } __attribute__ ((packed)); | ||
108 | |||
76 | enum usb_device_speed { | 109 | enum usb_device_speed { |
77 | USB_SPEED_UNKNOWN = 0, /* enumerating */ | 110 | USB_SPEED_UNKNOWN = 0, /* enumerating */ |
78 | USB_SPEED_LOW, USB_SPEED_FULL, /* usb 1.1 */ | 111 | USB_SPEED_LOW, USB_SPEED_FULL, /* usb 1.1 */ |
@@ -105,11 +138,42 @@ struct testdev { | |||
105 | }; | 138 | }; |
106 | static struct testdev *testdevs; | 139 | static struct testdev *testdevs; |
107 | 140 | ||
108 | static int is_testdev (struct usb_device_descriptor *dev) | 141 | static int testdev_ffs_ifnum(FILE *fd) |
109 | { | 142 | { |
143 | union { | ||
144 | char buf[255]; | ||
145 | struct usb_interface_descriptor intf; | ||
146 | } u; | ||
147 | |||
148 | for (;;) { | ||
149 | if (fread(u.buf, 1, 1, fd) != 1) | ||
150 | return -1; | ||
151 | if (fread(u.buf + 1, (unsigned char)u.buf[0] - 1, 1, fd) != 1) | ||
152 | return -1; | ||
153 | |||
154 | if (u.intf.bLength == sizeof u.intf | ||
155 | && u.intf.bDescriptorType == USB_DT_INTERFACE | ||
156 | && u.intf.bNumEndpoints == 2 | ||
157 | && u.intf.bInterfaceClass == USB_CLASS_VENDOR_SPEC | ||
158 | && u.intf.bInterfaceSubClass == 0 | ||
159 | && u.intf.bInterfaceProtocol == 0) | ||
160 | return (unsigned char)u.intf.bInterfaceNumber; | ||
161 | } | ||
162 | } | ||
163 | |||
164 | static int testdev_ifnum(FILE *fd) | ||
165 | { | ||
166 | struct usb_device_descriptor dev; | ||
167 | |||
168 | if (fread(&dev, sizeof dev, 1, fd) != 1) | ||
169 | return -1; | ||
170 | |||
171 | if (dev.bLength != sizeof dev || dev.bDescriptorType != USB_DT_DEVICE) | ||
172 | return -1; | ||
173 | |||
110 | /* FX2 with (tweaked) bulksrc firmware */ | 174 | /* FX2 with (tweaked) bulksrc firmware */ |
111 | if (dev->idVendor == 0x0547 && dev->idProduct == 0x1002) | 175 | if (dev.idVendor == 0x0547 && dev.idProduct == 0x1002) |
112 | return 1; | 176 | return 0; |
113 | 177 | ||
114 | /*----------------------------------------------------*/ | 178 | /*----------------------------------------------------*/ |
115 | 179 | ||
@@ -124,95 +188,108 @@ static int is_testdev (struct usb_device_descriptor *dev) | |||
124 | */ | 188 | */ |
125 | 189 | ||
126 | /* generic EZ-USB FX controller */ | 190 | /* generic EZ-USB FX controller */ |
127 | if (dev->idVendor == 0x0547 && dev->idProduct == 0x2235) | 191 | if (dev.idVendor == 0x0547 && dev.idProduct == 0x2235) |
128 | return 1; | 192 | return 0; |
129 | 193 | ||
130 | /* generic EZ-USB FX2 controller */ | 194 | /* generic EZ-USB FX2 controller */ |
131 | if (dev->idVendor == 0x04b4 && dev->idProduct == 0x8613) | 195 | if (dev.idVendor == 0x04b4 && dev.idProduct == 0x8613) |
132 | return 1; | 196 | return 0; |
133 | 197 | ||
134 | /* CY3671 development board with EZ-USB FX */ | 198 | /* CY3671 development board with EZ-USB FX */ |
135 | if (dev->idVendor == 0x0547 && dev->idProduct == 0x0080) | 199 | if (dev.idVendor == 0x0547 && dev.idProduct == 0x0080) |
136 | return 1; | 200 | return 0; |
137 | 201 | ||
138 | /* Keyspan 19Qi uses an21xx (original EZ-USB) */ | 202 | /* Keyspan 19Qi uses an21xx (original EZ-USB) */ |
139 | if (dev->idVendor == 0x06cd && dev->idProduct == 0x010b) | 203 | if (dev.idVendor == 0x06cd && dev.idProduct == 0x010b) |
140 | return 1; | 204 | return 0; |
141 | 205 | ||
142 | /*----------------------------------------------------*/ | 206 | /*----------------------------------------------------*/ |
143 | 207 | ||
144 | /* "gadget zero", Linux-USB test software */ | 208 | /* "gadget zero", Linux-USB test software */ |
145 | if (dev->idVendor == 0x0525 && dev->idProduct == 0xa4a0) | 209 | if (dev.idVendor == 0x0525 && dev.idProduct == 0xa4a0) |
146 | return 1; | 210 | return 0; |
147 | 211 | ||
148 | /* user mode subset of that */ | 212 | /* user mode subset of that */ |
149 | if (dev->idVendor == 0x0525 && dev->idProduct == 0xa4a4) | 213 | if (dev.idVendor == 0x0525 && dev.idProduct == 0xa4a4) |
150 | return 1; | 214 | return testdev_ffs_ifnum(fd); |
215 | /* return 0; */ | ||
151 | 216 | ||
152 | /* iso version of usermode code */ | 217 | /* iso version of usermode code */ |
153 | if (dev->idVendor == 0x0525 && dev->idProduct == 0xa4a3) | 218 | if (dev.idVendor == 0x0525 && dev.idProduct == 0xa4a3) |
154 | return 1; | 219 | return 0; |
155 | 220 | ||
156 | /* some GPL'd test firmware uses these IDs */ | 221 | /* some GPL'd test firmware uses these IDs */ |
157 | 222 | ||
158 | if (dev->idVendor == 0xfff0 && dev->idProduct == 0xfff0) | 223 | if (dev.idVendor == 0xfff0 && dev.idProduct == 0xfff0) |
159 | return 1; | 224 | return 0; |
160 | 225 | ||
161 | /*----------------------------------------------------*/ | 226 | /*----------------------------------------------------*/ |
162 | 227 | ||
163 | /* iBOT2 high speed webcam */ | 228 | /* iBOT2 high speed webcam */ |
164 | if (dev->idVendor == 0x0b62 && dev->idProduct == 0x0059) | 229 | if (dev.idVendor == 0x0b62 && dev.idProduct == 0x0059) |
165 | return 1; | 230 | return 0; |
166 | 231 | ||
167 | return 0; | 232 | /*----------------------------------------------------*/ |
233 | |||
234 | /* the FunctionFS gadget can have the source/sink interface | ||
235 | * anywhere. We look for an interface descriptor that match | ||
236 | * what we expect. We ignore configuratiens thou. */ | ||
237 | |||
238 | if (dev.idVendor == 0x0525 && dev.idProduct == 0xa4ac | ||
239 | && (dev.bDeviceClass == USB_CLASS_PER_INTERFACE | ||
240 | || dev.bDeviceClass == USB_CLASS_VENDOR_SPEC)) | ||
241 | return testdev_ffs_ifnum(fd); | ||
242 | |||
243 | return -1; | ||
168 | } | 244 | } |
169 | 245 | ||
170 | static int find_testdev (const char *name, const struct stat *sb, int flag) | 246 | static int find_testdev(const char *name, const struct stat *sb, int flag) |
171 | { | 247 | { |
172 | int fd; | 248 | FILE *fd; |
173 | struct usb_device_descriptor dev; | 249 | int ifnum; |
250 | struct testdev *entry; | ||
251 | |||
252 | (void)sb; /* unused */ | ||
174 | 253 | ||
175 | if (flag != FTW_F) | 254 | if (flag != FTW_F) |
176 | return 0; | 255 | return 0; |
177 | /* ignore /proc/bus/usb/{devices,drivers} */ | 256 | /* ignore /proc/bus/usb/{devices,drivers} */ |
178 | if (strrchr (name, '/')[1] == 'd') | 257 | if (strrchr(name, '/')[1] == 'd') |
179 | return 0; | 258 | return 0; |
180 | 259 | ||
181 | if ((fd = open (name, O_RDONLY)) < 0) { | 260 | fd = fopen(name, "rb"); |
182 | perror ("can't open dev file r/o"); | 261 | if (!fd) { |
262 | perror(name); | ||
183 | return 0; | 263 | return 0; |
184 | } | 264 | } |
185 | if (read (fd, &dev, sizeof dev) != sizeof dev) | ||
186 | fputs ("short devfile read!\n", stderr); | ||
187 | else if (is_testdev (&dev)) { | ||
188 | struct testdev *entry; | ||
189 | |||
190 | if ((entry = calloc (1, sizeof *entry)) == 0) { | ||
191 | fputs ("no mem!\n", stderr); | ||
192 | goto done; | ||
193 | } | ||
194 | entry->name = strdup (name); | ||
195 | if (!entry->name) { | ||
196 | free (entry); | ||
197 | goto done; | ||
198 | } | ||
199 | 265 | ||
200 | // FIXME better to look at each interface and ask if it's | 266 | ifnum = testdev_ifnum(fd); |
201 | // bound to 'usbtest', rather than assume interface 0 | 267 | fclose(fd); |
202 | entry->ifnum = 0; | 268 | if (ifnum < 0) |
203 | 269 | return 0; | |
204 | // FIXME ask usbfs what speed; update USBDEVFS_CONNECTINFO | ||
205 | // so it tells about high speed etc | ||
206 | 270 | ||
207 | fprintf (stderr, "%s speed\t%s\n", | 271 | entry = calloc(1, sizeof *entry); |
208 | speed (entry->speed), entry->name); | 272 | if (!entry) |
273 | goto nomem; | ||
209 | 274 | ||
210 | entry->next = testdevs; | 275 | entry->name = strdup(name); |
211 | testdevs = entry; | 276 | if (!entry->name) { |
277 | free(entry); | ||
278 | nomem: | ||
279 | perror("malloc"); | ||
280 | return 0; | ||
212 | } | 281 | } |
213 | 282 | ||
214 | done: | 283 | entry->ifnum = ifnum; |
215 | close (fd); | 284 | |
285 | /* FIXME ask usbfs what speed; update USBDEVFS_CONNECTINFO so | ||
286 | * it tells about high speed etc */ | ||
287 | |||
288 | fprintf(stderr, "%s speed\t%s\t%u\n", | ||
289 | speed(entry->speed), entry->name, entry->ifnum); | ||
290 | |||
291 | entry->next = testdevs; | ||
292 | testdevs = entry; | ||
216 | return 0; | 293 | return 0; |
217 | } | 294 | } |
218 | 295 | ||
@@ -277,11 +354,51 @@ restart: | |||
277 | return arg; | 354 | return arg; |
278 | } | 355 | } |
279 | 356 | ||
357 | static const char *usbfs_dir_find(void) | ||
358 | { | ||
359 | static char usbfs_path_0[] = "/dev/usb/devices"; | ||
360 | static char usbfs_path_1[] = "/proc/bus/usb/devices"; | ||
361 | |||
362 | static char *const usbfs_paths[] = { | ||
363 | usbfs_path_0, usbfs_path_1 | ||
364 | }; | ||
365 | |||
366 | static char *const * | ||
367 | end = usbfs_paths + sizeof usbfs_paths / sizeof *usbfs_paths; | ||
368 | |||
369 | char *const *it = usbfs_paths; | ||
370 | do { | ||
371 | int fd = open(*it, O_RDONLY); | ||
372 | close(fd); | ||
373 | if (fd >= 0) { | ||
374 | strrchr(*it, '/')[0] = '\0'; | ||
375 | return *it; | ||
376 | } | ||
377 | } while (++it != end); | ||
378 | |||
379 | return NULL; | ||
380 | } | ||
381 | |||
382 | static int parse_num(unsigned *num, const char *str) | ||
383 | { | ||
384 | unsigned long val; | ||
385 | char *end; | ||
386 | |||
387 | errno = 0; | ||
388 | val = strtoul(str, &end, 0); | ||
389 | if (errno || *end || val > UINT_MAX) | ||
390 | return -1; | ||
391 | *num = val; | ||
392 | return 0; | ||
393 | } | ||
394 | |||
280 | int main (int argc, char **argv) | 395 | int main (int argc, char **argv) |
281 | { | 396 | { |
397 | |||
282 | int c; | 398 | int c; |
283 | struct testdev *entry; | 399 | struct testdev *entry; |
284 | char *device; | 400 | char *device; |
401 | const char *usbfs_dir = NULL; | ||
285 | int all = 0, forever = 0, not = 0; | 402 | int all = 0, forever = 0, not = 0; |
286 | int test = -1 /* all */; | 403 | int test = -1 /* all */; |
287 | struct usbtest_param param; | 404 | struct usbtest_param param; |
@@ -303,23 +420,24 @@ int main (int argc, char **argv) | |||
303 | /* for easy use when hotplugging */ | 420 | /* for easy use when hotplugging */ |
304 | device = getenv ("DEVICE"); | 421 | device = getenv ("DEVICE"); |
305 | 422 | ||
306 | while ((c = getopt (argc, argv, "D:ac:g:hns:t:v:")) != EOF) | 423 | while ((c = getopt (argc, argv, "D:aA:c:g:hns:t:v:")) != EOF) |
307 | switch (c) { | 424 | switch (c) { |
308 | case 'D': /* device, if only one */ | 425 | case 'D': /* device, if only one */ |
309 | device = optarg; | 426 | device = optarg; |
310 | continue; | 427 | continue; |
428 | case 'A': /* use all devices with specified usbfs dir */ | ||
429 | usbfs_dir = optarg; | ||
430 | /* FALL THROUGH */ | ||
311 | case 'a': /* use all devices */ | 431 | case 'a': /* use all devices */ |
312 | device = 0; | 432 | device = NULL; |
313 | all = 1; | 433 | all = 1; |
314 | continue; | 434 | continue; |
315 | case 'c': /* count iterations */ | 435 | case 'c': /* count iterations */ |
316 | param.iterations = atoi (optarg); | 436 | if (parse_num(¶m.iterations, optarg)) |
317 | if (param.iterations < 0) | ||
318 | goto usage; | 437 | goto usage; |
319 | continue; | 438 | continue; |
320 | case 'g': /* scatter/gather entries */ | 439 | case 'g': /* scatter/gather entries */ |
321 | param.sglen = atoi (optarg); | 440 | if (parse_num(¶m.sglen, optarg)) |
322 | if (param.sglen < 0) | ||
323 | goto usage; | 441 | goto usage; |
324 | continue; | 442 | continue; |
325 | case 'l': /* loop forever */ | 443 | case 'l': /* loop forever */ |
@@ -329,8 +447,7 @@ int main (int argc, char **argv) | |||
329 | not = 1; | 447 | not = 1; |
330 | continue; | 448 | continue; |
331 | case 's': /* size of packet */ | 449 | case 's': /* size of packet */ |
332 | param.length = atoi (optarg); | 450 | if (parse_num(¶m.length, optarg)) |
333 | if (param.length < 0) | ||
334 | goto usage; | 451 | goto usage; |
335 | continue; | 452 | continue; |
336 | case 't': /* run just one test */ | 453 | case 't': /* run just one test */ |
@@ -339,15 +456,14 @@ int main (int argc, char **argv) | |||
339 | goto usage; | 456 | goto usage; |
340 | continue; | 457 | continue; |
341 | case 'v': /* vary packet size by ... */ | 458 | case 'v': /* vary packet size by ... */ |
342 | param.vary = atoi (optarg); | 459 | if (parse_num(¶m.vary, optarg)) |
343 | if (param.vary < 0) | ||
344 | goto usage; | 460 | goto usage; |
345 | continue; | 461 | continue; |
346 | case '?': | 462 | case '?': |
347 | case 'h': | 463 | case 'h': |
348 | default: | 464 | default: |
349 | usage: | 465 | usage: |
350 | fprintf (stderr, "usage: %s [-an] [-D dev]\n" | 466 | fprintf (stderr, "usage: %s [-n] [-D dev | -a | -A usbfs-dir]\n" |
351 | "\t[-c iterations] [-t testnum]\n" | 467 | "\t[-c iterations] [-t testnum]\n" |
352 | "\t[-s packetsize] [-g sglen] [-v vary]\n", | 468 | "\t[-s packetsize] [-g sglen] [-v vary]\n", |
353 | argv [0]); | 469 | argv [0]); |
@@ -361,13 +477,17 @@ usage: | |||
361 | goto usage; | 477 | goto usage; |
362 | } | 478 | } |
363 | 479 | ||
364 | if ((c = open ("/proc/bus/usb/devices", O_RDONLY)) < 0) { | 480 | /* Find usbfs mount point */ |
365 | fputs ("usbfs files are missing\n", stderr); | 481 | if (!usbfs_dir) { |
366 | return -1; | 482 | usbfs_dir = usbfs_dir_find(); |
483 | if (!usbfs_dir) { | ||
484 | fputs ("usbfs files are missing\n", stderr); | ||
485 | return -1; | ||
486 | } | ||
367 | } | 487 | } |
368 | 488 | ||
369 | /* collect and list the test devices */ | 489 | /* collect and list the test devices */ |
370 | if (ftw ("/proc/bus/usb", find_testdev, 3) != 0) { | 490 | if (ftw (usbfs_dir, find_testdev, 3) != 0) { |
371 | fputs ("ftw failed; is usbfs missing?\n", stderr); | 491 | fputs ("ftw failed; is usbfs missing?\n", stderr); |
372 | return -1; | 492 | return -1; |
373 | } | 493 | } |