[libc-commits] [libc] [libc][termios] Fix termios compatibility with glibc 2.42 or after. (PR #211039)

via libc-commits libc-commits at lists.llvm.org
Thu Jul 23 14:46:45 PDT 2026


https://github.com/lntue updated https://github.com/llvm/llvm-project/pull/211039

>From f75918f7a9251d7a8dd3d307f0c2a2ec7141bf40 Mon Sep 17 00:00:00 2001
From: Tue Ly <lntue.h at gmail.com>
Date: Tue, 21 Jul 2026 16:21:46 +0000
Subject: [PATCH 1/6] [libc][termios] Fix termios compatibility with glibc 2.42
 or after.

---
 libc/hdr/CMakeLists.txt                 |   9 ++
 libc/hdr/termios_macros.h               |  27 ++++
 libc/hdr/types/CMakeLists.txt           |  33 +++++
 libc/hdr/types/cc_t.h                   |  27 ++++
 libc/hdr/types/speed_t.h                |  27 ++++
 libc/hdr/types/struct_termios.h         |  27 ++++
 libc/hdr/types/tcflag_t.h               |  27 ++++
 libc/src/termios/linux/CMakeLists.txt   |  32 ++++-
 libc/src/termios/linux/cfgetispeed.cpp  |  12 +-
 libc/src/termios/linux/cfgetospeed.cpp  |  12 +-
 libc/src/termios/linux/cfsetispeed.cpp  |  15 +-
 libc/src/termios/linux/cfsetospeed.cpp  |  15 +-
 libc/src/termios/linux/kernel_termios.h |  10 +-
 libc/src/termios/linux/speed_utils.h    | 184 ++++++++++++++++++++++++
 libc/src/termios/linux/tcdrain.cpp      |   9 +-
 libc/src/termios/linux/tcflow.cpp       |   9 +-
 libc/src/termios/linux/tcflush.cpp      |   9 +-
 libc/src/termios/linux/tcgetattr.cpp    |  14 +-
 libc/src/termios/linux/tcgetsid.cpp     |   9 +-
 libc/src/termios/linux/tcsendbreak.cpp  |   9 +-
 libc/src/termios/linux/tcsetattr.cpp    |  10 +-
 libc/test/src/termios/CMakeLists.txt    |   2 +
 libc/test/src/termios/termios_test.cpp  |  11 +-
 23 files changed, 503 insertions(+), 36 deletions(-)
 create mode 100644 libc/hdr/termios_macros.h
 create mode 100644 libc/hdr/types/cc_t.h
 create mode 100644 libc/hdr/types/speed_t.h
 create mode 100644 libc/hdr/types/struct_termios.h
 create mode 100644 libc/hdr/types/tcflag_t.h
 create mode 100644 libc/src/termios/linux/speed_utils.h

diff --git a/libc/hdr/CMakeLists.txt b/libc/hdr/CMakeLists.txt
index 9df87558046fa..b511fd673eb40 100644
--- a/libc/hdr/CMakeLists.txt
+++ b/libc/hdr/CMakeLists.txt
@@ -234,6 +234,15 @@ add_proxy_header_library(
     libc.include.llvm-libc-macros.sys_wait_macros
 )
 
+add_proxy_header_library(
+  termios_macros
+  HDRS
+    termios_macros.h
+  FULL_BUILD_DEPENDS
+    libc.include.llvm-libc-macros.termios_macros
+)
+
+
 add_header_library(unistd_overlay HDRS unistd_overlay.h)
 add_proxy_header_library(
   unistd_macros
diff --git a/libc/hdr/termios_macros.h b/libc/hdr/termios_macros.h
new file mode 100644
index 0000000000000..9ccf917a293e3
--- /dev/null
+++ b/libc/hdr/termios_macros.h
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Proxy header for macros from termios.h.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_HDR_TERMIOS_MACROS_H
+#define LLVM_LIBC_HDR_TERMIOS_MACROS_H
+
+#ifdef LIBC_FULL_BUILD
+
+#include "include/llvm-libc-macros/termios-macros.h"
+
+#else // Overlay mode
+
+#include <termios.h>
+
+#endif // LIBC_FULL_BUILD
+
+#endif // LLVM_LIBC_HDR_TERMIOS_MACROS_H
diff --git a/libc/hdr/types/CMakeLists.txt b/libc/hdr/types/CMakeLists.txt
index 0f2590aed2deb..0e957813e8383 100644
--- a/libc/hdr/types/CMakeLists.txt
+++ b/libc/hdr/types/CMakeLists.txt
@@ -1218,3 +1218,36 @@ add_proxy_header_library(
   FULL_BUILD_DEPENDS
     libc.include.llvm-libc-types.regmatch_t
 )
+
+add_proxy_header_library(
+  speed_t
+  HDRS
+    speed_t.h
+  FULL_BUILD_DEPENDS
+    libc.include.llvm-libc-types.speed_t
+)
+
+add_proxy_header_library(
+  struct_termios
+  HDRS
+    struct_termios.h
+  FULL_BUILD_DEPENDS
+    libc.include.llvm-libc-types.struct_termios
+)
+
+add_proxy_header_library(
+  cc_t
+  HDRS
+    cc_t.h
+  FULL_BUILD_DEPENDS
+    libc.include.llvm-libc-types.cc_t
+)
+
+add_proxy_header_library(
+  tcflag_t
+  HDRS
+    tcflag_t.h
+  FULL_BUILD_DEPENDS
+    libc.include.llvm-libc-types.tcflag_t
+)
+
diff --git a/libc/hdr/types/cc_t.h b/libc/hdr/types/cc_t.h
new file mode 100644
index 0000000000000..b830fcd7baa01
--- /dev/null
+++ b/libc/hdr/types/cc_t.h
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Proxy header for cc_t.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_HDR_TYPES_CC_T_H
+#define LLVM_LIBC_HDR_TYPES_CC_T_H
+
+#ifdef LIBC_FULL_BUILD
+
+#include "include/llvm-libc-types/cc_t.h"
+
+#else
+
+#include <termios.h>
+
+#endif // LIBC_FULL_BUILD
+
+#endif // LLVM_LIBC_HDR_TYPES_CC_T_H
diff --git a/libc/hdr/types/speed_t.h b/libc/hdr/types/speed_t.h
new file mode 100644
index 0000000000000..93b4c239c02a4
--- /dev/null
+++ b/libc/hdr/types/speed_t.h
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Proxy header for speed_t.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_HDR_TYPES_SPEED_T_H
+#define LLVM_LIBC_HDR_TYPES_SPEED_T_H
+
+#ifdef LIBC_FULL_BUILD
+
+#include "include/llvm-libc-types/speed_t.h"
+
+#else
+
+#include <termios.h>
+
+#endif // LIBC_FULL_BUILD
+
+#endif // LLVM_LIBC_HDR_TYPES_SPEED_T_H
diff --git a/libc/hdr/types/struct_termios.h b/libc/hdr/types/struct_termios.h
new file mode 100644
index 0000000000000..88305a8809f45
--- /dev/null
+++ b/libc/hdr/types/struct_termios.h
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Proxy header for struct termios.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_HDR_TYPES_STRUCT_TERMIOS_H
+#define LLVM_LIBC_HDR_TYPES_STRUCT_TERMIOS_H
+
+#ifdef LIBC_FULL_BUILD
+
+#include "include/llvm-libc-types/struct_termios.h"
+
+#else
+
+#include <termios.h>
+
+#endif // LIBC_FULL_BUILD
+
+#endif // LLVM_LIBC_HDR_TYPES_STRUCT_TERMIOS_H
diff --git a/libc/hdr/types/tcflag_t.h b/libc/hdr/types/tcflag_t.h
new file mode 100644
index 0000000000000..4616889462274
--- /dev/null
+++ b/libc/hdr/types/tcflag_t.h
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Proxy header for tcflag_t.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_HDR_TYPES_TCFLAG_T_H
+#define LLVM_LIBC_HDR_TYPES_TCFLAG_T_H
+
+#ifdef LIBC_FULL_BUILD
+
+#include "include/llvm-libc-types/tcflag_t.h"
+
+#else
+
+#include <termios.h>
+
+#endif // LIBC_FULL_BUILD
+
+#endif // LLVM_LIBC_HDR_TYPES_TCFLAG_T_H
diff --git a/libc/src/termios/linux/CMakeLists.txt b/libc/src/termios/linux/CMakeLists.txt
index 5d5440ae69266..e89041b641bf9 100644
--- a/libc/src/termios/linux/CMakeLists.txt
+++ b/libc/src/termios/linux/CMakeLists.txt
@@ -1,7 +1,17 @@
 # There is no difference between input and output speeds on Linux.
 # However, since POSIX requires separate functions for setting and getting
-# of the input and output speeds, we use different entrypoints wiht the
+# of the input and output speeds, we use different entrypoints with the
 # same getter/setter logic.
+
+add_header_library(
+  speed_utils
+  HDRS
+    speed_utils.h
+  DEPENDS
+    libc.hdr.termios_macros
+    libc.hdr.types.speed_t
+)
+
 add_entrypoint_object(
   cfgetispeed
   SRCS
@@ -10,6 +20,8 @@ add_entrypoint_object(
     ../cfgetispeed.h
   DEPENDS
     libc.include.termios
+    libc.hdr.types.speed_t
+    libc.hdr.types.struct_termios
 )
 
 add_entrypoint_object(
@@ -19,7 +31,9 @@ add_entrypoint_object(
   HDRS
     ../cfsetispeed.h
   DEPENDS
+    .speed_utils
     libc.include.termios
+    libc.hdr.types.struct_termios
     libc.src.errno.errno
 )
 
@@ -31,6 +45,8 @@ add_entrypoint_object(
     ../cfgetospeed.h
   DEPENDS
     libc.include.termios
+    libc.hdr.types.speed_t
+    libc.hdr.types.struct_termios
 )
 
 add_entrypoint_object(
@@ -40,7 +56,9 @@ add_entrypoint_object(
   HDRS
     ../cfsetospeed.h
   DEPENDS
+    .speed_utils
     libc.include.termios
+    libc.hdr.types.struct_termios
     libc.src.errno.errno
 )
 
@@ -52,6 +70,7 @@ add_entrypoint_object(
     ../tcgetsid.h
   DEPENDS
     libc.include.termios
+    libc.hdr.types.struct_termios
     libc.src.__support.OSUtil.linux.syscall_wrappers.ioctl
     libc.src.errno.errno
 )
@@ -64,6 +83,7 @@ add_entrypoint_object(
     ../tcdrain.h
   DEPENDS
     libc.include.termios
+    libc.hdr.types.struct_termios
     libc.src.__support.OSUtil.linux.syscall_wrappers.ioctl
     libc.src.errno.errno
 )
@@ -76,6 +96,7 @@ add_entrypoint_object(
     ../tcflush.h
   DEPENDS
     libc.include.termios
+    libc.hdr.termios_macros
     libc.src.__support.OSUtil.linux.syscall_wrappers.ioctl
     libc.src.errno.errno
 )
@@ -88,6 +109,7 @@ add_entrypoint_object(
     ../tcflow.h
   DEPENDS
     libc.include.termios
+    libc.hdr.termios_macros
     libc.src.__support.OSUtil.linux.syscall_wrappers.ioctl
     libc.src.errno.errno
 )
@@ -100,6 +122,7 @@ add_entrypoint_object(
     ../tcsendbreak.h
   DEPENDS
     libc.include.termios
+    libc.hdr.types.struct_termios
     libc.src.__support.OSUtil.linux.syscall_wrappers.ioctl
     libc.src.errno.errno
 )
@@ -108,6 +131,9 @@ add_header_library(
   kernel_termios
   HDRS
     kernel_termios.h
+  DEPENDS
+    libc.hdr.types.cc_t
+    libc.hdr.types.tcflag_t
 )
 
 add_entrypoint_object(
@@ -118,7 +144,9 @@ add_entrypoint_object(
     ../tcgetattr.h
   DEPENDS
     .kernel_termios
+    .speed_utils
     libc.include.termios
+    libc.hdr.types.struct_termios
     libc.src.__support.OSUtil.linux.syscall_wrappers.ioctl
     libc.src.errno.errno
 )
@@ -132,6 +160,8 @@ add_entrypoint_object(
   DEPENDS
     .kernel_termios
     libc.include.termios
+    libc.hdr.termios_macros
+    libc.hdr.types.struct_termios
     libc.src.__support.OSUtil.linux.syscall_wrappers.ioctl
     libc.src.errno.errno
 )
diff --git a/libc/src/termios/linux/cfgetispeed.cpp b/libc/src/termios/linux/cfgetispeed.cpp
index 19e9b9140aa2b..9d9c912b4f672 100644
--- a/libc/src/termios/linux/cfgetispeed.cpp
+++ b/libc/src/termios/linux/cfgetispeed.cpp
@@ -1,22 +1,28 @@
-//===-- Linux implementation of cfgetispeed -------------------------------===//
+//===----------------------------------------------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
+///
+/// \file
+/// Linux implementation of cfgetispeed.
+///
+//===----------------------------------------------------------------------===//
 
 #include "src/termios/cfgetispeed.h"
 
 #include "src/__support/common.h"
 #include "src/__support/macros/config.h"
 
-#include <termios.h>
+#include "hdr/types/speed_t.h"
+#include "hdr/types/struct_termios.h"
 
 namespace LIBC_NAMESPACE_DECL {
 
 LLVM_LIBC_FUNCTION(speed_t, cfgetispeed, (const struct termios *t)) {
-  return t->c_cflag & CBAUD;
+  return t->c_ispeed;
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/termios/linux/cfgetospeed.cpp b/libc/src/termios/linux/cfgetospeed.cpp
index d633beb8061f8..7c069de269b9d 100644
--- a/libc/src/termios/linux/cfgetospeed.cpp
+++ b/libc/src/termios/linux/cfgetospeed.cpp
@@ -1,22 +1,28 @@
-//===-- Linux implementation of cfgetospeed -------------------------------===//
+//===----------------------------------------------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
+///
+/// \file
+/// Linux implementation of cfgetospeed.
+///
+//===----------------------------------------------------------------------===//
 
 #include "src/termios/cfgetospeed.h"
 
 #include "src/__support/common.h"
 #include "src/__support/macros/config.h"
 
-#include <termios.h>
+#include "hdr/types/speed_t.h"
+#include "hdr/types/struct_termios.h"
 
 namespace LIBC_NAMESPACE_DECL {
 
 LLVM_LIBC_FUNCTION(speed_t, cfgetospeed, (const struct termios *t)) {
-  return t->c_cflag & CBAUD;
+  return t->c_ospeed;
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/termios/linux/cfsetispeed.cpp b/libc/src/termios/linux/cfsetispeed.cpp
index 47b19974d21be..9fd31f86a1254 100644
--- a/libc/src/termios/linux/cfsetispeed.cpp
+++ b/libc/src/termios/linux/cfsetispeed.cpp
@@ -1,30 +1,37 @@
-//===-- Linux implementation of cfsetispeed -------------------------------===//
+//===----------------------------------------------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
+///
+/// \file
+/// Linux implementation of cfsetispeed.
+///
+//===----------------------------------------------------------------------===//
 
 #include "src/termios/cfsetispeed.h"
 
 #include "src/__support/common.h"
 #include "src/__support/libc_errno.h"
 #include "src/__support/macros/config.h"
+#include "src/termios/linux/speed_utils.h"
 
-#include <termios.h>
+#include "hdr/types/struct_termios.h"
 
 namespace LIBC_NAMESPACE_DECL {
 
 LLVM_LIBC_FUNCTION(int, cfsetispeed, (struct termios * t, speed_t speed)) {
   constexpr speed_t NOT_SPEED_MASK = ~speed_t(CBAUD);
+  speed_t encoded = encode_speed(speed);
   // A speed value is valid only if it is equal to one of the B<NN+> values.
-  if (t == nullptr || ((speed & NOT_SPEED_MASK) != 0)) {
+  if (t == nullptr || ((encoded & NOT_SPEED_MASK) != 0)) {
     libc_errno = EINVAL;
     return -1;
   }
 
-  t->c_cflag = (t->c_cflag & NOT_SPEED_MASK) | speed;
+  t->c_cflag = (t->c_cflag & NOT_SPEED_MASK) | encoded;
   t->c_ispeed = speed;
   return 0;
 }
diff --git a/libc/src/termios/linux/cfsetospeed.cpp b/libc/src/termios/linux/cfsetospeed.cpp
index d2f138257a47a..c5c2e2cf8512b 100644
--- a/libc/src/termios/linux/cfsetospeed.cpp
+++ b/libc/src/termios/linux/cfsetospeed.cpp
@@ -1,30 +1,37 @@
-//===-- Linux implementation of cfsetospeed -------------------------------===//
+//===----------------------------------------------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
+///
+/// \file
+/// Linux implementation of cfsetospeed.
+///
+//===----------------------------------------------------------------------===//
 
 #include "src/termios/cfsetospeed.h"
 #include "src/__support/libc_errno.h"
 #include "src/__support/macros/config.h"
 
 #include "src/__support/common.h"
+#include "src/termios/linux/speed_utils.h"
 
-#include <termios.h>
+#include "hdr/types/struct_termios.h"
 
 namespace LIBC_NAMESPACE_DECL {
 
 LLVM_LIBC_FUNCTION(int, cfsetospeed, (struct termios * t, speed_t speed)) {
   constexpr speed_t NOT_SPEED_MASK = ~speed_t(CBAUD);
+  speed_t encoded = encode_speed(speed);
   // A speed value is valid only if it is equal to one of the B<NN+> values.
-  if (t == nullptr || ((speed & NOT_SPEED_MASK) != 0)) {
+  if (t == nullptr || ((encoded & NOT_SPEED_MASK) != 0)) {
     libc_errno = EINVAL;
     return -1;
   }
 
-  t->c_cflag = (t->c_cflag & NOT_SPEED_MASK) | speed;
+  t->c_cflag = (t->c_cflag & NOT_SPEED_MASK) | encoded;
   t->c_ospeed = speed;
   return 0;
 }
diff --git a/libc/src/termios/linux/kernel_termios.h b/libc/src/termios/linux/kernel_termios.h
index 5a3a9722789eb..cc499e7ab42a5 100644
--- a/libc/src/termios/linux/kernel_termios.h
+++ b/libc/src/termios/linux/kernel_termios.h
@@ -1,17 +1,23 @@
-//===-- Definition of kernel's version of struct termios --------*- C++ -*-===//
+//===----------------------------------------------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
+///
+/// \file
+/// Definition of the kernel's version of struct termios.
+///
+//===----------------------------------------------------------------------===//
 
 #ifndef LLVM_LIBC_SRC_TERMIOS_LINUX_KERNEL_TERMIOS_H
 #define LLVM_LIBC_SRC_TERMIOS_LINUX_KERNEL_TERMIOS_H
 
+#include "hdr/types/cc_t.h"
+#include "hdr/types/tcflag_t.h"
 #include "src/__support/macros/config.h"
 #include <stddef.h>
-#include <termios.h>
 
 namespace LIBC_NAMESPACE_DECL {
 
diff --git a/libc/src/termios/linux/speed_utils.h b/libc/src/termios/linux/speed_utils.h
new file mode 100644
index 0000000000000..4dcbef630238b
--- /dev/null
+++ b/libc/src/termios/linux/speed_utils.h
@@ -0,0 +1,184 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Speed translation utilities for termios.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_TERMIOS_LINUX_SPEED_UTILS_H
+#define LLVM_LIBC_SRC_TERMIOS_LINUX_SPEED_UTILS_H
+
+#include "hdr/termios_macros.h"
+#include "hdr/types/speed_t.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+// glibc 2.42 changed the behavior of the termios Bxxx constants:
+// - Before glibc 2.42, the Bxxx constants correspond to the kernel speed
+// bitmasks
+//   directly (e.g. B50 = 1).
+// - Starting from glibc 2.42, to support arbitrary baud rates numerically, the
+//   Bxxx constants are defined as their actual integer values (e.g. B50 = 50).
+// In this case, we need to translate between the host's numerical speeds and
+// the kernel's speed bitmasks.
+// - If B50 == 1, then the host uses the kernel's bitmasks directly (no
+//   translation).
+// - If B50 is not 1 (e.g. B50 == 50 in glibc >= 2.42), we apply
+//   translation.
+#if (B50 == 1)
+
+LIBC_INLINE constexpr speed_t encode_speed(speed_t speed) { return speed; }
+LIBC_INLINE constexpr speed_t decode_speed(speed_t speed) { return speed; }
+
+#else // Overlay mode with numerical speeds (e.g. glibc)
+
+LIBC_INLINE constexpr speed_t encode_speed(speed_t speed) {
+  switch (speed) {
+  case 0:
+    return 0;
+  case 50:
+    return 0000001;
+  case 75:
+    return 0000002;
+  case 110:
+    return 0000003;
+  case 134:
+    return 0000004;
+  case 150:
+    return 0000005;
+  case 200:
+    return 0000006;
+  case 300:
+    return 0000007;
+  case 600:
+    return 0000010;
+  case 1200:
+    return 0000011;
+  case 1800:
+    return 0000012;
+  case 2400:
+    return 0000013;
+  case 4800:
+    return 0000014;
+  case 9600:
+    return 0000015;
+  case 19200:
+    return 0000016;
+  case 38400:
+    return 0000017;
+  case 57600:
+    return 0010001;
+  case 115200:
+    return 0010002;
+  case 230400:
+    return 0010003;
+  case 460800:
+    return 0010004;
+  case 500000:
+    return 0010005;
+  case 576000:
+    return 0010006;
+  case 921600:
+    return 0010007;
+  case 1000000:
+    return 0010010;
+  case 1152000:
+    return 0010011;
+  case 1500000:
+    return 0010012;
+  case 2000000:
+    return 0010013;
+  case 2500000:
+    return 0010014;
+  case 3000000:
+    return 0010015;
+  case 3500000:
+    return 0010016;
+  case 4000000:
+    return 0010017;
+  default:
+    return speed;
+  }
+}
+
+LIBC_INLINE constexpr speed_t decode_speed(speed_t kernel_speed) {
+  switch (kernel_speed) {
+  case 0:
+    return 0;
+  case 0000001:
+    return 50;
+  case 0000002:
+    return 75;
+  case 0000003:
+    return 110;
+  case 0000004:
+    return 134;
+  case 0000005:
+    return 150;
+  case 0000006:
+    return 200;
+  case 0000007:
+    return 300;
+  case 0000010:
+    return 600;
+  case 0000011:
+    return 1200;
+  case 0000012:
+    return 1800;
+  case 0000013:
+    return 2400;
+  case 0000014:
+    return 4800;
+  case 0000015:
+    return 9600;
+  case 0000016:
+    return 19200;
+  case 0000017:
+    return 38400;
+  case 0010001:
+    return 57600;
+  case 0010002:
+    return 115200;
+  case 0010003:
+    return 230400;
+  case 0010004:
+    return 460800;
+  case 0010005:
+    return 500000;
+  case 0010006:
+    return 576000;
+  case 0010007:
+    return 921600;
+  case 0010010:
+    return 1000000;
+  case 0010011:
+    return 1152000;
+  case 0010012:
+    return 1500000;
+  case 0010013:
+    return 2000000;
+  case 0010014:
+    return 2500000;
+  case 0010015:
+    return 3000000;
+  case 0010016:
+    return 3500000;
+  case 0010017:
+    return 4000000;
+  default:
+    return kernel_speed;
+  }
+}
+
+#endif // LIBC_FULL_BUILD
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_TERMIOS_LINUX_SPEED_UTILS_H
diff --git a/libc/src/termios/linux/tcdrain.cpp b/libc/src/termios/linux/tcdrain.cpp
index 4fce89d65a76f..fa2f5a1ed91ca 100644
--- a/libc/src/termios/linux/tcdrain.cpp
+++ b/libc/src/termios/linux/tcdrain.cpp
@@ -1,20 +1,25 @@
-//===-- Linux implementation of tcdrain -----------------------------------===//
+//===----------------------------------------------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
+///
+/// \file
+/// Linux implementation of tcdrain.
+///
+//===----------------------------------------------------------------------===//
 
 #include "src/termios/tcdrain.h"
 
+#include "hdr/types/struct_termios.h"
 #include "src/__support/OSUtil/linux/syscall_wrappers/ioctl.h"
 #include "src/__support/common.h"
 #include "src/__support/libc_errno.h"
 #include "src/__support/macros/config.h"
 
 #include <asm/ioctls.h> // Safe to include without the risk of name pollution.
-#include <termios.h>
 
 namespace LIBC_NAMESPACE_DECL {
 
diff --git a/libc/src/termios/linux/tcflow.cpp b/libc/src/termios/linux/tcflow.cpp
index 4ffd294997ad4..7efc9dd4a4cb6 100644
--- a/libc/src/termios/linux/tcflow.cpp
+++ b/libc/src/termios/linux/tcflow.cpp
@@ -1,20 +1,25 @@
-//===-- Linux implementation of tcflow -----------------------------------===//
+//===----------------------------------------------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
+///
+/// \file
+/// Linux implementation of tcflow.
+///
+//===----------------------------------------------------------------------===//
 
 #include "src/termios/tcflow.h"
 
+#include "hdr/termios_macros.h"
 #include "src/__support/OSUtil/linux/syscall_wrappers/ioctl.h"
 #include "src/__support/common.h"
 #include "src/__support/libc_errno.h"
 #include "src/__support/macros/config.h"
 
 #include <asm/ioctls.h> // Safe to include without the risk of name pollution.
-#include <termios.h>
 
 namespace LIBC_NAMESPACE_DECL {
 
diff --git a/libc/src/termios/linux/tcflush.cpp b/libc/src/termios/linux/tcflush.cpp
index 8a4676d97454a..684445a3d2732 100644
--- a/libc/src/termios/linux/tcflush.cpp
+++ b/libc/src/termios/linux/tcflush.cpp
@@ -1,20 +1,25 @@
-//===-- Linux implementation of tcflush -----------------------------------===//
+//===----------------------------------------------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
+///
+/// \file
+/// Linux implementation of tcflush.
+///
+//===----------------------------------------------------------------------===//
 
 #include "src/termios/tcflush.h"
 
+#include "hdr/termios_macros.h"
 #include "src/__support/OSUtil/linux/syscall_wrappers/ioctl.h"
 #include "src/__support/common.h"
 #include "src/__support/libc_errno.h"
 #include "src/__support/macros/config.h"
 
 #include <asm/ioctls.h> // Safe to include without the risk of name pollution.
-#include <termios.h>
 
 namespace LIBC_NAMESPACE_DECL {
 
diff --git a/libc/src/termios/linux/tcgetattr.cpp b/libc/src/termios/linux/tcgetattr.cpp
index 0569be4ae588f..588a1de61fb9c 100644
--- a/libc/src/termios/linux/tcgetattr.cpp
+++ b/libc/src/termios/linux/tcgetattr.cpp
@@ -1,20 +1,26 @@
-//===-- Linux implementation of tcgetattr ---------------------------------===//
+//===----------------------------------------------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
+///
+/// \file
+/// Linux implementation of tcgetattr.
+///
+//===----------------------------------------------------------------------===//
 
 #include "src/termios/tcgetattr.h"
+#include "hdr/types/struct_termios.h"
 #include "src/__support/OSUtil/linux/syscall_wrappers/ioctl.h"
 #include "src/__support/common.h"
 #include "src/__support/libc_errno.h"
 #include "src/__support/macros/config.h"
 #include "src/termios/linux/kernel_termios.h"
+#include "src/termios/linux/speed_utils.h"
 
 #include <asm/ioctls.h> // Safe to include without the risk of name pollution.
-#include <termios.h>
 
 namespace LIBC_NAMESPACE_DECL {
 
@@ -29,8 +35,8 @@ LLVM_LIBC_FUNCTION(int, tcgetattr, (int fd, struct termios *t)) {
   t->c_oflag = kt.c_oflag;
   t->c_cflag = kt.c_cflag;
   t->c_lflag = kt.c_lflag;
-  t->c_ispeed = kt.c_cflag & CBAUD;
-  t->c_ospeed = kt.c_cflag & CBAUD;
+  t->c_ispeed = decode_speed(kt.c_cflag & CBAUD);
+  t->c_ospeed = decode_speed(kt.c_cflag & CBAUD);
 
   size_t nccs = KERNEL_NCCS <= NCCS ? KERNEL_NCCS : NCCS;
   for (size_t i = 0; i < nccs; ++i)
diff --git a/libc/src/termios/linux/tcgetsid.cpp b/libc/src/termios/linux/tcgetsid.cpp
index f80fd31b65865..2cafb44b9a55f 100644
--- a/libc/src/termios/linux/tcgetsid.cpp
+++ b/libc/src/termios/linux/tcgetsid.cpp
@@ -1,20 +1,25 @@
-//===-- Linux implementation of tcgetsid ----------------------------------===//
+//===----------------------------------------------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
+///
+/// \file
+/// Linux implementation of tcgetsid.
+///
+//===----------------------------------------------------------------------===//
 
 #include "src/termios/tcgetsid.h"
 
+#include "hdr/types/struct_termios.h"
 #include "src/__support/OSUtil/linux/syscall_wrappers/ioctl.h"
 #include "src/__support/common.h"
 #include "src/__support/libc_errno.h"
 #include "src/__support/macros/config.h"
 
 #include <asm/ioctls.h> // Safe to include without the risk of name pollution.
-#include <termios.h>
 
 namespace LIBC_NAMESPACE_DECL {
 
diff --git a/libc/src/termios/linux/tcsendbreak.cpp b/libc/src/termios/linux/tcsendbreak.cpp
index e91ec7b748582..0157d6098b218 100644
--- a/libc/src/termios/linux/tcsendbreak.cpp
+++ b/libc/src/termios/linux/tcsendbreak.cpp
@@ -1,20 +1,25 @@
-//===-- Linux implementation of tcsendbreak -------------------------------===//
+//===----------------------------------------------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
+///
+/// \file
+/// Linux implementation of tcsendbreak.
+///
+//===----------------------------------------------------------------------===//
 
 #include "src/termios/tcsendbreak.h"
 
+#include "hdr/types/struct_termios.h"
 #include "src/__support/OSUtil/linux/syscall_wrappers/ioctl.h"
 #include "src/__support/common.h"
 #include "src/__support/libc_errno.h"
 #include "src/__support/macros/config.h"
 
 #include <asm/ioctls.h> // Safe to include without the risk of name pollution.
-#include <termios.h>
 
 namespace LIBC_NAMESPACE_DECL {
 
diff --git a/libc/src/termios/linux/tcsetattr.cpp b/libc/src/termios/linux/tcsetattr.cpp
index b2f08d078ee83..25e36813ca283 100644
--- a/libc/src/termios/linux/tcsetattr.cpp
+++ b/libc/src/termios/linux/tcsetattr.cpp
@@ -1,12 +1,19 @@
-//===-- Linux implementation of tcsetattr ---------------------------------===//
+//===----------------------------------------------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
+///
+/// \file
+/// Linux implementation of tcsetattr.
+///
+//===----------------------------------------------------------------------===//
 
 #include "src/termios/tcsetattr.h"
+#include "hdr/termios_macros.h"
+#include "hdr/types/struct_termios.h"
 #include "src/__support/OSUtil/linux/syscall_wrappers/ioctl.h"
 #include "src/__support/common.h"
 #include "src/__support/libc_errno.h"
@@ -14,7 +21,6 @@
 #include "src/termios/linux/kernel_termios.h"
 
 #include <asm/ioctls.h> // Safe to include without the risk of name pollution.
-#include <termios.h>
 
 namespace LIBC_NAMESPACE_DECL {
 
diff --git a/libc/test/src/termios/CMakeLists.txt b/libc/test/src/termios/CMakeLists.txt
index 059c272c105c4..2ff137e89ebc1 100644
--- a/libc/test/src/termios/CMakeLists.txt
+++ b/libc/test/src/termios/CMakeLists.txt
@@ -8,6 +8,8 @@ add_libc_unittest(
     termios_test.cpp
   DEPENDS
     libc.include.termios
+    libc.hdr.termios_macros
+    libc.hdr.types.struct_termios
     libc.src.errno.errno
     libc.src.fcntl.open
     libc.src.termios.cfgetispeed
diff --git a/libc/test/src/termios/termios_test.cpp b/libc/test/src/termios/termios_test.cpp
index 7a8075997a4a8..91ec1294be3ad 100644
--- a/libc/test/src/termios/termios_test.cpp
+++ b/libc/test/src/termios/termios_test.cpp
@@ -1,11 +1,18 @@
-//===-- Unittests for a bunch of functions in termios.h -------------------===//
+//===----------------------------------------------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
+///
+/// \file
+/// Unittests for functions in termios.h.
+///
+//===----------------------------------------------------------------------===//
 
+#include "hdr/termios_macros.h"
+#include "hdr/types/struct_termios.h"
 #include "src/__support/libc_errno.h"
 #include "src/fcntl/open.h"
 #include "src/termios/cfgetispeed.h"
@@ -20,8 +27,6 @@
 #include "test/UnitTest/ErrnoSetterMatcher.h"
 #include "test/UnitTest/Test.h"
 
-#include <termios.h>
-
 using LlvmLibcTermiosTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
 using namespace LIBC_NAMESPACE::testing::ErrnoSetterMatcher;
 

>From 00df12cd7e2bdb8f49a3c65c31078f0b20c17560 Mon Sep 17 00:00:00 2001
From: Tue Ly <lntue.h at gmail.com>
Date: Tue, 21 Jul 2026 17:53:46 +0000
Subject: [PATCH 2/6] Address comments.

---
 libc/src/termios/cfgetispeed.h         |  5 ++--
 libc/src/termios/cfgetospeed.h         |  5 ++--
 libc/src/termios/cfsetispeed.h         |  5 ++--
 libc/src/termios/cfsetospeed.h         |  5 ++--
 libc/src/termios/linux/CMakeLists.txt  | 13 +++++++--
 libc/src/termios/linux/cfgetispeed.cpp |  9 +++---
 libc/src/termios/linux/cfgetospeed.cpp |  9 +++---
 libc/src/termios/linux/cfsetispeed.cpp | 13 ++++-----
 libc/src/termios/linux/cfsetospeed.cpp | 15 +++++-----
 libc/src/termios/linux/speed_utils.h   |  2 +-
 libc/src/termios/linux/tcgetattr.cpp   | 20 +++++++++----
 libc/src/termios/linux/tcgetsid.cpp    |  2 +-
 libc/src/termios/linux/tcsendbreak.cpp |  3 +-
 libc/src/termios/linux/tcsetattr.cpp   | 20 +++++++++----
 libc/src/termios/tcgetattr.h           |  4 +--
 libc/src/termios/tcsetattr.h           |  4 +--
 libc/test/src/termios/termios_test.cpp | 40 ++++++++++++++++++++++++--
 17 files changed, 119 insertions(+), 55 deletions(-)

diff --git a/libc/src/termios/cfgetispeed.h b/libc/src/termios/cfgetispeed.h
index 56b0ab852cdb8..fb058b197fca4 100644
--- a/libc/src/termios/cfgetispeed.h
+++ b/libc/src/termios/cfgetispeed.h
@@ -9,12 +9,13 @@
 #ifndef LLVM_LIBC_SRC_TERMIOS_CFGETISPEED_H
 #define LLVM_LIBC_SRC_TERMIOS_CFGETISPEED_H
 
+#include "hdr/types/speed_t.h"
+#include "hdr/types/struct_termios.h"
 #include "src/__support/macros/config.h"
-#include <termios.h>
 
 namespace LIBC_NAMESPACE_DECL {
 
-speed_t cfgetispeed(const struct termios *t);
+speed_t cfgetispeed(const termios *t);
 
 } // namespace LIBC_NAMESPACE_DECL
 
diff --git a/libc/src/termios/cfgetospeed.h b/libc/src/termios/cfgetospeed.h
index 9d8e6656b0e9b..9126d0880f88d 100644
--- a/libc/src/termios/cfgetospeed.h
+++ b/libc/src/termios/cfgetospeed.h
@@ -9,12 +9,13 @@
 #ifndef LLVM_LIBC_SRC_TERMIOS_CFGETOSPEED_H
 #define LLVM_LIBC_SRC_TERMIOS_CFGETOSPEED_H
 
+#include "hdr/types/speed_t.h"
+#include "hdr/types/struct_termios.h"
 #include "src/__support/macros/config.h"
-#include <termios.h>
 
 namespace LIBC_NAMESPACE_DECL {
 
-speed_t cfgetospeed(const struct termios *t);
+speed_t cfgetospeed(const termios *t);
 
 } // namespace LIBC_NAMESPACE_DECL
 
diff --git a/libc/src/termios/cfsetispeed.h b/libc/src/termios/cfsetispeed.h
index 89b683e560489..5799bf1bf2455 100644
--- a/libc/src/termios/cfsetispeed.h
+++ b/libc/src/termios/cfsetispeed.h
@@ -9,12 +9,13 @@
 #ifndef LLVM_LIBC_SRC_TERMIOS_CFSETISPEED_H
 #define LLVM_LIBC_SRC_TERMIOS_CFSETISPEED_H
 
+#include "hdr/types/speed_t.h"
+#include "hdr/types/struct_termios.h"
 #include "src/__support/macros/config.h"
-#include <termios.h>
 
 namespace LIBC_NAMESPACE_DECL {
 
-int cfsetispeed(struct termios *t, speed_t speed);
+int cfsetispeed(termios *t, speed_t speed);
 
 } // namespace LIBC_NAMESPACE_DECL
 
diff --git a/libc/src/termios/cfsetospeed.h b/libc/src/termios/cfsetospeed.h
index 2f2c5315fb690..940e4d70ecff7 100644
--- a/libc/src/termios/cfsetospeed.h
+++ b/libc/src/termios/cfsetospeed.h
@@ -9,12 +9,13 @@
 #ifndef LLVM_LIBC_SRC_TERMIOS_CFSETOSPEED_H
 #define LLVM_LIBC_SRC_TERMIOS_CFSETOSPEED_H
 
+#include "hdr/types/speed_t.h"
+#include "hdr/types/struct_termios.h"
 #include "src/__support/macros/config.h"
-#include <termios.h>
 
 namespace LIBC_NAMESPACE_DECL {
 
-int cfsetospeed(struct termios *t, speed_t speed);
+int cfsetospeed(termios *t, speed_t speed);
 
 } // namespace LIBC_NAMESPACE_DECL
 
diff --git a/libc/src/termios/linux/CMakeLists.txt b/libc/src/termios/linux/CMakeLists.txt
index e89041b641bf9..ef2c115cb8ea0 100644
--- a/libc/src/termios/linux/CMakeLists.txt
+++ b/libc/src/termios/linux/CMakeLists.txt
@@ -22,6 +22,7 @@ add_entrypoint_object(
     libc.include.termios
     libc.hdr.types.speed_t
     libc.hdr.types.struct_termios
+    libc.src.__support.macros.null_check
 )
 
 add_entrypoint_object(
@@ -33,8 +34,10 @@ add_entrypoint_object(
   DEPENDS
     .speed_utils
     libc.include.termios
+    libc.hdr.types.speed_t
     libc.hdr.types.struct_termios
     libc.src.errno.errno
+    libc.src.__support.macros.null_check
 )
 
 add_entrypoint_object(
@@ -47,6 +50,7 @@ add_entrypoint_object(
     libc.include.termios
     libc.hdr.types.speed_t
     libc.hdr.types.struct_termios
+    libc.src.__support.macros.null_check
 )
 
 add_entrypoint_object(
@@ -58,8 +62,10 @@ add_entrypoint_object(
   DEPENDS
     .speed_utils
     libc.include.termios
+    libc.hdr.types.speed_t
     libc.hdr.types.struct_termios
     libc.src.errno.errno
+    libc.src.__support.macros.null_check
 )
 
 add_entrypoint_object(
@@ -70,7 +76,7 @@ add_entrypoint_object(
     ../tcgetsid.h
   DEPENDS
     libc.include.termios
-    libc.hdr.types.struct_termios
+    libc.hdr.types.pid_t
     libc.src.__support.OSUtil.linux.syscall_wrappers.ioctl
     libc.src.errno.errno
 )
@@ -122,7 +128,6 @@ add_entrypoint_object(
     ../tcsendbreak.h
   DEPENDS
     libc.include.termios
-    libc.hdr.types.struct_termios
     libc.src.__support.OSUtil.linux.syscall_wrappers.ioctl
     libc.src.errno.errno
 )
@@ -146,9 +151,11 @@ add_entrypoint_object(
     .kernel_termios
     .speed_utils
     libc.include.termios
+    libc.hdr.termios_macros
     libc.hdr.types.struct_termios
     libc.src.__support.OSUtil.linux.syscall_wrappers.ioctl
     libc.src.errno.errno
+    libc.src.__support.macros.null_check
 )
 
 add_entrypoint_object(
@@ -159,9 +166,11 @@ add_entrypoint_object(
     ../tcsetattr.h
   DEPENDS
     .kernel_termios
+    .speed_utils
     libc.include.termios
     libc.hdr.termios_macros
     libc.hdr.types.struct_termios
     libc.src.__support.OSUtil.linux.syscall_wrappers.ioctl
     libc.src.errno.errno
+    libc.src.__support.macros.null_check
 )
diff --git a/libc/src/termios/linux/cfgetispeed.cpp b/libc/src/termios/linux/cfgetispeed.cpp
index 9d9c912b4f672..bb0f49a206f6f 100644
--- a/libc/src/termios/linux/cfgetispeed.cpp
+++ b/libc/src/termios/linux/cfgetispeed.cpp
@@ -12,16 +12,15 @@
 //===----------------------------------------------------------------------===//
 
 #include "src/termios/cfgetispeed.h"
-
-#include "src/__support/common.h"
-#include "src/__support/macros/config.h"
-
 #include "hdr/types/speed_t.h"
 #include "hdr/types/struct_termios.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/null_check.h"
 
 namespace LIBC_NAMESPACE_DECL {
 
-LLVM_LIBC_FUNCTION(speed_t, cfgetispeed, (const struct termios *t)) {
+LLVM_LIBC_FUNCTION(speed_t, cfgetispeed, (const termios *t)) {
+  LIBC_CRASH_ON_NULLPTR(t);
   return t->c_ispeed;
 }
 
diff --git a/libc/src/termios/linux/cfgetospeed.cpp b/libc/src/termios/linux/cfgetospeed.cpp
index 7c069de269b9d..3da0447f961a4 100644
--- a/libc/src/termios/linux/cfgetospeed.cpp
+++ b/libc/src/termios/linux/cfgetospeed.cpp
@@ -12,16 +12,15 @@
 //===----------------------------------------------------------------------===//
 
 #include "src/termios/cfgetospeed.h"
-
-#include "src/__support/common.h"
-#include "src/__support/macros/config.h"
-
 #include "hdr/types/speed_t.h"
 #include "hdr/types/struct_termios.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/null_check.h"
 
 namespace LIBC_NAMESPACE_DECL {
 
-LLVM_LIBC_FUNCTION(speed_t, cfgetospeed, (const struct termios *t)) {
+LLVM_LIBC_FUNCTION(speed_t, cfgetospeed, (const termios *t)) {
+  LIBC_CRASH_ON_NULLPTR(t);
   return t->c_ospeed;
 }
 
diff --git a/libc/src/termios/linux/cfsetispeed.cpp b/libc/src/termios/linux/cfsetispeed.cpp
index 9fd31f86a1254..0b9b6e440bc8c 100644
--- a/libc/src/termios/linux/cfsetispeed.cpp
+++ b/libc/src/termios/linux/cfsetispeed.cpp
@@ -12,21 +12,20 @@
 //===----------------------------------------------------------------------===//
 
 #include "src/termios/cfsetispeed.h"
-
+#include "hdr/types/struct_termios.h"
 #include "src/__support/common.h"
 #include "src/__support/libc_errno.h"
-#include "src/__support/macros/config.h"
+#include "src/__support/macros/null_check.h"
 #include "src/termios/linux/speed_utils.h"
 
-#include "hdr/types/struct_termios.h"
-
 namespace LIBC_NAMESPACE_DECL {
 
-LLVM_LIBC_FUNCTION(int, cfsetispeed, (struct termios * t, speed_t speed)) {
-  constexpr speed_t NOT_SPEED_MASK = ~speed_t(CBAUD);
+LLVM_LIBC_FUNCTION(int, cfsetispeed, (termios * t, speed_t speed)) {
+  LIBC_CRASH_ON_NULLPTR(t);
+  constexpr speed_t NOT_SPEED_MASK = ~static_cast<speed_t>(CBAUD);
   speed_t encoded = encode_speed(speed);
   // A speed value is valid only if it is equal to one of the B<NN+> values.
-  if (t == nullptr || ((encoded & NOT_SPEED_MASK) != 0)) {
+  if ((encoded & NOT_SPEED_MASK) != 0) {
     libc_errno = EINVAL;
     return -1;
   }
diff --git a/libc/src/termios/linux/cfsetospeed.cpp b/libc/src/termios/linux/cfsetospeed.cpp
index c5c2e2cf8512b..38f4d3f310095 100644
--- a/libc/src/termios/linux/cfsetospeed.cpp
+++ b/libc/src/termios/linux/cfsetospeed.cpp
@@ -12,21 +12,20 @@
 //===----------------------------------------------------------------------===//
 
 #include "src/termios/cfsetospeed.h"
-#include "src/__support/libc_errno.h"
-#include "src/__support/macros/config.h"
-
+#include "hdr/types/struct_termios.h"
 #include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/null_check.h"
 #include "src/termios/linux/speed_utils.h"
 
-#include "hdr/types/struct_termios.h"
-
 namespace LIBC_NAMESPACE_DECL {
 
-LLVM_LIBC_FUNCTION(int, cfsetospeed, (struct termios * t, speed_t speed)) {
-  constexpr speed_t NOT_SPEED_MASK = ~speed_t(CBAUD);
+LLVM_LIBC_FUNCTION(int, cfsetospeed, (termios * t, speed_t speed)) {
+  LIBC_CRASH_ON_NULLPTR(t);
+  constexpr speed_t NOT_SPEED_MASK = ~static_cast<speed_t>(CBAUD);
   speed_t encoded = encode_speed(speed);
   // A speed value is valid only if it is equal to one of the B<NN+> values.
-  if (t == nullptr || ((encoded & NOT_SPEED_MASK) != 0)) {
+  if ((encoded & NOT_SPEED_MASK) != 0) {
     libc_errno = EINVAL;
     return -1;
   }
diff --git a/libc/src/termios/linux/speed_utils.h b/libc/src/termios/linux/speed_utils.h
index 4dcbef630238b..cc0d76e2cc3b5 100644
--- a/libc/src/termios/linux/speed_utils.h
+++ b/libc/src/termios/linux/speed_utils.h
@@ -177,7 +177,7 @@ LIBC_INLINE constexpr speed_t decode_speed(speed_t kernel_speed) {
   }
 }
 
-#endif // LIBC_FULL_BUILD
+#endif // (B50 == 1)
 
 } // namespace LIBC_NAMESPACE_DECL
 
diff --git a/libc/src/termios/linux/tcgetattr.cpp b/libc/src/termios/linux/tcgetattr.cpp
index 588a1de61fb9c..5e7f3d2f9c794 100644
--- a/libc/src/termios/linux/tcgetattr.cpp
+++ b/libc/src/termios/linux/tcgetattr.cpp
@@ -12,11 +12,13 @@
 //===----------------------------------------------------------------------===//
 
 #include "src/termios/tcgetattr.h"
+#include "hdr/termios_macros.h"
 #include "hdr/types/struct_termios.h"
+#include "src/__support/CPP/algorithm.h"
 #include "src/__support/OSUtil/linux/syscall_wrappers/ioctl.h"
 #include "src/__support/common.h"
 #include "src/__support/libc_errno.h"
-#include "src/__support/macros/config.h"
+#include "src/__support/macros/null_check.h"
 #include "src/termios/linux/kernel_termios.h"
 #include "src/termios/linux/speed_utils.h"
 
@@ -24,7 +26,8 @@
 
 namespace LIBC_NAMESPACE_DECL {
 
-LLVM_LIBC_FUNCTION(int, tcgetattr, (int fd, struct termios *t)) {
+LLVM_LIBC_FUNCTION(int, tcgetattr, (int fd, termios *t)) {
+  LIBC_CRASH_ON_NULLPTR(t);
   LIBC_NAMESPACE::kernel_termios kt;
   auto ret = linux_syscalls::ioctl(fd, TCGETS, &kt);
   if (!ret.has_value()) {
@@ -35,10 +38,17 @@ LLVM_LIBC_FUNCTION(int, tcgetattr, (int fd, struct termios *t)) {
   t->c_oflag = kt.c_oflag;
   t->c_cflag = kt.c_cflag;
   t->c_lflag = kt.c_lflag;
-  t->c_ispeed = decode_speed(kt.c_cflag & CBAUD);
-  t->c_ospeed = decode_speed(kt.c_cflag & CBAUD);
+  t->c_line = kt.c_line;
 
-  size_t nccs = KERNEL_NCCS <= NCCS ? KERNEL_NCCS : NCCS;
+  speed_t ospeed_mask = kt.c_cflag & CBAUD;
+  speed_t ispeed_mask = (kt.c_cflag & CIBAUD) >> 16;
+  if (ispeed_mask == 0)
+    ispeed_mask = ospeed_mask;
+
+  t->c_ospeed = decode_speed(ospeed_mask);
+  t->c_ispeed = decode_speed(ispeed_mask);
+
+  size_t nccs = cpp::min(KERNEL_NCCS, static_cast<size_t>(NCCS));
   for (size_t i = 0; i < nccs; ++i)
     t->c_cc[i] = kt.c_cc[i];
   if (NCCS > nccs) {
diff --git a/libc/src/termios/linux/tcgetsid.cpp b/libc/src/termios/linux/tcgetsid.cpp
index 2cafb44b9a55f..087f7ba4f8c64 100644
--- a/libc/src/termios/linux/tcgetsid.cpp
+++ b/libc/src/termios/linux/tcgetsid.cpp
@@ -13,7 +13,7 @@
 
 #include "src/termios/tcgetsid.h"
 
-#include "hdr/types/struct_termios.h"
+#include "hdr/types/pid_t.h"
 #include "src/__support/OSUtil/linux/syscall_wrappers/ioctl.h"
 #include "src/__support/common.h"
 #include "src/__support/libc_errno.h"
diff --git a/libc/src/termios/linux/tcsendbreak.cpp b/libc/src/termios/linux/tcsendbreak.cpp
index 0157d6098b218..90866c4f54d82 100644
--- a/libc/src/termios/linux/tcsendbreak.cpp
+++ b/libc/src/termios/linux/tcsendbreak.cpp
@@ -13,7 +13,6 @@
 
 #include "src/termios/tcsendbreak.h"
 
-#include "hdr/types/struct_termios.h"
 #include "src/__support/OSUtil/linux/syscall_wrappers/ioctl.h"
 #include "src/__support/common.h"
 #include "src/__support/libc_errno.h"
@@ -23,7 +22,7 @@
 
 namespace LIBC_NAMESPACE_DECL {
 
-LLVM_LIBC_FUNCTION(pid_t, tcsendbreak, (int fd, int /* unused duration */)) {
+LLVM_LIBC_FUNCTION(int, tcsendbreak, (int fd, int /* unused duration */)) {
   // POSIX leaves the behavior for non-zero duration implementation dependent.
   // Which means that the behavior can be the same as it is when duration is
   // zero. So, we just pass zero to the syscall.
diff --git a/libc/src/termios/linux/tcsetattr.cpp b/libc/src/termios/linux/tcsetattr.cpp
index 25e36813ca283..e22810bbc5a62 100644
--- a/libc/src/termios/linux/tcsetattr.cpp
+++ b/libc/src/termios/linux/tcsetattr.cpp
@@ -14,18 +14,20 @@
 #include "src/termios/tcsetattr.h"
 #include "hdr/termios_macros.h"
 #include "hdr/types/struct_termios.h"
+#include "src/__support/CPP/algorithm.h"
 #include "src/__support/OSUtil/linux/syscall_wrappers/ioctl.h"
 #include "src/__support/common.h"
 #include "src/__support/libc_errno.h"
-#include "src/__support/macros/config.h"
+#include "src/__support/macros/null_check.h"
 #include "src/termios/linux/kernel_termios.h"
+#include "src/termios/linux/speed_utils.h"
 
 #include <asm/ioctls.h> // Safe to include without the risk of name pollution.
 
 namespace LIBC_NAMESPACE_DECL {
 
-LLVM_LIBC_FUNCTION(int, tcsetattr,
-                   (int fd, int actions, const struct termios *t)) {
+LLVM_LIBC_FUNCTION(int, tcsetattr, (int fd, int actions, const termios *t)) {
+  LIBC_CRASH_ON_NULLPTR(t);
   struct kernel_termios kt;
   long cmd;
 
@@ -46,9 +48,17 @@ LLVM_LIBC_FUNCTION(int, tcsetattr,
 
   kt.c_iflag = t->c_iflag;
   kt.c_oflag = t->c_oflag;
-  kt.c_cflag = t->c_cflag;
+
+  speed_t ospeed = encode_speed(t->c_ospeed);
+  speed_t ispeed = t->c_ispeed == 0 ? ospeed : encode_speed(t->c_ispeed);
+
+  constexpr speed_t NOT_SPEED_MASK = ~static_cast<speed_t>(CBAUD | CIBAUD);
+  kt.c_cflag = (t->c_cflag & NOT_SPEED_MASK) | ospeed | (ispeed << 16);
+
   kt.c_lflag = t->c_lflag;
-  size_t nccs = KERNEL_NCCS <= NCCS ? KERNEL_NCCS : NCCS;
+  kt.c_line = t->c_line;
+
+  size_t nccs = cpp::min(KERNEL_NCCS, static_cast<size_t>(NCCS));
   for (size_t i = 0; i < nccs; ++i)
     kt.c_cc[i] = t->c_cc[i];
   if (nccs < KERNEL_NCCS) {
diff --git a/libc/src/termios/tcgetattr.h b/libc/src/termios/tcgetattr.h
index 54b169aedad88..fee34ad859832 100644
--- a/libc/src/termios/tcgetattr.h
+++ b/libc/src/termios/tcgetattr.h
@@ -9,12 +9,12 @@
 #ifndef LLVM_LIBC_SRC_TERMIOS_TCGETATTR_H
 #define LLVM_LIBC_SRC_TERMIOS_TCGETATTR_H
 
+#include "hdr/types/struct_termios.h"
 #include "src/__support/macros/config.h"
-#include <termios.h>
 
 namespace LIBC_NAMESPACE_DECL {
 
-int tcgetattr(int fd, struct termios *t);
+int tcgetattr(int fd, termios *t);
 
 } // namespace LIBC_NAMESPACE_DECL
 
diff --git a/libc/src/termios/tcsetattr.h b/libc/src/termios/tcsetattr.h
index 05c88f104836f..28a75b883e0f3 100644
--- a/libc/src/termios/tcsetattr.h
+++ b/libc/src/termios/tcsetattr.h
@@ -9,12 +9,12 @@
 #ifndef LLVM_LIBC_SRC_TERMIOS_TCSETATTR_H
 #define LLVM_LIBC_SRC_TERMIOS_TCSETATTR_H
 
+#include "hdr/types/struct_termios.h"
 #include "src/__support/macros/config.h"
-#include <termios.h>
 
 namespace LIBC_NAMESPACE_DECL {
 
-int tcsetattr(int fd, int actions, const struct termios *t);
+int tcsetattr(int fd, int actions, const termios *t);
 
 } // namespace LIBC_NAMESPACE_DECL
 
diff --git a/libc/test/src/termios/termios_test.cpp b/libc/test/src/termios/termios_test.cpp
index 91ec1294be3ad..ac484e6874b0f 100644
--- a/libc/test/src/termios/termios_test.cpp
+++ b/libc/test/src/termios/termios_test.cpp
@@ -35,7 +35,7 @@ using namespace LIBC_NAMESPACE::testing::ErrnoSetterMatcher;
 // from ninja/make which change the terminal behavior.
 
 TEST_F(LlvmLibcTermiosTest, SpeedSmokeTest) {
-  struct termios t;
+  termios t;
   ASSERT_THAT(LIBC_NAMESPACE::cfsetispeed(&t, B50), Succeeds(0));
   ASSERT_EQ(LIBC_NAMESPACE::cfgetispeed(&t), speed_t(B50));
   ASSERT_THAT(LIBC_NAMESPACE::cfsetospeed(&t, B75), Succeeds(0));
@@ -46,7 +46,7 @@ TEST_F(LlvmLibcTermiosTest, SpeedSmokeTest) {
 }
 
 TEST_F(LlvmLibcTermiosTest, GetAttrSmokeTest) {
-  struct termios t;
+  termios t;
   int fd = LIBC_NAMESPACE::open("/dev/tty", O_RDONLY);
   if (fd < 0) {
     // When /dev/tty is not available, no point continuing
@@ -70,3 +70,39 @@ TEST_F(LlvmLibcTermiosTest, TcGetSidSmokeTest) {
               returns(GT(pid_t(0))).with_errno(EQ(0)));
   ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
 }
+
+TEST_F(LlvmLibcTermiosTest, SplitSpeedTest) {
+  int fd = LIBC_NAMESPACE::open("/dev/ptmx", O_RDWR);
+  if (fd < 0) {
+    // Gracefully skip if /dev/ptmx is not available
+    libc_errno = 0;
+    return;
+  }
+  ASSERT_ERRNO_SUCCESS();
+
+  termios t;
+  ASSERT_THAT(LIBC_NAMESPACE::tcgetattr(fd, &t), Succeeds(0));
+
+  // 1. Test setting split speeds.
+  ASSERT_THAT(LIBC_NAMESPACE::cfsetispeed(&t, B50), Succeeds(0));
+  ASSERT_THAT(LIBC_NAMESPACE::cfsetospeed(&t, B75), Succeeds(0));
+  ASSERT_THAT(LIBC_NAMESPACE::tcsetattr(fd, TCSANOW, &t), Succeeds(0));
+
+  termios t2;
+  ASSERT_THAT(LIBC_NAMESPACE::tcgetattr(fd, &t2), Succeeds(0));
+  ASSERT_EQ(LIBC_NAMESPACE::cfgetispeed(&t2), speed_t(B50));
+  ASSERT_EQ(LIBC_NAMESPACE::cfgetospeed(&t2), speed_t(B75));
+
+  // 2. Test input speed 0 fallback.
+  ASSERT_THAT(LIBC_NAMESPACE::cfsetispeed(&t2, 0), Succeeds(0));
+  ASSERT_THAT(LIBC_NAMESPACE::tcsetattr(fd, TCSANOW, &t2), Succeeds(0));
+
+  termios t3;
+  ASSERT_THAT(LIBC_NAMESPACE::tcgetattr(fd, &t3), Succeeds(0));
+  ASSERT_EQ(LIBC_NAMESPACE::cfgetospeed(&t3), speed_t(B75));
+  // Under POSIX, if input speed was set to 0, it must be the same as output
+  // speed.
+  ASSERT_EQ(LIBC_NAMESPACE::cfgetispeed(&t3), speed_t(B75));
+
+  ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
+}

>From a36684f0b929e90b99db30a1f614338081deda03 Mon Sep 17 00:00:00 2001
From: Tue Ly <lntue.h at gmail.com>
Date: Tue, 21 Jul 2026 18:02:06 +0000
Subject: [PATCH 3/6] Remove unused <termios.h> include and dependency.

---
 libc/src/termios/linux/CMakeLists.txt | 12 ------------
 libc/src/termios/linux/tcdrain.cpp    |  1 -
 libc/src/termios/tcdrain.h            |  1 -
 libc/src/termios/tcflow.h             |  1 -
 libc/src/termios/tcflush.h            |  1 -
 libc/src/termios/tcgetsid.h           |  2 +-
 libc/src/termios/tcsendbreak.h        |  1 -
 7 files changed, 1 insertion(+), 18 deletions(-)

diff --git a/libc/src/termios/linux/CMakeLists.txt b/libc/src/termios/linux/CMakeLists.txt
index ef2c115cb8ea0..86565cf1294c7 100644
--- a/libc/src/termios/linux/CMakeLists.txt
+++ b/libc/src/termios/linux/CMakeLists.txt
@@ -19,7 +19,6 @@ add_entrypoint_object(
   HDRS
     ../cfgetispeed.h
   DEPENDS
-    libc.include.termios
     libc.hdr.types.speed_t
     libc.hdr.types.struct_termios
     libc.src.__support.macros.null_check
@@ -33,7 +32,6 @@ add_entrypoint_object(
     ../cfsetispeed.h
   DEPENDS
     .speed_utils
-    libc.include.termios
     libc.hdr.types.speed_t
     libc.hdr.types.struct_termios
     libc.src.errno.errno
@@ -47,7 +45,6 @@ add_entrypoint_object(
   HDRS
     ../cfgetospeed.h
   DEPENDS
-    libc.include.termios
     libc.hdr.types.speed_t
     libc.hdr.types.struct_termios
     libc.src.__support.macros.null_check
@@ -61,7 +58,6 @@ add_entrypoint_object(
     ../cfsetospeed.h
   DEPENDS
     .speed_utils
-    libc.include.termios
     libc.hdr.types.speed_t
     libc.hdr.types.struct_termios
     libc.src.errno.errno
@@ -75,7 +71,6 @@ add_entrypoint_object(
   HDRS
     ../tcgetsid.h
   DEPENDS
-    libc.include.termios
     libc.hdr.types.pid_t
     libc.src.__support.OSUtil.linux.syscall_wrappers.ioctl
     libc.src.errno.errno
@@ -88,8 +83,6 @@ add_entrypoint_object(
   HDRS
     ../tcdrain.h
   DEPENDS
-    libc.include.termios
-    libc.hdr.types.struct_termios
     libc.src.__support.OSUtil.linux.syscall_wrappers.ioctl
     libc.src.errno.errno
 )
@@ -101,7 +94,6 @@ add_entrypoint_object(
   HDRS
     ../tcflush.h
   DEPENDS
-    libc.include.termios
     libc.hdr.termios_macros
     libc.src.__support.OSUtil.linux.syscall_wrappers.ioctl
     libc.src.errno.errno
@@ -114,7 +106,6 @@ add_entrypoint_object(
   HDRS
     ../tcflow.h
   DEPENDS
-    libc.include.termios
     libc.hdr.termios_macros
     libc.src.__support.OSUtil.linux.syscall_wrappers.ioctl
     libc.src.errno.errno
@@ -127,7 +118,6 @@ add_entrypoint_object(
   HDRS
     ../tcsendbreak.h
   DEPENDS
-    libc.include.termios
     libc.src.__support.OSUtil.linux.syscall_wrappers.ioctl
     libc.src.errno.errno
 )
@@ -150,7 +140,6 @@ add_entrypoint_object(
   DEPENDS
     .kernel_termios
     .speed_utils
-    libc.include.termios
     libc.hdr.termios_macros
     libc.hdr.types.struct_termios
     libc.src.__support.OSUtil.linux.syscall_wrappers.ioctl
@@ -167,7 +156,6 @@ add_entrypoint_object(
   DEPENDS
     .kernel_termios
     .speed_utils
-    libc.include.termios
     libc.hdr.termios_macros
     libc.hdr.types.struct_termios
     libc.src.__support.OSUtil.linux.syscall_wrappers.ioctl
diff --git a/libc/src/termios/linux/tcdrain.cpp b/libc/src/termios/linux/tcdrain.cpp
index fa2f5a1ed91ca..ee4abd0f2d8a2 100644
--- a/libc/src/termios/linux/tcdrain.cpp
+++ b/libc/src/termios/linux/tcdrain.cpp
@@ -13,7 +13,6 @@
 
 #include "src/termios/tcdrain.h"
 
-#include "hdr/types/struct_termios.h"
 #include "src/__support/OSUtil/linux/syscall_wrappers/ioctl.h"
 #include "src/__support/common.h"
 #include "src/__support/libc_errno.h"
diff --git a/libc/src/termios/tcdrain.h b/libc/src/termios/tcdrain.h
index f7fcd8c734bfa..73a45b3f01076 100644
--- a/libc/src/termios/tcdrain.h
+++ b/libc/src/termios/tcdrain.h
@@ -10,7 +10,6 @@
 #define LLVM_LIBC_SRC_TERMIOS_TCDRAIN_H
 
 #include "src/__support/macros/config.h"
-#include <termios.h>
 
 namespace LIBC_NAMESPACE_DECL {
 
diff --git a/libc/src/termios/tcflow.h b/libc/src/termios/tcflow.h
index dbe63adea1ceb..4b101b1ed1696 100644
--- a/libc/src/termios/tcflow.h
+++ b/libc/src/termios/tcflow.h
@@ -10,7 +10,6 @@
 #define LLVM_LIBC_SRC_TERMIOS_TCFLOW_H
 
 #include "src/__support/macros/config.h"
-#include <termios.h>
 
 namespace LIBC_NAMESPACE_DECL {
 
diff --git a/libc/src/termios/tcflush.h b/libc/src/termios/tcflush.h
index 5c283a0203c89..7f92b9ab1a6d1 100644
--- a/libc/src/termios/tcflush.h
+++ b/libc/src/termios/tcflush.h
@@ -10,7 +10,6 @@
 #define LLVM_LIBC_SRC_TERMIOS_TCFLUSH_H
 
 #include "src/__support/macros/config.h"
-#include <termios.h>
 
 namespace LIBC_NAMESPACE_DECL {
 
diff --git a/libc/src/termios/tcgetsid.h b/libc/src/termios/tcgetsid.h
index a69db00a5dab0..0d8de57668ae4 100644
--- a/libc/src/termios/tcgetsid.h
+++ b/libc/src/termios/tcgetsid.h
@@ -9,8 +9,8 @@
 #ifndef LLVM_LIBC_SRC_TERMIOS_TCGETSID_H
 #define LLVM_LIBC_SRC_TERMIOS_TCGETSID_H
 
+#include "hdr/types/pid_t.h"
 #include "src/__support/macros/config.h"
-#include <termios.h>
 
 namespace LIBC_NAMESPACE_DECL {
 
diff --git a/libc/src/termios/tcsendbreak.h b/libc/src/termios/tcsendbreak.h
index 9edc602cff6c5..bbf540e89e9ab 100644
--- a/libc/src/termios/tcsendbreak.h
+++ b/libc/src/termios/tcsendbreak.h
@@ -10,7 +10,6 @@
 #define LLVM_LIBC_SRC_TERMIOS_TCSENDBREAK_H
 
 #include "src/__support/macros/config.h"
-#include <termios.h>
 
 namespace LIBC_NAMESPACE_DECL {
 

>From 068213980a48f33498658edab75aefd595a3f560 Mon Sep 17 00:00:00 2001
From: Tue Ly <lntue.h at gmail.com>
Date: Tue, 21 Jul 2026 21:15:48 +0000
Subject: [PATCH 4/6] Fix missing include paths and termios macro header.

---
 libc/CMakeLists.txt                                 |  3 +++
 libc/cmake/modules/LLVMLibCCompileOptionRules.cmake | 12 +++++++-----
 libc/include/llvm-libc-types/CMakeLists.txt         |  2 +-
 libc/include/llvm-libc-types/struct_termios.h       |  1 +
 4 files changed, 12 insertions(+), 6 deletions(-)

diff --git a/libc/CMakeLists.txt b/libc/CMakeLists.txt
index af820ba6ab6c0..e7d012ecdeeb3 100644
--- a/libc/CMakeLists.txt
+++ b/libc/CMakeLists.txt
@@ -162,6 +162,9 @@ option(LIBC_CONFIG_PATH "The path to user provided folder that configures the bu
 
 if(LIBC_TARGET_OS_IS_LINUX)
   set(kernel_headers "/usr/include")
+  if(CMAKE_LIBRARY_ARCHITECTURE AND EXISTS "/usr/include/${CMAKE_LIBRARY_ARCHITECTURE}")
+    list(APPEND kernel_headers "/usr/include/${CMAKE_LIBRARY_ARCHITECTURE}")
+  endif()
 endif()
 set(LIBC_KERNEL_HEADERS "${kernel_headers}" CACHE STRING "Path to Linux kernel headers")
 
diff --git a/libc/cmake/modules/LLVMLibCCompileOptionRules.cmake b/libc/cmake/modules/LLVMLibCCompileOptionRules.cmake
index c97f906cb663b..600df74f552b0 100644
--- a/libc/cmake/modules/LLVMLibCCompileOptionRules.cmake
+++ b/libc/cmake/modules/LLVMLibCCompileOptionRules.cmake
@@ -243,11 +243,13 @@ function(_get_common_compile_options output_var flags)
          (LIBC_CC_SUPPORTS_NOSTDLIBINC OR COMPILER_RESOURCE_DIR))
         # We use -idirafter to avoid preempting libc's own headers in case the
         # directory (e.g. /usr/include) contains other headers.
-        if(CMAKE_CROSSCOMPILING)
-          list(APPEND compile_options "-idirafter=${LIBC_KERNEL_HEADERS}")
-        else()
-          list(APPEND compile_options "-idirafter${LIBC_KERNEL_HEADERS}")
-        endif()
+        foreach(path IN LISTS LIBC_KERNEL_HEADERS)
+          if(CMAKE_CROSSCOMPILING)
+            list(APPEND compile_options "-idirafter=${path}")
+          else()
+            list(APPEND compile_options "-idirafter${path}")
+          endif()
+        endforeach()
       endif()
     endif()
 
diff --git a/libc/include/llvm-libc-types/CMakeLists.txt b/libc/include/llvm-libc-types/CMakeLists.txt
index 3cb64260aaa6c..1d5db0be56099 100644
--- a/libc/include/llvm-libc-types/CMakeLists.txt
+++ b/libc/include/llvm-libc-types/CMakeLists.txt
@@ -189,7 +189,7 @@ add_header(tss_dtor_t HDR tss_dtor_t.h)
 add_header(__atexithandler_t HDR __atexithandler_t.h)
 add_header(speed_t HDR speed_t.h)
 add_header(tcflag_t HDR tcflag_t.h)
-add_header(struct_termios HDR struct_termios.h DEPENDS .cc_t .speed_t .tcflag_t)
+add_header(struct_termios HDR struct_termios.h DEPENDS .cc_t .speed_t .tcflag_t libc.include.llvm-libc-macros.termios_macros)
 add_header(struct_winsize HDR struct_winsize.h)
 add_header(__getoptargv_t HDR __getoptargv_t.h)
 add_header(wchar_t HDR wchar_t.h)
diff --git a/libc/include/llvm-libc-types/struct_termios.h b/libc/include/llvm-libc-types/struct_termios.h
index e3c5f2809e439..800232069fcea 100644
--- a/libc/include/llvm-libc-types/struct_termios.h
+++ b/libc/include/llvm-libc-types/struct_termios.h
@@ -12,6 +12,7 @@
 #include "cc_t.h"
 #include "speed_t.h"
 #include "tcflag_t.h"
+#include "../llvm-libc-macros/termios-macros.h"
 
 struct termios {
   tcflag_t c_iflag; // Input mode flags

>From 78ba517b962af4728991c9a3becfb6aacdb10e02 Mon Sep 17 00:00:00 2001
From: Tue Ly <lntue.h at gmail.com>
Date: Tue, 21 Jul 2026 21:23:55 +0000
Subject: [PATCH 5/6] Run clang-format.

---
 libc/include/llvm-libc-types/struct_termios.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libc/include/llvm-libc-types/struct_termios.h b/libc/include/llvm-libc-types/struct_termios.h
index 800232069fcea..9fc820d0b1ccb 100644
--- a/libc/include/llvm-libc-types/struct_termios.h
+++ b/libc/include/llvm-libc-types/struct_termios.h
@@ -9,10 +9,10 @@
 #ifndef __LLVM_LIBC_TYPES_STRUCT_TERMIOS_H__
 #define __LLVM_LIBC_TYPES_STRUCT_TERMIOS_H__
 
+#include "../llvm-libc-macros/termios-macros.h"
 #include "cc_t.h"
 #include "speed_t.h"
 #include "tcflag_t.h"
-#include "../llvm-libc-macros/termios-macros.h"
 
 struct termios {
   tcflag_t c_iflag; // Input mode flags

>From 167022ca1c5b9ca67da4f6860d722b5631e7241f Mon Sep 17 00:00:00 2001
From: Tue Ly <lntue.h at gmail.com>
Date: Thu, 23 Jul 2026 21:46:17 +0000
Subject: [PATCH 6/6] Address comments.

---
 libc/src/termios/linux/cfsetispeed.cpp | 1 -
 libc/src/termios/linux/speed_utils.h   | 4 +++-
 libc/test/src/termios/termios_test.cpp | 6 ++++++
 3 files changed, 9 insertions(+), 2 deletions(-)

diff --git a/libc/src/termios/linux/cfsetispeed.cpp b/libc/src/termios/linux/cfsetispeed.cpp
index 0b9b6e440bc8c..d953e25427d98 100644
--- a/libc/src/termios/linux/cfsetispeed.cpp
+++ b/libc/src/termios/linux/cfsetispeed.cpp
@@ -30,7 +30,6 @@ LLVM_LIBC_FUNCTION(int, cfsetispeed, (termios * t, speed_t speed)) {
     return -1;
   }
 
-  t->c_cflag = (t->c_cflag & NOT_SPEED_MASK) | encoded;
   t->c_ispeed = speed;
   return 0;
 }
diff --git a/libc/src/termios/linux/speed_utils.h b/libc/src/termios/linux/speed_utils.h
index cc0d76e2cc3b5..5b731a00825ef 100644
--- a/libc/src/termios/linux/speed_utils.h
+++ b/libc/src/termios/linux/speed_utils.h
@@ -20,6 +20,8 @@
 
 namespace LIBC_NAMESPACE_DECL {
 
+constexpr speed_t INVALID_SPEED = ~static_cast<speed_t>(0);
+
 // glibc 2.42 changed the behavior of the termios Bxxx constants:
 // - Before glibc 2.42, the Bxxx constants correspond to the kernel speed
 // bitmasks
@@ -104,7 +106,7 @@ LIBC_INLINE constexpr speed_t encode_speed(speed_t speed) {
   case 4000000:
     return 0010017;
   default:
-    return speed;
+    return INVALID_SPEED;
   }
 }
 
diff --git a/libc/test/src/termios/termios_test.cpp b/libc/test/src/termios/termios_test.cpp
index ac484e6874b0f..1af2953b1a8ef 100644
--- a/libc/test/src/termios/termios_test.cpp
+++ b/libc/test/src/termios/termios_test.cpp
@@ -43,6 +43,12 @@ TEST_F(LlvmLibcTermiosTest, SpeedSmokeTest) {
 
   ASSERT_THAT(LIBC_NAMESPACE::cfsetispeed(&t, ~CBAUD), Fails(EINVAL));
   ASSERT_THAT(LIBC_NAMESPACE::cfsetospeed(&t, ~CBAUD), Fails(EINVAL));
+  ASSERT_THAT(LIBC_NAMESPACE::cfsetispeed(&t, 4096), Fails(EINVAL));
+  ASSERT_THAT(LIBC_NAMESPACE::cfsetospeed(&t, 4096), Fails(EINVAL));
+#if B50 != 1
+  ASSERT_THAT(LIBC_NAMESPACE::cfsetispeed(&t, 1), Fails(EINVAL));
+  ASSERT_THAT(LIBC_NAMESPACE::cfsetospeed(&t, 1), Fails(EINVAL));
+#endif
 }
 
 TEST_F(LlvmLibcTermiosTest, GetAttrSmokeTest) {



More information about the libc-commits mailing list