This is an automated email from the ASF dual-hosted git repository.

acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit 81b320b972415095739fd689e49a01626e21240e
Author: Marco Casaroli <[email protected]>
AuthorDate: Tue Oct 27 09:24:45 2026 +0100

    drivers/serial, drivers/net/telnet: Give the special characters in c_cc.
    
    TCGETS of a serial port, a pty and a telnet session set only c_cc[VMIN]
    and c_cc[VTIME] (serial) or nothing: the other entries of c_cc kept what
    the caller had in its buffer.  A program that reads the special
    characters (readline, stty and getty do) got random values.
    
    Now TCGETS gives every entry of c_cc: the INTR and SUSP characters
    (CONFIG_TTY_SIGINT_CHAR and CONFIG_TTY_SIGTSTP_CHAR at the start), ERASE
    (DEL) on a serial port, MIN and TIME, and _POSIX_VDISABLE for the
    characters that the terminal does not act on.  TCSETS sets the INTR and
    SUSP characters, and _POSIX_VDISABLE disables them.  A pty pair keeps one
    set for both ends.
    
    <unistd.h> now defines _POSIX_VDISABLE as '\0' (as Linux), which
    POSIX.1-2008 requires, and fpathconf(_PC_VDISABLE) returns it.  This
    also lets <sys/ttydefaults.h> with TTYDEFCHARS compile.
    
    Assisted-by: Claude Code:claude-opus-5-5
    Signed-off-by: Marco Casaroli <[email protected]>
---
 Documentation/standards/posix.rst |  2 +-
 drivers/net/telnet.c              | 40 ++++++++++++++++++++++++++++++++++++---
 drivers/serial/Kconfig            |  6 +++++-
 drivers/serial/pty.c              | 37 ++++++++++++++++++++++++++++++++++--
 drivers/serial/serial.c           | 37 ++++++++++++++++++++++++++++++++++--
 include/nuttx/serial/serial.h     |  6 ++++++
 include/unistd.h                  |  7 ++++++-
 libs/libc/unistd/lib_pathconf.c   |  3 +++
 8 files changed, 128 insertions(+), 10 deletions(-)

diff --git a/Documentation/standards/posix.rst 
b/Documentation/standards/posix.rst
index 929a1e72e3c..d005b18a510 100644
--- a/Documentation/standards/posix.rst
+++ b/Documentation/standards/posix.rst
@@ -251,7 +251,7 @@ POSIX.1 Option Requirements
 +-----------------------------+---------+---------+
 | `_POSIX_SAVED_IDS`_         | No      |         |
 +-----------------------------+---------+---------+
-| `_POSIX_VDISABLE`_          | No      |         |
+| `_POSIX_VDISABLE`_          | Yes     |         |
 +-----------------------------+---------+---------+
 | `_POSIX2_CHAR_TERM`_        | ?       |         |
 +-----------------------------+---------+---------+
diff --git a/drivers/net/telnet.c b/drivers/net/telnet.c
index d227117f69d..b7dd8226aa6 100644
--- a/drivers/net/telnet.c
+++ b/drivers/net/telnet.c
@@ -32,9 +32,10 @@
 #include <fcntl.h>
 #include <poll.h>
 #include <errno.h>
-#include <nuttx/debug.h>
 #include <termios.h>
+#include <unistd.h>
 
+#include <nuttx/debug.h>
 #include <nuttx/kmalloc.h>
 #include <nuttx/kthread.h>
 #include <nuttx/lib/lib.h>
@@ -132,6 +133,12 @@ struct telnet_dev_s
 #endif
 #ifdef HAVE_SIGNALS
   pid_t             td_pid;
