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 struct modem {
47 	struct libusb20_transfer *xfer_in;
48 	struct libusb20_transfer *xfer_out;
49 	struct libusb20_device *usb_dev;
50 
51 	struct bps rx_bytes;
52 	struct bps tx_bytes;
53 	uint32_t c0;
54 	uint32_t c1;
55 	uint32_t out_state;
56 	uint32_t in_last;
57 	uint32_t in_synced;
58 	uint32_t duration;
59 	uint32_t errors;
60 
61 	uint8_t use_vendor_specific;
62 	uint8_t	loop_data;
63 	uint8_t	modem_at_mode;
64 	uint8_t	data_stress_test;
65 	uint8_t	control_ep_test;
66 	uint8_t	usb_iface;
67 	uint8_t	random_tx_length;
68 	uint8_t	random_tx_delay;
69 
70 }	modem;
71 
72 static void
73 set_defaults(struct modem *p)
74 {
75 	memset(p, 0, sizeof(*p));
76 
77 	p->data_stress_test = 1;
78 	p->control_ep_test = 1;
79 	p->duration = 60;		/* seconds */
80 }
81 
82 void
83 do_bps(const char *desc, struct bps *bps, uint32_t len)
84 {
85 	bps->bytes += len;
86 }
87 
88 static void
89 modem_out_state(uint8_t *buf)
90 {
91 	if (modem.modem_at_mode) {
92 		switch (modem.out_state & 3) {
93 		case 0:
94 			*buf = 'A';
95 			break;
96 		case 1:
97 			*buf = 'T';
98 			break;
99 		case 2:
100 			*buf = '\r';
101 			break;
102 		default:
103 			*buf = '\n';
104 			modem.c0++;
105 			break;
106 		}
107 		modem.out_state++;
108 	} else {
109 		*buf = modem.out_state;
110 		modem.out_state++;
111 		modem.out_state %= 255;
112 	}
113 }
114 
115 static void
116 modem_in_state(uint8_t buf, uint32_t counter)
117 {
118 	if ((modem.in_last == 'O') && (buf == 'K')) {
119 		modem.c1++;
120 		modem.in_last = buf;
121 	} else if (buf == modem.in_last) {
122 		modem.c1++;
123 		modem.in_last++;
124 		modem.in_last %= 255;
125 		if (modem.in_synced == 0) {
126 			if (modem.errors < 64) {
127 				printf("Got sync\n");
128 			}
129 			modem.in_synced = 1;
130 		}
131 	} else {
132 		if (modem.in_synced) {
133 			if (modem.errors < 64) {
134 				printf("Lost sync @ %d, 0x%02x != 0x%02x\n",
135 				    counter % 512, buf, modem.in_last);
136 			}
137 			modem.in_synced = 0;
138 			modem.errors++;
139 		}
140 		modem.in_last = buf;
141 		modem.in_last++;
142 		modem.in_last %= 255;
143 	}
144 }
145 
146 static void
147 modem_write(uint8_t *buf, uint32_t len)
148 {
149 	uint32_t n;
150 
151 	for (n = 0; n != len; n++) {
152 		modem_out_state(buf + n);
153 	}
154 
155 	do_bps("transmitted", &modem.tx_bytes, len);
156 }
157 
158 static void
159 modem_read(uint8_t *buf, uint32_t len)
160 {
161 	uint32_t n;
162 
163 	for (n = 0; n != len; n++) {
164 		modem_in_state(buf[n], n);
165 	}
166 
167 	do_bps("received", &modem.rx_bytes, len);
168 }
169 
170 static void
171 usb_modem_control_ep_test(struct modem *p, uint32_t duration, uint8_t flag)
172 {
173 	struct timeval sub_tv;
174 	struct timeval ref_tv;
175 	struct timeval res_tv;
176 	struct LIBUSB20_CONTROL_SETUP_DECODED setup;
177 	struct usb_cdc_abstract_state ast;
178 	struct usb_cdc_line_state ls;
179 	uint16_t feature = UCDC_ABSTRACT_STATE;
180 	uint16_t state = UCDC_DATA_MULTIPLEXED;
181 	uint8_t iface_no;
182 	uint8_t buf[4];
183 	int id = 0;
184 	int iter = 0;
185 
186 	time_t last_sec;
187 
188 	iface_no = p->usb_iface - 1;
189 
190 	gettimeofday(&ref_tv, 0);
191 
192 	last_sec = ref_tv.tv_sec;
193 
194 	printf("\nTest=%d\n", (int)flag);
195 
196 	while (1) {
197 
198 		gettimeofday(&sub_tv, 0);
199 
200 		if (last_sec != sub_tv.tv_sec) {
201 
202 			printf("STATUS: ID=%u, COUNT=%u tests/sec ERR=%u\n",
203 			    (int)id,
204 			    (int)iter,
205 			    (int)p->errors);
206 
207 			fflush(stdout);
208 
209 			last_sec = sub_tv.tv_sec;
210 
211 			id++;
212 
213 			iter = 0;
214 		}
215 		timersub(&sub_tv, &ref_tv, &res_tv);
216 
217 		if ((res_tv.tv_sec < 0) || (res_tv.tv_sec >= (int)duration))
218 			break;
219 
220 		LIBUSB20_INIT(LIBUSB20_CONTROL_SETUP, &setup);
221 
222 		if (flag & 1) {
223 			setup.bmRequestType = UT_READ_CLASS_INTERFACE;
224 			setup.bRequest = 0x03;
225 			setup.wValue = 0x0001;
226 			setup.wIndex = iface_no;
227 			setup.wLength = 0x0002;
228 
229 			if (libusb20_dev_request_sync(p->usb_dev, &setup, buf, NULL, 250, 0)) {
230 				p->errors++;
231 			}
232 		}
233 		if (flag & 2) {
234 			setup.bmRequestType = UT_WRITE_CLASS_INTERFACE;
235 			setup.bRequest = UCDC_SET_COMM_FEATURE;
236 			setup.wValue = feature;
237 			setup.wIndex = iface_no;
238 			setup.wLength = UCDC_ABSTRACT_STATE_LENGTH;
239 			USETW(ast.wState, state);
240 
241 			if (libusb20_dev_request_sync(p->usb_dev, &setup, &ast, NULL, 250, 0)) {
242 				p->errors++;
243 			}
244 		}
245 		if (flag & 4) {
246 			USETDW(ls.dwDTERate, 115200);
247 			ls.bCharFormat = UCDC_STOP_BIT_1;
248 			ls.bParityType = UCDC_PARITY_NONE;
249 			ls.bDataBits = 8;
250 
251 			setup.bmRequestType = UT_WRITE_CLASS_INTERFACE;
252 			setup.bRequest = UCDC_SET_LINE_CODING;
253 			setup.wValue = 0;
254 			setup.wIndex = iface_no;
255 			setup.wLength = sizeof(ls);
256 
257 			if (libusb20_dev_request_sync(p->usb_dev, &setup, &ls, NULL, 250, 0)) {
258 				p->errors++;
259 			}
260 		}
261 		iter++;
262 	}
263 
264 	printf("\nModem control endpoint test done!\n");
265 }
266 
267 static void
268 usb_modem_data_stress_test(struct modem *p, uint32_t duration)
269 {
270 	struct timeval sub_tv;
271 	struct timeval ref_tv;
272 	struct timeval res_tv;
273 
274 	time_t last_sec;
275 
276 	uint8_t in_pending = 0;
277 	uint8_t in_ready = 0;
278 	uint8_t out_pending = 0;
279 
280 	uint32_t id = 0;
281 
282 	uint32_t in_max;
283 	uint32_t out_max;
284 	uint32_t io_max;
285 
286 	uint8_t *in_buffer = 0;
287 	uint8_t *out_buffer = 0;
288 
289 	gettimeofday(&ref_tv, 0);
290 
291 	last_sec = ref_tv.tv_sec;
292 
293 	printf("\n");
294 
295 	in_max = libusb20_tr_get_max_total_length(p->xfer_in);
296 	out_max = libusb20_tr_get_max_total_length(p->xfer_out);
297 
298 	/* get the smallest buffer size and use that */
299 	io_max = (in_max < out_max) ? in_max : out_max;
300 
301 	if (in_max != out_max)
302 		printf("WARNING: Buffer sizes are un-equal: %u vs %u\n", in_max, out_max);
303 
304 	in_buffer = malloc(io_max);
305 	if (in_buffer == NULL)
306 		goto fail;
307 
308 	out_buffer = malloc(io_max);
309 	if (out_buffer == NULL)
310 		goto fail;
311 
312 	while (1) {
313 
314 		gettimeofday(&sub_tv, 0);
315 
316 		if (last_sec != sub_tv.tv_sec) {
317 
318 			printf("STATUS: ID=%u, RX=%u bytes/sec, TX=%u bytes/sec, ERR=%d\n",
319 			    (int)id,
320 			    (int)p->rx_bytes.bytes,
321 			    (int)p->tx_bytes.bytes,
322 			    (int)p->errors);
323 
324 			p->rx_bytes.bytes = 0;
325 			p->tx_bytes.bytes = 0;
326 
327 			fflush(stdout);
328 
329 			last_sec = sub_tv.tv_sec;
330 
331 			id++;
332 		}
333 		timersub(&sub_tv, &ref_tv, &res_tv);
334 
335 		if ((res_tv.tv_sec < 0) || (res_tv.tv_sec >= (int)duration))
336 			break;
337 
338 		libusb20_dev_process(p->usb_dev);
339 
340 		if (!libusb20_tr_pending(p->xfer_in)) {
341 			if (in_pending) {
342 				if (libusb20_tr_get_status(p->xfer_in) == 0) {
343 					modem_read(in_buffer, libusb20_tr_get_length(p->xfer_in, 0));
344 				} else {
345 					p->errors++;
346 					usleep(10000);
347 				}
348 				in_pending = 0;
349 				in_ready = 1;
350 			}
351 			if (p->loop_data == 0) {
352 				libusb20_tr_setup_bulk(p->xfer_in, in_buffer, io_max, 0);
353 				libusb20_tr_start(p->xfer_in);
354 				in_pending = 1;
355 				in_ready = 0;
356 			}
357 		}
358 		if (!libusb20_tr_pending(p->xfer_out)) {
359 
360 			uint32_t len;
361 			uint32_t dly;
362 
363 			if (out_pending) {
364 				if (libusb20_tr_get_status(p->xfer_out) != 0) {
365 					p->errors++;
366 					usleep(10000);
367 				}
368 			}
369 			if (p->random_tx_length) {
370 				len = ((uint32_t)usb_ts_rand_noise()) % ((uint32_t)io_max);
371 			} else {
372 				len = io_max;
373 			}
374 
375 			if (p->random_tx_delay) {
376 				dly = ((uint32_t)usb_ts_rand_noise()) % 16000U;
377 			} else {
378 				dly = 0;
379 			}
380 
381 			if (p->loop_data != 0) {
382 				if (in_ready != 0) {
383 					len = libusb20_tr_get_length(p->xfer_in, 0);
384 					memcpy(out_buffer, in_buffer, len);
385 					in_ready = 0;
386 				} else {
387 					len = io_max + 1;
388 				}
389 				if (!libusb20_tr_pending(p->xfer_in)) {
390 					libusb20_tr_setup_bulk(p->xfer_in, in_buffer, io_max, 0);
391 					libusb20_tr_start(p->xfer_in);
392 					in_pending = 1;
393 				}
394 			} else {
395 				modem_write(out_buffer, len);
396 			}
397 
398 			if (len <= io_max) {
399 				libusb20_tr_setup_bulk(p->xfer_out, out_buffer, len, 0);
400 
401 				if (dly != 0)
402 					usleep(dly);
403 
404 				libusb20_tr_start(p->xfer_out);
405 
406 				out_pending = 1;
407 			}
408 		}
409 		libusb20_dev_wait_process(p->usb_dev, 500);
410 
411 		if (libusb20_dev_check_connected(p->usb_dev) != 0) {
412 			printf("Device disconnected\n");
413 			break;
414 		}
415 	}
416 
417 	libusb20_tr_stop(p->xfer_in);
418 	libusb20_tr_stop(p->xfer_out);
419 
420 	printf("\nData stress test done!\n");
421 
422 fail:
423 	if (in_buffer)
424 		free(in_buffer);
425 	if (out_buffer)
426 		free(out_buffer);
427 }
428 
429 static void
430 exec_host_modem_test(struct modem *p, struct uaddr uaddr)
431 {
432 	struct libusb20_device *pdev;
433 
434 	uint8_t ntest = 0;
435 	uint8_t x;
436 	uint8_t in_ep;
437 	uint8_t out_ep;
438 	uint8_t iface;
439 
440 	int error;
441 
442 	pdev = find_usb_device(uaddr);
443 	if (pdev == NULL) {
444 		printf("USB device not found\n");
445 		return;
446 	}
447 
448 	if (p->use_vendor_specific)
449 		find_usb_endpoints(pdev, 255, 255, 255, 0, &iface, &in_ep, &out_ep, 0);
450 	else
451 		find_usb_endpoints(pdev, 2, 2, 1, 0, &iface, &in_ep, &out_ep, 1);
452 
453 	if ((in_ep == 0) || (out_ep == 0)) {
454 		printf("Could not find USB endpoints\n");
455 		libusb20_dev_free(pdev);
456 		return;
457 	}
458 	printf("Attaching to: %s @ iface %d\n",
459 	    libusb20_dev_get_desc(pdev), iface);
460 
461 	if (libusb20_dev_open(pdev, 2)) {
462 		printf("Could not open USB device\n");
463 		libusb20_dev_free(pdev);
464 		return;
465 	}
466 	if (libusb20_dev_detach_kernel_driver(pdev, iface)) {
467 		printf("WARNING: Could not detach kernel driver\n");
468 	}
469 	p->xfer_in = libusb20_tr_get_pointer(pdev, 0);
470 	error = libusb20_tr_open(p->xfer_in, 65536 / 4, 1, in_ep);
471 	if (error) {
472 		printf("Could not open USB endpoint %d\n", in_ep);
473 		libusb20_dev_free(pdev);
474 		return;
475 	}
476 	p->xfer_out = libusb20_tr_get_pointer(pdev, 1);
477 	error = libusb20_tr_open(p->xfer_out, 65536 / 4, 1, out_ep);
478 	if (error) {
479 		printf("Could not open USB endpoint %d\n", out_ep);
480 		libusb20_dev_free(pdev);
481 		return;
482 	}
483 	p->usb_dev = pdev;
484 	p->usb_iface = iface;
485 	p->errors = 0;
486 
487 	if (p->control_ep_test)
488 		ntest += 7;
489 
490 	if (p->data_stress_test)
491 		ntest += 1;
492 
493 	if (ntest == 0) {
494 		printf("No tests selected\n");
495 	} else {
496 
497 		if (p->control_ep_test) {
498 			for (x = 1; x != 8; x++) {
499 				usb_modem_control_ep_test(p,
500 				    (p->duration + ntest - 1) / ntest, x);
501 			}
502 		}
503 		if (p->data_stress_test) {
504 			usb_modem_data_stress_test(p,
505 			    (p->duration + ntest - 1) / ntest);
506 		}
507 	}
508 
509 	printf("\nDone\n");
510 
511 	libusb20_dev_free(pdev);
512 }
513 
514 void
515 show_host_modem_test(uint8_t level, struct uaddr uaddr, uint32_t duration)
516 {
517 	uint8_t retval;
518 
519 	set_defaults(&modem);
520 
521 	modem.duration = duration;
522 
523 	while (1) {
524 
525 		retval = usb_ts_show_menu(level, "Modem Test Parameters",
526 		    " 1) Execute Data Stress Test: <%s>\n"
527 		    " 2) Execute Modem Control Endpoint Test: <%s>\n"
528 		    " 3) Use random transmit length: <%s>\n"
529 		    " 4) Use random transmit delay: <%s> ms\n"
530 		    " 5) Use vendor specific interface: <%s>\n"
531 		    "10) Loop data: <%s>\n"
532 		    "13) Set test duration: <%d> seconds\n"
533 		    "20) Reset parameters\n"
534 		    "30) Start test (VID=0x%04x, PID=0x%04x)\n"
535 		    "40) Select another device\n"
536 		    " x) Return to previous menu \n",
537 		    (modem.data_stress_test ? "YES" : "NO"),
538 		    (modem.control_ep_test ? "YES" : "NO"),
539 		    (modem.random_tx_length ? "YES" : "NO"),
540 		    (modem.random_tx_delay ? "16" : "0"),
541 		    (modem.use_vendor_specific ? "YES" : "NO"),
542 		    (modem.loop_data ? "YES" : "NO"),
543 		    (int)(modem.duration),
544 		    (int)uaddr.vid, (int)uaddr.pid);
545 
546 		switch (retval) {
547 		case 0:
548 			break;
549 		case 1:
550 			modem.data_stress_test ^= 1;
551 			break;
552 		case 2:
553 			modem.control_ep_test ^= 1;
554 			break;
555 		case 3:
556 			modem.random_tx_length ^= 1;
557 			break;
558 		case 4:
559 			modem.random_tx_delay ^= 1;
560 			break;
561 		case 5:
562 			modem.use_vendor_specific ^= 1;
563 			modem.control_ep_test = 0;
564 			break;
565 		case 10:
566 			modem.loop_data ^= 1;
567 			break;
568 		case 13:
569 			modem.duration = get_integer();
570 			break;
571 		case 20:
572 			set_defaults(&modem);
573 			break;
574 		case 30:
575 			exec_host_modem_test(&modem, uaddr);
576 			break;
577 		case 40:
578 			show_host_device_selection(level + 1, &uaddr);
579 			break;
580 		default:
581 			return;
582 		}
583 	}
584 }
585