linux/termios: test the kernel-side termios canonicalization
authorH. Peter Anvin <hpa@zytor.com>
Tue, 18 Nov 2025 18:21:18 +0000 (10:21 -0800)
committerH.J. Lu <hjl.tools@gmail.com>
Tue, 18 Nov 2025 23:54:51 +0000 (07:54 +0800)
Verify that the kernel side of the termios interface gets the various
speed fields set according to our current canonicalization policy.

[ v2.1: fix formatting - Adhemerval Netto ]
[ v4: fix typo in patch description - Dan HorĂ¡k ]

Signed-off-by: H. Peter Anvin (Intel) <hpa@zytor.com>
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org> (v2.1)
Reviewed-by: H.J. Lu <hjl.tools@gmail.com>
sysdeps/unix/sysv/linux/tst-termios-linux.c

index e4b0c8bcd6ed8ee8604fe86d30adc5c728305a9d..80ba9e88536398aeff3f9399d1522fbd34b52d11 100644 (file)
@@ -38,6 +38,8 @@
 #include <support/test-driver.h>
 #include <support/tty.h>
 
+#include <k_termios.h>        /* Definitions for the raw ioctl interface */
+
 /* Evaluate an expression and make sure errno did not get set; return
    the value of the expression */
 #define CHECKERR(expr)                         \
@@ -226,13 +228,66 @@ static void check_speeds_cf (const struct termios *tio_p,
               CHECKERR (cfgetibaud (tio_p)), 'i');
 }
 
-/* Use this after tc[gs]etattr () */
+/* Access the raw kernel interface and verify that the result is
+   canonicalized properly; this should be run after tcsetattr (). */
+static void
+check_speeds_kernel (int fd, speed_t ospeed, speed_t ispeed)
+{
+  struct termios2 k_termios;
+  tcflag_t expect_cbaud = speed_to_cbaud (ospeed);
+  tcflag_t expect_cibaud;
+
+  if (!ispeed)
+    ispeed = ospeed;
+
+  /* If ospeed == ispeed, tcsetattr() should set the kernel CIBAUD to 0,
+     for compatibility with programs that use the direct ioctl interface
+     but fail to account for CIBAUD. c_ispeed should still be correct. */
+  if (ospeed == ispeed)
+    expect_cibaud = 0;
+  else
+    expect_cibaud = speed_to_cbaud (ispeed);
+
+  memset (&k_termios, 0xed, sizeof k_termios); /* Fill with nonsense */
+  CHECKZERO (ioctl(fd, TCGETS2, &k_termios));
+
+  tcflag_t k_cbaud  = k_termios.c_cflag & CBAUD;
+  tcflag_t k_cibaud = (k_termios.c_cflag >> IBSHIFT) & CBAUD;
+
+  if (k_termios.c_ospeed != ospeed)
+    FAIL ("opeed %u ispeed %u: kernel c_ospeed = %u, expected %u",
+         ospeed, ispeed,
+         k_termios.c_ospeed, ospeed);
+
+  if (k_cbaud != expect_cbaud)
+    FAIL ("ospeed %u ispeed %u: kernel CBAUD = %s (%06o), expected %s (%06o)",
+         ospeed, ispeed,
+         cbaud_name (k_cbaud), k_cbaud,
+         cbaud_name (expect_cbaud), expect_cbaud);
+
+  if (k_termios.c_ispeed != ispeed)
+    FAIL ("ospeed %u ispeed %u: kernel c_ispeed == %u, expected %u",
+         ospeed, ispeed,
+         k_termios.c_ispeed, ispeed);
+
+  if (k_cibaud != expect_cibaud)
+    FAIL ("ospeed %u ispeed %u: kernel CIBAUD = %s (%06o), expected %s (%06o)",
+         ospeed, ispeed,
+         cbaud_name (k_cibaud), k_cibaud,
+         cbaud_name (expect_cibaud), expect_cibaud);
+}
+
+/* Use this after tcsetattr () */
 static void check_speeds_tc (int fd, speed_t ospeed, speed_t ispeed)
 {
   struct termios tio;
 
+  if (!ispeed)
+    ispeed = ospeed;
+
   CHECKZERO (tcgetattr (fd, &tio));
-  check_speeds_cf (&tio, ospeed, ispeed ? ispeed : ospeed);
+  check_speeds_cf (&tio, ospeed, ispeed);
+  check_speeds_kernel (fd, ospeed, ispeed);
 }
 
 /* For search and replace convenience */
@@ -250,7 +305,7 @@ set_speeds (int fd, speed_t ospeed, speed_t ispeed)
   CHECKZERO (cfsetispeed (&tio, ispeed));
   check_speeds_cf (&tio, ospeed, ispeed);
   CHECKZERO (tcsetattr (fd, TCSANOW, &tio));
-  check_speeds_tc (fd, ospeed, ispeed ? ispeed : ospeed);
+  check_speeds_tc (fd, ospeed, ispeed);
 }
 
 /* Actual tests */
This page took 0.085565 seconds and 5 git commands to generate.