1

I have a netcore app that opens the serial port, and writes "parity error" on the console once a parity error gets detected. It works fine in Windows 10 but can't get it to work under Linux.

My assumption is that the OS is not passing the parity error to netcore.

For checking the port settings I run:

stty -D /dev/ttyS0 -ignpar inpck

then I run:

stty -D /dev/ttyS0 -a 

and the settings seem to be properly set (-ignpar inpck) as expected.

Then I run my netcore 3 app but parity error is not detected.

So I run

stty -D /dev/ttyS0 -a 

for validating the settings, but these seem to be reset (-ignpar -inpck)

How can I force my app to run with the inpck attribute enabled?
Is there a way to make inpck to be enabled by default?

Thanks.

UPDATE: The netcore 3 app parity error detection works fine in windows 10, but it does not work under linux. My assumptions are that either:

  • A) netcore runtime is not passing the parity settings to the driver (not likely)
  • B) the OS is ignoring the instructions.
sawdust
  • 16,103
  • 3
  • 40
  • 50
Luis Alvarado Day
  • 217
  • 1
  • 3
  • 14
  • *"How can I force my app to run with the inpck fuse ..."* -- It's not a *"fuse"*, which is typically programmable only one time. The termios man page refers to INPCK and its kind as a "constant", "flag", and "attribute". So use the proper jargon, and pick any one of those three instead of *"fuse"*. – sawdust Jul 25 '19 at 04:57
  • 1
    The **stty** command is simply a method from the shell to utilize the termios API. Application programs are expected to use the termios API to configure the serial terminal to the exact requirements of the situation (rather than rely on an expected configuration on startup). If the app environment that you're using does not permit access to the termios API, then you may be using an inappropriate method. BTW "9-Bit serial" from user space is questionable. – sawdust Jul 25 '19 at 20:28
  • Thanks for clarifying the scope of stty Is not fully 9-bit. I just need to detect the parity error in order identify the first byte of a message which has this 9th bit forced to 1. So the netcore app sets the port parity to "space" and waits for the parity error for start reading the incoming buffer. It works fine in windows 10, but it does not work under linux. My assumptions are that either: A) netcore runtime is not passing the parity settings to the driver (not likely) B) the OS is ignoring the instructions. – Luis Alvarado Day Jul 26 '19 at 04:16
  • 1
    If your UART uses DMA or it's a USB-serial adapter, then properly identifying specific characters that have a parity error can be questionable. IMO you would need to use programmed I/O (rather than DMA) in order to detect a parity error in a specific received frame, i.e. status can be checked as each byte is received by the ISR. – sawdust Jul 26 '19 at 08:23
  • @sawdust thanks again you are providing me with good guidance. Regarding the PIO model I'll investigate how to set the serial port to this mode instead of DMA The strange thing is that not even a single error parity error is detected. Do you know any linux app that could explicitly react somehow to a parity error? I want to do the observation again putting my app logic and the netcore runtime aside. – Luis Alvarado Day Jul 26 '19 at 16:45
  • 1
    Setting the INPCK attribute (to test parity) is not a sufficient termios configuration. You also need to *clear* IGNPAR (to report the error) and *set* PARMRK (for verbose error indication). The char with error will be preceded by bytes of `0xFF` and `0x00` (i.e. total of three bytes). If you don't configure a verbose indication, then the error char is simply replaced with a null byte. – sawdust Jul 29 '19 at 20:31

1 Answers1

4

The stty command is simply a method from the shell to utilize the termios API.
Application programs are expected to use the termios API to configure the serial terminal to the exact requirements of the situation (rather than rely on an expected configuration on startup).
If the app environment that you're using does not permit access to the termios API, then you may be using an inappropriate method.

Do you know any linux app that could explicitly react somehow to a parity error?

The following C program reads lines (i.e. canonical mode) from a serial terminal, and is configured to detect a Mark (or 1) as the parity bit with an 8-bit character frame.

#define SERIALTERMINAL      "/dev/ttyS0"
#include <errno.h>
#include <fcntl.h> 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>

