Skip to content

Driver Compatibility with SerialPortStream

Jason Curl edited this page Jan 22, 2017 · 18 revisions

This section is intended to capture some of the quirks and behaviours of serial drivers. The SerialPortStream implementation is very reliant on how the Operating System behaves and how individual drivers behave.

Table of Contents

Design Considerations in the Application Layer

Don't use Parity as a Reliable Check Mechanism

When designing your software to talk over the serial port, do not rely on the correctness of error checking (e.g. Parity). Instead, use packet based protocols with your own CRC checks (or use existing protocols for your design, such as HDLC).

Hardware Flow Control isn't Generally Reliable

When testing hardware flow control on multiple drivers, I've observed issues with flow control not releasing, or working as expected. See below for more details.

Windows

Prolific

I recommend always using the most recent version of the driver. Some USB serial port adapters come with outdated drivers that often cause Blue Screen crashes.

Asynchronous Settings for PL2303H

Issue? NO. Workaround in code available.

Data corruption can occur when using synchronous settings with the PL2303 drivers (observed with the first implementation in 2010). The code block below should block until data arrives and if data is already in the buffer, it should return immediately. However, using loop back devices showed the data to be corrupted (it appeared interleaved) indicating an issue in the driver.

 // Non-asynchronous behaviour
 timeouts.ReadIntervalTimeout = -1;
 timeouts.ReadTotalTimeoutConstant = 0;
 timeouts.ReadTotalTimeoutMultiplier = 0;

The solution is to use read interval time outs. This block hasn't been tested with the newer PL2303RA chip sets that have working drivers for Windows 8 and later, as the workaround to have different settings is also compatible with all current drivers.

 timeouts.ReadIntervalTimeout = 10;
 timeouts.ReadTotalTimeoutConstant = 100;
 timeouts.ReadTotalTimeoutMultiplier = 0;

Block Size on Write

Issue? NO. Workaround in code available.

It's my experience that a Prolific PL2303H driver will not send an arbitrary size block of data in one write operation. My tests show that it accepts large blocks, but finishes the overlapped I/O after 12032 or 11776 bytes. This makes it more important that the SerialPortStream buffer the write data and automatically resends the data when finished.

Note, low level serial timeouts used are:

 WriteTotalTimeoutMultipler = 0
 WriteTotalTimeoutConstant = 0

If WriteTotalTimeoutConstant is 500ms, then blocks of 6144 or 5888 bytes are sent (at a baud of 115200, 6144 bytes is 533ms, 5888 bytes is 511ms). This kind of behaviour doesn't seem to be a problem, one just needs to be aware that generally a WriteFile() won't write all data (which is why the SerialPortStream will buffer the data for you).

Writing and the TX_EMPTY Event

Issue? NO. Workaround in code available.

In a simple test where 128KB of random data is sent from a Prolific adapter, the WriteFile() events occur generally before the WaitCommEvent() with the flag TX_EMPTY. I would have expected this to be generated only once, as observed by the FTDI driver.

Hardware Flow Control

Issue? YES.

When enabling Hardware Flow Control for the PL2303H when reading from the serial port, we observe that it doesn't release the CTS line as expected.

The test setup was to implement an artificial delay in the serial thread loop (of 1000ms), to simulate a badly behaving process. While the serial thread isn't able to receive data, it is expected that the driver set the CTS line when too much data has arrived, and release the CTS line when data has been read from the buffer.

An external program, such as ZOC or TeraTerm was used to send data via an FTDI based serial port, or a physical 16550A UART (on an older computer).

The following PL2303 drivers were tested and all have problems:

  • Prolific Driver Win 7 x86; Date 26.07.2012; Version 3.4.36.247
  • Prolific Driver Win 7 x86; Date 31.07.2007; Version 3.2.0.0
  • Prolific Driver Win 7 x86; Date 03.08.2005; Version 2.0.0.19 (unsigned)
  • Hardware: SiteCom CN-116
