[rtems commit] termios: Add Capability to Generate SIGINTR and SIGQUIT

Joel Sherrill joel at rtems.org
Wed Oct 9 19:34:18 UTC 2019


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

Author:    Joel Sherrill <joel at rtems.org>
Date:      Wed Oct  2 16:49:00 2019 -0500

termios: Add Capability to Generate SIGINTR and SIGQUIT

This patch adds the ability for termios to send SIGINTR on receipt
of VINTR and SIGQUIT for VKILL and return -1/EINTR from read() on
a termios channel. Importantly, this patch does not alter the default
behavior or force POSIX signal code in just because termios is used.
The application must explicitly enable the POSIX behavior of generating
a signal upon receipt of these characters. This is discussed in the
POSIX standard:

   https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap11.html

Closes #3800.

---

 cpukit/Makefile.am                                 |   1 +
 cpukit/include/rtems/libio.h                       | 116 ++++++++++-
 cpukit/include/rtems/rtems/status.h                |   9 +-
 cpukit/libcsupport/src/termios.c                   | 225 +++++++++++++++++----
 .../libcsupport/src/termios_posix_isig_handler.c   |  39 ++++
 cpukit/rtems/src/statustext.c                      |   1 +
 cpukit/rtems/src/statustoerrno.c                   |   1 +
 testsuites/libtests/Makefile.am                    |  22 +-
 testsuites/libtests/configure.ac                   |   2 +
 testsuites/libtests/termios10/init.c               | 191 +++++++++++++++++
 testsuites/libtests/termios10/termios10.doc        |  23 +++
 testsuites/libtests/termios10/termios10.scn        |  14 ++
 testsuites/libtests/termios11/termios11.doc        |  23 +++
 testsuites/libtests/termios11/termios11.scn        |  14 ++
 14 files changed, 634 insertions(+), 47 deletions(-)

diff --git a/cpukit/Makefile.am b/cpukit/Makefile.am
index 01ee409..7698fe5 100644
--- a/cpukit/Makefile.am
+++ b/cpukit/Makefile.am
@@ -250,6 +250,7 @@ librtemscpu_a_SOURCES += libcsupport/src/termios_baudtable.c
 librtemscpu_a_SOURCES += libcsupport/src/termios.c
 librtemscpu_a_SOURCES += libcsupport/src/termiosinitialize.c
 librtemscpu_a_SOURCES += libcsupport/src/termios_num2baud.c
+librtemscpu_a_SOURCES += libcsupport/src/termios_posix_isig_handler.c
 librtemscpu_a_SOURCES += libcsupport/src/termios_setbestbaud.c
 librtemscpu_a_SOURCES += libcsupport/src/termios_setinitialbaud.c
 librtemscpu_a_SOURCES += libcsupport/src/__times.c
diff --git a/cpukit/include/rtems/libio.h b/cpukit/include/rtems/libio.h
index f3b0f2c..b536232 100644
--- a/cpukit/include/rtems/libio.h
+++ b/cpukit/include/rtems/libio.h
@@ -1880,6 +1880,8 @@ extern const rtems_filesystem_mount_configuration
  */
 /**@{**/
 
