From ef8c00bc8873cb4b37ded1bf3e10a346fe2720ed Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Fri, 27 Jun 2014 13:10:15 +0200 Subject: 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. --- cpukit/libcsupport/include/rtems/termiostypes.h | 240 ++++++++++- cpukit/libcsupport/src/termios.c | 503 +++++++++++++++++++----- testsuites/libtests/termios01/init.c | 97 ++++- testsuites/libtests/termios01/termios01.doc | 5 + 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 #include #include +#include #include #include @@ -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); @@ -155,14 +383,6 @@ struct rtems_termios_linesw { int (*l_modem)(struct rtems_termios_tty *tp,int flags); }; -/* - * 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! */ 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 +#include +#include #include #include #include @@ -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. -- cgit v1.2.3