diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2017-02-23 15:43:41 +0100 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2017-02-28 09:09:19 +0100 |
commit | a165a9607abd4263fa1a572bb227bd9fb537053b (patch) | |
tree | cfe1593cacf1a76cbd3a95fb367eb7c8cdbf3101 | |
parent | termios: Introduce doTransmit() (diff) | |
download | rtems-a165a9607abd4263fa1a572bb227bd9fb537053b.tar.bz2 |
termios: Make write POSIX compatible
Currently only blocking read/write operations are implemented. A
blocking write must transfer at least one character. It should not wait
for the device for the second character and so on.
Close #2917.
-rw-r--r-- | cpukit/libcsupport/src/termios.c | 115 | ||||
-rw-r--r-- | testsuites/libtests/termios09/init.c | 138 |
2 files changed, 217 insertions, 36 deletions
diff --git a/cpukit/libcsupport/src/termios.c b/cpukit/libcsupport/src/termios.c index 376f176623..e59f97799c 100644 --- a/cpukit/libcsupport/src/termios.c +++ b/cpukit/libcsupport/src/termios.c @@ -1020,20 +1020,24 @@ startXmit ( /* * Send characters to device-specific code */ -static void -doTransmit (const char *buf, size_t len, rtems_termios_tty *tty) +static size_t +doTransmit (const char *buf, size_t len, rtems_termios_tty *tty, + bool wait, bool nextWait) { unsigned int newHead; rtems_termios_device_context *ctx = tty->device_context; rtems_interrupt_lock_context lock_context; rtems_status_code sc; + size_t todo; if (tty->handler.mode == TERMIOS_POLLED) { (*tty->handler.write)(ctx, buf, len); - return; + return len; } - while (len) { + todo = len; + + while (todo > 0) { size_t nToCopy; size_t nAvail; @@ -1043,18 +1047,25 @@ doTransmit (const char *buf, size_t len, rtems_termios_tty *tty) newHead -= tty->rawOutBuf.Size; rtems_termios_device_lock_acquire (ctx, &lock_context); - while (newHead == tty->rawOutBuf.Tail) { - tty->rawOutBufState = rob_wait; - rtems_termios_device_lock_release (ctx, &lock_context); - sc = rtems_semaphore_obtain( - tty->rawOutBuf.Semaphore, RTEMS_WAIT, RTEMS_NO_TIMEOUT); - if (sc != RTEMS_SUCCESSFUL) - rtems_fatal_error_occurred (sc); - rtems_termios_device_lock_acquire (ctx, &lock_context); + if (newHead == tty->rawOutBuf.Tail) { + if (wait) { + do { + tty->rawOutBufState = rob_wait; + rtems_termios_device_lock_release (ctx, &lock_context); + sc = rtems_semaphore_obtain( + tty->rawOutBuf.Semaphore, RTEMS_WAIT, RTEMS_NO_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + rtems_fatal_error_occurred (sc); + rtems_termios_device_lock_acquire (ctx, &lock_context); + } while (newHead == tty->rawOutBuf.Tail); + } else { + rtems_termios_device_lock_release (ctx, &lock_context); + return len - todo; + } } /* Determine free space up to current tail or end of ring buffer */ - nToCopy = len; + nToCopy = todo; if (tty->rawOutBuf.Tail > tty->rawOutBuf.Head) { /* Available space is contiguous from Head to Tail */ nAvail = tty->rawOutBuf.Tail - tty->rawOutBuf.Head - 1; @@ -1086,22 +1097,44 @@ doTransmit (const char *buf, size_t len, rtems_termios_tty *tty) rtems_termios_device_lock_release (ctx, &lock_context); buf += nToCopy; - len -= nToCopy; + todo -= nToCopy; + wait = nextWait; } + + return len; } void rtems_termios_puts ( const void *_buf, size_t len, struct rtems_termios_tty *tty) { - doTransmit (_buf, len, tty); + doTransmit (_buf, len, tty, true, true); +} + +static bool +canTransmit (rtems_termios_tty *tty, bool wait, size_t len) +{ + rtems_termios_device_context *ctx; + rtems_interrupt_lock_context lock_context; + unsigned int capacity; + + if (wait || tty->handler.mode == TERMIOS_POLLED) { + return true; + } + + ctx = tty->device_context; + rtems_termios_device_lock_acquire (ctx, &lock_context); + capacity = (tty->rawOutBuf.Tail - tty->rawOutBuf.Head - 1) % + tty->rawOutBuf.Size; + rtems_termios_device_lock_release (ctx, &lock_context); + return capacity >= len; } /* * Handle output processing */ -static void -oproc (unsigned char c, struct rtems_termios_tty *tty) +static bool +oproc (unsigned char c, rtems_termios_tty *tty, bool wait) { char buf[8]; size_t len; @@ -1118,16 +1151,21 @@ oproc (unsigned char c, struct rtems_termios_tty *tty) if (tty->termios.c_oflag & ONLRET) columnAdj = -oldColumn; if (tty->termios.c_oflag & ONLCR) { + len = 2; + + if (!canTransmit (tty, wait, len)) { + return false; + } + columnAdj = -oldColumn; buf[0] = '\r'; buf[1] = c; - len = 2; } break; case '\r': if ((tty->termios.c_oflag & ONOCR) && (oldColumn == 0)) - return; + return true; if (tty->termios.c_oflag & OCRNL) { buf[0] = '\n'; if (tty->termios.c_oflag & ONLRET) @@ -1144,6 +1182,10 @@ oproc (unsigned char c, struct rtems_termios_tty *tty) len = (size_t) columnAdj; + if (!canTransmit (tty, wait, len)) { + return false; + } + for (i = 0; i < columnAdj; ++i) { buf[i] = ' '; } @@ -1168,26 +1210,31 @@ oproc (unsigned char c, struct rtems_termios_tty *tty) tty->column = oldColumn + columnAdj; } - doTransmit (buf, len, tty); + return doTransmit (buf, len, tty, wait, true) > 0; } static uint32_t -rtems_termios_write_tty (struct rtems_termios_tty *tty, const char *buffer, - uint32_t initial_count) +rtems_termios_write_tty (rtems_termios_tty *tty, const char *buf, uint32_t len) { + bool wait = true; if (tty->termios.c_oflag & OPOST) { - uint32_t count; + uint32_t todo = len; - count = initial_count; + while (todo > 0) { + if (!oproc (*buf, tty, wait)) { + break; + } - while (count--) - oproc (*buffer++, tty); + ++buf; + --todo; + wait = false; + } + + return len - todo; } else { - doTransmit (buffer, initial_count, tty); + return doTransmit (buf, len, tty, wait, false); } - - return initial_count; } rtems_status_code @@ -1222,10 +1269,10 @@ echo (unsigned char c, struct rtems_termios_tty *tty) echobuf[0] = '^'; echobuf[1] = c ^ 0x40; - doTransmit (echobuf, 2, tty); + doTransmit (echobuf, 2, tty, true, true); tty->column += 2; } else { - oproc (c, tty); + oproc (c, tty, true); } } @@ -1282,18 +1329,18 @@ erase (struct rtems_termios_tty *tty, int lineFlag) * Back up over the tab */ while (tty->column > col) { - doTransmit ("\b", 1, tty); + doTransmit ("\b", 1, tty, true, true); tty->column--; } } else { if (iscntrl (c) && (tty->termios.c_lflag & ECHOCTL)) { - doTransmit ("\b \b", 3, tty); + doTransmit ("\b \b", 3, tty, true, true); if (tty->column) tty->column--; } if (!iscntrl (c) || (tty->termios.c_lflag & ECHOCTL)) { - doTransmit ("\b \b", 3, tty); + doTransmit ("\b \b", 3, tty, true, true); if (tty->column) tty->column--; } diff --git a/testsuites/libtests/termios09/init.c b/testsuites/libtests/termios09/init.c index 58a23ea2c4..648d1bf7ed 100644 --- a/testsuites/libtests/termios09/init.c +++ b/testsuites/libtests/termios09/init.c @@ -55,6 +55,8 @@ 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 = { @@ -910,6 +912,128 @@ static void test_olcuc(test_context *ctx) } } +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 | XTABS; + 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); + + 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); + + 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 XTABS 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); + + 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 Init(rtems_task_argument arg) { test_context *ctx = &test_instance; @@ -932,21 +1056,31 @@ static void Init(rtems_task_argument arg) test_opost(ctx); test_xtabs(ctx); test_olcuc(ctx); + test_write(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_CONSOLE_DRIVER #define CONFIGURE_LIBIO_MAXIMUM_FILE_DESCRIPTORS 5 -#define CONFIGURE_MAXIMUM_TASKS 1 +#define CONFIGURE_MAXIMUM_TASKS 2 #define CONFIGURE_MAXIMUM_SEMAPHORES 7 -#define CONFIGURE_INITIAL_EXTENSIONS RTEMS_TEST_INITIAL_EXTENSION +#define CONFIGURE_INITIAL_EXTENSIONS \ + { .thread_switch = switch_extension }, \ + RTEMS_TEST_INITIAL_EXTENSION #define CONFIGURE_RTEMS_INIT_TASKS_TABLE |