1254159Shselasky/* $FreeBSD$ */
2254159Shselasky/*-
3254159Shselasky * Copyright (c) 2007-2010 Hans Petter Selasky. All rights reserved.
4254159Shselasky *
5254159Shselasky * Redistribution and use in source and binary forms, with or without
6254159Shselasky * modification, are permitted provided that the following conditions
7254159Shselasky * are met:
8254159Shselasky * 1. Redistributions of source code must retain the above copyright
9254159Shselasky *    notice, this list of conditions and the following disclaimer.
10254159Shselasky * 2. Redistributions in binary form must reproduce the above copyright
11254159Shselasky *    notice, this list of conditions and the following disclaimer in the
12254159Shselasky *    documentation and/or other materials provided with the distribution.
13254159Shselasky *
14254159Shselasky * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15254159Shselasky * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16254159Shselasky * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17254159Shselasky * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18254159Shselasky * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19254159Shselasky * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20254159Shselasky * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21254159Shselasky * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22254159Shselasky * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23254159Shselasky * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24254159Shselasky * SUCH DAMAGE.
25254159Shselasky */
26254159Shselasky
27254159Shselasky#include <stdio.h>
28254159Shselasky#include <stdint.h>
29254159Shselasky#include <stdlib.h>
30254159Shselasky#include <err.h>
31254159Shselasky#include <string.h>
32254159Shselasky#include <errno.h>
33254159Shselasky#include <unistd.h>
34254159Shselasky
35254159Shselasky#include <sys/sysctl.h>
36254159Shselasky#include <sys/time.h>
37254159Shselasky
38254159Shselasky#include <libusb20.h>
39254159Shselasky#include <libusb20_desc.h>
40254159Shselasky
41254159Shselasky#include <dev/usb/usb_endian.h>
42254159Shselasky#include <dev/usb/usb.h>
43254159Shselasky#include <dev/usb/usb_cdc.h>
44254159Shselasky
45254159Shselasky#include "usbtest.h"
46254159Shselasky
47254159Shselaskystatic struct modem {
48254159Shselasky	struct libusb20_transfer *xfer_in;
49254159Shselasky	struct libusb20_transfer *xfer_out;
50254159Shselasky	struct libusb20_device *usb_dev;
51254159Shselasky
52254159Shselasky	struct bps rx_bytes;
53254159Shselasky	struct bps tx_bytes;
54254159Shselasky	uint32_t c0;
55254159Shselasky	uint32_t c1;
56254159Shselasky	uint32_t out_state;
57254159Shselasky	uint32_t in_last;
58254159Shselasky	uint32_t in_synced;
59254159Shselasky	uint32_t duration;
60254159Shselasky	uint32_t errors;
61254159Shselasky
62254159Shselasky	uint8_t use_vendor_specific;
63254159Shselasky	uint8_t	loop_data;
64254159Shselasky	uint8_t	modem_at_mode;
65254159Shselasky	uint8_t	data_stress_test;
66254159Shselasky	uint8_t	control_ep_test;
67254159Shselasky	uint8_t	usb_iface;
68254159Shselasky	uint8_t	random_tx_length;
69254159Shselasky	uint8_t	random_tx_delay;
70254159Shselasky
71254159Shselasky}	modem;
72254159Shselasky
73254159Shselaskystatic void
74254159Shselaskyset_defaults(struct modem *p)
75254159Shselasky{
76254159Shselasky	memset(p, 0, sizeof(*p));
77254159Shselasky
78254159Shselasky	p->data_stress_test = 1;
79254159Shselasky	p->control_ep_test = 1;
80254159Shselasky	p->duration = 60;		/* seconds */
81254159Shselasky}
82254159Shselasky
83254159Shselaskyvoid
84254159Shselaskydo_bps(const char *desc, struct bps *bps, uint32_t len)
85254159Shselasky{
86254159Shselasky	bps->bytes += len;
87254159Shselasky}
88254159Shselasky
89254159Shselaskystatic void
90254159Shselaskymodem_out_state(uint8_t *buf)
91254159Shselasky{
92254159Shselasky	if (modem.modem_at_mode) {
93254159Shselasky		switch (modem.out_state & 3) {
94254159Shselasky		case 0:
95254159Shselasky			*buf = 'A';
96254159Shselasky			break;
97254159Shselasky		case 1:
98254159Shselasky			*buf = 'T';
99254159Shselasky			break;
100254159Shselasky		case 2:
101254159Shselasky			*buf = '\r';
102254159Shselasky			break;
103254159Shselasky		default:
104254159Shselasky			*buf = '\n';
105254159Shselasky			modem.c0++;
106254159Shselasky			break;
107254159Shselasky		}
108254159Shselasky		modem.out_state++;
109254159Shselasky	} else {
110254159Shselasky		*buf = modem.out_state;
111254159Shselasky		modem.out_state++;
112254159Shselasky		modem.out_state %= 255;
113254159Shselasky	}
114254159Shselasky}
115254159Shselasky
116254159Shselaskystatic void
117254159Shselaskymodem_in_state(uint8_t buf, uint32_t counter)
118254159Shselasky{
119254159Shselasky	if ((modem.in_last == 'O') && (buf == 'K')) {
120254159Shselasky		modem.c1++;
121254159Shselasky		modem.in_last = buf;
122254159Shselasky	} else if (buf == modem.in_last) {
123254159Shselasky		modem.c1++;
124254159Shselasky		modem.in_last++;
125254159Shselasky		modem.in_last %= 255;
126254159Shselasky		if (modem.in_synced == 0) {
127254159Shselasky			if (modem.errors < 64) {
128254159Shselasky				printf("Got sync\n");
129254159Shselasky			}
130254159Shselasky			modem.in_synced = 1;
131254159Shselasky		}
132254159Shselasky	} else {
133254159Shselasky		if (modem.in_synced) {
134254159Shselasky			if (modem.errors < 64) {
135254159Shselasky				printf("Lost sync @ %d, 0x%02x != 0x%02x\n",
136254159Shselasky				    counter % 512, buf, modem.in_last);
137254159Shselasky			}
138254159Shselasky			modem.in_synced = 0;
139254159Shselasky			modem.errors++;
140254159Shselasky		}
141254159Shselasky		modem.in_last = buf;
142254159Shselasky		modem.in_last++;
143254159Shselasky		modem.in_last %= 255;
144254159Shselasky	}
145254159Shselasky}
146254159Shselasky
147254159Shselaskystatic void
148254159Shselaskymodem_write(uint8_t *buf, uint32_t len)
149254159Shselasky{
150254159Shselasky	uint32_t n;
151254159Shselasky
152254159Shselasky	for (n = 0; n != len; n++) {
153254159Shselasky		modem_out_state(buf + n);
154254159Shselasky	}
155254159Shselasky
156254159Shselasky	do_bps("transmitted", &modem.tx_bytes, len);
157254159Shselasky}
158254159Shselasky
159254159Shselaskystatic void
160254159Shselaskymodem_read(uint8_t *buf, uint32_t len)
161254159Shselasky{
162254159Shselasky	uint32_t n;
163254159Shselasky
164254159Shselasky	for (n = 0; n != len; n++) {
165254159Shselasky		modem_in_state(buf[n], n);
166254159Shselasky	}
167254159Shselasky
168254159Shselasky	do_bps("received", &modem.rx_bytes, len);
169254159Shselasky}
170254159Shselasky
171254159Shselaskystatic void
172254159Shselaskyusb_modem_control_ep_test(struct modem *p, uint32_t duration, uint8_t flag)
173254159Shselasky{
174254159Shselasky	struct timeval sub_tv;
175254159Shselasky	struct timeval ref_tv;
176254159Shselasky	struct timeval res_tv;
177254159Shselasky	struct LIBUSB20_CONTROL_SETUP_DECODED setup;
178254159Shselasky	struct usb_cdc_abstract_state ast;
179254159Shselasky	struct usb_cdc_line_state ls;
180254159Shselasky	uint16_t feature = UCDC_ABSTRACT_STATE;
181254159Shselasky	uint16_t state = UCDC_DATA_MULTIPLEXED;
182254159Shselasky	uint8_t iface_no;
183254159Shselasky	uint8_t buf[4];
184254159Shselasky	int id = 0;
185254159Shselasky	int iter = 0;
186254159Shselasky
187254159Shselasky	time_t last_sec;
188254159Shselasky
189254159Shselasky	iface_no = p->usb_iface - 1;
190254159Shselasky
191254159Shselasky	gettimeofday(&ref_tv, 0);
192254159Shselasky
193254159Shselasky	last_sec = ref_tv.tv_sec;
194254159Shselasky
195254159Shselasky	printf("\nTest=%d\n", (int)flag);
196254159Shselasky
197254159Shselasky	while (1) {
198254159Shselasky
199254159Shselasky		gettimeofday(&sub_tv, 0);
200254159Shselasky
201254159Shselasky		if (last_sec != sub_tv.tv_sec) {
202254159Shselasky
203254159Shselasky			printf("STATUS: ID=%u, COUNT=%u tests/sec ERR=%u\n",
204254159Shselasky			    (int)id,
205254159Shselasky			    (int)iter,
206254159Shselasky			    (int)p->errors);
207254159Shselasky
208254159Shselasky			fflush(stdout);
209254159Shselasky
210254159Shselasky			last_sec = sub_tv.tv_sec;
211254159Shselasky
212254159Shselasky			id++;
213254159Shselasky
214254159Shselasky			iter = 0;
215254159Shselasky		}
216254159Shselasky		timersub(&sub_tv, &ref_tv, &res_tv);
217254159Shselasky
218254241Shselasky		if ((res_tv.tv_sec < 0) || (res_tv.tv_sec >= (int)duration))
219254159Shselasky			break;
220254159Shselasky
221254159Shselasky		LIBUSB20_INIT(LIBUSB20_CONTROL_SETUP, &setup);
222254159Shselasky
223254159Shselasky		if (flag & 1) {
224254159Shselasky			setup.bmRequestType = UT_READ_CLASS_INTERFACE;
225254159Shselasky			setup.bRequest = 0x03;
226254159Shselasky			setup.wValue = 0x0001;
227254159Shselasky			setup.wIndex = iface_no;
228254159Shselasky			setup.wLength = 0x0002;
229254159Shselasky
230254159Shselasky			if (libusb20_dev_request_sync(p->usb_dev, &setup, buf, NULL, 250, 0)) {
231254159Shselasky				p->errors++;
232254159Shselasky			}
233254159Shselasky		}
234254159Shselasky		if (flag & 2) {
235254159Shselasky			setup.bmRequestType = UT_WRITE_CLASS_INTERFACE;
236254159Shselasky			setup.bRequest = UCDC_SET_COMM_FEATURE;
237254159Shselasky			setup.wValue = feature;
238254159Shselasky			setup.wIndex = iface_no;
239254159Shselasky			setup.wLength = UCDC_ABSTRACT_STATE_LENGTH;
240254159Shselasky			USETW(ast.wState, state);
241254159Shselasky
242254159Shselasky			if (libusb20_dev_request_sync(p->usb_dev, &setup, &ast, NULL, 250, 0)) {
243254159Shselasky				p->errors++;
244254159Shselasky			}
245254159Shselasky		}
246254159Shselasky		if (flag & 4) {
247254159Shselasky			USETDW(ls.dwDTERate, 115200);
248254159Shselasky			ls.bCharFormat = UCDC_STOP_BIT_1;
249254159Shselasky			ls.bParityType = UCDC_PARITY_NONE;
250254159Shselasky			ls.bDataBits = 8;
251254159Shselasky
252254159Shselasky			setup.bmRequestType = UT_WRITE_CLASS_INTERFACE;
253254159Shselasky			setup.bRequest = UCDC_SET_LINE_CODING;
254254159Shselasky			setup.wValue = 0;
255254159Shselasky			setup.wIndex = iface_no;
256254159Shselasky			setup.wLength = sizeof(ls);
257254159Shselasky
258254159Shselasky			if (libusb20_dev_request_sync(p->usb_dev, &setup, &ls, NULL, 250, 0)) {
259254159Shselasky				p->errors++;
260254159Shselasky			}
261254159Shselasky		}
262254159Shselasky		iter++;
263254159Shselasky	}
264254159Shselasky
265254159Shselasky	printf("\nModem control endpoint test done!\n");
266254159Shselasky}
267254159Shselasky
268254159Shselaskystatic void
269254159Shselaskyusb_modem_data_stress_test(struct modem *p, uint32_t duration)
270254159Shselasky{
271254159Shselasky	struct timeval sub_tv;
272254159Shselasky	struct timeval ref_tv;
273254159Shselasky	struct timeval res_tv;
274254159Shselasky
275254159Shselasky	time_t last_sec;
276254159Shselasky
277254159Shselasky	uint8_t in_pending = 0;
278254159Shselasky	uint8_t in_ready = 0;
279254159Shselasky	uint8_t out_pending = 0;
280254159Shselasky
281254159Shselasky	uint32_t id = 0;
282254159Shselasky
283254159Shselasky	uint32_t in_max;
284254159Shselasky	uint32_t out_max;
285254159Shselasky	uint32_t io_max;
286254159Shselasky
287254159Shselasky	uint8_t *in_buffer = 0;
288254159Shselasky	uint8_t *out_buffer = 0;
289254159Shselasky
290254159Shselasky	gettimeofday(&ref_tv, 0);
291254159Shselasky
292254159Shselasky	last_sec = ref_tv.tv_sec;
293254159Shselasky
294254159Shselasky	printf("\n");
295254159Shselasky
296254159Shselasky	in_max = libusb20_tr_get_max_total_length(p->xfer_in);
297254159Shselasky	out_max = libusb20_tr_get_max_total_length(p->xfer_out);
298254159Shselasky
299254159Shselasky	/* get the smallest buffer size and use that */
300254159Shselasky	io_max = (in_max < out_max) ? in_max : out_max;
301254159Shselasky
302254159Shselasky	if (in_max != out_max)
303254159Shselasky		printf("WARNING: Buffer sizes are un-equal: %u vs %u\n", in_max, out_max);
304254159Shselasky
305254159Shselasky	in_buffer = malloc(io_max);
306254159Shselasky	if (in_buffer == NULL)
307254159Shselasky		goto fail;
308254159Shselasky
309254159Shselasky	out_buffer = malloc(io_max);
310254159Shselasky	if (out_buffer == NULL)
311254159Shselasky		goto fail;
312254159Shselasky
313254159Shselasky	while (1) {
314254159Shselasky
315254159Shselasky		gettimeofday(&sub_tv, 0);
316254159Shselasky
317254159Shselasky		if (last_sec != sub_tv.tv_sec) {
318254159Shselasky
319254159Shselasky			printf("STATUS: ID=%u, RX=%u bytes/sec, TX=%u bytes/sec, ERR=%d\n",
320254159Shselasky			    (int)id,
321254159Shselasky			    (int)p->rx_bytes.bytes,
322254159Shselasky			    (int)p->tx_bytes.bytes,
323254159Shselasky			    (int)p->errors);
324254159Shselasky
325254159Shselasky			p->rx_bytes.bytes = 0;
326254159Shselasky			p->tx_bytes.bytes = 0;
327254159Shselasky
328254159Shselasky			fflush(stdout);
329254159Shselasky
330254159Shselasky			last_sec = sub_tv.tv_sec;
331254159Shselasky
332254159Shselasky			id++;
333254159Shselasky		}
334254159Shselasky		timersub(&sub_tv, &ref_tv, &res_tv);
335254159Shselasky
336254241Shselasky		if ((res_tv.tv_sec < 0) || (res_tv.tv_sec >= (int)duration))
337254159Shselasky			break;
338254159Shselasky
339254159Shselasky		libusb20_dev_process(p->usb_dev);
340254159Shselasky
341254159Shselasky		if (!libusb20_tr_pending(p->xfer_in)) {
342254159Shselasky			if (in_pending) {
343254159Shselasky				if (libusb20_tr_get_status(p->xfer_in) == 0) {
344254159Shselasky					modem_read(in_buffer, libusb20_tr_get_length(p->xfer_in, 0));
345254159Shselasky				} else {
346254159Shselasky					p->errors++;
347254159Shselasky					usleep(10000);
348254159Shselasky				}
349254159Shselasky				in_pending = 0;
350254159Shselasky				in_ready = 1;
351254159Shselasky			}
352254159Shselasky			if (p->loop_data == 0) {
353254159Shselasky				libusb20_tr_setup_bulk(p->xfer_in, in_buffer, io_max, 0);
354254159Shselasky				libusb20_tr_start(p->xfer_in);
355254159Shselasky				in_pending = 1;
356254159Shselasky				in_ready = 0;
357254159Shselasky			}
358254159Shselasky		}
359254159Shselasky		if (!libusb20_tr_pending(p->xfer_out)) {
360254159Shselasky
361254159Shselasky			uint32_t len;
362254159Shselasky			uint32_t dly;
363254159Shselasky
364254159Shselasky			if (out_pending) {
365254159Shselasky				if (libusb20_tr_get_status(p->xfer_out) != 0) {
366254159Shselasky					p->errors++;
367254159Shselasky					usleep(10000);
368254159Shselasky				}
369254159Shselasky			}
370254159Shselasky			if (p->random_tx_length) {
371254159Shselasky				len = ((uint32_t)usb_ts_rand_noise()) % ((uint32_t)io_max);
372254159Shselasky			} else {
373254159Shselasky				len = io_max;
374254159Shselasky			}
375254159Shselasky
376254159Shselasky			if (p->random_tx_delay) {
377254159Shselasky				dly = ((uint32_t)usb_ts_rand_noise()) % 16000U;
378254159Shselasky			} else {
379254159Shselasky				dly = 0;
380254159Shselasky			}
381254159Shselasky
382254159Shselasky			if (p->loop_data != 0) {
383254159Shselasky				if (in_ready != 0) {
384254159Shselasky					len = libusb20_tr_get_length(p->xfer_in, 0);
385254159Shselasky					memcpy(out_buffer, in_buffer, len);
386254159Shselasky					in_ready = 0;
387254159Shselasky				} else {
388254159Shselasky					len = io_max + 1;
389254159Shselasky				}
390254159Shselasky				if (!libusb20_tr_pending(p->xfer_in)) {
391254159Shselasky					libusb20_tr_setup_bulk(p->xfer_in, in_buffer, io_max, 0);
392254159Shselasky					libusb20_tr_start(p->xfer_in);
393254159Shselasky					in_pending = 1;
394254159Shselasky				}
395254159Shselasky			} else {
396254159Shselasky				modem_write(out_buffer, len);
397254159Shselasky			}
398254159Shselasky
399254159Shselasky			if (len <= io_max) {
400254159Shselasky				libusb20_tr_setup_bulk(p->xfer_out, out_buffer, len, 0);
401254159Shselasky
402254159Shselasky				if (dly != 0)
403254159Shselasky					usleep(dly);
404254159Shselasky
405254159Shselasky				libusb20_tr_start(p->xfer_out);
406254159Shselasky
407254159Shselasky				out_pending = 1;
408254159Shselasky			}
409254159Shselasky		}
410254159Shselasky		libusb20_dev_wait_process(p->usb_dev, 500);
411254159Shselasky
412254159Shselasky		if (libusb20_dev_check_connected(p->usb_dev) != 0) {
413254159Shselasky			printf("Device disconnected\n");
414254159Shselasky			break;
415254159Shselasky		}
416254159Shselasky	}
417254159Shselasky
418254159Shselasky	libusb20_tr_stop(p->xfer_in);
419254159Shselasky	libusb20_tr_stop(p->xfer_out);
420254159Shselasky
421254159Shselasky	printf("\nData stress test done!\n");
422254159Shselasky
423254159Shselaskyfail:
424254159Shselasky	if (in_buffer)
425254159Shselasky		free(in_buffer);
426254159Shselasky	if (out_buffer)
427254159Shselasky		free(out_buffer);
428254159Shselasky}
429254159Shselasky
430254159Shselaskystatic void
431254159Shselaskyexec_host_modem_test(struct modem *p, uint16_t vid, uint16_t pid)
432254159Shselasky{
433254159Shselasky	struct libusb20_device *pdev;
434254159Shselasky
435254159Shselasky	uint8_t ntest = 0;
436254159Shselasky	uint8_t x;
437254159Shselasky	uint8_t in_ep;
438254159Shselasky	uint8_t out_ep;
439254159Shselasky	uint8_t iface;
440254159Shselasky
441254159Shselasky	int error;
442254159Shselasky
443254159Shselasky	pdev = find_usb_device(vid, pid);
444254159Shselasky	if (pdev == NULL) {
445254159Shselasky		printf("USB device not found\n");
446254159Shselasky		return;
447254159Shselasky	}
448254159Shselasky
449254159Shselasky	if (p->use_vendor_specific)
450254159Shselasky		find_usb_endpoints(pdev, 255, 255, 255, 0, &iface, &in_ep, &out_ep, 0);
451254159Shselasky	else
452254159Shselasky		find_usb_endpoints(pdev, 2, 2, 1, 0, &iface, &in_ep, &out_ep, 1);
453254159Shselasky
454254159Shselasky	if ((in_ep == 0) || (out_ep == 0)) {
455254159Shselasky		printf("Could not find USB endpoints\n");
456254159Shselasky		libusb20_dev_free(pdev);
457254159Shselasky		return;
458254159Shselasky	}
459254159Shselasky	printf("Attaching to: %s @ iface %d\n",
460254159Shselasky	    libusb20_dev_get_desc(pdev), iface);
461254159Shselasky
462254159Shselasky	if (libusb20_dev_open(pdev, 2)) {
463254159Shselasky		printf("Could not open USB device\n");
464254159Shselasky		libusb20_dev_free(pdev);
465254159Shselasky		return;
466254159Shselasky	}
467254159Shselasky	if (libusb20_dev_detach_kernel_driver(pdev, iface)) {
468254159Shselasky		printf("WARNING: Could not detach kernel driver\n");
469254159Shselasky	}
470254159Shselasky	p->xfer_in = libusb20_tr_get_pointer(pdev, 0);
471254159Shselasky	error = libusb20_tr_open(p->xfer_in, 65536 / 4, 1, in_ep);
472254159Shselasky	if (error) {
473254159Shselasky		printf("Could not open USB endpoint %d\n", in_ep);
474254159Shselasky		libusb20_dev_free(pdev);
475254159Shselasky		return;
476254159Shselasky	}
477254159Shselasky	p->xfer_out = libusb20_tr_get_pointer(pdev, 1);
478254159Shselasky	error = libusb20_tr_open(p->xfer_out, 65536 / 4, 1, out_ep);
479254159Shselasky	if (error) {
480254159Shselasky		printf("Could not open USB endpoint %d\n", out_ep);
481254159Shselasky		libusb20_dev_free(pdev);
482254159Shselasky		return;
483254159Shselasky	}
484254159Shselasky	p->usb_dev = pdev;
485254159Shselasky	p->usb_iface = iface;
486254159Shselasky	p->errors = 0;
487254159Shselasky
488254159Shselasky	if (p->control_ep_test)
489254159Shselasky		ntest += 7;
490254159Shselasky
491254159Shselasky	if (p->data_stress_test)
492254159Shselasky		ntest += 1;
493254159Shselasky
494254159Shselasky	if (ntest == 0) {
495254159Shselasky		printf("No tests selected\n");
496254159Shselasky	} else {
497254159Shselasky
498254159Shselasky		if (p->control_ep_test) {
499254159Shselasky			for (x = 1; x != 8; x++) {
500254159Shselasky				usb_modem_control_ep_test(p,
501254159Shselasky				    (p->duration + ntest - 1) / ntest, x);
502254159Shselasky			}
503254159Shselasky		}
504254159Shselasky		if (p->data_stress_test) {
505254159Shselasky			usb_modem_data_stress_test(p,
506254159Shselasky			    (p->duration + ntest - 1) / ntest);
507254159Shselasky		}
508254159Shselasky	}
509254159Shselasky
510254159Shselasky	printf("\nDone\n");
511254159Shselasky
512254159Shselasky	libusb20_dev_free(pdev);
513254159Shselasky}
514254159Shselasky
515254159Shselaskyvoid
516254159Shselaskyshow_host_modem_test(uint8_t level, uint16_t vid, uint16_t pid, uint32_t duration)
517254159Shselasky{
518254159Shselasky	uint8_t retval;
519254159Shselasky
520254159Shselasky	set_defaults(&modem);
521254159Shselasky
522254159Shselasky	modem.duration = duration;
523254159Shselasky
524254159Shselasky	while (1) {
525254159Shselasky
526254159Shselasky		retval = usb_ts_show_menu(level, "Modem Test Parameters",
527254159Shselasky		    " 1) Execute Data Stress Test: <%s>\n"
528254159Shselasky		    " 2) Execute Modem Control Endpoint Test: <%s>\n"
529254159Shselasky		    " 3) Use random transmit length: <%s>\n"
530254159Shselasky		    " 4) Use random transmit delay: <%s> ms\n"
531254159Shselasky		    " 5) Use vendor specific interface: <%s>\n"
532254159Shselasky		    "10) Loop data: <%s>\n"
533254159Shselasky		    "13) Set test duration: <%d> seconds\n"
534254159Shselasky		    "20) Reset parameters\n"
535254159Shselasky		    "30) Start test (VID=0x%04x, PID=0x%04x)\n"
536254159Shselasky		    "40) Select another device\n"
537254159Shselasky		    " x) Return to previous menu \n",
538254159Shselasky		    (modem.data_stress_test ? "YES" : "NO"),
539254159Shselasky		    (modem.control_ep_test ? "YES" : "NO"),
540254159Shselasky		    (modem.random_tx_length ? "YES" : "NO"),
541254159Shselasky		    (modem.random_tx_delay ? "16" : "0"),
542254159Shselasky		    (modem.use_vendor_specific ? "YES" : "NO"),
543254159Shselasky		    (modem.loop_data ? "YES" : "NO"),
544254159Shselasky		    (int)(modem.duration),
545254159Shselasky		    (int)vid, (int)pid);
546254159Shselasky
547254159Shselasky		switch (retval) {
548254159Shselasky		case 0:
549254159Shselasky			break;
550254159Shselasky		case 1:
551254159Shselasky			modem.data_stress_test ^= 1;
552254159Shselasky			break;
553254159Shselasky		case 2:
554254159Shselasky			modem.control_ep_test ^= 1;
555254159Shselasky			break;
556254159Shselasky		case 3:
557254159Shselasky			modem.random_tx_length ^= 1;
558254159Shselasky			break;
559254159Shselasky		case 4:
560254159Shselasky			modem.random_tx_delay ^= 1;
561254159Shselasky			break;
562254159Shselasky		case 5:
563254159Shselasky			modem.use_vendor_specific ^= 1;
564254159Shselasky			modem.control_ep_test = 0;
565254159Shselasky			break;
566254159Shselasky		case 10:
567254159Shselasky			modem.loop_data ^= 1;
568254159Shselasky			break;
569254159Shselasky		case 13:
570254159Shselasky			modem.duration = get_integer();
571254159Shselasky			break;
572254159Shselasky		case 20:
573254159Shselasky			set_defaults(&modem);
574254159Shselasky			break;
575254159Shselasky		case 30:
576254159Shselasky			exec_host_modem_test(&modem, vid, pid);
577254159Shselasky			break;
578254159Shselasky		case 40:
579254159Shselasky			show_host_device_selection(level + 1, &vid, &pid);
580254159Shselasky			break;
581254159Shselasky		default:
582254159Shselasky			return;
583254159Shselasky		}
584254159Shselasky	}
585254159Shselasky}
586