[PATCH 1/4] termios: New low-level device API

Sebastian Huber sebastian.huber at embedded-brains.de
Mon Jul 7 11:39:16 UTC 2014


Add a new low-level device API to Termios that passes the TTY structure
to the low-level device functions.  This greatly simplifies the
low-level device drivers since they are no longer forced to derive their
private data from the minor number.

It makes it possible to use the TTY low-level lock in the device driver
low-level functions which is necessary for proper SMP support.  For
example to set the attributes it is often necessary to perform a
read-modify-write operation on a control register used also by interrupt
routines.

A compatibility layer is provided to support device drivers using the
old callback functions so it is not necessary to modify existing device
drivers.
---
 cpukit/libcsupport/include/rtems/termiostypes.h |  236 ++++++++++-
 cpukit/libcsupport/src/termios.c                |  502 ++++++++++++++++++-----
 testsuites/libtests/termios01/init.c            |   97 +++++-
 testsuites/libtests/termios01/termios01.doc     |    5 +
 4 files changed, 726 insertions(+), 114 deletions(-)

diff --git a/cpukit/libcsupport/include/rtems/termiostypes.h b/cpukit/libcsupport/include/rtems/termiostypes.h
index dcc8f69..9ae1189 100644
--- a/cpukit/libcsupport/include/rtems/termiostypes.h
+++ b/cpukit/libcsupport/include/rtems/termiostypes.h
@@ -19,6 +19,7 @@
 #include <rtems.h>
 #include <rtems/libio.h>
 #include <rtems/assoc.h>
+#include <rtems/chain.h>
 #include <stdint.h>
 #include <termios.h>
 
@@ -52,11 +53,136 @@ struct rtems_termios_rawbuf {
   volatile unsigned int  Size;
   rtems_id    Semaphore;
 };
+
+typedef enum {
+  TERMIOS_POLLED,
+  TERMIOS_IRQ_DRIVEN,
+  TERMIOS_TASK_DRIVEN
+} rtems_termios_device_mode;
+
+struct rtems_termios_tty;
+
+/**
+ * @brief Termios device handler.
+ *
+ * @see rtems_termios_device_install().
+ */
+typedef struct {
+  /**
+   * @brief First open of this device.
+   *
+   * @param[in] tty The Termios control.
+   * @param[in] args The open/close arguments.
+   *
+   * @retval true Successful operation.
+   * @retval false Cannot open device.
+   *
+   * @see rtems_termios_get_device_context().
+   */
+  bool (*first_open)(
+    struct rtems_termios_tty *tty,
+    rtems_libio_open_close_args_t *args
+  );
+
+  /**
+   * @brief Last close of this device.
+   *
+   * @param[in] tty The Termios control.
+   * @param[in] args The open/close arguments.
+   *
+   * @see rtems_termios_get_device_context().
+   */
+  void (*last_close)(
+    struct rtems_termios_tty *tty,
+    rtems_libio_open_close_args_t *args
+  );
+
+  /**
+   * @brief Polled read.
+   *
+   * In case mode is TERMIOS_IRQ_DRIVEN or TERMIOS_TASK_DRIVEN, then data is
+   * received via rtems_termios_enqueue_raw_characters().
+   *
+   * @param[in] tty The Termios control.
+   *
+   * @retval char The received data encoded as unsigned char.
+   * @retval -1 No data currently available.
+   *
+   * @see rtems_termios_get_device_context().
+   */
+  int (*poll_read)(struct rtems_termios_tty *tty);
+
+  /**
+   * @brief Polled write in case mode is TERMIOS_POLLED or write support
+   * otherwise.
+   *
+   * @param[in] tty The Termios control.
+   * @param[in] buf The output buffer.
+   * @param[in] len The output buffer length in characters.
+   *
+   * @see rtems_termios_get_device_context().
+   */
+  void (*write)(struct rtems_termios_tty *tty, const char *buf, size_t len);
+
+  /**
+   * @brief Set attributes after a Termios settings change.
+   *
+   * @param[in] tty The Termios control.
+   * @param[in] term The new Termios attributes.
+   *
+   * @retval true Successful operation.
+   * @retval false Invalid attributes.
+   *
+   * @see rtems_termios_get_device_context().
+   */
+  bool (*set_attributes)(
+    struct rtems_termios_tty *tty,
+    const struct termios *term
+  );
+
+  /**
+   * @brief Indicate to stop remote transmitter.
+   *
+   * @param[in] tty The Termios control.
+   *
+   * @see rtems_termios_get_device_context().
+   */
+  void (*stop_remote_tx)(struct rtems_termios_tty *tty);
+
+  /**
+   * @brief Indicate to start remote transmitter.
+   *
+   * @param[in] tty The Termios control.
+   *
+   * @see rtems_termios_get_device_context().
+   */
+  void (*start_remote_tx)(struct rtems_termios_tty *tty);
+
+  /**
+   * @brief Termios device mode.
+   */
+  rtems_termios_device_mode mode;
+} rtems_termios_device_handler;
+
+/**
+ * @brief Termios device node for installed devices.
+ *
+ * @see rtems_termios_device_install().
+ */
+typedef struct rtems_termios_device_node {
+  rtems_chain_node                    node;
+  rtems_device_major_number           major;
+  rtems_device_minor_number           minor;
+  const rtems_termios_device_handler *handler;
+  void                               *context;
+  struct rtems_termios_tty           *tty;
+} rtems_termios_device_node;
+
 /*
  * Variables associated with each termios instance.
  * One structure for each hardware I/O device.
  */