+struct rtems_termios_tty;
+
 typedef struct rtems_termios_callbacks {
   int    (*firstOpen)(int major, int minor, void *arg);
   int    (*lastClose)(int major, int minor, void *arg);
@@ -1907,6 +1909,114 @@ rtems_status_code rtems_termios_bufsize (
   size_t raw_output    /* raw output buffer size */
 );
 
+/**
+ * @brief Type returned by all input processing (isig) methods
+ */ 
+typedef enum {
+  /**
+   * This indicates that the input character was processed
+   * and possibly placed into the buffer.
+   */
+  RTEMS_TERMIOS_ISIG_WAS_PROCESSED,
+  /**
+   * This indicates that the input character was not processed and
+   * subsequent processing is required.
+   */
+  RTEMS_TERMIOS_ISIG_WAS_NOT_PROCESSED,
+  /**
+   * This indicates that the character was processed and determined
+   * to be one that requires a signal to be raised (e.g. VINTR or
+   * VKILL). The tty must be in the right termios mode for this to
+   * occur. There is no further processing of this character required and
+   * the pending read() operation should be interrupted.
+   */
+  RTEMS_TERMIOS_ISIG_INTERRUPT_READ
+} rtems_termios_isig_status_code;
+
+/**
+ * @brief Type for ISIG (VINTR/VKILL) handler
+ *
+ * This type defines the interface to a method which will process
+ * VKILL and VINTR characters. The user can register a handler to
+ * override the default behavior which is to take no special action
+ * on these characters. The POSIX required behavior is to
+ * generate a SIGINTR or SIGQUIT signal. This behavior is implemented if
+ * the user registers the @ref rtems_termios_posix_isig_handler().
+ *
+ * @param c     character that was input
+ * @param tty   termios structure pointer for this input tty
+ *
+ * @return The value returned is according to that documented
+ *         for @ref rtems_termios_isig_status_code.
+ */
+typedef rtems_termios_isig_status_code (*rtems_termios_isig_handler)(
+  unsigned char             c,
+  struct rtems_termios_tty *tty
+);
+
+/**
+ * @brief Default handler for ISIG (VINTR/VKILL)
+ *
+ * This handler is installed by default by termios. It performs
+ * no actions on receiving the VINTR and VKILL characters. This is
+ * not POSIX conformant behavior but is the historical RTEMS behavior
+ * and avoid any default dependency on signal processing code.
+ *
+ * @param c     character that was input
+ * @param tty   termios structure pointer for this input tty
+ *
+ * The installed ISIG handler is only invoked if the character is
+ * (VKILL or VINTR) and ISIG is enabled. Thus the handler need only
+ * check the character and perform the appropriate action.
+ *
+ * @return The value returned is according to that documented
+ *         for all methods adhering to @ref rtems_termios_isig_handler.
+ */
+rtems_termios_isig_status_code rtems_termios_default_isig_handler(
+  unsigned char             c,
+  struct rtems_termios_tty *tty
+);
+
+/**
+ * @brief POSIX handler for ISIG (VINTR/VKILL)
+ *
+ * This handler is installed by the used to obtain POSIX behavior
+ * upon receiving the VINTR and VKILL characters.  The POSIX required
+ * behavior is to generate a SIGINTR or SIGQUIT signal if ISIG is enabled.
+ *
+ * @param c     character that was input
+ * @param tty   termios structure pointer for this input tty
+ *
+ * @return The value returned is according to that documented
+ *         for all methods adhering to @ref rtems_termios_isig_handler.
+ */
+rtems_termios_isig_status_code rtems_termios_posix_isig_handler(
+  unsigned char             c,
+  struct rtems_termios_tty *tty
+);
+
+/**
+ * @brief Register handler for ISIG (VINTR/VKILL)
+ *
+ * This method is invoked to install an ISIG handler. This may be used
+ * to install @ref rtems_termios_posix_isig_handler() to obtain POSIX
+ * behavior or a custom handler. The handler
+ * @ref rtems_termios_default_isig_handler() is installed by default.
+ *
+ * @param handler entry point of the new ISIG handler
+ *
+ * @retval RTEMS_SUCCESSFUL if successful or error code if unsuccessful.
+ */
+rtems_status_code rtems_termios_register_isig_handler(
+  rtems_termios_isig_handler handler
+);
+
+int rtems_termios_enqueue_raw_characters(
+  void *ttyp,
+  const char *buf,
+  int   len
+);
+
 rtems_status_code rtems_termios_open (
   rtems_device_major_number      major,
   rtems_device_minor_number      minor,
@@ -1930,12 +2040,6 @@ rtems_status_code rtems_termios_ioctl(
   void *arg
 );
 
-int rtems_termios_enqueue_raw_characters(
-  void *ttyp,
-  const char *buf,
-  int   len
-);
-
 int rtems_termios_dequeue_characters(
   void *ttyp,
   int   len
diff --git a/cpukit/include/rtems/rtems/status.h b/cpukit/include/rtems/rtems/status.h
index 88ae576..7310fe9 100644
--- a/cpukit/include/rtems/rtems/status.h
+++ b/cpukit/include/rtems/rtems/status.h
@@ -167,6 +167,12 @@ typedef enum {
    */
   RTEMS_IO_ERROR                 = 27,
   /**
+   *  This is the status used internally to indicate a blocking device
+   *  driver call has been interrupted and should be reflected to the
+   *  called as an INTERRUPTED.
+   */
+  RTEMS_INTERRUPTED              = 28,
+  /**
    *  This is the status is used internally to RTEMS when performing
    *  operations on behalf of remote tasks.  This is referred to as
    *  proxying operations and this status indicates that the operation
@@ -174,7 +180,7 @@ typedef enum {
    *
    *  @note This status will @b NOT be returned to the user.
    */
-  RTEMS_PROXY_BLOCKING           = 28
+  RTEMS_PROXY_BLOCKING           = 29
 } rtems_status_code;
 
 /**
@@ -235,6 +241,7 @@ RTEMS_INLINE_ROUTINE bool rtems_are_statuses_equal(
  *  @retval ENODEV RTEMS_UNSATISFIED
  *  @retval ENOSYS RTEMS_NOT_IMPLEMENTED, RTEMS_NOT_CONFIGURED
  *  @retval ENOMEM RTEMS_NO_MEMORY
+ *  @retval EINTR RTEMS_INTERRUPTED
  */
 int rtems_status_code_to_errno(rtems_status_code sc);
 
diff --git a/cpukit/libcsupport/src/termios.c b/cpukit/libcsupport/src/termios.c
index 0fc9de45..fedaa80 100644
--- a/cpukit/libcsupport/src/termios.c
+++ b/cpukit/libcsupport/src/termios.c
@@ -1301,76 +1301,169 @@ iprocEarly (unsigned char c, rtems_termios_tty *tty)
 }
 
 /*
+ * This points to the currently registered method to perform
+ * ISIG processing of VKILL and VQUIT characters.
+ */
+static rtems_termios_isig_handler termios_isig_handler =
+   rtems_termios_default_isig_handler;
+
+/*
+ * This is the default method to process VKILL or VQUIT characters if
+ * ISIG processing is enabled. Note that it does nothing.
+ */
+rtems_termios_isig_status_code rtems_termios_default_isig_handler(
+  unsigned char c,
+  struct rtems_termios_tty *tty
+)
+{
+  return RTEMS_TERMIOS_ISIG_WAS_NOT_PROCESSED;
+}
+
+/*
+ * Register a method to process VKILL or VQUIT characters if
+ * ISIG processing is enabled.
+ */
+rtems_status_code rtems_termios_register_isig_handler(
+  rtems_termios_isig_handler handler
+)
+{
+  if (handler == NULL) {
+    return RTEMS_INVALID_ADDRESS;
+  }
+
+  termios_isig_handler = handler;
+  return RTEMS_SUCCESSFUL;
+}
+
+/**
+ * @brief Type returned by all input processing (iproc) methods
+ */ 
+typedef enum {
+  /**
+   * This indicates that the input character was processed
+   * and the results were placed into the buffer.
+   */
+  RTEMS_TERMIOS_IPROC_IN_BUFFER,
+  /**
+   * This indicates that the input character was processed
+   * and the result did not go into the buffer.
+   */
+  RTEMS_TERMIOS_IPROC_WAS_PROCESSED,
+  /**
+   * This indicates that the input character was not processed and
+   * subsequent processing is required.
+   */
+  RTEMS_TERMIOS_IPROC_WAS_NOT_PROCESSED,
+  /**
+   * This indicates that the character was processed and determined
+   * to be one that requires a signal to be raised (e.g. VINTR or
+   * VKILL). The tty must be in the right termios mode for this to
+   * occur. There is no further processing of this character required and
+   * the pending read() operation should be interrupted.
+   */
+  RTEMS_TERMIOS_IPROC_INTERRUPT_READ
+} rtems_termios_iproc_status_code;
+
+/*
  * Process a single input character
  */
-static int
+static rtems_termios_iproc_status_code
 iproc (unsigned char c, struct rtems_termios_tty *tty)
 {
+  /*
+   * If signals are enabled, then allow possibility of VINTR causing
+   * SIGINTR and VQUIT causing SIGQUIT. Invoke user provided isig handler.
+   */
+  if ((tty->termios.c_lflag & ISIG)) {
+    if ((c == tty->termios.c_cc[VINTR]) || (c == tty->termios.c_cc[VQUIT])) {
+      rtems_termios_isig_status_code rc;
+      rc = (*termios_isig_handler)(c, tty);
+      if (rc == RTEMS_TERMIOS_ISIG_INTERRUPT_READ) {
+         return RTEMS_TERMIOS_IPROC_INTERRUPT_READ;
+      }
+      if (rc == RTEMS_TERMIOS_ISIG_WAS_PROCESSED) {
+         return RTEMS_TERMIOS_IPROC_IN_BUFFER;
+      }
+      _Assert(rc == RTEMS_TERMIOS_ISIG_WAS_NOT_PROCESSED);
+    }
+  }
+
+  /*
+   * Perform canonical character processing.
+   */
   if ((c != '\0') && (tty->termios.c_lflag & ICANON)) {
     if (c == tty->termios.c_cc[VERASE]) {
       erase (tty, 0);
-      return 0;
+      return RTEMS_TERMIOS_IPROC_WAS_PROCESSED;
     }
     else if (c == tty->termios.c_cc[VKILL]) {
       erase (tty, 1);
-      return 0;
+      return RTEMS_TERMIOS_IPROC_WAS_PROCESSED;
     }
     else if (c == tty->termios.c_cc[VEOF]) {
-      return 1;
+      return RTEMS_TERMIOS_IPROC_IN_BUFFER;
     } else if (c == '\n') {
       if (tty->termios.c_lflag & (ECHO | ECHONL))
         echo (c, tty);
       tty->cbuf[tty->ccount++] = c;
-      return 1;
+      return RTEMS_TERMIOS_IPROC_IN_BUFFER;
     } else if ((c == tty->termios.c_cc[VEOL]) ||
                (c == tty->termios.c_cc[VEOL2])) {
       if (tty->termios.c_lflag & ECHO)
         echo (c, tty);
       tty->cbuf[tty->ccount++] = c;
-      return 1;
+      return RTEMS_TERMIOS_IPROC_IN_BUFFER;
     }
   }
 
   /*
+   * Perform non-canonical character processing.
+   *
    * FIXME: Should do IMAXBEL handling somehow
    */
   if (tty->ccount < (CBUFSIZE-1)) {
     if (tty->termios.c_lflag & ECHO)
       echo (c, tty);
     tty->cbuf[tty->ccount++] = c;
+    return RTEMS_TERMIOS_IPROC_IN_BUFFER;
   }
-  return 0;
+  return RTEMS_TERMIOS_IPROC_WAS_NOT_PROCESSED;
 }
 
 /*
  * Process input character, with semaphore.
  */
-static int
+static rtems_termios_iproc_status_code
 siproc (unsigned char c, struct rtems_termios_tty *tty)
 {
-  int i;
+  rtems_termios_iproc_status_code rc;
 
   /*
    * Obtain output semaphore if character will be echoed
    */
   if (tty->termios.c_lflag & (ECHO|ECHOE|ECHOK|ECHONL|ECHOPRT|ECHOCTL|ECHOKE)) {
     rtems_mutex_lock (&tty->osem);
-    i = iproc (c, tty);
+    rc = iproc (c, tty);
     rtems_mutex_unlock (&tty->osem);
   }
   else {
-    i = iproc (c, tty);
+    rc = iproc (c, tty);
   }
-  return i;
+  return rc;
 }
 
-static int
+static rtems_termios_iproc_status_code
 siprocPoll (unsigned char c, rtems_termios_tty *tty)
 {
   if (c == '\r' && (tty->termios.c_iflag & IGNCR) != 0) {
-    return 0;
+    return RTEMS_TERMIOS_IPROC_WAS_NOT_PROCESSED;
   }
 
+  /*
+   * iprocEarly is done at the interrupt level for interrupt driven
+   * devices so we need to perform those actions before processing
+   * the character.
+   */
   c = iprocEarly (c, tty);
   return siproc (c, tty);
 }
@@ -1378,10 +1471,12 @@ siprocPoll (unsigned char c, rtems_termios_tty *tty)
 /*
  * Fill the input buffer by polling the device
  */
-static void
+static rtems_termios_iproc_status_code
 fillBufferPoll (struct rtems_termios_tty *tty)
 {
-  int n;
+  int                             n;
+  rtems_termios_iproc_status_code rc;
+  rtems_termios_iproc_status_code retval = RTEMS_TERMIOS_IPROC_WAS_PROCESSED;
 
   if (tty->termios.c_lflag & ICANON) {
     for (;;) {
@@ -1389,8 +1484,14 @@ fillBufferPoll (struct rtems_termios_tty *tty)
       if (n < 0) {
         rtems_task_wake_after (1);
       } else {
-        if  (siprocPoll (n, tty))
+        rc = siprocPoll (n, tty);
+        if (rc == RTEMS_TERMIOS_IPROC_IN_BUFFER) {
+          break;
+        }
+        if (rc == RTEMS_TERMIOS_IPROC_INTERRUPT_READ) {
+          retval = RTEMS_TERMIOS_IPROC_INTERRUPT_READ;
           break;
+        }
       }
     }
   } else {
@@ -1417,7 +1518,11 @@ fillBufferPoll (struct rtems_termios_tty *tty)
         }
         rtems_task_wake_after (1);
       } else {
-        siprocPoll (n, tty);
+        rc = siprocPoll (n, tty);
+        if (rc == RTEMS_TERMIOS_IPROC_INTERRUPT_READ) {
+          retval = RTEMS_TERMIOS_IPROC_INTERRUPT_READ;
+          break;
+        }
         if (tty->ccount >= tty->termios.c_cc[VMIN])
           break;
         if (tty->termios.c_cc[VMIN] && tty->termios.c_cc[VTIME])
@@ -1425,12 +1530,13 @@ fillBufferPoll (struct rtems_termios_tty *tty)
       }
     }
   }
