71GA
71GA

Reputation: 1399

Bytes are not sent to the serial driver buffer

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?

enter image description here

Upvotes: 0

Views: 75

Answers (1)

jordanvrtanoski
jordanvrtanoski

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

Related Questions