1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
|
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
|