From: guy@Auspex.COM (Guy Harris)
Newsgroups: comp.sys.sun.hardware
Subject: Re: Additional RS232 ports
Date: 14 Aug 92 02:48:37 GMT
Organization: Auspex Systems, Santa Clara

>The root cause of the problem,
>however, has to do with AT&T's STREAMS code not honoring a non-blocking
>close() call nor following the STREAMS close() semantics of timing out after
>a certain period of time from when the user application did a close() to the
>serial port and there was still data to be sent to the device.

Eh?

AT&T's code *does* honor closes on non-blocking descriptors, and *does*
time out after STRTIMOUT seconds, as of SVR3.1.

The SunOS 4.x code is derived from the SVR3.1 code, and it does the
same.

*HOWEVER*:

That very behavior caused *bugs* to be filed against SunOS 4.0.  The
problem is that if you have a printer (or other output device; I think
one complaint was about a plotter) attached to a serial port, and that
printer requires flow control, and the software that drives the printer
writes a bunch of stuff to the printer and then closes the printer, then
if the printer takes too long to print out the last stuff written to it,
the stream gets dismantled while stuff is still queued up to get sent to
the printer, and Bad Things Happen.

In particular, once "ldterm" is popped off the stream, you don't get
^S/^Q flow control any more, and you run the risk of overrunning the
printer.

The workaround I suggested when those bugs started arriving was to have
some other process hold the serial port open, so that the stream *isn't*
dismantled when the close is done.

I think that kept the problem from occurring, but it's kind of ugly, and
I guess the tech support people may have gotten tired of explaining it
over and over again, so the ^S/^Q flow control stuff was moved down to
the driver, at least in one of the serial port patches.

In 4.1, the flow control stuff was moved out of the driver, and "ldterm"
was changed so that, in its close routine, it sent an "ioctl" streams
message downstream, doing a TCSBRK "ioctl" with a non-zero argument.  A
TCSBRK with a non-zero argument blocks waiting for all output to drain. 
This means that the "ldterm" close routine will block until output
drains - no matter how long that takes.

Now, at least in the case of printers like that, the STREAMS close
semantics are Very Bad, as indicated (anyone who disagrees with that
will be sentenced to 100 years in purgatory answering calls from SunOS
4.0 customers whose printers don't work :-)).  (In fact, my first
unpleasant encounter with AT&T-style tty close semantics was with System
III, long before any System N release had STREAMS; the problem described
above occurred, because the printer in question required ^S/^Q to be
honored.)

Unfortunately, that means that if the port *never* drains, a close can
hang forever.

In part, that can be fixed by making sure that if carrier drops, all
queued-up data is flushed, *and* any TCSBRK "ioctl"s waiting for output
to drain get an acknowledgement sent upstream, so that the close can
continue.  A quick look at the "zs" driver indicates that this might not
happen; the code that sends the M_HANGUP message upstream when carrier
drops doesn't flush all queued-up data.  If so, that might be the cause
of the problem.

That fix should mean that if some ^S'ed output was pending when carrier
dropped, and a close was blocked waiting for the output to drain, the
close would unblock.  It would have to be applied to all serial port
drivers.

It still doesn't handle the case where the line has soft carrier, and
somebody ^S's output and then turns the terminal off, say.  If somebody
then kills off that person's session, the close will, I think, hang
until somebody turns the terminal back on again and does a ^Q.

To handle *that* problem, the "wait for output to drain" must be made
optional.  This could be done by:

	1) having some "ioctl" to specify whether a close should do that
	   or not.

	   If wait-for-output-to-drain were made the default, "getty" or
	   "ttymon" or whatever would presumably turn it off.

	   If it were *not* the default, any code doing printing would
	   probably want to turn it *on* if the printer required flow
	   control, unless the mode is "sticky" (i.e., unlike most UNIX
	   tty modes, persists even after the last close), in which case
	   you could have one of the "/etc/rc" files set that mode on
	   the ports with printers of that sort on them.

	2) having any code doing printing explicitly do the TCSBRK
	   "ioctl" before closing the port.
Xref: sparky comp.sys.sun.hardware:3939 comp.sys.sun.admin:5655