# Unix V7 Serial Programming
### Unix V7 - Introduction

Unix V7 was one of the early Unix versions which gained some wider distribution also outside of universities. It was commercially released in 1978. As a consequence, the Unix V7 way of accessing and controlling terminals set the standard for terminal and serial communication in Unix for quite some time. The V7 API does not consist of any particular C function calls. Instead, the settings and configuration of the serial device is done via the standard ioctl(2) system call, and data transfer is done via the standard read(2) and write(2) system calls.
Although, the API originated in Unix V7 it of course didn't remain unchanged. E.g. XENIX used a different name for the tchars data structure (tc). Some systems use a 32 bit value for the sg_flags entry in the sgttyb structure, others a 16 bit value. BSD added a bunch of ioctls, BSD 4.3 added a bunch of option codes (together sometimes called the BSD I/O environment), partly incompatible with Unix SVR3 codes, ...
All in all, if a later terminal API like termio or termios is available, it makes much sense to use the later API instead of the Unix V7, or V7-lookalike API. If the Unix V7 API is really used, some extensive study of the particular system's documentation and include files is in order. Nevertheless, there is some chance to find that the API is used in some classic Unix source code, particular in free software from that age that survived until today.
Originally, the necessary definitions for the terminal (serial) specific ioctl(2) system calls could be found in the sgtty.h and sys/tty.h> headers. However, in later Unix versions the contents was moved to sys/ttold.h and parts of it also ended up in termios.h, or the API was removed completely.
Mentioning termios.h here sounds rather strange, since it is a later API. But termio and termios borrowed from the V7 API, and systems which still support the V7 API often do this by emulating the API on top of the normal termio or termios interface with a compatible device driver. This is done by pushing an additional STREAMS device driver module on top of the termio or termios device driver. That STREAMS module is typically called ttycompat. How a particular Unix system can be configured to automatically push the module onto a device, how it can be manually pushed, or how the STREAMS device framework works is out of the scope of this description.

### Unix V7 - Mode of Operation

As already mentioned, the Unix V7 way of controlling terminal devices is to use ioctl(2) system calls. ioctl(2) is not specific to serial devices. The system call is part of Unix's generic device driver interface. The more widely used system calls for this interface are read(2) and write(2). While read(2) and write(2) do transport the "payload" (the data), ioctl(2) on the other hand controls the I/O operation as such. That is, it enables a program to configure the device.
The generic signature of the system call is
or
or
With the following arguments:

#### fd

    is a file descriptor pointing to an opened device.

#### command

    is an integer which tells the device what to do, and logically needs to be known by the device. The list of commands a serial device is supposed to understand is defined in sys/ttold.h (newer Unix systems) or sgtty.h plus sys/tt.h (old Unix systems like Unix V7) as macros. For the remainder of this section sys/ttold.h is used. Typically, sys/ttold.h is itself included by sys/ioctl.h. However, it doesn't hurt to include it explicitly for good measure.
    The names of the core terminal I/O control command macros start with TIO.... In addition, a few file I/O control commands are also applicable. Their names start with FIO.... We come to the commands and their purpose later, however, device-specific commands (DIO..., MX...) are not discussed.
    The actual parameter for the vararg ellipse ... arguments of ioctl is typically only a single pointer to some data structure - at least for the terminal control commands. This matches the old ioctl format, where there was one single char *data argument.

#### data

    For terminal control the data pointer points to a data structure of type
   * struct sgttyb,
   * struct tchars or
   * struct ltchars.
These data structures are used to communicate different types of information between the device driver and the application program. They control which settings are supposed to be done, or allow to fetch which parameters are currently set for the device.
Typically struct sgttyb looks like:
As one can see, it allows a program to communicate basic settings like the line speed and essential characters for the line discipline. Remember, the interface was made for communication with terminals, and therefore provides the support for a line discipline.
Typically struct tchars contains additional information about terminal control characters and looks like:
In addition to these data structures BSD added more terminal control characters in:
To summarize, a configuration of a serial device generally follows the following template of statements (pseudo code, error handling, etc. excluded):

### Unix V7 - Terminal I/O Control Commands

Template:Stubpar

#### Overview

As already mentioned, the possible terminal I/O control commands to be used for terminal devices via ioctl are defined as macros. This section discusses their meaning and usage in ioctl system calls.
Unfortunately, the set of commands is a mess. Almost every implementation of Unix decided it would be cool to add their own, incompatible commands. To add insult to injury, some popular Unix versions defined particular commands, but couldn't be bothered to implement them for decades, then dropped them. And to make even more programmers happy, half of the commands where never in any way seriously documented or described. The programmers apparently thought that only those with access to the Kernel source code are deemed worthy to program serial interfaces.

