There are two lower half tty device driver routines, as well as a few helper routines. The two main lower half routines are the output interrupt routine ttyoin(), and the input interrupt routine ttyiin().
Ttyoin() is pretty straight forward. The point of it is to cause the SLU to transmit one character when it is called. Remember that it will be called as a result of an interrupt generated when the SLU transmitter finds itself with nothing to do (is idle).
First, ttyoin() checks a special buffer - the echo buffer to see if there are any characters in it. The echo buffer is filled with characters as they are received by the SLU, but only if the iecho field of the tty control block structure is set to true. The idea is that some terminals do not display characters as they are being typed. Thus the tty driver provides the service of echoing every character back out the SLU (presumably to the terminal screen) as it received. Because this isn't always desirable, there is a flag in the tty control block that controls this. The echo buffer is the first thing checked by ttyoin() in order that echoed characters receive the highest priority for transmission (nothing is more annoying than waiting for a character you just typed to appear).
Next, ttyoin checks to see if the oheld flag is set in the tty control block. This flag is set when the character [ctrl-S] is received, and reset when [ctrl-Q] is received. If the flag is set, the SLU is disabled (so it will not interrupt again until later enabled - presumably when a [ctrl-Q] is received).
Next, ttyoin() checks to see if there is anything in the output buffer. If there is, it sends the character at the tail, and advances the tail pointer.
Finally, now that ttyoin has created an extra space in the output buffer, you'd expect it to signal the output semaphore - thus allowing the upper half routine to write another character to the buffer if it has one. However, ttyoin will only do this if there is a reasonable amount of free space in the output buffer (more than or equal to OBMINSP bytes). The reason is that if it is going to unblock the upper half routine (and therefore an application process), we want to be sure that it will be able to put a series of characters in the output buffer before being blocked - not only one. The alternative of unblocking the upper half routine when there is only one space would have us doing one context switch for every character sent. Given the cost of a context switch this would be unacceptable.
The technique used to implement this is called water-marks. It
works like this: imagine a (non circular - for simplicity) output buffer.
The buffer is filled from the left. There are two "fill" levels
defined for the buffer - the low water mark and the high water
mark. This arrangement is depicted below:
As ttyoin() removes characters from the buffer, it examines the current fill level. If the buffer becomes near full (reaches above the high water mark), ttyoin() will continue to empty the buffer as it is able, but will not tell the upper level routines that there is space in the buffer until it has emptied it to below the low water mark. At that point the upper half is informed of the extra space and can begin to fill the buffer again. This is a very common technique.