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" 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 --- 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 is sane], [sp_cv_termios_sane], [ + AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ +#include +#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 " 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, [ speeds are sane])], +[ # Check for specific termios structures. AC_CHECK_TYPES([struct termios2],,, [[#include ]]) @@ -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 ]]) +AC_CHECK_DECLS([BOTHER],,, [[#include ]])]) # Check for serial_struct. AC_CHECK_TYPES([struct serial_struct],,, [[#include ]]) 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 #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