#### Original Unix V7 ioctls

This section lists the original Unix V7 I/O control commands. They are presented as they are used as part of an ioctl(2) call. This has been done, so the passed data types can also be presented. The argument data types are underlined in the following list.

##### Line Discipline
###### ioctl(fd, TIOCGETD, int *dscp)

    Get line discipline. See below.

###### ioctl(fd, TIOCSETD, int *dscp

    Set line discipline.
These commands did exist in V7, and were implemented. However, they were not documented. Particularly, the argument's meaning was not documented (and probably not used). Later the following argument interpretation was added:

   ###### OTTYDISC or 0

       Unix V7 tty driver behavior

   ###### NETLDISC or 1

       BSD Unix driver behavior

   ###### NTTYDISC or 2

       New tty line discipline (whatever that is)
A typical usage looks as it follows. The #ifdef test is done to avoid executing the ioctl on a system with undefined TIOCSETD argument semantics (like the original V7, where the arguments of the command were not documented). The following changes the line discipline of the device identified by the handle fd to the Unix V7 tty driver behavior.

##### Hang-up on close

When a DT (data terminal, the computer) is no longer ready to send and receive serial data on an interface, it is supposed to drop the DTR (Data Terminal Ready) RS-232 control line of that serial interface. The following command allows to automate this.

###### ioctl(fd, TIOCHPCL, NULL)

    Set hang up on last close flag. Typically this is implemented as dropping the DTR (Data Terminal Ready) pin on the serial interface when the last close(2) is called on the device. Note, there is no direct inverse operation in V7. However, the flag can be cleared in some implementations via the sg_flags member of struct sgttyb.
A typical sequence of events would look like it follows:

##### Modem State

Unix V7 had undocumented and probably unsupported commands for getting and setting a modem state.

###### ioctl(fd, TIOCMODG, int *state)

    Get modem state. Usually not implemented and not used. See TIOCMGET for getting the RS-232 control line status.

###### ioctl(fd, TIOCMODS, int *state)

    Set modem state. Usually not implemented and not used. See TIOCMSET for setting the RS-232 control lines.
The original semantics of the state variable have been lost in the mist of time. On some implementations the later TIOCM_x macros for TIOCMGET/TIOCMSET are available and do also work with TIOCMODx. The TIOCM_x flags are:

###### TIOCM_LE

    (not V7) Line enable signal

###### TIOCM_DTR

    (not V7) Data terminal ready

###### TIOCM_RTS

    (not V7) Request to send

###### TIOCM_ST

    (not V7) Secondary transmit (?)

###### TIOCM_SR

    (not V7) Secondary receive (?)

###### TIOCM_CTS

    (not V7) Clear to send

###### TIOCM_CD, TIOCM_CAR

    (not V7) Carrier detect

###### TIOCM_RI, TIOCM_RNG

    (not V7) Ring indicator

###### TIOCM_DSR

    (not V7) Data set ready

##### Terminal Parameters
###### ioctl(fd, TIOCGETP, struct sgttyb *data)

    Get the terminal parameters. They are stored in data.

###### gtty(fd, struct sgttyb *data)

    Short version for TIOCGETP ioctl on some systems derived from V7.

###### ioctl(fd, TIOCSETP, struct sgttyb *data)

    Set the interface to the provided parameters. Waits until output has drained. Clears (throws away) any pending input data, and then applies the parameters.

###### stty(fd, struct sgttyb *data)

    Short version for TIOCSETP ioctl on some systems derived from V7.

###### ioctl(fd, TIOCSETN, struct sgttyb *data)

    Sets the interface to the provided parameters, but does not wait for drained output and does not clear input data. Depending on the hardware this used to generate some garbage input/output characters.
See section #Unix V7 - struct sgttyb below for more details regarding the use of the struct sgttyb structure components.

##### Exclusive Mode
###### ioctl(fd, TIOCEXCL, NULL)

    Turn on exclusive mode. No other open is permitted on the device.

###### ioctl(fd, TIOCNXCL, NULL)

    Turn off exclusive mode. The device might be opened multiple times.

##### Flush
###### ioctl(fd, TIOCFLUSH, NULL) /* original */

    Input and output data is flushed.

###### ioctl(fd, TIOCTSTP, NULL)

    Other name for TIOCFLUSH

###### ioctl(fd, TIOCFLUSH, int *mode) /* later versions */

    Flush input and/or output according to mode. Constants are defined in sys/file.h

   ###### 0 or (FREAD | FWRITE)

       Flush Input and output.

   ###### FREAD

       Flush input.

   ###### FWRITE

       Flush output.

