From 791469bd4fb1eba09ea8464795f100d7dd09379e Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Mon, 6 Nov 2017 14:49:32 +0100 Subject: termios: Fix canonical mode In canonical mode, input is made available line by line. We must stop the canonical buffer filling upon reception of an end-of-line character. Close #3218. --- cpukit/libcsupport/src/termios.c | 6 ++- testsuites/libtests/termios09/init.c | 97 ++++++++++++++++++++++++++++-------- 2 files changed, 81 insertions(+), 22 deletions(-) diff --git a/cpukit/libcsupport/src/termios.c b/cpukit/libcsupport/src/termios.c index 7a114a74b9..8303e9f18d 100644 --- a/cpukit/libcsupport/src/termios.c +++ b/cpukit/libcsupport/src/termios.c @@ -1570,8 +1570,10 @@ fillBufferQueue (struct rtems_termios_tty *tty) /* continue processing new character */ if (tty->termios.c_lflag & ICANON) { - if (siproc (c, tty)) - wait = false; + if (siproc (c, tty)) { + /* In canonical mode, input is made available line by line */ + return; + } } else { siproc (c, tty); if (tty->ccount >= tty->termios.c_cc[VMIN]) diff --git a/testsuites/libtests/termios09/init.c b/testsuites/libtests/termios09/init.c index 7509101682..62138bc213 100644 --- a/testsuites/libtests/termios09/init.c +++ b/testsuites/libtests/termios09/init.c @@ -36,6 +36,8 @@ const char rtems_test_name[] = "TERMIOS 9"; #define OUTPUT_BUFFER_SIZE 64 +#define INPUT_BUFFER_SIZE 64 + static const char * const paths[DEVICE_COUNT] = { "/interrupt", "/polled" @@ -47,7 +49,9 @@ typedef struct { size_t output_pending; size_t output_count; char output_buf[OUTPUT_BUFFER_SIZE]; - int input_char; + size_t input_head; + size_t input_tail; + unsigned char input_buf[INPUT_BUFFER_SIZE]; int callback_counter; } device_context; @@ -64,8 +68,7 @@ static test_context test_instance = { { .base = RTEMS_TERMIOS_DEVICE_CONTEXT_INITIALIZER("Interrupt") }, { - .base = RTEMS_TERMIOS_DEVICE_CONTEXT_INITIALIZER("Polled"), - .input_char = -1 + .base = RTEMS_TERMIOS_DEVICE_CONTEXT_INITIALIZER("Polled") } } }; @@ -112,9 +115,14 @@ static void write_interrupt( static int read_polled(rtems_termios_device_context *base) { device_context *dev = (device_context *) base; - int c = dev->input_char; + int c; - dev->input_char = -1; + 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; } @@ -187,12 +195,16 @@ static void setup(test_context *ctx) 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(ctx->devices[i].tty, &c, sizeof(c)); + rtems_termios_enqueue_raw_characters(dev->tty, &c, sizeof(c)); break; case POLLED: - ctx->devices[i].input_char = (unsigned char) c; + 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); @@ -523,11 +535,13 @@ static void test_rx_callback_icanon(test_context *ctx) input(ctx, i, '\n'); rtems_test_assert(dev->callback_counter == 1); - n = read(ctx->fds[i], buf, 3); - rtems_test_assert(n == 3); + n = read(ctx->fds[i], buf, 2); + rtems_test_assert(n == 1); rtems_test_assert(buf[0] == '\n'); - rtems_test_assert(buf[1] == 'a'); - rtems_test_assert(buf[2] == '\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); @@ -538,11 +552,15 @@ static void test_rx_callback_icanon(test_context *ctx) input(ctx, i, '\n'); rtems_test_assert(dev->callback_counter == 2); - n = read(ctx->fds[i], buf, 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); @@ -552,12 +570,15 @@ static void test_rx_callback_icanon(test_context *ctx) input(ctx, i, '\n'); rtems_test_assert(dev->callback_counter == 3); - n = read(ctx->fds[i], buf, 3); - rtems_test_assert(n == 3); + n = read(ctx->fds[i], buf, 2); + rtems_test_assert(n == 1); rtems_test_assert(buf[0] == '1'); - rtems_test_assert(buf[1] == 'c'); - rtems_test_assert(buf[2] == '\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'); + /* EOL2 */ input(ctx, i, '2'); rtems_test_assert(dev->callback_counter == 4); @@ -567,11 +588,13 @@ static void test_rx_callback_icanon(test_context *ctx) input(ctx, i, '\n'); rtems_test_assert(dev->callback_counter == 4); - n = read(ctx->fds[i], buf, 3); - rtems_test_assert(n == 3); + n = read(ctx->fds[i], buf, 2); + rtems_test_assert(n == 1); rtems_test_assert(buf[0] == '2'); - rtems_test_assert(buf[1] == 'd'); - rtems_test_assert(buf[2] == '\n'); + 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'); @@ -591,6 +614,38 @@ static void test_rx_callback_icanon(test_context *ctx) 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) { @@ -1106,6 +1161,8 @@ static void Init(rtems_task_argument arg) 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); -- cgit v1.2.3