int set_interface_attribs(int fd, int speed)
{
    struct termios tty;

    if (tcgetattr(fd, &tty) < 0) {
        printf("Error from tcgetattr: %s\n", strerror(errno));
        return -1;
    }

    cfsetospeed(&tty, (speed_t)speed);
    cfsetispeed(&tty, (speed_t)speed);

    tty.c_cflag |= CLOCAL | CREAD;
    tty.c_cflag &= ~CSIZE;
    tty.c_cflag |= CS8;         /* 8-bit characters */
    tty.c_cflag |= PARENB;      /* enable parity */
    tty.c_cflag &= ~PARODD;     /* Even parity */
    tty.c_cflag |= CMSPAR;      /* force Even parity to SPACE */
    tty.c_cflag &= ~CSTOPB;     /* only need 1 stop bit */
    tty.c_cflag &= ~CRTSCTS;    /* no hardware flowcontrol */

    tty.c_lflag |= ICANON | ISIG;  /* canonical input */
    tty.c_lflag &= ~(ECHO | ECHOE | ECHONL | IEXTEN);

    tty.c_iflag &= ~IGNCR;  /* preserve carriage return */
    tty.c_iflag &= ~(INLCR | ICRNL | IUCLC | IMAXBEL);
    tty.c_iflag &= ~(IXON | IXOFF | IXANY);   /* no SW flowcontrol */
    tty.c_iflag |= IGNBRK;  /* ignore breaks */
    tty.c_iflag &= ~ISTRIP;
    tty.c_iflag &= ~IGNPAR; /* report error */
    tty.c_iflag |= INPCK;   /* test parity */
    tty.c_iflag |= PARMRK;  /* verbose parity err */

    tty.c_oflag &= ~OPOST;

    tty.c_cc[VEOL] = 0;
    tty.c_cc[VEOL2] = 0;
    tty.c_cc[VEOF] = 0x04;

    if (tcsetattr(fd, TCSANOW, &tty) != 0) {
        printf("Error from tcsetattr: %s\n", strerror(errno));
        return -1;
    }
    return 0;
}


int main(void)
{
    char *portname = SERIALTERMINAL;
    int fd;
    int wlen;

    fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC);
    if (fd < 0) {
        printf("Error opening %s: %s\n", portname, strerror(errno));
        return -1;
    }
    /*baudrate 115200, 8 bits, Space for parity, 1 stop bit */
    set_interface_attribs(fd, B115200);

    /* simple output */
    wlen = write(fd, "Hello!\n", 7);
    if (wlen != 7) {
        printf("Error from write: %d, %d\n", wlen, errno);
    }
    tcdrain(fd);    /* delay for output */


    /* simple canonical input, read lines */
    do {
        unsigned char buf[81];
        unsigned char *p;
        int rdlen;

        rdlen = read(fd, buf, sizeof(buf) - 1);
        if (rdlen > 0) {
            buf[rdlen] = 0;
            printf("Read %d:", rdlen);
            /* first display as hex numbers then ASCII */
            for (p = buf; rdlen-- > 0; p++) {
                printf(" 0x%x", *p);
                if (*p < ' ')
                    *p = '.';   /* replace any control chars */
            }
            printf("\n    \"%s\"\n\n", buf);
        } else if (rdlen < 0) {
            printf("Error from read: %d: %s\n", rdlen, strerror(errno));
        } else {  /* rdlen == 0 */
            printf("Nothing read. EOF?\n");
        }               
        /* repeat read */
    } while (1);
}

The program was executed on an old Linux (Ubuntu 14.04.2 LTS) PC that has a built-in 16550A serial port.

[    2.656593] 00:08: ttyS0 at I/O 0x3f8 (irq = 4, base_baud = 115200) is a 16550A

This serial port does not seem capable of transmitting 8-bits with parity (an 11-bit frame), but does seem capable of reading 8-bits with parity.

The serial data is generated from a SBC that has a 9-bit capable UART. An oscilloscope was used to capture frames to confirm that the 8S1 and 8E1 frames were 11 bits long.
(An FTDI USB-to-RS232 converter was not reliable in generating all parity configurations with 8-bit characters.)


When the sender is configured for 8-bits and Space for parity (which matches the program), the PC program reads "ABCDEFG\n" as:

Read 8: 0x41 0x42 0x43 0x44 0x45 0x46 0x47 0xa
    "ABCDEFG."

The data is read correctly.


When the sender is configured for 8-bits and Even parity, the PC program reads "ABCDEFG\n" as:

Read 14: 0x41 0x42 0xff 0x0 0x43 0x44 0xff 0x0 0x45 0xff 0x0 0x46 0x47 0xa
    "AB�.CD�.E�.FG."

The read (correctly) identifies three characters that have Mark instead of Space as the parity bit.
Each character with "parity error" is preceded by bytes of 0xFF 0x00 (i.e. a total of three bytes).

Note that when the actual received datum is 0xFF (with no parity error), termios will expand that datum to two bytes of 0xFF 0xFF. So beware that when the next datum is 0x00, then this is not the error indication. IOW reading 0xFF 0xFF 0x00 converts to actual data 0xFF 0x00.
But when the actual received datum is 0xFF with a parity error, then read returns 0xFF 0x00 0xFF (i.e. there is no expansion combined with the error indication).

sawdust
  • 16,103
  • 3
  • 40
  • 50