/*
* Copyright (c) 2017 embedded brains GmbH. All rights reserved.
*
* embedded brains GmbH
* Dornierstr. 4
* 82178 Puchheim
* Germany
* <rtems@embedded-brains.de>
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
* http://www.rtems.org/license/LICENSE.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <rtems/termiostypes.h>
#include "tmacros.h"
const char rtems_test_name[] = "TERMIOS 9";
#define INTERRUPT 0
#define POLLED 1
#define DEVICE_COUNT 2
#define OUTPUT_BUFFER_SIZE 64
#define INPUT_BUFFER_SIZE 64
static const char * const paths[DEVICE_COUNT] = {
"/interrupt",
"/polled"
};
typedef struct {
rtems_termios_device_context base;
rtems_termios_tty *tty;
size_t output_pending;
size_t output_count;
char output_buf[OUTPUT_BUFFER_SIZE];
size_t input_head;
size_t input_tail;
unsigned char input_buf[INPUT_BUFFER_SIZE];
int callback_counter;
} device_context;
typedef struct {
device_context devices[DEVICE_COUNT];
int fds[DEVICE_COUNT];
struct termios term[DEVICE_COUNT];
int context_switch_counter;
rtems_id flush_task_id;
} test_context;
static test_context test_instance = {
.devices = {
{
.base = RTEMS_TERMIOS_DEVICE_CONTEXT_INITIALIZER("Interrupt")
}, {
.base = RTEMS_TERMIOS_DEVICE_CONTEXT_INITIALIZER("Polled")
}
}
};
static bool first_open(
rtems_termios_tty *tty,
rtems_termios_device_context *base,
struct termios *term,
rtems_libio_open_close_args_t *args
)
{
device_context *dev = (device_context *) base;
dev->tty = tty;
return true;
}
static void write_polled(
rtems_termios_device_context *base,
const char *buf,
size_t len
)
{
device_context *dev = (device_context *) base;
rtems_test_assert(dev->output_count + len <= OUTPUT_BUFFER_SIZE);
memcpy(&dev->output_buf[dev->output_count], buf, len);
dev->output_count += len;
}
static void write_interrupt(
rtems_termios_device_context *base,
const char *buf,
size_t len
)
{
device_context *dev = (device_context *) base;
write_polled(base, buf, len);
dev->output_pending = len;
}
static int read_polled(rtems_termios_device_context *base)
{
device_context *dev = (device_context *) base;
int c;
if (dev->input_head != dev->input_tail) {
c = dev->input_buf[dev->input_head];
dev->input_head = (dev->input_head + 1) % INPUT_BUFFER_SIZE;
} else {
c = -1;
}
return c;
}
static const rtems_termios_device_handler handlers[DEVICE_COUNT] = {
{
.first_open = first_open,
.write = write_interrupt,
.mode = TERMIOS_IRQ_DRIVEN
}, {
.first_open = first_open,
.write = write_polled,
.poll_read = read_polled,
.mode = TERMIOS_POLLED
}
};
static void set_term(test_context *ctx, size_t i)
{
int rv;
rv = tcsetattr(ctx->fds[i], TCSANOW, &ctx->term[i]);
rtems_test_assert(rv == 0);
}
static void init_term(test_context *ctx, size_t i)
{
int rv;
rv = tcgetattr(ctx->fds[i], &ctx->term[i]);
rtems_test_assert(rv == 0);
ctx->term[i].c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
| INLCR | IGNCR | ICRNL | IXON);
ctx->term[i].c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL | ECHOPRT
| ECHOCTL | ECHOKE | ICANON | ISIG | IEXTEN);
ctx->term[i].c_cflag &= ~(CSIZE | PARENB);
ctx->term[i].c_cflag |= CS8;
ctx->term[i].c_oflag &= ~(OPOST | ONLRET | ONLCR | OCRNL | ONLRET
| TABDLY | OLCUC);
ctx->term[i].c_cc[VMIN] = 0;
ctx->term[i].c_cc[VTIME] = 0;
set_term(ctx, i);
}
static void setup(test_context *ctx)
{
rtems_status_code sc;
size_t i;
rtems_termios_initialize();
for (i = 0; i < DEVICE_COUNT; ++i) {
sc = rtems_termios_device_install(
paths[i],
&handlers[i],
NULL,
&ctx->devices[i].base
);
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
ctx->fds[i] = open(paths[i], O_RDWR);
rtems_test_assert(ctx->fds[i] >= 0);
init_term(ctx, i);
}
}
static void input(test_context *ctx, size_t i, char c)
{
device_context *dev = &ctx->devices[i];
switch (i) {
case INTERRUPT:
rtems_termios_enqueue_raw_characters(dev->tty, &c, sizeof(c));
break;
case POLLED:
dev->input_buf[dev->input_tail] = (unsigned char) c;
dev->input_tail = (dev->input_tail + 1) % INPUT_BUFFER_SIZE;
rtems_test_assert(dev->input_head != dev->input_tail);
break;
default:
rtems_test_assert(0);
}
}
static void enable_non_blocking(test_context *ctx, size_t i, bool enable)
{
int flags;
int rv;
flags = fcntl(ctx->fds[i], F_GETFL, 0);
rtems_test_assert(flags >= 0);
if (enable) {
flags |= O_NONBLOCK;
} else {
flags &= ~O_NONBLOCK;
}
rv = fcntl(ctx->fds[i], F_SETFL, flags);
rtems_test_assert(rv == 0);
}
static void clear_set_iflag(
test_context *ctx,
size_t i,
tcflag_t clear,
tcflag_t set
)
{
ctx->term[i].c_iflag &= ~clear;
ctx->term[i].c_iflag |= set;
set_term(ctx, i);
}
static void clear_set_lflag(
test_context *ctx,
size_t i,
tcflag_t clear,
tcflag_t set
)
{
ctx->term[i].c_lflag &= ~clear;
ctx->term[i].c_lflag |= set;
set_term(ctx, i);
}
static void clear_set_oflag(
test_context *ctx,
size_t i,
tcflag_t clear,
tcflag_t set
)
{
ctx->term[i].c_oflag &= ~clear;
ctx->term[i].c_oflag |= set;
set_term(ctx, i);
}
static void set_vmin_vtime(
test_context *ctx,
size_t i,
cc_t vmin,
cc_t vtime
)
{
ctx->term[i].c_cc[VMIN] = vmin;
ctx->term[i].c_cc[VTIME] = vtime;
set_term(ctx, i);
}
static void set_veol_veol2(
test_context *ctx,
size_t i,
cc_t veol,
cc_t veol2
)
{
ctx->term[i].c_cc[VEOL] = veol;
ctx->term[i].c_cc[VEOL2] = veol2;
set_term(ctx, i);
}
static void test_igncr(test_context *ctx)
{
size_t i;
for (i = 0; i < DEVICE_COUNT; ++i) {
ssize_t n;
char c;
c = 'x';
clear_set_iflag(ctx, i, 0, IGNCR);
n = read(ctx->fds[i], &c, sizeof(c));
rtems_test_assert(n == 0);
rtems_test_assert(c == 'x');
input(ctx, i, '\r');
n = read(ctx->fds[i], &c, sizeof(c));
rtems_test_assert(n == 0);
rtems_test_assert(c == 'x');
clear_set_iflag(ctx, i, IGNCR, 0);
input(ctx, i, '\r');
n = read(ctx->fds[i], &c, sizeof(c));
rtems_test_assert(n == 1);
rtems_test_assert(c == '\r');
}
}
static void test_istrip(test_context *ctx)
{
size_t i;
for (i = 0; i < DEVICE_COUNT; ++i) {
ssize_t n;
char c;
c = 'x';
clear_set_iflag(ctx, i, 0, ISTRIP);
n = read(ctx->fds[i], &c, sizeof(c));
rtems_test_assert(n == 0);
rtems_test_assert(c == 'x');
input(ctx, i, '\376');
n = read(ctx->fds[i], &c, sizeof(c));
rtems_test_assert(n == 1);
rtems_test_assert(c == '~');
clear_set_iflag(ctx, i, ISTRIP, 0);
input(ctx, i, '\376');
n = read(ctx->fds[i], &c, sizeof(c));
rtems_test_assert(n == 1);
rtems_test_assert(c == '\376');
}
}
static void test_iuclc(test_context *ctx)
{
size_t i;
for (i = 0; i < DEVICE_COUNT; ++i) {
ssize_t n;
char c;
c = 'x';
clear_set_iflag(ctx, i, 0, IUCLC);
n = read(ctx->fds[i], &c, sizeof(c));
rtems_test_assert(n == 0);
rtems_test_assert(c == 'x');
input(ctx, i, 'A');
n = read(ctx->fds[i], &c, sizeof(c));
rtems_test_assert(n == 1);
rtems_test_assert(c == 'a');
clear_set_iflag(ctx, i, IUCLC, 0);
input(ctx, i, 'A');
n = read(ctx->fds[i], &c, sizeof(c));
rtems_test_assert(n == 1);
rtems_test_assert(c == 'A');
}
}
static void test_icrnl(test_context *ctx)
{
size_t i;
for (i = 0; i < DEVICE_COUNT; ++i) {
ssize_t n;
char c;
c = 'x';
clear_set_iflag(ctx, i, 0, ICRNL);
n = read(ctx->fds[i], &c, sizeof(c));
rtems_test_assert(n == 0);
rtems_test_assert(c == 'x');
input(ctx, i, '\r');
n = read(ctx->fds[i], &c, sizeof(c));
rtems_test_assert(n == 1);
rtems_test_assert(c == '\n');
clear_set_iflag(ctx, i, ICRNL, 0);
input(ctx, i, '\r');
n = read(ctx->fds[i], &c, sizeof(c));
rtems_test_assert(n == 1);
rtems_test_assert(c == '\r');
}
}
static void test_inlcr(test_context *ctx)
{
size_t i;
for (i = 0; i < DEVICE_COUNT; ++i) {
ssize_t n;
char c;
c = 'x';
clear_set_iflag(ctx, i, 0, INLCR);
n = read(ctx->fds[i], &c, sizeof(c));
rtems_test_assert(n == 0);
rtems_test_assert(c == 'x');
input(ctx, i, '\n');
n = read(ctx->fds[i], &c, sizeof(c));
rtems_test_assert(n == 1);
rtems_test_assert(c == '\r');
clear_set_iflag(ctx, i, INLCR, 0);
input(ctx, i, '\n');
n = read(ctx->fds[i], &c, sizeof(c));
rtems_test_assert(n == 1);
rtems_test_assert(c == '\n');
}
}
static void callback(struct termios *tty, void *arg)
{
device_context *ctx = arg;
++ctx->callback_counter;
}
static void test_rx_callback(test_context *ctx)
{
size_t i = INTERRUPT;
device_context *dev = &ctx->devices[i];
ssize_t n;
char buf[3];
buf[0] = 'x';
dev->callback_counter = 0;
dev->tty->tty_rcv.sw_pfn = callback;
dev->tty->tty_rcv.sw_arg = dev;
clear_set_lflag(ctx, i, ICANON, 0);
set_vmin_vtime(ctx, i, 0, 0);
n = read(ctx->fds[i], buf, 1);
rtems_test_assert(n == 0);
rtems_test_assert(buf[0] == 'x');
input(ctx, i, 'a');
rtems_test_assert(dev->callback_counter == 1);
input(ctx, i, 'b');
rtems_test_assert(dev->callback_counter == 1);
n = read(ctx->fds[i], buf, 2);
rtems_test_assert(n == 2);
rtems_test_assert(buf[0] == 'a');
rtems_test_assert(buf[1] == 'b');
set_vmin_vtime(ctx, i, 2, 0);
input(ctx, i, 'd');
rtems_test_assert(dev->callback_counter == 1);
input(ctx, i, 'e');
rtems_test_assert(dev->callback_counter == 2);
input(ctx, i, 'f');
rtems_test_assert(dev->callback_counter == 2);
n = read(ctx->fds[i], buf, 3);
rtems_test_assert(n == 3);
rtems_test_assert(buf[0] == 'd');
rtems_test_assert(buf[1] == 'e');
rtems_test_assert(buf[2] == 'f');
dev->tty->tty_rcv.sw_pfn = NULL;
dev->tty->tty_rcv.sw_arg = NULL;
}
static void test_rx_callback_icanon(test_context *ctx)
{
size_t i = INTERRUPT;
device_context *dev = &ctx->devices[i];
ssize_t n;
char buf[255];
size_t j;
buf[0] = 'x';
dev->callback_counter = 0;
dev->tty->tty_rcv.sw_pfn = callback;
dev->tty->tty_rcv.sw_arg = dev;
set_vmin_vtime(ctx, i, 0, 0);
n = read(ctx->fds[i], buf, 1);
rtems_test_assert(n == 0);
rtems_test_assert(buf[0] == 'x');
clear_set_lflag(ctx, i, 0, ICANON);
set_veol_veol2(ctx, i, '1', '2');
input(ctx, i, '\n');
rtems_test_assert(dev->callback_counter == 1);
input(ctx, i, 'a');
rtems_test_assert(dev->callback_counter == 1);
input(ctx, i, '\n');
rtems_test_assert(dev->callback_counter == 1);
n = read(ctx->fds[i], buf, 2);
rtems_test_assert(n == 1);
rtems_test_assert(buf[0] == '\n');
n = read(ctx->fds[i], buf, 3);
rtems_test_assert(n == 2);
rtems_test_assert(buf[0] == 'a');
rtems_test_assert(buf[1] == '\n');
input(ctx, i, '\4');
rtems_test_assert(dev->callback_counter == 2);
input(ctx, i, 'b');
rtems_test_assert(dev->callback_counter == 2);
input(ctx, i, '\n');
rtems_test_assert(dev->callback_counter == 2);
n = read(ctx->fds[i], buf, 1);
rtems_test_assert(n == 0);
n = read(ctx->fds[i], buf, 3);
rtems_test_assert(n == 2);
rtems_test_assert(buf[0] == 'b');
rtems_test_assert(buf[1] == '\n');
/* EOL */
input(ctx, i, '1');
rtems_test_assert(dev->callback_counter == 3);
input(ctx, i, 'c');
rtems_test_assert(dev->callback_counter == 3);
input(ctx, i, '\n');
rtems_test_assert(dev->callback_counter == 3);
n = read(ctx->fds[i], buf, 2);
rtems_test_assert(n == 1);
rtems_test_assert(buf[0] == '1');
n = read(ctx->fds[i], buf, 3);
rtems_test_assert(n == 2);
rtems_test_assert(buf[0] == 'c');
rtems_test_assert(buf[1] == '\n');
/* EOL2 */
input(ctx, i, '2');
rtems_test_assert(dev->callback_counter == 4);
input(ctx, i, 'd');
rtems_test_assert(dev->callback_counter == 4);
input(ctx, i, '\n');
rtems_test_assert(dev->callback_counter == 4);
n = read(ctx->fds[i], buf, 2);
rtems_test_assert(n == 1);
rtems_test_assert(buf[0] == '2');
n = read(ctx->fds[i], buf, 3);
rtems_test_assert(n == 2);
rtems_test_assert(buf[0] == 'd');
rtems_test_assert(buf[1] == '\n');
for (j = 0; j < 255; ++j) {
input(ctx, i, 'e');
rtems_test_assert(dev->callback_counter == 4);
}
/* Raw input buffer overflow */
input(ctx, i, 'e');
rtems_test_assert(dev->callback_counter == 5);
n = read(ctx->fds[i], buf, 255);
rtems_test_assert(n == 255);
dev->tty->tty_rcv.sw_pfn = NULL;
dev->tty->tty_rcv.sw_arg = NULL;
set_veol_veol2(ctx, i, '\0', '\0');
clear_set_lflag(ctx, i, ICANON, 0);
}
static void test_read_icanon(test_context *ctx, size_t i)
{
ssize_t n;
char buf[3];
clear_set_lflag(ctx, i, 0, ICANON);
input(ctx, i, 'a');
input(ctx, i, '\n');
input(ctx, i, 'b');
input(ctx, i, '\n');
input(ctx, i, 'c');
input(ctx, i, '\n');
n = read(ctx->fds[i], buf, 3);
rtems_test_assert(n == 2);
rtems_test_assert(buf[0] == 'a');
rtems_test_assert(buf[1] == '\n');
n = read(ctx->fds[i], buf, 3);
rtems_test_assert(n == 2);
rtems_test_assert(buf[0] == 'b');
rtems_test_assert(buf[1] == '\n');
n = read(ctx->fds[i], buf, 3);
rtems_test_assert(n == 2);
rtems_test_assert(buf[0] == 'c');
rtems_test_assert(buf[1] == '\n');
clear_set_lflag(ctx, i, ICANON, 0);
}
static void flush_output(test_context *ctx, size_t i)
{
if (i == INTERRUPT) {
device_context *dev = &ctx->devices[i];
int left;
do {
left = rtems_termios_dequeue_characters(dev->tty, dev->output_pending);
} while (left > 0);
}
}
static void clear_output(test_context *ctx, size_t i)
{
device_context *dev = &ctx->devices[i];
flush_output(ctx, i);
dev->output_count = 0;
memset(&dev->output_buf, 0, OUTPUT_BUFFER_SIZE);
}
static void test_onlret(test_context *ctx)
{
tcflag_t oflags = OPOST | ONLRET;
size_t i;
for (i = 0; i < DEVICE_COUNT; ++i) {
device_context *dev = &ctx->devices[i];
char c;
ssize_t n;
dev->tty->column = 0;
clear_output(ctx, i);
clear_set_oflag(ctx, i, 0, oflags);
c = 'a';
n = write(ctx->fds[i], &c, sizeof(c));
rtems_test_assert(n == 1);
rtems_test_assert(dev->tty->column == 1);
flush_output(ctx, i);
rtems_test_assert(dev->output_count == 1);
rtems_test_assert(dev->output_buf[0] == 'a');
c = '\n';
n = write(ctx->fds[i], &c, sizeof(c));
rtems_test_assert(n == 1);
rtems_test_assert(dev->tty->column == 0);
flush_output(ctx, i);
rtems_test_assert(dev->output_count == 2);
rtems_test_assert(dev->output_buf[1] == '\n');
clear_set_oflag(ctx, i, oflags, 0);
}
}
static void test_onlcr(test_context *ctx)
{
tcflag_t oflags = OPOST | ONLCR;
size_t i;
for (i = 0; i < DEVICE_COUNT; ++i) {
device_context *dev = &ctx->devices[i];
char c;
ssize_t n;
dev->tty->column = 0;
clear_output(ctx, i);
clear_set_oflag(ctx, i, 0, oflags);
c = 'a';
n = write(ctx->fds[i], &c, sizeof(c));
rtems_test_assert(n == 1);
rtems_test_assert(dev->tty->column == 1);
flush_output(ctx, i);
rtems_test_assert(dev->output_count == 1);
rtems_test_assert(dev->output_buf[0] == 'a');
c = '\n';
n = write(ctx->fds[i], &c, sizeof(c));
rtems_test_assert(n == 1);
rtems_test_assert(dev->tty->column == 0);
flush_output(ctx, i);
rtems_test_assert(dev->output_count == 3);
rtems_test_assert(dev->output_buf[1] == '\r');
rtems_test_assert(dev->output_buf[2] == '\n');
clear_set_oflag(ctx, i, oflags, 0);
}
}
static void test_onocr(test_context *ctx)
{
tcflag_t oflags = OPOST | ONOCR;
size_t i;
for (i = 0; i < DEVICE_COUNT; ++i) {
device_context *dev = &ctx->devices[i];
char c;
ssize_t n;
dev->tty->column = 0;
clear_output(ctx, i);
clear_set_oflag(ctx, i, 0, oflags);
c = '\r';
n = write(ctx->fds[i], &c, sizeof(c));
rtems_test_assert(n == 1);
rtems_test_assert(dev->tty->column == 0);
flush_output(ctx, i);
rtems_test_assert(dev->output_count == 0);
c = 'a';
n = write(ctx->fds[i], &c, sizeof(c));
rtems_test_assert(n == 1);
rtems_test_assert(dev->tty->column == 1);
flush_output(ctx, i);
rtems_test_assert(dev->output_count == 1);
rtems_test_assert(dev->output_buf[0] == 'a');
c = '\r';
n = write(ctx->fds[i], &c, sizeof(c));
rtems_test_assert(n == 1);
rtems_test_assert(dev->tty->column == 0);
flush_output(ctx, i);
rtems_test_assert(dev->output_count == 2);
rtems_test_assert(dev->output_buf[1] == '\r');
clear_set_oflag(ctx, i, oflags, 0);
}
}
static void test_ocrnl(test_context *ctx)
{
tcflag_t oflags = OPOST | OCRNL;
size_t i;
for (i = 0; i < DEVICE_COUNT; ++i) {
device_context *dev = &ctx->devices[i];
char c;
ssize_t n;
dev->tty->column = 0;
clear_output(ctx, i);
clear_set_oflag(ctx, i, 0, oflags);
c = '\r';
n = write(ctx->fds[i], &c, sizeof(c));
rtems_test_assert(n == 1);
rtems_test_assert(dev->tty->column == 0);
flush_output(ctx, i);
rtems_test_assert(dev->output_count == 1);
rtems_test_assert(dev->output_buf[0] == '\n');
clear_set_oflag(ctx, i, oflags, 0);
}
}
static void test_ocrnl_onlret(test_context *ctx)
{
tcflag_t oflags = OPOST | OCRNL | ONLRET;
size_t i;
for (i = 0; i < DEVICE_COUNT; ++i) {
device_context *dev = &ctx->devices[i];
char c;
ssize_t n;
dev->tty->column = 0;
clear_output(ctx, i);
clear_set_oflag(ctx, i, 0, oflags);
c = 'a';
n = write(ctx->fds[i], &c, sizeof(c));
rtems_test_assert(n == 1);
rtems_test_assert(dev->tty->column == 1);
flush_output(ctx, i);
rtems_test_assert(dev->output_count == 1);
rtems_test_assert(dev->output_buf[0] == 'a');
c = '\r';
n = write(ctx->fds[i], &c, sizeof(c));
rtems_test_assert(n == 1);
rtems_test_assert(dev->tty->column == 0);
flush_output(ctx, i);
rtems_test_assert(dev->output_count == 2);
rtems_test_assert(dev->output_buf[1] == '\n');
clear_set_oflag(ctx, i, oflags, 0);
}
}
static void test_opost(test_context *ctx)
{
tcflag_t oflags = OPOST;
size_t i;
for (i = 0; i < DEVICE_COUNT; ++i) {
device_context *dev = &ctx->devices[i];
char c;
ssize_t n;
dev->tty->column = 0;
clear_output(ctx, i);
clear_set_oflag(ctx, i, 0, oflags);
c = 'a';
n = write(ctx->fds[i], &c, sizeof(c));
rtems_test_assert(n == 1);
rtems_test_assert(dev->tty->column == 1);
flush_output(ctx, i);
rtems_test_assert(dev->output_count == 1);
rtems_test_assert(dev->output_buf[0] == 'a');
c = '\33';
n = write(ctx->fds[i], &c, sizeof(c));
rtems_test_assert(n == 1);
rtems_test_assert(dev->tty->column == 1);
flush_output(ctx, i);
rtems_test_assert(dev->output_count == 2);
rtems_test_assert(dev->output_buf[1] == '\33');
c = '\t';
n = write(ctx->fds[i], &c, sizeof(c));
rtems_test_assert(n == 1);
rtems_test_assert(dev->tty->column == 8);
flush_output(ctx, i);
rtems_test_assert(dev->output_count == 3);
rtems_test_assert(dev->output_buf[2] == '\t');
c = '\b';
n = write(ctx->fds[i], &c, sizeof(c));
rtems_test_assert(n == 1);
rtems_test_assert(dev->tty->column == 7);
flush_output(ctx, i);
rtems_test_assert(dev->output_count == 4);
rtems_test_assert(dev->output_buf[3] == '\b');
c = '\r';
n = write(ctx->fds[i], &c, sizeof(c));
rtems_test_assert(n == 1);
rtems_test_assert(dev->tty->column == 0);
flush_output(ctx, i);
rtems_test_assert(dev->output_count == 5);
rtems_test_assert(dev->output_buf[4] == '\r');
clear_set_oflag(ctx, i, oflags, 0);
}
}
static void test_xtabs(test_context *ctx)
{
tcflag_t oflags = OPOST | OXTABS;
size_t i;
for (i = 0; i < DEVICE_COUNT; ++i) {
device_context *dev = &ctx->devices[i];
char c;
ssize_t n;
dev->tty->column = 0;
clear_output(ctx, i);
clear_set_oflag(ctx, i, 0, oflags);
c = 'a';
n = write(ctx->fds[i], &c, sizeof(c));
rtems_test_assert(n == 1);
rtems_test_assert(dev->tty->column == 1);
flush_output(ctx, i);
rtems_test_assert(dev->output_count == 1);
rtems_test_assert(dev->output_buf[0] == 'a');
c = '\t';
n = write(ctx->fds[i], &c, sizeof(c));
rtems_test_assert(n == 1);
rtems_test_assert(dev->tty->column == 8);
flush_output(ctx, i);
rtems_test_assert(dev->output_count == 8);
rtems_test_assert(dev->output_buf[1] == ' ');
rtems_test_assert(dev->output_buf[2] == ' ');
rtems_test_assert(dev->output_buf[3] == ' ');
rtems_test_assert(dev->output_buf[4] == ' ');
rtems_test_assert(dev->output_buf[5] == ' ');
rtems_test_assert(dev->output_buf[6] == ' ');
rtems_test_assert(dev->output_buf[7] == ' ');
clear_set_oflag(ctx, i, oflags, 0);
}
}
static void test_olcuc(test_context *ctx)
{
tcflag_t oflags = OPOST | OLCUC;
size_t i;
for (i = 0; i < DEVICE_COUNT; ++i) {
device_context *dev = &ctx->devices[i];
char c;
ssize_t n;
dev->tty->column = 0;
clear_output(ctx, i);
clear_set_oflag(ctx, i, 0, oflags);
c = 'a';
n = write(ctx->fds[i], &c, sizeof(c));
rtems_test_assert(n == 1);
rtems_test_assert(dev->tty->column == 1);
flush_output(ctx, i);
rtems_test_assert(dev->output_count == 1);
rtems_test_assert(dev->output_buf[0] == 'A');
c = 'B';
n = write(ctx->fds[i], &c, sizeof(c));
rtems_test_assert(n == 1);
rtems_test_assert(dev->tty->column == 2);
flush_output(ctx, i);
rtems_test_assert(dev->output_count == 2);
rtems_test_assert(dev->output_buf[1] == 'B');
c = '9';
n = write(ctx->fds[i], &c, sizeof(c));
rtems_test_assert(n == 1);
rtems_test_assert(dev->tty->column == 3);
flush_output(ctx, i);
rtems_test_assert(dev->output_count == 3);
rtems_test_assert(dev->output_buf[2] == '9');
clear_set_oflag(ctx, i, oflags, 0);
}
}
static void
set_self_prio(rtems_task_priority prio)
{
rtems_status_code sc;
sc = rtems_task_set_priority(RTEMS_SELF, prio, &prio);
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
}
static void flush_task(rtems_task_argument arg)
{
test_context *ctx = (test_context *) arg;
while (true) {
set_self_prio(1);
flush_output(ctx, INTERRUPT);
set_self_prio(2);
}
}
static void test_write(test_context *ctx)
{
tcflag_t oflags = OPOST | ONLCR | OXTABS;
rtems_status_code sc;
size_t i = INTERRUPT;
device_context *dev = &ctx->devices[i];
char buf[OUTPUT_BUFFER_SIZE];
ssize_t n;
ctx->context_switch_counter = 0;
sc = rtems_task_create(
rtems_build_name('F', 'L', 'S', 'H'),
2,
RTEMS_MINIMUM_STACK_SIZE,
RTEMS_DEFAULT_MODES,
RTEMS_DEFAULT_ATTRIBUTES,
&ctx->flush_task_id
);
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
sc = rtems_task_start(
ctx->flush_task_id,
flush_task,
(rtems_task_argument) ctx
);
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
clear_output(ctx, i);
memset(buf, 'a', OUTPUT_BUFFER_SIZE);
n = write(ctx->fds[i], &buf[0], OUTPUT_BUFFER_SIZE);
rtems_test_assert(n == OUTPUT_BUFFER_SIZE - 1);
rtems_test_assert(ctx->context_switch_counter == 0);
enable_non_blocking(ctx, i, true);
n = write(ctx->fds[i], &buf[OUTPUT_BUFFER_SIZE - 1], 1);
rtems_test_assert(n == 0);
enable_non_blocking(ctx, i, false);
n = write(ctx->fds[i], &buf[OUTPUT_BUFFER_SIZE - 1], 1);
rtems_test_assert(n == 1);
rtems_test_assert(ctx->context_switch_counter == 2);
rtems_test_assert(dev->output_count == OUTPUT_BUFFER_SIZE);
rtems_test_assert(memcmp(dev->output_buf, buf, OUTPUT_BUFFER_SIZE) == 0);
clear_set_oflag(ctx, i, 0, oflags);
/* Ensure that ONLCR output expansion is taken into account */
dev->tty->column = 0;
clear_output(ctx, i);
memset(buf, 'b', OUTPUT_BUFFER_SIZE - 1);
buf[OUTPUT_BUFFER_SIZE - 2] = '\n';
n = write(ctx->fds[i], &buf[0], OUTPUT_BUFFER_SIZE - 3);
rtems_test_assert(n == OUTPUT_BUFFER_SIZE - 3);
rtems_test_assert(ctx->context_switch_counter == 2);
n = write(ctx->fds[i], &buf[OUTPUT_BUFFER_SIZE - 3], 2);
rtems_test_assert(n == 1);
rtems_test_assert(ctx->context_switch_counter == 2);
enable_non_blocking(ctx, i, true);
n = write(ctx->fds[i], &buf[OUTPUT_BUFFER_SIZE - 2], 1);
rtems_test_assert(n == 0);
enable_non_blocking(ctx, i, false);
n = write(ctx->fds[i], &buf[OUTPUT_BUFFER_SIZE - 2], 1);
rtems_test_assert(n == 1);
rtems_test_assert(ctx->context_switch_counter == 4);
rtems_test_assert(dev->output_count == OUTPUT_BUFFER_SIZE);
buf[OUTPUT_BUFFER_SIZE - 2] = '\r';
buf[OUTPUT_BUFFER_SIZE - 1] = '\n';
rtems_test_assert(memcmp(dev->output_buf, buf, OUTPUT_BUFFER_SIZE) == 0);
/* Ensure that OXTABS output expansion is taken into account */
dev->tty->column = 0;
clear_output(ctx, i);
memset(buf, 'c', OUTPUT_BUFFER_SIZE - 8);
buf[OUTPUT_BUFFER_SIZE - 8] = '\t';
n = write(ctx->fds[i], &buf[0], OUTPUT_BUFFER_SIZE - 9);
rtems_test_assert(n == OUTPUT_BUFFER_SIZE - 9);
rtems_test_assert(ctx->context_switch_counter == 4);
n = write(ctx->fds[i], &buf[OUTPUT_BUFFER_SIZE - 9], 2);
rtems_test_assert(n == 1);
rtems_test_assert(ctx->context_switch_counter == 4);
enable_non_blocking(ctx, i, true);
n = write(ctx->fds[i], &buf[OUTPUT_BUFFER_SIZE - 8], 1);
rtems_test_assert(n == 0);
enable_non_blocking(ctx, i, false);
n = write(ctx->fds[i], &buf[OUTPUT_BUFFER_SIZE - 8], 1);
rtems_test_assert(n == 1);
rtems_test_assert(ctx->context_switch_counter == 6);
rtems_test_assert(dev->output_count == OUTPUT_BUFFER_SIZE);
memset(&buf[OUTPUT_BUFFER_SIZE - 8], ' ', 8);
rtems_test_assert(memcmp(dev->output_buf, buf, OUTPUT_BUFFER_SIZE) == 0);
clear_set_oflag(ctx, i, oflags, 0);
sc = rtems_task_delete(ctx->flush_task_id);
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
}
static void test_tx_callback(test_context *ctx)
{
size_t i = INTERRUPT;
device_context *dev = &ctx->devices[i];
char c;
ssize_t n;
clear_output(ctx, i);
dev->callback_counter = 0;
dev->tty->tty_snd.sw_pfn = callback;
dev->tty->tty_snd.sw_arg = dev;
c = 'a';
n = write(ctx->fds[i], &c, sizeof(c));
rtems_test_assert(n == 1);
rtems_test_assert(dev->callback_counter == 0);
flush_output(ctx, i);
rtems_test_assert(dev->callback_counter == 1);
rtems_test_assert(dev->output_count == 1);
rtems_test_assert(dev->output_buf[0] == 'a');
dev->tty->tty_snd.sw_pfn = NULL;
dev->tty->tty_snd.sw_arg = NULL;
}
static void Init(rtems_task_argument arg)
{
test_context *ctx = &test_instance;
TEST_BEGIN();
setup(ctx);
test_igncr(ctx);
test_istrip(ctx);
test_iuclc(ctx);
test_icrnl(ctx);
test_inlcr(ctx);
test_rx_callback(ctx);
test_rx_callback_icanon(ctx);
test_read_icanon(ctx, INTERRUPT);
test_read_icanon(ctx, POLLED);
test_onlret(ctx);
test_onlcr(ctx);
test_onocr(ctx);
test_ocrnl(ctx);
test_ocrnl_onlret(ctx);
test_opost(ctx);
test_xtabs(ctx);
test_olcuc(ctx);
test_write(ctx);
test_tx_callback(ctx);
TEST_END();
rtems_test_exit(0);
}
static void switch_extension(Thread_Control *executing, Thread_Control *heir)
{
test_context *ctx = &test_instance;
++ctx->context_switch_counter;
}
#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
#define CONFIGURE_APPLICATION_NEEDS_SIMPLE_CONSOLE_DRIVER
#define CONFIGURE_LIBIO_MAXIMUM_FILE_DESCRIPTORS 5
#define CONFIGURE_MAXIMUM_TASKS 2
#define CONFIGURE_MAXIMUM_SEMAPHORES 7
#define CONFIGURE_INITIAL_EXTENSIONS \
{ .thread_switch = switch_extension }, \
RTEMS_TEST_INITIAL_EXTENSION
#define CONFIGURE_RTEMS_INIT_TASKS_TABLE
#define CONFIGURE_INIT
#include <rtems/confdefs.h>