aboutsummaryrefslogtreecommitdiff
path: root/sysdeps/unix/sysv/linux/speed.c
blob: 4efb0ded59bafe02a6108abccc9e97ec5430eea2 (plain)
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
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
/* `struct termios' speed frobnication functions.  Linux version.
   Copyright (C) 1991-2025 Free Software Foundation, Inc.
   This file is part of the GNU C Library.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   The GNU C Library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, see
   <https://www.gnu.org/licenses/>.  */

#include <termios_internals.h>

/* Conversions between legacy c_cflag fields and actual baud rates */

/* These expressions may seem complicated; the _cbix() macro
   compresses the CBAUD field into an index in the range 0-31. On most
   Linux platforms, the CBAUD field is 5 bits, but the topmost bit
   indicated by CBAUDEX, is discontinous with the rest.

   The resulting masks look like:

		Alpha		PowerPC		others

   CBAUD	0x001f		0x00ff		0x100f
   CBAUDEX	0x0000		0x0010		0x1000

   LOWCBAUD	0x001f		0x000f		0x000f
   CBAUDMASK	0x001f		0x001f		0x100f

   CBAUDMASK is used to test for invalid values passed to the
   compatibility functions or in termios::c_cflag on PowerPC.

   The divide-multiply sequence in the _cbix() macro gets converted
   to shift and masks as necessary by the compiler. */

#define LOWCBAUD (CBAUD & (CBAUDEX-1))
#define _cbix(x) (((x) & LOWCBAUD) | \
		  (CBAUDEX ? ((x) & CBAUDEX)/CBAUDEX * (LOWCBAUD+1) : 0))
#define CBAUDMASK (LOWCBAUD | CBAUDEX)

/* Compile time sanity checks for broken CBAUD or CIBAUD definitions */
#if CIBAUD != (CBAUD << IBSHIFT)
# error "CIBAUD should == CBAUD << IBSHIFT"
#elif CBAUDEX & (CBAUDEX-1)
# error "CBAUDEX should either be 0 or a single bit"
#elif !(CBAUD & 1)
# error "The CBAUD field should start at bit 0"
#elif CBAUDEX & ~CBAUD
# error "CBAUD should include the CBAUDEX bit"
#endif

speed_t
___cbaud_to_speed (tcflag_t c_cflag, speed_t other)
{
  static const speed_t cbaudix_to_speed [] =
    {
      [0 ... _cbix(CBAUDMASK)] = -1,
      [_cbix(__B0)] = 0,
      [_cbix(__B50)] = 50,
      [_cbix(__B75)] = 75,
      [_cbix(__B110)] = 110,
      [_cbix(__B134)] = 134,
      [_cbix(__B150)] = 150,
      [_cbix(__B200)] = 200,
      [_cbix(__B300)] = 300,
      [_cbix(__B600)] = 600,
      [_cbix(__B1200)] = 1200,
      [_cbix(__B1800)] = 1800,
      [_cbix(__B2400)] = 2400,
      [_cbix(__B4800)] = 4800,
      [_cbix(__B9600)] = 9600,
      [_cbix(__B19200)] = 19200,
      [_cbix(__B38400)] = 38400,
      [_cbix(__B57600)] = 57600,
      [_cbix(__B115200)] = 115200,
      [_cbix(__B230400)] = 230400,
      [_cbix(__B460800)] = 460800,
      [_cbix(__B500000)] = 500000,
      [_cbix(__B576000)] = 576000,
      [_cbix(__B921600)] = 921600,
      [_cbix(__B1000000)] = 1000000,
      [_cbix(__B1152000)] = 1152000,
      [_cbix(__B1500000)] = 1500000,
      [_cbix(__B2000000)] = 2000000,
#ifdef __B7200
      [_cbix(__B7200)] = 7200,
#endif
#ifdef __B14400
      [_cbix(__B14400)] = 14400,
#endif
#ifdef __B28800
      [_cbix(__B28800)] = 28800,
#endif
#ifdef __B76800
      [_cbix(__B76800)] = 76800,
#endif
#ifdef __B153600
      [_cbix(__B153600)] = 153600,
#endif
#ifdef __B307200
      [_cbix(__B307200)] = 307200,
#endif
#ifdef __B614400
      [_cbix(__B614400)] = 614400,
#endif
#ifdef __B2500000
      [_cbix(__B2500000)] = 2500000,
#endif
#ifdef __B3000000
      [_cbix(__B3000000)] = 3000000,
#endif
#ifdef __B3500000
      [_cbix(__B3500000)] = 3500000,
#endif
#ifdef __B4000000
      [_cbix(__B4000000)] = 4000000,
#endif
    };
  speed_t speed;

  if (c_cflag & (tcflag_t)(~CBAUDMASK))
    return other;

  speed = cbaudix_to_speed[_cbix(c_cflag)];
  return speed == (speed_t)-1 ? other : speed;
}