###### ioctl(fd, TIOHMODE, data_p)
##### Special Character State
###### ioctl(fd, TIOCGETC, struct tchars *data)

    Get the terminal state special characters from the device.

###### ioctl(fd, TIOCSETC, struct tchars *data)

    Set the terminal state special characters.

###### ioctl(fd, TIOCSETP, struct tchars *data)
#### More Original Unix V7 sgtty.h ioctls

The following ioctls were all defined in the Unix V7 tty interface, but with a few exceptions not properly documented. The D ioctls were probably intended to control dialers (ACUs). The F ioctls are borrowed from file I/O. The M ioctls are actually intended for Unix/V7 special multiplexed (mxp) files - an Unix/V7 mechanism for inter-process communication. Why these ioctls are defined in sgtty.h is unclear, other mpx ioctls are defined in sys/mx.h instead.

##### DIOCGETP

    Per line-discipline get data.

##### DIOCSETP

    Per line-discipline set data.

##### FIOCLEX

    Set the close-on-exec flag for this file descriptor. If the process is spawned (forked/execed), the file descriptor will be closed by the exec system call. That way the spawned process doesn't inherit the open terminal interface.

##### FIONCLEX

    Clear the close-on-exec flag for this file descriptor. If the process is spawned (forked/execed), the file descriptor will not be closed by the exec system call. That way the spawned process inherits the open terminal interface.

##### MXLSTN

    Place the mpx file in listener mode.

##### MXNBLK

    Use non-blocking mode for the mpx file.

#### Terminal ioctls acquired over time

There are quite some additional I/O controls which have been acquired over time and which enhance the Unix V7 teletype API. The following are the ones which are rather common. Later interfaces like termio and termios also added I/O controls. These are not listed here.
Sending a break signal can be performed with the following two commands:

##### ioctl(fd, TIOCSBRK, NULL)

    Set the break flag. Sends a break signal over the serial line. A break is an all-zero framing error.

##### ioctl(fd, TIOCCBRK, NULL)

    Clear the break flag. Stops sending a break signal.
A simplified way to send a one-second break looks as it follows:
The following commands can be used to control the RS-232 control lines:

##### ioctl(fd, TIOCSDTR, NULL)

    Set DTR (Data Terminal Ready) signal.

##### ioctl(fd, TIOCCDTR, NULL)

    Clear DTR (Data Terminal Ready) signal.

##### ioctl(fd, TIOCMODG, int *state)

    Get modem state. See below.

##### ioctl(fd, TIOCMODS, int *state)

    Set modem state. Allows to access all RS-232 control lines. The status of each line is indicated by a bit in the state argument. The bits have the following symbolic names:

   ##### TIOCM_CAR or TIOCM_CD

       DCD . Data Carrier Detect

   ##### TIOCM_CTS

       CTS - Clear to send

   ##### TIOCM_DTR

       DTR - Data terminal ready

   ##### TIOCM_LE or TIOCM_DSR

       DSR - Data set ready

   ##### TIOCM_RNG or TIOCM_RI

       RNG - Ring indicator

   ##### TIOCM_RTS

       RTS - Request to send

   ##### TIOCM_SR

       Secondary RxD

   ##### TIOCM_ST

       Secondary TxD
Depending on which of the above commands is available, DTR can, for example be set as it follows:
or

##### ioctl(fd, TIOCSTOP, NULL)

    Stop output as if the stop character has been typed.

##### ioctl(fd, TIOCSTART, data_p)

    Restart output as if the start character has been typed.

##### ioctl(fd, TIOCLGET, int *flags)

    Get the terminal flags. See element sg_flags of struct sgttyp for the details.

##### ioctl(fd, TIOCLBIS, int *flags)

    Set particular terminal flags. The flags are or-ed with the currently already set flags.

##### ioctl(fd, TIOCLBIC, int *flags)

    Clear particular terminal flags. The flags are "not-and-ed" with the existing flags.

##### ioctl(fd, TIOCLSET, int *flags)

    Set the terminal flags.

##### ioctl(fd, TIOCGLTC, struct ltchars *data)

    Get ltchars data.

##### ioctl(fd, TIOCSLTC, struct ltchars *data)

    Set ltchars data.

##### ioctl(fd, FIORDCHK, NULL)

    Return the number of currently available input characters in the input buffer. The number is returned as the return value of the function call.

##### ioctl(fd, FIONREAD, int *nbr)

    Return the number of currently available input characters in the input buffer. The number is written in to the *nbt argument.