3 * Copyright (c) 2007-2010 Hans Petter Selasky. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
35 #include <sys/sysctl.h>
39 #include <libusb20_desc.h>
41 #include <dev/usb/usb_endian.h>
42 #include <dev/usb/usb.h>
43 #include <dev/usb/usb_cdc.h>
48 struct libusb20_transfer *xfer_in;
49 struct libusb20_transfer *xfer_out;
50 struct libusb20_device *usb_dev;
62 uint8_t use_vendor_specific;
64 uint8_t modem_at_mode;
65 uint8_t data_stress_test;
66 uint8_t control_ep_test;
68 uint8_t random_tx_length;
69 uint8_t random_tx_delay;
74 set_defaults(struct modem *p)
76 memset(p, 0, sizeof(*p));
78 p->data_stress_test = 1;
79 p->control_ep_test = 1;
80 p->duration = 60; /* seconds */
84 do_bps(const char *desc, struct bps *bps, uint32_t len)
90 modem_out_state(uint8_t *buf)
92 if (modem.modem_at_mode) {
93 switch (modem.out_state & 3) {
110 *buf = modem.out_state;
112 modem.out_state %= 255;
117 modem_in_state(uint8_t buf, uint32_t counter)
119 if ((modem.in_last == 'O') && (buf == 'K')) {
122 } else if (buf == modem.in_last) {
125 modem.in_last %= 255;
126 if (modem.in_synced == 0) {
127 if (modem.errors < 64) {
128 printf("Got sync\n");
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);
143 modem.in_last %= 255;
148 modem_write(uint8_t *buf, uint32_t len)
152 for (n = 0; n != len; n++) {
153 modem_out_state(buf + n);
156 do_bps("transmitted", &modem.tx_bytes, len);
160 modem_read(uint8_t *buf, uint32_t len)
164 for (n = 0; n != len; n++) {
165 modem_in_state(buf[n], n);
168 do_bps("received", &modem.rx_bytes, len);
172 usb_modem_control_ep_test(struct modem *p, uint32_t duration, uint8_t flag)
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;
189 iface_no = p->usb_iface - 1;
191 gettimeofday(&ref_tv, 0);
193 last_sec = ref_tv.tv_sec;
195 printf("\nTest=%d\n", (int)flag);
199 gettimeofday(&sub_tv, 0);
201 if (last_sec != sub_tv.tv_sec) {
203 printf("STATUS: ID=%u, COUNT=%u tests/sec ERR=%u\n",
210 last_sec = sub_tv.tv_sec;
216 timersub(&sub_tv, &ref_tv, &res_tv);
218 if ((res_tv.tv_sec < 0) || (res_tv.tv_sec >= (int)duration))
221 LIBUSB20_INIT(LIBUSB20_CONTROL_SETUP, &setup);
224 setup.bmRequestType = UT_READ_CLASS_INTERFACE;
225 setup.bRequest = 0x03;
226 setup.wValue = 0x0001;
227 setup.wIndex = iface_no;
228 setup.wLength = 0x0002;
230 if (libusb20_dev_request_sync(p->usb_dev, &setup, buf, NULL, 250, 0)) {
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);
242 if (libusb20_dev_request_sync(p->usb_dev, &setup, &ast, NULL, 250, 0)) {
247 USETDW(ls.dwDTERate, 115200);
248 ls.bCharFormat = UCDC_STOP_BIT_1;
249 ls.bParityType = UCDC_PARITY_NONE;
252 setup.bmRequestType = UT_WRITE_CLASS_INTERFACE;
253 setup.bRequest = UCDC_SET_LINE_CODING;
255 setup.wIndex = iface_no;
256 setup.wLength = sizeof(ls);
258 if (libusb20_dev_request_sync(p->usb_dev, &setup, &ls, NULL, 250, 0)) {
265 printf("\nModem control endpoint test done!\n");
269 usb_modem_data_stress_test(struct modem *p, uint32_t duration)
271 struct timeval sub_tv;
272 struct timeval ref_tv;
273 struct timeval res_tv;
277 uint8_t in_pending = 0;
278 uint8_t in_ready = 0;
279 uint8_t out_pending = 0;
287 uint8_t *in_buffer = 0;
288 uint8_t *out_buffer = 0;
290 gettimeofday(&ref_tv, 0);
292 last_sec = ref_tv.tv_sec;
296 in_max = libusb20_tr_get_max_total_length(p->xfer_in);
297 out_max = libusb20_tr_get_max_total_length(p->xfer_out);
299 /* get the smallest buffer size and use that */
300 io_max = (in_max < out_max) ? in_max : out_max;
302 if (in_max != out_max)
303 printf("WARNING: Buffer sizes are un-equal: %u vs %u\n", in_max, out_max);
305 in_buffer = malloc(io_max);
306 if (in_buffer == NULL)
309 out_buffer = malloc(io_max);
310 if (out_buffer == NULL)
315 gettimeofday(&sub_tv, 0);
317 if (last_sec != sub_tv.tv_sec) {
319 printf("STATUS: ID=%u, RX=%u bytes/sec, TX=%u bytes/sec, ERR=%d\n",
321 (int)p->rx_bytes.bytes,
322 (int)p->tx_bytes.bytes,
325 p->rx_bytes.bytes = 0;
326 p->tx_bytes.bytes = 0;
330 last_sec = sub_tv.tv_sec;
334 timersub(&sub_tv, &ref_tv, &res_tv);
336 if ((res_tv.tv_sec < 0) || (res_tv.tv_sec >= (int)duration))
339 libusb20_dev_process(p->usb_dev);
341 if (!libusb20_tr_pending(p->xfer_in)) {
343 if (libusb20_tr_get_status(p->xfer_in) == 0) {
344 modem_read(in_buffer, libusb20_tr_get_length(p->xfer_in, 0));
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);
359 if (!libusb20_tr_pending(p->xfer_out)) {
365 if (libusb20_tr_get_status(p->xfer_out) != 0) {
370 if (p->random_tx_length) {
371 len = ((uint32_t)usb_ts_rand_noise()) % ((uint32_t)io_max);
376 if (p->random_tx_delay) {
377 dly = ((uint32_t)usb_ts_rand_noise()) % 16000U;
382 if (p->loop_data != 0) {
384 len = libusb20_tr_get_length(p->xfer_in, 0);
385 memcpy(out_buffer, in_buffer, len);
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);
396 modem_write(out_buffer, len);
400 libusb20_tr_setup_bulk(p->xfer_out, out_buffer, len, 0);
405 libusb20_tr_start(p->xfer_out);
410 libusb20_dev_wait_process(p->usb_dev, 500);
412 if (libusb20_dev_check_connected(p->usb_dev) != 0) {
413 printf("Device disconnected\n");
418 libusb20_tr_stop(p->xfer_in);
419 libusb20_tr_stop(p->xfer_out);
421 printf("\nData stress test done!\n");
431 exec_host_modem_test(struct modem *p, uint16_t vid, uint16_t pid)
433 struct libusb20_device *pdev;
443 pdev = find_usb_device(vid, pid);
445 printf("USB device not found\n");
449 if (p->use_vendor_specific)
450 find_usb_endpoints(pdev, 255, 255, 255, 0, &iface, &in_ep, &out_ep, 0);
452 find_usb_endpoints(pdev, 2, 2, 1, 0, &iface, &in_ep, &out_ep, 1);
454 if ((in_ep == 0) || (out_ep == 0)) {
455 printf("Could not find USB endpoints\n");
456 libusb20_dev_free(pdev);
459 printf("Attaching to: %s @ iface %d\n",
460 libusb20_dev_get_desc(pdev), iface);
462 if (libusb20_dev_open(pdev, 2)) {
463 printf("Could not open USB device\n");
464 libusb20_dev_free(pdev);
467 if (libusb20_dev_detach_kernel_driver(pdev, iface)) {
468 printf("WARNING: Could not detach kernel driver\n");
470 p->xfer_in = libusb20_tr_get_pointer(pdev, 0);
471 error = libusb20_tr_open(p->xfer_in, 65536 / 4, 1, in_ep);
473 printf("Could not open USB endpoint %d\n", in_ep);
474 libusb20_dev_free(pdev);
477 p->xfer_out = libusb20_tr_get_pointer(pdev, 1);
478 error = libusb20_tr_open(p->xfer_out, 65536 / 4, 1, out_ep);
480 printf("Could not open USB endpoint %d\n", out_ep);
481 libusb20_dev_free(pdev);
485 p->usb_iface = iface;
488 if (p->control_ep_test)
491 if (p->data_stress_test)
495 printf("No tests selected\n");
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);
504 if (p->data_stress_test) {
505 usb_modem_data_stress_test(p,
506 (p->duration + ntest - 1) / ntest);
512 libusb20_dev_free(pdev);
516 show_host_modem_test(uint8_t level, uint16_t vid, uint16_t pid, uint32_t duration)
520 set_defaults(&modem);
522 modem.duration = duration;
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),
551 modem.data_stress_test ^= 1;
554 modem.control_ep_test ^= 1;
557 modem.random_tx_length ^= 1;
560 modem.random_tx_delay ^= 1;
563 modem.use_vendor_specific ^= 1;
564 modem.control_ep_test = 0;
567 modem.loop_data ^= 1;
570 modem.duration = get_integer();
573 set_defaults(&modem);
576 exec_host_modem_test(&modem, vid, pid);
579 show_host_device_selection(level + 1, &vid, &pid);