summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSebastian Huber <sebastian.huber@embedded-brains.de>2014-06-27 13:10:15 +0200
committerSebastian Huber <sebastian.huber@embedded-brains.de>2014-07-09 11:55:00 +0200
commitef8c00bc8873cb4b37ded1bf3e10a346fe2720ed (patch)
tree1305467e597ff9f95b6d32f9e6114ad3d2c2941a
parentscore: _Scheduler_Thread_get_own_node() (diff)
downloadrtems-ef8c00bc8873cb4b37ded1bf3e10a346fe2720ed.tar.bz2
termios: PR2153: New low-level device API
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.
-rw-r--r--cpukit/libcsupport/include/rtems/termiostypes.h240
-rw-r--r--cpukit/libcsupport/src/termios.c503
-rw-r--r--testsuites/libtests/termios01/init.c97
-rw-r--r--testsuites/libtests/termios01/termios01.doc5
4 files changed, 731 insertions, 114 deletions
diff --git a/cpukit/libcsupport/include/rtems/termiostypes.h b/cpukit/libcsupport/include/rtems/termiostypes.h
index dcc8f69c15..80912b662e 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,138 @@ 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. This is parameter provided to
+ * support legacy drivers. It must not be used by new drivers.
+ *
+ * @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. This is parameter provided to
+ * support legacy drivers. It must not be used by new drivers.
+ *
+ * @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 +247,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 +276,101 @@ 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. It must be persistent throughout the
+ * installed time of the device.
+ * @param[in] context The device context. It must be persistent throughout the
+ * installed time of the device.
+ *
+ * @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 +384,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 1159bcaaae..afc466e888 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,39 @@ 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);
+
+ if (tty->device_node != NULL)
+ tty->device_node->tty = NULL;
+
+ 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 +725,31 @@ 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);
+
+ rtems_termios_close_tty (tty, arg);
+
+ rtems_semaphore_release (rtems_termios_ttyMutex);
+
return RTEMS_SUCCESSFUL;
}
@@ -490,8 +787,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 +808,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 +889,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 +974,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 +1008,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 +1295,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 +1308,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 +1370,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 +1429,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 +1527,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 +1549,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 +1605,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 +1621,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 +1638,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 +1658,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 +1675,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 +1692,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 +1727,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 +1815,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 284bd49dd2..8c3ed92a9c 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 117844dcf8..7d0589c343 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.