STREAMS Programming Guide

Appendix C STREAMS-Based Terminal Subsystem

This chapter describes how a terminal subsystem is set up and how interrupts are handled. Different protocols are addressed, as well as canonical processing and line discipline substitution.

Overview of Terminal Subsystem

STREAMS provides a uniform interface for implementing character I/O devices and networking protocols in the kernel. The SunOS 5.6 software implements the terminal subsystem in STREAMS. The STREAMS-based terminal subsystem (Figure C–1) provides many benefits:

Figure C–1 STREAMS-based Terminal Subsystem

Diagram shows the stream components of a STREAMS-based terminal

The initial setup of the STREAMS-based terminal subsystem is handled with the ttymon(1M) command within the framework of the Service Access Facility (autopush feature). See STREAMS Administrative Driver for more information.

The STREAMS-based terminal subsystem supports termio(7I), the termios(3C) specification of the POSIX standard, multiple-byte characters for internationalization, the interface to asynchronous hardware flow control and peripheral controllers for asynchronous terminals. XENIXTM and BSD compatibility can also be provided by pushing the ttcompat module.

To use shl with the STREAMS-based terminal subsystem, the sxt driver is implemented as a STREAMS-based driver. The sxt feature is being discontinued and might not be available, so try to use the job-control mechanism instead of sxt. Note that both shl and job control should not be run simultaneously.

Master Driver and Slave Driver Characteristics

The master driver and slave driver have the following characteristics:

Line-Discipline Module

A STREAMS line-discipline module called ldterm is a key part of the STREAMS-based terminal subsystem. Throughout this chapter, the terms line discipline and ldterm are used interchangeably and refer to the STREAMS version of the standard line discipline and not the traditional character version. ldterm performs the standard terminal I/O processing traditionally done through the linesw mechanism.

The termio(7I) and termios(3C) specifications describe four flags that are used to control the terminal:

To process these flags elsewhere (for example, in the firmware or in another process), a mechanism is in place to turn the processing of these flags on and off. When ldterm(7M) is pushed, it sends an M_CTL message downstream that asks the driver which flags the driver will process. The driver sends back that message in response if it needs to change the ldterm default processing. By default, ldterm(7M) assumes that it must process all flags except c_cflag unless it receives a message indicating otherwise.

Default Settings

When ldterm is pushed on the stream, the open routine initializes the settings of the termio flags. The default settings are:

c_oflag = OPOST|ONLCR|TAB3
c_cflag = CREAD|CS8|B9600

In canonical mode (ICANON flag in c_lflag is turned on), read from the terminal file descriptor is in message non-discard (RMSGN) mode (see streamio(7I)). This implies that in canonical mode, read on the terminal file descriptor always returns at most one line, regardless of how many characters have been requested. In non-canonical mode, read is in byte-stream (RNORM) mode. The flag ECHOCTL has been added for SunOS 4.1 compatibility.

For information on user-configurable settings, see termio(7I).

Module open and close Routines

The open routine of the ldterm(7M) module allocates space for holding the TTY structure (see ldtermstd_state_t in ldterm.h) by allocating a buffer from the STREAMS buffer pool. The number of modules that can be pushed on one stream, as well as the number of TTYs in use, is limited. The number of instances of ldterm that have been pushed is limited only by available memory. The open also sends an M_SETOPTS message upstream to set the stream head high-water and low-water marks to 1024 and 200, respectively. These are the current values.

The ldterm module identifies itself as a TTY to the stream head by sending an M_SETOPTS message upstream with the SO_ISTTY bit of so_flags set. The stream head allocates the controlling TTY on the open, if one is not already allocated.

To maintain compatibility with existing applications that use the O_NDELAY flag, the open routine sets the SO_NDELON flag on in the so_flags field of the stroptions(9S) structure in the M_SETOPTS message.

The open routine fails if there are no buffers available (so it cannot allocate the internal state structure) or when an interrupt occurs while waiting for a buffer to become available.

The close routine frees all the outstanding buffers allocated by this stream. It also sends an M_SETOPTS message to the stream head to undo the changes made by the open routine. The ldterm(7M) module also sends M_START messages downstream to undo the effect of any previous M_STOP messages.

Read-Side Processing

The ldterm(7M) module's read-side processing has put and service procedures. High-water and low-water marks for the read queue are 1024 and 200, respectively. These are the current values.

ldterm(7M) can send the following messages upstream:


