Linux, Serial Port, Non-Buffering Mode

Linux, serial port, non-buffering mode

Looks like I found out the solution.

/* set input mode (non-canonical, no echo,...) */
newtio.c_lflag = 0;

newtio.c_cc[VTIME] = 0; /* inter-character timer unused */
newtio.c_cc[VMIN] = 1; /* blocking read until 1 char received */

tcflush(fd, TCIFLUSH);
Taken from : http://tldp.org/HOWTO/Serial-Programming-HOWTO/x115.html

Linux Blocking vs. non Blocking Serial Read

The code you mention is IMO poorly coded and commented. That code does not conform to POSIX practices for portability as described in Setting Terminal Modes Properly and Serial Programming Guide for POSIX Operating Systems. That code does not mention that it uses non-canonical (aka raw) mode, and reuses the "blocking" and "nonblocking" terminology to describe the VMIN and VTIME attributes.

(The author of that code reports that it predates the POSIX standard, and hence its non-compliance. That is understandable, but to then post and advocate the use of old code that may not be portable (i.e. function as expected in an alternate situation) is questionable.)


The conventional definition of a "blocking" versus "nonblocking" read is based on "when" the read call will return to your program (and resume execute with the next statement) and whether there will be data stored in your program's read buffer. A blocking read is the default mode, unless non-blocking is requested by opening the serial terminal with the O_NONBLOCK or O_NDELAY flag.

Canonical mode

For a blocking canonical read call of a serial terminal, a line (aka record) of text will always be returned in the provided buffer (unless an error occurred). The read call will block (i.e. suspend execution of your program) for as long as it takes for a line termination character to be received and processed.

A nonblocking canonical read call of a serial terminal will always return "immediately". The read may or may not return any data.

If (since the previous read call) at least a line of text has been received and stored in the system buffer, then the oldest line will be removed from the system buffer and copied to the program's buffer. The return code will indicate the data length.

If (since the previous read call) a line-termination character has not been received and processed, then there is no (complete) line of text available. The read() will return an EAGAIN error (i.e. a -1 return code and errno set to EAGAIN). Your program can then perform some calculation, or request I/O from another device, or delay/sleep. Either after an arbitrary delay or by notification by poll() or select(), your program can retry the read().

An example program using blocking canonical mode for reads is included in this answer.

Non-canonical mode

When the serial terminal is configured for non-canonical mode, the termios c_cc array elements VMIN and VTIME should be used to control "blocking", but this requires that the terminal be opened in the default blocking mode, i.e. do not specify the O_NONBLOCK open flag.

Otherwise O_NONBLOCK will have precedence over the VMIN and VTIME specification, and read() will set errno to EAGAIN and immediately return -1 instead of 0 when there is no data available. (This is the behavior observed in recent Linux 3.x kernels; older 2.6.x kernels may behave differently.)

The termios man page describes (c_cc array index) VMIN as the "minimum number of characters for noncanonical read", and (c_cc array index) VTIME as the "timeout in deciseconds for noncanonical read".

VMIN should be adjusted by your program to accommodate the typical message or datagram length that is expected and/or the minimum size for data to retrieve & process per read().

VTIME should be adjusted by your program to accommodate the typical burstiness or arrival rate of serial data that is expected and/or the maximum time to wait for data or a datum.

The VMIN and VTIME values interact to determine the criterion for when read should return; their precise meanings depend on which of them are nonzero. There are four possible cases.

This web page explains it as:

  • VMIN = 0 and VTIME = 0

This is a completely non-blocking read - the call is satisfied immediately directly from the driver's input queue. If data are available, it's transferred to the caller's buffer up to nbytes and returned. Otherwise zero is immediately returned to indicate "no data". We'll note that this is "polling" of the serial port, and it's almost always a bad idea. If done repeatedly, it can consume enormous amounts of processor time and is highly inefficient. Don't use this mode unless you really, really know what you're doing.

  • VMIN = 0 and VTIME > 0

This is a pure timed read. If data are available in the input queue, it's transferred to the caller's buffer up to a maximum of nbytes, and returned immediately to the caller. Otherwise the driver blocks until data arrives, or when VTIME tenths expire from the start of the call. If the timer expires without data, zero is returned. A single byte is sufficient to satisfy this read call, but if more is available in the input queue, it's returned to the caller. Note that this is an overall timer, not an intercharacter one.

  • VMIN > 0 and VTIME > 0

A read() is satisfied when either VMIN characters have been transferred to the caller's buffer, or when VTIME tenths expire between characters. Since this timer is not started until the first character arrives, this call can block indefinitely if the serial line is idle. This is the most common mode of operation, and we consider VTIME to be an intercharacter timeout, not an overall one. This call should never return zero bytes read.

  • VMIN > 0 and VTIME = 0

This is a counted read that is satisfied only when at least VMIN characters have been transferred to the caller's buffer - there is no timing component involved. This read can be satisfied from the driver's input queue (where the call could return immediately), or by waiting for new data to arrive: in this respect the call could block indefinitely. We believe that it's undefined behavior if nbytes is less then VMIN.