We see from TeraTerm, that the first 3000 bytes are sent. Then no more data is sent. The SerialPortStream serial thread doesn't get an event indicating RX_CHAR anymore. We confirmed this with sniffing programs that hook into kernel32.dll that this is the case (not that there is a logic problem in the SerialPortStream).

The sender doesn't send any data, because it sees the PL2303H indicates to not send data. If the sender or the receiver (or both) have hardware flow control disabled, then data is sent (with the possibility of data overflow of course).

If the roles are reversed, so that data is received with hardware flow control enabled, but the PL2303 is sending data (instead of receiving), small chunks of data (4096 bytes, as configured to the drivers for the driver input buffer - independent of the SerialPortStream receive buffer) are sent, quickly for a short period of time, then pauses, repeating every second. This is the expected behaviour.

Workarounds - No Luck

There are no known workarounds with the PL2303 driver that I could find, other than to not use a PL2303 for receiving data, or by turning off hardware flow control completely.

  • Tried to perform a read regardless of the EV_RXCHAR event, to test if data is really present. No data is read, indicating that the sending application doesn't send the data and that EV_RXCHAR is behaving as expected.
  • Getting the DCB and rewriting the DCB doesn't help.
  • Trying to write the RTS parameter explicitly with the code below, resulted in error 87 (ERROR_INVALID_PARAMETER)
  • After fixing reading errors with the PL2303, retested and the issue with Hardware Flow control remains
 Native.DCB m_Dcb = new Native.DCB();
 Native.GetCommState(m_ComPortHandle, ref m_Dcb);
 if ((m_Dcb.Flags & Native.DcbFlags.RtsControlMask) == Native.DcbFlags.RtsControlHandshake) {
   if (!Native.EscapeCommFunction(m_ComPortHandle, Native.ExtendedFunctions.SETRTS)) {
     Console.WriteLine("Couldn't set RTS: {0}", Marshal.GetLastWin32Error());
   }
 }

Future Technology Devices International (FTDI)

The FTDI chipset is by far one of the most reliable and best working chip sets and drivers that I've encountered.

EOF Character

Issue? YES. Ensure you have sufficient time outs in your code.

The FTDI driver interprets the EOF character (set to default 0x1A). When reading data, it may be that a timeout of 100ms is too short. When using a test case to sending random data, with a buffer timeout of 100ms from a PL2303 chipset to a FTDI chipset (for reading), on reception of the 0x1A character, a small pause in reading is observed, so that no data is received, unless a second read is made, or a timeout of 180ms or more is used.

In particular, the small loop:

 // Receive sent data
 int rcv = 0;
 byte[] rcvbuf = new byte[150000];
 while (rcv < rcvbuf.Length) {
   int b = dst.Read(rcvbuf, rcv, rcvbuf.Length - rcv);
   if (b == 0) break;
   rcv += b;
 }

The last byte to be received, when dst.ReadTimeout is only 100ms, is the character 0x1A. It was observed, that by changing the DCB EOF character, the behaviour changes, so that the last byte is the same as the DCB EOF character. According to the documentation by FTDI D2XX Programmer's Guide, the EOF character should be ignored when the fBinary flag is used, as this is what is documented by MSDN DCB structure, any other flag other than fBinary being 1 is not supported).

In any case, this is simple to work around, simply increase the timeouts.

Com-0-Com

For Windows, com0com is the software driver that is used often to test SerialPortStream from Windows XP to Windows 10 (1607). The Windows SerialPort class will only work with COMx interfaces, not names such as CNCA0. This implementation will work with CNCA0, etc.

Parity Test Cases Fail

Issue? YES. Ensure that your applications are properly configured.

This driver is based on bytes, not individual bits, so some of the test cases will fail with this driver as we send 8-bit data to be received using a configuration of 7-bit data with parity. Naturally this will work with real serial ports as the data is indistinguishable.

FlexControl VSP Driver from FlexRadio Systems

Break State

Issue? NO. Resolved in SerialPortStream 2.0.3

