Home | History | Annotate | Line # | Download | only in pppd
      1 /* SPDX-License-Identifier: GPL-2.0+ OR BSD-4-Clause OR BSD-3-Clause OR BSD-2-Clause */
      2 /*
      3  * termios fuctions to support arbitrary baudrates (on Linux)
      4  *
      5  * Copyright (c) 2021 Pali Rohr <pali (at) kernel.org>
      6  * Copyright (c) 2021 Marek Behn <kabel (at) kernel.org>
      7  */
      8 
      9 #ifndef PPP_TERMIOS_LINUX_H
     10 #define PPP_TERMIOS_LINUX_H
     11 
     12 #include "pppdconf.h"
     13 
     14 /*
     15  * We need to use raw TCGETS2/TCSETS2 or TCGETS/TCSETS ioctls with the BOTHER
     16  * flag in struct termios2/termios, defined in Linux headers <asm/ioctls.h>
     17  * and <asm/termbits.h>. Since these headers conflict with glibc's header file
     18  * <termios.h>, it is not possible to use libc's termios functions and we need
     19  * to reimplement them via ioctl() calls.
     20  *
     21  * An arbitrary baudrate is supported when the macro BOTHER is defined. The
     22  * baudrate value itself is then stored into the c_ospeed and c_ispeed members.
     23  * If ioctls TCGETS2/TCSETS2 are defined and supported then these fields are
     24  * present in struct termios2, otherwise these fields are present in struct
     25  * termios.
     26  *
     27  * Note that the Bnnn constants from <termios.h> need not be compatible with
     28  * Bnnn constants from <asm/termbits.h>.
     29  */
     30 
     31 #include <errno.h>
     32 #include <sys/ioctl.h>
     33 #include <sys/types.h>
     34 #include <asm/ioctls.h>
     35 #include <asm/termbits.h>
     36 
     37 #if defined(BOTHER) && defined(TCGETS2)
     38 #define termios termios2
     39 #endif
     40 
     41 static inline int tcgetattr(int fd, struct termios *t)
     42 {
     43 #if defined(BOTHER) && defined(TCGETS2)
     44 	return ioctl(fd, TCGETS2, t);
     45 #else
     46 	return ioctl(fd, TCGETS, t);
     47 #endif
     48 }
     49 
     50 static inline int tcsetattr(int fd, int a, const struct termios *t)
     51 {
     52 	int cmd;
     53 
     54 	switch (a) {
     55 #if defined(BOTHER) && defined(TCGETS2)
     56 	case TCSANOW:
     57 		cmd = TCSETS2;
     58 		break;
     59 	case TCSADRAIN:
     60 		cmd = TCSETSW2;
     61 		break;
     62 	case TCSAFLUSH:
     63 		cmd = TCSETSF2;
     64 		break;
     65 #else
     66 	case TCSANOW:
     67 		cmd = TCSETS;
     68 		break;
     69 	case TCSADRAIN:
     70 		cmd = TCSETSW;
     71 		break;
     72 	case TCSAFLUSH:
     73 		cmd = TCSETSF;
     74 		break;
     75 #endif
     76 	default:
     77 		errno = EINVAL;
     78 		return -1;
     79 	}
     80 
     81 	return ioctl(fd, cmd, t);
     82 }
     83 
     84 static inline int tcdrain(int fd)
     85 {
     86 	return ioctl(fd, TCSBRK, 1);
     87 }
     88 
     89 static inline int tcflush(int fd, int q)
     90 {
     91 	return ioctl(fd, TCFLSH, q);
     92 }
     93 
     94 static inline int tcsendbreak(int fd, int d)
     95 {
     96 #ifdef TCSBRKP
     97 	return ioctl(fd, TCSBRKP, d);
     98 #else
     99 	return ioctl(fd, TCSBRK, 0);
    100 #endif
    101 }
    102 
    103 static inline int tcflow(int fd, int a)
    104 {
    105 	return ioctl(fd, TCXONC, a);
    106 }
    107 
    108 static inline pid_t tcgetsid(int fd)
    109 {
    110 	pid_t sid;
    111 
    112 	if (ioctl(fd, TIOCGSID, &sid) < 0)
    113 		return (pid_t)-1;
    114 
    115 	return sid;
    116 }
    117 
    118 static inline speed_t cfgetospeed(const struct termios *t)
    119 {
    120 	return t->c_cflag & CBAUD;
    121 }
    122 
    123 static inline int cfsetospeed(struct termios *t, speed_t s)
    124 {
    125 	if (s & ~CBAUD) {
    126 		errno = EINVAL;
    127 		return -1;
    128 	}
    129 
    130 	t->c_cflag &= ~CBAUD;
    131 	t->c_cflag |= s;
    132 
    133 	return 0;
    134 }
    135 
    136 #ifdef IBSHIFT
    137 static inline speed_t cfgetispeed(const struct termios *t)
    138 {
    139 	speed_t s = (t->c_cflag >> IBSHIFT) & CBAUD;
    140 
    141 	if (s == B0)
    142 		return cfgetospeed(t);
    143 	else
    144 		return s;
    145 }
    146 
    147 static inline int cfsetispeed(struct termios *t, speed_t s)
    148 {
    149 	if (s == 0)
    150 		s = B0;
    151 
    152 	if (s & ~CBAUD) {
    153 		errno = EINVAL;
    154 		return -1;
    155 	}
    156 
    157 	t->c_cflag &= ~(CBAUD << IBSHIFT);
    158 	t->c_cflag |= s << IBSHIFT;
    159 
    160 	return 0;
    161 }
    162 #else /* !IBSHIFT */
    163 static inline speed_t cfgetispeed(const struct termios *t)
    164 {
    165 	return cfgetospeed(t);
    166 }
    167 
    168 static inline int cfsetispeed(struct termios *t, speed_t s)
    169 {
    170 	return cfsetospeed(t, s);
    171 }
    172 #endif /* !IBSHIFT */
    173 
    174 static inline int cfsetspeed(struct termios *t, speed_t s)
    175 {
    176 	if (cfsetospeed(t, s))
    177 		return -1;
    178 #ifdef IBSHIFT
    179 	if (cfsetispeed(t, s))
    180 		return -1;
    181 #endif
    182 
    183 	return 0;
    184 }
    185 
    186 static void cfmakeraw(struct termios *t)
    187 {
    188 	t->c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR |
    189 			ICRNL | IXON);
    190 	t->c_oflag &= ~OPOST;
    191 	t->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
    192 	t->c_cflag &= ~(CSIZE | PARENB);
    193 	t->c_cflag |= CS8;
    194 }
    195 
    196 #endif /* PPP_TERMIOS_LINUX_H */
    197