+  return retval;
 }
 
 /*
  * Fill the input buffer from the raw input queue
  */
-static void
+static rtems_termios_iproc_status_code
 fillBufferQueue (struct rtems_termios_tty *tty)
 {
   rtems_termios_device_context *ctx = tty->device_context;
@@ -1448,8 +1554,9 @@ fillBufferQueue (struct rtems_termios_tty *tty)
 
     while ((tty->rawInBuf.Head != tty->rawInBuf.Tail) &&
                        (tty->ccount < (CBUFSIZE-1))) {
-      unsigned char c;
-      unsigned int newHead;
+      unsigned char                    c;
+      unsigned int                     newHead;
+      rtems_termios_iproc_status_code  rc;
 
       newHead = (tty->rawInBuf.Head + 1) % tty->rawInBuf.Size;
       c = tty->rawInBuf.theBuf[newHead];
@@ -1479,12 +1586,21 @@ fillBufferQueue (struct rtems_termios_tty *tty)
 
       /* continue processing new character */
       if (tty->termios.c_lflag & ICANON) {
-        if (siproc (c, tty)) {
-          /* In canonical mode, input is made available line by line */
-          return;
+        rc = siproc (c, tty);
+        /* If the read() should be interrupted, return */
+        if (rc == RTEMS_TERMIOS_IPROC_INTERRUPT_READ) {
+          return RTEMS_TERMIOS_IPROC_INTERRUPT_READ;
+        }
+	/* In canonical mode, input is made available line by line */
+        if (rc == RTEMS_TERMIOS_IPROC_IN_BUFFER) {
+          return RTEMS_TERMIOS_IPROC_IN_BUFFER;
         }
       } else {
-        siproc (c, tty);
+        rc = siproc (c, tty);
+        /* If the read() should be interrupted, return */
+        if (rc == RTEMS_TERMIOS_IPROC_INTERRUPT_READ) {
+          return RTEMS_TERMIOS_IPROC_INTERRUPT_READ;
+        }
         if (tty->ccount >= tty->termios.c_cc[VMIN])
           wait = false;
       }
@@ -1519,13 +1635,19 @@ fillBufferQueue (struct rtems_termios_tty *tty)
       }
     }
   }
