1

Summary:

I am currently working on a program on embedded Linux using termios. If I do not enable even parity, my program works fine. I start to have issues when I set PARENB active and PARODD inactive. My program is designed to be 8E1 at 115200 baud.

What happens is that the first time I run the program on a fresh boot with Even parity enabled, it will transmit a message. The transmitted message does send, but it doesn't send with the parity bit. The second time I run the program it fails at the tcsetattr function with the errno == EINVAL.

I am running this program on Raspberry Pi for debugging and then the program will be ported to a yocto environment.

Current Environment:

Raspberry pi: 4B Debian version: 10.10 GCC: (Raspbian 8.3.0-6+rpi1) 8.3.0.

My Question:

Does anyone have insight into what piece is missing? I have tried to do this myself as well as tried 2 people's online examples I found on GitHub. All of them have this issue.

The code that I am running is:

// C library headers
#include <stdio.h>
#include <string.h>

// Linux headers
#include <errno.h>  // Error integer and strerror() function
#include <fcntl.h>  // Contains file controls like O_RDWR
#include <stdlib.h>
#include <sys/types.h>
#include <termios.h>  // Contains POSIX terminal control definitions
#include <unistd.h>   // write(), read(), close()

#define SERIAL_DEVICE "/dev/ttyS0"

int main() {
    struct termios serial_port_settings;
    int fd;
    int retval;
    char buf[] = "hello world \n bye\n";
    char ch;
    int i;

    //SERAIL_DEVICE is "/dev/ttyS0"
    fd = open(SERIAL_DEVICE, O_RDWR);
    if (fd < 0) {
        perror("Failed to open SERIAL_DEVICE");
        exit(1);
    }

    retval = tcgetattr(fd, &serial_port_settings);
    if (retval < 0) {
        perror("Failed to get termios structure");
        exit(2);
    }

    //setting baud rate to B115200
    retval = cfsetospeed(&serial_port_settings, B115200);
    if (retval < 0) {
        perror("Failed to set 115200 output baud rate");
        exit(3);
    }
    retval = cfsetispeed(&serial_port_settings, B115200);
    if (retval < 0) {
        perror("Failed to set 115200 input baud rate");
        exit(4);
    }

    //parity and bytes, grabbed from documentation
    serial_port_settings.c_cflag |= PARENB;   //enable parity
    serial_port_settings.c_cflag &= ~PARODD;  //disable odd parity, therefore-> even parity
    serial_port_settings.c_cflag &= ~CSTOPB; //disable 2 stop bits, therefore-> 1 stop bit
    serial_port_settings.c_cflag &= ~CSIZE;  //remove size
    serial_port_settings.c_cflag |= CS8;     //8 data bits
    serial_port_settings.c_cflag |= CLOCAL | CREAD;  //ignore modem control lines, enable reciever
    serial_port_settings.c_lflag |= ICANON; // enable canonical mode

    
    retval = tcsetattr(fd, TCSANOW, &serial_port_settings);
    /*************** ERROR HAPPENS HERE ********************/
    /* errno is set to EINVAL here */
    
    if (retval < 0) {
        perror("Failed to set serial attributes");
        exit(5);
    }
    printf("Successfully set the baud rate\n");

    retval = write(fd, buf, 18);
    if (retval < 0) {
        perror("write on SERIAL_DEVICE failed");
        exit(6);
    }

    retval = tcdrain(fd);
    if (retval < 0) {
        perror("write on SERIAL_DEVICE failed");
        exit(6);
    }

    close(fd);
    return 0;
}
Victor.Wiedemann
  • 178
  • 1
  • 12
  • 1
    Just looking through the man pages it appears `ICANON` is for `c_cflag` & not `c_lflag` – jiveturkey Sep 23 '21 at 17:02
  • @jiveturkey Good catch. I did fix that in my second attempt but forgot to fix it on this one. I just tried my program again with that edit. The issue persists. I have edited my question to fix this. – Victor.Wiedemann Sep 23 '21 at 17:25
  • 1
    ICANON is in the c_lflag member. You had it correct originally, and it is now wrong. Read the man page yourself before blindly following comments. FWIW I added the necessary #include statements, compiled your code, and encountered no error. – sawdust Sep 23 '21 at 17:31
  • @sawdust, I thought I had fixed it. I should have double-checked. I had a feeling this would work for other people. I added my includes to the question, can you confirm if I had missed anything? What environment did you build/run yours on? I am curious if it is a Raspberry Pi issue. – Victor.Wiedemann Sep 23 '21 at 17:38
  • 2
    Suggest you add details of this embedded Linux board and your toolchain to your post. Retested the revised version, and same successful execution. This is on an x86 laptop. I do have an assortment of SBCs to test, but no RPi or Broadcom SoC. – sawdust Sep 23 '21 at 17:39
  • @sawdust, I updated the information here. Thank you for the tests for me. I may give this a go on my ubuntu machine. It may be a weird RPi issue. – Victor.Wiedemann Sep 23 '21 at 17:49
  • you never initialize your struct, it could just be all 0's as a starting place in x86-64 ubuntu, and could be garbage data in a different runtime... try tossing in a memset or bzero in there to initialize your stack based struct... – Grady Player Sep 23 '21 at 18:04
  • 1
    `Raspberry pi: 4B` At least on RPI3 had two uart drivers, the full one used for bluetooth and the other one is a mini UART driver that is very very limited. Most probably you are using that mini UART, and it does not support any modes. I see, rpi documentation changed, it's documented here: https://www.raspberrypi.org/documentation/computers/configuration.html#configuring-uarts – KamilCuk Sep 23 '21 at 18:09
  • @KamilCuk this confirms my assumption that there was a limitation of the Raspberry Pi system. I was able to get it running on Ubuntu. – Victor.Wiedemann Sep 23 '21 at 18:38

1 Answers1

0

@sawdust and @KamilCuk helped identify my issue. The software was not the problem. It looks like ttyS0 on the Raspberry Pi 4 isn't a fully functional COM port. Because of this trying to add a parity bit would fail.

The code above runs fine on a different Linux machine with a full-featured COM port.

Victor.Wiedemann
  • 178
  • 1
  • 12