The ldterm(7M) module's read side processes M_BREAK, M_DATA, M_CTL, M_FLUSH, M_HANGUP, M_IOCACK and M_IOCNAK messages. All other messages are sent upstream unchanged.

The put procedure scans the message for flow-control characters (IXON), signal-generating characters, and, after (possible) transformation of the message, queues the message for the service procedure. Echoing is handled completely by the service procedure.

If the ICANON flag is on in c_lflag, canonical processing is performed. If the ICANON flag is off, non-canonical processing is performed (see termio(7I) for more details). Handling of VMIN/VTIME in the STREAMS environment is somewhat complicated, because read needs to activate a timer in the ldterm module in some cases; hence, read notification becomes necessary. When a user issues an ioctl(2) to put ldterm(7M) in non-canonical mode, the module sends an M_SETOPTS message to the stream head to register read notification. Further reads on the terminal file descriptor will cause the stream head to issue an M_READ message downstream and data will be sent upstream in response to the M_READ message. With read notification, buffering of raw data is performed by ldterm(7M). Canonizing the raw data when the user has switched from raw to canonical mode is possible. However, the reverse is not possible.

To summarize, in non-canonical mode, the ldterm(7M) module buffers all data until VMIN or VTIME criteria are met. For example, if VMIN=3 and VTIME=0, and three bytes have been buffered, these characters are sent to the stream head regardless of whether there is a pending M_READ, and no M_READ needs to be sent downstream. If an M_READ message is received, the number of bytes sent upstream is the argument of the M_READ message unless VTIME is satisfied before VMIN (for example. the timer has expired) , in which case whatever characters are available will be sent upstream.

The service procedure of ldterm(7M) handles STREAMS-related flow control. Because the read side high-water and low-water marks are 1024 and 200 respectively, placing 1024 characters or more on the read queue causes the QFULL flag be turned on, indicating that the module below should not send more data upstream.

Input flow control is regulated by the line-discipline module which generates M_STARTI and M_STOPI high priority messages. When sent downstream, receiving drivers or modules take appropriate action to regulate the sending of data upstream. Output flow control is activated when ldterm(7M) receives flow control characters in its data stream. The module then sets an internal flag indicating that output processing is to be restarted/stopped and sends an M_START/M_STOP message downstream.

Write-Side Processing

Write-side processing of the ldterm(7M) module is performed by the write-side put and service procedures.

The ldterm module supports the following ioctls:


All ioctls not recognized by the ldterm(7M) module are passed downstream to the neighboring module or driver.

The following messages can be received on the write side:


On the write side, the ldterm module processes M_FLUSH, M_DATA, M_IOCTL, and M_READ messages, and all other messages are passed downstream unchanged.

An M_CTL message is generated by ldterm(7M) as a query to the driver for an intelligent peripheral and to decide on the functional split for termio(7I) processing. If all or part of termio(7I) processing is done by the intelligent peripheral, ldterm(7M) can turn off this processing to avoid computational overhead. This is done by sending an appropriate response to the M_CTL message, as follows:

One difference between AT&T STREAMS and SunOS 5 STREAMS is that AT&T's line discipline module does not check whether write-side flow control is in effect before forwarding data downstream. It expects the downstream module or driver to add the messages to its queue until flow control is lifted. This is not true in SunOS 5 STREAMS.

EUC Handling in ldterm

Post-processing (the o_flags) should not be handled by the host processor unless the board software is prepared to deal with international (EUC) character sets properly because that post-processing must take the EUC information into account. ldterm(7M) allots the appropriate screen width of characters (that is, how many columns are taken by characters from each given code set on the current physical display) and it takes this width into account when calculating tab expansions. When using multi-byte characters or multi-column characters ldterm automatically handles tab expansion (when TAB3 is set) and does not leave this handling to a lower module or driver.

By default, multi-byte handling by ldterm is turned off. When ldterm receives an EUC_WSET ioctl(2), it turns multi-byte processing on if it is essential to properly handle the indicated code set. Thus, if you use single byte 8-bit codes and have no special multi-column requirements, the special multi-column processing is not used at all. This means that multi-byte processing does not reduce the processing speed or efficiency of ldterm unless it is actually used.

The following describes how the EUC handling in ldterm works:

First, the multi-byte and multi-column character handling is only enabled when the EUC_WSET ioctl indicates that one of the following conditions is met:

