[rtems commit] termios: Change receive callback invocation

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


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

Author:    Sebastian Huber <sebastian.huber at embedded-brains.de>
Date:      Thu Feb 23 11:35:44 2017 +0100

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.

---

 cpukit/libcsupport/src/termios.c     |  58 +++++--
 testsuites/libtests/termios09/init.c | 326 ++++++++++++++++++++++++++++++++++-
 2 files changed, 372 insertions(+), 12 deletions(-)

diff --git a/cpukit/libcsupport/src/termios.c b/cpukit/libcsupport/src/termios.c
index dccdfd7..62964e4 100644
--- a/cpukit/libcsupport/src/termios.c
+++ b/cpukit/libcsupport/src/termios.c
@@ -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);
       }
     }
   }
diff --git a/testsuites/libtests/termios09/init.c b/testsuites/libtests/termios09/init.c
index 6e651bf..a32ebda 100644
--- a/testsuites/libtests/termios09/init.c
+++ b/testsuites/libtests/termios09/init.c
@@ -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);



More information about the vc mailing list