Почему моя длина буфера игнорируется?

Я разрабатываю приемник для небольшого аппаратного проекта. Я работаю на небольшой плате, которая использует UART для передачи данных.
Код приемника показан полностью ниже, я кратко объясню неисправные бит отдельно.

#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;
}

Поскольку в настоящее время у меня нет моего оборудования, я решил проверить свой приемник на регулярной tty (#define TTY "/dev/ttys002"). Чтобы протестировать его, я просто скомпилировал и выполнил вышеуказанный код, а затем открыл отдельный терминал и:

echo "text" >> /dev/ttys002

Все это прекрасно работает, и я получаю все данные, которые я повторяю в tty.

Однако проблема возникает при вводе длинного сообщения в tty:

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

Я получаю все сообщение в виде одной строки в своем программном выпуске. Почему это так? Я бы ожидал, что текст будет разбит на блоки из 8 символов (const size_t count = 8;).

Если это важно, я использую это руководство в качестве моего варианта конфигурации.

Изменить: Пожалуйста, просмотрите комментарии для дальнейшего обсуждения проблемы.

Ответ 1

IMHO, ваше сообщение разделено на блоки из восьми символов: n = read(fd, buf, count); не может выдавать больше, чем считать байты за раз.

Но поскольку вы не настраиваете строку tty в режиме RAW, все еще находится в режиме буферизации в строке. Таким образом, основной драйвер блокирует первое чтение до тех пор, пока он не завершит полную строку с помощью \n (или превысит емкость буфера).

Затем чтение возвращается с первыми 8 байтами, а последующие чтения сразу возвращаются с 8 байтами, так как данные доступны в буфере драйвера.

Вы должны смотреть на неканонический режим в человеческих терминах, если вы хотите использовать режим сырого ввода:

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 */

Но в любом случае вы читаете chararacters и никогда не добавляете завершающий null, поэтому buf не имеет причины для отказа от null. Вы должны только напечатать количество фактически прочитанных символов:

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 */
    }
}

Ответ 2

Кажется, что ваша проблема имеет то же имя, что и на этом сайте:)

Вы выделяете 8 байт данных в стек для вашей переменной buf. Затем в функции read вы пишете строку "это более длинное тестовое сообщение", длина которой составляет более 8 байтов. Когда вы используете функцию printf с "% s", printf перестанет печатать, когда она достигнет нулевого символа (\0), так как ожидает, что ваша строка будет завершена с нулевой отметкой. В вашем случае вы должны проверить, не изменена ли ваша переменная buf. Если это не так, вы должны сделать последний байт buf равным null символу

Ответ 3

То, что вы ищете, - это псевдо-tty (PTY).

Простым способом создания PTY является использование socat(1) следующим образом:

socat pty,link=/tmp/testpty stdio

Это создаст PTY и прикрепит текущий терминал stdio к его главной стороне. Независимо от того, какие данные вы ввели здесь, будут отправлены на подчиненную сторону PTY.

Теперь подключитесь к подчиненной стороне из вашей программы, и она будет работать так, как вы ожидаете:

#define TTY "/tmp/testpty"