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