https://bugs.gentoo.org/962830
https://sourceware.org/bugzilla/show_bug.cgi?id=33340#c24

From f051e30dff057a7304a6c94a942da9a09740851d Mon Sep 17 00:00:00 2001
From: "H. Peter Anvin" <hpa@zytor.com>
Date: Wed, 10 Sep 2025 18:01:48 -0700
Subject: [PATCH 3/3] termios: check to see if termios speed_t is a direct map
 to baud

If termios speed_t is a direct mapping to bauds, it is presumably safe
to assume that it can be used as a generic interface.  This applies to
Linux with glibc 2.42+, GNU Hurd, and at least some BSDs.

Try to detect this case and if so, do the simple thing.

Signed-off-by: H. Peter Anvin <hpa@zytor.com>
---
 configure.ac             | 56 +++++++++++++++++++++++++++++++++++++++-
 libserialport_internal.h |  7 +++--
 linux_termios.c          | 12 ++++++---
 serialport.c             | 19 +++++++++++++-
 4 files changed, 86 insertions(+), 8 deletions(-)

diff --git a/configure.ac b/configure.ac
index d71833fea141..ae6406e5e08b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -111,6 +111,60 @@ AC_SYS_LARGEFILE
 # Define size_t if not defined as standard.
 AC_TYPE_SIZE_T
 
