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