drivers/serial: Add support for Ctrl-Z. This works just like the recently added Ctrl-C support except that SIGSTP is sent when the Ctrl-Z characters is encountered vs. SIGINT.

This commit is contained in:
Gregory Nutt
2018-09-02 15:36:25 -06:00
parent 093348030e
commit 993321dda6
6 changed files with 131 additions and 43 deletions
+43 -5
View File
@@ -165,17 +165,55 @@ config TTY_SIGINT
should cause an immediate End-of-File result. should cause an immediate End-of-File result.
config TTY_SIGINT_CHAR config TTY_SIGINT_CHAR
int "Serial parse SIGINT characters" hex "Serial parse SIGINT characters"
default 3 if SERIAL_CONSOLE default 0x03 if SERIAL_CONSOLE
default 4 if !SERIAL_CONSOLE default 0x04 if !SERIAL_CONSOLE
depends on TTY_SIGINT depends on TTY_SIGINT
---help--- ---help---
Use ASCII 3 (Ctrl-c) or 4 (ctrl-d) inputs to determine whether to Use ASCII 0x03 (Ctrl-c) or 0x04 (ctrl-d) inputs to determine whether
send a SIGINT event. Other characters may also be selected. to send a SIGINT event. Other characters may also be selected.
REVISIT: Traditionally Ctrl-C would generate SIGINT. Ctrl-D is the REVISIT: Traditionally Ctrl-C would generate SIGINT. Ctrl-D is the
End-of-File character that should close the stream. 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 # Serial console selection
# #
+3 -3
View File
@@ -1372,7 +1372,7 @@ static int uart_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
break; break;
#endif #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 */ /* Make the given terminal the controlling terminal of the calling process */
case TIOCSCTTY: 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_oflag = termiosp->c_oflag;
dev->tc_lflag = termiosp->c_lflag; 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- /* If the ISIG flag has been cleared in c_lflag, then un-
* register the controlling terminal. * register the controlling terminal.
*/ */
@@ -1611,7 +1611,7 @@ errout:
int uart_register(FAR const char *path, FAR uart_dev_t *dev) 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. */ /* Initialize of the task that will receive SIGINT signals. */
dev->pid = (pid_t)-1; dev->pid = (pid_t)-1;
+45 -22
View File
@@ -53,58 +53,81 @@
************************************************************************************/ ************************************************************************************/
/************************************************************************************ /************************************************************************************
* Name: uart_check_sigint * Name: uart_check_signo
* *
* Description: * 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 #if defined(CONFIG_TTY_SIGINT) || defined(CONFIG_TTY_SIGSTP)
static bool uart_check_sigint(const char *buf, size_t size) static int uart_check_signo(const char *buf, size_t size)
{ {
size_t i; size_t i;
for (i = 0; i < size; i++) for (i = 0; i < size; i++)
{ {
#ifdef CONFIG_TTY_SIGINT
if (buf[i] == CONFIG_TTY_SIGINT_CHAR) 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 #endif
/************************************************************************************ /************************************************************************************
* Name: uart_recvchars_sigkill * Name: uart_recvchars_signo
* *
* Description: * Description:
* Check if the SIGINT character is anywhere in the newly received DMA buffer. * 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. * should not be read as normal data by the caller.
* *
************************************************************************************/ ************************************************************************************/
#ifdef CONFIG_TTY_SIGINT #if defined(CONFIG_TTY_SIGINT) || defined(CONFIG_TTY_SIGSTP)
static bool uart_recvchars_sigkill(FAR uart_dev_t *dev) static int uart_recvchars_signo(FAR uart_dev_t *dev)
{ {
FAR struct uart_dmaxfer_s *xfer = &dev->dmarx; 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) if (xfer->nbytes <= xfer->length)
{ {
return uart_check_sigint(xfer->buffer, xfer->nbytes); return uart_check_signo(xfer->buffer, xfer->nbytes);
} }
else 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 #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_dmaxfer_s *xfer = &dev->dmarx;
FAR struct uart_buffer_s *rxbuf = &dev->recv; FAR struct uart_buffer_s *rxbuf = &dev->recv;
size_t nbytes = xfer->nbytes; size_t nbytes = xfer->nbytes;
#ifdef CONFIG_TTY_SIGINT #if defined(CONFIG_TTY_SIGINT) || defined(CONFIG_TTY_SIGSTP)
bool needkill = false; int signo = 0;
/* Check if the SIGINT character is anywhere in the newly received DMA buffer. */ /* 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 #endif
@@ -350,12 +373,12 @@ void uart_recvchars_done(FAR uart_dev_t *dev)
uart_datareceived(dev); uart_datareceived(dev);
} }
#ifdef CONFIG_TTY_SIGINT #if defined(CONFIG_TTY_SIGINT) || defined(CONFIG_TTY_SIGSTP)
/* Send the SIGINT signal if needed */ /* Send the signal if necessary */
if (needkill) if (signo != 0)
{ {
kill(dev->pid, SIGINT); kill(dev->pid, signo);
uart_reset_sem(dev); uart_reset_sem(dev);
} }
#endif #endif
+29 -9
View File
@@ -122,12 +122,12 @@ void uart_recvchars(FAR uart_dev_t *dev)
FAR struct uart_buffer_s *rxbuf = &dev->recv; FAR struct uart_buffer_s *rxbuf = &dev->recv;
#ifdef CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS #ifdef CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS
unsigned int watermark; unsigned int watermark;
#endif
#ifdef CONFIG_TTY_SIGINT
bool needkill = false;
#endif #endif
unsigned int status; unsigned int status;
int nexthead = rxbuf->head + 1; int nexthead = rxbuf->head + 1;
#if defined(CONFIG_TTY_SIGINT) || defined(CONFIG_TTY_SIGSTP)
int signo = 0;
#endif
uint16_t nbytes = 0; uint16_t nbytes = 0;
if (nexthead >= rxbuf->size) if (nexthead >= rxbuf->size)
@@ -136,7 +136,7 @@ void uart_recvchars(FAR uart_dev_t *dev)
} }
#ifdef CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS #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; watermark = (CONFIG_SERIAL_IFLOWCONTROL_UPPER_WATERMARK * rxbuf->size) / 100;
#endif #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. * 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 else
#endif #endif
@@ -249,12 +269,12 @@ void uart_recvchars(FAR uart_dev_t *dev)
uart_datareceived(dev); uart_datareceived(dev);
} }
#ifdef CONFIG_TTY_SIGINT #if defined(CONFIG_TTY_SIGINT) || defined(CONFIG_TTY_SIGSTP)
/* Send the SIGINT signal if needed */ /* Send the signal if necessary */
if (needkill) if (signo != 0)
{ {
kill(dev->pid, SIGINT); kill(dev->pid, signo);
uart_reset_sem(dev); uart_reset_sem(dev);
} }
#endif #endif
+2 -2
View File
@@ -286,8 +286,8 @@ struct uart_dev_s
tcflag_t tc_iflag; /* Input modes */ tcflag_t tc_iflag; /* Input modes */
tcflag_t tc_oflag; /* Output modes */ tcflag_t tc_oflag; /* Output modes */
tcflag_t tc_lflag; /* Local modes */ tcflag_t tc_lflag; /* Local modes */
#ifdef CONFIG_TTY_SIGINT #if defined(CONFIG_TTY_SIGINT) || defined(CONFIG_TTY_SIGSTP)
pid_t pid; /* Thread PID to receive SIGINT signals (-1 if none) */ pid_t pid; /* Thread PID to receive signals (-1 if none) */
#endif #endif
#endif #endif
+9 -2
View File
@@ -39,6 +39,7 @@
#include <nuttx/config.h> #include <nuttx/config.h>
#include <sys/wait.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@@ -240,6 +241,12 @@ static void nxsig_abnormal_termination(int signo)
static void nxsig_stop_task(int signo) static void nxsig_stop_task(int signo)
{ {
FAR struct tcb_s *rtcb = (FAR struct tcb_s *)this_task(); 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 /* Careful: In the multi-threaded task, the signal may be handled on a
* child pthread. * child pthread.
@@ -254,7 +261,7 @@ static void nxsig_stop_task(int signo)
group_suspendchildren(rtcb); group_suspendchildren(rtcb);
#endif #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(). * call sched_suspend().
*/ */
@@ -266,7 +273,7 @@ static void nxsig_stop_task(int signo)
* waitpid flags. * waitpid flags.
*/ */
if (group->tg_waitflags & WUNTRACED) != 0) if ((group->tg_waitflags & WUNTRACED) != 0)
{ {
/* Return zero for exit status (we are not exiting, however) */ /* Return zero for exit status (we are not exiting, however) */