Quantcast

[PATCH] Fixed purge & added purge_test.c; Fixed C++ bit-or's & const.

classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

[PATCH] Fixed purge & added purge_test.c; Fixed C++ bit-or's & const.

Eric Schott
The FTDI1 purge functionality with FT4232H was not implementing
behavior equivalent to Linux tcflush(3). Added examples/purge_test
which (when using a loopback connector) tests purge/tcflush for both
Linux and FTDI1 UARTs.  Testing showed the ftdi.h definitions for
SIO_RESET_PURGE_TX and SIO_RESET_PURGE_RX were "swapped" (i.e., the
former flushed the hardware's RX queue and the latter flushed the
hardware TX queue).  After correcting the definitions, the purge
function for the FT4232H works similarly to the behavoir to the Linux
driver for this chip.  While the Linux driver correctly purges the
FT232, neither the pre- or post-fix of the FTDI1 library flushed
correctly for the FT232.

For FTDI1 C++ implementation, corrected a const and changed the purge
(and ModemCtl) definitions to not use an enumeration value of zero for
a bit masks.  For the later, the enumerations were defined so programs
compiled with the old ftdi.hpp include would work as previously (doing
nothing when the enumeration with the zero value was bit-or'ed into
the argument).

The provided patch is at the user's risk.
No licensing of this patch can preclude its use by the governement of
the United States or The Applied Research Laboratory of The
Pennsylvania State University.
---
  examples/CMakeLists.txt                            |   2 +
  examples/purge_test.c                              | 602
+++++++++++++++++++++
  examples/purge_test_results/README.txt             |  14 +
  .../purge_test_results/purge-16550A-115200.lis     |  26 +
  examples/purge_test_results/purge-16550A-1200.lis  |  26 +
  .../purge_test_results/purge-ft232-kernel-1200.lis |  33 ++
  .../purge-ft232-libftdi1-post-fix-1200.lis         |  33 ++
  .../purge-ft232-libftdi1-pre-fix-1200.lis          |  34 ++
  .../purge-ft4232-kernel-115200.lis                 |  31 ++
  .../purge-ft4232-kernel-1200.lis                   |  31 ++
  .../purge-ft4232-libftdi1-post-fix-115200.lis      |  30 +
  .../purge-ft4232-libftdi1-post-fix-1200.lis        |  30 +
  .../purge-ft4232-libftdi1-pre-fix-115200.lis       |  32 ++
  .../purge-ft4232-libftdi1-pre-fix-1200.lis         |  32 ++
  ftdipp/ftdi.cpp                                    |  26 +-
  ftdipp/ftdi.hpp                                    |  12 +-
  src/ftdi.h                                         |   4 +-
  17 files changed, 984 insertions(+), 14 deletions(-)
  create mode 100644 examples/purge_test.c
  create mode 100644 examples/purge_test_results/README.txt
  create mode 100644 examples/purge_test_results/purge-16550A-115200.lis
  create mode 100644 examples/purge_test_results/purge-16550A-1200.lis
  create mode 100644 examples/purge_test_results/purge-ft232-kernel-1200.lis
  create mode 100644
examples/purge_test_results/purge-ft232-libftdi1-post-fix-1200.lis
  create mode 100644
examples/purge_test_results/purge-ft232-libftdi1-pre-fix-1200.lis
  create mode 100644
examples/purge_test_results/purge-ft4232-kernel-115200.lis
  create mode 100644
examples/purge_test_results/purge-ft4232-kernel-1200.lis
  create mode 100644
examples/purge_test_results/purge-ft4232-libftdi1-post-fix-115200.lis
  create mode 100644
examples/purge_test_results/purge-ft4232-libftdi1-post-fix-1200.lis
  create mode 100644
examples/purge_test_results/purge-ft4232-libftdi1-pre-fix-115200.lis
  create mode 100644
examples/purge_test_results/purge-ft4232-libftdi1-pre-fix-1200.lis

diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index 1263c62..bf21557 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -18,6 +18,7 @@ if (EXAMPLES)
      add_executable(serial_test serial_test.c)
      add_executable(baud_test baud_test.c)
      add_executable(stream_test stream_test.c)
+    add_executable(purge_test purge_test.c)
      add_executable(eeprom eeprom.c)

      # Linkage
@@ -30,6 +31,7 @@ if (EXAMPLES)
      target_link_libraries(serial_test ftdi1)
      target_link_libraries(baud_test ftdi1)
      target_link_libraries(stream_test ftdi1)
+    target_link_libraries(purge_test ftdi1)
      target_link_libraries(eeprom ftdi1)

      # libftdi++ examples
