Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is my buffer length ignored?

Tags:

c

uart

I'm developing a receiver for a small hardware project. I'm working on a small board that uses UART to transfer data.
The receiver code is shown in full below, I'll explain the problematic bits separately shortly.

#define TTY "/dev/ttys002"

#include <stdio.h>
#include <string.h>
#include <unistd.h> //Unix standard functions
#include <fcntl.h> //File controls
#include <errno.h> //Error numbers
#include <assert.h>
#include <termios.h> //Posix terminal

int open_port(const char * tty) {
    int fd;
    fd = open(tty, (O_RDWR | O_NOCTTY | O_NDELAY));
    assert("__failed to open port__" && fd != -1);
    //Block until read
    fcntl(fd, F_SETFL, 0);
    return fd;
}

void configure_port(int fd) {
    struct termios options;
    //Get current options
    tcgetattr(fd, &options);

    //9600 Baud
    cfsetispeed(&options, B9600);
    cfsetospeed(&options, B9600);

    //Receive & local mode
    options.c_cflag |= (CLOCAL | CREAD);

    //Raw output
    options.c_oflag &= ~OPOST;

    //No hardware flow control
    options.c_cflag &= ~CRTSCTS;

    //No parity, 1 stop bit
    options.c_cflag &= ~PARENB;
    options.c_cflag &= ~CSTOPB;

    //8 data bits
    options.c_cflag &= ~CSIZE;
    options.c_cflag |= CS8;

    //Write options back, set them immediately
    tcsetattr(fd, TCSANOW, &options);
}

int main(int argc, const char * argv[]) {
    int fd = open_port(TTY);
    const size_t count = 8;
    char buf[count + 1];
    ssize_t n;
    configure_port(fd);
    while (1) {
        n = read(fd, buf, count);
        buf[count] = '\0';
        if (n > 0) {
            printf("%s\n", buf);
        }
    }
    return 0;
}

Since I don't have my hardware at hand currently, I decided to test my receiver over a regular tty (#define TTY "/dev/ttys002"). To test it, I simply compiled and ran the above code and then opened a separate terminal and:

echo "text" >> /dev/ttys002

All this works fine and well and I get all the data I'm echoing into the tty.

However, the problem arises when I input a long message into the tty:

echo "this is a longer test message" >> /dev/ttys002

I receive the whole message as a single string in my program output. Why is this so? I would have expected the text to be split up into blocks of 8 chars (const size_t count = 8;).

If it's of importance, I'm using this guide as my go-to for the configuration.

Edit: Please see the comments for further discussion on the issue.

like image 809
Etheryte Avatar asked Jun 05 '15 12:06

Etheryte


1 Answers

IMHO, your message is split into blocks of eight chars : n = read(fd, buf, count); cannot give more than count bytes at a time.

But as you do not configure the tty line in RAW mode, is is still in line buffered mode. So the underlying driver blocks the first read until it has a full line terminated with a \n (or exceeding the buffer capacity).

Then the read returns with the first 8 bytes, and next reads immediately return with 8 bytes too since data is available in driver buffer.

You should look at non canonical mode in man termios, if you want to use the raw input mode :

options.c_cflags &= ~ICANON;
/* optionally if defaults are not appropriates */
options.c_cc[VMIN] = vmin; /* default is 1 */
options.c_cc[VTIME] = vtime; /* default is 0 */

But anyway, you read chararacters and never add the terminating null, so buf has no reason to be null terminated. You should only print the number of characters actually read:

const size_t count = 8;
char buf[count];
ssize_t n;
configure_port(fd);
while (1) {
    n = read(fd, buf, count);
    if (n > 0) {
        printf("%.*s\n", n, buf); /* only print the n characters */
    }
}
like image 187
Serge Ballesta Avatar answered Oct 25 '22 17:10

Serge Ballesta