+  return RTEMS_TERMIOS_IPROC_WAS_PROCESSED;
 }
 
-static uint32_t
-rtems_termios_read_tty (struct rtems_termios_tty *tty, char *buffer,
-  uint32_t initial_count)
+static rtems_status_code
+rtems_termios_read_tty (
+  struct rtems_termios_tty *tty,
+  char                     *buffer,
+  uint32_t                  initial_count,
+  uint32_t                 *count_read
+)
 {
-  uint32_t count;
+  uint32_t                         count;
+  rtems_termios_iproc_status_code  rc = RTEMS_TERMIOS_IPROC_WAS_PROCESSED;
 
   count = initial_count;
 
@@ -1533,28 +1655,43 @@ rtems_termios_read_tty (struct rtems_termios_tty *tty, char *buffer,
     tty->cindex = tty->ccount = 0;
     tty->read_start_column = tty->column;
     if (tty->handler.poll_read != NULL && tty->handler.mode == TERMIOS_POLLED)
-      fillBufferPoll (tty);
+      rc = fillBufferPoll (tty);
     else
-      fillBufferQueue (tty);
+      rc = fillBufferQueue (tty);
   }
+
+  /*
+   * If there are characters in the buffer, then copy them to the caller.
+   */
   while (count && (tty->cindex < tty->ccount)) {
     *buffer++ = tty->cbuf[tty->cindex++];
     count--;
-  }
+   }
   tty->tty_rcvwakeup = false;
