Merge branch 'vendor/DHCPCD'
[dragonfly.git] / tools / tools / usbtest / usb_modem_test.c
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 }