3 * Test for purge TX/RX functions.
5 * The chip must be wired to loop TX data to RX data (loopback).
7 * This program works with "standard" linux drivers and the FTDI1 library.
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.
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.
16 * This needs testing with other devices, which I do not have.
26 /* Prevent deprecated messages when building library */
27 #define _FTDI_DISABLE_DEPRECATED
30 #include <termios.h> // For baudcodes & linux UARTs
31 #include <sys/types.h>
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;
47 static const int latency_min = 2;
48 static const int latency_max = 255;
50 static volatile long long usec_test_start;
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();
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",
72 static const char * chip_types[] = {
84 # define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
89 /**********************************************************************
92 usage(const char *argv0)
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"
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"
114 "NOTE: To function correctly, this program requires a loopback connector\n"
115 " attached to the UART under test.\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);
125 /**********************************************************************
127 int main(int argc, char **argv)
133 unsigned char * retMsg;
135 char * note_default = NULL;
137 long int msg_xmit_time_us;
138 static struct option long_options[] = {{NULL},};
140 while ((c = getopt_long(argc, argv, "n:b:i:l:N:P", long_options, &option_index)) !=- 1)
146 baud = ascii2int(optarg, argv[0]);
149 if (optarg == NULL || strlen(optarg) != 1)
155 interface = INTERFACE_A;
160 interface = INTERFACE_B;
165 interface = INTERFACE_C;
170 interface = INTERFACE_D;
178 latency = ascii2int(optarg, argv[0]);
179 if (latency < latency_min || latency > latency_max)
181 fprintf(stderr, "latency [-l] must be an integer in the range %d..%d\n",
182 latency_min, latency_max);
185 latency_specified = 1;
188 msg_size = ascii2int(optarg, argv[0]);
191 fprintf(stderr, "msg-size [-n] must be an integer greater than 0\n");
199 broken_purge_test = 1;
208 if (optind == argc - 1)
210 // Exactly one extra argument- a dump file
211 dev_string = argv[optind];
213 else if (optind < argc)
215 // Too many extra args
219 baud_code = baud_2_baud_code(baud);
222 fprintf(stderr, "Invalid baud [-b]\n");
226 if (dev_string[0] == '/')
228 struct termios termios;
230 if (latency_specified) {
231 fprintf(stderr, "Latency (-l) option not support on this device; ignored\n");
234 if (broken_purge_test) {
235 fprintf(stderr, "Broken-purge (-P) option not support with Linux kernel driver\n");
239 dev_fd = open(dev_string, O_NOCTTY | O_RDWR);
242 fprintf(stderr, "Error opening Linux device \"%s\": %s\n",
243 dev_string, strerror(errno));
247 if (! isatty(dev_fd))
249 fprintf(stderr, "Not a TTY device: \"%s\"\n", dev_string);
253 if (tcgetattr(dev_fd, &termios) == -1)
255 fprintf(stderr, "Error getting TTY attributes for \"%s\": %s\n",
256 dev_string, strerror(errno));
260 note_default = "Linux kernel driver";
267 termios.c_cflag &= ~CSIZE;
268 termios.c_cflag |= CS8;
270 cfsetspeed(&termios, baud_code);
275 termios.c_cc[VMIN] = 1; // Character at a time input
276 termios.c_cc[VTIME] = 0; // with blocking
278 if (tcsetattr(dev_fd, TCSAFLUSH, &termios) == -1) {
279 fprintf(stderr, "Error setting TTY attributes for \"%s\": %s\n",
280 dev_string, strerror(errno));
287 if ((ftdi = ftdi_new()) == 0)
289 fprintf(stderr, "ftdi_new failed\n");
293 if (ftdi_set_interface(ftdi, interface) < 0)
295 fprintf(stderr, "ftdi_set_interface failed\n");
300 if (ftdi_usb_open_string(ftdi, dev_string) < 0)
302 fprintf(stderr,"Error opening ftdi device \"%s\": %s\n", dev_string,
303 ftdi_get_error_string(ftdi));
308 if(ftdi_set_latency_timer(ftdi, (unsigned char) latency))
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));
321 if (ftdi_set_line_property2(ftdi, BITS_8, STOP_BIT_1, NONE, BREAK_OFF) < 0)
323 fprintf(stderr,"Error setting line properties ftdi device \"%s\": %s\n", dev_string,
324 ftdi_get_error_string(ftdi));
329 if (ftdi_set_baudrate(ftdi, baud) < 0)
331 fprintf(stderr,"Error setting baud rate for ftdi device \"%s\": %s\n", dev_string,
332 ftdi_get_error_string(ftdi));
337 if (ftdi_setflowctrl(ftdi, SIO_DISABLE_FLOW_CTRL))
339 fprintf(stderr,"Error setting flow control for ftdi device \"%s\": %s\n", dev_string,
340 ftdi_get_error_string(ftdi));
345 if (broken_purge_test)
346 note_default = "libftdi w/ deprecated purge";
348 note_default = "libftdi w/ new flush methods";
351 printf("Purge (tcflush) test for device %s\n", dev_string);
352 printf("Note: %s\n", (note ? note : note_default));
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]);
360 printf("FTDI chip type is %d (unknown)\n", ftdi->type);
363 printf("# purge_test" );
364 for (c = 1; c < argc; ++c)
366 const char *p = argv[c];
374 printf(" '%s'", argv[c]);
376 printf(" %s", argv[c]);
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);
384 msg = malloc(msg_size + 1);
387 fprintf(stderr, "Could not allocate send message buffer\n");
392 char dataChar = '0' + ((get_time_usec() / 1000) % 31);
394 for (i = 0; i < msg_size; ++i) {
403 if (dataChar > 'z') {
407 msg[msg_size] = '\0';
410 printf("TX Message is \"%s\"\n", msg);
412 retMsgSize = 2 * msg_size;
413 retMsg = malloc(retMsgSize);
416 fprintf(stderr, "Could not allocate received message buffer\n");
422 for (test = 0; test <= 2; ++test)
424 long long usec_delay;
425 long long usec_to_now;
428 printf("\n******** Test purge %s; expect %s ********\n"
429 " -- Flushing UART\n",
430 flushTestName[test], expected[test]);
432 usleep(msg_xmit_time_us);
436 usec_test_start = get_time_usec();
438 rc = write(dev_fd, msg, msg_size);
440 rc = ftdi_write_data(ftdi, msg, msg_size);
444 fprintf(stderr, "Data write was short: %d: %s\n",
445 rc, ftdi_get_error_string(ftdi));
448 usec_to_now = get_time_usec() - usec_test_start;
449 usec_delay = msg_xmit_time_us / 2 - usec_to_now;
452 printf(" -- %9.1f ms Write completes; delaying to TX midpoint (%.1f ms)\n",
453 usec_to_now * .001, usec_delay * .001);
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]);
462 printf(" -- %9.1f ms Calling drain to wait for transmit to complete\n",
463 (get_time_usec() - usec_test_start) * .001);
466 usec_to_now = get_time_usec() - usec_test_start;
468 /* If input only flush, check drain time. */
469 if (flushQueueSelector[test] == TCIFLUSH &&
470 usec_to_now < (msg_xmit_time_us * 90ll) / 100ll)
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",
476 ((msg_xmit_time_us * 90ll) / 100ll) * .001,
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));
487 printf(" -- %9.1f ms Reading data.\n",
488 (get_time_usec() - usec_test_start) * .001);
490 rc = read(dev_fd, retMsg, retMsgSize);
492 rc = ftdi_read_data(ftdi, retMsg, retMsgSize - 1);
494 usec_to_now = get_time_usec() - usec_test_start;
497 fprintf(stderr, " -- %9.1f ms Read returned error %s\n",
499 (dev_fd >= 0 ? strerror(errno) : ftdi_get_error_string(ftdi)));
503 printf(" -- %9.1f ms Read returns %d bytes; msg: \"%s\"\n",
504 usec_to_now * .001, rc, retMsg);
506 usleep(char_cnt_2_usec(10));
517 ftdi_usb_close(ftdi);
524 /**********************************************************************
526 static int ascii2int(const char * str, const char * pgm_name)
530 if (str == NULL || strlen(str) == 0)
532 rc = strtol(str, &endptr, 10);
533 if (endptr == str || *endptr != '\0')
539 /**********************************************************************
541 static struct Baud_Table {
542 int32_t baud, baud_code;
566 /**********************************************************************
568 static int baud_2_baud_code(int baud)
570 struct Baud_Table *p;
572 for (p = baud_table ; p->baud != -1; ++p) {
580 static long int char_cnt_2_usec(int char_count)
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);
590 static long int drain()
593 long long start_time = get_time_usec();
595 rc = tcdrain(dev_fd);
598 long int sleep_interval = char_cnt_2_usec(10);
600 unsigned short modem_status = 0;
601 int rc = ftdi_poll_modem_status(ftdi, &modem_status);
604 if (modem_status & (1 << (6 + 8))) {
607 usleep(sleep_interval);
612 usleep(char_cnt_2_usec(2));
613 return get_time_usec() - start_time;
617 static int flush(int queue_selector)
621 rc = tcflush(dev_fd, queue_selector);
622 else if (! broken_purge_test)
624 switch (queue_selector) {
627 rc = ftdi_tcioflush(ftdi);
631 rc = ftdi_tciflush(ftdi);
635 rc = ftdi_tcoflush(ftdi);
645 switch (queue_selector) {
648 rc = ftdi_usb_purge_buffers(ftdi);
652 rc = ftdi_usb_purge_rx_buffer(ftdi);
656 rc = ftdi_usb_purge_tx_buffer(ftdi);
669 static long long int get_time_usec()
672 gettimeofday(&tv, NULL);
673 return tv.tv_sec * 1000000ll + tv.tv_usec;