diff --git a/examples/purge_test.c b/examples/purge_test.c
new file mode 100644
index 0000000..d2c9001
--- /dev/null
+++ b/examples/purge_test.c
@@ -0,0 +1,602 @@
+/* purge_test.c
+ *
+ * Test for purge TX/RX functions.
+ *
+ * The chip must be wired to loop TX data to RX data (loopback).
+ *
+ * This program works with "standard" linux drivers and the FTDI1 library.
+ *
+ * Usage: purge_test [-b baud] [-i interface] [-n msg-size] [-N note]
device-specifier
+ *   See usage below for more information on command usage.
+ *
+ * This program works well with the FT4231H which is newer and has large
+ * FIFOs. This program does not work well with FT232, either pre or post
+ * switching the SIO_RESET_PURGE_TX/SIO_RESET_PURGE_RX values.
+ *
+ * This needs testing with other devices, which I do not have.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <signal.h>
+#include <errno.h>
+#include <ftdi.h>
+#include <termios.h>        // For baudcodes & linux UARTs
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+
+static struct ftdi_context *ftdi = NULL;
+static int dev_fd = -1;
+static char * dev_string = NULL;
+static int baud = 9600;
+static int baud_code = -1;
+static enum ftdi_interface interface = INTERFACE_A;
+static int msg_size = 80;
+
+
+static volatile long long usec_test_start;
+
+
+
+static int ascii2int(const char * str, const char * pgm_name);
+static int baud_2_baud_code(int baud);
+static long int char_cnt_2_usec(int char_count);
+static long int drain();
+static int flush(int queue_selector);
+static long long int get_time_usec();
+
+static const int flushQueueSelector[] = {
+    TCIFLUSH, TCOFLUSH, TCIOFLUSH }; /* See /usr/include/bits/termios.h */
+static const char * flushTestName[] = {
+  "Input-only", "Output-only", "Input+Output" };
+static const char * expected[] = {
+    "last half of message",
+    "first half of message",
+    "a handful of mid-message characters",
+};
+
+
+static const char * chip_types[] = {
+    "am",
+    "bm",
+    "2232C",
+    "R",
+    "2232H",
+    "4232H",
+    "232H",
+    "230X",
+};
+
+#ifndef ARRAY_SIZE
+#  define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
+#endif
+
+
+
+/**********************************************************************
+ */
+static void
+usage(const char *argv0)
+{
+   fprintf(stderr,
+           "Usage: %s [options...] device-specifier\n"
+           "Flush test for UARTS.\n"
+       " with loopback connector\n"
+           "    [-b baud]        baud rate (e.g., 300, 600, 1200,
...230400)\n"
+           "    [-i {a|b|c|d}]   FTDI interface for chips which have
multiple UARTS\n"
+           "    [-n msg-size]    Number of bytes in test message\n"
+           "    [-N note]        Note for the output\n"
+           "\n"
+           "    device-specifier String specifying the UART.  If the
first character\n"
+       "                     is the '/' character, the program assumes
a Linux UART\n"
+       "                     is to be tested and the string would be
something like\n"
+       "                     '/dev/ttyS0' or '/dev/ttyUSB0'. Otherwise,
the program\n"
+       "                     assumes an FTDI device is being tested
with the FTDI1\n"
+       "                     library. The device-specifier must be a
string\n"
+       "                     accepted by the ftdi_usb_open_string
function. An\n"
+       "                     example would be 'i:0x0403:0x6011[:index]'.\n"
+       "\n"
+       "NOTE: To function correctly, this program requires a loopback
connector\n"
+       "      attached to the UART under test.\n"
+           "\n"
+           "Adapted from stream_test.c 2017. Eric Schott <[hidden email]>\n"
+           "Copyright (C) 2009 Micah Dowty <[hidden email]>\n"
+           "Adapted for use with libftdi (C) 2010 Uwe Bonnes
<[hidden email]>\n",
+           argv0);
+   exit(1);
+}
+
+
+/**********************************************************************
+ */
+int main(int argc, char **argv)
+{
+    int c, i;
+    int option_index;
+    int test;
+    unsigned char * msg;
+    unsigned char * retMsg;
+    char * note = NULL;
+    size_t retMsgSize;
+    long int msg_xmit_time_us;
+    static struct option long_options[] = {{NULL},};
+
+    while ((c = getopt_long(argc, argv, "n:b:i:N:", long_options,
&option_index)) !=- 1)
+        switch (c)
+        {
+        case -1:
+            break;
+        case 'b':
+            baud = ascii2int(optarg, argv[0]);
+            break;
+        case 'i':
+            if (optarg == NULL || strlen(optarg) != 1)
+                usage(argv[0]);
+            switch (optarg[0])
+            {
+            case 'a':
+            case 'A':
+                interface = INTERFACE_A;
+                break;
+
+            case 'b':
+            case 'B':
+                interface = INTERFACE_B;
+                break;
+
+            case 'c':
+            case 'C':
+                interface = INTERFACE_C;
+                break;
+
+            case 'd':
+            case 'D':
+                interface = INTERFACE_D;
+                break;
+
+            default:
+                usage(argv[0]);
+            }
+            break;
+        case 'n':
+            msg_size = ascii2int(optarg, argv[0]);
+            if (msg_size < 1)
+            {
+                fprintf(stderr, "msg-size [-n] must be an integer
greater than 0\n");
+                usage(argv[0]);
+            }
+            break;
+        case 'N':
+            note = optarg;
+            break;
+        default:
+            usage(argv[0]);
+        }
+
+    if (optind == argc)
+        usage(argv[0]);
+
+    if (optind == argc - 1)
+    {
+        // Exactly one extra argument- a dump file
+        dev_string = argv[optind];
+    }
+    else if (optind < argc)
+    {
+        // Too many extra args
+        usage(argv[0]);
+    }
+
+    baud_code = baud_2_baud_code(baud);
+    if (baud_code < 1)
+    {
+        fprintf(stderr, "Invalid baud [-b]\n");
+        usage(argv[0]);
+    }
+
+    if (dev_string[0] == '/')
+    {
+        struct termios termios;
+        dev_fd = open(dev_string, O_NOCTTY | O_RDWR);
+        if (dev_fd < 0)
+        {
+            fprintf(stderr, "Error opening Linux device \"%s\": %s\n",
+                    dev_string, strerror(errno));
+            return EXIT_FAILURE;
+        }
+
+        if (! isatty(dev_fd))
+        {
+            fprintf(stderr, "Not a TTY device: \"%s\"\n", dev_string);
+            return EXIT_FAILURE;
+        }
+
+        if (tcgetattr(dev_fd, &termios) == -1)
+        {
+            fprintf(stderr, "Error getting TTY attributes for \"%s\":
%s\n",
+                    dev_string, strerror(errno));
+            return EXIT_FAILURE;
+        }
+
+        cfmakeraw(&termios);
+
+        termios.c_cflag &=
+            ~(CSTOPB | CRTSCTS);
+
+        termios.c_cflag &= ~CSIZE;
+        termios.c_cflag |= CS8;
+
+        cfsetspeed(&termios, baud_code);
+
+        termios.c_cflag |=
+            CLOCAL;
+
+        termios.c_cc[VMIN] = 1;    // Character at a time input
+        termios.c_cc[VTIME] = 0;    // with blocking
+
+        if (tcsetattr(dev_fd, TCSAFLUSH, &termios) == -1) {
+            fprintf(stderr, "Error setting TTY attributes for \"%s\":
%s\n",
+                    dev_string, strerror(errno));
+            return EXIT_FAILURE;
+        }
+    }
+    else
+    {
+
+        if ((ftdi = ftdi_new()) == 0)
+        {
+            fprintf(stderr, "ftdi_new failed\n");
+            return EXIT_FAILURE;
+        }
+
+        if (ftdi_set_interface(ftdi, interface) < 0)
+        {
+            fprintf(stderr, "ftdi_set_interface failed\n");
+            ftdi_free(ftdi);
+            return EXIT_FAILURE;
+        }
+
+        if (ftdi_usb_open_string(ftdi, dev_string) < 0)
+        {
+            fprintf(stderr,"Error opening ftdi device \"%s\": %s\n",
dev_string,
+                    ftdi_get_error_string(ftdi));
+            ftdi_free(ftdi);
+            return EXIT_FAILURE;
+        }
+
+        if (ftdi_set_line_property2(ftdi, BITS_8, STOP_BIT_1, NONE,
BREAK_OFF) < 0)
+        {
+            fprintf(stderr,"Error setting line properties ftdi device
\"%s\": %s\n", dev_string,
+                    ftdi_get_error_string(ftdi));
+            ftdi_free(ftdi);
+            return EXIT_FAILURE;
+        }
+
+        if (ftdi_set_baudrate(ftdi, baud) < 0)
+        {
+            fprintf(stderr,"Error setting baud rate for ftdi device
\"%s\": %s\n", dev_string,
+                    ftdi_get_error_string(ftdi));
+            ftdi_free(ftdi);
+            return EXIT_FAILURE;
+        }
+
+        if (ftdi_setflowctrl(ftdi, SIO_DISABLE_FLOW_CTRL))
+        {
+            fprintf(stderr,"Error setting flow control for ftdi device
\"%s\": %s\n", dev_string,
+                    ftdi_get_error_string(ftdi));
+            ftdi_free(ftdi);
+            return EXIT_FAILURE;
+        }
+
+    }
+
+    printf("Purge (tcflush) test for device %s\n", dev_string);
+    if (note)
+    {
+        printf("Note: %s\n", note);
+    }
+    if (dev_fd < 0)
+    {
+        if (ftdi->type >0 && ftdi->type < ARRAY_SIZE(chip_types))
+            printf("FTDI chip type is %d (%s)\n",
+                   ftdi->type, chip_types[ftdi->type]);
+        else
+            printf("FTDI chip type is %d (unknown)\n", ftdi->type);
+    }
+
+    printf("# purge_test" );
+    for (c = 1; c < argc; ++c)
+    {
+        const char *p = argv[c];
+        while (*p != '\0')
+        {
+            if (*p == ' ')
+                break;
+            ++p;
+        }
+        if (*p == ' ')
+            printf(" '%s'", argv[c]);
+        else
+            printf(" %s", argv[c]);
+    }
+    printf("\n");
+
+    msg_xmit_time_us = char_cnt_2_usec(msg_size);
+    printf("%d chars at %d baud takes about %.0f ms to transmit\n",
msg_size,
+           baud, msg_xmit_time_us * .001);
+
+    msg = malloc(msg_size + 1);
+    if (msg == NULL)
+    {
+        fprintf(stderr, "Could not allocate send message buffer\n");
+        return EXIT_FAILURE;
+    }
+
+    {
+        char dataChar = '0' + ((get_time_usec() / 1000) % 31);
+        char next = 'A';
+        for (i = 0; i < msg_size; ++i) {
+            if (dataChar == '`')
+            {
+                msg[i] = next++;
+                ++dataChar;
+            }
+            else
+                msg[i] = dataChar++;
+
+            if (dataChar > 'z') {
+                dataChar = '`';
+            }
+        }
+        msg[msg_size] = '\0';
+    }
+
+    printf("TX Message is \"%s\"\n", msg);
+
+    retMsgSize = 2 * msg_size;
+    retMsg = malloc(retMsgSize);
+    if (retMsg == NULL)
+    {
+        fprintf(stderr, "Could not allocate received message buffer\n");
+        return EXIT_FAILURE;
+    }
+
+    flush(TCIOFLUSH);
+
+    for (test = 0; test <= 2; ++test)
+    {
+        long long usec_delay;
+        long long usec_to_now;
+        int rc;
+
+        printf("\n********  Test purge %s; expect %s  ********\n"
+           "  --              Flushing UART\n",
+               flushTestName[test], expected[test]);
+        flush(TCIOFLUSH);
+        usleep(msg_xmit_time_us);
+        flush(TCIOFLUSH);
+        usleep(100000);
+
+        usec_test_start = get_time_usec();
+        if (dev_fd >= 0)
+            rc = write(dev_fd, msg, msg_size);
+        else
+            rc = ftdi_write_data(ftdi, msg, msg_size);
+
+        if (rc != msg_size)
+        {
+            fprintf(stderr, "Data write was short: %d: %s\n",
+                    rc, ftdi_get_error_string(ftdi));
+            exit(1);
+        }
+        usec_to_now = get_time_usec() - usec_test_start;
+        usec_delay = msg_xmit_time_us / 2 - usec_to_now;
+        if (usec_delay < 0)
+            usec_delay = 0;
+        printf("  -- %9.1f ms Write completes; delaying to TX midpoint
(%.1f ms)\n",
+               usec_to_now * .001, usec_delay * .001);
+        if (usec_delay > 0)
+            usleep(usec_delay);
+
+        printf("  -- %9.1f ms Issuing %s flush (purge)\n",
+               (get_time_usec() - usec_test_start) * .001,
+               flushTestName[test]);
+        flush(flushQueueSelector[test]);
+
+        printf("  -- %9.1f ms Calling drain to wait for transmit to
complete\n",
+               (get_time_usec() - usec_test_start) * .001);
+        drain();
+
+        usec_to_now = get_time_usec() - usec_test_start;
+
+        /* If input only flush, check drain time. */
+        if (flushQueueSelector[test] == TCIFLUSH &&
+            usec_to_now < (msg_xmit_time_us * 90ll) / 100ll)
+        {
+            usec_delay = (msg_xmit_time_us * 110ll) / 100ll - usec_to_now;
+            printf("  -- %9.1f ms Drain() completed too early; expected
at least %.1f ms\n"
+                   "                  Delaying for %.1f ms\n",
+                   usec_to_now * .001,
+                   ((msg_xmit_time_us * 90ll) / 100ll) * .001,
+                   usec_delay * .001);
+            usleep(usec_delay);
+        }
+        else
+    {
+            printf("  -- %9.1f ms Drain() reports completed; timing OK;
delaying for 4 bytes\n",
+                   (get_time_usec() - usec_test_start) * .001);
+            usleep(char_cnt_2_usec(4));
+        }
+
+        printf("  -- %9.1f ms Reading data.\n",
+               (get_time_usec() - usec_test_start) * .001);
+        if (dev_fd >= 0)
+            rc = read(dev_fd, retMsg, retMsgSize);
+        else
+            rc = ftdi_read_data(ftdi, retMsg, retMsgSize - 1);
+
+        usec_to_now = get_time_usec() - usec_test_start;
+        if (rc < 0)
+        {
+            fprintf(stderr, "  -- %9.1f ms Read returned error %s\n",
+                    usec_to_now * .001,
+                    (dev_fd >= 0 ? strerror(errno) :
ftdi_get_error_string(ftdi)));
+            exit(1);
+        }
+        retMsg[rc] = '\0';
+        printf("  -- %9.1f ms Read returns %d bytes; msg: \"%s\"\n",
+               usec_to_now * .001, rc, retMsg);
+
+        usleep(char_cnt_2_usec(10));
+
+    }
+
+
+    if (dev_fd >= 0)
+    {
+        close(dev_fd);
+    }
+    else
+    {
+        ftdi_usb_close(ftdi);
+        ftdi_free(ftdi);
+    }
+
+    exit (0);
+}
+
+/**********************************************************************
+ */
+static int ascii2int(const char * str, const char * pgm_name)
+{
+    int rc;
+    char * endptr;
+    if (str == NULL || strlen(str) == 0)
+        usage(pgm_name);
+    rc = strtol(str, &endptr, 10);
+    if (endptr == str || *endptr != '\0')
+        usage(pgm_name);
+    return rc;
+}
+
+
+/**********************************************************************
+ */
+static struct Baud_Table {
+    int32_t baud, baud_code;
+} baud_table [] =
+{
+    { 50,     B50     },
+    { 75,     B75     },
+    { 110,    B110    },
+    { 134,    B134    },
+    { 150,    B150    },
+    { 200,    B200    },
+    { 300,    B300    },
+    { 600,    B600    },
+    { 1200,   B1200   },
+    { 1800,   B1800   },
+    { 2400,   B2400   },
+    { 4800,   B4800   },
+    { 9600,   B9600   },
+    { 19200,  B19200  },
+    { 38400,  B38400  },
+    { 57600,  B57600  },
+    { 115200, B115200 },
+    { 230400, B230400 },
+    { -1,     -1,     }
+};
+
+/**********************************************************************
+ */
+static int baud_2_baud_code(int baud)
+{
+    struct Baud_Table *p;
+
+    for (p = baud_table ; p->baud != -1; ++p) {
+        if (p->baud == baud)
+            break;
+    }
+    return p->baud_code;
+}
+
+
+static long int char_cnt_2_usec(int char_count)
+{
+    long long bits = 8 + 1 + 1;                 /* Number of bits in
each character */
+    bits *= (char_count == 0 ? 1 : char_count); /* Total number of bits */
+    bits *= 1000000;                            /* Convert to us */
+    lldiv_t parts = lldiv(bits, baud);          /* Number of us for
message */
+    return (parts.quot + 1);
+}
+
+
+static long int drain()
+{
+    long int rc = 0;
+    long long start_time = get_time_usec();
+    if (dev_fd >= 0)
+        rc = tcdrain(dev_fd);
+    else
+    {
+        long int sleep_interval = char_cnt_2_usec(10);
+        while (1) {
+            unsigned short modem_status = 0;
+            int rc = ftdi_poll_modem_status(ftdi, &modem_status);
+            if (rc < 0)
+                return -1;
+            if (modem_status & (1 << (6 + 8))) {
+                break;
+            }
+            usleep(sleep_interval);
+        }
+    }
+    if (rc < 0)
+        return rc;
+    usleep(char_cnt_2_usec(2));
+    return get_time_usec() - start_time;
+}
+
+
+static int flush(int queue_selector)
+{
+    int rc;
+    if (dev_fd >= 0)
+        rc = tcflush(dev_fd, queue_selector);
+    else
+    {
+        switch (queue_selector) {
+
+        case TCIOFLUSH:
+            rc = ftdi_usb_purge_buffers(ftdi);
+            break;
+
+        case TCIFLUSH:
+            rc = ftdi_usb_purge_rx_buffer(ftdi);
+            break;
+
+        case TCOFLUSH:
+            rc = ftdi_usb_purge_tx_buffer(ftdi);
+            break;
+
+        default:
+            errno = EINVAL;
+            return -1;
+        }
+    }
+
+    return rc;
+}
+
+
+static long long int get_time_usec()
+{
+    struct timeval tv;
+    gettimeofday(&tv, NULL);
+    return tv.tv_sec * 1000000ll + tv.tv_usec;
+}
diff --git a/examples/purge_test_results/README.txt
b/examples/purge_test_results/README.txt
new file mode 100644
index 0000000..8daa99e
--- /dev/null
+++ b/examples/purge_test_results/README.txt
@@ -0,0 +1,14 @@
+This directory contains test output for tests of the purge (tcflush())
+operations.  The 16550 files display the Linux driver default behavior.
+The remaining files display behavior for other tests cases.
+
+The tests run three tests which consist of
+  - Queuing a block of data for TX output
+  - Waiting for 1/2 the transmit time
+  - Issuing one of the three purge (flush) operations
+  - Waiting for the TX hardware queue to empty (drain)
+
+The FTDI1 library purge function for the FT4232H works similarly to
+the behavoir to the Linux driver for this chip.  While the Linux
+driver correctly purges the FT232, neither the pre- or post-fix of the
+FTDI1 library flushed correctly for the FT232.
diff --git a/examples/purge_test_results/purge-16550A-115200.lis
b/examples/purge_test_results/purge-16550A-115200.lis
new file mode 100644
index 0000000..3067786
--- /dev/null
+++ b/examples/purge_test_results/purge-16550A-115200.lis
@@ -0,0 +1,26 @@
+Purge (tcflush) test for device /dev/ttyS0
+Note: Standard 16550A UART
+# purge_test -b 115200 -n 100 '-NStandard 16550A UART' /dev/ttyS0
+100 chars at 115200 baud takes about 9 ms to transmit
+TX Message is
"KLMNOPQRSTUVWXYZ[\]^_AabcdefghijklmnopqrstuvwxyzBabcdefghijklmnopqrstuvwxyzCabcdefghijklmnopqrstuvwx"
+
+********  Test purge Input-only; expect last half of message ********
+  --       0.0 ms Write completes; delaying to TX midpoint (4.3 ms)
+  --       4.4 ms Issuing Input-only flush (purge)
+  --       4.4 ms Calling drain to wait for transmit to complete
+  --      10.5 ms Output (drain( should be completed; delaying for 4 bytes
+  --      10.5 ms Read returns 53 bytes; msg:
"zBabcdefghijklmnopqrstuvwxyzCabcdefghijklmnopqrstuvwx"
+
+********  Test purge Output-only; expect first half of message ********
+  --       0.0 ms Write completes; delaying to TX midpoint (4.3 ms)
+  --       4.7 ms Issuing Output-only flush (purge)
+  --       4.7 ms Calling drain to wait for transmit to complete
+  --       6.9 ms Output (drain( should be completed; delaying for 4 bytes
+  --       6.9 ms Read returns 64 bytes; msg:
"KLMNOPQRSTUVWXYZ[\]^_AabcdefghijklmnopqrstuvwxyzBabcdefghijklmno"
+
+********  Test purge Input+Output; expect a handful of mid-message
characters  ********
+  --       0.0 ms Write completes; delaying to TX midpoint (4.3 ms)
+  --       4.4 ms Issuing Input+Output flush (purge)
+  --       4.4 ms Calling drain to wait for transmit to complete
+  --       6.0 ms Output (drain( should be completed; delaying for 4 bytes
+  --       6.0 ms Read returns 16 bytes; msg: "zBabcdefghijklmn"
diff --git a/examples/purge_test_results/purge-16550A-1200.lis
b/examples/purge_test_results/purge-16550A-1200.lis
new file mode 100644
index 0000000..79e942f
--- /dev/null
+++ b/examples/purge_test_results/purge-16550A-1200.lis
@@ -0,0 +1,26 @@
+Purge (tcflush) test for device /dev/ttyS0
+Note: Standard 16550A UART
+# purge_test -b 1200 -n 100 '-NStandard 16550A UART' /dev/ttyS0
+100 chars at 1200 baud takes about 833 ms to transmit
+TX Message is
"?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_AabcdefghijklmnopqrstuvwxyzBabcdefghijklmnopqrstuvwxyzCabcdefghijkl"
+
+********  Test purge Input-only; expect last half of message ********
+  --       0.0 ms Write completes; delaying to TX midpoint (416.6 ms)
+  --     416.8 ms Issuing Input-only flush (purge)
+  --     416.8 ms Calling drain to wait for transmit to complete
+  --     849.9 ms Output (drain( should be completed; delaying for 4 bytes
+  --     849.9 ms Read returns 50 bytes; msg:
"qrstuvwxyzBabcdefghijklmnopqrstuvwxyzCabcdefghijkl"
+
+********  Test purge Output-only; expect first half of message ********
+  --       0.0 ms Write completes; delaying to TX midpoint (416.6 ms)
+  --     416.7 ms Issuing Output-only flush (purge)
+  --     416.8 ms Calling drain to wait for transmit to complete
+  --     550.6 ms Output (drain( should be completed; delaying for 4 bytes
+  --     550.6 ms Read returns 64 bytes; msg:
"?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_AabcdefghijklmnopqrstuvwxyzBabc"
+
+********  Test purge Input+Output; expect a handful of mid-message
characters  ********
+  --       0.0 ms Write completes; delaying to TX midpoint (416.6 ms)
+  --     416.7 ms Issuing Input+Output flush (purge)
+  --     416.8 ms Calling drain to wait for transmit to complete
+  --     551.4 ms Output (drain( should be completed; delaying for 4 bytes
+  --     551.5 ms Read returns 14 bytes; msg: "qrstuvwxyzBabc"
diff --git a/examples/purge_test_results/purge-ft232-kernel-1200.lis
b/examples/purge_test_results/purge-ft232-kernel-1200.lis
new file mode 100644
index 0000000..5d516fd
--- /dev/null
+++ b/examples/purge_test_results/purge-ft232-kernel-1200.lis
@@ -0,0 +1,33 @@
+Purge (tcflush) test for device /dev/ttyUSB0
+Note: FT232 with Linux drivers
+# purge_test -b 1200 '-NFT232 with Linux drivers' -n 60 /dev/ttyUSB0
+60 chars at 1200 baud takes about 500 ms to transmit
+TX Message is
"JKLMNOPQRSTUVWXYZ[\]^_AabcdefghijklmnopqrstuvwxyzBabcdefghij"
+
+********  Test purge Input-only; expect last half of message ********
+  --              Flushing UART
+  --       0.0 ms Write completes; delaying to TX midpoint (250.0 ms)
+  --     250.1 ms Issuing Input-only flush (purge)
+  --     250.1 ms Calling drain to wait for transmit to complete
+  --     275.2 ms Drain() completed too early; expected at least 450.0 ms
+                  Delaying for 274.8 ms
+  --     550.1 ms Reading data.
+  --     550.1 ms Read returns 32 bytes; msg:
"fghijklmnopqrstuvwxyzBabcdefghij"
+
+********  Test purge Output-only; expect first half of message ********
+  --              Flushing UART
+  --       0.0 ms Write completes; delaying to TX midpoint (250.0 ms)
+  --     250.1 ms Issuing Output-only flush (purge)
+  --     250.1 ms Calling drain to wait for transmit to complete
+  --     274.2 ms Drain() reports completed; timing OK; delaying for 4
bytes
+  --     307.7 ms Reading data.
+  --     307.7 ms Read returns 35 bytes; msg:
"JKLMNOPQRSTUVWXYZ[\]^_Aabcdefghijkl"
+
+********  Test purge Input+Output; expect a handful of mid-message
characters  ********
+  --              Flushing UART
+  --       0.0 ms Write completes; delaying to TX midpoint (250.0 ms)
+  --     250.1 ms Issuing Input+Output flush (purge)
+  --     250.2 ms Calling drain to wait for transmit to complete
+  --     274.8 ms Drain() reports completed; timing OK; delaying for 4
bytes
+  --     308.3 ms Reading data.
+  --     308.3 ms Read returns 8 bytes; msg: "efghijkl"
diff --git
a/examples/purge_test_results/purge-ft232-libftdi1-post-fix-1200.lis
b/examples/purge_test_results/purge-ft232-libftdi1-post-fix-1200.lis
new file mode 100644
index 0000000..a5106b2
--- /dev/null
+++ b/examples/purge_test_results/purge-ft232-libftdi1-post-fix-1200.lis
@@ -0,0 +1,33 @@
+Purge (tcflush) test for device i:0x403:0x6001:1
+Note: FT232 with FTDI1 drivers post-fix
+FTDI chip type is 0 (unknown)
+# purge_test -b 1200 '-NFT232 with FTDI1 drivers post-fix' -n 60
i:0x403:0x6001:1
+60 chars at 1200 baud takes about 500 ms to transmit
+TX Message is
"3456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_Aabcdefghijklmn"
+
+********  Test purge Input-only; expect last half of message ********
+  --              Flushing UART
+  --       0.3 ms Write completes; delaying to TX midpoint (249.7 ms)
+  --     250.1 ms Issuing Input-only flush (purge)
+  --     250.3 ms Calling drain to wait for transmit to complete
+  --     518.1 ms Drain() reports completed; timing OK; delaying for 4
bytes
+  --     551.6 ms Reading data.
+  --     568.3 ms Read returns 60 bytes; msg:
"3456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_Aabcdefghijklmn"
+
+********  Test purge Output-only; expect first half of message ********
+  --              Flushing UART
+  --       0.2 ms Write completes; delaying to TX midpoint (249.8 ms)
+  --     250.1 ms Issuing Output-only flush (purge)
+  --     250.3 ms Calling drain to wait for transmit to complete
+  --     267.1 ms Drain() reports completed; timing OK; delaying for 4
bytes
+  --     300.6 ms Reading data.
+  --     300.9 ms Read returns 0 bytes; msg: ""
+
+********  Test purge Input+Output; expect a handful of mid-message
characters  ********
+  --              Flushing UART
+  --       0.2 ms Write completes; delaying to TX midpoint (249.8 ms)
+  --     250.1 ms Issuing Input+Output flush (purge)
+  --     250.7 ms Calling drain to wait for transmit to complete
+  --     267.6 ms Drain() reports completed; timing OK; delaying for 4
bytes
+  --     301.1 ms Reading data.
+  --     301.4 ms Read returns 29 bytes; msg:
"RSTUVWXYZ[\]^_Aabcdefghijklmn"
diff --git
a/examples/purge_test_results/purge-ft232-libftdi1-pre-fix-1200.lis
b/examples/purge_test_results/purge-ft232-libftdi1-pre-fix-1200.lis
new file mode 100644
index 0000000..921bdb6
--- /dev/null
+++ b/examples/purge_test_results/purge-ft232-libftdi1-pre-fix-1200.lis
@@ -0,0 +1,34 @@
+Purge (tcflush) test for device i:0x403:0x6001:1
+Note: FT232 with FTDI1 drivers pre-fix
+FTDI chip type is 0 (unknown)
+# purge_test -b 1200 '-NFT232 with FTDI1 drivers pre-fix' -n 60
i:0x403:0x6001:1
+60 chars at 1200 baud takes about 500 ms to transmit
+TX Message is
"3456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_Aabcdefghijklmn"
+
+********  Test purge Input-only; expect last half of message ********
+  --              Flushing UART
+  --       0.3 ms Write completes; delaying to TX midpoint (249.7 ms)
+  --     250.1 ms Issuing Input-only flush (purge)
+  --     250.3 ms Calling drain to wait for transmit to complete
+  --     267.3 ms Drain() completed too early; expected at least 450.0 ms
+                  Delaying for 282.7 ms
+  --     550.1 ms Reading data.
+  --     550.3 ms Read returns 0 bytes; msg: ""
+
+********  Test purge Output-only; expect first half of message ********
+  --              Flushing UART
+  --       0.2 ms Write completes; delaying to TX midpoint (249.8 ms)
+  --     250.1 ms Issuing Output-only flush (purge)
+  --     250.3 ms Calling drain to wait for transmit to complete
+  --     267.3 ms Drain() reports completed; timing OK; delaying for 4
bytes
+  --     300.7 ms Reading data.
+  --     301.0 ms Read returns 29 bytes; msg:
"RSTUVWXYZ[\]^_Aabcdefghijklmn"
+
+********  Test purge Input+Output; expect a handful of mid-message
characters  ********
+  --              Flushing UART
+  --       0.2 ms Write completes; delaying to TX midpoint (249.8 ms)
+  --     250.1 ms Issuing Input+Output flush (purge)
+  --     250.9 ms Calling drain to wait for transmit to complete
+  --     268.2 ms Drain() reports completed; timing OK; delaying for 4
bytes
+  --     301.7 ms Reading data.
+  --     315.7 ms Read returns 31 bytes; msg:
"3456789:;<=>?@ABCDEFGHIJKLMNOPQ"
diff --git a/examples/purge_test_results/purge-ft4232-kernel-115200.lis
b/examples/purge_test_results/purge-ft4232-kernel-115200.lis
new file mode 100644
index 0000000..204cb78
--- /dev/null
+++ b/examples/purge_test_results/purge-ft4232-kernel-115200.lis
@@ -0,0 +1,31 @@
+Purge (tcflush) test for device /dev/ttyUSB1
+Note: FT4232-linux-driver
+# purge_test -b 115200 -n 100 -NFT4232-linux-driver /dev/ttyUSB1
+100 chars at 115200 baud takes about 9 ms to transmit
+TX Message is
">?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_AabcdefghijklmnopqrstuvwxyzBabcdefghijklmnopqrstuvwxyzCabcdefghijk"
+
+********  Test purge Input-only; expect last half of message ********
+  --       0.1 ms Write completes; delaying to TX midpoint (4.3 ms)
+  --       4.6 ms Issuing Input-only flush (purge)
+  --       4.7 ms Calling drain to wait for transmit to complete
+  --       5.6 ms Drain() completed too early; expected at least 7.8 ms
+                  Delaying for 3.9 ms
+  --       9.8 ms Output (drain) should be completed; delaying for 4 bytes
+  --      10.3 ms Reading data.
+  --      10.3 ms Read returns 60 bytes; msg:
"fghijklmnopqrstuvwxyzBabcdefghijklmnopqrstuvwxyzCabcdefghijk"
+
+********  Test purge Output-only; expect first half of message ********
+  --       0.0 ms Write completes; delaying to TX midpoint (4.3 ms)
+  --       4.4 ms Issuing Output-only flush (purge)
+  --       4.4 ms Calling drain to wait for transmit to complete
+  --       5.1 ms Output (drain) should be completed; delaying for 4 bytes
+  --       5.5 ms Reading data.
+  --       5.5 ms Read returns 58 bytes; msg:
">?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_Aabcdefghijklmnopqrstuvw"
+
+********  Test purge Input+Output; expect a handful of mid-message
characters  ********
+  --       0.0 ms Write completes; delaying to TX midpoint (4.3 ms)
+  --       4.4 ms Issuing Input+Output flush (purge)
+  --       4.4 ms Calling drain to wait for transmit to complete
+  --       5.5 ms Output (drain) should be completed; delaying for 4 bytes
+  --       5.9 ms Reading data.
+  --       5.9 ms Read returns 23 bytes; msg: "fghijklmnopqrstuvwxyzBa"
diff --git a/examples/purge_test_results/purge-ft4232-kernel-1200.lis
b/examples/purge_test_results/purge-ft4232-kernel-1200.lis
new file mode 100644
index 0000000..2235df2
--- /dev/null
+++ b/examples/purge_test_results/purge-ft4232-kernel-1200.lis
@@ -0,0 +1,31 @@
+Purge (tcflush) test for device /dev/ttyUSB1
+Note: FT4232-linux-driver
+# purge_test -b 1200 -n 100 -NFT4232-linux-driver /dev/ttyUSB1
+100 chars at 1200 baud takes about 833 ms to transmit
+TX Message is
"23456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_AabcdefghijklmnopqrstuvwxyzBabcdefghijklmnopqrstuvwxyz"
+
+********  Test purge Input-only; expect last half of message ********
+  --       0.0 ms Write completes; delaying to TX midpoint (416.6 ms)
+  --     416.7 ms Issuing Input-only flush (purge)
+  --     416.8 ms Calling drain to wait for transmit to complete
+  --     441.2 ms Drain() completed too early; expected at least 750.0 ms
+                  Delaying for 475.5 ms
+  --     916.7 ms Output (drain) should be completed; delaying for 4 bytes
+  --     950.1 ms Reading data.
+  --     950.2 ms Read returns 51 bytes; msg:
"cdefghijklmnopqrstuvwxyzBabcdefghijklmnopqrstuvwxyz"
+
+********  Test purge Output-only; expect first half of message ********
+  --       0.0 ms Write completes; delaying to TX midpoint (416.6 ms)
+  --     416.7 ms Issuing Output-only flush (purge)
+  --     416.8 ms Calling drain to wait for transmit to complete
+  --     441.3 ms Output (drain) should be completed; delaying for 4 bytes
+  --     474.9 ms Reading data.
+  --     474.9 ms Read returns 56 bytes; msg:
"23456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_Aabcdefghi"
+
+********  Test purge Input+Output; expect a handful of mid-message
characters  ********
+  --       0.0 ms Write completes; delaying to TX midpoint (416.7 ms)
+  --     416.9 ms Issuing Input+Output flush (purge)
+  --     417.0 ms Calling drain to wait for transmit to complete
+  --     442.2 ms Output (drain) should be completed; delaying for 4 bytes
+  --     475.6 ms Reading data.
+  --     475.6 ms Read returns 7 bytes; msg: "NOPQRST"
diff --git
a/examples/purge_test_results/purge-ft4232-libftdi1-post-fix-115200.lis
b/examples/purge_test_results/purge-ft4232-libftdi1-post-fix-115200.lis
new file mode 100644
index 0000000..6985763
--- /dev/null
+++ b/examples/purge_test_results/purge-ft4232-libftdi1-post-fix-115200.lis
@@ -0,0 +1,30 @@
+Purge (tcflush) test for device i:0x403:0xdaed
+Note: FT4232-libftdi1-post-fix
+FTDI chip type is 5 (4232H)
+# purge_test -b 115200 -n 100 -NFT4232-libftdi1-post-fix -i B
i:0x403:0xdaed
+100 chars at 115200 baud takes about 9 ms to transmit
+TX Message is
"?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_AabcdefghijklmnopqrstuvwxyzBabcdefghijklmnopqrstuvwxyzCabcdefghijkl"
+
+********  Test purge Input-only; expect last half of message ********
+  --       0.2 ms Write completes; delaying to TX midpoint (4.2 ms)
+  --       4.4 ms Issuing Input-only flush (purge)
+  --       4.5 ms Calling drain to wait for transmit to complete
+  --       9.0 ms Output (drain) should be completed; delaying for 4 bytes
+  --       9.4 ms Reading data.
+  --       9.8 ms Read returns 51 bytes; msg:
"pqrstuvwxyzBabcdefghijklmnopqrstuvwxyzCabcdefghijkl"
+
+********  Test purge Output-only; expect first half of message ********
+  --       0.2 ms Write completes; delaying to TX midpoint (4.1 ms)
+  --       4.4 ms Issuing Output-only flush (purge)
+  --       4.6 ms Calling drain to wait for transmit to complete
+  --       5.1 ms Output (drain) should be completed; delaying for 4 bytes
+  --       5.4 ms Reading data.
+  --       5.7 ms Read returns 52 bytes; msg:
"?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_Aabcdefghijklmnopqr"
+
+********  Test purge Input+Output; expect a handful of mid-message
characters  ********
+  --       0.2 ms Write completes; delaying to TX midpoint (4.2 ms)
+  --       4.6 ms Issuing Input+Output flush (purge)
+  --       4.9 ms Calling drain to wait for transmit to complete
+  --       5.2 ms Output (drain) should be completed; delaying for 4 bytes
+  --       5.5 ms Reading data.
+  --       6.7 ms Read returns 3 bytes; msg: "tuv"
diff --git
a/examples/purge_test_results/purge-ft4232-libftdi1-post-fix-1200.lis
b/examples/purge_test_results/purge-ft4232-libftdi1-post-fix-1200.lis
new file mode 100644
index 0000000..406d12d
--- /dev/null
+++ b/examples/purge_test_results/purge-ft4232-libftdi1-post-fix-1200.lis
@@ -0,0 +1,30 @@
+Purge (tcflush) test for device i:0x403:0xdaed
+Note: FT4232-libftdi1-post-fix
+FTDI chip type is 5 (4232H)
+# purge_test -b 1200 -n 100 -NFT4232-libftdi1-post-fix -i B i:0x403:0xdaed
+100 chars at 1200 baud takes about 833 ms to transmit
+TX Message is
"MNOPQRSTUVWXYZ[\]^_AabcdefghijklmnopqrstuvwxyzBabcdefghijklmnopqrstuvwxyzCabcdefghijklmnopqrstuvwxyz"
+
+********  Test purge Input-only; expect last half of message ********
+  --       0.4 ms Write completes; delaying to TX midpoint (416.3 ms)
+  --     416.8 ms Issuing Input-only flush (purge)
+  --     417.1 ms Calling drain to wait for transmit to complete
+  --     853.7 ms Output (drain) should be completed; delaying for 4 bytes
+  --     887.2 ms Reading data.
+  --     887.8 ms Read returns 51 bytes; msg:
"cdefghijklmnopqrstuvwxyzCabcdefghijklmnopqrstuvwxyz"
+
+********  Test purge Output-only; expect first half of message ********
+  --       0.5 ms Write completes; delaying to TX midpoint (416.2 ms)
+  --     416.7 ms Issuing Output-only flush (purge)
+  --     417.0 ms Calling drain to wait for transmit to complete
+  --     434.0 ms Output (drain) should be completed; delaying for 4 bytes
+  --     467.4 ms Reading data.
+  --     467.9 ms Read returns 51 bytes; msg:
"MNOPQRSTUVWXYZ[\]^_AabcdefghijklmnopqrstuvwxyzBabcd"
+
+********  Test purge Input+Output; expect a handful of mid-message
characters  ********
+  --       0.2 ms Write completes; delaying to TX midpoint (416.4 ms)
+  --     416.7 ms Issuing Input+Output flush (purge)
+  --     417.0 ms Calling drain to wait for transmit to complete
+  --     433.8 ms Output (drain) should be completed; delaying for 4 bytes
+  --     467.5 ms Reading data.
+  --     468.5 ms Read returns 2 bytes; msg: "cd"
diff --git
a/examples/purge_test_results/purge-ft4232-libftdi1-pre-fix-115200.lis
b/examples/purge_test_results/purge-ft4232-libftdi1-pre-fix-115200.lis
new file mode 100644
index 0000000..395c546
--- /dev/null
+++ b/examples/purge_test_results/purge-ft4232-libftdi1-pre-fix-115200.lis
@@ -0,0 +1,32 @@
+Purge (tcflush) test for device i:0x403:0xdaed
+Note: FT4232-libftdi1-pre-fix
+FTDI chip type is 5 (4232H)
+# purge_test -b 115200 -n 100 -NFT4232-libftdi1-pre-fix -i B i:0x403:0xdaed
+100 chars at 115200 baud takes about 9 ms to transmit
+TX Message is
"JKLMNOPQRSTUVWXYZ[\]^_AabcdefghijklmnopqrstuvwxyzBabcdefghijklmnopqrstuvwxyzCabcdefghijklmnopqrstuvw"
+
+********  Test purge Input-only; expect last half of message ********
+  --       0.1 ms Write completes; delaying to TX midpoint (4.2 ms)
+  --       4.4 ms Issuing Input-only flush (purge)
+  --       4.5 ms Calling drain to wait for transmit to complete
+  --       4.8 ms Drain() completed too early; expected at least 7.8 ms
+                  Delaying for 4.7 ms
+  --       9.6 ms Output (drain) should be completed; delaying for 4 bytes
+  --      10.0 ms Reading data.
+  --      10.2 ms Read returns 52 bytes; msg:
"JKLMNOPQRSTUVWXYZ[\]^_AabcdefghijklmnopqrstuvwxyzBab"
+
+********  Test purge Output-only; expect first half of message ********
+  --       0.1 ms Write completes; delaying to TX midpoint (4.3 ms)
+  --       4.4 ms Issuing Output-only flush (purge)
+  --       4.6 ms Calling drain to wait for transmit to complete
+  --       8.9 ms Output (drain) should be completed; delaying for 4 bytes
+  --       9.3 ms Reading data.
+  --       9.6 ms Read returns 50 bytes; msg:
"abcdefghijklmnopqrstuvwxyzCabcdefghijklmnopqrstuvw"
+
+********  Test purge Input+Output; expect a handful of mid-message
characters  ********
+  --       0.2 ms Write completes; delaying to TX midpoint (4.1 ms)
+  --       4.4 ms Issuing Input+Output flush (purge)
+  --       4.7 ms Calling drain to wait for transmit to complete
+  --       5.0 ms Output (drain) should be completed; delaying for 4 bytes
+  --       5.4 ms Reading data.
+  --       5.6 ms Read returns 0 bytes; msg: ""
diff --git
a/examples/purge_test_results/purge-ft4232-libftdi1-pre-fix-1200.lis
b/examples/purge_test_results/purge-ft4232-libftdi1-pre-fix-1200.lis
new file mode 100644
index 0000000..823f8e6
--- /dev/null
+++ b/examples/purge_test_results/purge-ft4232-libftdi1-pre-fix-1200.lis
@@ -0,0 +1,32 @@
+Purge (tcflush) test for device i:0x403:0xdaed
+Note: FT4232-libftdi1-pre-fix
+FTDI chip type is 5 (4232H)
+# purge_test -b 1200 -n 100 -NFT4232-libftdi1-pre-fix -i B i:0x403:0xdaed
+100 chars at 1200 baud takes about 833 ms to transmit
+TX Message is
"KLMNOPQRSTUVWXYZ[\]^_AabcdefghijklmnopqrstuvwxyzBabcdefghijklmnopqrstuvwxyzCabcdefghijklmnopqrstuvwx"
+
+********  Test purge Input-only; expect last half of message ********
+  --       0.2 ms Write completes; delaying to TX midpoint (416.4 ms)
+  --     416.7 ms Issuing Input-only flush (purge)
+  --     416.8 ms Calling drain to wait for transmit to complete
+  --     433.6 ms Drain() completed too early; expected at least 750.0 ms
+                  Delaying for 483.0 ms
+  --     917.0 ms Output (drain) should be completed; delaying for 4 bytes
+  --     950.6 ms Reading data.
+  --     951.6 ms Read returns 51 bytes; msg:
"KLMNOPQRSTUVWXYZ[\]^_AabcdefghijklmnopqrstuvwxyzBab"
+
+********  Test purge Output-only; expect first half of message ********
+  --       0.3 ms Write completes; delaying to TX midpoint (416.4 ms)
+  --     416.7 ms Issuing Output-only flush (purge)
+  --     416.9 ms Calling drain to wait for transmit to complete
+  --     851.5 ms Output (drain) should be completed; delaying for 4 bytes
+  --     884.9 ms Reading data.
+  --     885.4 ms Read returns 51 bytes; msg:
"abcdefghijklmnopqrstuvwxyzCabcdefghijklmnopqrstuvwx"
+
+********  Test purge Input+Output; expect a handful of mid-message
characters  ********
+  --       0.2 ms Write completes; delaying to TX midpoint (416.5 ms)
+  --     416.7 ms Issuing Input+Output flush (purge)
+  --     417.1 ms Calling drain to wait for transmit to complete
+  --     434.1 ms Output (drain) should be completed; delaying for 4 bytes
+  --     467.5 ms Reading data.
+  --     467.8 ms Read returns 2 bytes; msg: "ab"
diff --git a/ftdipp/ftdi.cpp b/ftdipp/ftdi.cpp
index 92b6e1f..e6bae47 100644
--- a/ftdipp/ftdi.cpp
+++ b/ftdipp/ftdi.cpp
@@ -144,12 +144,26 @@ int Context::reset()

  int Context::flush(int mask)
  {
-    int ret = 1;
+    int ret;

-    if (mask & Input)
-        ret &= ftdi_usb_purge_rx_buffer(d->ftdi);
-    if (mask & Output)
-        ret &= ftdi_usb_purge_tx_buffer(d->ftdi);
+    switch (mask & (Input | Output)) {
+    case Input:
+        ret = ftdi_usb_purge_rx_buffer(d->ftdi);
+        break;
+
+    case Output:
+        ret = ftdi_usb_purge_tx_buffer(d->ftdi);
+        break;
+
+    case Input | Output:
+        ret = ftdi_usb_purge_buffers(d->ftdi);
+        break;
+
+    default:
+        d->ftdi->error_str = "Wrong arguments";
+        ret = -101;
+        break;
+    }

      return ret;
  }
@@ -219,7 +233,7 @@ int Context::read_chunk_size()
      return chunk;
  }

-int Context::write(unsigned char *buf, int size)
+int Context::write(const unsigned char *buf, int size)
  {
      return ftdi_write_data(d->ftdi, buf, size);
  }
diff --git a/ftdipp/ftdi.hpp b/ftdipp/ftdi.hpp
index a438d96..ae6adbc 100644
--- a/ftdipp/ftdi.hpp
+++ b/ftdipp/ftdi.hpp
@@ -55,16 +55,16 @@ public:
       */
      enum Direction
      {
-        Input,
-        Output
+        Input = 0x2,
+        Output = 0x1,
      };

      /*! \brief Modem control flags.
       */
      enum ModemCtl
      {
-        Dtr,
-        Rts
+        Dtr = 0x2,
+        Rts = 0x1,
      };

      /* Constructor, Destructor */
@@ -100,7 +100,7 @@ public:

      /* I/O */
      int read(unsigned char *buf, int size);
-    int write(unsigned char *buf, int size);
+    int write(const unsigned char *buf, int size);
      int set_read_chunk_size(unsigned int chunksize);
      int set_write_chunk_size(unsigned int chunksize);
      int read_chunk_size();
@@ -108,7 +108,7 @@ public:

      /* Async IO
      TODO: should wrap?
-    int writeAsync(unsigned char *buf, int size);
+    int writeAsync(const unsigned char *buf, int size);
      void asyncComplete(int wait_for_more);
      */

diff --git a/src/ftdi.h b/src/ftdi.h
index bb66c53..acc1869 100644
--- a/src/ftdi.h
+++ b/src/ftdi.h
@@ -173,8 +173,8 @@ enum ftdi_module_detach_mode


  #define SIO_RESET_SIO 0
-#define SIO_RESET_PURGE_RX 1
-#define SIO_RESET_PURGE_TX 2
+#define SIO_RESET_PURGE_TX 1
+#define SIO_RESET_PURGE_RX 2

  #define SIO_DISABLE_FLOW_CTRL 0x0
  #define SIO_RTS_CTS_HS (0x1 << 8)
--
2.1.4



--
libftdi - see http://www.intra2net.com/en/developer/libftdi for details.
To unsubscribe send a mail to [hidden email]  

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: [PATCH] Fixed purge & added purge_test.c; Fixed C++ bit-or's & const.

Thomas Jarosch
Hello Eric,

thanks for your patch! Some questions below.

On Monday, 10 April 2017 15:54:15 CEST Eric Schott wrote:
> The provided patch is at the user's risk.
> No licensing of this patch can preclude its use by the governement of
> the United States or The Applied Research Laboratory of The
> Pennsylvania State University.

would you be ok with licensing the purge_test.c code
as GPL v2 like the other example codes?

That would still fulfill your "license requirement" above.

Perhaps it's a good idea to split the purge fix,
the new example code and the C++ fixes into three (logical) commits.

> diff --git a/src/ftdi.h b/src/ftdi.h
> index bb66c53..acc1869 100644
> --- a/src/ftdi.h
> +++ b/src/ftdi.h
> @@ -173,8 +173,8 @@ enum ftdi_module_detach_mode
>
>
>   #define SIO_RESET_SIO 0
> -#define SIO_RESET_PURGE_RX 1
> -#define SIO_RESET_PURGE_TX 2
> +#define SIO_RESET_PURGE_TX 1
> +#define SIO_RESET_PURGE_RX 2


this change puzzles me. I've searched for my original USB "API documentation"
received under NDA in 2003 and it specifies these control values:

0: Reset SIO
1: Purge RX buffer
2: Purge TX buffer.


Did you run your tests only with the FT4232 type chips?

Also you mention the purge doesn't work correctly,
even after fixing it?

I guess there's an equivalent purge function in D2XX,
it might make sense to take a look either at the sent USB URBs
or I might even disassemble the purge function of it.
(which is legal in Germany as long as it's to ensure compatibility)

Cheers,
Thomas


--
libftdi - see http://www.intra2net.com/en/developer/libftdi for details.
To unsubscribe send a mail to [hidden email]  

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: [PATCH] Fixed purge & added purge_test.c; Fixed C++ bit-or's & const.

Eric Schott
On Friday, 21 Mar 2017 18:23:38 +0200, Thomas Jarosch
<[hidden email]> wrote

> thanks for your patch! Some questions below.
>
> On Monday, 10 April 2017 15:54:15 CEST Eric Schott wrote:
>> The provided patch is at the user's risk.
>> No licensing of this patch can preclude its use by the governement of
>> the United States or The Applied Research Laboratory of The
>> Pennsylvania State University.
>
> would you be ok with licensing the purge_test.c code
> as GPL v2 like the other example codes?
>
> That would still fulfill your "license requirement" above.

Yes. This is fine. I included text as directed by my supervisor.  The
GPL is a difficult concept.

> Perhaps it's a good idea to split the purge fix,
> the new example code and the C++ fixes into three (logical) commits.

I will do that.

>> diff --git a/src/ftdi.h b/src/ftdi.h
>> index bb66c53..acc1869 100644
>> --- a/src/ftdi.h
>> +++ b/src/ftdi.h
>> @@ -173,8 +173,8 @@ enum ftdi_module_detach_mode
>>
>>
>>   #define SIO_RESET_SIO 0
>> -#define SIO_RESET_PURGE_RX 1
>> -#define SIO_RESET_PURGE_TX 2
>> +#define SIO_RESET_PURGE_TX 1
>> +#define SIO_RESET_PURGE_RX 2
>
>
> this change puzzles me. I've searched for my original USB "API documentation"
> received under NDA in 2003 and it specifies these control values:
> 0: Reset SIO
> 1: Purge RX buffer
> 2: Purge TX buffer.

I suspected you would be puzzled by the changes. I do not have
access to the NDA.

My desire was to as closely emulate the standard behavior of
the Linux serial port behavior of the ftdi_sio driver with respect
to purge (tcflush) and tcdrain functions. This is difficult based upon
the libftdi's different architecture (more on this shortly).

With the devices we control (e.g., GPS) consistent behavior is
important when needing to change baud rate or wait until all
data has been transmitted.

First, my observations of the Linux kernel with ftdi_sio driver

 1. ftdi_sio never uses the SIO_RESET_PURGE definitions.

 2. The kernel behavior for write: buffer the data and return
    immediately.  The driver outputs the buffered data in
    background.

 3. The kernel behavior for read is to always read data and
    store into an internal buffer.  The data is returned
    immediately to the user application with the read.

The test program (with a loopback) does
  *  A write of a block of data
  *  A delay for 1/2 of the anticipated transmit interval
  *  Issue one of the three flush calls (read, write and both)
  *  Do a drain function for the remainder of the data to be
     transmitted.
  *  Call the read function to obtain any buffered received data.

The Linux kernel behavior with all chips is
  1  Read flush:
     *  The first 1/2 of the data is discarded (which was received)
     *  The drain function waits the remaining output data has been
        sent out of the hardware.
     *  The read returns the second half of the data
  2  Write flush:
     *  The second have the data (in kernel buffers) is discarded
     *  The drain returns immediately (since there is no data do
        send)
     *  The read returns the first half of the data.
  3  Read & Write flush
     *  The first half of the data (already buffered) is discarded
     *  The buffered output data is flushed
     *  The drain returns effectively immediately
     *  The read returns a handful of characters in the middle
        of the message.

When using the Linux drivers, I have observed this (expected) behavior
with the test program with for the following
  *  16550 UART (which has shallow hardware buffers)
  *  FT232 (which appears to have shallow hardware buffers)
  *  FT4232 (which has deep hardware buffers).

Returning to the promised discussion of libftdi's implementation
architecture: it basically does not have the background processing
as does the kernel.  The standard read, write, and flush all
effectively perform hardware I/O via libusb. (This is somewhat of
a simplification. Please excuse me if I have some incorrectly
represented the library).

My observations with libftdi the FT4232 before swapping TX and RX
purge definitions was the read flush exhibited behavior of the Linux
write flush and the write flush exhibited the behavior of the
read flush.  I found it difficult to characterize the behavior on
the FT232 chip.

> Did you run your tests only with the FT4232 type chips?

I performed tests on the hardware I had available (16650, FT232,
and FT4232). Obviously I could not use libftdi for the 16650.

> Also you mention the purge doesn't work correctly,
> even after fixing it?

I was referring to the the FT232 chip only.  The FT4232's deep
FIFOs make libftdi's purge functions work correctly

> I guess there's an equivalent purge function in D2XX,
> it might make sense to take a look either at the sent USB URBs
> or I might even disassemble the purge function of it.
> (which is legal in Germany as long as it's to ensure compatibility)

I do not have the luxury of this.  Please do as you see fit.

That includes being free to discard this patch if you feel it is not
in the community's best interest.  I will not be offended. I will
continue to apply my patch.  My desire in submitting the patch was to
aid other developers when they may encounter similar issues.

It will be short time before I rearrange the patches.

Best regards.
Eric.


--
libftdi - see http://www.intra2net.com/en/developer/libftdi for details.
To unsubscribe send a mail to [hidden email]  

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: [PATCH] Fixed purge & added purge_test.c; Fixed C++ bit-or's & const.

Thomas Jarosch
Hello Eric,

>>> -#define SIO_RESET_PURGE_RX 1
>>> -#define SIO_RESET_PURGE_TX 2
>>> +#define SIO_RESET_PURGE_TX 1
>>> +#define SIO_RESET_PURGE_RX 2

I've downloaded the d2xx driver from FTDI for the first time
and checked their header file: PURGE_RX is 1, PURGE_TX is 2.
So it's identical to libftdi.

FT_Purge() also behaves the same as libftdi,
at least as far as I can tell.

So I'm rather inclined to change the current behavior
as it might cause breakage for existing users of the lib.

If you are curious, you could also download the d2xx library
for your platform and experiment with the FT_Purge() function.

Cheers,
Thomas

--
libftdi - see http://www.intra2net.com/en/developer/libftdi for details.
To unsubscribe send a mail to [hidden email]  

Loading...