+#endif
+#ifdef CONFIG_TTY_SIGINT
+  cc_t              td_vintr;     /* c_cc[VINTR] */
+#endif
+#ifdef CONFIG_TTY_SIGTSTP
+  cc_t              td_vsusp;     /* c_cc[VSUSP] */
 #endif
   tcflag_t          td_lflag;     /* Local modes */
   FAR struct socket td_psock;     /* A clone of the internal socket structure 
*/
@@ -268,7 +275,8 @@ static void telnet_check_ctrlchar(FAR struct telnet_dev_s 
*priv,
        * signal?
        */
 
-      if (*buffer == CONFIG_TTY_SIGINT_CHAR)
+      if (priv->td_vintr != _POSIX_VDISABLE &&
+          (cc_t)*buffer == priv->td_vintr)
         {
           /* Yes.. note that the kill is needed and do not put the character
            * into the Rx buffer.  It should not be read as normal data.
@@ -284,7 +292,8 @@ static void telnet_check_ctrlchar(FAR struct telnet_dev_s 
*priv,
        * signal?
        */
 
-      if (*buffer == CONFIG_TTY_SIGTSTP_CHAR)
+      if (priv->td_vsusp != _POSIX_VDISABLE &&
+          (cc_t)*buffer == priv->td_vsusp)
         {
           /* Note that the kill is needed and do not put the character
            * into the Rx buffer.  It should not be read as normal data.
@@ -1108,6 +1117,12 @@ static int telnet_session(FAR struct telnet_session_s 
*session)
   /* Setting terminal attributes */
 
   priv->td_lflag = ECHO;
+#ifdef CONFIG_TTY_SIGINT
+  priv->td_vintr = CONFIG_TTY_SIGINT_CHAR;
+#endif
+#ifdef CONFIG_TTY_SIGTSTP
+  priv->td_vsusp = CONFIG_TTY_SIGTSTP_CHAR;
+#endif
 
   /* Register the driver */
 
@@ -1221,6 +1236,19 @@ static int telnet_ioctl(FAR struct file *filep, int cmd, 
unsigned long arg)
           cfmakeraw(termiosp);
 
           termiosp->c_lflag = priv->td_lflag;
+
+          /* The special characters that the session acts on.  The others
+           * are disabled.
+           */
+
+          memset(termiosp->c_cc, _POSIX_VDISABLE, sizeof(termiosp->c_cc));
+#ifdef CONFIG_TTY_SIGINT
+          termiosp->c_cc[VINTR] = priv->td_vintr;
+#endif
+#ifdef CONFIG_TTY_SIGTSTP
+          termiosp->c_cc[VSUSP] = priv->td_vsusp;
+#endif
+          termiosp->c_cc[VMIN] = 1;
         }
         break;
 
@@ -1232,6 +1260,12 @@ static int telnet_ioctl(FAR struct file *filep, int cmd, 
unsigned long arg)
           /* Save the termios settings */
 
           priv->td_lflag = termiosp->c_lflag;
+#ifdef CONFIG_TTY_SIGINT
+          priv->td_vintr = termiosp->c_cc[VINTR];
+#endif
+#ifdef CONFIG_TTY_SIGTSTP
+          priv->td_vsusp = termiosp->c_cc[VSUSP];
+#endif
 
           if ((priv->td_lflag & ECHO) != 0)
             {
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index 3b368991099..5df44cf3e8f 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -451,6 +451,8 @@ config TTY_SIGINT_CHAR
        ---help---
                Use ASCII 0x03 (Ctrl-c) or 0x04 (ctrl-d) inputs to determine 
whether
                to send a SIGINT event.  Other characters may also be selected.
+               This is the INTR character (c_cc[VINTR]) of a terminal when it
+               starts; tcsetattr() can change or disable it.
 
                REVISIT:  Traditionally Ctrl-C would generate SIGINT.  Ctrl-D 
is the
                End-of-File character that should close the stream.
@@ -489,7 +491,9 @@ config TTY_SIGTSTP_CHAR
        depends on TTY_SIGTSTP
        ---help---
                Use ASCII 0x1a (Ctrl-z) input to determine whether to send a 
SIGTSTP
-               event.  Other characters may also be selected.
+               event.  Other characters may also be selected.  This is the SUSP
+               character (c_cc[VSUSP]) of a terminal when it starts; 
tcsetattr()
+               can change or disable it.
 
 #
 # Serial console selection
diff --git a/drivers/serial/pty.c b/drivers/serial/pty.c
index e005afbfbd6..1a0f096ea0f 100644
--- a/drivers/serial/pty.c
+++ b/drivers/serial/pty.c
@@ -114,6 +114,12 @@ struct pty_devpair_s
   struct pty_dev_s pp_master;   /* Maseter device */
   struct pty_dev_s pp_slave;    /* Slave device */
 
+#ifdef CONFIG_TTY_SIGINT
+  cc_t pp_vintr;                /* c_cc[VINTR] */
+#endif
+#ifdef CONFIG_TTY_SIGTSTP
+  cc_t pp_vsusp;                /* c_cc[VSUSP] */
+#endif
   bool pp_susv1;                /* SUSv1 or BSD style */
   bool pp_locked;               /* Slave is locked */
   bool pp_unlinked;             /* File has been unlinked */
@@ -667,7 +673,8 @@ static ssize_t pty_write(FAR struct file *filep,
             }
 
 #ifdef CONFIG_TTY_SIGINT
-          if (pid > 0 && ch == CONFIG_TTY_SIGINT_CHAR)
+          if (pid > 0 && dev->pd_devpair->pp_vintr != _POSIX_VDISABLE &&
+              (cc_t)ch == dev->pd_devpair->pp_vintr)
             {
               nxsig_kill(pid, SIGINT);
               return 1;
@@ -675,7 +682,8 @@ static ssize_t pty_write(FAR struct file *filep,
 #endif
 
 #ifdef CONFIG_TTY_SIGTSTP
-          if (pid > 0 && ch == CONFIG_TTY_SIGTSTP_CHAR)
+          if (pid > 0 && dev->pd_devpair->pp_vsusp != _POSIX_VDISABLE &&
+              (cc_t)ch == dev->pd_devpair->pp_vsusp)
             {
               nxsig_kill(pid, SIGTSTP);
               return 1;
@@ -836,6 +844,19 @@ static int pty_ioctl(FAR struct file *filep, int cmd, 
unsigned long arg)
           termiosp->c_iflag = dev->pd_iflag;
           termiosp->c_oflag = dev->pd_oflag;
           termiosp->c_lflag = dev->pd_lflag;
+
+          /* The special characters that the pair acts on (one set for
+           * both ends).  The others are disabled.
+           */
+
+          memset(termiosp->c_cc, _POSIX_VDISABLE, sizeof(termiosp->c_cc));
+#ifdef CONFIG_TTY_SIGINT
+          termiosp->c_cc[VINTR] = dev->pd_devpair->pp_vintr;
+#endif
+#ifdef CONFIG_TTY_SIGTSTP
+          termiosp->c_cc[VSUSP] = dev->pd_devpair->pp_vsusp;
+#endif
+          termiosp->c_cc[VMIN] = 1;
           ret = OK;
         }
         break;
@@ -855,6 +876,12 @@ static int pty_ioctl(FAR struct file *filep, int cmd, 
unsigned long arg)
           dev->pd_iflag = termiosp->c_iflag;
           dev->pd_oflag = termiosp->c_oflag;
           dev->pd_lflag = termiosp->c_lflag;
+#ifdef CONFIG_TTY_SIGINT
+          dev->pd_devpair->pp_vintr = termiosp->c_cc[VINTR];
+#endif
+#ifdef CONFIG_TTY_SIGTSTP
+          dev->pd_devpair->pp_vsusp = termiosp->c_cc[VSUSP];
+#endif
           ret = OK;
         }
         break;
@@ -1183,6 +1210,12 @@ int pty_register2(int minor, bool susv1)
   devpair->pp_master.pd_pid = INVALID_PROCESS_ID;
   devpair->pp_slave.pd_pid = INVALID_PROCESS_ID;
 #endif
+#ifdef CONFIG_TTY_SIGINT
+  devpair->pp_vintr = CONFIG_TTY_SIGINT_CHAR;
+#endif
+#ifdef CONFIG_TTY_SIGTSTP
+  devpair->pp_vsusp = CONFIG_TTY_SIGTSTP_CHAR;
+#endif
 
   /* Register the master device
    *
diff --git a/drivers/serial/serial.c b/drivers/serial/serial.c
index c151bf194b1..46943e5382a 100644
--- a/drivers/serial/serial.c
+++ b/drivers/serial/serial.c
@@ -2061,9 +2061,26 @@ static int uart_ioctl(FAR struct file *filep, int cmd, 
unsigned long arg)
               termiosp->c_iflag = dev->tc_iflag;
               termiosp->c_oflag = dev->tc_oflag;
               termiosp->c_lflag = dev->tc_lflag;
+
+              /* The special characters that this layer acts on.  The
+               * others are disabled.  ERASE cannot be changed: ICANON
+               * erases with DEL and with BS.
+               */
+
+              memset(termiosp->c_cc, _POSIX_VDISABLE,
+                     sizeof(termiosp->c_cc));
+              termiosp->c_cc[VERASE] = ASCII_DEL;
+#ifdef CONFIG_TTY_SIGINT
+              termiosp->c_cc[VINTR] = dev->tc_vintr;
+#endif
+#ifdef CONFIG_TTY_SIGTSTP
+              termiosp->c_cc[VSUSP] = dev->tc_vsusp;
+#endif
 #ifdef CONFIG_SERIAL_TERMIOS
               termiosp->c_cc[VTIME] = dev->timeout;
               termiosp->c_cc[VMIN] = dev->minread;
+#else
+              termiosp->c_cc[VMIN] = 1;
 #endif
 
               ret = 0;
@@ -2083,6 +2100,12 @@ static int uart_ioctl(FAR struct file *filep, int cmd, 
unsigned long arg)
               dev->tc_iflag = termiosp->c_iflag;
               dev->tc_oflag = termiosp->c_oflag;
               dev->tc_lflag = termiosp->c_lflag;
+#ifdef CONFIG_TTY_SIGINT
+              dev->tc_vintr = termiosp->c_cc[VINTR];
+#endif
+#ifdef CONFIG_TTY_SIGTSTP
+              dev->tc_vsusp = termiosp->c_cc[VSUSP];
+#endif
 #ifdef CONFIG_SERIAL_TERMIOS
               dev->timeout = termiosp->c_cc[VTIME];
               dev->minread = termiosp->c_cc[VMIN];
@@ -2366,6 +2389,14 @@ int uart_register(FAR const char *path, FAR uart_dev_t 
*dev)
   dev->pid = INVALID_PROCESS_ID;
 #endif
 
+#ifdef CONFIG_TTY_SIGINT
+  dev->tc_vintr = CONFIG_TTY_SIGINT_CHAR;
+#endif
+
+#ifdef CONFIG_TTY_SIGTSTP
+  dev->tc_vsusp = CONFIG_TTY_SIGTSTP_CHAR;
+#endif
+
 #ifdef CONFIG_TTY_FORCE_PANIC
   dev->panic_count = 0;
 #endif
@@ -2606,14 +2637,16 @@ int uart_check_special(FAR uart_dev_t *dev, FAR const 
char *buf, size_t size)
 #endif
 
 #ifdef CONFIG_TTY_SIGINT
-      if (dev->pid > 0 && buf[i] == CONFIG_TTY_SIGINT_CHAR)
+      if (dev->pid > 0 && dev->tc_vintr != _POSIX_VDISABLE &&
+          (cc_t)buf[i] == dev->tc_vintr)
         {
           return SIGINT;
         }
 #endif
 
 #ifdef CONFIG_TTY_SIGTSTP
-      if (dev->pid > 0 && buf[i] == CONFIG_TTY_SIGTSTP_CHAR)
+      if (dev->pid > 0 && dev->tc_vsusp != _POSIX_VDISABLE &&
+          (cc_t)buf[i] == dev->tc_vsusp)
         {
           return SIGTSTP;
         }
diff --git a/include/nuttx/serial/serial.h b/include/nuttx/serial/serial.h
index e4238d40a21..66532289862 100644
--- a/include/nuttx/serial/serial.h
+++ b/include/nuttx/serial/serial.h
@@ -354,6 +354,12 @@ 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
+  cc_t                 tc_vintr;     /* c_cc[VINTR] */
+#endif
+#ifdef CONFIG_TTY_SIGTSTP
+  cc_t                 tc_vsusp;     /* c_cc[VSUSP] */
+#endif
 
   /* Semaphores & mutex */
 
diff --git a/include/unistd.h b/include/unistd.h
index da114528e14..c7eafe7ed17 100644
--- a/include/unistd.h
+++ b/include/unistd.h
@@ -97,7 +97,12 @@
 
 #undef  _POSIX_CHOWN_RESTRICTED
 #undef  _POSIX_NO_TRUNC
-#undef  _POSIX_VDISABLE
+
+/* The value of a c_cc entry of struct termios that disables the special
+ * character.
+ */
+
+#define _POSIX_VDISABLE '\0'
 
 #define _POSIX_SYNC_IO 1
 #undef  _POSIX_ASYNC_IO
diff --git a/libs/libc/unistd/lib_pathconf.c b/libs/libc/unistd/lib_pathconf.c
index 394d158f616..70078f1969c 100644
--- a/libs/libc/unistd/lib_pathconf.c
+++ b/libs/libc/unistd/lib_pathconf.c
@@ -139,6 +139,9 @@ long fpathconf(int fildes, int name)
       case _PC_PRIO_IO:
         return _POSIX_PRIO_IO;
 
+      case _PC_VDISABLE:
+        return _POSIX_VDISABLE;
+
       default:
 
         /* Assume valid but not implemented for the time being */

Reply via email to