[rtems commit] termios: Make write POSIX compatible

Sebastian Huber sebh at rtems.org
Tue Feb 28 08:43:50 UTC 2017


Module:    rtems
Branch:    master
Commit:    a165a9607abd4263fa1a572bb227bd9fb537053b
Changeset: http://git.rtems.org/rtems/commit/?id=a165a9607abd4263fa1a572bb227bd9fb537053b

Author:    Sebastian Huber <sebastian.huber at embedded-brains.de>
Date:      Thu Feb 23 15:43:41 2017 +0100

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.

---

 cpukit/libcsupport/src/termios.c     | 115 ++++++++++++++++++++---------
 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 376f176..e59f977 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 58a23ea..648d1bf 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
 



More information about the vc mailing list