tcflag_t
___speed_to_cbaud (speed_t speed)
{
  switch (speed) {
  case 0:
    return __B0;
  case 50:
    return __B50;
  case 75:
    return __B75;
  case 110:
    return __B110;
  case 134:
    return __B134;
  case 150:
    return __B150;
  case 200:
    return __B200;
  case 300:
    return __B300;
  case 600:
    return __B600;
  case 1200:
    return __B1200;
  case 1800:
    return __B1800;
  case 2400:
    return __B2400;
  case 4800:
    return __B4800;
  case 9600:
    return __B9600;
  case 19200:
    return __B19200;
  case 38400:
    return __B38400;
  case 57600:
    return __B57600;
  case 115200:
    return __B115200;
  case 230400:
    return __B230400;
  case 460800:
    return __B460800;
  case 500000:
    return __B500000;
  case 576000:
    return __B576000;
  case 921600:
    return __B921600;
  case 1000000:
    return __B1000000;
  case 1152000:
    return __B1152000;
  case 1500000:
    return __B1500000;
  case 2000000:
    return __B2000000;
#ifdef __B76800
  case 76800:
    return __B76800;
#endif
#ifdef __B153600
  case 153600:
    return __B153600;
#endif
#ifdef __B307200
  case 307200:
    return __B307200;
#endif
#ifdef __B614400
  case 614400:
    return __B614400;
#endif
#ifdef __B2500000
  case 2500000:
    return __B2500000;
#endif
#ifdef __B3000000
  case 3000000:
    return __B3000000;
#endif
#ifdef __B3500000
  case 3500000:
    return __B3500000;
#endif
#ifdef __B4000000
  case 4000000:
    return __B4000000;
#endif
  default:
    return __BOTHER;
  }
}


/* Canonicalize the representation of speed fields in a kernel
   termios2 structure.  Specifically, if there is a valid legacy cbaud
   representation (not __BOTHER), use it and propagate the
   corresponding speed value to ispeed/ospeed, otherwise the other way
   around if possible.  Finally, if the input speed is zero, copy the
   output speed to the input speed.

   The kernel doesn't do this canonicalization, which can affect
   legacy utilities, so do it here.

   This is used by tcgetattr() and tcsetattr(). */
void
___termios2_canonicalize_speeds (struct termios2 *k_termios_p)
{
  k_termios_p->c_ospeed =
      ___cbaud_to_speed (cbaud (k_termios_p->c_cflag),  k_termios_p->c_ospeed);
  k_termios_p->c_ispeed =
      ___cbaud_to_speed (cibaud (k_termios_p->c_cflag), k_termios_p->c_ispeed);

  if (!k_termios_p->c_ispeed)
    k_termios_p->c_ispeed = k_termios_p->c_ospeed;

  k_termios_p->c_cflag &= ~(CBAUD | CIBAUD);
  k_termios_p->c_cflag |= ___speed_to_cbaud (k_termios_p->c_ospeed);
  k_termios_p->c_cflag |= ___speed_to_cbaud (k_termios_p->c_ispeed) << IBSHIFT;
}