-struct rtems_termios_tty {
+typedef struct rtems_termios_tty {
   /*
    * Linked-list of active TERMIOS devices
    */
@@ -119,6 +245,12 @@ struct rtems_termios_tty {
    * Callbacks to device-specific routines
    */
   rtems_termios_callbacks  device;
+
+  /**
+   * @brief The device handler.
+   */
+  rtems_termios_device_handler handler;
+
   volatile unsigned int    flow_ctrl;
   unsigned int             lowwater,highwater;
 
@@ -142,7 +274,99 @@ struct rtems_termios_tty {
   int              tty_rcvwakeup;
 
   rtems_interrupt_lock interrupt_lock;
-};
+
+  /**
+   * @brief Corresponding device node.
+   */
+  rtems_termios_device_node *device_node;
+
+  /**
+   * @brief Context for device driver.
+   *
+   * @see rtems_termios_get_device_context().
+   */
+  void *device_context;
+} rtems_termios_tty;
+
+/**
+ * @brief Installes a Termios device.
+ *
+ * @param[in] device_file If not @c NULL, then a device file for the specified
+ * major and minor number will be created.
+ * @param[in] major The device major number of the corresponding device driver.
+ * @param[in] minor The device minor number of the corresponding device driver.
+ * @param[in] handler The device handler.
+ * @param[in] context The device context.
+ *
+ * @retval RTEMS_SUCCESSFUL Successful operation.
+ * @retval RTEMS_NO_MEMORY Not enough memory to create a device node.
+ * @retval RTEMS_UNSATISFIED Creation of the device file failed.
+ * @retval RTEMS_RESOURCE_IN_USE There exists a device node for this major and
+ * minor number pair.
+ * @retval RTEMS_INCORRECT_STATE Termios is not initialized.
+ *
+ * @see rtems_termios_device_remove(), rtems_termios_device_open(),
+ * rtems_termios_device_close() and rtems_termios_get_device_context().
+ */
+rtems_status_code rtems_termios_device_install(
+  const char                         *device_file,
+  rtems_device_major_number           major,
+  rtems_device_minor_number           minor,
+  const rtems_termios_device_handler *handler,
+  void                               *context
+);
+
+/**
+ * @brief Removes a Termios device.
+ *
+ * @param[in] device_file If not @c NULL, then the device file to remove.
+ * @param[in] major The device major number of the corresponding device driver.
+ * @param[in] minor The device minor number of the corresponding device driver.
+ *
+ * @retval RTEMS_SUCCESSFUL Successful operation.
+ * @retval RTEMS_INVALID_ID There is no device installed with this major and
+ * minor number pair.
+ * @retval RTEMS_RESOURCE_IN_USE This device is currently in use.
+ * @retval RTEMS_UNSATISFIED Removal of the device file failed.
+ * @retval RTEMS_INCORRECT_STATE Termios is not initialized.
+ *
+ * @see rtems_termios_device_install().
+ */
+rtems_status_code rtems_termios_device_remove(
+  const char                *device_file,
+  rtems_device_major_number  major,
+  rtems_device_minor_number  minor
+);
+
+/**
+ * @brief Opens an installed Termios device.
+ *
+ * @see rtems_termios_device_install().
+ */
+rtems_status_code rtems_termios_device_open(
+  rtems_device_major_number  major,
+  rtems_device_minor_number  minor,
+  void                      *arg
+);
+
+/**
+ * @brief Closes an installed Termios device.
+ *
+ * @retval RTEMS_SUCCESSFUL Successful operation.
+ *
+ * @see rtems_termios_device_install().
+ */
+rtems_status_code rtems_termios_device_close(void *arg);
+
+/**
+ * @brief Returns the device context of an installed Termios device.
+ */
+RTEMS_INLINE_ROUTINE void *rtems_termios_get_device_context(
+  const rtems_termios_tty *tty
+)
+{
+  return tty->device_context;
+}
 
 struct rtems_termios_linesw {
   int (*l_open) (struct rtems_termios_tty *tp);
@@ -156,14 +380,6 @@ struct rtems_termios_linesw {
 };
 
 /*
- * FIXME: this should move to libio.h!
- * values for rtems_termios_callbacks.outputUsesInterrupts
- */
-#define TERMIOS_POLLED      0
-#define TERMIOS_IRQ_DRIVEN  1
-#define TERMIOS_TASK_DRIVEN 2
-
-/*
  * FIXME: this should move to termios.h!
  */
 void rtems_termios_rxirq_occured(struct rtems_termios_tty *tty);
diff --git a/cpukit/libcsupport/src/termios.c b/cpukit/libcsupport/src/termios.c
index 1159bca..6c92917 100644
--- a/cpukit/libcsupport/src/termios.c
+++ b/cpukit/libcsupport/src/termios.c
@@ -85,6 +85,8 @@ static size_t rtems_termios_raw_output_size = 64;
 static struct rtems_termios_tty *rtems_termios_ttyHead;
 static struct rtems_termios_tty *rtems_termios_ttyTail;
 
+static RTEMS_CHAIN_DEFINE_EMPTY(rtems_termios_devices);
+
 static rtems_task rtems_termios_rxdaemon(rtems_task_argument argument);
 static rtems_task rtems_termios_txdaemon(rtems_task_argument argument);
 /*
@@ -103,34 +105,197 @@ static rtems_task rtems_termios_txdaemon(rtems_task_argument argument);
 #define TERMIOS_RX_PROC_EVENT      RTEMS_EVENT_1
 #define TERMIOS_RX_TERMINATE_EVENT RTEMS_EVENT_0
 
-/*
- * Open a termios device
- */
-rtems_status_code
-rtems_termios_open (
-  rtems_device_major_number      major,
-  rtems_device_minor_number      minor,
-  void                          *arg,
-  const rtems_termios_callbacks *callbacks
+static rtems_termios_device_node *
+rtems_termios_find_device_node(
+  rtems_device_major_number major,
+  rtems_device_minor_number minor
 )
 {
-  rtems_status_code sc;
-  rtems_libio_open_close_args_t *args = arg;
-  struct rtems_termios_tty *tty;
+  rtems_chain_node *tail = rtems_chain_tail(&rtems_termios_devices);
+  rtems_chain_node *current = rtems_chain_first(&rtems_termios_devices);
+
+  while (current != tail) {
+    rtems_termios_device_node *device_node =
+      (rtems_termios_device_node *) current;
+
+    if (device_node->major == major && device_node->minor == minor) {
+      return device_node;
+    }
+
+    current = rtems_chain_next(current);
+  }
+
+  return NULL;
+}
+
+rtems_status_code rtems_termios_device_install(
+  const char                         *device_file,
+  rtems_device_major_number           major,
+  rtems_device_minor_number           minor,
+  const rtems_termios_device_handler *handler,
+  void                               *context
+)
+{
+  rtems_status_code          sc;
+  rtems_termios_device_node *new_device_node;
+  rtems_termios_device_node *existing_device_node;
+
+  new_device_node = malloc(sizeof(*new_device_node));
+  if (new_device_node == NULL) {
+    return RTEMS_NO_MEMORY;
+  }
+
+  new_device_node->major = major;
+  new_device_node->minor = minor;
+  new_device_node->handler = handler;
+  new_device_node->context = context;
+  new_device_node->tty = NULL;
 
-  /*
-   * See if the device has already been opened
-   */
   sc = rtems_semaphore_obtain(
     rtems_termios_ttyMutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
-  if (sc != RTEMS_SUCCESSFUL)
-    return sc;
+  if (sc != RTEMS_SUCCESSFUL) {
+    free(new_device_node);
+    return RTEMS_INCORRECT_STATE;
+  }
 
-  for (tty = rtems_termios_ttyHead ; tty != NULL ; tty = tty->forw) {
-    if ((tty->major == major) && (tty->minor == minor))
-      break;
+  existing_device_node = rtems_termios_find_device_node (major, minor);
+  if (existing_device_node != NULL) {
+    free(new_device_node);
+    rtems_semaphore_release (rtems_termios_ttyMutex);
+    return RTEMS_RESOURCE_IN_USE;
+  }
+
+  if (device_file != NULL) {
+    sc = rtems_io_register_name (device_file, major, minor);
+    if (sc != RTEMS_SUCCESSFUL) {
+      free(new_device_node);
+      rtems_semaphore_release (rtems_termios_ttyMutex);
+      return RTEMS_UNSATISFIED;
+    }
+  }
+
+  rtems_chain_append_unprotected(
+    &rtems_termios_devices, &new_device_node->node);
+
+  rtems_semaphore_release (rtems_termios_ttyMutex);
+
+  return RTEMS_SUCCESSFUL;
+}
+
+rtems_status_code rtems_termios_device_remove(
+  const char                *device_file,
+  rtems_device_major_number  major,
+  rtems_device_minor_number  minor
+)
+{
+  rtems_status_code          sc;
+  rtems_termios_device_node *device_node;
+
+  sc = rtems_semaphore_obtain(
+    rtems_termios_ttyMutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
+  if (sc != RTEMS_SUCCESSFUL) {
+    return RTEMS_INCORRECT_STATE;
+  }
+
+  device_node = rtems_termios_find_device_node (major, minor);
+  if (device_node == NULL) {
+    rtems_semaphore_release (rtems_termios_ttyMutex);
+    return RTEMS_INVALID_ID;
   }
 
+  if (device_node->tty != NULL) {
+    rtems_semaphore_release (rtems_termios_ttyMutex);
+    return RTEMS_RESOURCE_IN_USE;
+  }
+
+  if (device_file != NULL) {
+    int rv = unlink (device_file);
+
+    if (rv != 0) {
+      rtems_semaphore_release (rtems_termios_ttyMutex);
+      return RTEMS_UNSATISFIED;
+    }
+  }
+
+  rtems_chain_extract_unprotected (&device_node->node);
+  free (device_node);
+
+  rtems_semaphore_release (rtems_termios_ttyMutex);
+
+  return RTEMS_SUCCESSFUL;
+}
+
+static bool
+rtems_termios_callback_firstOpen(
+  rtems_termios_tty             *tty,
+  rtems_libio_open_close_args_t *args
+)
+{
+  (*tty->device.firstOpen) (tty->major, tty->minor, args);
+
+  return true;
+}
+
+static void
+rtems_termios_callback_lastClose(
+  rtems_termios_tty             *tty,
+  rtems_libio_open_close_args_t *args
+)
+{
+  (*tty->device.lastClose) (tty->major, tty->minor, args);
+}
+
+static int
+rtems_termios_callback_pollRead (struct rtems_termios_tty *tty)
+{
+  return (*tty->device.pollRead) (tty->minor);
+}
+
+static void
+rtems_termios_callback_write(
+  rtems_termios_tty *tty,
+  const char        *buf,
+  size_t             len
+)
+{
+  (*tty->device.write) (tty->minor, buf, len);
+}
+
+static bool
+rtems_termios_callback_setAttributes(
+  rtems_termios_tty    *tty,
+  const struct termios *term
+)
+{
+  (*tty->device.setAttributes) (tty->minor, term);
+
+  return true;
+}
+
+static void
+rtems_termios_callback_stopRemoteTx (rtems_termios_tty *tty)
+{
+  (*tty->device.stopRemoteTx) (tty->minor);
+}
+
+static void
+rtems_termios_callback_startRemoteTx (rtems_termios_tty *tty)
+{
+  (*tty->device.startRemoteTx) (tty->minor);
+}
+
+static rtems_termios_tty *
+rtems_termios_open_tty(
+  rtems_device_major_number      major,
+  rtems_device_minor_number      minor,
+  rtems_libio_open_close_args_t *args,
+  rtems_termios_tty             *tty,
+  rtems_termios_device_node     *device_node,
+  const rtems_termios_callbacks *callbacks
+)
+{
+  rtems_status_code sc;
+
   if (tty == NULL) {
     static char c = 'a';
 
@@ -139,8 +304,7 @@ rtems_termios_open (
      */
     tty = calloc (1, sizeof (struct rtems_termios_tty));
     if (tty == NULL) {
-      rtems_semaphore_release (rtems_termios_ttyMutex);
-      return RTEMS_NO_MEMORY;
+      return NULL;
     }
     /*
      * allocate raw input buffer
@@ -149,8 +313,7 @@ rtems_termios_open (
     tty->rawInBuf.theBuf = malloc (tty->rawInBuf.Size);
     if (tty->rawInBuf.theBuf == NULL) {
             free(tty);
-      rtems_semaphore_release (rtems_termios_ttyMutex);
-      return RTEMS_NO_MEMORY;
+      return NULL;
     }
     /*
      * allocate raw output buffer
@@ -160,8 +323,7 @@ rtems_termios_open (
     if (tty->rawOutBuf.theBuf == NULL) {
             free((void *)(tty->rawInBuf.theBuf));
             free(tty);
-      rtems_semaphore_release (rtems_termios_ttyMutex);
-      return RTEMS_NO_MEMORY;
+      return NULL;
     }
     /*
      * allocate cooked buffer
@@ -171,8 +333,7 @@ rtems_termios_open (
             free((void *)(tty->rawOutBuf.theBuf));
             free((void *)(tty->rawInBuf.theBuf));
             free(tty);
-      rtems_semaphore_release (rtems_termios_ttyMutex);
-      return RTEMS_NO_MEMORY;
+      return NULL;
     }
     /*
      * Initialize wakeup callbacks
@@ -183,17 +344,6 @@ rtems_termios_open (
     tty->tty_rcv.sw_arg = NULL;
     tty->tty_rcvwakeup  = 0;
 
-    /*
-     * link tty
-     */
-    tty->forw = rtems_termios_ttyHead;
-    tty->back = NULL;
-    if (rtems_termios_ttyHead != NULL)
-      rtems_termios_ttyHead->back = tty;
-    rtems_termios_ttyHead = tty;
-    if (rtems_termios_ttyTail == NULL)
-      rtems_termios_ttyTail = tty;
-
     tty->minor = minor;
     tty->major = major;
 
@@ -229,14 +379,39 @@ rtems_termios_open (
     /*
      * Set callbacks
      */
-    tty->device = *callbacks;
+    if (device_node != NULL) {
+      device_node->tty = tty;
+      tty->handler = *device_node->handler;
+      tty->device_node = device_node;
+      tty->device_context = device_node->context;
+      memset(&tty->device, 0, sizeof(tty->device));
+    } else {
+      tty->handler.first_open = callbacks->firstOpen != NULL ?
+        rtems_termios_callback_firstOpen : NULL;
+      tty->handler.last_close = callbacks->lastClose != NULL ?
+        rtems_termios_callback_lastClose : NULL;
+      tty->handler.poll_read = callbacks->pollRead != NULL ?
+        rtems_termios_callback_pollRead : NULL;
+      tty->handler.write = callbacks->write != NULL ?
+        rtems_termios_callback_write : NULL;
+      tty->handler.set_attributes = callbacks->setAttributes != NULL ?
+        rtems_termios_callback_setAttributes : NULL;
+      tty->handler.stop_remote_tx = callbacks->stopRemoteTx != NULL ?
+        rtems_termios_callback_stopRemoteTx : NULL;
+      tty->handler.start_remote_tx = callbacks->startRemoteTx != NULL ?
+        rtems_termios_callback_startRemoteTx : NULL;
+      tty->handler.mode = callbacks->outputUsesInterrupts;
+      tty->device_context = NULL;
+      tty->device_node = NULL;
+      tty->device = *callbacks;
+    }
 
     rtems_interrupt_lock_initialize (&tty->interrupt_lock, "Termios");
 
     /*
      * Create I/O tasks
      */
-    if (tty->device.outputUsesInterrupts == TERMIOS_TASK_DRIVEN) {
+    if (tty->handler.mode == TERMIOS_TASK_DRIVEN) {
       sc = rtems_task_create (
                                    rtems_build_name ('T', 'x', 'T', c),
            TERMIOS_TXTASK_PRIO,
@@ -259,8 +434,8 @@ rtems_termios_open (
         rtems_fatal_error_occurred (sc);
 
     }
-    if ((tty->device.pollRead == NULL) ||
-        (tty->device.outputUsesInterrupts == TERMIOS_TASK_DRIVEN)){
+    if ((tty->handler.poll_read == NULL) ||
+        (tty->handler.mode == TERMIOS_TASK_DRIVEN)){
       sc = rtems_semaphore_create (
         rtems_build_name ('T', 'R', 'r', c),
         0,
@@ -311,13 +486,13 @@ rtems_termios_open (
   }
   args->iop->data1 = tty;
   if (!tty->refcount++) {
-    if (tty->device.firstOpen)
-      (*tty->device.firstOpen)(major, minor, arg);
+    if (tty->handler.first_open)
+      (*tty->handler.first_open)(tty, args);
 
     /*
      * start I/O tasks, if needed
      */
-    if (tty->device.outputUsesInterrupts == TERMIOS_TASK_DRIVEN) {
+    if (tty->handler.mode == TERMIOS_TASK_DRIVEN) {
       sc = rtems_task_start(
         tty->rxTaskId, rtems_termios_rxdaemon, (rtems_task_argument)tty);
       if (sc != RTEMS_SUCCESSFUL)
@@ -329,7 +504,93 @@ rtems_termios_open (
         rtems_fatal_error_occurred (sc);
     }
   }
+
+  return tty;
+}
+
+rtems_status_code
+rtems_termios_device_open(
+  rtems_device_major_number  major,
+  rtems_device_minor_number  minor,
+  void                      *arg
+)
+{
+  rtems_status_code          sc;
+  rtems_termios_device_node *device_node;
+  struct rtems_termios_tty  *tty;
+
+  sc = rtems_semaphore_obtain(
+    rtems_termios_ttyMutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
+  if (sc != RTEMS_SUCCESSFUL)
+    return sc;
+
+  device_node = rtems_termios_find_device_node (major, minor);
+  if (device_node == NULL) {
+    rtems_semaphore_release (rtems_termios_ttyMutex);
+    return RTEMS_INVALID_ID;
+  }
+
+  tty = rtems_termios_open_tty(
+    major, minor, arg, device_node->tty, device_node, NULL);
+  if (tty == NULL) {
+    rtems_semaphore_release (rtems_termios_ttyMutex);
+    return RTEMS_NO_MEMORY;
+  }
+
+  rtems_semaphore_release (rtems_termios_ttyMutex);
+
+  return RTEMS_SUCCESSFUL;
+}
+
+/*
+ * Open a termios device
+ */
+rtems_status_code
+rtems_termios_open (
+  rtems_device_major_number      major,
+  rtems_device_minor_number      minor,
+  void                          *arg,
+  const rtems_termios_callbacks *callbacks
+)
+{
+  rtems_status_code sc;
+  struct rtems_termios_tty *tty;
+
+  /*
+   * See if the device has already been opened
+   */
+  sc = rtems_semaphore_obtain(
+    rtems_termios_ttyMutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
+  if (sc != RTEMS_SUCCESSFUL)
+    return sc;
+
+  for (tty = rtems_termios_ttyHead ; tty != NULL ; tty = tty->forw) {
+    if ((tty->major == major) && (tty->minor == minor))
+      break;
+  }
+
+  tty = rtems_termios_open_tty(
+    major, minor, arg, tty, NULL, callbacks);
+  if (tty == NULL) {
+    rtems_semaphore_release (rtems_termios_ttyMutex);
+    return RTEMS_NO_MEMORY;
+  }
+
+  if (tty->refcount == 1) {
+    /*
+     * link tty
+     */
+    tty->forw = rtems_termios_ttyHead;
+    tty->back = NULL;
+    if (rtems_termios_ttyHead != NULL)
+      rtems_termios_ttyHead->back = tty;
+    rtems_termios_ttyHead = tty;
+    if (rtems_termios_ttyTail == NULL)
+      rtems_termios_ttyTail = tty;
+  }
+
   rtems_semaphore_release (rtems_termios_ttyMutex);
+
   return RTEMS_SUCCESSFUL;
 }
 
@@ -342,7 +603,7 @@ drainOutput (struct rtems_termios_tty *tty)
   rtems_interrupt_lock_context lock_context;
   rtems_status_code sc;
 
-  if (tty->device.outputUsesInterrupts != TERMIOS_POLLED) {
+  if (tty->handler.mode != TERMIOS_POLLED) {
     rtems_termios_interrupt_lock_acquire (tty, &lock_context);
     while (tty->rawOutBuf.Tail != tty->rawOutBuf.Head) {
       tty->rawOutBufState = rob_wait;
@@ -380,17 +641,11 @@ flushInput (struct rtems_termios_tty *tty)
   rtems_termios_interrupt_lock_release (tty, &lock_context);
 }
 
-rtems_status_code
-rtems_termios_close (void *arg)
+static void
+rtems_termios_close_tty (rtems_termios_tty *tty, void *arg)
 {
-  rtems_libio_open_close_args_t *args = arg;
-  struct rtems_termios_tty *tty = args->iop->data1;
   rtems_status_code sc;
 
-  sc = rtems_semaphore_obtain(
-    rtems_termios_ttyMutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
-  if (sc != RTEMS_SUCCESSFUL)
-    rtems_fatal_error_occurred (sc);
   if (--tty->refcount == 0) {
     if (rtems_termios_linesw[tty->t_line].l_close != NULL) {
       /*
@@ -409,7 +664,7 @@ rtems_termios_close (void *arg)
       rtems_semaphore_release (tty->osem);
     }
 
-    if (tty->device.outputUsesInterrupts == TERMIOS_TASK_DRIVEN) {
+    if (tty->handler.mode == TERMIOS_TASK_DRIVEN) {
       /*
        * send "terminate" to I/O tasks
        */
@@ -420,8 +675,36 @@ rtems_termios_close (void *arg)
       if (sc != RTEMS_SUCCESSFUL)
         rtems_fatal_error_occurred (sc);
     }
-    if (tty->device.lastClose)
-       (*tty->device.lastClose)(tty->major, tty->minor, arg);
+    if (tty->handler.last_close)
+       (*tty->handler.last_close)(tty, arg);
+
+    rtems_semaphore_delete (tty->isem);
+    rtems_semaphore_delete (tty->osem);
+    rtems_semaphore_delete (tty->rawOutBuf.Semaphore);
+    if ((tty->handler.poll_read == NULL) ||
+        (tty->handler.mode == TERMIOS_TASK_DRIVEN))
+      rtems_semaphore_delete (tty->rawInBuf.Semaphore);
+    rtems_interrupt_lock_destroy (&tty->interrupt_lock);
+    free (tty->rawInBuf.theBuf);
+    free (tty->rawOutBuf.theBuf);
+    free (tty->cbuf);
+    free (tty);
+  }
+}
+
+rtems_status_code
+rtems_termios_close (void *arg)
+{
+  rtems_status_code sc;
+  rtems_libio_open_close_args_t *args = arg;
+  struct rtems_termios_tty *tty = args->iop->data1;
+
+  sc = rtems_semaphore_obtain(
+    rtems_termios_ttyMutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
+  if (sc != RTEMS_SUCCESSFUL)
+    rtems_fatal_error_occurred (sc);
+
+  if (tty->refcount == 1) {
     if (tty->forw == NULL) {
       rtems_termios_ttyTail = tty->back;
       if ( rtems_termios_ttyTail != NULL ) {
@@ -439,20 +722,33 @@ rtems_termios_close (void *arg)
     } else {
       tty->back->forw = tty->forw;
     }
-
-    rtems_semaphore_delete (tty->isem);
-    rtems_semaphore_delete (tty->osem);
-    rtems_semaphore_delete (tty->rawOutBuf.Semaphore);
-    if ((tty->device.pollRead == NULL) ||
-        (tty->device.outputUsesInterrupts == TERMIOS_TASK_DRIVEN))
-      rtems_semaphore_delete (tty->rawInBuf.Semaphore);
-    rtems_interrupt_lock_destroy (&tty->interrupt_lock);
-    free (tty->rawInBuf.theBuf);
-    free (tty->rawOutBuf.theBuf);
-    free (tty->cbuf);
-    free (tty);
   }
+
+  rtems_termios_close_tty (tty, arg);
+
   rtems_semaphore_release (rtems_termios_ttyMutex);
+
+  return RTEMS_SUCCESSFUL;
+}
+
+rtems_status_code
+rtems_termios_device_close (void *arg)
+{
+  rtems_status_code sc;
+  rtems_libio_open_close_args_t *args = arg;
+  struct rtems_termios_tty *tty = args->iop->data1;
+
+  sc = rtems_semaphore_obtain(
+    rtems_termios_ttyMutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
+  if (sc != RTEMS_SUCCESSFUL)
+    rtems_fatal_error_occurred (sc);
+
+  tty->device_node->tty = NULL;
+
+  rtems_termios_close_tty (tty, arg);
+
+  rtems_semaphore_release (rtems_termios_ttyMutex);
+
   return RTEMS_SUCCESSFUL;
 }
 
@@ -490,8 +786,8 @@ termios_set_flowctrl(struct rtems_termios_tty *tty)
       /* check for chars in output buffer (or rob_state?) */
       if (tty->rawOutBufState != rob_idle) {
         /* if chars available, call write function... */
-        (*tty->device.write)(
-          tty->minor, &tty->rawOutBuf.theBuf[tty->rawOutBuf.Tail],1);
+        (*tty->handler.write)(
+          tty, &tty->rawOutBuf.theBuf[tty->rawOutBuf.Tail],1);
       }
       /* reenable interrupts */
       rtems_termios_interrupt_lock_release (tty, &lock_context);
@@ -511,8 +807,9 @@ termios_set_flowctrl(struct rtems_termios_tty *tty)
     tty->flow_ctrl &= ~(FL_MDRTS);
 
     /* restart remote Tx, if it was stopped */
-    if ((tty->flow_ctrl & FL_IRTSOFF) && (tty->device.startRemoteTx != NULL)) {
-      tty->device.startRemoteTx(tty->minor);
+    if ((tty->flow_ctrl & FL_IRTSOFF) &&
+        (tty->handler.start_remote_tx != NULL)) {
+      tty->handler.start_remote_tx(tty);
     }
     tty->flow_ctrl &= ~(FL_IRTSOFF);
   }
@@ -591,8 +888,8 @@ rtems_termios_ioctl (void *arg)
         }
       }
     }
-    if (tty->device.setAttributes)
-      (*tty->device.setAttributes)(tty->minor, &tty->termios);
+    if (tty->handler.set_attributes)
+      (*tty->handler.set_attributes)(tty, &tty->termios);
     break;
 
   case RTEMS_IO_TCDRAIN:
@@ -676,8 +973,8 @@ rtems_termios_puts (
   rtems_interrupt_lock_context lock_context;
   rtems_status_code sc;
 
-  if (tty->device.outputUsesInterrupts == TERMIOS_POLLED) {
-    (*tty->device.write)(tty->minor, buf, len);
+  if (tty->handler.mode == TERMIOS_POLLED) {
+    (*tty->handler.write)(tty, buf, len);
     return;
   }
   newHead = tty->rawOutBuf.Head;
@@ -710,8 +1007,8 @@ rtems_termios_puts (
     if (tty->rawOutBufState == rob_idle) {
       /* check, whether XOFF has been received */
       if (!(tty->flow_ctrl & FL_ORCVXOF)) {
-        (*tty->device.write)(
-          tty->minor, &tty->rawOutBuf.theBuf[tty->rawOutBuf.Tail],1);
+        (*tty->handler.write)(
+          tty, &tty->rawOutBuf.theBuf[tty->rawOutBuf.Tail],1);
       } else {
         /* remember that output has been stopped due to flow ctrl*/
         tty->flow_ctrl |= FL_OSTOP;
@@ -997,7 +1294,7 @@ fillBufferPoll (struct rtems_termios_tty *tty)
 
   if (tty->termios.c_lflag & ICANON) {
     for (;;) {
-      n = (*tty->device.pollRead)(tty->minor);
+      n = (*tty->handler.poll_read)(tty);
       if (n < 0) {
         rtems_task_wake_after (1);
       } else {
@@ -1010,7 +1307,7 @@ fillBufferPoll (struct rtems_termios_tty *tty)
 
     then = rtems_clock_get_ticks_since_boot();
     for (;;) {
-      n = (*tty->device.pollRead)(tty->minor);
+      n = (*tty->handler.poll_read)(tty);
       if (n < 0) {
         if (tty->termios.c_cc[VMIN]) {
           if (tty->termios.c_cc[VTIME] && tty->ccount) {
@@ -1072,13 +1369,13 @@ fillBufferQueue (struct rtems_termios_tty *tty)
             && ((tty->rawOutBufState == rob_idle)
           || (tty->flow_ctrl & FL_OSTOP))) {
           /* XON should be sent now... */
-          (*tty->device.write)(
-            tty->minor, (void *)&(tty->termios.c_cc[VSTART]), 1);
+          (*tty->handler.write)(
+            tty, (void *)&(tty->termios.c_cc[VSTART]), 1);
         } else if (tty->flow_ctrl & FL_MDRTS) {
           tty->flow_ctrl &= ~FL_IRTSOFF;
           /* activate RTS line */
-          if (tty->device.startRemoteTx != NULL) {
-            tty->device.startRemoteTx(tty->minor);
+          if (tty->handler.start_remote_tx != NULL) {
+            tty->handler.start_remote_tx(tty);
           }
         }
       }
@@ -1131,8 +1428,7 @@ rtems_termios_read (void *arg)
   if (tty->cindex == tty->ccount) {
     tty->cindex = tty->ccount = 0;
     tty->read_start_column = tty->column;
-    if (tty->device.pollRead != NULL &&
-        tty->device.outputUsesInterrupts == TERMIOS_POLLED)
+    if (tty->handler.poll_read != NULL && tty->handler.mode == TERMIOS_POLLED)
       sc = fillBufferPoll (tty);
     else
       sc = fillBufferQueue (tty);
@@ -1230,8 +1526,8 @@ rtems_termios_enqueue_raw_characters (void *ttyp, const char *buf, int len)
         /* check for chars in output buffer (or rob_state?) */
         if (tty->rawOutBufState != rob_idle) {
           /* if chars available, call write function... */
-          (*tty->device.write)(
-            tty->minor, &tty->rawOutBuf.theBuf[tty->rawOutBuf.Tail], 1);
+          (*tty->handler.write)(
+            tty, &tty->rawOutBuf.theBuf[tty->rawOutBuf.Tail], 1);
         }
         /* reenable interrupts */
         rtems_termios_interrupt_lock_release (tty, &lock_context);
@@ -1252,14 +1548,14 @@ rtems_termios_enqueue_raw_characters (void *ttyp, const char *buf, int len)
             /* if tx is stopped due to XOFF or out of data */
             /*    call write function here                 */
             tty->flow_ctrl |= FL_ISNTXOF;
-            (*tty->device.write)(tty->minor,
+            (*tty->handler.write)(tty,
                 (void *)&(tty->termios.c_cc[VSTOP]), 1);
           }
         } else if ((tty->flow_ctrl & (FL_MDRTS | FL_IRTSOFF)) == (FL_MDRTS) ) {
           tty->flow_ctrl |= FL_IRTSOFF;
           /* deactivate RTS line */
-          if (tty->device.stopRemoteTx != NULL) {
-            tty->device.stopRemoteTx(tty->minor);
+          if (tty->handler.stop_remote_tx != NULL) {
+            tty->handler.stop_remote_tx(tty);
           }
         }
       }
@@ -1308,7 +1604,7 @@ rtems_termios_refill_transmitter (struct rtems_termios_tty *tty)
   if ((tty->flow_ctrl & (FL_MDXOF | FL_IREQXOF | FL_ISNTXOF))
       == (FL_MDXOF | FL_IREQXOF)) {
     /* XOFF should be sent now... */
-    (*tty->device.write)(tty->minor, (void *)&(tty->termios.c_cc[VSTOP]), 1);
+    (*tty->handler.write)(tty, (void *)&(tty->termios.c_cc[VSTOP]), 1);
 
     tty->t_dqlen--;
     tty->flow_ctrl |= FL_ISNTXOF;
@@ -1324,7 +1620,7 @@ rtems_termios_refill_transmitter (struct rtems_termios_tty *tty)
      * buffer, although the corresponding data is not yet out!
      * Therefore the dequeue "length" should be reduced by 1
      */
-    (*tty->device.write)(tty->minor, (void *)&(tty->termios.c_cc[VSTART]), 1);
+    (*tty->handler.write)(tty, (void *)&(tty->termios.c_cc[VSTART]), 1);
 
     tty->t_dqlen--;
     tty->flow_ctrl &= ~FL_ISNTXOF;
@@ -1341,7 +1637,7 @@ rtems_termios_refill_transmitter (struct rtems_termios_tty *tty)
       wakeUpWriterTask = true;
     }
 
-    (*tty->device.write) (tty->minor, NULL, 0);
+    (*tty->handler.write) (tty, NULL, 0);
     nToSend = 0;
   } else {
     len = tty->t_dqlen;
@@ -1361,7 +1657,7 @@ rtems_termios_refill_transmitter (struct rtems_termios_tty *tty)
        * Buffer has become empty
        */
       tty->rawOutBufState = rob_idle;
-      (*tty->device.write) (tty->minor, NULL, 0);
+      (*tty->handler.write) (tty, NULL, 0);
       nToSend = 0;
 
       /*
@@ -1378,7 +1674,7 @@ rtems_termios_refill_transmitter (struct rtems_termios_tty *tty)
       /* set flag, that output has been stopped */
       tty->flow_ctrl |= FL_OSTOP;
       tty->rawOutBufState = rob_busy; /*apm*/
-      (*tty->device.write) (tty->minor, NULL, 0);
+      (*tty->handler.write) (tty, NULL, 0);
       nToSend = 0;
     } else {
       /*
@@ -1395,8 +1691,8 @@ rtems_termios_refill_transmitter (struct rtems_termios_tty *tty)
         nToSend = 1;
       }
       tty->rawOutBufState = rob_busy; /*apm*/
-      (*tty->device.write)(
-        tty->minor, &tty->rawOutBuf.theBuf[newTail], nToSend);
+      (*tty->handler.write)(
+        tty, &tty->rawOutBuf.theBuf[newTail], nToSend);
     }
     tty->rawOutBuf.Tail = newTail; /*apm*/
   }
@@ -1430,7 +1726,7 @@ rtems_termios_dequeue_characters (void *ttyp, int len)
    */
   tty->t_dqlen += len;
 
-  if (tty->device.outputUsesInterrupts == TERMIOS_TASK_DRIVEN) {
+  if (tty->handler.mode == TERMIOS_TASK_DRIVEN) {
     /*
      * send wake up to transmitter task
      */
@@ -1518,10 +1814,10 @@ static rtems_task rtems_termios_rxdaemon(rtems_task_argument argument)
     /*
      * do something
      */
-    c = tty->device.pollRead(tty->minor);
+    c = tty->handler.poll_read(tty);
     if (c != EOF) {
       /*
-       * pollRead did call enqueue on its own
+       * poll_read did call enqueue on its own
        */
       c_buf = c;
       rtems_termios_enqueue_raw_characters ( tty,&c_buf,1);
diff --git a/testsuites/libtests/termios01/init.c b/testsuites/libtests/termios01/init.c
index 284bd49..8c3ed92 100644
--- a/testsuites/libtests/termios01/init.c
+++ b/testsuites/libtests/termios01/init.c
@@ -13,6 +13,8 @@
 
 #include "tmacros.h"
 #include <termios.h>
+#include <rtems/libcsupport.h>
+#include <rtems/malloc.h>
 #include <rtems/termiostypes.h>
 #include <fcntl.h>
 #include <limits.h>
@@ -515,6 +517,94 @@ static void test_termios_cfmakeraw(void)
   rtems_test_assert( term.c_cflag & CS8 );
 }
 
+static void test_early_device_install_remove(
+  rtems_device_major_number major,
+  rtems_device_minor_number minor,
+  void *arg
+)
+{
+  rtems_resource_snapshot snapshot;
+  rtems_status_code sc;
+
+  rtems_resource_snapshot_take( &snapshot );
+
+  sc = rtems_termios_device_install( "/", 0, 0, NULL, NULL );
+  rtems_test_assert( sc == RTEMS_INCORRECT_STATE );
+
+  sc = rtems_termios_device_remove( "/", 0, 0 );
+  rtems_test_assert( sc == RTEMS_INCORRECT_STATE );
+
+  rtems_test_assert( rtems_resource_snapshot_check( &snapshot ) );
+}
+
+static void test_device_install_remove(void)
+{
+  static const rtems_termios_device_handler handler;
+  static const rtems_device_major_number major = 123456789;
+  static const rtems_device_minor_number minor = 0xdeadbeef;
+  static const char dev[] = "/foobar";
+
+  rtems_resource_snapshot snapshot;
+  rtems_status_code sc;
+  void *greedy;
+  rtems_libio_t iop;
+  rtems_libio_open_close_args_t args;
+
+  memset( &iop, 0, sizeof( iop ) );
+  memset( &args, 0, sizeof( args ) );
+  args.iop = &iop;
+
+  rtems_resource_snapshot_take( &snapshot );
+
+  greedy = rtems_heap_greedy_allocate( NULL, 0 );
+
+  sc = rtems_termios_device_install( "/", major, minor, &handler, NULL );
+  rtems_test_assert( sc == RTEMS_NO_MEMORY );
+
+  rtems_heap_greedy_free( greedy );
+
+  rtems_test_assert( rtems_resource_snapshot_check( &snapshot ) );
+
+  sc = rtems_termios_device_install( NULL, major, minor, &handler, NULL );
+  rtems_test_assert( sc == RTEMS_SUCCESSFUL );
+
+  sc = rtems_termios_device_install( NULL, major, minor, &handler, NULL );
+  rtems_test_assert( sc == RTEMS_RESOURCE_IN_USE );
+
+  sc = rtems_termios_device_remove( NULL, major, minor );
+  rtems_test_assert( sc == RTEMS_SUCCESSFUL );
+
+  rtems_test_assert( rtems_resource_snapshot_check( &snapshot ) );
+
+  sc = rtems_termios_device_install( "/", major, minor, &handler, NULL );
+  rtems_test_assert( sc == RTEMS_UNSATISFIED );
+
+  rtems_test_assert( rtems_resource_snapshot_check( &snapshot ) );
+
+  sc = rtems_termios_device_remove( NULL, major, minor );
+  rtems_test_assert( sc == RTEMS_INVALID_ID );
+
+  sc = rtems_termios_device_install( &dev[0], major, minor, &handler, NULL );
+  rtems_test_assert( sc == RTEMS_SUCCESSFUL );
+
+  sc = rtems_termios_device_remove( "/barfoo", major, minor );
+  rtems_test_assert( sc == RTEMS_UNSATISFIED );
+
+  sc = rtems_termios_device_open( major, minor, &args );
+  rtems_test_assert( sc == RTEMS_SUCCESSFUL );
+
+  sc = rtems_termios_device_remove( &dev[0], major, minor );
+  rtems_test_assert( sc == RTEMS_RESOURCE_IN_USE );
+
+  sc = rtems_termios_device_close( &args );
+  rtems_test_assert( sc == RTEMS_SUCCESSFUL );
+
+  sc = rtems_termios_device_remove( &dev[0], major, minor );
+  rtems_test_assert( sc == RTEMS_SUCCESSFUL );
+
+  rtems_test_assert( rtems_resource_snapshot_check( &snapshot ) );
+}
+
 static rtems_task Init(
   rtems_task_argument ignored
 )
@@ -668,17 +758,22 @@ static rtems_task Init(
   }
   puts( "" );
 
+  test_device_install_remove();
+
   TEST_END();
   rtems_test_exit(0);
 }
 
 /* configuration information */
 
+#define CONFIGURE_APPLICATION_PREREQUISITE_DRIVERS \
+  { .initialization_entry = test_early_device_install_remove }
+
 #define CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER
 #define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
 
 /* include an extra slot for registering the termios one dynamically */
-#define CONFIGURE_MAXIMUM_DRIVERS 3
+#define CONFIGURE_MAXIMUM_DRIVERS 4
 
 /* one for the console and one for the test port */
 #define CONFIGURE_NUMBER_OF_TERMIOS_PORTS 3
diff --git a/testsuites/libtests/termios01/termios01.doc b/testsuites/libtests/termios01/termios01.doc
index 117844d..7d0589c 100644
--- a/testsuites/libtests/termios01/termios01.doc
+++ b/testsuites/libtests/termios01/termios01.doc
@@ -24,8 +24,13 @@ directives:
   cfsetspeed
   cfsetispeed
   cfsetospeed
+  rtems_termios_device_install
+  rtems_termios_device_remove
+  rtems_termios_device_open
+  rtems_termios_device_close
 
 concepts:
 
 + Exercise termios ioctl for all baud, character size, parity and
   bits per character options.
++ Ensure that Termios device install/remove works.
-- 
1.7.7



More information about the devel mailing list