5

I have studied many useful threads and some tutorials, but I'm still having some issues with something that should be very simple. For reference here are some threads that I've perused:

How to implement a timeout in read function call?

how to open, read, and write from serial port in C

At any rate, I have a bit of a problem. My code works fine if I receive data. If I don't, the read() function stalls and the only way to get out of my program is to use kill -9 (NOTE: I use signal handling to signal to the thread reading the serial data to terminate. This is not the culprit, the read() call still stalls even if I have removed my signal handling). What I'm trying to do is to have a read that blocks and reads a chunk at a time (therefore saving CPU usage), however if the read receives no data, I wan't it to timeout.

Here are the settings that I'm applying to the port:

struct termios serial_struct;
serial_struct.c_cflag = B115200 | CS8 | CLOCAL | CREAD;
serial_struct.c_iflag = IGNPAR;
serial_struct.c_oflag = 0;
serial_struct.c_lflag = 0;
serial_struct.c_cc[VTIME] = 1;  // timeout after .1s that isn't working
serial_struct.c_cc[VMIN] = 64;  // want to read a chunk of 64 bytes at a given time

I then set these settings with tcsetattr() and confirm that the port received the settings via tcgetattr(). I'm thinking that my settings may be conflicting, because my reads appear to be blocking and wait until 64 bytes are received, but do not do anything with regards to the timeout. I understand that I can use select() to deal with a timeout, but I'm hoping to avoid the multiple system calls.

As always, thanks in advance for the help.

Community
  • 1
  • 1
It'sPete
  • 5,083
  • 8
  • 39
  • 72
  • 1
    Q: What is the device (e.g. /dev/ttyS0)? What is the device on this port (an RS232 COM port? Something else?) ALSO: this is an excellent link, if you're not already familiar with it: http://www.tldp.org/HOWTO/Serial-Programming-HOWTO/. You're probably interested in the "asynchronous I/O" parts. – paulsm4 Aug 06 '13 at 02:14
  • See https://stackoverflow.com/questions/25996171/linux-blocking-vs-non-blocking-serial-read BTW your termios code is incomplete, and therefore unreliable. See [Setting Terminal Modes Properly](https://www.gnu.org/software/libc/manual/html_node/Setting-Modes.html) and [Serial Programming Guide for POSIX Operating Systems](http://www.cmrr.umn.edu/~strupp/serial.html) – sawdust Mar 14 '22 at 00:11

1 Answers1

6

From man 3 termios:

MIN > 0; TIME > 0: TIME specifies the limit for a timer in tenths of a second. Once an initial byte of input becomes available, the timer is restarted after each further byte is received. read(2) returns either when the lesser of the number of bytes requested or MIN byte have been read, or when the inter-byte timeout expires. Because the timer is only started after the initial byte becomes available, at least one byte will be read.

Note that the timer does not start until at least one byte of data is received. After receiving that first data byte, the read will timeout if there is ever a gap of TIME tenths of a second between receiving consecutive data bytes.

Casey
  • 41,449
  • 7
  • 95
  • 125
  • 1
    AHA! So termios has no way to set a "real" timeout in the event that no data is being passed? How do I solve this then? With select()? – It'sPete Aug 06 '13 at 02:30
  • 1
    Yes: The link I cited above also discusses "select()". Q: What is the device? – paulsm4 Aug 06 '13 at 02:33
  • 1
    @It'sPete I think almost every program eventually grows to the point that you end up either (a) using `select` or `poll` to multiplex I/O or (b) handle separate streams with separate threads. May as well get it over with. – Casey Aug 06 '13 at 02:33
  • @Casey I'm only hoping to accomplish reading from one device, a single serial port that is configurable. One thread handles reading from the port and putting the data in a location where it can be accessed by consumers. I was hoping to avoid multiple system calls, because the read happens in a loop. However, if there is no other way to find out if I'm getting data, then it looks like I'll have to... – It'sPete Aug 06 '13 at 02:36
  • @It'sPete You can minimize the number of system calls by setting the descriptor to non-blocking mode and then only calling `select` when `read` returns `EAGAIN` or `EWOULDBLOCK`. – Casey Aug 06 '13 at 02:38
  • @Casey Yea, I was thinking about that to. My problem is that my block size is large enough that I would be exiting out of read() alot in a non-blocking mode. It looks like I'll have to use select() in the outer conditional to see if data is present and then use read() as I am. The performance hit shouldn't be too much of a detriment. Thanks for your answer and help! – It'sPete Aug 06 '13 at 02:42
  • 1
    **The above answer is incomplete**. VMIN and VTIME have no effect unless the serial teminal is in non-blocking mode *and* non-canonical mode is used. *"So termios has no way to set a "real" timeout in the event that no data is being passed?"* -- Use `VMIN = 0` and `VTIME = ` (*and* non-blocking, non-canonical mode). But don't expect to receive whole messages per syscall. – sawdust Mar 14 '22 at 00:00