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
set_defaults(struct modem * p)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
do_bps(const char * desc,struct bps * bps,uint32_t len)83 do_bps(const char *desc, struct bps *bps, uint32_t len)
84 {
85 bps->bytes += len;
86 }
87
88 static void
modem_out_state(uint8_t * buf)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
modem_in_state(uint8_t buf,uint32_t counter)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
modem_write(uint8_t * buf,uint32_t len)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
modem_read(uint8_t * buf,uint32_t len)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
usb_modem_control_ep_test(struct modem * p,uint32_t duration,uint8_t flag)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
usb_modem_data_stress_test(struct modem * p,uint32_t duration)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
exec_host_modem_test(struct modem * p,struct uaddr uaddr)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
show_host_modem_test(uint8_t level,struct uaddr uaddr,uint32_t duration)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