A termios path

H.J. Lu (hjl@lucon.org)
Sun, 6 Dec 1998 11:08:37 -0800 (PST)


Hi,

This patch for glibc 2.1 works around a Linux kernel bug, which I don't
see any easy fix. Also

cfsetispeed (termios_p, (speed_t) 0)

has a special meaning. It should set the input baud rate to the output
baud rate when tcsetattr is called with termios_p. I included a kludge
here.

Thanks.

H.J. Lu (hjl@gnu.org)
----
Sat Dec 5 23:13:24 1998 H.J. Lu <hjl@gnu.org>

* sysdeps/unix/sysv/linux/bits/termios.h (IBAUD0): NEW.

* sysdeps/unix/sysv/linux/speed.c (cfgetispeed): New.
(cfsetispeed): New.

* sysdeps/unix/sysv/linux/tcsetattr.c (tcsetattr): Clear
the IBAUD0 bit in c_iflag when making the system call.
Work around a Linux kernel bug which silently changes the
PARENB/CREAD/CSIZE bits in c_cflag on pty.

Index: sysdeps/unix/sysv/linux/bits/termios.h
===================================================================
RCS file: /home/work/cvs/gnu/glibc/sysdeps/unix/sysv/linux/bits/termios.h,v
retrieving revision 1.1.1.5
diff -u -p -r1.1.1.5 termios.h
--- sysdeps/unix/sysv/linux/bits/termios.h 1998/07/15 00:23:53 1.1.1.5
+++ sysdeps/unix/sysv/linux/bits/termios.h 1998/12/06 06:58:26
@@ -73,6 +73,9 @@ struct termios
#define IXOFF 0010000
#define IMAXBEL 0020000

+/* When this bit is set, the input baud rate is the numerical 0. */
+#define IBAUD0 020000000000
+
/* c_oflag bits */
#define OPOST 0000001
#define OLCUC 0000002
Index: sysdeps/unix/sysv/linux/speed.c
===================================================================
RCS file: /home/work/cvs/gnu/glibc/sysdeps/unix/sysv/linux/speed.c,v
retrieving revision 1.1.1.2
diff -u -p -r1.1.1.2 speed.c
--- sysdeps/unix/sysv/linux/speed.c 1997/06/24 19:18:40 1.1.1.2
+++ sysdeps/unix/sysv/linux/speed.c 1998/12/06 18:54:20
@@ -31,8 +31,16 @@ cfgetospeed (termios_p)
}

/* Return the input baud rate stored in *TERMIOS_P.
- For Linux there is no difference between input and output speed. */
-strong_alias (cfgetospeed, cfgetispeed);
+ Although for Linux there is no difference between input and output
+ speed, the numerical 0 is a special case for the input baud rate. It
+ should set the input baud rate to the output baud rate. */
+speed_t
+cfgetispeed (termios_p)
+ const struct termios *termios_p;
+{
+ return (termios_p->c_iflag & IBAUD0)
+ ? 0 : termios_p->c_cflag & (CBAUD | CBAUDEX);
+}

/* Set the output baud rate stored in *TERMIOS_P to SPEED. */
int
@@ -54,5 +62,31 @@ cfsetospeed (termios_p, speed)
}

/* Set the input baud rate stored in *TERMIOS_P to SPEED.
- For Linux there is no difference between input and output speed. */
-strong_alias (cfsetospeed, cfsetispeed);
+ Although for Linux there is no difference between input and output
+ speed, the numerical 0 is a special case for the input baud rate. It
+ should set the input baud rate to the output baud rate. */
+int
+cfsetispeed (termios_p, speed)
+ struct termios *termios_p;
+ speed_t speed;
+{
+ if ((speed & ~CBAUD) != 0
+ && (speed < B57600 || speed > B460800))
+ {
+ __set_errno (EINVAL);
+ return -1;
+ }
+
+ if (speed == 0)
+ {
+ termios_p->c_iflag |= IBAUD0;
+ }
+ else
+ {
+ termios_p->c_iflag &= ~IBAUD0;
+ termios_p->c_cflag &= ~(CBAUD | CBAUDEX);
+ termios_p->c_cflag |= speed;
+ }
+
+ return 0;
+}
Index: sysdeps/unix/sysv/linux/tcsetattr.c
===================================================================
RCS file: /home/work/cvs/gnu/glibc/sysdeps/unix/sysv/linux/tcsetattr.c,v
retrieving revision 1.1.1.3
diff -u -p -r1.1.1.3 tcsetattr.c
--- sysdeps/unix/sysv/linux/tcsetattr.c 1998/05/09 20:57:57 1.1.1.3
+++ sysdeps/unix/sysv/linux/tcsetattr.c 1998/12/06 19:06:30
@@ -37,6 +37,7 @@ tcsetattr (fd, optional_actions, termios
{
struct __kernel_termios k_termios;
unsigned long int cmd;
+ int retval;

switch (optional_actions)
{
@@ -54,7 +55,7 @@ tcsetattr (fd, optional_actions, termios
return -1;
}

- k_termios.c_iflag = termios_p->c_iflag;
+ k_termios.c_iflag = termios_p->c_iflag & ~IBAUD0;
k_termios.c_oflag = termios_p->c_oflag;
k_termios.c_cflag = termios_p->c_cflag;
k_termios.c_lflag = termios_p->c_lflag;
@@ -68,5 +69,32 @@ tcsetattr (fd, optional_actions, termios
memcpy (&k_termios.c_cc[0], &termios_p->c_cc[0],
__KERNEL_NCCS * sizeof (cc_t));

- return __ioctl (fd, cmd, &k_termios);
+ /* The Linux kernel has a bug which silently ignore the invalid
+ c_cflag on pty. We have to check it here. */
+ retval = __ioctl (fd, cmd, &k_termios);
+ if (retval == 0)
+ {
+ int save = errno;
+ retval = __ioctl (fd, TCGETS, &k_termios);
+ if (retval)
+ {
+ /* We cannot verify if the setting is ok. We don't return
+ an error (?). */
+ __set_errno (save);
+ retval = 0;
+ }
+ else if ((termios_p->c_cflag & (PARENB | CREAD))
+ != (k_termios.c_cflag & (PARENB | CREAD))
+ || ((termios_p->c_cflag & CSIZE)
+ && (termios_p->c_cflag & CSIZE)
+ != (k_termios.c_cflag & CSIZE)))
+ {
+ /* It looks like the Linux kernel silently changed the
+ PARENB/CREAD/CSIZE bits in c_cflag. Report it as an
+ error. */
+ __set_errno (EINVAL);
+ retval = -1;
+ }
+ }
+ return retval;
}

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/