Linux and MARK/SPACE Parity
When developing the libmip library, I faced the problem of MARK/SPACE parity under Linux. Since there is very few information available on this topic, I summarize here what I know about it. Comments and hints are greatly welcome.
For general information about serial ports, you may want to check out the Serial Programming Guide for POSIX Operating Systems by Michael R. Sweet.
Most serial port transmissions use EVEN, ODD or NO parity. EVEN or ODD parity allows the receiver to detect (not correct) single-bit errors at the expense of sending one additional bit per byte. This bit is transmitted between the data bits and the stop bit(s). With EVEN parity, the parity bit is set to 0 if there is an even number of 1's in the data bits. With ODD parity, the parity bit is set to 0 if there is an odd number of 1's in the data bits. With NO parity, the parity bit is not sent at all, i.e. the first stop bit immediately follows the data bits.
Beyond these three modes, there are two other modes: MARK and SPACE parity. With MARK parity, the parity bit is always 1 (independent of the data bits). With SPACE parity, the parity bit is always 0. MARK and SPACE parity does not allow detecting transmission errors, but it may be used as an additional information bit. Therefore, these modes are sometimes referred to as CS9 (9 data bits).
MARK and SPACE parity, although implemented in most hardware, are not defined in the POSIX standard. The manpage of of the Unix/Linux termios library, for instance, doesn't say a single word about these two parity modes. (Note that PARMRK has nothing to do with MARK parity.)
How can MARK and SPACE parity be used on these systems?
The modes 7M1 (7 data bits, MARK parity, 1 stop bit) and 7S1 (7 data bits, SPACE parity, 1 stop bit) can easily be emulated using 8N1 (8 data bits, NO parity, 1 stop bit) and setting the 8th data bit to 1 resp. 0. This is relatively simple to implement and cannot be distinguished by the receiver.
The mode 8M1 (8 data bits, MARK parity, 1 stop bit) can be emulated with 8N2. Instead of sending a parity bit and a stop bit, two stop bits are transmitted. Since stop bits are always 1 (mark bits), the two modes are equivalent.
The problematic mode is 8S1 (8 data bits, SPACE parity, 1 stop bit). It cannot be emulated with an additional data bit (CS9 doesn't exist in the termios manpage and is probably not implemented in the serial port driver neither) nor with an additional stop bit (since stop bits are always 1). Fortunately, this is not the end of the story. 8S1 may be achieved in two ways:
- Emulation with EVEN/ODD parity
- By means of the undocumented CMSPAR flag
These two methods are presented in the following paragraphs.
Emulating 8S1 with EVEN/ODD Parity
To emulate 8S1, set EVEN or ODD parity for each transmitted byte individually. I.e., before sending a byte, count the number of 1's in the byte to transmit and set the parity. If the number of 1's is odd, ODD parity has be set for the transmission of this byte. Otherwise, EVEN parity has to be used.
The disadvantage of this method is the additional (small) overhead and the fact that it is a "hack".
For an implementation example, check out the Mip.cpp source file of the libmip library.
Setting 8S1 or 8M1 with the undocumented CMSPAR flag
Although undocumented, many systems support the CMSPAR flag (control mode flag for MARK/SPACE parity) defined as:
#define CMSPAR 010000000000
If this flag is set together with the PARENB (parity enable), SPACE/MARK parity is used instead of EVEN/ODD parity. To select SPACE parity, use
tio.c_cflag |= PARENB | CMSPAR;
tio.c_cflag &= ~PARODD;
in the termios struct of the serial device (see the termios manpage). MARK parity is selected by
tio.c_cflag |= PARENB | CMSPAR | PARODD;
The disadvantage of this method is that it might not work on all systems.
For an implementation example, check out the Mip.cpp and termioscms.h files of the libmip library.