+# Check to see if the baud rates in termios.h seem sane.
+AC_CACHE_CHECK([if <termios.h> is sane], [sp_cv_termios_sane], [
+	AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
+#include <termios.h>
+#if (!defined(B0) || B0 == 0) \
+ && (!defined(B50) || B50 == 50) \
+ && (!defined(B75) || B75 == 75) \
+ && (!defined(B110) || B110 == 110) \
+ && (!defined(B134) || B134 == 134) \
+ && (!defined(B150) || B150 == 150) \
+ && (!defined(B200) || B200 == 200) \
+ && (!defined(B300) || B300 == 300) \
+ && (!defined(B600) || B600 == 600) \
+ && (!defined(B1200) || B1200 == 1200) \
+ && (!defined(B1800) || B1800 == 1800) \
+ && (!defined(B2400) || B2400 == 2400) \
+ && (!defined(B4800) || B4800 == 4800) \
+ && (!defined(B7200) || B7200 == 7200) \
+ && (!defined(B9600) || B9600 == 9600) \
+ && (!defined(B14400) || B14400 == 14400) \
+ && (!defined(B19200) || B19200 == 19200) \
+ && (!defined(B28800) || B28800 == 28800) \
+ && (!defined(B33600) || B33600 == 33600) \
+ && (!defined(B38400) || B38400 == 38400) \
+ && (!defined(B57600) || B57600 == 57600) \
+ && (!defined(B76800) || B76800 == 76800) \
+ && (!defined(B115200) || B115200 == 115200) \
+ && (!defined(B153600) || B153600 == 153600) \
+ && (!defined(B230400) || B230400 == 230400) \
+ && (!defined(B307200) || B307200 == 307200) \
+ && (!defined(B460800) || B460800 == 460800) \
+ && (!defined(B500000) || B500000 == 500000) \
+ && (!defined(B576000) || B576000 == 576000) \
+ && (!defined(B614400) || B614400 == 614400) \
+ && (!defined(B921600) || B921600 == 921600) \
+ && (!defined(B1000000) || B1000000 == 1000000) \
+ && (!defined(B1152000) || B1152000 == 1152000) \
+ && (!defined(B1500000) || B1500000 == 1500000) \
+ && (!defined(B2000000) || B2000000 == 2000000) \
+ && (!defined(B2500000) || B2500000 == 2500000) \
+ && (!defined(B3000000) || B3000000 == 3000000) \
+ && (!defined(B3500000) || B3500000 == 3500000) \
+ && (!defined(B4000000) || B4000000 == 4000000) \
+ && (!defined(B5000000) || B5000000 == 5000000) \
+ && (!defined(B10000000) || B10000000 == 10000000)
+#  define TERMIOS_SPEED_T_SANE 1
+#else
+# error "<termios.h> uses stupid constants"
+#endif
+]])], [sp_cv_termios_sane=yes], [sp_cv_termios_sane=no])])
+
+AS_IF([test x$sp_cv_termios_sane = xyes],
+[AC_DEFINE(HAVE_SANE_TERMIOS, 1, [<termios.h> speeds are sane])],
+[
 # Check for specific termios structures.
 AC_CHECK_TYPES([struct termios2],,,
 	[[#include <linux/termios.h>]])
@@ -121,7 +175,7 @@ AC_CHECK_MEMBERS([struct termios.c_ispeed, struct termios.c_ospeed,
 # Check for the BOTHER definition, needed for setting arbitrary baud rates.
 # We can't just #ifdef BOTHER in the code, because of the separation between
 # code using libc headers and code using kernel termios.h headers.
-AC_CHECK_DECLS([BOTHER],,, [[#include <linux/termios.h>]])
+AC_CHECK_DECLS([BOTHER],,, [[#include <linux/termios.h>]])])
 
 # Check for serial_struct.
 AC_CHECK_TYPES([struct serial_struct],,, [[#include <linux/serial.h>]])
diff --git a/libserialport_internal.h b/libserialport_internal.h
index 57346d653ad3..88bb9f60b06b 100644
--- a/libserialport_internal.h
+++ b/libserialport_internal.h
@@ -130,8 +130,11 @@
 #endif
 
 /* Non-standard baudrates are not available everywhere. */
-#if (defined(HAVE_TERMIOS_SPEED) || defined(HAVE_TERMIOS2_SPEED)) && HAVE_DECL_BOTHER
-#define USE_TERMIOS_SPEED
+#ifdef HAVE_SANE_TERMIOS
+/* Directly supported by termios */
+# undef USE_TERMIOS_SPEED
+#elif (defined(HAVE_TERMIOS_SPEED) || defined(HAVE_TERMIOS2_SPEED)) && HAVE_DECL_BOTHER
+# define USE_TERMIOS_SPEED
 #endif
 
 struct sp_port {
diff --git a/linux_termios.c b/linux_termios.c
index 0dd0b105726f..dad0e9cb4208 100644
--- a/linux_termios.c
+++ b/linux_termios.c
@@ -18,10 +18,10 @@
  */
 
 /*
- * At the time of writing, glibc does not support the Linux kernel interfaces
- * for setting non-standard baud rates and flow control. We therefore have to
- * prepare the correct ioctls ourselves, for which we need the declarations in
- * linux/termios.h.
+ * glibc before version 2.42 does not support the Linux kernel
+ * interfaces for setting non-standard baud rates and flow control. We
+ * therefore have to prepare the correct ioctls ourselves, for which
+ * we need the declarations in linux/termios.h.
  *
  * We can't include linux/termios.h in serialport.c however, because its
  * contents conflict with the termios.h provided by glibc. So this file exists
@@ -38,6 +38,8 @@
 #include <linux/termios.h>
 #include "linux_termios.h"
 
+#ifndef HAVE_SANE_TERMIOS
+
 SP_PRIV unsigned long get_termios_get_ioctl(void)
 {
 #ifdef HAVE_STRUCT_TERMIOS2
@@ -127,3 +129,5 @@ SP_PRIV void set_termiox_flow(void *data, int rts, int cts, int dtr, int dsr)
 		termx->x_cflag |= DSRXON;
 }
 #endif
+
+#endif
diff --git a/serialport.c b/serialport.c
index 392ec61e95f2..f1279cff87ca 100644
--- a/serialport.c
+++ b/serialport.c
@@ -23,6 +23,7 @@
 
 #include "libserialport_internal.h"
 
+#ifndef HAVE_SANE_TERMIOS
 static const struct std_baudrate std_baudrates[] = {
 #ifdef _WIN32
 	/*
@@ -42,8 +43,8 @@ static const struct std_baudrate std_baudrates[] = {
 #endif
 #endif
 };
-
 #define NUM_STD_BAUDRATES ARRAY_SIZE(std_baudrates)
+#endif
 
 void (*sp_debug_handler)(const char *format, ...) = sp_default_debug_handler;
 
@@ -1692,7 +1693,9 @@ static enum sp_return set_flow(int fd, struct port_data *data)
 static enum sp_return get_config(struct sp_port *port, struct port_data *data,
 	struct sp_port_config *config)
 {
+#ifndef HAVE_SANE_TERMIOS
 	unsigned int i;
+#endif
 
 	TRACE("%p, %p, %p", port, data, config);
 
@@ -1811,6 +1814,9 @@ static enum sp_return get_config(struct sp_port *port, struct port_data *data,
 	data->termiox_supported = 0;
 #endif
 
+#ifdef HAVE_SANE_TERMIOS
+	config->baudrate = cfgetospeed(&data->term);
+#else
 	for (i = 0; i < NUM_STD_BAUDRATES; i++) {
 		if (cfgetospeed(&data->term) == std_baudrates[i].index) {
 			config->baudrate = std_baudrates[i].value;
@@ -1827,6 +1833,7 @@ static enum sp_return get_config(struct sp_port *port, struct port_data *data,
 		config->baudrate = -1;
 #endif
 	}
+#endif
 
 	switch (data->term.c_cflag & CSIZE) {
 	case CS8:
@@ -1898,7 +1905,10 @@ static enum sp_return get_config(struct sp_port *port, struct port_data *data,
 static enum sp_return set_config(struct sp_port *port, struct port_data *data,
 	const struct sp_port_config *config)
 {
+#ifndef HAVE_SANE_TERMIOS
 	unsigned int i;
+#endif
+
 #ifdef __APPLE__
 	BAUD_TYPE baud_nonstd;
 
@@ -2064,6 +2074,12 @@ static enum sp_return set_config(struct sp_port *port, struct port_data *data,
 	int controlbits;
 
 	if (config->baudrate >= 0) {
+#ifdef HAVE_SANE_TERMIOS
+		if (cfsetospeed(&data->term, config->baudrate) < 0)
+			RETURN_FAIL("cfsetospeed() failed");
+		if (cfsetispeed(&data->term, config->baudrate) < 0)
+			RETURN_FAIL("cfsetispeed() failed");
+#else
 		for (i = 0; i < NUM_STD_BAUDRATES; i++) {
 			if (config->baudrate == std_baudrates[i].value) {
 				if (cfsetospeed(&data->term, std_baudrates[i].index) < 0)
@@ -2088,6 +2104,7 @@ static enum sp_return set_config(struct sp_port *port, struct port_data *data,
 			RETURN_ERROR(SP_ERR_SUPP, "Non-standard baudrate not supported");
 #endif
 		}
+#endif
 	}
 
 	if (config->bits >= 0) {
-- 
2.51.0
