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