Assuming that one or more of the previous conditions exists, EUC handling is enabled. At this point, a parallel array (see ldterm_mod structure) used for other information is allocated and a pointer to it is stored in t_eucp_mp. The parallel array that it holds is pointed to by t_eucp. The t_codeset field holds the flag that indicates which of the code sets is currently being processed on the read side. When a byte with the high bit arrives, ldterm checks to see if it is SS2 or SS3. If yes, it belongs to code set 2 or 3. Otherwise, it is a byte that comes from code set 1.

Once the extended code set flag has been set, the input processor retrieves the subsequent bytes, as they arrive, to build one multi-byte character. The counter field t_eucleft tells the input processor how many bytes remain to be read for the current character. The parallel array t_eucp holds its display width for each logical character in the canonical buffer. During erase processing, positions in the parallel array are consulted to determine how many backspaces need to be send to erase each logical character. (In canonical mode, one backspace of input erases one logical character, no matter how many bytes or columns that character consumes.) This greatly simplifies erase processing for EUC.

The t_maxeuc field holds the maximum length, in memory bytes, of the EUC character mapping currently in use. The eucwioc field is a substructure that holds information about each extended code set.

The t_eucign field aids in output post-processing (tab expansion). When characters are output, ldterm(7M) keeps a column to indicate what the current cursor column is supposed to be. When it sends the first byte of an extended character, it adds the number of columns required for that character to the output column. It then subtracts one from the total width in memory bytes of that character and stores the result in t_eucign. This field tells ldterm(7M) how many subsequent bytes to ignore for the purposes of column calculation. (ldterm(7M) calculates the appropriate number of columns when it sees the first byte of the character.)

The field t_eucwarn is a counter for occurrences of bad extended characters. It is mostly useful for debugging. After receiving a certain number of illegal EUC characters (perhaps because of some problem on the line or with declared values), a warning is given on the system console.

There are two relevant files for handling multi-byte characters: euc.h and eucioctl.h. eucioctl.h contains the structure that is passed with EUC_WSET and EUC_WGET calls. The normal way to use this structure is to get CSWIDTH from the locale using a mechanism such as getwidth(3C) or setlocale(3C), copy the values into the structure in eucioctl.h, and send the structure using an I_STR ioctl(2). The EUC_WSET call informs the ldterm(7M) module about the number of bytes in extended characters and how many columns the extended characters from each set consume on the screen. This enables ldterm(7M) to treat multi-byte characters as single units for the purpose of erase processing and to correctly calculate tab expansions for multi-byte characters.

Note –

LC_CTYPE (instead of CSWIDTH) should be used in SunOS 5 systems.

The file euc.h has fields for EUC width, screen width, and wide-character width. The functions in Example C–1 are used to set and get EUC widths (these functions assume the environment where the eucwidth_t structure is needed and available).

Example C–1 EUC Header File

#include <eucioctl.h>			/* need others,like stropts.h*/

struct eucioc eucw;			/*for EUC_WSET/WGET to line disc*/
eucwidth_t width;				/* ret struct from _getwidth() */
 * set_euc					Send EUC code widths to line discipline.
set_euc(struct eucioc *e)
	struct strioctl sb;

	sb.ic_cmd = EUC_WSET;
	sb.ic_timout = 15;
	sb.ic_len = sizeof(struct eucioc);
	sb.ic_dp = (char *) e;

	if (ioctl(0, I_STR, &sb) < 0)
 * euclook.   Get current EUC code widths from line discipline.
euclook(struct eucioc *e)
	struct strioctl sb;

	sb.ic_cmd = EUC_WGET;
	sb.ic_timout = 15;
	sb.ic_len = sizeof(struct eucioc);
	sb.ic_dp = (char *) e;

	if (ioctl(0, I_STR, &sb) < 0)

			e->eucw[1], e->scrw[1],
			e->eucw[2], e->scrw[2],
			e->eucw[3], e->scrw[3]);

Hardware Emulation Module

