Feature #14169
openusbftdi should set/support setting the FTDI latency timer
0%
Description
FTDI's USB serial chips support a pair of control commands to get and set the latency timer. The default is 16 ms, which seems to end up in the total latency of sending or receiving data; one can read a bit about this at https://projectgus.com/2011/10/notes-on-ftdi-latency-with-arduino/ and https://www.ftdichip.com/Support/Knowledgebase/index.html?an232beffectbuffsizeandlatency.htm (a more formal app note is at http://www.ftdichip.com/Documents/AppNotes/AN232B-04_DataLatencyFlow.pdf), and https://github.com/torvalds/linux/blob/8fe31e0995f048d16b378b90926793a0aa4af1e5/drivers/usb/serial/ftdi_sio.h#L302-L350. As you'd expect from the last link, the Linux driver supports these commands and exposes them to userland rather unhelpfully via their sysfs.
Why is this important? It has a very substantial effect on performance, at least with some traffic patterns. The default value of 16 ms allows Xmodem transmission using 1 KiB blocks over a 3 Mbit/s link at approximately 60 KiB/s; at 1 ms the same link and protocol will support ~220-250 KiB/s. This is because Xmodem has no sliding window or deferred acknowledgement: after each 1 KiB block is sent, the sender waits for a 1-byte ack from the receiver. Other applications (including interactive ones) with similar properties will suffer similarly from this added latency.
It's be nice to expose this as an ioctl, or perhaps to try setting it ourselves heuristically based on baud rate, but unfortunately an ioctl would require changing user software so I'm not sure what the most useful way is to expose it. The grossest kludge, similar to Linux's, would be a driver.conf parameter; note that we already have a comparable kludge there in the form of ignore-cd.
Updated by Joshua M. Clulow over 1 year ago
I have produced a test program that uses two FTDI devices wired together on the same host. It sends a single byte ping with a random value into the first port, and waits for it to arrive on the second port. We confirm that the sent value matches the received value and then aggregate timing statistics in a sliding window.
With the stock driver, one can definitely see the 16ms timer in effect:
$ /tmp/serialping ping/sec 64 avg 15.81 max 16.01 min 8.82 cur 15.92 ping/sec 63 avg 15.87 max 16.01 min 8.82 cur 15.94 ping/sec 63 avg 15.92 max 15.97 min 15.68 cur 15.93 ping/sec 63 avg 15.92 max 15.98 min 15.68 cur 15.92 ping/sec 63 avg 15.93 max 15.99 min 15.86 cur 15.93 ping/sec 63 avg 15.93 max 15.99 min 15.84 cur 15.93 ping/sec 63 avg 15.92 max 15.98 min 15.84 cur 15.92 ping/sec 63 avg 15.92 max 16.03 min 15.83 cur 15.93 ping/sec 63 avg 15.92 max 16.03 min 15.82 cur 15.92 ping/sec 63 avg 15.92 max 16.00 min 15.79 cur 15.93 ping/sec 63 avg 15.92 max 16.00 min 15.79 cur 15.93 ping/sec 63 avg 15.93 max 16.01 min 15.83 cur 15.95 ping/sec 63 avg 15.93 max 16.01 min 15.83 cur 15.92 ping/sec 63 avg 15.92 max 15.99 min 15.84 cur 15.92
With a modified driver, with the timer set to its minimum value of 1ms, the situation is much improved:
$ /tmp/serialping ping/sec 753 avg 0.97 max 0.99 min 0.90 cur 0.97 ping/sec 1002 avg 0.97 max 0.99 min 0.91 cur 0.97 ping/sec 1002 avg 0.97 max 0.99 min 0.91 cur 0.97 ping/sec 1002 avg 0.97 max 0.98 min 0.92 cur 0.98 ping/sec 1002 avg 0.97 max 0.98 min 0.90 cur 0.96 ping/sec 1002 avg 0.97 max 0.98 min 0.91 cur 0.97 ping/sec 1002 avg 0.97 max 0.98 min 0.91 cur 0.97 ping/sec 1002 avg 0.97 max 0.98 min 0.91 cur 0.97 ping/sec 1002 avg 0.97 max 0.98 min 0.91 cur 0.91 ping/sec 1002 avg 0.97 max 0.98 min 0.90 cur 0.97