Files
2026-02-02 14:35:59 -06:00

1224 lines
31 KiB
C

/* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017 embedded brains GmbH & Co. KG
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#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
)
{
(void) term;
(void) 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 )
{
(void) tty;
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 )
{
(void) 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 )
{
(void) 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 )
{
(void) executing;
(void) 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_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>