diff options
Diffstat (limited to 'cpukit')
-rw-r--r-- | cpukit/Makefile.am | 1 | ||||
-rw-r--r-- | cpukit/include/rtems/libio.h | 116 | ||||
-rw-r--r-- | cpukit/include/rtems/rtems/status.h | 9 | ||||
-rw-r--r-- | cpukit/libcsupport/src/termios.c | 225 | ||||
-rw-r--r-- | cpukit/libcsupport/src/termios_posix_isig_handler.c | 39 | ||||
-rw-r--r-- | cpukit/rtems/src/statustext.c | 1 | ||||
-rw-r--r-- | cpukit/rtems/src/statustoerrno.c | 1 |
7 files changed, 346 insertions, 46 deletions
diff --git a/cpukit/Makefile.am b/cpukit/Makefile.am index 01ee40980f..7698fe5482 100644 --- a/cpukit/Makefile.am +++ b/cpukit/Makefile.am @@ -250,6 +250,7 @@ librtemscpu_a_SOURCES += libcsupport/src/termios_baudtable.c librtemscpu_a_SOURCES += libcsupport/src/termios.c librtemscpu_a_SOURCES += libcsupport/src/termiosinitialize.c librtemscpu_a_SOURCES += libcsupport/src/termios_num2baud.c +librtemscpu_a_SOURCES += libcsupport/src/termios_posix_isig_handler.c librtemscpu_a_SOURCES += libcsupport/src/termios_setbestbaud.c librtemscpu_a_SOURCES += libcsupport/src/termios_setinitialbaud.c librtemscpu_a_SOURCES += libcsupport/src/__times.c diff --git a/cpukit/include/rtems/libio.h b/cpukit/include/rtems/libio.h index f3b0f2cbf9..b5362320e8 100644 --- a/cpukit/include/rtems/libio.h +++ b/cpukit/include/rtems/libio.h @@ -1880,6 +1880,8 @@ extern const rtems_filesystem_mount_configuration */ /**@{**/ +struct rtems_termios_tty; + typedef struct rtems_termios_callbacks { int (*firstOpen)(int major, int minor, void *arg); int (*lastClose)(int major, int minor, void *arg); @@ -1907,6 +1909,114 @@ rtems_status_code rtems_termios_bufsize ( size_t raw_output /* raw output buffer size */ ); +/** + * @brief Type returned by all input processing (isig) methods + */ +typedef enum { + /** + * This indicates that the input character was processed + * and possibly placed into the buffer. + */ + RTEMS_TERMIOS_ISIG_WAS_PROCESSED, + /** + * This indicates that the input character was not processed and + * subsequent processing is required. + */ + RTEMS_TERMIOS_ISIG_WAS_NOT_PROCESSED, + /** + * This indicates that the character was processed and determined + * to be one that requires a signal to be raised (e.g. VINTR or + * VKILL). The tty must be in the right termios mode for this to + * occur. There is no further processing of this character required and + * the pending read() operation should be interrupted. + */ + RTEMS_TERMIOS_ISIG_INTERRUPT_READ +} rtems_termios_isig_status_code; + +/** + * @brief Type for ISIG (VINTR/VKILL) handler + * + * This type defines the interface to a method which will process + * VKILL and VINTR characters. The user can register a handler to + * override the default behavior which is to take no special action + * on these characters. The POSIX required behavior is to + * generate a SIGINTR or SIGQUIT signal. This behavior is implemented if + * the user registers the @ref rtems_termios_posix_isig_handler(). + * + * @param c character that was input + * @param tty termios structure pointer for this input tty + * + * @return The value returned is according to that documented + * for @ref rtems_termios_isig_status_code. + */ +typedef rtems_termios_isig_status_code (*rtems_termios_isig_handler)( + unsigned char c, + struct rtems_termios_tty *tty +); + +/** + * @brief Default handler for ISIG (VINTR/VKILL) + * + * This handler is installed by default by termios. It performs + * no actions on receiving the VINTR and VKILL characters. This is + * not POSIX conformant behavior but is the historical RTEMS behavior + * and avoid any default dependency on signal processing code. + * + * @param c character that was input + * @param tty termios structure pointer for this input tty + * + * The installed ISIG handler is only invoked if the character is + * (VKILL or VINTR) and ISIG is enabled. Thus the handler need only + * check the character and perform the appropriate action. + * + * @return The value returned is according to that documented + * for all methods adhering to @ref rtems_termios_isig_handler. + */ +rtems_termios_isig_status_code rtems_termios_default_isig_handler( + unsigned char c, + struct rtems_termios_tty *tty +); + +/** + * @brief POSIX handler for ISIG (VINTR/VKILL) + * + * This handler is installed by the used to obtain POSIX behavior + * upon receiving the VINTR and VKILL characters. The POSIX required + * behavior is to generate a SIGINTR or SIGQUIT signal if ISIG is enabled. + * + * @param c character that was input + * @param tty termios structure pointer for this input tty + * + * @return The value returned is according to that documented + * for all methods adhering to @ref rtems_termios_isig_handler. + */ +rtems_termios_isig_status_code rtems_termios_posix_isig_handler( + unsigned char c, + struct rtems_termios_tty *tty +); + +/** + * @brief Register handler for ISIG (VINTR/VKILL) + * + * This method is invoked to install an ISIG handler. This may be used + * to install @ref rtems_termios_posix_isig_handler() to obtain POSIX + * behavior or a custom handler. The handler + * @ref rtems_termios_default_isig_handler() is installed by default. + * + * @param handler entry point of the new ISIG handler + * + * @retval RTEMS_SUCCESSFUL if successful or error code if unsuccessful. + */ +rtems_status_code rtems_termios_register_isig_handler( + rtems_termios_isig_handler handler +); + +int rtems_termios_enqueue_raw_characters( + void *ttyp, + const char *buf, + int len +); + rtems_status_code rtems_termios_open ( rtems_device_major_number major, rtems_device_minor_number minor, @@ -1930,12 +2040,6 @@ rtems_status_code rtems_termios_ioctl( void *arg ); -int rtems_termios_enqueue_raw_characters( - void *ttyp, - const char *buf, - int len -); - int rtems_termios_dequeue_characters( void *ttyp, int len diff --git a/cpukit/include/rtems/rtems/status.h b/cpukit/include/rtems/rtems/status.h index 88ae576eac..7310fe905b 100644 --- a/cpukit/include/rtems/rtems/status.h +++ b/cpukit/include/rtems/rtems/status.h @@ -167,6 +167,12 @@ typedef enum { */ RTEMS_IO_ERROR = 27, /** + * This is the status used internally to indicate a blocking device + * driver call has been interrupted and should be reflected to the + * called as an INTERRUPTED. + */ + RTEMS_INTERRUPTED = 28, + /** * This is the status is used internally to RTEMS when performing * operations on behalf of remote tasks. This is referred to as * proxying operations and this status indicates that the operation @@ -174,7 +180,7 @@ typedef enum { * * @note This status will @b NOT be returned to the user. */ - RTEMS_PROXY_BLOCKING = 28 + RTEMS_PROXY_BLOCKING = 29 } rtems_status_code; /** @@ -235,6 +241,7 @@ RTEMS_INLINE_ROUTINE bool rtems_are_statuses_equal( * @retval ENODEV RTEMS_UNSATISFIED * @retval ENOSYS RTEMS_NOT_IMPLEMENTED, RTEMS_NOT_CONFIGURED * @retval ENOMEM RTEMS_NO_MEMORY + * @retval EINTR RTEMS_INTERRUPTED */ int rtems_status_code_to_errno(rtems_status_code sc); diff --git a/cpukit/libcsupport/src/termios.c b/cpukit/libcsupport/src/termios.c index 0fc9de452b..fedaa80ba0 100644 --- a/cpukit/libcsupport/src/termios.c +++ b/cpukit/libcsupport/src/termios.c @@ -1301,76 +1301,169 @@ iprocEarly (unsigned char c, rtems_termios_tty *tty) } /* + * This points to the currently registered method to perform + * ISIG processing of VKILL and VQUIT characters. + */ +static rtems_termios_isig_handler termios_isig_handler = + rtems_termios_default_isig_handler; + +/* + * This is the default method to process VKILL or VQUIT characters if + * ISIG processing is enabled. Note that it does nothing. + */ +rtems_termios_isig_status_code rtems_termios_default_isig_handler( + unsigned char c, + struct rtems_termios_tty *tty +) +{ + return RTEMS_TERMIOS_ISIG_WAS_NOT_PROCESSED; +} + +/* + * Register a method to process VKILL or VQUIT characters if + * ISIG processing is enabled. + */ +rtems_status_code rtems_termios_register_isig_handler( + rtems_termios_isig_handler handler +) +{ + if (handler == NULL) { + return RTEMS_INVALID_ADDRESS; + } + + termios_isig_handler = handler; + return RTEMS_SUCCESSFUL; +} + +/** + * @brief Type returned by all input processing (iproc) methods + */ +typedef enum { + /** + * This indicates that the input character was processed + * and the results were placed into the buffer. + */ + RTEMS_TERMIOS_IPROC_IN_BUFFER, + /** + * This indicates that the input character was processed + * and the result did not go into the buffer. + */ + RTEMS_TERMIOS_IPROC_WAS_PROCESSED, + /** + * This indicates that the input character was not processed and + * subsequent processing is required. + */ + RTEMS_TERMIOS_IPROC_WAS_NOT_PROCESSED, + /** + * This indicates that the character was processed and determined + * to be one that requires a signal to be raised (e.g. VINTR or + * VKILL). The tty must be in the right termios mode for this to + * occur. There is no further processing of this character required and + * the pending read() operation should be interrupted. + */ + RTEMS_TERMIOS_IPROC_INTERRUPT_READ +} rtems_termios_iproc_status_code; + +/* * Process a single input character */ -static int +static rtems_termios_iproc_status_code iproc (unsigned char c, struct rtems_termios_tty *tty) { + /* + * If signals are enabled, then allow possibility of VINTR causing + * SIGINTR and VQUIT causing SIGQUIT. Invoke user provided isig handler. + */ + if ((tty->termios.c_lflag & ISIG)) { + if ((c == tty->termios.c_cc[VINTR]) || (c == tty->termios.c_cc[VQUIT])) { + rtems_termios_isig_status_code rc; + rc = (*termios_isig_handler)(c, tty); + if (rc == RTEMS_TERMIOS_ISIG_INTERRUPT_READ) { + return RTEMS_TERMIOS_IPROC_INTERRUPT_READ; + } + if (rc == RTEMS_TERMIOS_ISIG_WAS_PROCESSED) { + return RTEMS_TERMIOS_IPROC_IN_BUFFER; + } + _Assert(rc == RTEMS_TERMIOS_ISIG_WAS_NOT_PROCESSED); + } + } + + /* + * Perform canonical character processing. + */ if ((c != '\0') && (tty->termios.c_lflag & ICANON)) { if (c == tty->termios.c_cc[VERASE]) { erase (tty, 0); - return 0; + return RTEMS_TERMIOS_IPROC_WAS_PROCESSED; } else if (c == tty->termios.c_cc[VKILL]) { erase (tty, 1); - return 0; + return RTEMS_TERMIOS_IPROC_WAS_PROCESSED; } else if (c == tty->termios.c_cc[VEOF]) { - return 1; + return RTEMS_TERMIOS_IPROC_IN_BUFFER; } else if (c == '\n') { if (tty->termios.c_lflag & (ECHO | ECHONL)) echo (c, tty); tty->cbuf[tty->ccount++] = c; - return 1; + return RTEMS_TERMIOS_IPROC_IN_BUFFER; } else if ((c == tty->termios.c_cc[VEOL]) || (c == tty->termios.c_cc[VEOL2])) { if (tty->termios.c_lflag & ECHO) echo (c, tty); tty->cbuf[tty->ccount++] = c; - return 1; + return RTEMS_TERMIOS_IPROC_IN_BUFFER; } } /* + * Perform non-canonical character processing. + * * FIXME: Should do IMAXBEL handling somehow */ if (tty->ccount < (CBUFSIZE-1)) { if (tty->termios.c_lflag & ECHO) echo (c, tty); tty->cbuf[tty->ccount++] = c; + return RTEMS_TERMIOS_IPROC_IN_BUFFER; } - return 0; + return RTEMS_TERMIOS_IPROC_WAS_NOT_PROCESSED; } /* * Process input character, with semaphore. */ -static int +static rtems_termios_iproc_status_code siproc (unsigned char c, struct rtems_termios_tty *tty) { - int i; + rtems_termios_iproc_status_code rc; /* * Obtain output semaphore if character will be echoed */ if (tty->termios.c_lflag & (ECHO|ECHOE|ECHOK|ECHONL|ECHOPRT|ECHOCTL|ECHOKE)) { rtems_mutex_lock (&tty->osem); - i = iproc (c, tty); + rc = iproc (c, tty); rtems_mutex_unlock (&tty->osem); } else { - i = iproc (c, tty); + rc = iproc (c, tty); } - return i; + return rc; } -static int +static rtems_termios_iproc_status_code siprocPoll (unsigned char c, rtems_termios_tty *tty) { if (c == '\r' && (tty->termios.c_iflag & IGNCR) != 0) { - return 0; + return RTEMS_TERMIOS_IPROC_WAS_NOT_PROCESSED; } + /* + * iprocEarly is done at the interrupt level for interrupt driven + * devices so we need to perform those actions before processing + * the character. + */ c = iprocEarly (c, tty); return siproc (c, tty); } @@ -1378,10 +1471,12 @@ siprocPoll (unsigned char c, rtems_termios_tty *tty) /* * Fill the input buffer by polling the device */ -static void +static rtems_termios_iproc_status_code fillBufferPoll (struct rtems_termios_tty *tty) { - int n; + int n; + rtems_termios_iproc_status_code rc; + rtems_termios_iproc_status_code retval = RTEMS_TERMIOS_IPROC_WAS_PROCESSED; if (tty->termios.c_lflag & ICANON) { for (;;) { @@ -1389,8 +1484,14 @@ fillBufferPoll (struct rtems_termios_tty *tty) if (n < 0) { rtems_task_wake_after (1); } else { - if (siprocPoll (n, tty)) + rc = siprocPoll (n, tty); + if (rc == RTEMS_TERMIOS_IPROC_IN_BUFFER) { + break; + } + if (rc == RTEMS_TERMIOS_IPROC_INTERRUPT_READ) { + retval = RTEMS_TERMIOS_IPROC_INTERRUPT_READ; break; + } } } } else { @@ -1417,7 +1518,11 @@ fillBufferPoll (struct rtems_termios_tty *tty) } rtems_task_wake_after (1); } else { - siprocPoll (n, tty); + rc = siprocPoll (n, tty); + if (rc == RTEMS_TERMIOS_IPROC_INTERRUPT_READ) { + retval = RTEMS_TERMIOS_IPROC_INTERRUPT_READ; + break; + } if (tty->ccount >= tty->termios.c_cc[VMIN]) break; if (tty->termios.c_cc[VMIN] && tty->termios.c_cc[VTIME]) @@ -1425,12 +1530,13 @@ fillBufferPoll (struct rtems_termios_tty *tty) } } } + return retval; } /* * Fill the input buffer from the raw input queue */ -static void +static rtems_termios_iproc_status_code fillBufferQueue (struct rtems_termios_tty *tty) { rtems_termios_device_context *ctx = tty->device_context; @@ -1448,8 +1554,9 @@ fillBufferQueue (struct rtems_termios_tty *tty) while ((tty->rawInBuf.Head != tty->rawInBuf.Tail) && (tty->ccount < (CBUFSIZE-1))) { - unsigned char c; - unsigned int newHead; + unsigned char c; + unsigned int newHead; + rtems_termios_iproc_status_code rc; newHead = (tty->rawInBuf.Head + 1) % tty->rawInBuf.Size; c = tty->rawInBuf.theBuf[newHead]; @@ -1479,12 +1586,21 @@ fillBufferQueue (struct rtems_termios_tty *tty) /* continue processing new character */ if (tty->termios.c_lflag & ICANON) { - if (siproc (c, tty)) { - /* In canonical mode, input is made available line by line */ - return; + rc = siproc (c, tty); + /* If the read() should be interrupted, return */ + if (rc == RTEMS_TERMIOS_IPROC_INTERRUPT_READ) { + return RTEMS_TERMIOS_IPROC_INTERRUPT_READ; + } + /* In canonical mode, input is made available line by line */ + if (rc == RTEMS_TERMIOS_IPROC_IN_BUFFER) { + return RTEMS_TERMIOS_IPROC_IN_BUFFER; } } else { - siproc (c, tty); + rc = siproc (c, tty); + /* If the read() should be interrupted, return */ + if (rc == RTEMS_TERMIOS_IPROC_INTERRUPT_READ) { + return RTEMS_TERMIOS_IPROC_INTERRUPT_READ; + } if (tty->ccount >= tty->termios.c_cc[VMIN]) wait = false; } @@ -1519,13 +1635,19 @@ fillBufferQueue (struct rtems_termios_tty *tty) } } } + return RTEMS_TERMIOS_IPROC_WAS_PROCESSED; } -static uint32_t -rtems_termios_read_tty (struct rtems_termios_tty *tty, char *buffer, - uint32_t initial_count) +static rtems_status_code +rtems_termios_read_tty ( + struct rtems_termios_tty *tty, + char *buffer, + uint32_t initial_count, + uint32_t *count_read +) { - uint32_t count; + uint32_t count; + rtems_termios_iproc_status_code rc = RTEMS_TERMIOS_IPROC_WAS_PROCESSED; count = initial_count; @@ -1533,28 +1655,43 @@ rtems_termios_read_tty (struct rtems_termios_tty *tty, char *buffer, tty->cindex = tty->ccount = 0; tty->read_start_column = tty->column; if (tty->handler.poll_read != NULL && tty->handler.mode == TERMIOS_POLLED) - fillBufferPoll (tty); + rc = fillBufferPoll (tty); else - fillBufferQueue (tty); + rc = fillBufferQueue (tty); } + + /* + * If there are characters in the buffer, then copy them to the caller. + */ while (count && (tty->cindex < tty->ccount)) { *buffer++ = tty->cbuf[tty->cindex++]; count--; - } + } tty->tty_rcvwakeup = false; - return initial_count - count; + *count_read = initial_count - count; + + /* + * fillBufferPoll and fillBufferQueue can indicate that the operation + * was interrupted. The isig handler can do nothing, have POSIX behavior + * and cause a signal, or a user defined action. + */ + if (rc == RTEMS_TERMIOS_IPROC_INTERRUPT_READ) { + return RTEMS_INTERRUPTED; + } + + return RTEMS_SUCCESSFUL; } rtems_status_code rtems_termios_read (void *arg) { - rtems_libio_rw_args_t *args = arg; + rtems_libio_rw_args_t *args = arg; struct rtems_termios_tty *tty = args->iop->data1; + rtems_status_code sc; rtems_mutex_lock (&tty->isem); if (rtems_termios_linesw[tty->t_line].l_read != NULL) { - rtems_status_code sc; sc = rtems_termios_linesw[tty->t_line].l_read(tty,args); tty->tty_rcvwakeup = false; @@ -1562,9 +1699,14 @@ rtems_termios_read (void *arg) return sc; } - args->bytes_moved = rtems_termios_read_tty (tty, args->buffer, args->count); + sc = rtems_termios_read_tty( + tty, + args->buffer, + args->count, + &args->bytes_moved + ); rtems_mutex_unlock (&tty->isem); - return RTEMS_SUCCESSFUL; + return sc; } /* @@ -2022,7 +2164,8 @@ static ssize_t rtems_termios_imfs_read (rtems_libio_t *iop, void *buffer, size_t count) { struct rtems_termios_tty *tty; - uint32_t bytes_moved; + uint32_t bytes_moved; + rtems_status_code sc; tty = iop->data1; @@ -2049,9 +2192,13 @@ rtems_termios_imfs_read (rtems_libio_t *iop, void *buffer, size_t count) return (ssize_t) args.bytes_moved; } - bytes_moved = rtems_termios_read_tty (tty, buffer, count); + sc = rtems_termios_read_tty(tty, buffer, count, &bytes_moved); rtems_mutex_unlock (&tty->isem); + if (sc != RTEMS_SUCCESSFUL) { + return rtems_status_code_to_errno (sc); + } return (ssize_t) bytes_moved; + } static ssize_t diff --git a/cpukit/libcsupport/src/termios_posix_isig_handler.c b/cpukit/libcsupport/src/termios_posix_isig_handler.c new file mode 100644 index 0000000000..105e7f463d --- /dev/null +++ b/cpukit/libcsupport/src/termios_posix_isig_handler.c @@ -0,0 +1,39 @@ +/** + * @file + * TERMIOS POSIX behavior on INTR and QUIT characters + */ + +/* + * COPYRIGHT (c) 1989-2012,2019. + * On-Line Applications Research Corporation (OAR). + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> +#include <rtems/libio.h> +#include <rtems/termiostypes.h> + +#include <signal.h> + +rtems_termios_isig_status_code rtems_termios_posix_isig_handler( + unsigned char c, + struct rtems_termios_tty *tty +) +{ + if (c == tty->termios.c_cc[VINTR]) { + raise(SIGINT); + return RTEMS_TERMIOS_ISIG_INTERRUPT_READ; + } + + if (c == tty->termios.c_cc[VQUIT]) { + raise(SIGQUIT); + return RTEMS_TERMIOS_ISIG_INTERRUPT_READ; + } + + return RTEMS_TERMIOS_ISIG_WAS_NOT_PROCESSED; +} diff --git a/cpukit/rtems/src/statustext.c b/cpukit/rtems/src/statustext.c index f701ebd356..23697942fd 100644 --- a/cpukit/rtems/src/statustext.c +++ b/cpukit/rtems/src/statustext.c @@ -53,6 +53,7 @@ static const char *const status_code_text[] = { "RTEMS_INTERNAL_ERROR", "RTEMS_NO_MEMORY", "RTEMS_IO_ERROR", + "RTEMS_INTERRUPTED", "RTEMS_PROXY_BLOCKING" }; diff --git a/cpukit/rtems/src/statustoerrno.c b/cpukit/rtems/src/statustoerrno.c index 8f34ff5921..76a9e2c056 100644 --- a/cpukit/rtems/src/statustoerrno.c +++ b/cpukit/rtems/src/statustoerrno.c @@ -45,6 +45,7 @@ static const int status_code_to_errno [RTEMS_STATUS_CODES_LAST + 1] = { [RTEMS_INTERNAL_ERROR] = EIO, [RTEMS_NO_MEMORY] = ENOMEM, [RTEMS_IO_ERROR] = EIO, + [RTEMS_INTERRUPTED] = EINTR, [RTEMS_PROXY_BLOCKING] = EIO }; |