Reputation: 1399
I have this program:
// C headers
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdint.h>
// POSIX headers
#include <unistd.h>
#include <sys/ioctl.h>
// Other headers
#include "ZL_sleep.h"
#include "ZL_utf8.h"
#include "ZL_serial.h"
#define BS 4 // Buffer size
int main(int argc, char * argv[]){
int32_t p; // Port
int32_t r; // Read finished
uint8_t b[BS]; // Buffer
uint32_t n; // Number of bytes to be read from driver buffer
if(argc != 2){
printf("ERROR: Supply a \"serial port\" device file.");
exit(EXIT_FAILURE);
}
p = ZL_open_tty(argv[1]);
if(p < 0){
perror("ERROR: open()");
exit(EXIT_FAILURE);
}
while(1){
memset(b, '\0', BS);
r = read(p, b, BS);
if(r < 0){
if(errno == EAGAIN){
}
else{
perror("ERROR: read()");
close(p);
exit(EXIT_FAILURE);
}
}
ZL_utf8_print_bytes(b, BS);
putchar('\n');
}
close(p);
return EXIT_SUCCESS;
}
that uses function ZL_utf8_print_bytes()
that prints the buffer bytes (one by one in a for loop). It also calls function ZL_open_tty()
and passes the argv[1]
(/dev/pts/1
) to it as an argument.
Function ZL_open_tty()
function sets O_NONBLOCK
flag with open()
in order for open()
to return immediately i.e. "nonblocking" with whatever status. Then before any other I/O function is used, function clears O_NONBLOCK
flag and switches back to "blocking". It then sets VMIN
and VTIME
so that read()
:
blocks until
VMIN
or more bytes is received/exists in driver buffer. (It may block indefinitely)
uint32_t ZL_open_tty(const char * terminal){
uint32_t fd; // File's descriptor
uint32_t fl; // File's flags
struct termios fo; // File's options
fd = open(terminal, O_RDWR | O_NOCTTY | O_NONBLOCK);
if(fd < 0){
return -1;
}
fl = fcntl(fd, F_GETFL, 0);
if(fl < 0){
perror("ERROR: fcntl():");
return -1;
}
fcntl(fd, F_SETFL, fl & ~(O_NONBLOCK | O_APPEND | O_DSYNC | O_RSYNC | O_SYNC));
tcgetattr(fd, &fo);
fo.c_cc[VMIN] = 1;
fo.c_cc[VTIME] = 0;
tcsetattr(fd, TCSANOW, &fo);
fputs("─────────────────────────────────────────────────── terminal settings\n", stdout);
printf("VMIN = %d\n", fo.c_cc[VMIN]);
printf("VTIME = %d\n", fo.c_cc[VTIME]);
putchar('\n');
return(fd);
Afterwards my program enters an endless while loop where read()
blocks until I enter any key when keyboard focus is in /dev/pts/1
.
The problem that I am facing is that sometimes I press a key in /dev/pts/1
and bytes registered with this key aren't transmitted to the driver buffer?! Bytes just stays in the /dev/pts/1
. This happens with ASCII (1-byte) characters and UTF8 (multi-byte) bytes...
I know that read()
tries to read requested amount of bytes (BS
) from the driver buffer. But it reads less bytes if there aren't BS
bytes available in the driver buffer. So... if some bytes would arrive later it could read them later. But these bytes never arrive to the driver buffer for some reason...
What could be causing bytes not to arrive in the driver buffer and remain forever in the /dev/pts/1
?
Upvotes: 0
Views: 75
Reputation: 5557
The pts
was created when a virtual terminal was attached to the system (usually ssh
). The pts
was connected as stdin
/stdout
for a shell which was started for the connection, therefore you already have a process attached to the pts
and reading from it.
Once you attach your application, you effectively start a race between both processes (your application and the shell), so whoever is faster will receive the content of the buffer. The character had not remained in the buffer of the pts
, rather it was read by the shell attached to the pts
.
To be able to read without interruption from the attached process, you need to intercept the buffer of the pts
by informing the master multiplexer ptmx
, see more in ptmx(4). You can study how it's done in the interceptty
Upvotes: 1