Remove more example programs during rpm build
[libftdi] / examples / purge_test.c
CommitLineData
41b114da
ES
1/* purge_test.c
2 *
3 * Test for purge TX/RX functions.
4 *
5 * The chip must be wired to loop TX data to RX data (loopback).
6 *
7 * This program works with "standard" linux drivers and the FTDI1 library.
8 *
9 * Usage: purge_test [-b baud] [-i interface] [-n msg-size] [-N note] device-specifier
10 * See usage below for more information on command usage.
11 *
12 * This program works well with the FT4231H which is newer and has large
13 * FIFOs. This program does not work well with FT232, either pre or post
14 * switching the SIO_RESET_PURGE_TX/SIO_RESET_PURGE_RX values.
15 *
16 * This needs testing with other devices, which I do not have.
17 */
18#include <stdio.h>
19#include <stdlib.h>
20#include <stdint.h>
21#include <string.h>
22#include <unistd.h>
23#include <getopt.h>
24#include <signal.h>
25#include <errno.h>
26/* Prevent deprecated messages when building library */
27#define _FTDI_DISABLE_DEPRECATED
28#include <ftdi.h>
29
30#include <termios.h> // For baudcodes & linux UARTs
31#include <sys/types.h>
32#include <sys/stat.h>
33#include <fcntl.h>
34
35
36static struct ftdi_context *ftdi = NULL;
37static int dev_fd = -1;
38static char * dev_string = NULL;
39static int latency_specified = 0;
40static int latency = 5;
41static int baud = 9600;
42static int baud_code = -1;
43static enum ftdi_interface interface = INTERFACE_A;
44static int msg_size = 80;
45static int broken_purge_test = 0;
46
47static const int latency_min = 2;
48static const int latency_max = 255;
49
50static volatile long long usec_test_start;
51
52
53
54static int ascii2int(const char * str, const char * pgm_name);
55static int baud_2_baud_code(int baud);
56static long int char_cnt_2_usec(int char_count);
57static long int drain();
58static int flush(int queue_selector);
59static long long int get_time_usec();
60
61static const int flushQueueSelector[] = {
62 TCIFLUSH, TCOFLUSH, TCIOFLUSH }; /* See /usr/include/bits/termios.h */
63static const char * flushTestName[] = {
64 "Input-only", "Output-only", "Input+Output" };
65static const char * expected[] = {
66 "last portion of message",
67 "first portion of message",
68 "mid-message characters",
69};
70
71
72static const char * chip_types[] = {
73 "am",
74 "bm",
75 "2232C",
76 "R",
77 "2232H",
78 "4232H",
79 "232H",
80 "230X",
81};
82
83#ifndef ARRAY_SIZE
84# define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
85#endif
86
87
88
89/**********************************************************************
90 */
91static void
92usage(const char *argv0)
93{
94 fprintf(stderr,
95 "Usage: %s [options...] device-specifier\n"
96 "Flush test for UARTS.\n"
97 " with loopback connector\n"
98 " [-b baud] baud rate (e.g., 300, 600, 1200, ...230400)\n"
99 " [-i {a|b|c|d}] FTDI interface for chips which have multiple UARTS\n"
100 " [-l latency] Latency (%d..%d)\n"
101 " [-n msg-size] Number of bytes in test message\n"
102 " [-N note] Note for the output\n"
103 " [-P] Use broken libftdi1 purge methods (over new flush)\n"
104 "\n"
105 " device-specifier String specifying the UART. If the first character\n"
106 " is the '/' character, the program assumes a Linux UART\n"
107 " is to be tested and the string would be something like\n"
108 " '/dev/ttyS0' or '/dev/ttyUSB0'. Otherwise, the program\n"
109 " assumes an FTDI device is being tested with the FTDI1\n"
110 " library. The device-specifier must be a string\n"
111 " accepted by the ftdi_usb_open_string function. An\n"
112 " example would be 'i:0x0403:0x6011[:index]'.\n"
113 "\n"
114 "NOTE: To function correctly, this program requires a loopback connector\n"
115 " attached to the UART under test.\n"
116 "\n"
117 "Adapted from stream_test.c 2018. Eric Schott <els6@psu.edu>\n"
118 "Copyright (C) 2009 Micah Dowty <micah@navi.cx>\n"
119 "Adapted for use with libftdi (C) 2010 Uwe Bonnes <bon@elektron.ikp.physik.tu-darmstadt.de>\n",
120 argv0, latency_min, latency_max);
121 exit(1);
122}
123
124
125/**********************************************************************
126 */
127int main(int argc, char **argv)
128{
129 int c, i;
130 int option_index;
131 int test;
132 unsigned char * msg;
133 unsigned char * retMsg;
134 char * note = NULL;
135 char * note_default = NULL;
136 size_t retMsgSize;
137 long int msg_xmit_time_us;
138 static struct option long_options[] = {{NULL},};
139
140 while ((c = getopt_long(argc, argv, "n:b:i:l:N:P", long_options, &option_index)) !=- 1)
141 switch (c)
142 {
143 case -1:
144 break;
145 case 'b':
146 baud = ascii2int(optarg, argv[0]);
147 break;
148 case 'i':
149 if (optarg == NULL || strlen(optarg) != 1)
150 usage(argv[0]);
151 switch (optarg[0])
152 {
153 case 'a':
154 case 'A':
155 interface = INTERFACE_A;
156 break;
157
158 case 'b':
159 case 'B':
160 interface = INTERFACE_B;
161 break;
162
163 case 'c':
164 case 'C':
165 interface = INTERFACE_C;
166 break;
167
168 case 'd':
169 case 'D':
170 interface = INTERFACE_D;
171 break;
172
173 default:
174 usage(argv[0]);
175 }
176 break;
177 case 'l':
178 latency = ascii2int(optarg, argv[0]);
179 if (latency < latency_min || latency > latency_max)
180 {
181 fprintf(stderr, "latency [-l] must be an integer in the range %d..%d\n",
182 latency_min, latency_max);
183 usage(argv[0]);
184 }
185 latency_specified = 1;
186 break;
187 case 'n':
188 msg_size = ascii2int(optarg, argv[0]);
189 if (msg_size < 1)
190 {
191 fprintf(stderr, "msg-size [-n] must be an integer greater than 0\n");
192 usage(argv[0]);
193 }
194 break;
195 case 'N':
196 note = optarg;
197 break;
198 case 'P':
199 broken_purge_test = 1;
200 break;
201 default:
202 usage(argv[0]);
203 }
204
205 if (optind == argc)
206 usage(argv[0]);
207
208 if (optind == argc - 1)
209 {
210 // Exactly one extra argument- a dump file
211 dev_string = argv[optind];
212 }
213 else if (optind < argc)
214 {
215 // Too many extra args
216 usage(argv[0]);
217 }
218
219 baud_code = baud_2_baud_code(baud);
220 if (baud_code < 1)
221 {
222 fprintf(stderr, "Invalid baud [-b]\n");
223 usage(argv[0]);
224 }
225
226 if (dev_string[0] == '/')
227 {
228 struct termios termios;
229
230 if (latency_specified) {
231 fprintf(stderr, "Latency (-l) option not support on this device; ignored\n");
232 }
233
234 if (broken_purge_test) {
235 fprintf(stderr, "Broken-purge (-P) option not support with Linux kernel driver\n");
236 return EXIT_FAILURE;
237 }
238
239 dev_fd = open(dev_string, O_NOCTTY | O_RDWR);
240 if (dev_fd < 0)
241 {
242 fprintf(stderr, "Error opening Linux device \"%s\": %s\n",
243 dev_string, strerror(errno));
244 return EXIT_FAILURE;
245 }
246
247 if (! isatty(dev_fd))
248 {
249 fprintf(stderr, "Not a TTY device: \"%s\"\n", dev_string);
250 return EXIT_FAILURE;
251 }
252
253 if (tcgetattr(dev_fd, &termios) == -1)
254 {
255 fprintf(stderr, "Error getting TTY attributes for \"%s\": %s\n",
256 dev_string, strerror(errno));
257 return EXIT_FAILURE;
258 }
259
260 note_default = "Linux kernel driver";
261
262 cfmakeraw(&termios);
263
264 termios.c_cflag &=
265 ~(CSTOPB | CRTSCTS);
266
267 termios.c_cflag &= ~CSIZE;
268 termios.c_cflag |= CS8;
269
270 cfsetspeed(&termios, baud_code);
271
272 termios.c_cflag |=
273 CLOCAL;
274
275 termios.c_cc[VMIN] = 1; // Character at a time input
276 termios.c_cc[VTIME] = 0; // with blocking
277
278 if (tcsetattr(dev_fd, TCSAFLUSH, &termios) == -1) {
279 fprintf(stderr, "Error setting TTY attributes for \"%s\": %s\n",
280 dev_string, strerror(errno));
281 return EXIT_FAILURE;
282 }
283 }
284 else
285 {
286
287 if ((ftdi = ftdi_new()) == 0)
288 {
289 fprintf(stderr, "ftdi_new failed\n");
290 return EXIT_FAILURE;
291 }
292
293 if (ftdi_set_interface(ftdi, interface) < 0)
294 {
295 fprintf(stderr, "ftdi_set_interface failed\n");
296 ftdi_free(ftdi);
297 return EXIT_FAILURE;
298 }
299
300 if (ftdi_usb_open_string(ftdi, dev_string) < 0)
301 {
302 fprintf(stderr,"Error opening ftdi device \"%s\": %s\n", dev_string,
303 ftdi_get_error_string(ftdi));
304 ftdi_free(ftdi);
305 return EXIT_FAILURE;
306 }
307
308 if(ftdi_set_latency_timer(ftdi, (unsigned char) latency))
309 {
310 if (latency_specified &&
311 (ftdi->type == TYPE_AM || ftdi->type == TYPE_232H)) {
312 fprintf(stderr, "Latency (-l) option not support on this device; ignored\n");
313 } else if (ftdi->type != TYPE_AM && ftdi->type != TYPE_232H) {
314 fprintf(stderr,"Error setting latency for ftdi device \"%s\" (%d): %s\n",
315 dev_string, ftdi->type, ftdi_get_error_string(ftdi));
316 ftdi_free(ftdi);
317 return EXIT_FAILURE;
318 }
319 }
320
321 if (ftdi_set_line_property2(ftdi, BITS_8, STOP_BIT_1, NONE, BREAK_OFF) < 0)
322 {
323 fprintf(stderr,"Error setting line properties ftdi device \"%s\": %s\n", dev_string,
324 ftdi_get_error_string(ftdi));
325 ftdi_free(ftdi);
326 return EXIT_FAILURE;
327 }
328
329 if (ftdi_set_baudrate(ftdi, baud) < 0)
330 {
331 fprintf(stderr,"Error setting baud rate for ftdi device \"%s\": %s\n", dev_string,
332 ftdi_get_error_string(ftdi));
333 ftdi_free(ftdi);
334 return EXIT_FAILURE;
335 }
336
337 if (ftdi_setflowctrl(ftdi, SIO_DISABLE_FLOW_CTRL))
338 {
339 fprintf(stderr,"Error setting flow control for ftdi device \"%s\": %s\n", dev_string,
340 ftdi_get_error_string(ftdi));
341 ftdi_free(ftdi);
342 return EXIT_FAILURE;
343 }
344
345 if (broken_purge_test)
346 note_default = "libftdi w/ deprecated purge";
347 else
348 note_default = "libftdi w/ new flush methods";
349 }
350
351 printf("Purge (tcflush) test for device %s\n", dev_string);
352 printf("Note: %s\n", (note ? note : note_default));
353
354 if (dev_fd < 0)
355 {
356 if (ftdi->type >0 && ftdi->type < ARRAY_SIZE(chip_types))
357 printf("FTDI chip type is %d (%s)\n",
358 ftdi->type, chip_types[ftdi->type]);
359 else
360 printf("FTDI chip type is %d (unknown)\n", ftdi->type);
361 }
362
363 printf("# purge_test" );
364 for (c = 1; c < argc; ++c)
365 {
366 const char *p = argv[c];
367 while (*p != '\0')
368 {
369 if (*p == ' ')
370 break;
371 ++p;
372 }
373 if (*p == ' ')
374 printf(" '%s'", argv[c]);
375 else
376 printf(" %s", argv[c]);
377 }
378 printf("\n");
379
380 msg_xmit_time_us = char_cnt_2_usec(msg_size);
381 printf("%d chars at %d baud takes about %.0f ms to transmit\n", msg_size,
382 baud, msg_xmit_time_us * .001);
383
384 msg = malloc(msg_size + 1);
385 if (msg == NULL)
386 {
387 fprintf(stderr, "Could not allocate send message buffer\n");
388 return EXIT_FAILURE;
389 }
390
391 {
392 char dataChar = '0' + ((get_time_usec() / 1000) % 31);
393 char next = 'A';
394 for (i = 0; i < msg_size; ++i) {
395 if (dataChar == '`')
396 {
397 msg[i] = next++;
398 ++dataChar;
399 }
400 else
401 msg[i] = dataChar++;
402
403 if (dataChar > 'z') {
404 dataChar = '`';
405 }
406 }
407 msg[msg_size] = '\0';
408 }
409
410 printf("TX Message is \"%s\"\n", msg);
411
412 retMsgSize = 2 * msg_size;
413 retMsg = malloc(retMsgSize);
414 if (retMsg == NULL)
415 {
416 fprintf(stderr, "Could not allocate received message buffer\n");
417 return EXIT_FAILURE;
418 }
419
420 flush(TCIOFLUSH);
421
422 for (test = 0; test <= 2; ++test)
423 {
424 long long usec_delay;
425 long long usec_to_now;
426 int rc;
427
428 printf("\n******** Test purge %s; expect %s ********\n"
429 " -- Flushing UART\n",
430 flushTestName[test], expected[test]);
431 flush(TCIOFLUSH);
432 usleep(msg_xmit_time_us);
433 flush(TCIOFLUSH);
434 usleep(100000);
435
436 usec_test_start = get_time_usec();
437 if (dev_fd >= 0)
438 rc = write(dev_fd, msg, msg_size);
439 else
440 rc = ftdi_write_data(ftdi, msg, msg_size);
441
442 if (rc != msg_size)
443 {
444 fprintf(stderr, "Data write was short: %d: %s\n",
445 rc, ftdi_get_error_string(ftdi));
446 exit(1);
447 }
448 usec_to_now = get_time_usec() - usec_test_start;
449 usec_delay = msg_xmit_time_us / 2 - usec_to_now;
450 if (usec_delay < 0)
451 usec_delay = 0;
452 printf(" -- %9.1f ms Write completes; delaying to TX midpoint (%.1f ms)\n",
453 usec_to_now * .001, usec_delay * .001);
454 if (usec_delay > 0)
455 usleep(usec_delay);
456
457 printf(" -- %9.1f ms Issuing %s flush (purge)\n",
458 (get_time_usec() - usec_test_start) * .001,
459 flushTestName[test]);
460 flush(flushQueueSelector[test]);
461
462 printf(" -- %9.1f ms Calling drain to wait for transmit to complete\n",
463 (get_time_usec() - usec_test_start) * .001);
464 drain();
465
466 usec_to_now = get_time_usec() - usec_test_start;
467
468 /* If input only flush, check drain time. */
469 if (flushQueueSelector[test] == TCIFLUSH &&
470 usec_to_now < (msg_xmit_time_us * 90ll) / 100ll)
471 {
472 usec_delay = (msg_xmit_time_us * 110ll) / 100ll - usec_to_now;
473 printf(" -- %9.1f ms Drain() completed too early; expected at least %.1f ms\n"
474 " Delaying for %.1f ms\n",
475 usec_to_now * .001,
476 ((msg_xmit_time_us * 90ll) / 100ll) * .001,
477 usec_delay * .001);
478 usleep(usec_delay);
479 }
480 else
481 {
482 printf(" -- %9.1f ms Drain() reports completed; timing OK; delaying for 4 bytes\n",
483 (get_time_usec() - usec_test_start) * .001);
484 usleep(char_cnt_2_usec(4));
485 }
486
487 printf(" -- %9.1f ms Reading data.\n",
488 (get_time_usec() - usec_test_start) * .001);
489 if (dev_fd >= 0)
490 rc = read(dev_fd, retMsg, retMsgSize);
491 else
492 rc = ftdi_read_data(ftdi, retMsg, retMsgSize - 1);
493
494 usec_to_now = get_time_usec() - usec_test_start;
495 if (rc < 0)
496 {
497 fprintf(stderr, " -- %9.1f ms Read returned error %s\n",
498 usec_to_now * .001,
499 (dev_fd >= 0 ? strerror(errno) : ftdi_get_error_string(ftdi)));
500 exit(1);
501 }
502 retMsg[rc] = '\0';
503 printf(" -- %9.1f ms Read returns %d bytes; msg: \"%s\"\n",
504 usec_to_now * .001, rc, retMsg);
505
506 usleep(char_cnt_2_usec(10));
507
508 }
509
510
511 if (dev_fd >= 0)
512 {
513 close(dev_fd);
514 }
515 else
516 {
517 ftdi_usb_close(ftdi);
518 ftdi_free(ftdi);
519 }
520
521 exit (0);
522}
523
524/**********************************************************************
525 */
526static int ascii2int(const char * str, const char * pgm_name)
527{
528 int rc;
529 char * endptr;
530 if (str == NULL || strlen(str) == 0)
531 usage(pgm_name);
532 rc = strtol(str, &endptr, 10);
533 if (endptr == str || *endptr != '\0')
534 usage(pgm_name);
535 return rc;
536}
537
538
539/**********************************************************************
540 */
541static struct Baud_Table {
542 int32_t baud, baud_code;
543} baud_table [] =
544{
545 { 50, B50 },
546 { 75, B75 },
547 { 110, B110 },
548 { 134, B134 },
549 { 150, B150 },
550 { 200, B200 },
551 { 300, B300 },
552 { 600, B600 },
553 { 1200, B1200 },
554 { 1800, B1800 },
555 { 2400, B2400 },
556 { 4800, B4800 },
557 { 9600, B9600 },
558 { 19200, B19200 },
559 { 38400, B38400 },
560 { 57600, B57600 },
561 { 115200, B115200 },
562 { 230400, B230400 },
563 { -1, -1, }
564};
565
566/**********************************************************************
567 */
568static int baud_2_baud_code(int baud)
569{
570 struct Baud_Table *p;
571
572 for (p = baud_table ; p->baud != -1; ++p) {
573 if (p->baud == baud)
574 break;
575 }
576 return p->baud_code;
577}
578
579
580static long int char_cnt_2_usec(int char_count)
581{
582 long long bits = 8 + 1 + 1; /* Number of bits in each character */
583 bits *= (char_count == 0 ? 1 : char_count); /* Total number of bits */
584 bits *= 1000000; /* Convert to us */
585 lldiv_t parts = lldiv(bits, baud); /* Number of us for message */
586 return (parts.quot + 1);
587}
588
589
590static long int drain()
591{
592 long int rc = 0;
593 long long start_time = get_time_usec();
594 if (dev_fd >= 0)
595 rc = tcdrain(dev_fd);
596 else
597 {
598 long int sleep_interval = char_cnt_2_usec(10);
599 while (1) {
600 unsigned short modem_status = 0;
601 int rc = ftdi_poll_modem_status(ftdi, &modem_status);
602 if (rc < 0)
603 return -1;
604 if (modem_status & (1 << (6 + 8))) {
605 break;
606 }
607 usleep(sleep_interval);
608 }
609 }
610 if (rc < 0)
611 return rc;
612 usleep(char_cnt_2_usec(2));
613 return get_time_usec() - start_time;
614}
615
616
617static int flush(int queue_selector)
618{
619 int rc;
620 if (dev_fd >= 0)
621 rc = tcflush(dev_fd, queue_selector);
622 else if (! broken_purge_test)
623 {
624 switch (queue_selector) {
625
626 case TCIOFLUSH:
627 rc = ftdi_tcioflush(ftdi);
628 break;
629
630 case TCIFLUSH:
631 rc = ftdi_tciflush(ftdi);
632 break;
633
634 case TCOFLUSH:
635 rc = ftdi_tcoflush(ftdi);
636 break;
637
638 default:
639 errno = EINVAL;
640 return -1;
641 }
642 }
643 else
644 {
645 switch (queue_selector) {
646
647 case TCIOFLUSH:
648 rc = ftdi_usb_purge_buffers(ftdi);
649 break;
650
651 case TCIFLUSH:
652 rc = ftdi_usb_purge_rx_buffer(ftdi);
653 break;
654
655 case TCOFLUSH:
656 rc = ftdi_usb_purge_tx_buffer(ftdi);
657 break;
658
659 default:
660 errno = EINVAL;
661 return -1;
662 }
663 }
664
665 return rc;
666}
667
668
669static long long int get_time_usec()
670{
671 struct timeval tv;
672 gettimeofday(&tv, NULL);
673 return tv.tv_sec * 1000000ll + tv.tv_usec;
674}