1/* $FreeBSD$ */
2/*-
3 * Copyright (c) 2007-2010 Hans Petter Selasky. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <stdio.h>
28#include <stdint.h>
29#include <stdlib.h>
30#include <err.h>
31#include <string.h>
32#include <errno.h>
33#include <unistd.h>
34
35#include <sys/sysctl.h>
36#include <sys/time.h>
37
38#include <libusb20.h>
39#include <libusb20_desc.h>
40
41#include <dev/usb/usb_endian.h>
42#include <dev/usb/usb.h>
43#include <dev/usb/usb_cdc.h>
44
45#include "usbtest.h"
46
47static void
48set_ctrl_ep_fail(int bus, int dev, int ds_fail, int ss_fail)
49{
50	int error;
51
52	error = sysctlbyname("hw.usb.ctrl_bus_fail", NULL, NULL,
53	    &bus, sizeof(bus));
54	if (error != 0)
55		goto emissing;
56
57	error = sysctlbyname("hw.usb.ctrl_dev_fail", NULL, NULL,
58	    &dev, sizeof(dev));
59	if (error != 0)
60		goto emissing;
61
62	error = sysctlbyname("hw.usb.ctrl_ds_fail", NULL, NULL,
63	    &ds_fail, sizeof(ds_fail));
64	if (error != 0)
65		goto emissing;
66
67	error = sysctlbyname("hw.usb.ctrl_ss_fail", NULL, NULL,
68	    &ss_fail, sizeof(ss_fail));
69	if (error != 0)
70		goto emissing;
71	return;
72
73emissing:
74	printf("Cannot set USB sysctl, missing USB_REQ_DEBUG option?\n");
75}
76
77void
78usb_control_ep_error_test(uint16_t vid, uint16_t pid)
79{
80	struct LIBUSB20_CONTROL_SETUP_DECODED req;
81	struct libusb20_device *pdev;
82	uint8_t buffer[256];
83	int error;
84	int fail = 0;
85	int bus;
86	int dev;
87	int cfg;
88
89	pdev = find_usb_device(vid, pid);
90	if (pdev == NULL) {
91		printf("USB device not found\n");
92		return;
93	}
94	error = libusb20_dev_open(pdev, 0);
95	if (error) {
96		printf("Could not open USB device\n");
97		libusb20_dev_free(pdev);
98		return;
99	}
100
101	bus = libusb20_dev_get_bus_number(pdev);
102	dev = libusb20_dev_get_address(pdev);
103
104	for (cfg = 0; cfg != 255; cfg++) {
105
106		LIBUSB20_INIT(LIBUSB20_CONTROL_SETUP, &req);
107		req.bmRequestType = 0x80; /* read */
108		req.bRequest = 0x06; /* descriptor */
109		req.wValue = 0x0200 | cfg; /* config descriptor */
110		req.wIndex = 0;
111		req.wLength = 255;
112
113		printf("Test #%d.1/3 ...\n", cfg);
114
115		set_ctrl_ep_fail(-1,-1,0,0);
116
117		error = libusb20_dev_request_sync(pdev, &req, buffer,
118		    NULL, 1000, 0);
119		if (error != 0) {
120			printf("Last configuration index is: %d\n", cfg - 1);
121			break;
122		}
123
124		printf("Test #%d.2/3 ...\n", cfg);
125
126		set_ctrl_ep_fail(bus,dev,1,1);
127
128		error = libusb20_dev_request_sync(pdev, &req, buffer,
129		    NULL, 1000, 0);
130
131		set_ctrl_ep_fail(-1,-1,0,0);
132
133		error = libusb20_dev_request_sync(pdev, &req, buffer,
134		    NULL, 1000, 0);
135		if (error != 0) {
136			printf("Cannot fetch descriptor (unexpected)\n");
137			fail++;
138		}
139
140		printf("Test #%d.3/3 ...\n", cfg);
141
142		set_ctrl_ep_fail(bus,dev,0,1);
143
144		error = libusb20_dev_request_sync(pdev, &req, buffer,
145		    NULL, 1000, 0);
146
147		set_ctrl_ep_fail(-1,-1,0,0);
148
149		error = libusb20_dev_request_sync(pdev, &req, buffer,
150		    NULL, 1000, 0);
151		if (error != 0) {
152			printf("Cannot fetch descriptor (unexpected)\n");
153			fail++;
154		}
155	}
156
157	libusb20_dev_close(pdev);
158	libusb20_dev_free(pdev);
159
160	printf("Test completed detecting %d failures\nDone\n\n", fail);
161}
162
163void
164usb_get_string_desc_test(uint16_t vid, uint16_t pid)
165{
166	struct libusb20_device *pdev;
167	uint32_t x;
168	uint32_t y;
169	uint32_t valid;
170	uint8_t *buf;
171	int error;
172
173	pdev = find_usb_device(vid, pid);
174	if (pdev == NULL) {
175		printf("USB device not found\n");
176		return;
177	}
178	error = libusb20_dev_open(pdev, 0);
179	if (error) {
180		printf("Could not open USB device\n");
181		libusb20_dev_free(pdev);
182		return;
183	}
184	buf = malloc(256);
185	if (buf == NULL) {
186		printf("Cannot allocate memory\n");
187		libusb20_dev_free(pdev);
188		return;
189	}
190	valid = 0;
191
192	printf("Starting string descriptor test for "
193	    "VID=0x%04x PID=0x%04x\n", vid, pid);
194
195	for (x = 0; x != 256; x++) {
196
197		if (libusb20_dev_check_connected(pdev) != 0) {
198			printf("Device disconnected\n");
199			break;
200		}
201		printf("%d .. ", (int)x);
202
203		fflush(stdout);
204
205		error = libusb20_dev_req_string_simple_sync(pdev, x, buf, 255);
206
207		if (error == 0) {
208			printf("\nINDEX=%d, STRING='%s' (Default language)\n", (int)x, buf);
209			fflush(stdout);
210		} else {
211			continue;
212		}
213
214		valid = 0;
215
216		for (y = 0; y != 65536; y++) {
217
218			if (libusb20_dev_check_connected(pdev) != 0) {
219				printf("Device disconnected\n");
220				break;
221			}
222			error = libusb20_dev_req_string_sync(pdev, x, y, buf, 256);
223			if (error == 0)
224				valid++;
225		}
226
227		printf("String at INDEX=%d responds to %d "
228		    "languages\n", (int)x, (int)valid);
229	}
230
231	printf("\nDone\n");
232
233	free(buf);
234
235	libusb20_dev_free(pdev);
236}
237
238void
239usb_port_reset_test(uint16_t vid, uint16_t pid, uint32_t duration)
240{
241	struct timeval sub_tv;
242	struct timeval ref_tv;
243	struct timeval res_tv;
244
245	struct libusb20_device *pdev;
246
247	int error;
248	int iter;
249	int errcnt;
250
251	time_t last_sec;
252
253	/* sysctl() - no set config */
254
255	pdev = find_usb_device(vid, pid);
256	if (pdev == NULL) {
257		printf("USB device not found\n");
258		return;
259	}
260	error = libusb20_dev_open(pdev, 0);
261	if (error) {
262		libusb20_dev_free(pdev);
263		printf("Could not open USB device\n");
264		return;
265	}
266	iter = 0;
267
268	errcnt = 0;
269
270	gettimeofday(&ref_tv, 0);
271
272	last_sec = ref_tv.tv_sec;
273
274	while (1) {
275
276		gettimeofday(&sub_tv, 0);
277
278		if (last_sec != sub_tv.tv_sec) {
279
280			printf("STATUS: ID=%u, ERR=%u\n",
281			    (int)iter, (int)errcnt);
282
283			fflush(stdout);
284
285			last_sec = sub_tv.tv_sec;
286		}
287		timersub(&sub_tv, &ref_tv, &res_tv);
288
289		if ((res_tv.tv_sec < 0) || (res_tv.tv_sec >= (int)duration))
290			break;
291
292		if (libusb20_dev_reset(pdev)) {
293			errcnt++;
294			usleep(50000);
295		}
296		if (libusb20_dev_check_connected(pdev) != 0) {
297			printf("Device disconnected\n");
298			break;
299		}
300		iter++;
301	}
302
303	libusb20_dev_reset(pdev);
304
305	libusb20_dev_free(pdev);
306}
307
308void
309usb_set_config_test(uint16_t vid, uint16_t pid, uint32_t duration)
310{
311	struct libusb20_device *pdev;
312	struct LIBUSB20_DEVICE_DESC_DECODED *ddesc;
313	int x;
314	int error;
315	int failed;
316	int exp;
317
318	pdev = find_usb_device(vid, pid);
319	if (pdev == NULL) {
320		printf("USB device not found\n");
321		return;
322	}
323	error = libusb20_dev_open(pdev, 0);
324	if (error) {
325		printf("Could not open USB device\n");
326		libusb20_dev_free(pdev);
327		return;
328	}
329	failed = 0;
330
331	printf("Starting set config test for "
332	    "VID=0x%04x PID=0x%04x\n", vid, pid);
333
334	for (x = 255; x > -1; x--) {
335
336		error = libusb20_dev_set_config_index(pdev, x);
337		if (error == 0) {
338			if (x == 255) {
339				printf("Unconfiguring USB device "
340				    "was successful\n");
341			} else {
342				printf("Setting configuration %d "
343				    "was successful\n", x);
344			}
345		} else {
346			failed++;
347		}
348	}
349
350	ddesc = libusb20_dev_get_device_desc(pdev);
351	if (ddesc != NULL)
352		exp = ddesc->bNumConfigurations + 1;
353	else
354		exp = 1;
355
356	printf("\n\n"
357	    "Set configuration summary\n"
358	    "Valid count:  %d/%d %s\n"
359	    "Failed count: %d\n",
360	    256 - failed, exp,
361	    (exp == (256 - failed)) ? "(expected)" : "(unexpected)",
362	    failed);
363
364	libusb20_dev_free(pdev);
365}
366
367void
368usb_get_descriptor_test(uint16_t vid, uint16_t pid, uint32_t duration)
369{
370	struct libusb20_device *pdev;
371
372	pdev = find_usb_device(vid, pid);
373	if (pdev == NULL) {
374		printf("USB device not found\n");
375		return;
376	}
377	libusb20_dev_free(pdev);
378}
379
380void
381usb_suspend_resume_test(uint16_t vid, uint16_t pid, uint32_t duration)
382{
383	struct timeval sub_tv;
384	struct timeval ref_tv;
385	struct timeval res_tv;
386
387	struct libusb20_device *pdev;
388
389	time_t last_sec;
390
391	int iter;
392	int error;
393	int ptimo;
394	int errcnt;
395	int power_old;
396
397	ptimo = 1;			/* second(s) */
398
399	error = sysctlbyname("hw.usb.power_timeout", NULL, NULL,
400	    &ptimo, sizeof(ptimo));
401
402	if (error != 0) {
403		printf("WARNING: Could not set power "
404		    "timeout to 1 (error=%d) \n", errno);
405	}
406	pdev = find_usb_device(vid, pid);
407	if (pdev == NULL) {
408		printf("USB device not found\n");
409		return;
410	}
411	error = libusb20_dev_open(pdev, 0);
412	if (error) {
413		printf("Could not open USB device\n");
414		libusb20_dev_free(pdev);
415		return;
416	}
417	power_old = libusb20_dev_get_power_mode(pdev);
418
419	printf("Starting suspend and resume "
420	    "test for VID=0x%04x PID=0x%04x\n", vid, pid);
421
422	iter = 0;
423	errcnt = 0;
424
425	gettimeofday(&ref_tv, 0);
426
427	last_sec = ref_tv.tv_sec;
428
429	while (1) {
430
431		if (libusb20_dev_check_connected(pdev) != 0) {
432			printf("Device disconnected\n");
433			break;
434		}
435		gettimeofday(&sub_tv, 0);
436
437		if (last_sec != sub_tv.tv_sec) {
438
439			printf("STATUS: ID=%u, ERR=%u\n",
440			    (int)iter, (int)errcnt);
441
442			fflush(stdout);
443
444			last_sec = sub_tv.tv_sec;
445		}
446		timersub(&sub_tv, &ref_tv, &res_tv);
447
448		if ((res_tv.tv_sec < 0) || (res_tv.tv_sec >= (int)duration))
449			break;
450
451		error = libusb20_dev_set_power_mode(pdev, (iter & 1) ?
452		    LIBUSB20_POWER_ON : LIBUSB20_POWER_SAVE);
453
454		if (error)
455			errcnt++;
456
457		/* wait before switching power mode */
458		usleep(4100000 +
459		    (((uint32_t)usb_ts_rand_noise()) % 2000000U));
460
461		iter++;
462	}
463
464	/* restore default power mode */
465	libusb20_dev_set_power_mode(pdev, power_old);
466
467	libusb20_dev_free(pdev);
468}
469
470void
471usb_set_and_clear_stall_test(uint16_t vid, uint16_t pid)
472{
473	struct libusb20_device *pdev;
474	struct libusb20_transfer *pxfer;
475
476	int iter;
477	int error;
478	int errcnt;
479	int ep;
480
481	pdev = find_usb_device(vid, pid);
482	if (pdev == NULL) {
483		printf("USB device not found\n");
484		return;
485	}
486	error = libusb20_dev_open(pdev, 1);
487	if (error) {
488		printf("Could not open USB device\n");
489		libusb20_dev_free(pdev);
490		return;
491	}
492	printf("Starting set and clear stall test "
493	    "for VID=0x%04x PID=0x%04x\n", vid, pid);
494
495	iter = 0;
496	errcnt = 0;
497
498	for (ep = 2; ep != 32; ep++) {
499
500		struct LIBUSB20_CONTROL_SETUP_DECODED setup_set_stall;
501		struct LIBUSB20_CONTROL_SETUP_DECODED setup_get_status;
502
503		uint8_t epno = ((ep / 2) | ((ep & 1) << 7));
504		uint8_t buf[1];
505
506		LIBUSB20_INIT(LIBUSB20_CONTROL_SETUP, &setup_set_stall);
507		setup_set_stall.bmRequestType = 0x02;	/* write endpoint */
508		setup_set_stall.bRequest = 0x03;	/* set feature */
509		setup_set_stall.wValue = 0x00;	/* UF_ENDPOINT_HALT */
510		setup_set_stall.wIndex = epno;
511		setup_set_stall.wLength = 0;
512
513		LIBUSB20_INIT(LIBUSB20_CONTROL_SETUP, &setup_get_status);
514		setup_get_status.bmRequestType = 0x82;	/* read endpoint */
515		setup_get_status.bRequest = 0x00;	/* get status */
516		setup_get_status.wValue = 0x00;
517		setup_get_status.wIndex = epno;
518		setup_get_status.wLength = 1;
519
520		if (libusb20_dev_check_connected(pdev) != 0) {
521			printf("Device disconnected\n");
522			break;
523		}
524		pxfer = libusb20_tr_get_pointer(pdev, 0);
525
526		error = libusb20_tr_open(pxfer, 1, 1, epno);
527
528		if (error != 0) {
529			printf("Endpoint 0x%02x does not exist "
530			    "in current setting. (%s, ignored)\n",
531			    epno, libusb20_strerror(error));
532			continue;
533		}
534		printf("Stalling endpoint 0x%02x\n", epno);
535
536		/* set stall */
537		error = libusb20_dev_request_sync(pdev,
538		    &setup_set_stall, NULL, NULL, 250, 0);
539
540		if (error != 0) {
541			printf("Endpoint 0x%02x does not allow "
542			    "setting of stall. (%s)\n",
543			    epno, libusb20_strerror(error));
544			errcnt++;
545		}
546		/* get EP status */
547		buf[0] = 0;
548		error = libusb20_dev_request_sync(pdev,
549		    &setup_get_status, buf, NULL, 250, 0);
550
551		if (error != 0) {
552			printf("Endpoint 0x%02x does not allow "
553			    "reading status. (%s)\n",
554			    epno, libusb20_strerror(error));
555			errcnt++;
556		} else {
557			if (!(buf[0] & 1)) {
558				printf("Endpoint 0x%02x status is "
559				    "not set to stalled\n", epno);
560				errcnt++;
561			}
562		}
563
564		buf[0] = 0;
565		error = libusb20_tr_bulk_intr_sync(pxfer, buf, 1, NULL, 250);
566		if (error != LIBUSB20_TRANSFER_STALL) {
567			printf("Endpoint 0x%02x does not appear to "
568			    "have stalled. Missing stall PID!\n", epno);
569			errcnt++;
570		}
571		printf("Unstalling endpoint 0x%02x\n", epno);
572
573		libusb20_tr_clear_stall_sync(pxfer);
574
575		/* get EP status */
576		buf[0] = 0;
577		error = libusb20_dev_request_sync(pdev,
578		    &setup_get_status, buf, NULL, 250, 0);
579
580		if (error != 0) {
581			printf("Endpoint 0x%02x does not allow "
582			    "reading status. (%s)\n",
583			    epno, libusb20_strerror(error));
584			errcnt++;
585		} else {
586			if (buf[0] & 1) {
587				printf("Endpoint 0x%02x status is "
588				    "still stalled\n", epno);
589				errcnt++;
590			}
591		}
592
593		libusb20_tr_close(pxfer);
594		iter++;
595	}
596
597	libusb20_dev_free(pdev);
598
599	printf("\n"
600	    "Test summary\n"
601	    "============\n"
602	    "Endpoints tested: %d\n"
603	    "Errors: %d\n", iter, errcnt);
604}
605
606void
607usb_set_alt_interface_test(uint16_t vid, uint16_t pid)
608{
609	struct libusb20_device *pdev;
610	struct libusb20_config *config;
611
612	int iter;
613	int error;
614	int errcnt;
615	int n;
616	int m;
617
618	pdev = find_usb_device(vid, pid);
619	if (pdev == NULL) {
620		printf("USB device not found\n");
621		return;
622	}
623	printf("Starting set alternate setting test "
624	    "for VID=0x%04x PID=0x%04x\n", vid, pid);
625
626	config = libusb20_dev_alloc_config(pdev,
627	    libusb20_dev_get_config_index(pdev));
628	if (config == NULL) {
629		printf("Could not get configuration descriptor\n");
630		libusb20_dev_free(pdev);
631		return;
632	}
633	iter = 0;
634	errcnt = 0;
635
636	for (n = 0; n != config->num_interface; n++) {
637		/* detach kernel driver */
638		libusb20_dev_detach_kernel_driver(pdev, n);
639
640		error = libusb20_dev_open(pdev, 0);
641		if (error)
642			printf("ERROR could not open device\n");
643
644		/* Try the alternate settings */
645		for (m = 0; m != config->interface[n].num_altsetting; m++) {
646
647			iter++;
648
649			if (libusb20_dev_set_alt_index(pdev, n, m + 1)) {
650				printf("ERROR on interface %d alt %d\n", n, m + 1);
651				errcnt++;
652			}
653		}
654
655		/* Restore to default */
656
657		iter++;
658
659		if (libusb20_dev_set_alt_index(pdev, n, 0)) {
660			printf("ERROR on interface %d alt %d\n", n, 0);
661			errcnt++;
662		}
663		libusb20_dev_close(pdev);
664	}
665
666	libusb20_dev_free(pdev);
667
668	printf("\n"
669	    "Test summary\n"
670	    "============\n"
671	    "Interfaces tested: %d\n"
672	    "Errors: %d\n", iter, errcnt);
673}
674