If a stream supports a terminal interface, a driver or module that understands all ioctls is needed to support terminal semantics (specified by termio(7I) and termiox(7I). If there is no hardware driver that understands all ioctl commands downstream from the ldterm module, a hardware emulation module must be placed downstream from the line-discipline module. The function of the hardware emulation module is to understand and acknowledge the ioctls that may be sent to the process at the stream head and to mediate the passage of control information downstream. Together, the line-discipline module and the hardware emulation module behave as if there was an actual terminal on that stream.

The hardware emulation module is necessary whenever there is no TTY driver at the end of the stream. For example, the module is necessary in a pseudo-TTY situation where there is process-to-process communication on one system (discussed in STREAMS-based Pseudo-Terminal Subsystem), or in a network situation where a termio interface is expected (for example, remote login) but there is no TTY driver on the stream.

Most of the actions taken by the hardware emulation module are the same regardless of the underlying architecture. However, there are some actions that are different, depending on whether the communication is local or remote and whether the underlying transport protocol is used to support the remote connection.

Each hardware emulation module has an open, close, read queue put procedure, and write queue put procedure.

The hardware emulation module does the following:

The hardware emulation module processes messages in a way consistent with the driver that exists.

STREAMS-based Pseudo-Terminal Subsystem

The STREAMS-based pseudo-terminal subsystem provides the user with an interface that is identical to the STREAMS-based terminal subsystem described earlier in this chapter. The pseudo-terminal subsystem (pseudo-TTY) supports a pair of STREAMS-based devices called the master device and slave device. The slave device provides processes with an interface that is identical to the terminal interface. However, where devices that provide the terminal interface have some kind of hardware device behind them, the slave device has another process manipulating it through the master half of the pseudo-terminal. Anything written on the master device is given to the slave as an input, and anything written on the slave device is presented as an input on the master side.

Figure C–2 illustrates the architecture of the STREAMS-based pseudo-terminal subsystem. The master driver (called ptm) is accessed through the clone driver and is the controlling part of the system. The slave driver (called pts) works with the line discipline module and the hardware emulation module to provide a terminal interface to the user process. An optional packetizing module (called pckt) is also provided. It can be pushed on the master side to support packet mode (this is discussed in Packet Mode).

The number of pseudo-TTY devices that can be installed on a system depends on available memory.

Line-Discipline Module

In the pseudo-TTY subsystem, the line discipline module is pushed on the slave side to present the user with the terminal interface.

ldterm(7M) can turn off the processing of the c_iflag, c_oflag, and c_lflag fields to allow processing to take place elsewhere. The ldterm(7M) module can also turn off all canonical processing when it receives an M_CTL message with the MC_NO_CANON command to support remote mode. Although ldterm(7M) passes through messages without processing them, the appropriate flags are set when an ioctl(2), such as TCGETA or TCGETS, is issued to indicate that canonical processing is being performed.

Figure C–2 Pseudo-TTY Subsystem Architecture

Diagram shows the client and server streams in a pseudo-TTY subsystem.

Pseudo-TTY Emulation Module: ptem

Because the pseudo-TTY subsystem has no hardware driver downstream from the ldterm(7M) module to process the terminal ioctl(2) calls, another module that understands the ioctl commands is placed downstream from the ldterm(7M). This module, ptem(7M), processes all of the terminal ioctl(2)calls and mediates the passage of control information downstream.

ldterm(7M) and ptem(7M) together behave like a real terminal. Since there is no real terminal or modem in the pseudo-TTY subsystem, some of the ioctl(2) commands are ignored and cause only an acknowledgment of the command. ptem(7M) keeps track of the terminal parameters set by the various set commands such as TCSETA or TCSETAW but does not usually perform any action. For example, if a “set” ioctl is called, none of the bits in the c_cflag field of termio(7I) has any effect on the pseudo-terminal unless the baud rate is set to 0. Setting the baud rate to 0 has the effect of hanging up the pseudo-terminal.

The pseudo-terminal does not recognize parity, so none of the flags in the c_iflag that control the processing of parity errors have any effect. The delays specified in the c_oflag field are also not supported.

ptem(7M) does the following:

ptem Data Structure

Each instantiation of ptem(7M) is associated with a local area. These data are held in a structure called ptem that has the following format:

struct ptem
	long cflags;					/* copy of c_flags */
	mblk_t *dack_ptr;			/* pointer to preallocated msg blk
									   used to send disconnect */
	queue_t *q_ptr;				/* pointer to ptem's read queue */
	struct winsize wsz;			/*struct to hold windowing info*/
	unsigned short state;		/* state of ptem entry */

When ptem(7M) is pushed onto the slave side stream, a search of the ptem structure is made for a free entry (state is not set to INUSE). The c_cflags of the termio(7I) structure and the windowing variables are stored in cflags and wsz respectively. The dack_ptr is a pointer to a message block used to send a zero-length message whenever a hang-up occurs on the slave side.

Note –

ptem(7M) internal implementation might change. This structure should be relevant only to people wanting to change the module.

open and close Routines

Caution – Caution –

The following information is implementation-dependent.

In the open routine of ptem(7M) a STREAMS message block is allocated for a zero-length message for delivering a hangup message. This allocation of a buffer is done before it is needed to ensure that a buffer is available. An M_SETOPTS message is sent upstream to set the read-side stream head queues, to assign high-water and low-water marks (1024 and 256 respectively), and to establish a controlling terminal.

The same default values as for the line-discipline module are assigned to cflags, and INUSE to the state field.

Note –

These default values are currently being examined and may change in the future.

The open routine fails if:

The close routine is called on the last close of the slave-side stream. Pointers to read and write queues are cleared and the buffer for the zero-length message is freed.

Remote Mode

Remote mode available with the pseudo-TTY subsystem, is used for applications that perform the canonical function normally done by ldterm(7M) and the TTY driver. The remote mode enables applications on the master side to turn off the canonical processing. An TIOCREMOTE ioctl(2) with a nonzero parameter (ioctl(fd, TIOCREMOTE, 1)) is issued on the master side to enter the remote mode. When this occurs, an M_CTL message with the command MC_NO_CANON is sent to ldterm(7M), indicating that data should be passed when received on the read side and that no canonical processing is to take place. The remote mode may be disabled by ioctl(fd, TIOCREMOTE, 0).

Packet Mode

In the STREAMS-based pseudo-terminal subsystem packet mode is used to inform the process on the master side when state changes have occurred in the pseudo-TTY. Packet mode is enabled by pushing the pckt module on the master side. Data written on the master side is processed normally. When data is written on the slave side, or when other messages are encountered by the pckt module, a header is added to the message so it can be subsequently retrieved by the master side with a getmsg operation.

pckt(7M) does the following:

Pseudo-TTY Drivers: ptm and pts

To use the pseudo-TTY subsystem, a node for the master side driver /dev/ptmx and N number of slave drivers must be installed (N is determined at installation). The names of the slave devices are /dev/pts/M where M has the values 0 through N-1. A user accesses a pseudo-TTY device through the master device (called ptm) that in turn is accessed through the clone driver. The master device is set up as a clone device where its major device number is the major for the clone device and its minor device number is the major for the ptm(7D) driver.

The master pseudo driver is opened by calling open(2) with /dev/ptmx as the device to be opened. The clone open finds the next available minor device for that major device. A master device is available only if it, and its corresponding slave device, are not already open. There are no nodes in the file system for master devices.

When the master device is opened, the corresponding slave device is automatically locked out. No user may open that slave device until it is unlocked. A user may invoke a function grantpt to change the owner of the slave device to that of the user who is running this process, change the group ID to TTY, and change the mode of the device to 0620. Once the permissions have been changed, the device may be unlocked by the user. Only the owner or the root user can access the slave device. The user must then invoke the unlockpt function to unlock the slave device. Before opening the slave device, the user must call the ptsname function to obtain the name of the slave device. The functions grantpt, unlockpt, and ptsname are called with the file descriptor of the master device. The user may then invoke the open system call with the name that was returned by the ptsname function to open the slave device.

The following example shows how a user may invoke the pseudo-TTY subsystem:

int fdm fds;
char *slavename;
extern char *ptsname();

fdm = open("/dev/ptmx", O_RDWR);  /* open master */
grantpt(fdm);                     /* change permission of slave */
unlockpt(fdm);                    /* unlock slave */
slavename = ptsname(fdm);         /* get name of slave */
fds = open(slavename, O_RDWR);    /* open slave */
ioctl(fds, I_PUSH, "ptem");       /* push ptem */
ioctl(fds, I_PUSH, "ldterm");    /* push ldterm */

Unrelated processes may open the pseudo device. The initial user may pass the master file descriptor using a STREAMS-based pipe or a slave name to another process to enable it to open the slave. After the slave device is open, the owner is free to change the permissions.

Note –

Certain programs such as write and wall are set group ID (setgid(2)) to TTY and are also able to access the slave device.

After both the master and slave have been opened, the user has two file descriptors that provide full-duplex communication using two streams. The two streams are automatically connected. The user may then push modules onto either side of the stream. The user also needs to push the ptem and ldterm modules onto the slave side of the pseudo-terminal subsystem to get terminal semantics.

The master and slave drivers pass all STREAMS messages to their adjacent queues. Only the M_FLUSH needs some processing. Because the read queue of one side is connected to the write queue of the other, the FLUSHR flag is changed to FLUSHW and vice versa.

When the master device is closed, an M_HANGUP message is sent to the slave device to render the device unusable. The process on the slave side gets the errno ENXIO when attempting to write on that stream, but it will be able to read any data remaining on the stream head read queue. When all the data has been read, read(2) returns 0, indicating that the stream can no longer be used.

On the last close of the slave device, a zero-length message is sent to the master device. When the application on the master side issues a read or getmsg and 0 is returned, the user of the master device decides whether to issue a close that dismantles the pseudo-terminal subsystem. If the master device is not closed, the pseudo-TTY subsystem is available to another user to open the slave device.

Since zero-length messages are used to indicate that the process on the slave side has closed (and should be interpreted that way by the process on the master side), applications on the slave side should not write zero-length messages. If that occurs, the write returns 0, and the zero-length message is discarded by the ptem module.

The standard STREAMS system calls can access the pseudo-TTY devices. The slave devices support the O_NDELAY and O_NONBLOCK flags. Because the master side does not act like the terminal, if O_NONBLOCK or O_NDELAY is set, read on the master side returns -1 with errno set to EAGAIN if no data is available, and write(2) returns -1 with errno set to EAGAIN if there is internal flow control.

The master driver supports the ISPTM and UNLKPT ioctl(2) that are used by the functionsgrantpt(3C)unlockpt(3C), , and ptsname(3C). The ISPTM ioctl(2) determines whether the file descriptor is that of an open master device. On success, it returns the major/minor number (type dev_t) of the master device which can be used to determine the name of the corresponding slave device. The UNLKPT ioctl(2) unlocks the master and slave devices. It returns 0 on success. On failure, the errno is set to EINVAL indicating that the master device is not open.

The format of these commands is:

int ioctl (int fd, int command, int arg)

where command is either ISPTM or UNLKPT and arg is 0. On failure, -1 is returned.

When data is written to the master side, the entire block of data written is treated as a single line. The slave-side process reading the terminal receives the entire block of data. Data is not edited by the ldterm module at input, regardless of the terminal mode. The master-side application is responsible for detecting an interrupt character and sending an interrupt signal SIGINT to the process in the slave side. This can be done as follows:


where SIGINT is defined in the file signal.h. When a process on the master side issues this ioctl(2), the argument is the number of the signal that should be sent. The specified signal is then sent to the process group on the slave side.


grantpt(3C) changes the mode and the ownership of the slave device that is associated with the given master device. Given a file descriptor fd, grantpt(3C) first checks that the file descriptor is that of the master device. If so, it obtains the name of the associated slave device and sets the user ID to that of the user running the process and the group ID to TTY. The mode of the slave device is set to 0620.

If the process is already running as root, the permission of the slave can be changed directly without invoking this function. grantpt(3C) returns 0 on success and -1 on failure. It fails if one or more of the following occurs: fd is not an open file descriptor, fd is not associated with a master device, the corresponding slave could not be accessed, or a system call failed because no more processes could be created.


unlockpt(3C) clears a lock flag associated with a master/slave device pair. unlockpt(3C) returns 0 on success and -1 on failure. It fails if one or more of the following occurs: fd is not an open file descriptor or fd is not associated with a master device.


ptsname(3C) returns the name of the slave device that is associated with the given master device. It first checks that the file descriptor is that of the master. If it is, it then determines the name of the corresponding slave device /dev/pts/M and returns a pointer to a string containing the null-terminated path name. The return value points to static data whose content is overwritten by each call. ptsname(3C) returns a non-NULL path name upon success and a NULL pointer upon failure. It fails if one or more of the following occurs: fd is not an open file descriptor or fd is not associated with the master device.

Pseudo-TTY Streams

Drivers and modules can make the stream head act as a terminal stream by sending an M_SETOPTS message with the SO_ISTTY flag set upstream. This state may be changed by sending an M_SETOPTS message with the SO_ISNTTY flag set upstream.

Controlling terminals are allocated with the open(2) interface. The device must tell the stream head that it is acting as a terminal.

The TOSTOP flag is set on reception of an M_SETOPTS message with the SO_TOSTOP flag set in the so_flags field. It is cleared on reception of an M_SETOPTS message with the SO_TONSTOP flag set.

Stream head processing is isolated from modules and drivers by using several message types, such as M_ERROR, M_HANGUP and M_SETOPS, which only affect the stream in which they are sent.