This is an automated email from the ASF dual-hosted git repository. xiaoxiang781216 pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit d04f56f660ec7efeebdcf4be93d4869d7bb34aea Author: Marco Casaroli <[email protected]> AuthorDate: Sat Oct 31 23:07:41 2026 +0100 drivers/serial: Discard the INTR and SUSP characters that send a signal. When ISIG is set and the terminal knows its foreground process, the INTR (Ctrl-C) and SUSP (Ctrl-Z) characters send SIGINT and SIGTSTP. The driver also put the character into the receive buffer, so the next read() gave it to the program: after Ctrl-C a shell got "^C" in front of the next command line. POSIX says that the terminal discards such a character when it processes it. Now uart_check_special() removes every special character that it acts on: INTR and SUSP when they send a signal, and the LAUNCH and FORCE_PANIC characters. Its size argument is now in and out: it returns the number of characters that are left. All receive paths use it: receive(), recvbuf() and the DMA path, where the characters that are left in the second region move to follow the first region. A character that sends no signal (no foreground process, or ISIG not set) stays as before. uart_check_special() now handles every character of the batch, not only the ones before the first special character, and returns the signal of the first INTR or SUSP character. uart_recvchars() keeps the first signal of a batch: the check of each later character set it back to 0, so a Ctrl-C with more characters in the same interrupt sent no signal. Assisted-by: Claude Code:claude-opus-5-5 Signed-off-by: Marco Casaroli <[email protected]> --- drivers/serial/serial.c | 60 ++++++++++++++++++++++++++----------------- drivers/serial/serial_dma.c | 59 +++++++++++++++++++++++++++++------------- drivers/serial/serial_io.c | 29 ++++++++++++++++++--- include/nuttx/serial/serial.h | 18 +++++-------- 4 files changed, 110 insertions(+), 56 deletions(-) diff --git a/drivers/serial/serial.c b/drivers/serial/serial.c index 46943e5382a..775c25938d5 100644 --- a/drivers/serial/serial.c +++ b/drivers/serial/serial.c @@ -2585,24 +2585,23 @@ void uart_reset_sem(FAR uart_dev_t *dev) * Name: uart_check_special * * Description: - * Check if the SIGINT or SIGTSTP 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!) + * Act on the special characters in the *size characters at buf and + * remove them: the INTR and SUSP characters that send a signal (POSIX: + * the terminal discards them), the launch character and the panic + * character. *size returns the number of characters that are left. * * Returned Value: - * 0 if a signal-related character does not appear in the. Otherwise, - * SIGKILL or SIGTSTP may be returned to indicate the appropriate signal - * action. + * SIGINT or SIGTSTP for the first INTR or SUSP character, else 0. * ****************************************************************************/ #if defined(CONFIG_TTY_SIGINT) || defined(CONFIG_TTY_SIGTSTP) || \ defined(CONFIG_TTY_FORCE_PANIC) || defined(CONFIG_TTY_LAUNCH) -int uart_check_special(FAR uart_dev_t *dev, FAR const char *buf, size_t size) +int uart_check_special(FAR uart_dev_t *dev, FAR char *buf, + FAR size_t *size) { + size_t kept = 0; + int signo = 0; size_t i; if ((dev->tc_lflag & ISIG) == 0) @@ -2610,49 +2609,62 @@ int uart_check_special(FAR uart_dev_t *dev, FAR const char *buf, size_t size) return 0; } - for (i = 0; i < size; i++) + for (i = 0; i < *size; i++) { + char ch = buf[i]; + #ifdef CONFIG_TTY_FORCE_PANIC - if (buf[i] == CONFIG_TTY_FORCE_PANIC_CHAR) + if (ch == CONFIG_TTY_FORCE_PANIC_CHAR) { if (++dev->panic_count >= CONFIG_TTY_FORCE_PANIC_REPEAT_COUNT) { PANIC_WITH_REGS("Force panic by user.", NULL); } - return 0; - } - else - { - dev->panic_count = 0; + continue; } + + dev->panic_count = 0; #endif #ifdef CONFIG_TTY_LAUNCH - if (buf[i] == CONFIG_TTY_LAUNCH_CHAR) + if (ch == CONFIG_TTY_LAUNCH_CHAR) { uart_launch(); - return 0; + continue; } #endif #ifdef CONFIG_TTY_SIGINT if (dev->pid > 0 && dev->tc_vintr != _POSIX_VDISABLE && - (cc_t)buf[i] == dev->tc_vintr) + (cc_t)ch == dev->tc_vintr) { - return SIGINT; + if (signo == 0) + { + signo = SIGINT; + } + + continue; } #endif #ifdef CONFIG_TTY_SIGTSTP if (dev->pid > 0 && dev->tc_vsusp != _POSIX_VDISABLE && - (cc_t)buf[i] == dev->tc_vsusp) + (cc_t)ch == dev->tc_vsusp) { - return SIGTSTP; + if (signo == 0) + { + signo = SIGTSTP; + } + + continue; } #endif + + buf[kept++] = ch; } - return 0; + *size = kept; + return signo; } #endif diff --git a/drivers/serial/serial_dma.c b/drivers/serial/serial_dma.c index 836717f78f3..4ccad37ad9d 100644 --- a/drivers/serial/serial_dma.c +++ b/drivers/serial/serial_dma.c @@ -29,6 +29,7 @@ #include <assert.h> #include <sys/types.h> #include <stdint.h> +#include <string.h> #include <nuttx/debug.h> #include <nuttx/signal.h> @@ -45,11 +46,10 @@ * Name: uart_recvchars_check_special * * Description: - * Check if the SIGINT character is anywhere in the newly received DMA - * buffer. - * - * REVISIT: We must also remove the SIGINT/SIGTSTP character from the Rx - * buffer. It should not be read as normal data by the caller. + * Act on the special characters in the newly received DMA buffer, remove + * them and update xfer->nbytes. The data can be in two regions: the + * characters that are left in the second region move to follow the ones + * that are left in the first. * ****************************************************************************/ @@ -59,27 +59,49 @@ static int uart_recvchars_check_special(FAR uart_dev_t *dev) { FAR struct uart_dmaxfer_s *xfer = &dev->dmarx; + size_t nfirst; + size_t nsecond; + size_t n; int signo; + int sig; /* Check if the valid DMAed data is in one or two contiguous regions */ if (xfer->nbytes <= xfer->length) { - return uart_check_special(dev, xfer->buffer, xfer->nbytes); + nfirst = xfer->nbytes; + signo = uart_check_special(dev, xfer->buffer, &nfirst); + xfer->nbytes = nfirst; + return signo; } - else + + nfirst = xfer->length; + nsecond = xfer->nbytes - xfer->length; + signo = uart_check_special(dev, xfer->buffer, &nfirst); + sig = uart_check_special(dev, xfer->nbuffer, &nsecond); + if (signo == 0) { - /* REVISIT: Additional signals could be in the second region. */ + signo = sig; + } - signo = uart_check_special(dev, xfer->buffer, xfer->length); - if (signo != 0) - { - return signo; - } + /* Close the gap at the end of the first region with the start of the + * second region, then move the rest of the second region to its start. + */ - return uart_check_special(dev, xfer->nbuffer, - xfer->nbytes - xfer->length); + n = xfer->length - nfirst; + if (n > nsecond) + { + n = nsecond; } + + if (n > 0) + { + memcpy(&xfer->buffer[nfirst], xfer->nbuffer, n); + memmove(xfer->nbuffer, &xfer->nbuffer[n], nsecond - n); + } + + xfer->nbytes = nfirst + nsecond; + return signo; } #endif @@ -317,13 +339,13 @@ 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; + size_t nbytes; #if defined(CONFIG_TTY_SIGINT) || defined(CONFIG_TTY_SIGTSTP) || \ defined(CONFIG_TTY_FORCE_PANIC) || defined(CONFIG_TTY_LAUNCH) int signo = 0; - /* Check if the SIGINT character is anywhere in the newly received DMA - * buffer. + /* Act on the special characters in the newly received DMA buffer and + * remove them. */ signo = uart_recvchars_check_special(dev); @@ -331,6 +353,7 @@ void uart_recvchars_done(FAR uart_dev_t *dev) /* Move head for nbytes. */ + nbytes = xfer->nbytes; rxbuf->head = (rxbuf->head + nbytes) % rxbuf->size; xfer->nbytes = 0; xfer->length = xfer->nlength = 0; diff --git a/drivers/serial/serial_io.c b/drivers/serial/serial_io.c index 3361c11f3f4..057d1299157 100644 --- a/drivers/serial/serial_io.c +++ b/drivers/serial/serial_io.c @@ -164,7 +164,7 @@ void uart_recvchars(FAR uart_dev_t *dev) defined(CONFIG_TTY_FORCE_PANIC) || defined(CONFIG_TTY_LAUNCH) int signo = 0; #endif - uint16_t nbytes = 0; + size_t nbytes = 0; /* Loop putting characters into the receive buffer until there are no * further characters to available. @@ -183,6 +183,10 @@ void uart_recvchars(FAR uart_dev_t *dev) sbuf_size_t tail = rxbuf->tail; bool is_full = (nexthead == tail); FAR char *pbuf = NULL; +#if defined(CONFIG_TTY_SIGINT) || defined(CONFIG_TTY_SIGTSTP) || \ + defined(CONFIG_TTY_FORCE_PANIC) || defined(CONFIG_TTY_LAUNCH) + int sig = 0; +#endif char ch; #ifdef CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS @@ -260,6 +264,10 @@ void uart_recvchars(FAR uart_dev_t *dev) } nbytes = ret; +#if defined(CONFIG_TTY_SIGINT) || defined(CONFIG_TTY_SIGTSTP) || \ + defined(CONFIG_TTY_FORCE_PANIC) || defined(CONFIG_TTY_LAUNCH) + sig = uart_check_special(dev, pbuf, &nbytes); +#endif rxbuf->head += nbytes; if (rxbuf->head >= rxbuf->size) { @@ -276,6 +284,11 @@ void uart_recvchars(FAR uart_dev_t *dev) { continue; } + +#if defined(CONFIG_TTY_SIGINT) || defined(CONFIG_TTY_SIGTSTP) || \ + defined(CONFIG_TTY_FORCE_PANIC) || defined(CONFIG_TTY_LAUNCH) + sig = uart_check_special(dev, pbuf, &nbytes); +#endif } } else if(dev->ops->receive) @@ -286,6 +299,11 @@ void uart_recvchars(FAR uart_dev_t *dev) pbuf = &ch; nbytes = 1; +#if defined(CONFIG_TTY_SIGINT) || defined(CONFIG_TTY_SIGTSTP) || \ + defined(CONFIG_TTY_FORCE_PANIC) || defined(CONFIG_TTY_LAUNCH) + sig = uart_check_special(dev, pbuf, &nbytes); +#endif + /* If the RX buffer becomes full, then the serial data is * discarded. This is necessary because on most serial hardware, * you must read the data in order to clear the RX interrupt. @@ -295,7 +313,7 @@ void uart_recvchars(FAR uart_dev_t *dev) * (unless it has some large internal buffering). */ - if (!is_full) + if (!is_full && nbytes > 0) { /* Add the character to the buffer */ @@ -309,7 +327,12 @@ void uart_recvchars(FAR uart_dev_t *dev) #if defined(CONFIG_TTY_SIGINT) || defined(CONFIG_TTY_SIGTSTP) || \ defined(CONFIG_TTY_FORCE_PANIC) || defined(CONFIG_TTY_LAUNCH) - signo = uart_check_special(dev, pbuf, nbytes); + /* Keep the first signal: a later character must not clear it */ + + if (signo == 0) + { + signo = sig; + } #endif } diff --git a/include/nuttx/serial/serial.h b/include/nuttx/serial/serial.h index 66532289862..65877653176 100644 --- a/include/nuttx/serial/serial.h +++ b/include/nuttx/serial/serial.h @@ -571,24 +571,20 @@ void uart_reset_sem(FAR uart_dev_t *dev); * Name: uart_check_special * * Description: - * Check if the SIGINT or SIGTSTP 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!) + * Act on the special characters in the *size characters at buf and + * remove them: the INTR and SUSP characters that send a signal (POSIX: + * the terminal discards them), the launch character and the panic + * character. *size returns the number of characters that are left. * * Returned Value: - * 0 if a signal-related character does not appear in the. Otherwise, - * SIGKILL or SIGTSTP may be returned to indicate the appropriate signal - * action. + * SIGINT or SIGTSTP for the first INTR or SUSP character, else 0. * ****************************************************************************/ #if defined(CONFIG_TTY_SIGINT) || defined(CONFIG_TTY_SIGTSTP) || \ defined(CONFIG_TTY_FORCE_PANIC) || defined(CONFIG_TTY_LAUNCH) -int uart_check_special(FAR uart_dev_t *dev, FAR const char *buf, - size_t size); +int uart_check_special(FAR uart_dev_t *dev, FAR char *buf, + FAR size_t *size); #endif /****************************************************************************