-  return initial_count - count;
+  *count_read = initial_count - count;
+
+  /*
+   * fillBufferPoll and fillBufferQueue can indicate that the operation
+   * was interrupted. The isig handler can do nothing, have POSIX behavior
+   * and cause a signal, or a user defined action.
+   */
+  if (rc == RTEMS_TERMIOS_IPROC_INTERRUPT_READ) {
+    return RTEMS_INTERRUPTED;
+  }
+
+  return RTEMS_SUCCESSFUL;
 }
 
 rtems_status_code
 rtems_termios_read (void *arg)
 {
-  rtems_libio_rw_args_t *args = arg;
+  rtems_libio_rw_args_t    *args = arg;
   struct rtems_termios_tty *tty = args->iop->data1;
+  rtems_status_code         sc;
 
   rtems_mutex_lock (&tty->isem);
 
   if (rtems_termios_linesw[tty->t_line].l_read != NULL) {
-    rtems_status_code sc;
 
     sc = rtems_termios_linesw[tty->t_line].l_read(tty,args);
     tty->tty_rcvwakeup = false;
@@ -1562,9 +1699,14 @@ rtems_termios_read (void *arg)
     return sc;
   }
 
-  args->bytes_moved = rtems_termios_read_tty (tty, args->buffer, args->count);
+  sc = rtems_termios_read_tty(
+    tty,
+    args->buffer,
+    args->count,
+    &args->bytes_moved
+  );
   rtems_mutex_unlock (&tty->isem);
-  return RTEMS_SUCCESSFUL;
+  return sc;
 }
 
 /*
@@ -2022,7 +2164,8 @@ static ssize_t
 rtems_termios_imfs_read (rtems_libio_t *iop, void *buffer, size_t count)
 {
   struct rtems_termios_tty *tty;
-  uint32_t bytes_moved;
+  uint32_t                  bytes_moved;
+  rtems_status_code         sc;
 
   tty = iop->data1;
 
@@ -2049,9 +2192,13 @@ rtems_termios_imfs_read (rtems_libio_t *iop, void *buffer, size_t count)
     return (ssize_t) args.bytes_moved;
   }
 
-  bytes_moved = rtems_termios_read_tty (tty, buffer, count);
+  sc = rtems_termios_read_tty(tty, buffer, count, &bytes_moved);
   rtems_mutex_unlock (&tty->isem);
+  if (sc != RTEMS_SUCCESSFUL) {
+     return rtems_status_code_to_errno (sc);
+  }
   return (ssize_t) bytes_moved;
+  
 }
 
 static ssize_t
diff --git a/cpukit/libcsupport/src/termios_posix_isig_handler.c b/cpukit/libcsupport/src/termios_posix_isig_handler.c
new file mode 100644
index 0000000..105e7f4
--- /dev/null
+++ b/cpukit/libcsupport/src/termios_posix_isig_handler.c
@@ -0,0 +1,39 @@
+/**
+ * @file
+ * TERMIOS POSIX behavior on INTR and QUIT characters
+ */
+
+/*
+ *  COPYRIGHT (c) 1989-2012,2019.
+ *  On-Line Applications Research Corporation (OAR).
+ *
+ *  SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <rtems.h>
+#include <rtems/libio.h>
+#include <rtems/termiostypes.h>
+
+#include <signal.h>
+
+rtems_termios_isig_status_code rtems_termios_posix_isig_handler(
+  unsigned char             c,
+  struct rtems_termios_tty *tty
+)
+{
+  if (c == tty->termios.c_cc[VINTR]) {
+    raise(SIGINT);
+    return RTEMS_TERMIOS_ISIG_INTERRUPT_READ;
+  }
+
+  if (c == tty->termios.c_cc[VQUIT]) {
+    raise(SIGQUIT);
+    return RTEMS_TERMIOS_ISIG_INTERRUPT_READ;
+  }
+
+  return RTEMS_TERMIOS_ISIG_WAS_NOT_PROCESSED;
+}
diff --git a/cpukit/rtems/src/statustext.c b/cpukit/rtems/src/statustext.c
index f701ebd..2369794 100644
--- a/cpukit/rtems/src/statustext.c
+++ b/cpukit/rtems/src/statustext.c
@@ -53,6 +53,7 @@ static const char *const status_code_text[] = {
   "RTEMS_INTERNAL_ERROR",
   "RTEMS_NO_MEMORY",
   "RTEMS_IO_ERROR",
+  "RTEMS_INTERRUPTED",
   "RTEMS_PROXY_BLOCKING"
 };
 
diff --git a/cpukit/rtems/src/statustoerrno.c b/cpukit/rtems/src/statustoerrno.c
index 8f34ff5..76a9e2c 100644
--- a/cpukit/rtems/src/statustoerrno.c
+++ b/cpukit/rtems/src/statustoerrno.c
@@ -45,6 +45,7 @@ static const int status_code_to_errno [RTEMS_STATUS_CODES_LAST + 1] = {
   [RTEMS_INTERNAL_ERROR]           = EIO,
   [RTEMS_NO_MEMORY]                = ENOMEM,
   [RTEMS_IO_ERROR]                 = EIO,
+  [RTEMS_INTERRUPTED]              = EINTR,
   [RTEMS_PROXY_BLOCKING]           = EIO
 };
 
diff --git a/testsuites/libtests/Makefile.am b/testsuites/libtests/Makefile.am
index 1b80283..20a5a6f 100644
--- a/testsuites/libtests/Makefile.am
+++ b/testsuites/libtests/Makefile.am
@@ -1489,7 +1489,27 @@ lib_screens += termios09/termios09.scn
 lib_docs += termios09/termios09.doc
 termios09_SOURCES = termios09/init.c
 termios09_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_FLAGS_termios09) \
-	$(support_includes)
+	$(support_includes) 
+endif
+
+if TEST_termios10
+lib_tests += termios10
+lib_screens += termios10/termios10.scn
+lib_docs += termios10/termios10.doc
+termios10_SOURCES  = termios10/init.c
+termios10_SOURCES += termios03/termios_testdriver_polled.c
+termios10_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_FLAGS_termios10) \
+	$(support_includes) -I$(top_srcdir)/termios03
+endif
+
+if TEST_termios11
+lib_tests += termios11
+lib_screens += termios11/termios11.scn
+lib_docs += termios11/termios11.doc
+termios11_SOURCES  = termios10/init.c
+termios11_SOURCES += termios04/termios_testdriver_intr.c
+termios11_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_FLAGS_termios11) \
+	$(support_includes) -I$(top_srcdir)/termios04 -DINTERRUPT_DRIVEN
 endif
 
 if TEST_top
diff --git a/testsuites/libtests/configure.ac b/testsuites/libtests/configure.ac
index c02b42a..4d0707c 100644
--- a/testsuites/libtests/configure.ac
+++ b/testsuites/libtests/configure.ac
@@ -225,6 +225,8 @@ RTEMS_TEST_CHECK([termios06])
 RTEMS_TEST_CHECK([termios07])
 RTEMS_TEST_CHECK([termios08])
 RTEMS_TEST_CHECK([termios09])
+RTEMS_TEST_CHECK([termios10])
+RTEMS_TEST_CHECK([termios11])
 RTEMS_TEST_CHECK([top])
 RTEMS_TEST_CHECK([ttest01])
 RTEMS_TEST_CHECK([tztest])
diff --git a/testsuites/libtests/termios10/init.c b/testsuites/libtests/termios10/init.c
new file mode 100644
index 0000000..ca5de97
--- /dev/null
+++ b/testsuites/libtests/termios10/init.c
@@ -0,0 +1,191 @@
+/*
+ *  COPYRIGHT (c) 1989-2012,2019.
+ *  On-Line Applications Research Corporation (OAR).
+ *
+ *  SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <tmacros.h>
+#include "test_support.h"
+
+#ifdef INTERRUPT_DRIVEN
+#include "termios_testdriver_intr.h"
+const char rtems_test_name[] = "TERMIOS 11 -- Interrupt driven";
+#else
+#include "termios_testdriver_polled.h"
+const char rtems_test_name[] = "TERMIOS 10 -- Polled";
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <signal.h>
+#include <termios.h>
+#include <rtems/dumpbuf.h>
+#include <rtems/libio.h>
+
+int Test_fd;
+
+static void open_it(void)
+{
+  Test_fd = open( TERMIOS_TEST_DRIVER_DEVICE_NAME, O_RDWR );
+  rtems_test_assert( Test_fd != -1 );
+}
+
+static void change_lflag( const char *desc, int mask, int new )
+{ 
+  int            rc;
+  struct termios attr;
+  
+  if (desc) {
+    printf( "Changing c_lflag to: %s\n", desc );
+  }
+
+  rc = tcgetattr( Test_fd, &attr );
+  rtems_test_assert( rc == 0 );
+  
+  attr.c_lflag &= ~mask;
+  attr.c_lflag |= new;
+  
+  rc = tcsetattr( Test_fd, TCSANOW, &attr );
+  rtems_test_assert( rc == 0 );
+}
+
+
+static void read_it(ssize_t expected, int expected_intr)
+{
+  ssize_t rc;
+  char    buf[32];
+
+  rtems_test_assert( expected <= sizeof(buf) );
+
+  rc = read( Test_fd, buf, expected ); 
+  if (expected_intr) {
+    rtems_test_assert( rc == -1 );
+    rtems_test_assert( errno == EINTR );
+  } else {
+    if ( expected != rc )
+      printf( "ERROR - expected=%zd rc=%zd\n", expected, rc );
+    rtems_test_assert( expected == rc );
+  }
+}
+
+static void close_it(void)
+{
+  int rc;
+
+  rc = close( Test_fd );
+  rtems_test_assert( rc == 0 );
+}
+
+volatile int sigint_occurred = 0;
+volatile int sigquit_occurred = 0;
+
+static void sigint_handler(int signo)
+{
+  rtems_test_assert(signo == SIGINT);
+  sigint_occurred = 1; 
+}
+
+static void sigquit_handler(int signo)
+{
+  rtems_test_assert(signo == SIGQUIT);
+  sigquit_occurred = 1; 
+}
+
+static void test_read_for_signal(
+  const char *description,
+  int         isig_value,
+  char        c,
+  int         sigint_expected,
+  int         sigquit_expected
+)
+{
+  char expected[3];
+
+  printf("Test read for %s\n", description);
+
+  expected[0] = c;
+  expected[1] = '\n'; /* in canonical mode, so need \n for read to return */
+  expected[2] = '\0';
+
+  sigint_occurred  = 0;
+  sigquit_occurred = 0;
+
+  open_it();
+
+  change_lflag(NULL, ISIG, isig_value);
+
+  termios_test_driver_set_rx( expected, 2 );
+
+  read_it(1, (sigint_expected || sigquit_expected));
+
+  rtems_test_assert(sigint_occurred == sigint_expected);
+  rtems_test_assert(sigquit_occurred == sigquit_expected);
+  close_it();
+}
+
+/*
+ * Use a POSIX init thread so signals are enabled.
+ */
+static void *POSIX_Init(void *argument)
+{
+  int rc;
+
+  TEST_BEGIN();
+
+  signal(SIGINT, sigint_handler);
+  signal(SIGQUIT, sigquit_handler);
+
+  puts( "Exercise default ISIG handler with ISIG enabled");
+  test_read_for_signal("VKILL - no signals", ISIG, '\003', 0, 0);
+  test_read_for_signal("VQUIT - no signals", ISIG, '\034', 0, 0);
+
+  puts( "Exercise POSIX ISIG handler with ISIG enabled");
+  rc = rtems_termios_register_isig_handler(rtems_termios_posix_isig_handler);
+  rtems_test_assert( rc == 0 );
+  test_read_for_signal("VKILL - signal caught", ISIG, '\003', 1, 0);
+  test_read_for_signal("VQUIT - signal caught", ISIG, '\034', 0, 1);
+
+  puts( "Exercise default ISIG handler with ISIG enabled");
+  rc = rtems_termios_register_isig_handler(rtems_termios_default_isig_handler);
+  rtems_test_assert( rc == 0 );
+  test_read_for_signal("VKILL - signal caught", ISIG, '\003', 0, 0);
+  test_read_for_signal("VQUIT - signal caught", ISIG, '\034', 0, 0);
+
+  puts( "Exercise POSIX ISIG handler with ISIG disabled");
+  rc = rtems_termios_register_isig_handler(rtems_termios_posix_isig_handler);
+  rtems_test_assert( rc == 0 );
+  test_read_for_signal("VKILL - signal caught", 0, '\003', 0, 0);
+  test_read_for_signal("VQUIT - signal caught", 0, '\034', 0, 0);
+
+  TEST_END();
+
+  rtems_test_exit(0);
+}
+
+/* configuration information */
+
+#define CONFIGURE_APPLICATION_NEEDS_SIMPLE_CONSOLE_DRIVER
+#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
+#define CONFIGURE_APPLICATION_EXTRA_DRIVERS \
+  TERMIOS_TEST_DRIVER_TABLE_ENTRY
+
+/* we need to be able to open the test device */
+#define CONFIGURE_LIBIO_MAXIMUM_FILE_DESCRIPTORS 4
+#define CONFIGURE_MAXIMUM_POSIX_THREADS     1
+#define CONFIGURE_MAXIMUM_TIMERS            2
+#define CONFIGURE_INITIAL_EXTENSIONS RTEMS_TEST_INITIAL_EXTENSION
+
+#define CONFIGURE_POSIX_INIT_THREAD_TABLE
+
+#define CONFIGURE_INIT
+
+#include <rtems/confdefs.h>
+/* end of file */
diff --git a/testsuites/libtests/termios10/termios10.doc b/testsuites/libtests/termios10/termios10.doc
new file mode 100644
index 0000000..c98e5ca
--- /dev/null
+++ b/testsuites/libtests/termios10/termios10.doc
@@ -0,0 +1,23 @@
+#  COPYRIGHT (c) 2019.
+#  On-Line Applications Research Corporation (OAR).
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+
+test name:  termios10
+
+directives:
+  rtems_termios_register_isig_handler
+
+concepts:
+
++ Verifies signals are not delivered on QUIT and INTR characters
+  as default behavior.
+
++ Verified that signals are delivered when QUIT or INTR characters
+  are entered after rtems_termios_posix_isig_handler()
+  is registered using rtems_termios_register_isig_handler().
+
++ Verifies signals are not delivered on QUIT and INTR characters
+  after the default handler rtems_termios_default_isig_handler()
+  is registered using rtems_termios_register_isig_handler().
diff --git a/testsuites/libtests/termios10/termios10.scn b/testsuites/libtests/termios10/termios10.scn
new file mode 100644
index 0000000..1987528
--- /dev/null
+++ b/testsuites/libtests/termios10/termios10.scn
@@ -0,0 +1,14 @@
+*** BEGIN OF TEST TERMIOS 10 -- Polled ***
+Exercise default ISIG handler with ISIG enabled
+Test read for VKILL - no signals
+Test read for VQUIT - no signals
+Exercise POSIX ISIG handler with ISIG enabled
+Test read for VKILL - signal caught
+Test read for VQUIT - signal caught
+Exercise default ISIG handler with ISIG enabled
+Test read for VKILL - signal caught
+Test read for VQUIT - signal caught
+Exercise POSIX ISIG handler with ISIG disabled
+Test read for VKILL - signal caught
+Test read for VQUIT - signal caught
+*** END OF TEST TERMIOS 10 -- Polled ***
diff --git a/testsuites/libtests/termios11/termios11.doc b/testsuites/libtests/termios11/termios11.doc
new file mode 100644
index 0000000..c98e5ca
--- /dev/null
+++ b/testsuites/libtests/termios11/termios11.doc
@@ -0,0 +1,23 @@
+#  COPYRIGHT (c) 2019.
+#  On-Line Applications Research Corporation (OAR).
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+
+test name:  termios10
+
+directives:
+  rtems_termios_register_isig_handler
+
+concepts:
+
++ Verifies signals are not delivered on QUIT and INTR characters
+  as default behavior.
+
++ Verified that signals are delivered when QUIT or INTR characters
+  are entered after rtems_termios_posix_isig_handler()
+  is registered using rtems_termios_register_isig_handler().
+
++ Verifies signals are not delivered on QUIT and INTR characters
+  after the default handler rtems_termios_default_isig_handler()
+  is registered using rtems_termios_register_isig_handler().
diff --git a/testsuites/libtests/termios11/termios11.scn b/testsuites/libtests/termios11/termios11.scn
new file mode 100644
index 0000000..d4c3798
--- /dev/null
+++ b/testsuites/libtests/termios11/termios11.scn
@@ -0,0 +1,14 @@
+*** BEGIN OF TEST TERMIOS 11 -- Interrupt driven ***
+Exercise default ISIG handler with ISIG enabled
+Test read for VKILL - no signals
+Test read for VQUIT - no signals
+Exercise POSIX ISIG handler with ISIG enabled
+Test read for VKILL - signal caught
+Test read for VQUIT - signal caught
+Exercise default ISIG handler with ISIG enabled
+Test read for VKILL - signal caught
+Test read for VQUIT - signal caught
+Exercise POSIX ISIG handler with ISIG disabled
+Test read for VKILL - signal caught
+Test read for VQUIT - signal caught
+*** END OF TEST TERMIOS 11 -- Interrupt driven ***



More information about the vc mailing list