Implement tc[io]flush methods & deprecate broken purge_buffers methods.
authorEric Schott <els6@psu.edu>
Fri, 4 Jan 2019 00:52:02 +0000 (19:52 -0500)
committerThomas Jarosch <thomas.jarosch@intra2net.com>
Fri, 18 Jan 2019 14:27:25 +0000 (15:27 +0100)
The definitions for the SIO_RESET_PURGE_RX or SIO_RESET_PURGE_TX values are
with respect to the FTDI chip, not the CPU.  That is, when the FTDI chip
receives a USB control transfer request with the command SIO_RESET_PURGE_RX,
the FTDI chip empties the FIFO containing data received from the CPU awaiting
transfer out the serial port to the connected serial device (e.g., a modem).
Likewise, upon reception of the SIO_RESET_PURGE_TX command, the FTDI chip
empties the FIFO of data received from the attached serial device destined to
be transmitted to the CPU.

Unfortunately the coding of the previous releases of libfti assumed these
commands had the opposite effect. This resulted in the function
ftdi_usb_purge_tx_buffer clearing data received from the attached serial
device.  Similarly, the function ftdi_usb_purge_rx_buffer cleared the FTDI
FIFO containing data to be transmitted to the attached serial device.  More
seriously, this latter function clear the libftid's internal buffer of data
received from the serial device, destined to the application program.

This patch adds the ftdi_tciflush, ftdi_tcoflush, and ftdi_tcioflush functions
which emulate the Linux serial port tcflush(3) function.

examples/async.c
ftdipp/ftdi.cpp
ftdipp/ftdi.hpp
src/ftdi.c
src/ftdi.h
src/ftdi_stream.c

index 0589479..18989c1 100644 (file)
@@ -93,9 +93,9 @@ int main(int argc, char **argv)
         goto do_deinit;
     }
     ftdi_list_free(&devlist);
