[comp.bugs.sys5] Bug in TCSETA ioctl?

ian@sibyl.eleceng.ua.OZ (Ian Dall) (04/06/88)

There appears to be a bug in the code to impliment the TCSETA[F,W]
code Sys5.2 rel 2. I would be interested to know if this bug is a)
already known or b) fixed in later releases.

According to the "termio" man pages

              "TCSETA    Set the parameters associated with the
                         terminal from the structure referenced by
                         arg.  The change is immediate.

               TCSETAW   Wait for the output to drain before setting
                         the new parameters.  This form should be used
                         when changing parameters that will affect
                         output."

The implication is that the converse of the last sentence holds. And that
providing your change does not affect output post processing TCSETA should
be fine. In practice this turns out not to be the case. I only have a binary
licence which does include the hardware tt driver source but not the
generic tt driver stuff.

The hardware tt driver calls ttiocom and if it returns non zero goes and
stuffs things in the DUART registers to set up baud rates no of stop bits
etc. Not too surprisingly this tends to corrupt anything the DUART is in
the process of outputing.

Using adb on the kernal to find out what ttiocom does (remember I don't
have the source) reveals that the code must be some thing like:

ttiocom(.........)

{
  .
  .
  .
    switch ( command )

	case TCSETAW:
	case TCSETAF:
		ttywait(...);
		if (command == TCSETAF)
			ttyflush(....);
	case TCSETA:
                .
		.
		.
		/* code to get parameters from users area */

		return(1);

	case TCGETA:
                .
	        .
		.
		/* code to put parameters into users area */

                break;

	case TCSBRK:
                .
	        .
		.
		break;

	case TCXONC:
                .
	        .
		.
		break;

	case TCFLSH:
                .
	        .
		.
		break;
	return(0);
}


I contend that this code should only return 1 if the HARDWARE setup has
changed. Many (most?) TCSETAs will NOT change the hardware setup.

Something like replacing the "return (1);" with

           if ( old_c_cflag == c_cflag ) break;
	   else return (1);

should do the trick. Do later SysVs do this? Perhaps other hardware is
less fussy and don't get upset by setting the hardware registers to the
same as they already are.

Here is a test program so you can see if your system exhibits this bug.

If the output is garbled you have the bug.

# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by ian on Wed Apr  6 16:07:04 CST 1988
# Contents:  checktermio.c
 
echo x - checktermio.c
sed 's/^@//' > "checktermio.c" <<'@//E*O*F checktermio.c//'
#include <termio.h>
#include <stdio.h>
#define TSET TCSETA
int kbd_count;
char *kbd_ptr, kbd_buffer[256];
char longstring[] = "The quick brown fox jumped over the lazy dogs back and\
 made him howl horribly.\n";

main()
{
  register i;
  for (i=1; i < 20; i++)
    {
      printf(longstring);
      read_avail_input();
    }
}

read_avail_input ()
{
  struct termio tnow, told;

  if (ioctl (fileno (stdin), TCGETA, &tnow) < 0)
    return;

  told = tnow;
  tnow.c_lflag &= ~ICANON;
  tnow.c_cc[VMIN] = 0;
  tnow.c_cc[VTIME] = 0;
  ioctl (fileno (stdin), TCSETA, &tnow);
  kbd_ptr = kbd_buffer;
  kbd_count = read (fileno (stdin), kbd_buffer, sizeof kbd_buffer);
  ioctl (fileno (stdin), TCSETA, &told);
}
@//E*O*F checktermio.c//
chmod u=rw,g=r,o=r checktermio.c
 
exit 0

geoff@desint.UUCP (Geoff Kuenning) (04/14/88)

In article <107@sibyl.eleceng.ua.OZ> ian@sibyl.OZ (Ian Dall) writes:

> The hardware tt driver calls ttiocom and if it returns non zero goes and
> stuffs things in the DUART registers to set up baud rates no of stop bits
> etc. Not too surprisingly this tends to corrupt anything the DUART is in
> the process of outputing.

Your hardware driver is broken.  The driver should *never* modify the DUART
settings while characters are still in its output buffers.  With most DUARTs,
there is a way to tell whether the output buffers (usually two of them) are
empty yet.  Your code may have to loop, calling swtch() (and, I think,
setrun()) until those two characters are sent.

> I contend that this code should only return 1 if the HARDWARE setup has
> changed. Many (most?) TCSETAs will NOT change the hardware setup.
> 
> Something like replacing the "return (1);" with
> 
>            if ( old_c_cflag == c_cflag ) break;

In general, it is not possible to predict what tty settings will change the
hardware setup.  On complex hardware, changing iflag or oflag could require
changing the hardware.  Conversely, on many DUARTs, not every bit in cflag
requires a hardware change.

I'd recommend changing the driver to keep track of the current hardware
settings on each port, and to compare these with the new ones whenever
the ioctl routine is called.  If no change is needed, don't change the
hardware unnecessarily.  If something has changed, be sure to wait until
the output buffers have drained before clobbering them.
-- 
	Geoff Kuenning   geoff@ITcorp.com   {uunet,trwrb}!desint!geoff