0

I need to communicate with a piece of hardware over a USB virtual serial device. All I need is it to pass raw bytes back and forth at speed with the right UART settings, I do not ever want to use a terminal. The proof of concept software using termios isn't configuring the right bits and doesn't work unless I feed in a magic config string over stty before running.

I attempted to replicate every setting from stty that appears in the POSIX man page for termios.h and it still doesn't work, and now has a screen full of boilerplate flag setting code.

What should be the minimum configuration using termios.h to get a terminal-less serial port? And if there's any additions for Linux specifically I'll need to know those.

Thomas Dickey
  • 51,086
  • 7
  • 70
  • 105
davolfman
  • 266
  • 1
  • 9

2 Answers2

1
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <termios.h>

int serial_open(char *port, int baud)
{
    int fd;
    struct termios tty;
    if((fd = open(port, O_RDWR | O_NOCTTY | O_SYNC)) < 0)
    {
        return -1;
    }

    if(tcgetattr(fd, &tty) < 0)
    {
        return -1;
    }

    cfsetospeed(&tty, (speed_t)baud);
    cfsetispeed(&tty, (speed_t)baud);
    tty.c_cflag |= (CLOCAL | CREAD);
    tty.c_cflag &= ~CSIZE;
    tty.c_cflag |= CS8;
    tty.c_cflag &= ~PARENB;
    tty.c_cflag |= CSTOPB;
    tty.c_cflag &= ~CRTSCTS;
    tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
    tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
    tty.c_oflag &= ~OPOST;
    tty.c_cc[VMIN] = 1;
    tty.c_cc[VTIME] = 1;
    if(tcsetattr(fd, TCSANOW, &tty))
    {
        return -1;
    }

    return fd;
}

/* example usage */
int fd = serial_open("/dev/ttyUSB0", B9600);
anton-tchekov
  • 1,028
  • 8
  • 20
  • My code still isn't working so there's obviously something major I don't understand. stty doesn't report the same settings even while the program is active (it shows 0 baud and CS6 for example which can't be right). I have no idea if different processes see different settings or if my binary is just unable to set the port while running under root. I'll get back to this after I've split out some variables. Thanks though. I'll try to keep the question and answers general. – davolfman Jan 22 '20 at 21:04
  • I ended up having to use `cfmakeraw()` and then it worked. – davolfman Jan 27 '20 at 17:21
1

On Linux at least the function cfmakeraw() will set the flags in a struct termios serial settings blob for raw IO. In this case the workflow, with error checking removed, looks like:

int fd = open(portname, O_RDWR | O_NOCTTY);
struct termios portSettings;
tcgetattr(fd, &portSettings);
cfsetispeed(&portSettings, B115200);
cfsetospeed(&portSettings, B115200);
cfmakeraw(&portSettings);
tcsetattr(fd, TCSANOW, &portSettings);
davolfman
  • 266
  • 1
  • 9