From b21711c71f2bf9537f5985339cf6e224738315ef Mon Sep 17 00:00:00 2001 From: pali <7141871+pali@users.noreply.github.com> Date: Mon, 27 Sep 2021 09:10:31 +0200 Subject: [PATCH] pppd: Fix usage of BOTHER ioctl API on Linux (#314) MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Linux architectures have different content of struct termios2 and also different value of BOTHER macro. So do not declare any struct termios2 nor BOTHER macro. Current definitions in ppp were applicable only for x86. Correct definitions for current architecture are only in and header files. But Linux header file is in conflict with glibc header file and only one can be included in one source unit. Moreover both header files contains struct termios but with different content. So it is not possible to use glibc tc* functions with definitions. For this reason provide a new include header file "termios_linux.h" which provides custom implementation of all glibc's termios.h functions via Linux ioctl() interface with definitions from Linux header file. Thus this "termios_linux.h" is replacement for with additional support for BOTHER Linux termios API. Same "termios_linux.h" is going to be used by U-Boot's kwboot utility for the same reason to use arbitrary baudrate value via BOTHER ioctl API. Hopefully one day glibc will provide some API functions for functionality provided currently by BOTHER Linux API. Signed-off-by: Pali Rohár --- pppd/Makefile.am | 2 +- pppd/sys-linux.c | 71 +++++++--------- pppd/termios_linux.h | 194 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 224 insertions(+), 43 deletions(-) create mode 100644 pppd/termios_linux.h diff --git a/pppd/Makefile.am b/pppd/Makefile.am index 2244507..03c6fd4 100644 --- a/pppd/Makefile.am +++ b/pppd/Makefile.am @@ -72,7 +72,7 @@ pppd_LDFLAGS = pppd_LIBS = if LINUX -pppd_SOURCES += sys-linux.c +pppd_SOURCES += sys-linux.c termios_linux.h pppd_LIBS += $(CRYPT_LIBS) $(UTIL_LIBS) endif diff --git a/pppd/sys-linux.c b/pppd/sys-linux.c index e1bb810..513fc3d 100644 --- a/pppd/sys-linux.c +++ b/pppd/sys-linux.c @@ -96,7 +96,6 @@ #include #include #include -#include #include /* This is in netdevice.h. However, this compile will fail miserably if @@ -110,7 +109,6 @@ #if !defined(__GLIBC__) || __GLIBC__ >= 2 #include /* glibc 2 conflicts with linux/types.h */ -#include #include #include #include @@ -165,19 +163,11 @@ #include #endif -#ifndef BOTHER -#define BOTHER 0010000 -#endif -struct termios2 { - unsigned int c_iflag; - unsigned int c_oflag; - unsigned int c_cflag; - unsigned int c_lflag; - unsigned char c_line; - unsigned char c_cc[19]; - unsigned int c_ispeed; - unsigned int c_ospeed; -}; +/* + * Instead of system header file use local "termios_linux.h" header + * file as it provides additional support for arbitrary baud rates via BOTHER. + */ +#include "termios_linux.h" #ifdef INET6 #ifndef _LINUX_IN6_H @@ -1053,40 +1043,32 @@ void set_up_tty(int tty_fd, int local) cfsetospeed (&tios, speed); cfsetispeed (&tios, speed); speed = cfgetospeed(&tios); + baud_rate = baud_rate_of(speed); + } else { +#ifdef BOTHER + tios.c_cflag &= ~CBAUD; + tios.c_cflag |= BOTHER; + tios.c_ospeed = inspeed; +#ifdef IBSHIFT + /* B0 sets input baudrate to the output baudrate */ + tios.c_cflag &= ~(CBAUD << IBSHIFT); + tios.c_cflag |= B0 << IBSHIFT; + tios.c_ispeed = inspeed; +#endif + baud_rate = inspeed; +#else + baud_rate = 0; +#endif } - baud_rate = baud_rate_of(speed); } else { speed = cfgetospeed(&tios); baud_rate = baud_rate_of(speed); - } - - while (tcsetattr(tty_fd, TCSAFLUSH, &tios) < 0 && !ok_error(errno)) - if (errno != EINTR) - fatal("tcsetattr: %m (line %d)", __LINE__); - restore_term = 1; - -/* Most Linux architectures and drivers support arbitrary baud rate values via BOTHER */ -#ifdef TCGETS2 - if (!baud_rate) { - struct termios2 tios2; - if (ioctl(tty_fd, TCGETS2, &tios2) == 0) { - if (inspeed) { - tios2.c_cflag &= ~CBAUD; - tios2.c_cflag |= BOTHER; - tios2.c_ispeed = inspeed; - tios2.c_ospeed = inspeed; -#ifdef TCSETS2 - if (ioctl(tty_fd, TCSETS2, &tios2) == 0) - baud_rate = inspeed; +#ifdef BOTHER + if (!baud_rate) + baud_rate = tios.c_ospeed; #endif - } else { - if ((tios2.c_cflag & CBAUD) == BOTHER && tios2.c_ospeed) - baud_rate = tios2.c_ospeed; - } - } } -#endif /* * We can't proceed if the serial port baud rate is unknown, @@ -1098,6 +1080,11 @@ void set_up_tty(int tty_fd, int local) else fatal("Baud rate for %s is 0; need explicit baud rate", devnam); } + + while (tcsetattr(tty_fd, TCSAFLUSH, &tios) < 0 && !ok_error(errno)) + if (errno != EINTR) + fatal("tcsetattr: %m (line %d)", __LINE__); + restore_term = 1; } /******************************************************************** diff --git a/pppd/termios_linux.h b/pppd/termios_linux.h new file mode 100644 index 0000000..9c79d16 --- /dev/null +++ b/pppd/termios_linux.h @@ -0,0 +1,194 @@ +/* SPDX-License-Identifier: GPL-2.0+ OR BSD-4-Clause OR BSD-3-Clause OR BSD-2-Clause */ +/* + * termios fuctions to support arbitrary baudrates (on Linux) + * + * Copyright (c) 2021 Pali Rohár + * Copyright (c) 2021 Marek Behún + */ + +#ifndef _TERMIOS_LINUX_H_ +#define _TERMIOS_LINUX_H_ + +/* + * We need to use raw TCGETS2/TCSETS2 or TCGETS/TCSETS ioctls with the BOTHER + * flag in struct termios2/termios, defined in Linux headers + * and . Since these headers conflict with glibc's header file + * , it is not possible to use libc's termios functions and we need + * to reimplement them via ioctl() calls. + * + * An arbitrary baudrate is supported when the macro BOTHER is defined. The + * baudrate value itself is then stored into the c_ospeed and c_ispeed members. + * If ioctls TCGETS2/TCSETS2 are defined and supported then these fields are + * present in struct termios2, otherwise these fields are present in struct + * termios. + * + * Note that the Bnnn constants from need not be compatible with + * Bnnn constants from . + */ + +#include +#include +#include +#include +#include + +#if defined(BOTHER) && defined(TCGETS2) +#define termios termios2 +#endif + +static inline int tcgetattr(int fd, struct termios *t) +{ +#if defined(BOTHER) && defined(TCGETS2) + return ioctl(fd, TCGETS2, t); +#else + return ioctl(fd, TCGETS, t); +#endif +} + +static inline int tcsetattr(int fd, int a, const struct termios *t) +{ + int cmd; + + switch (a) { +#if defined(BOTHER) && defined(TCGETS2) + case TCSANOW: + cmd = TCSETS2; + break; + case TCSADRAIN: + cmd = TCSETSW2; + break; + case TCSAFLUSH: + cmd = TCSETSF2; + break; +#else + case TCSANOW: + cmd = TCSETS; + break; + case TCSADRAIN: + cmd = TCSETSW; + break; + case TCSAFLUSH: + cmd = TCSETSF; + break; +#endif + default: + errno = EINVAL; + return -1; + } + + return ioctl(fd, cmd, t); +} + +static inline int tcdrain(int fd) +{ + return ioctl(fd, TCSBRK, 1); +} + +static inline int tcflush(int fd, int q) +{ + return ioctl(fd, TCFLSH, q); +} + +static inline int tcsendbreak(int fd, int d) +{ +#ifdef TCSBRKP + return ioctl(fd, TCSBRKP, d); +#else + return ioctl(fd, TCSBRK, 0); +#endif +} + +static inline int tcflow(int fd, int a) +{ + return ioctl(fd, TCXONC, a); +} + +static inline pid_t tcgetsid(int fd) +{ + pid_t sid; + + if (ioctl(fd, TIOCGSID, &sid) < 0) + return (pid_t)-1; + + return sid; +} + +static inline speed_t cfgetospeed(const struct termios *t) +{ + return t->c_cflag & CBAUD; +} + +static inline int cfsetospeed(struct termios *t, speed_t s) +{ + if (s & ~CBAUD) { + errno = EINVAL; + return -1; + } + + t->c_cflag &= ~CBAUD; + t->c_cflag |= s; + + return 0; +} + +#ifdef IBSHIFT +static inline speed_t cfgetispeed(const struct termios *t) +{ + speed_t s = (t->c_cflag >> IBSHIFT) & CBAUD; + + if (s == B0) + return cfgetospeed(t); + else + return s; +} + +static inline int cfsetispeed(struct termios *t, speed_t s) +{ + if (s == 0) + s = B0; + + if (s & ~CBAUD) { + errno = EINVAL; + return -1; + } + + t->c_cflag &= ~(CBAUD << IBSHIFT); + t->c_cflag |= s << IBSHIFT; + + return 0; +} +#else /* !IBSHIFT */ +static inline speed_t cfgetispeed(const struct termios *t) +{ + return cfgetospeed(t); +} + +static inline int cfsetispeed(struct termios *t, speed_t s) +{ + return cfsetospeed(t, s); +} +#endif /* !IBSHIFT */ + +static inline int cfsetspeed(struct termios *t, speed_t s) +{ + if (cfsetospeed(t, s)) + return -1; +#ifdef IBSHIFT + if (cfsetispeed(t, s)) + return -1; +#endif + + return 0; +} + +static void cfmakeraw(struct termios *t) +{ + t->c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | + ICRNL | IXON); + t->c_oflag &= ~OPOST; + t->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); + t->c_cflag &= ~(CSIZE | PARENB); + t->c_cflag |= CS8; +} + +#endif /* _TERMIOS_LINUX_H_ */ -- 2.39.2