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