Reputation: 714
I am trying to understand how VMIN
and VTIME
works to control the blocking behaviour of the read()
posix call.
In my example, I have set VTIME
to 10 (and have tried other combinations too) which should block the read for 1 second until it's unblocked, yes? That's my understanding but that doesn't seem to be the case.
I opened minimum on my host, and I see read()
would only unblock as soon as I hit enter
in minicom as opposed to waiting for 1 second to unblock.
Is my understanding incorrect? If not so, what could be wrong?
int Serial_Open(char *port)
{
int serial_port = open(port, O_RDWR);
struct termios tty;
// Read in existing settings, and handle any error
if(tcgetattr(serial_port, &tty) != 0)
{
printf("Error from tcgetattr: %s\n", strerror(errno));
}
tty.c_cflag &= ~PARENB; // Clear parity bit, disabling parity (most common)
tty.c_cflag &= ~CSTOPB; // Clear stop field, only one stop bit used in communication (most common)
tty.c_cflag &= ~CSIZE; // Clear all bits that set the data size
tty.c_cflag |= CS8; // 8 bits per byte (most common)
tty.c_cflag &= ~CRTSCTS; // Disable RTS/CTS hardware flow control (most common)
tty.c_cflag |= CREAD | CLOCAL; // Turn on READ & ignore ctrl lines (CLOCAL = 1)
tty.c_lflag &= ~ICANON;
tty.c_lflag &= ~ECHO; // Disable echo
tty.c_lflag &= ~ECHOE; // Disable erasure
tty.c_lflag &= ~ECHONL; // Disable new-line echo
tty.c_lflag &= ~ISIG; // Disable interpretation of INTR, QUIT and SUSP
tty.c_iflag &= ~(IXON | IXOFF | IXANY); // Turn off s/w flow ctrl
tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL); // Disable any special handling of received bytes
tty.c_oflag &= ~OPOST; // Prevent special interpretation of output bytes (e.g. newline chars)
tty.c_oflag &= ~ONLCR; // Prevent conversion of newline to carriage return/line feed
tty.c_cc[VTIME] = 10; // Wait for up to 1s, returning as soon as any data is received.
tty.c_cc[VMIN] = 0;
cfsetspeed(&tty, B115200);
if (tcsetattr(serial_port, TCSANOW, &tty) != 0)
{
printf("Error tcsetattr %s\n", strerror(errno));
}
return serial_port;
}
int main(void)
{
char buffer[100];
int ret;
int fd = Serial_Open("/dev/ttyUSB4");
while(1)
{
ret = read(fd, buffer, sizeof(buffer));
if (ret <= 0)
{
printf ("No data or error\n");
}
else
{
printf ("Rxd data: %s\n", buffer);
}
}
return 1;
}
Upvotes: 1
Views: 792
Reputation: 391
I cannot duplicate the claimed behaviour, using the following example program:
// SPDX-License-Identifier: CC0-1.0
#define _POSIX_C_SOURCE 200809L
#define _GNU_SOURCE
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
static int tty_fd = -1;
static struct termios tty_old;
static void tty_close(void)
{
if (tty_fd == -1)
return;
tcsetattr(tty_fd, TCSAFLUSH, &tty_old);
if (tty_fd != STDIN_FILENO &&
tty_fd != STDOUT_FILENO &&
tty_fd != STDERR_FILENO)
close(tty_fd);
tty_fd = -1;
}
static int tty_open(const char *ttypath)
{
struct termios tty_new;
int fd;
if (tty_fd != -1)
tty_close();
if (!ttypath || !*ttypath)
return errno = ENOENT;
do {
fd = open(ttypath, O_RDWR | O_NOCTTY | O_CLOEXEC);
} while (fd == -1 && errno == EINTR);
if (fd == -1)
return errno;
if (!isatty(fd)) {
close(fd);
return errno = ENOTTY;
}
if (tcgetattr(fd, &tty_old) == -1) {
const int saved_errno = errno;
close(fd);
return errno = saved_errno;
}
tty_new = tty_old;
/* No input processing. No input flow control. Ignore parity. Break reads as '\0'. */
tty_new.c_iflag &= ~(IGNBRK | BRKINT | INPCK | ISTRIP | INLCR | IGNCR | ICRNL | IUCLC | IXON | IXANY | IXOFF);
tty_new.c_iflag |= IGNPAR;
/* No output processing. */
// tty_new.c_oflag &= ~(OPOST | OLCUC | ONLCR | OCRNL | ONOCR | ONLRET);
tty_new.c_oflag &= ~OPOST;
/* 8 data bits, No parity, 1 stop bit, no hardware flow control, ignore modem control lines. */
tty_new.c_cflag &= ~(CSIZE | PARENB | CSTOPB | CRTSCTS);
tty_new.c_cflag |= CS8 | CREAD | CLOCAL;
/* Raw mode, no signals, no echo. */
tty_new.c_lflag &= ~(ICANON | ISIG | ECHO | IEXTEN);
/* VMIN=0, VTIME=10 */
tty_new.c_cc[VMIN] = 0;
tty_new.c_cc[VTIME] = 10;
if (tcsetattr(fd, TCSANOW, &tty_new) == -1) {
const int saved_errno = errno;
close(fd);
return errno = saved_errno;
}
/* Some of the above settings may not have been applied.
We could check, but really, we don't care that much. */
tty_fd = fd;
return 0;
}
#ifndef TTY_BUFSIZ
#define TTY_BUFSIZ 128
#endif
static unsigned char tty_buf[TTY_BUFSIZ];
static volatile unsigned char *tty_head = tty_buf;
static volatile unsigned char *tty_tail = tty_buf;
#define TTY_NONE -1
#define TTY_EOF -2
#define TTY_ERROR -3
static int tty_getc_read(void)
{
tty_head = tty_tail = tty_buf;
if (tty_fd == -1)
return TTY_EOF;
ssize_t n = read(tty_fd, tty_buf, sizeof tty_buf);
if (n > 0) {
tty_tail = tty_buf + n;
return *(tty_head++);
} else
if (n == 0) {
return TTY_NONE;
} else
if (n != -1 || (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK)) {
return TTY_ERROR;
} else {
return TTY_NONE;
}
}
static inline int tty_getc(void)
{
if (tty_tail > tty_head)
return *(tty_head++);
else
return tty_getc_read();
}
static volatile sig_atomic_t done = 0;
static void handle_done(int signum)
{
if (!done)
done = (signum > 0) ? signum : -1;
}
static int install_done(int signum)
{
struct sigaction act;
memset(&act, 0, sizeof act);
sigemptyset(&act.sa_mask);
act.sa_handler = handle_done;
act.sa_flags = 0;
if (sigaction(signum, &act, NULL) == -1)
return errno;
return 0;
}
int main(int argc, char *argv[])
{
if (argc != 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
const char *cmd = (argc > 0 && argv && argv[0] && argv[0][0]) ? argv[0] : "(this)";
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", cmd);
fprintf(stderr, " %s TTY-DEVICE\n", cmd);
fprintf(stderr, "\n");
fprintf(stderr, "This reads in raw mode from the TTY device.\n");
fprintf(stderr, "\n");
return EXIT_SUCCESS;
}
if (install_done(SIGINT) || install_done(SIGTERM) ||
install_done(SIGHUP) || install_done(SIGQUIT)) {
fprintf(stderr, "Cannot install signal handlers: %s.\n", strerror(errno));
return EXIT_SUCCESS;
}
if (tty_open(argv[1])) {
fprintf(stderr, "%s: Cannot open TTY: %s.\n", argv[1], strerror(errno));
return EXIT_FAILURE;
}
printf("Press CTRL+C to exit.\r\n");
fflush(stdout);
int last_none = 0;
while (!done) {
int ch = tty_getc();
if (ch == TTY_NONE) {
printf(".");
fflush(stdout);
last_none = 1;
continue;
} else
if (last_none) {
printf("\r\n");
fflush(stdout);
last_none = 0;
}
if (ch == TTY_EOF) {
printf("End-of-input received.\r\n");
fflush(stdout);
break;
} else
if (ch == TTY_ERROR) {
printf("Read error occurred.\r\n");
fflush(stdout);
break;
} else
if (ch == 3) {
printf("Received 0x03 = \\003, assuming Ctrl+C. Exiting.\r\n");
fflush(stdout);
break;
} else {
if (ch >= 32 && ch <= 126)
printf("Received 0x%02x = \\%03o = %3d = '%c'\r\n", (unsigned int)ch, (unsigned int)ch, ch, ch);
else
printf("Received 0x%02x = \\%03o = %3d\r\n", (unsigned int)ch, (unsigned int)ch, ch);
fflush(stdout);
}
}
if (last_none) {
printf("\r\n");
fflush(stdout);
}
tty_close();
return EXIT_SUCCESS;
}
Specify the serial or terminal or pseudoterminal device path on the command line; use $(tty)
for the current terminal/pseudoterminal.
Comparing OP's, Serial_Open()
and my tty_open()
, I do believe the terminal settings are essentially the same (and moreover, any differences there are do not explain the difference in behaviour).
You can compile the above example (example.c) as e.g. gcc -Wall -O2 example.c -o example
and then run it via ./example $(tty)
to read input from the same terminal window.
This leads me to believe the problem is in the other end: that whatever OP uses to generate the data read by this end, is line-buffered.
Upvotes: 1