Note when VMIN=1 that the VTIME specification will be irrelevant. The availability of any data will always satisfy the minimum criterion of a single byte, so the time criterion can be ignored (since it would be an intercharacter time specification with a nonzero VMIN). This special case was pointed out by @IanAbbot.


That code you mention configures "nonblocking" mode as VMIN=0 and VTIME=5. This will not cause the read() to return immediately like a nonblocking canonical read would; with that code a read() should always wait at least a half second before returning.

The conventional definition of a "nonblocking" is that your calling program is not preempted during the syscall and gets control back (almost) immediately.

To get an (unconditional and) immediate return (for a non-canonical read), set VMIN=0 and VTIME=0 (with the attendant warnings).

How to handle buffering serial data

To minimize the overhead of making many read() syscalls of small byte counts (e.g. the misguided solution of reading a byte at a time), use an intermediate buffer in your code.

The read() of the serial terminal should be in blocking mode to avoid a return code of zero bytes.

#define BLEN    1024
unsigned char rbuf[BLEN];
unsigned char *rp = &rbuf[BLEN];
int bufcnt = 0;

/* get a byte from intermediate buffer of serial terminal */
static unsigned char getbyte(void)
{
if ((rp - rbuf) >= bufcnt) {
/* buffer needs refill */
bufcnt = read(fd, rbuf, BLEN);
if (bufcnt <= 0) {
/* report error, then abort */
}
rp = rbuf;
}
return *rp++;
}

For proper termios initialization code for the serial terminal, see this answer. You should increase the VMIN parameter to something closer to the BLEN value or at least the length of longest expected message, and a VTIME of 1.

Now you can conveniently access the received data a byte at a time with minimal performance penalty.

#define MLEN    1024  /* choose appropriate value for message protocol */
int main()
{
unsigned char mesg[MLEN];
...

while (1) {
while (getbyte() != 0x10)
/* discard data until start found */ ;

length = 0;
while ((mesg[length] = getbyte()) != 0x11) {
/* accumulate data until end found */
length++;
}

/* process the message */
...


} /* loop for next message */
...
}

Note that your detection for a message frame is not robust.

If the data is binary and therefore can use the same values as these start and end bytes, then this parsing of the received data is prone to misaligned message frames.

See this answer for a description of a proper alogrithm.

Read from serial port without removing from the buffer (linux)

The serial port is accessed as though it is a file using a file descriptor so a fgetc ungetc method should work.

Canonical Mode Linux Serial Port

is it putting the 0xD0xA (CRLF) bytes at the beginning of the transmission line to tell the read() function that data is ready to be read?

Your question has essentially already been answered by my last comment to you in your other post.

Apparently you don't believe the man page or me, and are also unclear what "line delimiter", line termination, and EOL mean.

The "serial port" or "hardware" has no concept of a "beginning" or an "end" of the "transmission line". It's all just payload data to the U[S]ART.

Line termination only has context when using termios in canonical mode to read the serial terminal buffer.

See Linux serial drivers to understand how removed your userspace code is from the hardware.

Linux uses the newline character, or linefeed which has ASCII code 0x0A, as the line terminator, as clearly stated in the man page (which you have quoted).

Termios allows the definition of additional end-of-line characters, i.e. VEOL and VEOL2 for serial terminals.

Each and every occurrence of a line delimiter character can and will cause a (pending) canonical read() to return.

The line delimiter character will be the last character returned in the buffer, unless the user buffer is too small to contain the entire line.

The character defined for EOF, i.e. VEOF which defaults to the ASCII code 0x04 for EOT, is handled slightly different by termios.

The receipt of the EOF character causes a (pending) canonical read() to return just like a line delimiter character, but the EOF character is not stored in the returned buffer.

Hence, when the EOF is preceded by a line delimiter, read() will have a return code of zero, an actual empty line!

If you're such a Doubting Thomas, then you should cross-connect a pair of USB-RS232 adapters together, and test what happens when reading from a serial terminal using termios.

Use a terminal emulator program such as minicom on the first serial terminal to enter data, and use the following C program to see the canonical reads on the other serial terminal.

#define SERIALTERMINAL      "/dev/ttyUSB1"
#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; /* no parity bit */
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 &= ~INPCK;
tty.c_iflag &= ~(INLCR | ICRNL | IUCLC | IMAXBEL);
tty.c_iflag &= ~(IXON | IXOFF | IXANY); /* no SW flowcontrol */

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()
{
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, no 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 */
do {
unsigned char buf[83];
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);
}

Note that the program does not strip out '\r' characters (i.e. attribute IGNCR is cleared), but carriage return is not defined as a line delimiter either.

Hence carriage returns in this termios configuration have no special meaning, and are passed through just like any printable character.

So typing (the equivalent of) ABCDEFG^M^J is read as:

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

123^Mabc^J is read as:

Read 8: 0x31 0x32 0x33 0xd 0x61 0x62 0x63 0xa
"123.abc."

An alternate termios configuration could strip out the carriage returns or treat the carriage returns as a line delimiter.



Related Topics



Leave a reply



Submit