/* Return the output baud rate stored in *TERMIOS_P.  */
speed_t
__cfgetospeed (const struct termios *termios_p)
{
  return termios_p->c_ospeed;
}
libc_hidden_def (__cfgetospeed)
versioned_symbol (libc, __cfgetospeed, cfgetospeed, GLIBC_2_42);

/* Return the input baud rate stored in *TERMIOS_P.  */
speed_t
__cfgetispeed (const struct termios *termios_p)
{
  return termios_p->c_ispeed;
}
libc_hidden_def (__cfgetispeed)
versioned_symbol (libc, __cfgetispeed, cfgetispeed, GLIBC_2_42);

/* Set the output baud rate stored in *TERMIOS_P to SPEED.  */
int
__cfsetospeed (struct termios *termios_p, speed_t speed)
{
  tcflag_t cbaud = ___speed_to_cbaud (speed);

  termios_p->c_ospeed = speed;
  termios_p->c_cflag &= ~CBAUD;
  termios_p->c_cflag |= cbaud;

  return 0;
}
libc_hidden_def (__cfsetospeed)
versioned_symbol (libc, __cfsetospeed, cfsetospeed, GLIBC_2_42);

/* Set the input baud rate stored in *TERMIOS_P to SPEED. */
int
__cfsetispeed (struct termios *termios_p, speed_t speed)
{
  tcflag_t cbaud = ___speed_to_cbaud (speed);

  termios_p->c_ispeed = speed;
  termios_p->c_cflag &= ~CIBAUD;
  termios_p->c_cflag |= cbaud << IBSHIFT;

  return 0;
}
libc_hidden_def (__cfsetispeed)
versioned_symbol (libc, __cfsetispeed, cfsetispeed, GLIBC_2_42);

#if _TERMIOS_OLD_COMPAT

/* Legacy versions which returns cbaud-encoded speed_t values */

speed_t
attribute_compat_text_section
__old_cfgetospeed (const old_termios_t *termios_p)
{
  return cbaud (termios_p->c_cflag);
}
compat_symbol (libc, __old_cfgetospeed, cfgetospeed, GLIBC_2_0);

speed_t
attribute_compat_text_section
__old_cfgetispeed (const old_termios_t *termios_p)
{
  return cibaud (termios_p->c_cflag);
}
compat_symbol (libc, __old_cfgetispeed, cfgetispeed, GLIBC_2_0);

int
attribute_compat_text_section
__old_cfsetospeed (old_termios_t *termios_p, speed_t speed)
{
  speed_t real_speed = ___cbaud_to_speed (speed, -1);
  if (real_speed == (speed_t)-1)
    return INLINE_SYSCALL_ERROR_RETURN_VALUE (EINVAL);

#if !_HAVE_STRUCT_OLD_TERMIOS
  /* Otherwise this field doesn't exist in old_termios_t */
  termios_p->c_ospeed = real_speed;
#endif
  termios_p->c_cflag &= ~CBAUD;
  termios_p->c_cflag |= speed;

  return 0;
}
compat_symbol (libc, __old_cfsetospeed, cfsetospeed, GLIBC_2_0);

int
attribute_compat_text_section
__old_cfsetispeed (old_termios_t *termios_p, speed_t speed)
{
  speed_t real_speed = ___cbaud_to_speed (speed, -1);
  if (real_speed == (speed_t)-1)
    return INLINE_SYSCALL_ERROR_RETURN_VALUE (EINVAL);

#if !_HAVE_STRUCT_OLD_TERMIOS
  /* Otherwise this field doesn't exist in old_termios_t */
  termios_p->c_ispeed = real_speed;
#endif
  termios_p->c_cflag &= ~CIBAUD;
  termios_p->c_cflag |= speed << IBSHIFT;

  return 0;
}
compat_symbol (libc, __old_cfsetispeed, cfsetispeed, GLIBC_2_0);

#endif /* _TERMIOS_OLD_COMPAT */