See Issue #12 on GitHUB. When opening the serial port, it would clear the break state. On this particular driver, it caused the current thread to hang for 30s. The workaround implemented was not to set the Comm state on open (or on changes to any other property).

Behaviour can still be reproduced for those drivers when setting the Break state property direct.

Other General Issues

Break State is Not Supported

Not all serial port drivers support a break state.

  • Issue #2 describes a u-Blox Cellular Modem that raises exceptions.

Linux

You should also refer to the document dll/serialunix/README.linux for more information. Some of it is repeated here.

16550A

This appears to be the only driver that I've tested that works flawlessly, including receiving invalid data, and properly handling parity errors on receive.

Multiple Driver Issues

Parity detection

Chipset Affected
PL2303H YES
PL2303RA YES
FTDI YES
16550A NO

On receiving data that has parity errors, the affected chip sets receive invalid data, not just for the byte that has the parity error, but a block of bytes.

The test cases are

 libnserial/comptest
  SerialParityTest.Parity7O1ReceiveError
  SerialParityTest.Parity7E1ReceiveError

For example, for the case that the byte 0x45 has received the wrong parity, the following results might be expected:

In this case, bytes 0x3A to 0x45 were considered incorrect, even though only bytes 0x45 was the only incorrectly sent byte.

 0000030: 3031 3233 3435 3637 3839 ff00 3aff 003b  0123456789..:..;
 0000040: ff00 3cff 003d ff00 3eff 003f ff00 40ff  ..<..=..>..?..@.
 0000050: 0041 ff00 42ff 0043 ff00 44ff 0045 4647  .A..B..C..D..EFG

And in this case 0x44 was considered incorrect.

 0000030: 3031 3233 3435 3637 3839 3a3b 3c3d 3e3f  0123456789:;<=>?
 0000040: 4041 4243 ff00 4445 4647 4849 4a4b 4c4d  @ABC..DEFGHIJKLM
 0000050: 4e4f 5051 5253 5455 5657 5859 5a5b 5c5d  NOPQRSTUVWXYZ[\]

Hardware Flow Control

Closing or Disposing

Chipset Affected
PL2303H YES
PL2303RA YES
FTDI YES
16550A NO

If you close the serial port while send blocked (e.g. due to flow control being active and the remote side has the RTS signal disabled), calling the serial_close() method may take an excessive amount of time. Times are typically 30 seconds and blocks in the Linux close() system call.

As soon as the process is no longer send blocked, the close follows immediately.

Changing Properties

Chipset Affected
PL2303H YES
PL2303RA YES
FTDI YES
16550A YES

You should not change the properties of the serial port (baudrate, flow control, etc.) with anything that might call the tcsetattr() Linux system call. Doing so will block the thread forever, until data can be sent again.

Garbage Data on Start

Chipset Affected
PL2303H YES
PL2303RA NO
FTDI YES
16550A NO

In some cases, when ending a program and restarting it, unexpected data can be read which never appeared on the wire. For more details, see the notes in: libnserial/NOTES.bug-serialportclose.txt

The test program to recreate this (raw, without using the library) is given by: libnserial/comptest/kernelbug.c

On opening the serial port, an initial read could return a variable number of zero bytes before all other data, but would only occur when the first "real" data arrives over the wire. So it is not possible to previously flush the serial port using operating system commands, or to continuously read.

Tested was

  • Ubuntu 14.04.3 with kernel:
    • Linux leon-ubuntu 3.19.0-49-generic #55~14.04.1-Ubuntu SMP Fri Jan 22 11:23:34 UTC 2016 i686 i686 i686 GNU/Linux
  • Ubuntu 16.04
    • Linux leon-ubuntu 4.4.0-15-generic #31-Ubuntu SMP Fri Mar 18 19:08:31 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
    • Linux leon-ubuntu 4.4.0-59-generic #80-Ubuntu SMP Fri Jan 6 17:36:54 UTC 2017 i686 i686 i686 GNU/Linux
It was reported on Ubuntu Launchpad, but due to lack of time wasn't further investigated.

https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1542862