-    int err = ftdi_usb_purge_buffers(ftdi);
+    int err = ftdi_tcioflush(ftdi);
     if (err != 0) {
-        fprintf(stderr, "ftdi_usb_purge_buffer: %d: %s\n",
+        fprintf(stderr, "ftdi_tcioflush: %d: %s\n",
                 err, ftdi_get_error_string(ftdi));
         retval = -1;
         goto do_deinit;
index 7bf4ca6..dd777be 100644 (file)
@@ -27,6 +27,7 @@ This exception does not invalidate any other reasons why a work based
 on this file might be covered by the GNU General Public License.
 */
 #include <libusb.h>
+#define _FTDI_DISABLE_DEPRECATED
 #include "ftdi.hpp"
 #include "ftdi_i.h"
 #include "ftdi.h"
@@ -168,6 +169,32 @@ int Context::flush(int mask)
     return ret;
 }
 
+int Context::tcflush(int mask)
+{
+    int ret;
+
+    switch (mask & (Input | Output)) {
+    case Input:
+        ret = ftdi_tciflush(d->ftdi);
+        break;
+
+    case Output:
+        ret = ftdi_tcoflush(d->ftdi);
+        break;
+
+    case Input | Output:
+        ret = ftdi_tcioflush(d->ftdi);
+        break;
+
+    default:
+        // Emulate behavior of previous version.
+        ret = 1;
+        break;
+    }
+
+    return ret;
+}
+
 int Context::set_interface(enum ftdi_interface interface)
 {
     return ftdi_set_interface(d->ftdi, interface);
index 2289a84..a5ede3c 100644 (file)
@@ -85,7 +85,8 @@ public:
     int open(const std::string& description);
     int close();
     int reset();
-    int flush(int mask = Input|Output);
+    int DEPRECATED(flush)(int mask = Input|Output);
+    int tcflush(int mask = Input|Output);
     int set_interface(enum ftdi_interface interface);
     void set_usb_device(struct libusb_device_handle *dev);
 
index 52266d0..9414c2d 100644 (file)
@@ -35,6 +35,8 @@
 #include <stdlib.h>
 
 #include "ftdi_i.h"
+/* Prevent deprecated messages when building library */
+#define _FTDI_DISABLE_DEPRECATED
 #include "ftdi.h"
 #include "ftdi_version_i.h"
 
@@ -1016,6 +1018,7 @@ int ftdi_usb_reset(struct ftdi_context *ftdi)
 
 /**
     Clears the read buffer on the chip and the internal read buffer.
+    This is the correct behavior for an RX flush.
 
     \param ftdi pointer to ftdi_context
 
@@ -1023,6 +1026,36 @@ int ftdi_usb_reset(struct ftdi_context *ftdi)
     \retval -1: read buffer purge failed
     \retval -2: USB device unavailable
 */
+int ftdi_tciflush(struct ftdi_context *ftdi)
+{
+    if (ftdi == NULL || ftdi->usb_dev == NULL)
+        ftdi_error_return(-2, "USB device unavailable");
+
+    if (libusb_control_transfer(ftdi->usb_dev, FTDI_DEVICE_OUT_REQTYPE,
+                                SIO_RESET_REQUEST, SIO_TCIFLUSH,
+                                ftdi->index, NULL, 0, ftdi->usb_write_timeout) < 0)
+        ftdi_error_return(-1, "FTDI purge of RX buffer failed");
+
+    // Invalidate data in the readbuffer
+    ftdi->readbuffer_offset = 0;
+    ftdi->readbuffer_remaining = 0;
+
+    return 0;
+}
+
+
+/**
+    Clears the write buffer on the chip and the internal read buffer.
+    This is incorrect behavior for an RX flush.
+
+    \param ftdi pointer to ftdi_context
+
+    \retval  0: all fine
+    \retval -1: write buffer purge failed
+    \retval -2: USB device unavailable
+
+    \deprecated Use \ref ftdi_tciflush(struct ftdi_context *ftdi)
+*/
 int ftdi_usb_purge_rx_buffer(struct ftdi_context *ftdi)
 {
     if (ftdi == NULL || ftdi->usb_dev == NULL)
@@ -1042,6 +1075,7 @@ int ftdi_usb_purge_rx_buffer(struct ftdi_context *ftdi)
 
 /**
     Clears the write buffer on the chip.
+    This is correct behavior for a TX flush.
 
     \param ftdi pointer to ftdi_context
 
@@ -1049,6 +1083,32 @@ int ftdi_usb_purge_rx_buffer(struct ftdi_context *ftdi)
     \retval -1: write buffer purge failed
     \retval -2: USB device unavailable
 */
+int ftdi_tcoflush(struct ftdi_context *ftdi)
+{
+    if (ftdi == NULL || ftdi->usb_dev == NULL)
+        ftdi_error_return(-2, "USB device unavailable");
+
+    if (libusb_control_transfer(ftdi->usb_dev, FTDI_DEVICE_OUT_REQTYPE,
+                                SIO_RESET_REQUEST, SIO_TCOFLUSH,
+                                ftdi->index, NULL, 0, ftdi->usb_write_timeout) < 0)
+        ftdi_error_return(-1, "FTDI purge of TX buffer failed");
+
+    return 0;
+}
+
+
+/**
+    Clears the read buffer on the chip.
+    This is incorrect behavior for a TX flush.
+
+    \param ftdi pointer to ftdi_context
+
+    \retval  0: all fine
+    \retval -1: read buffer purge failed
+    \retval -2: USB device unavailable
+
+    \deprecated Use \ref ftdi_tcoflush(struct ftdi_context *ftdi)
+*/
 int ftdi_usb_purge_tx_buffer(struct ftdi_context *ftdi)
 {
     if (ftdi == NULL || ftdi->usb_dev == NULL)
@@ -1063,7 +1123,37 @@ int ftdi_usb_purge_tx_buffer(struct ftdi_context *ftdi)
 }
 
 /**
+    Clears the RX and TX FIFOs on the chip and the internal read buffer.
+    This is correct behavior for both RX and TX flush.
+
+    \param ftdi pointer to ftdi_context
+
+    \retval  0: all fine
+    \retval -1: read buffer purge failed
+    \retval -2: write buffer purge failed
+    \retval -3: USB device unavailable
+*/
+int ftdi_tcioflush(struct ftdi_context *ftdi)
+{
+    int result;
+
+    if (ftdi == NULL || ftdi->usb_dev == NULL)
+        ftdi_error_return(-3, "USB device unavailable");
+
+    result = ftdi_tcoflush(ftdi);
+    if (result < 0)
+        return -1;
+
+    result = ftdi_tciflush(ftdi);
+    if (result < 0)
+        return -2;
+
+    return 0;
+}
+
+/**
     Clears the buffers on the chip and the internal read buffer.
+    While coded incorrectly, the result is satisfactory.
 
     \param ftdi pointer to ftdi_context
 
@@ -1071,6 +1161,8 @@ int ftdi_usb_purge_tx_buffer(struct ftdi_context *ftdi)
     \retval -1: read buffer purge failed
     \retval -2: write buffer purge failed
     \retval -3: USB device unavailable
+
+    \deprecated Use \ref ftdi_tcioflush(struct ftdi_context *ftdi)
 */
 int ftdi_usb_purge_buffers(struct ftdi_context *ftdi)
 {
index f170881..832fa0b 100644 (file)
 #include <sys/time.h>
 #endif
 
+/* Define _FTDI_DISABLE_DEPRECATED to disable deprecated messages. */
+#ifdef _FTDI_DISABLE_DEPRECATED
+#define _Ftdi_Pragma(_msg)
+#else
+#define _Ftdi_Pragma(_msg) _Pragma(_msg)
+#endif
+
 /* 'interface' might be defined as a macro on Windows, so we need to
  * undefine it so as not to break the current libftdi API, because
  * struct ftdi_context has an 'interface' member
@@ -174,8 +181,45 @@ enum ftdi_module_detach_mode
 
 
 #define SIO_RESET_SIO 0
+
+/* ** WARNING ** SIO_RESET_PURGE_RX or SIO_RESET_PURGE_TX are values used
+ * internally by libftdi to purge the RX and/or TX FIFOs (buffers).
+ * APPLICATION PROGRAMS SHOULD NOT BE USING THESE VALUES. Application
+ * programs should use one of the ftdi_tciflush, ftdi_tcoflush, or
+ * ftdi_tcioflush functions which emulate the Linux serial port tcflush(3)
+ * function.
+ *
+ * History:
+ *
+ * The definitions for these values are with respect to the FTDI chip, not the
+ * CPU. That is, when the FTDI chip receives a USB control transfer request
+ * with the command SIO_RESET_PURGE_RX, the FTDI chip empties the FIFO
+ * containing data received from the CPU awaiting transfer out the serial
+ * port to the connected serial device (e.g., a modem). Likewise, upon
+ * reception of the SIO_RESET_PURGE_TX command, the FTDI chip empties the
+ * FIFO of data received from the attached serial device destined to be
+ * transmitted to the CPU.
+ *
+ * Unfortunately the coding of the previous releases of libfti assumed these
+ * commands had the opposite effect. This resulted in the function
+ * ftdi_usb_purge_tx_buffer clearing data received from the attached serial
+ * device.  Similarly, the function ftdi_usb_purge_rx_buffer cleared the
+ * FTDI FIFO containing data to be transmitted to the attached serial
+ * device.  More seriously, this latter function clear the libftid's
+ * internal buffer of data received from the serial device, destined
+ * to the application program.
+ */
+#ifdef __GNUC__
+#define SIO_RESET_PURGE_RX _Ftdi_Pragma("GCC warning \"SIO_RESET_PURGE_RX\" deprecated: - use tciflush() method") 1
+#define SIO_RESET_PURGE_TX _Ftdi_Pragma("GCC warning \"SIO_RESET_PURGE_RX\" deprecated: - use tcoflush() method") 2
+#else
+#pragma message("WARNING: You need to implement deprecated #define for this compiler")
 #define SIO_RESET_PURGE_RX 1
 #define SIO_RESET_PURGE_TX 2
+#endif
+/* New names for the values used internally to flush (purge). */
+#define SIO_TCIFLUSH 2
+#define SIO_TCOFLUSH 1
 
 #define SIO_DISABLE_FLOW_CTRL 0x0
 #define SIO_RTS_CTS_HS (0x1 << 8)
@@ -195,14 +239,18 @@ enum ftdi_module_detach_mode
    (taken from libusb) */
 #define FTDI_URB_USERCONTEXT_COOKIE ((void *)0x1)
 
+#ifdef _FTDI_DISABLE_DEPRECATED
+#define DEPRECATED(func) func
+#else
 #ifdef __GNUC__
-#define DEPRECATED(func) func __attribute__ ((deprecated))
+#define DEPRECATED(func) __attribute__ ((deprecated)) func
 #elif defined(_MSC_VER)
 #define DEPRECATED(func) __declspec(deprecated) func
 #else
 #pragma message("WARNING: You need to implement DEPRECATED for this compiler")
 #define DEPRECATED(func) func
 #endif
+#endif
 
 struct ftdi_transfer_control
 {
@@ -509,9 +557,12 @@ extern "C"
 
     int ftdi_usb_close(struct ftdi_context *ftdi);
     int ftdi_usb_reset(struct ftdi_context *ftdi);
-    int ftdi_usb_purge_rx_buffer(struct ftdi_context *ftdi);
-    int ftdi_usb_purge_tx_buffer(struct ftdi_context *ftdi);
-    int ftdi_usb_purge_buffers(struct ftdi_context *ftdi);
+    int ftdi_tciflush(struct ftdi_context *ftdi);
+    int ftdi_tcoflush(struct ftdi_context *ftdi);
+    int ftdi_tcioflush(struct ftdi_context *ftdi);
+    int DEPRECATED(ftdi_usb_purge_rx_buffer(struct ftdi_context *ftdi));
+    int DEPRECATED(ftdi_usb_purge_tx_buffer(struct ftdi_context *ftdi));
+    int DEPRECATED(ftdi_usb_purge_buffers(struct ftdi_context *ftdi));
 
     int ftdi_set_baudrate(struct ftdi_context *ftdi, int baudrate);
     int ftdi_set_line_property(struct ftdi_context *ftdi, enum ftdi_bits_type bits,
index 1dc46ea..75f1ace 100644 (file)
@@ -172,9 +172,9 @@ ftdi_readstream(struct ftdi_context *ftdi,
     }
 
     /* Purge anything remaining in the buffers*/
-    if (ftdi_usb_purge_buffers(ftdi) < 0)
+    if (ftdi_tcioflush(ftdi) < 0)
     {
-        fprintf(stderr,"Can't Purge\n");
+        fprintf(stderr,"Can't flush FIFOs & buffers\n");
         return 1;
     }