Add program to test flush (purge) functionality.
[libftdi] / examples / purge_test.c
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
36 static struct ftdi_context *ftdi = NULL;
37 static int dev_fd = -1;
38 static char * dev_string = NULL;
39 static int latency_specified = 0;
40 static int latency = 5;
41 static int baud = 9600;
42 static int baud_code = -1;
43 static enum ftdi_interface interface = INTERFACE_A;
44 static int msg_size = 80;
45 static int broken_purge_test = 0;
46
47 static const int latency_min = 2;
48 static const int latency_max = 255;
49
50 static volatile long long usec_test_start;
51
52
53
54 static int ascii2int(const char * str, const char * pgm_name);
55 static int baud_2_baud_code(int baud);
56 static long int char_cnt_2_usec(int char_count);
57 static long int drain();
58 static int flush(int queue_selector);
59 static long long int get_time_usec();
60
61 static const int flushQueueSelector[] = {
62     TCIFLUSH, TCOFLUSH, TCIOFLUSH }; /* See /usr/include/bits/termios.h */
63 static const char * flushTestName[] = {
64   "Input-only", "Output-only", "Input+Output" };
65 static const char * expected[] = {
66     "last portion of message",
67     "first portion of message",
68     "mid-message characters",
69 };
70
71
72 static 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  */
91 static void
92 usage(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  */
127 int 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  */
526 static 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  */
541 static 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  */
568 static 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
580 static 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
590 static 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
617 static 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
669 static 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 }