1 /* $FreeBSD: head/tools/tools/usbtest/usb_modem_test.c 254241 2013-08-12 09:15:33Z hselasky $ */
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 <bus/u4b/usb_endian.h>
42 #include <bus/u4b/usb.h>
43 #include <bus/u4b/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
);