forked from Imagelibrary/rtems
termios: Change receive callback invocation
Call the receive callback in case a read will succeed without to block. This enables the use of the receive callback for a poll() and select() support. Increase raw input buffer size to allow buffering of one line. Close #2916.
This commit is contained in:
@@ -84,7 +84,7 @@ int rtems_termios_nlinesw =
|
||||
extern rtems_id rtems_termios_ttyMutex;
|
||||
|
||||
static size_t rtems_termios_cbufsize = 256;
|
||||
static size_t rtems_termios_raw_input_size = 128;
|
||||
static size_t rtems_termios_raw_input_size = 256;
|
||||
static size_t rtems_termios_raw_output_size = 64;
|
||||
|
||||
static const IMFS_node_control rtems_termios_imfs_node_control;
|
||||
@@ -1282,11 +1282,8 @@ erase (struct rtems_termios_tty *tty, int lineFlag)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Process a single input character
|
||||
*/
|
||||
static int
|
||||
iproc (unsigned char c, struct rtems_termios_tty *tty)
|
||||
static unsigned char
|
||||
iprocEarly (unsigned char c, rtems_termios_tty *tty)
|
||||
{
|
||||
if (tty->termios.c_iflag & ISTRIP)
|
||||
c &= 0x7f;
|
||||
@@ -1302,6 +1299,15 @@ iproc (unsigned char c, struct rtems_termios_tty *tty)
|
||||
c = '\r';
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
/*
|
||||
* Process a single input character
|
||||
*/
|
||||
static int
|
||||
iproc (unsigned char c, struct rtems_termios_tty *tty)
|
||||
{
|
||||
if ((c != '\0') && (tty->termios.c_lflag & ICANON)) {
|
||||
if (c == tty->termios.c_cc[VERASE]) {
|
||||
erase (tty, 0);
|
||||
@@ -1372,6 +1378,7 @@ siprocPoll (unsigned char c, rtems_termios_tty *tty)
|
||||
return 0;
|
||||
}
|
||||
|
||||
c = iprocEarly (c, tty);
|
||||
return siproc (c, tty);
|
||||
}
|
||||
|
||||
@@ -1568,6 +1575,20 @@ void rtems_termios_rxirq_occured(struct rtems_termios_tty *tty)
|
||||
rtems_event_send(tty->rxTaskId,TERMIOS_RX_PROC_EVENT);
|
||||
}
|
||||
|
||||
static bool
|
||||
mustCallReceiveCallback (const rtems_termios_tty *tty, unsigned char c,
|
||||
unsigned int newTail, unsigned int head)
|
||||
{
|
||||
if ((tty->termios.c_lflag & ICANON) != 0) {
|
||||
return c == '\n' || c == tty->termios.c_cc[VEOF] ||
|
||||
c == tty->termios.c_cc[VEOL] || c == tty->termios.c_cc[VEOL2];
|
||||
} else {
|
||||
unsigned int rawContentSize = (newTail - head) % tty->rawInBuf.Size;
|
||||
|
||||
return rawContentSize >= tty->termios.c_cc[VMIN];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Place characters on raw queue.
|
||||
* NOTE: This routine runs in the context of the
|
||||
@@ -1645,11 +1666,14 @@ rtems_termios_enqueue_raw_characters (void *ttyp, const char *buf, int len)
|
||||
unsigned int head;
|
||||
unsigned int oldTail;
|
||||
unsigned int newTail;
|
||||
bool callReciveCallback;
|
||||
|
||||
if (c == '\r' && (tty->termios.c_iflag & IGNCR) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
c = iprocEarly (c, tty);
|
||||
|
||||
rtems_termios_device_lock_acquire (ctx, &lock_context);
|
||||
|
||||
head = tty->rawInBuf.Head;
|
||||
@@ -1680,22 +1704,34 @@ rtems_termios_enqueue_raw_characters (void *ttyp, const char *buf, int len)
|
||||
}
|
||||
}
|
||||
|
||||
callReciveCallback = false;
|
||||
|
||||
if (newTail != head) {
|
||||
tty->rawInBuf.theBuf[newTail] = c;
|
||||
tty->rawInBuf.Tail = newTail;
|
||||
|
||||
rtems_termios_device_lock_release (ctx, &lock_context);
|
||||
|
||||
/*
|
||||
* check to see if rcv wakeup callback was set
|
||||
*/
|
||||
if (tty->tty_rcv.sw_pfn != NULL && !tty->tty_rcvwakeup) {
|
||||
tty->tty_rcvwakeup = true;
|
||||
(*tty->tty_rcv.sw_pfn)(&tty->termios, tty->tty_rcv.sw_arg);
|
||||
if (mustCallReceiveCallback (tty, c, newTail, head)) {
|
||||
tty->tty_rcvwakeup = true;
|
||||
callReciveCallback = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
++dropped;
|
||||
rtems_termios_device_lock_release (ctx, &lock_context);
|
||||
|
||||
if (tty->tty_rcv.sw_pfn != NULL && !tty->tty_rcvwakeup) {
|
||||
tty->tty_rcvwakeup = true;
|
||||
callReciveCallback = true;
|
||||
}
|
||||
}
|
||||
|
||||
rtems_termios_device_lock_release (ctx, &lock_context);
|
||||
|
||||
if (callReciveCallback) {
|
||||
(*tty->tty_rcv.sw_pfn)(&tty->termios, tty->tty_rcv.sw_arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ typedef struct {
|
||||
size_t output_count;
|
||||
char output_buf[OUTPUT_BUFFER_SIZE];
|
||||
int input_char;
|
||||
int callback_counter;
|
||||
} device_context;
|
||||
|
||||
typedef struct {
|
||||
@@ -146,7 +147,8 @@ static void init_term(test_context *ctx, size_t i)
|
||||
|
||||
ctx->term[i].c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
|
||||
| INLCR | IGNCR | ICRNL | IXON);
|
||||
ctx->term[i].c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
|
||||
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
|
||||
@@ -207,6 +209,42 @@ static void clear_set_iflag(
|
||||
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 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;
|
||||
@@ -238,6 +276,286 @@ static void test_igncr(test_context *ctx)
|
||||
}
|
||||
}
|
||||
|
||||
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, 3);
|
||||
rtems_test_assert(n == 3);
|
||||
rtems_test_assert(buf[0] == '\n');
|
||||
rtems_test_assert(buf[1] == 'a');
|
||||
rtems_test_assert(buf[2] == '\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, 2);
|
||||
rtems_test_assert(n == 2);
|
||||
rtems_test_assert(buf[0] == 'b');
|
||||
rtems_test_assert(buf[1] == '\n');
|
||||
|
||||
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, 3);
|
||||
rtems_test_assert(n == 3);
|
||||
rtems_test_assert(buf[0] == '1');
|
||||
rtems_test_assert(buf[1] == 'c');
|
||||
rtems_test_assert(buf[2] == '\n');
|
||||
|
||||
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, 3);
|
||||
rtems_test_assert(n == 3);
|
||||
rtems_test_assert(buf[0] == '2');
|
||||
rtems_test_assert(buf[1] == 'd');
|
||||
rtems_test_assert(buf[2] == '\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);
|
||||
|
||||
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 Init(rtems_task_argument arg)
|
||||
{
|
||||
test_context *ctx = &test_instance;
|
||||
@@ -246,6 +564,12 @@ static void Init(rtems_task_argument arg)
|
||||
|
||||
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_END();
|
||||
rtems_test_exit(0);
|
||||
|
||||
Reference in New Issue
Block a user