diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 052dd6bc52a..0a08b0cfffb 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -165,17 +165,55 @@ config TTY_SIGINT should cause an immediate End-of-File result. config TTY_SIGINT_CHAR - int "Serial parse SIGINT characters" - default 3 if SERIAL_CONSOLE - default 4 if !SERIAL_CONSOLE + hex "Serial parse SIGINT characters" + default 0x03 if SERIAL_CONSOLE + default 0x04 if !SERIAL_CONSOLE depends on TTY_SIGINT ---help--- - Use ASCII 3 (Ctrl-c) or 4 (ctrl-d) inputs to determine whether to - send a SIGINT event. Other characters may also be selected. + Use ASCII 0x03 (Ctrl-c) or 0x04 (ctrl-d) inputs to determine whether + to send a SIGINT event. Other characters may also be selected. REVISIT: Traditionally Ctrl-C would generate SIGINT. Ctrl-D is the End-of-File character that should close the stream. +config TTY_SIGSTP + bool "Support SIGSTP" + default n + select CONFIG_SIG_SIGSTOP_ACTION + depends on !DISABLE_SIGNALS && SERIAL_TERMIOS + ---help--- + Whether support Ctrl-z event. Enabled automatically for console + devices. May be enabled for other serial devices using the ISIG bit + in the Termios c_lflag. + + REVISIT: This implementation is compliant but incomplete. The + c_lflag ISIG bit normally enables/disables INTR, QUIT, SUSP, and + DSUSP character processing. The relationship between these names, + standard signals, and typical key presses are as follows: + + INTR SIGINT Ctrl-C ETX(0x03) Interrupt + KILL SIGKILL Ctrl-U NAK(0x15) Kill + QUIT SIGQUIT Ctrl-\ FS (0x1c) Quit + SUSP SIGSTP Ctrl-Z SUB(0x1a) Suspend + DSUSP SIGSTP Ctrl-Y EM (0x19) Delayed suspend + + Additional requirements: + - SIGKILL cannot be caught or ignored. Compared to SIGTERM which + is like SIGKILL but can be caught or ignored. + - SIGQUIT is like SIGINT but causes generation of a core dump + - SIGSTOP cannot be caught or ignored. SIGSTP is like SIGSTOP but + can be caught or ignored ignored. + - The delayed suspend (DSUSD) is like suspend (SUPD), except that + the suspension is delayed until the next read operation + +config TTY_SIGSTP_CHAR + hex "Serial parse SIGSTP characters" + default 0x1a + depends on TTY_SIGSTP + ---help--- + Use ASCII 0x1a (Ctrl-z) input to determine whether to send a SIGSTP + event. Other characters may also be selected. + # # Serial console selection # diff --git a/drivers/serial/serial.c b/drivers/serial/serial.c index 4900143516e..c8214c593b2 100644 --- a/drivers/serial/serial.c +++ b/drivers/serial/serial.c @@ -1372,7 +1372,7 @@ static int uart_ioctl(FAR struct file *filep, int cmd, unsigned long arg) break; #endif -#ifdef CONFIG_TTY_SIGINT +#if defined(CONFIG_TTY_SIGINT) || defined(CONFIG_TTY_SIGSTP) /* Make the given terminal the controlling terminal of the calling process */ case TIOCSCTTY: @@ -1436,7 +1436,7 @@ static int uart_ioctl(FAR struct file *filep, int cmd, unsigned long arg) dev->tc_oflag = termiosp->c_oflag; dev->tc_lflag = termiosp->c_lflag; -#ifdef CONFIG_TTY_SIGINT +#if defined(CONFIG_TTY_SIGINT) || defined(CONFIG_TTY_SIGSTP) /* If the ISIG flag has been cleared in c_lflag, then un- * register the controlling terminal. */ @@ -1611,7 +1611,7 @@ errout: int uart_register(FAR const char *path, FAR uart_dev_t *dev) { -#ifdef CONFIG_TTY_SIGINT +#if defined(CONFIG_TTY_SIGINT) || defined(CONFIG_TTY_SIGSTP) /* Initialize of the task that will receive SIGINT signals. */ dev->pid = (pid_t)-1; diff --git a/drivers/serial/serial_dma.c b/drivers/serial/serial_dma.c index 82838b05fec..9559a4dcd4d 100644 --- a/drivers/serial/serial_dma.c +++ b/drivers/serial/serial_dma.c @@ -53,58 +53,81 @@ ************************************************************************************/ /************************************************************************************ - * Name: uart_check_sigint + * Name: uart_check_signo * * Description: - * Check if the SIGINT character is in the contiguous Rx DMA buffer region. + * Check if the SIGINT or SIGSTP character is in the contiguous Rx DMA buffer + * region. The first signal associated with the first such character is returned. + * + * If there multiple such characters in the buffer, only the signal associated + * with the first is returned (this a bug!) + * + * Returned Value: + * 0 if a signal-related character does not appear in the. Otherwise, SIGKILL or + * SIGSTP may be returned to indicate the appropriate signal action. * ************************************************************************************/ -#ifdef CONFIG_TTY_SIGINT -static bool uart_check_sigint(const char *buf, size_t size) +#if defined(CONFIG_TTY_SIGINT) || defined(CONFIG_TTY_SIGSTP) +static int uart_check_signo(const char *buf, size_t size) { size_t i; for (i = 0; i < size; i++) { +#ifdef CONFIG_TTY_SIGINT if (buf[i] == CONFIG_TTY_SIGINT_CHAR) { - return true; + return SIGINT; } +#endif + +#ifdef CONFIG_TTY_SIGSTP + if (buf[i] == CONFIG_TTY_SIGSTP_CHAR) + { + return SIGSTP; + } +#endif } - return false; + return 0; } #endif /************************************************************************************ - * Name: uart_recvchars_sigkill + * Name: uart_recvchars_signo * * Description: * Check if the SIGINT character is anywhere in the newly received DMA buffer. * - * REVISIT: We must also remove the SIGINT character from the Rx buffer. It + * REVISIT: We must also remove the SIGINT/SIGSTP character from the Rx buffer. It * should not be read as normal data by the caller. * ************************************************************************************/ -#ifdef CONFIG_TTY_SIGINT -static bool uart_recvchars_sigkill(FAR uart_dev_t *dev) +#if defined(CONFIG_TTY_SIGINT) || defined(CONFIG_TTY_SIGSTP) +static int uart_recvchars_signo(FAR uart_dev_t *dev) { FAR struct uart_dmaxfer_s *xfer = &dev->dmarx; + int signo; + + /* Check if the valid DMAed data is in one or two contiguous regions */ if (xfer->nbytes <= xfer->length) { - return uart_check_sigint(xfer->buffer, xfer->nbytes); + return uart_check_signo(xfer->buffer, xfer->nbytes); } else { - if (uart_check_sigint(xfer->buffer, xfer->length)) + /* REVISIT: Additional signals could be in the second region. */ + + signo = uart_check_signo(xfer->buffer, xfer->length); + if (signo != 0) { - return true; + return signo; } - return uart_check_sigint(xfer->nbuffer, xfer->nbytes - xfer->length); + return uart_check_signo(xfer->nbuffer, xfer->nbytes - xfer->length); } } #endif @@ -324,14 +347,14 @@ void uart_recvchars_done(FAR uart_dev_t *dev) FAR struct uart_dmaxfer_s *xfer = &dev->dmarx; FAR struct uart_buffer_s *rxbuf = &dev->recv; size_t nbytes = xfer->nbytes; -#ifdef CONFIG_TTY_SIGINT - bool needkill = false; +#if defined(CONFIG_TTY_SIGINT) || defined(CONFIG_TTY_SIGSTP) + int signo = 0; /* Check if the SIGINT character is anywhere in the newly received DMA buffer. */ - if (dev->pid >= 0 && uart_recvchars_sigkill(dev)) + if (dev->pid >= 0) { - needkill = true; + signo = uart_recvchars_signo(dev); } #endif @@ -350,12 +373,12 @@ void uart_recvchars_done(FAR uart_dev_t *dev) uart_datareceived(dev); } -#ifdef CONFIG_TTY_SIGINT - /* Send the SIGINT signal if needed */ +#if defined(CONFIG_TTY_SIGINT) || defined(CONFIG_TTY_SIGSTP) + /* Send the signal if necessary */ - if (needkill) + if (signo != 0) { - kill(dev->pid, SIGINT); + kill(dev->pid, signo); uart_reset_sem(dev); } #endif diff --git a/drivers/serial/serial_io.c b/drivers/serial/serial_io.c index 3a588970372..94a41e8b077 100644 --- a/drivers/serial/serial_io.c +++ b/drivers/serial/serial_io.c @@ -122,12 +122,12 @@ void uart_recvchars(FAR uart_dev_t *dev) FAR struct uart_buffer_s *rxbuf = &dev->recv; #ifdef CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS unsigned int watermark; -#endif -#ifdef CONFIG_TTY_SIGINT - bool needkill = false; #endif unsigned int status; int nexthead = rxbuf->head + 1; +#if defined(CONFIG_TTY_SIGINT) || defined(CONFIG_TTY_SIGSTP) + int signo = 0; +#endif uint16_t nbytes = 0; if (nexthead >= rxbuf->size) @@ -136,7 +136,7 @@ void uart_recvchars(FAR uart_dev_t *dev) } #ifdef CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS - /* Pre-calcuate the watermark level that we will need to test against. */ + /* Pre-calculate the watermark level that we will need to test against. */ watermark = (CONFIG_SERIAL_IFLOWCONTROL_UPPER_WATERMARK * rxbuf->size) / 100; #endif @@ -210,7 +210,27 @@ void uart_recvchars(FAR uart_dev_t *dev) * into the Rx buffer. It should not be read as normal data. */ - needkill = true; + signo = SIGINT; + } + else +#endif +#ifdef CONFIG_TTY_SIGSTP + /* Is this the special character that will generate the SIGSTP signal? */ + + if (dev->pid >= 0 && ch == CONFIG_TTY_SIGSTP_CHAR) + { +#ifdef CONFIG_TTY_SIGINT + /* Give precedence to SIGINT */ + + if (signo == 0) +#endif + { + /* Note that the kill is needed and do not put the character + * into the Rx buffer. It should not be read as normal data. + */ + + signo = SIGSTP; + } } else #endif @@ -249,12 +269,12 @@ void uart_recvchars(FAR uart_dev_t *dev) uart_datareceived(dev); } -#ifdef CONFIG_TTY_SIGINT - /* Send the SIGINT signal if needed */ +#if defined(CONFIG_TTY_SIGINT) || defined(CONFIG_TTY_SIGSTP) + /* Send the signal if necessary */ - if (needkill) + if (signo != 0) { - kill(dev->pid, SIGINT); + kill(dev->pid, signo); uart_reset_sem(dev); } #endif diff --git a/include/nuttx/serial/serial.h b/include/nuttx/serial/serial.h index 0d60fb54b5d..950d24cfc51 100644 --- a/include/nuttx/serial/serial.h +++ b/include/nuttx/serial/serial.h @@ -286,8 +286,8 @@ struct uart_dev_s tcflag_t tc_iflag; /* Input modes */ tcflag_t tc_oflag; /* Output modes */ tcflag_t tc_lflag; /* Local modes */ -#ifdef CONFIG_TTY_SIGINT - pid_t pid; /* Thread PID to receive SIGINT signals (-1 if none) */ +#if defined(CONFIG_TTY_SIGINT) || defined(CONFIG_TTY_SIGSTP) + pid_t pid; /* Thread PID to receive signals (-1 if none) */ #endif #endif diff --git a/sched/signal/sig_default.c b/sched/signal/sig_default.c index e8c9550ec85..b950c67c34c 100644 --- a/sched/signal/sig_default.c +++ b/sched/signal/sig_default.c @@ -39,6 +39,7 @@ #include +#include #include #include #include @@ -240,6 +241,12 @@ static void nxsig_abnormal_termination(int signo) static void nxsig_stop_task(int signo) { FAR struct tcb_s *rtcb = (FAR struct tcb_s *)this_task(); +#if defined(CONFIG_SCHED_WAITPID) && !defined(CONFIG_SCHED_HAVE_PARENT) + FAR struct task_group_s *group; + + DEBUGASSERT(rtcb != NULL && rtcb->group != NULL); + group = rtcb->group; +#endif /* Careful: In the multi-threaded task, the signal may be handled on a * child pthread. @@ -254,7 +261,7 @@ static void nxsig_stop_task(int signo) group_suspendchildren(rtcb); #endif - /* Lock the scheudler so this thread is not pre-empted until after we + /* Lock the scheduler so this thread is not pre-empted until after we * call sched_suspend(). */ @@ -266,7 +273,7 @@ static void nxsig_stop_task(int signo) * waitpid flags. */ - if (group->tg_waitflags & WUNTRACED) != 0) + if ((group->tg_waitflags & WUNTRACED) != 0) { /* Return zero for exit status (we are not exiting, however) */