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