Part IV of this manual contains appendixes that summarize message types, kernel utility interfaces, the STREAMS-based terminal subsystem, and frequently asked questions.
Appendix A, Message Types, MESSAGE Types |
Provides a summary of ordinary and high-priority messages. |
Appendix B, Kernel Utility Interface Summary, Kernel Utility Interface Summary |
Provides a summary of the kernel utility interfaces. |
Appendix C, STREAMS-Based Terminal Subsystem, STREAMS-Based Terminal Subsystem |
Describes how a terminal subsystem is set up and how interrupts are handled. |
Appendix D, STREAMS FAQ, STREAMS FAQ |
Provides a collection of frequently asked questions. |
STREAMS message types differ in their intended purposes, their treatment at the stream head, and their message-queueing priority.
STREAMS does not prevent a module or driver from generating any message type and sending it in any direction on the stream. However, established processing and direction rules should be observed. Stream head processing according to message type is fixed, although certain parameters can be altered.
The message types found in sys/stream.h are described in this appendix, classified according to their message queueing priority. Ordinary messages are described first, with high-priority messages following. In certain cases, two message types may perform similar functions, differing only in priority. Message construction is described in Chapter 3, STREAMS Application-Level Mechanisms. The use of the word module generally implies module or driver.
Ordinary messages are also called normal or nonpriority messages. Ordinary messages are subject to flow control whereas high-priority messages are not.
Sent to a driver to request that BREAK be transmitted on whatever media the driver is controlling.
The message format is not defined by STREAMS and its use is developer dependent. This message may be considered a special case of an M_CTL message. An M_BREAK message cannot be generated by a user-level process and is always discarded if passed to the stream head.
Generated by modules that send information to a particular module or type of module. M_CTL messages are typically used for intermodule communication, as when adjacent STREAMS protocol modules negotiate the terms of their interface. An M_CTL message cannot be generated by a user-level process and is always discarded if passed to the stream head.
Contains ordinary data. Messages allocated by allocb(9F) are M_DATA type by default. M_DATA messages are generally sent bidirectionally on a stream and their contents can be passed between a process and the stream head. In the getmsg(2) and putmsg(2) system calls, the contents of M_DATA message blocks are referred to as the data part. Messages composed of multiple message blocks typically have M_DATA as the message type for all message blocks following the first.
Sent to a media driver to request a real-time delay on output. The data buffer associated with this message is expected to contain an integer to indicate the number of machine cycles of delay desired. M_DELAY messages are typically used to prevent transmitted data from exceeding the buffering capacity of slower terminals.
The message format is not defined by STREAMS and its use is developer dependent. Not all media drivers may understand this message. This message may be considered a special case of an M_CTL message. An M_DELAY message cannot be generated by a user-level process and is always discarded if passed to the stream head.
Generated by the stream head in response to I_STR, I_LINK, I_UNLINK, I_PLINK, and I_PUNLINK ioctls (see streamio(7I)). This message is also generated in response to ioctl calls that contain a command argument value not defined in streamio(7I). When one of these ioctl(2) is received from a user process, the stream head uses values supplied in the call and values from the process to create an M_IOCTL message containing them, then sends the message downstream. M_IOCTL messages perform the general ioctl(2) functions of character device drivers.
For an I_STR ioctl(2), the user values are supplied in a structure of the following form, provided as an argument to the ioctl(2) call (see I_STR in streamio(7I)):
struct strioctl { int ic_cmd; /* downstream request */ int ic_timout; /* ACK/NAK timeout */ int ic_len; /* length of data arg */ char *ic_dp; /* ptr to data arg */ }; |
where ic_cmd is the request (or command) defined by a downstream module or driver, ic_timout is the time the stream head waits for acknowledgement to the M_IOCTL message before timing out, and ic_dp points to an optional data buffer. On input, ic_len contains the length of the data in the buffer that was passed in. On return from the call, it contains the length of the data, if any, being returned to the user in the same buffer.
The M_IOCTL message format is one M_IOCTL message block followed by zero or more M_DATA message blocks. STREAMS constructs an M_IOCTL message block by placing an iocblk(9S) structure, defined in <sys/stream.h>, in its data buffer. The iocblk(9S) structure differs for 64–bit and 32–bit architectures.
#if defined(_LP64) struct iocblk { int ioc_cmd; /* ioctl command type */ cred_t *ioc_cr; /* full credentials */ uint ioc_id; /* ioctl id */ uint ioc_flag; /* see below */ size_t ioc_count; /* count of bytes in data field */ int ioc_rval; /* return value */ int ioc_error; /* error code */ }; #else struct iocblk { int ioc_cmd; /* ioctl command type */ cred_t *ioc_cr; /* full credentials */ uint ioc_id; /* ioctl id */ size_t ioc_count; /* count of bytes in data field */ int ioc_error; /* error code */ int ioc_rval; /* return value */ intt ioc_fill1; uint ioc_flag; /* see below */ int ioc_filler[2]; /* reserved for future use */ }; #endif /* _LP64 */ |
For an I_STR ioctl(2), ioc_cmd corresponds to ic_cmd of the strioctl structure. ioc_cr points to a credentials structure defining the user process's permissions (see cred.hfile).. Its contents can be tested to determine whether the user issuing the ioctl(2) call is authorized to do so. For an I_STR ioctl(2), ioc_count is the number of data bytes, if any, contained in the message and corresponds to ic_len.
ioc_id is an identifier generated internally, and is used by the stream head to match each M_IOCTL message sent downstream with response messages sent upstream to the stream head. The response message that completes the stream-head processing for the ioctl(2) is an M_IOCACK (positive acknowledgement) or an M_IOCNAK (negative acknowledgement) message.
For an I_STR ioctl(2), if a user supplies data to be sent downstream, the stream head copies the data (pointed to by ic_dp in the strioctl structure) into M_DATA message blocks and links the blocks to the initial M_IOCTL message block. ioc_count is copied from ic_len. If there are no data, ioc_count is zero.
If the stream head does not recognize the command argument of an ioctl(2), the head creates a transparent M_IOCTL message. The format of a transparent M_IOCTL message is one M_IOCTL message block followed by one M_DATA block. The form of the iocblk structure is the same as above. However, ioc_cmd is set to the value of the command argument in the ioctl(2) and ioc_count is set to TRANSPARENT, defined in <sys/stream.h>. TRANSPARENT distinguishes the case where an I_STR ioctl(2) specifies a value of ioc_cmd equivalent to the command argument of a transparent ioctl(2). The M_DATA block of the message contains the value of the arg parameter in the ioctl(2).
The first module or driver that understands the ioc_cmd request contained in the M_IOCTL acts on it. For an I_STR ioctl(2), this action generally includes an immediate upstream transmission of an M_IOCACK message. For transparent M_IOCTLs, this action generally includes the upstream transmission of an M_COPYIN or M_COPYOUT message.
Intermediate modules that do not recognize a particular request must pass the message on. If a driver does not recognize the request, or the receiving module can not acknowledge it, an M_IOCNAK message must be returned.
M_IOCACK and M_IOCNAK message types have the same format as an M_IOCTL message and contain an iocblk structure in the first block. An M_IOCACK block may be linked to following M_DATA blocks. If one of these messages reaches the stream head with an identifier that does not match that of the currently outstanding M_IOCTL message, the response message is discarded. A common means of ensuring that the correct identifier is returned is for the replying module to convert the M_IOCTL message into the appropriate response type and set ioc_count to 0 if no data is returned. Then, qreply(9F) is used to send the response to the stream head.
In an M_IOCACK or M_IOCNAK message, ioc_error holds any return error condition set by a downstream module. If this value is non-zero, it is returned to the user in errno. Note that both an M_IOCNAK and an M_IOCACK can return an error. However, only an M_IOCACK can have a return value. For an M_IOCACK, ioc_rval holds any return value set by a responding module. For an M_IOCNAK, ioc_rval is ignored by the stream head.
If a module processing an I_STR ioctl(2) is sending data to a user process, it must use the M_IOCACK message that it constructs such that the M_IOCACK block is linked to one or more following M_DATA blocks containing the user data. The module must set ioc_count to the number of data bytes sent. The stream head places the data in the address pointed to by ic_dp in the user I_STR strioctl structure.
A module processing a transparent ioctl(2) that is sending data to a user process can use only an M_COPYOUT message. For a transparent ioctl(2), no data can be sent to the user process in an M_IOCACK message. All data must be sent in a preceding M_COPYOUT message. The stream head ignores any data contained in an M_IOCACK message (in M_DATA blocks) and frees the blocks.
No data can be sent with an M_IOCNAK message for any type of M_IOCTL. The stream head ignores and frees any M_DATA blocks.
The stream head blocks the user process until an M_IOCACK or M_IOCNAK response to the M_IOCTL (same ioc_id) is received. For an M_IOCTL generated from an I_STR ioctl(2), the stream head times out if no response is received in ic_timout interval (the user can specify an explicit interval or specify use of the default interval). For M_IOCTL messages generated from all other ioctl(2)s, the default (infinite) is used.
The M_PASSFP message passes a file pointer from the stream head at one end of a stream pipe to the stream head at the other end of the same stream pipe.
The message is generated as a result of an I_SENDFD ioctl(2) (see streamio(7I)) issued by a process to the sending stream head. STREAMS places the M_PASSFP message directly on the destination stream head's read queue to be retrieved by an _RECVFD ioctl(2) (see streamio(7I)). The message is placed without passing it through the stream (that is, it is not seen by any modules or drivers in the stream). This message should never be present on any queue except the read queue of a stream head. Consequently, modules and drivers do not need to recognize this message, and it can be ignored by module and driver developers.
The M_PROTO message contains control information and associated data. The message format is one or more M_PROTO message blocks followed by zero or more M_DATA message blocks. The semantics of the M_DATA and M_PROTO message blocks are determined by the STREAMS module that receives the message.
On the write side, the user can only generate M_PROTO messages containing one M_PROTO message block.
The M_PROTO message block typically contains implementation dependent control information. M_PROTO messages are generally sent bidirectionally on a stream, and their contents can be passed between a process and the stream head. The contents of the first message block of an M_PROTO message is generally referred to as the control part, and the contents of any following M_DATA message blocks are referred to as the data part. In the getmsg(2) and putmsg(2), the control and data parts are passed separately.
The format of M_PROTO and M_PCPROTO (generically PROTO) messages sent upstream to the stream head allows multiple PROTO blocks at the beginning of the message although its use is not recommended. getmsg(2)) compacts the blocks into a single control part when passing them to the user process.
This message is reserved for internal use. Modules that do not recognize this message must pass it on. Drivers that do not recognize it must free it.
This message is used to alter some characteristics of the stream head. It is generated by any downstream module, and is interpreted by the stream head. The data buffer of the message has the following structure, as defined in stream.h:
struct stroptions { uint so_flags; /* options to set */ shor so_readopt; /* read option */ ushort so_wroff; /* write offset */ ssize_t so_minpsz; /* minimum read packet size */ ssize_t so_maxpsz; /* maximum read packet size */ size_t so_hiwat; /* read queue high–water mark */ size_t so_lowat; /* read queue low–water mark */ unsigned char so_band; /* band for water marks */ ushort so_erropt; /* error option */ ssize_t so_maxblk; /* maximum message block size */ ushort so_copyopt; /* copy options (see stropts.h) */ }; |
where so_flags specifies which options are to be altered, and can be any combination of the following:
SO_ALL — Update all options according to the values specified in the remaining fields of the stroptions structure.
SO_READOPT — Set the read mode (see read(2)) as specified by the value of so_readopt to:
RNORM — Byte stream
RMSGD — Message discard
RMSGN — Message non-discard
RPROTNORM — Normal protocol
RPROTDAT — Turn M_PROTO and M_PCPROTO msgs into M_DATA msgs
RPROTDIS — Discard M_PROTO and M_PCPROTO blocks in a msg and retain any linked M_DATA blocks
SO_WROFF — Direct the stream head to insert an offset (unwritten area), specified by so_wroff into the first message block of all M_DATA messages created as a result of a write(2). The same offset is inserted into the first M_DATA message block, if any, of all messages created by a putmsg system call. The default offset is zero.
The offset must be less than the maximum message buffer size (system dependent). Under certain circumstances, a write offset may not be inserted. A module or driver must test that b_rptr in the msgb(9S)structure is greater than db_base in the datab(9S) structure to determine that an offset has been inserted in the first message block.
SO_MINPSZ — Change the minimum packet size value associated with the stream head read queue to so_minpsz. This value is advisory for the module immediately below the stream head. It should limit the size of M_DATA messages that the module should put to the stream head. There is no intended minimum size for other message types. The default value in the stream head is zero.
SO_MAXPSZ — Change the maximum packet size value associated with the stream head read queue to so_maxpsz. This value is advisory for the module immediately below the stream head. It should limit the size of M_DATA messages that the module should put to the stream head. There is no intended maximum size for other message types. The default value in the stream head is INFPSZ, the maximum STREAMS allows.
SO_HIWAT — Change the flow control high–water mark (q_hiwat in the queue(9S) structure, qb_hiwat in the qband(9S) structure) on the stream-head read queue to the value specified in so_hiwat.
SO_LOWAT — Change the flow control low–water mark (q_lowat in the queue(9S) structure, qb_lowat in the qband(9S) structure) on the stream-head read queue to the value specified in so_lowat.
SO_MREADON — Enable the stream head to generate M_READ messages when processing a read(2) system call. If both SO_MREADON and SO_MREADOFF are set in so_flags, SO_MREADOFF takes precedence.
SO_MREADOFF — Disable the stream head generation of M_READ messages when processing a read(2) system call. This is the default. If both SO_MREADON and SO_MREADOFF are set in so_flags, SO_MREADOFF takes precedence.
SO_NDELON — Set non-STREAMS TTY semantics for O_NDELAY(or O_NONBLOCK) processing on read(2) and write(2). If O_NDELAY(or O_NONBLOCK) is set, a read(2) returns 0 if no data is waiting to be read at the stream head. If O_NDELAY(or O_NONBLOCK) is clear, a read(2) blocks until data become available at the stream head. (See note below)
Regardless of the state of O_NDELAY (or O_NONBLOCK), a write(2) blocks on flow control and blocks if buffers are not available.
If both SO_NDELON and SO_NDELOFF are set in so_flags, SO_NDELOFF takes precedence.
For conformance with the POSIX standard, new applications should use the O_NONBLOCK flag whose behavior is the same as that of O_NDELAY unless otherwise noted.
SO_NDELOFF — Set STREAMS semantics for O_NDELAY (or O_NONBLOCK) processing on read(2) and write(2) system calls. If O_NDELAY(or O_NONBLOCK) is set, a read(2) will return -1 and set EAGAIN if no data is waiting to be read at the stream head. If O_NDELAY (or O_NONBLOCK) is clear, a read(2) blocks until data become available at the stream head. (See note above.)
If O_NDELAY (or O_NONBLOCK) is set, a write(2) returns -1 and sets EAGAIN if flow control is in effect when the call is received. It blocks if buffers are not available. If O_NDELAY (or O_NONBLOCK) is set, part of the buffer has been written, and a flow control or “buffers not available” condition is encountered, write(2) terminates and returns the number of bytes written.
If O_NDELAY (or O_NONBLOCK) is clear, a write(2) will block on flow control and will block if buffers are not available.
This is the default. If both SO_NDELON and SO_NDELOFF are set in so_flags, SO_NDELOFF takes precedence.
In the STREAMS-based pipe mechanism, the behavior of read(2) and write(2) is different for the O_NDELAY and O_NONBLOCK flags.
SO_BAND — Set watermarks in a band. If the SO_BAND flag is set with the SO_HIWAT or SO_LOWAT flag, the so_band field contains the priority band number related to the so_hiwat and so_lowat fields.
If the SO_BAND flag is not set and the SO_HIWAT and SO_LOWAT flags are on, the normal high-water and low-water marks are affected. The SO_BAND flag has no effect if SO_HIWAT and SO_LOWAT flags are off.
Only one band's water marks can be updated with a single M_SETOPTS message.
SO_ISTTY — Inform the stream head that the stream is acting like a controlling terminal.
SO_ISNTTY — Inform the stream head that the stream is no longer acting like a controlling terminal.
For SO_ISTTY, the stream may be allocated as a controlling terminal via an M_SETOPTS message arriving upstream during open processing. If the stream head is opened before receiving this message, the stream will not be allocated as a controlling terminal until it is queued again by a session leader.
SO_TOSTOP — Stop on background writes to the stream.
SO_TONSTOP — Do not stop on background writes to the stream. SO_TOSTOP and SO_TONSTOP are used in conjunction with job control.
SO_DELIM — Messages are delimited.
SO_NODELIM — Messages are not delimited.
SO_STRHOLD — Enable strwrite message coalescing.
The M_SIG message is sent upstream by modules or drivers to post a signal to a process. When the message reaches the front of the stream-head read queue, it evaluates the first data byte of the message as a signal number, defined in <sys/signal.h>. (The signal is not generated until it reaches the front of the stream-head read queue.) The associated signal will be sent to processes under the following conditions:
If the signal is SIGPOLL, it is sent only to those processes that have explicitly registered to receive the signal (see I_SETSIG in streamio(7I)).
If the signal is not SIGPOLL and the stream containing the sending module or driver is a controlling TTY, the signal is sent to the associated process group. A stream becomes the controlling TTY for its process group if, on open(2) a module or driver sends an M_SETOPTS message to the stream head with the SO_ISTTY flag set.
If the signal is not SIGPOLL and the stream is not a controlling TTY, no signal is sent, except in case of SIOCSPGRP and TIOCSPGRP. These two ioctls set the process group field in the stream head so the stream can generate signals even if it is not a controlling TTY.
High-priority messages are particularly suitable for acknowledging service requests when the acknowledgement should be placed ahead of any other messages at the stream head. High-priority messages are not subject to flow control.
The M_COPYIN message is generated by a module or driver and sent upstream to request that the stream head perform a copyin(9F) on behalf of the module or driver. It is valid only after receiving an M_IOCTL message and before an M_IOCACK or M_IOCNAK.
The message format is one M_COPYIN message block containing a copyreq(9S) structure, defined in <sys/stream.h>. Different structures are defined for 64–bit and 32–bit architectures.
#if defined(_LP64) struct copyreq { int cq_cmd; /* ioctl command (from ioc_cmd) */ cred_t *cq_cr; /* full credentials (from ioc_cmd) */ uint cq_id; /* ioctl id (from ioc_id) */ uint cq_flag; /* see below */ mblk_t *cq_private; /* private state information */ caddr_t cq_addr; /* address to copy data to/from */ size_t cq_size; /* number of bytes to copy */ }; #else struct copyreq { int cq_cmd; /* ioctl command (from ioc_cmd) */ cred_t *cq_cr; /* full credentials */ uint cq_id; /* ioctl id (from ioc_id) */ caddr_t cq_addr; /* address to copy data to/from */ size_t cq_size; /* number of bytes to copy */ uint cq_flag; /* see below */ mblk_t *cq_private; /* private state information */ int cq_filler[4]; /* reserved for future use */ }; #endif /* _LP64 */ |
The first four members of the structure correspond to those of the iocblk(9S) structure in the M_IOCTL message that allows the same message block to be reused for both structures. The stream head guarantees that the message block allocated for the M_IOCTL message is large enough to contain a copyreq(9S). The cq_addr field contains the user space address from which the data is to be copied. The cq_size field is the number of bytes to copy from user space. The cq_flag field is reserved for future use and should be set to zero.
This message should not be queued by a module or driver unless it intends to process the data for the ioctl(2).
The M_COPYOUT message is generated by a module or driver and sent upstream to request that the stream head perform a copyout(9F) on behalf of the module or driver. It is valid only after receiving an M_IOCTL message and before an M_IOCACK or M_IOCNAK.
The message format is one M_COPYOUT message block followed by one or more M_DATA blocks. The M_COPYOUT message block contains a copyreq(9S) as described in the M_COPYIN message with the following differences: the cq_addr field contains the user space address to which the data is to be copied. The cq_size field is the number of bytes to copy to user space.
Data to be copied to user space is contained in the linked M_DATA blocks.
This message should not be queued by a module or driver unless it processes the data for the ioctl in some way.
For more information, see copyin and copyout in the Writing Device Drivers manual.
The M_ERROR message is sent upstream by modules or drivers to report a downstream error condition. When the message reaches the stream head, the stream is marked so that all subsequent system calls issued to the stream, excluding close(2) and poll(2), fail with errno set to the first data byte of the message. POLLERR is set if the stream is being polled. All processes sleeping on a system call to the stream are awakened. An M_FLUSH message with FLUSHRW is sent downstream.
The stream head maintains two error fields, one for the read side and one for the write side. The one-byte format M_ERROR message sets both of these fields to the error specified by the first byte in the message.
There is also a two-byte format of the M_ERROR message. The first byte is the read error and the second byte is the write error. This enables modules to set a different error on the read side and write side. If one of the bytes is set to NOERROR, then the field for the corresponding side of the stream is unchanged. The module can then ignore an error on one side of the stream. For example, if the stream head was not in an error state and a module sent an M_ERROR message upstream with the first byte set to EPROTO and the second byte set to NOERROR, all subsequent read-like system calls (such as read(2)and getmsg(2)) fail with EPROTO, but all write-like system calls (such as write(2) and putmsg(2)) still succeed. If a byte is set to 0, the error state is cleared for the corresponding side of the stream. The values NOERROR and 0 are not valid for the one-byte form of the M_ERROR message.
The M_FLUSH message requests all modules and drivers that receive it to flush their message queues (discard all messages in those queues) as indicated in the message. An M_FLUSH can originate at the stream head, or in any module or driver. The first byte of the message contains flags that specify one of the following actions:
FLUSHR — Flush the read queue of the module
FLUSHW — Flush the write queue of the module
FLUSHRW — Flush both the read queue and the write queue of the module
FLUSHBAND — Flush the message according to the priority associated with the band
Each module passes this message to its neighbor after flushing its appropriate queues until the message reaches one of the ends of the stream.
Drivers are expected to include the following processing for M_FLUSH messages. When an M_FLUSH message is sent downstream through the write queues in a stream, the driver at the stream end discards it if the message action indicates that the read queues in the stream are not to be flushed (only FLUSHW set). If the message indicates that the read queues are to be flushed, the driver shuts off the FLUSHW flag, and sends the message up the stream's read queues.
When a flush message is sent up a stream's read side, the stream head checks to see if the write side of the stream is to be flushed. If only FLUSHR is set, the stream head discards the message. However, if the write side of the stream is to be flushed, the stream head sets the M_FLUSH flag to FLUSHW and sends the message down the stream's write side. All modules that queue messages must identify and process this message type.
If FLUSHBAND is set, the second byte of the message contains the value of the priority band to flush.
The M_HANGUP message is sent upstream by a driver to report that it can no longer send data upstream. For example, this might be due to an error, or to a remote line connection being dropped. When the message reaches the stream head, the stream is marked so that all subsequent write(2) and putmsg(2) calls issued to the stream will fail and return an ENXIO error. Those ioctls that cause messages to be sent downstream are also failed. POLLHUP is set if the stream is being polled.
Subsequent read(2) or getmsg(2) calls to the stream will not generate an error. These calls will return any messages (according to their function) that were on, or in transit to, the stream-head read queue before the M_HANGUP message was received. When all such messages have been read, read(2) returns 0 and getmsg(2) will set each of its two length fields to 0.
This message also causes a SIGHUP signal to be sent to the controlling process instead of the foreground process group, as the allocation and deallocation of controlling terminals to a session is the responsibility of the controlling process.
The M_IOCACK message signals the positive acknowledgement of a previous M_IOCTL message. The message format is one M_IOCACK block (containing an iocblk(9S) structure, see M_IOCTL) followed by zero or more M_DATA blocks. The iocblk(9S) may contain a value in ioc_rval to be returned to the user process. It may also contain a value in ioc_error to be returned to the user process in errno.
If this message is responding to an I_STR ioctl (see streamio(7I)), it may contain data from the receiving module or driver to be sent to the user process. In this case, message format is one M_IOCACK block followed by one or more M_DATA blocks containing the user data. The stream head returns the data to the user if there is a corresponding outstanding M_IOCTL request. Otherwise, the M_IOCACK message is ignored and all blocks in the message are freed.
Data cannot be returned in an M_IOCACK message responding to a transparent M_IOCTL. The data must have been sent with preceding M_COPYOUT messages. If any M_DATA blocks follow the M_IOCACK block, the stream head ignores and frees them.
The format and use of this message type is described further under M_IOCTL.
The M_IOCDATA message is generated by the stream head and sent downstream as a response to an M_COPYIN or M_COPYOUT message. The message format is one M_IOCDATA message block followed by zero or more M_DATA blocks. The M_IOCDATA message block contains a copyresp(9S), defined in sys/stream.h.
#if defined(_LP64) struct copyresp { int cp_cmd; /* ioctl command (from ioc_cmd) */ cred_t *cp_cr; /* full credentials (from ioc_cmd) */ uint cp_id; /* ioctl id (from ioc_id) */ uint cp_flag; /* see above */ mblk_t *cp_private; /* private state information */ caddr_t cp_rval; /* status of request: 0 -> success */ /* non-zero -> failure */ }; #else struct copyresp { int cp_cmd; /* ioctl command (from ioc_cmd) */ cred_t *cp_cr; /* full credentials */ uint cp_id; /* ioctl id (from ioc_id) */ caddr_t cp_rval /* status of request: 0 -> success */ /* non-zero -> failure */ size_t cp_pad1; uint cp_pad2; mblk_t *cp_private; /* private state information */ uint cp_flag; /* see above */ int cp_filler[3]; }; #endif /* _LP64 */ |
The first three members of the structure correspond to those of the iocblk(9S) in the M_IOCTL message that allows the same message blocks to be reused for all of the related transparent messages (M_COPYIN, M_COPYOUT, M_IOCACK, M_IOCNAK). The cp_rval field contains the result of the request at the stream head. Zero indicates success and non-zero indicates failure. If failure is indicated, the module should not generate an M_IOCNAK message. It must abort all ioctl(2) processing, clean up its data structures, and return.
The cp_private field is copied from the cq_private field in the associated M_COPYIN or M_COPYOUT message. It is included in the M_IOCDATA message so the message can be self-describing. This is intended to simplify ioctl(2) processing by modules and drivers.
If the message is in response to an M_COPYIN message and success is indicated, the M_IOCDATA block is followed by M_DATA blocks containing the data copied in.
If an M_IOCDATA block is reused, any unused fields defined for the resultant message block should be cleared (particularly in an M_IOCACK or M_IOCNAK).
This message should not be queued by a module or driver unless it processes the data for the ioctl in some way.
The M_IOCNAK message signals the negative acknowledgement (failure) of a previous M_IOCTL message. Its form is one M_IOCNAK block containing an iocblk(9S). The iocblk(9S) can contain a value in ioc_error to be returned to the user process in errno. Unlike the M_IOCACK, no user data or return value can be sent with this message. If any M_DATA blocks follow the M_IOCNAK block, the stream head ignores and frees them. When the stream head receives an M_IOCNAK, the outstanding ioctl(2) request, if any, fails. The format and use of this message type is described further under M_IOCTL.
The M_IOCPROTO message is the same as the M_PROTO message type, except for the priority and the following additional attributes. When an M_PCPROTO message is placed on a queue, its service procedure is always enabled. The stream head allows only one M_PCPROTO message to be placed in its read queue at a time. If an M_PCPROTO message is already in the queue when another arrives, the second message is discarded and its message blocks freed.
This message is intended to allow data and control information to be sent outside the normal flow control constraints.
getmsg(2) and putmsg(2) refer to messages as high priority messages.
The M_PCRSE message is reserved for internal use. Modules that do not recognize this message must pass it on. Drivers that do not recognize it must free it.
The M_PCSIG message is the same as the M_SIG message, except for the priority. M_PCSIG is often preferable to M_SIG especially in TTY applications, because M_SIG may be queued while M_PCSIG is more likely to get through quickly. For example, if an M_SIG message is generated when the DEL (delete) key is pressed on the terminal and the user has already typed ahead, the M_SIG message becomes queued and the user does not get the call until too late, becoming impossible to kill or interrupt a process by pressing a delete key.
The M_READ message is generated by the stream head and sent downstream for a read(2) if no messages are waiting to be read at the stream head and if read notification has been enabled. Read notification is enabled with the SO_MREADON flag of the M_SETOPTS message and disabled by use of the SO_MREADOFF flag.
The message content is set to the value of the nbyte parameter (the number of bytes to be read) in read(2).
M_READ notifies modules and drivers of the occurrence of a read. It also supports communication between streams that reside in separate processors. The use of the M_READ message is developer dependent. Modules may take specific action and pass on or free the M_READ message. Modules that do not recognize this message must pass it on. All other drivers may or may not take action and then free the message.
This message cannot be generated by a user-level process and should not be generated by a module or driver. It is always discarded if passed to the stream head.
The SO_MREADOFF and M_STOP messages request devices to start or stop their output. They are used to produce momentary pauses in a device's output, not to turn devices on or off.
The message format is not defined by STREAMS and its use is developer dependent. These messages may be considered special cases of an M_CTL message. These messages cannot be generated by a user-level process and each is always discarded if passed to the stream head.
The SO_MREADOFFI and M_STOPI messages are the same as SO_MREADOFF and M_STOP except that SO_MREADOFFI and M_STOPI are used to start and stop input.
The M_UNHANGUP message reconnects the carrier after it has been dropped.
Use the following kernel utilities to develop and maintain drivers.
Table B–1 Kernel Utility Interfaces
Routine |
Description |
---|---|
Recover from failure of allocb(9F) |
|
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.
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:
Reusable line discipline modules. The same module can be used in many streams where the configuration of these streams may be different.
Line-discipline substitution. Although the Solaris operating environment provides a standard terminal line-discipline module, another one conforming to the interface can be substituted. For example, a remote login feature may use the terminal subsystem line discipline module to provide a terminal interface to the user.
Internationalization. The modularity and flexibility of the STREAMS-based terminal subsystem enables an easy implementation of a system that supports multiple-byte characters for internationalization. This modularity also allows easy addition of new features to the terminal subsystem.
Easy customizing. Users may customize their terminal subsystem environment by adding and removing modules of their choice.
The pseudo-terminal subsystem. The pseudo-terminal subsystem can be easily supported (this is discussed in more detail in the section STREAMS-based Pseudo-Terminal Subsystem).
Merge with networking. By pushing a line discipline module on a network line, you can make the network look like a terminal line.
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.
The master driver and slave driver have the following characteristics:
Each master driver has one-to-one relationship with a slave device based on major/minor device numbers.
Only one open is allowed on a master device. Multiple opens are allowed on the slave device according to standard file mode and ownership permissions.
Each slave driver minor device has a node in the file system.
An open on a master device automatically locks out an open on the corresponding slave driver.
A slave cannot be opened unless the corresponding master is open and has unlocked the slave.
To provide a TTY interface to the user, the ldterm and ptem modules are pushed on the slave side.
A close on the master sends a hang up to the slave and renders both streams unusable, after all data have been consumed by the process on the slave side.
The last close on the slave side sends a zero-length message to the master but does not sever the connection between the master and slave drivers.
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:
c_iflag defines input modes
c_oflag defines output modes
c_cflag defines hardware control modes
c_lflag defines terminal functions used by ldterm(7M)
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.
When ldterm is pushed on the stream, the open routine initializes the settings of the termio flags. The default settings are:
c_iflag = BRKINT|ICRNL|IXON|IMAXBEL c_oflag = OPOST|ONLCR|TAB3 c_cflag = CREAD|CS8|B9600 c_lflag = ISIG|ICANON|ECHO|ECHOK|IEXTEN|ECHOE|ECHOKE | ECHOCTL |
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).
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.
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:
M_DATA, M_BREAK, M_PCSIG, M_SIG, M_FLUSH, M_ERROR, M_IOCACK, M_IOCNAK, M_HANGUP, M_CTL, M_SETOPTS, M_COPYOUT, and M_COPYIN.
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 of the ldterm(7M) module is performed by the write-side put and service procedures.
The ldterm module supports the following ioctls:
TCSETA, TCSETAW, TCSETAF, TCSETS, TCSETSW, TCSETSF, TCGETA, TCGETS, TCXONC, TCFLSH, and TCSBRK.
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:
M_DATA, M_DELAY, M_BREAK, M_FLUSH, M_STOP, M_START, M_STOP, M_START, M_READ, M_IOCDATA, M_CTL, and M_IOCTL.
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:
If all of the termio(7I) processing is done by the peripheral hardware, the driver sends an M_CTL message back to ldterm(7M) with ioc_cmd of the structure iocblk(9S) set to MC_NO_CANON. If ldterm(7M) is to handle all termio(7I) processing, the driver sends an M_CTL message with ioc_cmd set to MC_DO_CANON. The default is MC_DO_CANON.
If the peripheral hardware handles only part of the termio(7I) processing, it informs ldterm(7M) in the following way:
The driver for the peripheral device allocates an M_DATA message large enough to hold atermios(3C) structure. The driver then turns on those c_iflag, c_oflag, and c_lflag fields of the termios(3C) structure that are processed on the peripheral device by executing an OR operation on the flag values. The M_DATA message is then attached to the b_cont field of the M_CTL message it received. The message is sent back to ldterm(7M) with ioc_cmd in the data buffer of the M_CTL message set to MC_PART_CANON.
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.
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:
Code set consists of more than one byte (including the SS2 and/or SS3) of characters
Code set requires more than one column to display on the current device, as indicated in the EUC_WSET structure
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.
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) .
#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) fail(); } /* * 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) fail(); printf("CSWIDTH=%d:%d,%d:%d,%d:%d", e->eucw[1], e->scrw[1], e->eucw[2], e->scrw[2], e->eucw[3], e->scrw[3]); }
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:
Processes, if appropriate, and acknowledges receipt of the following ioctls on its write queue by sending an M_IOCACK message back upstream: TCSETA, TCSETAW, TCSETAF, TCSETS, TCSETSW, TCSETSF, TCGETA, TCGETS, and TCSBRK.
Acknowledges the Extended UNIX Code (EUC) ioctl(2).
If the environment supports windowing, it acknowledges the windowing TIOCSWINSZ, TIOCGWINSZ, and JWINSIZE ioctl(2)s. If the environment does not support windowing, an M_IOCNAK message is sent upstream.
If another ioctl(2) is received on its write queue, it sends an M_IOCNAK message upstream. It doesn't pass any unrecognized ioctls to the slave driver.
When the hardware emulation module receives an M_IOCTL message of type TCSBRK on its write queue, it sends an M_IOCACK message upstream and the appropriate message downstream. For example, an M_BREAK message could be sent downstream.
When the hardware emulation module receives an M_IOCTL message on its write queue to set the baud rate to 0 (TCSETAW with CBAUD set to B0), it sends an M_IOCACK message upstream and an appropriate message downstream; for networking situations this will probably be an M_PROTO message, which is a TPI T_DISCON_REQ message requesting the transport provider to disconnect.
All other messages (M_DATA, for instance) not mentioned here are passed to the next module or driver in the stream.
The hardware emulation module processes messages in a way consistent with the driver that exists.
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.
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.
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:
Processes, if appropriate, and acknowledges receipt of the following ioctls on its write queue by sending an M_IOCACK message back upstream: TCSETA, TCSETAW, TCSETAF, TCSETS, TCSETSW, TCSETSF, TCGETA, TCGETS, and TCSBRK.
Keeps track of the window size; information needed for the TIOCSWINSZ, TIOCGWINSZ, and JWINSIZE ioctl.
When it receives an ioctl, other than for TIOCSWINSZ, TIOCGWINSZ, or JWINSIZE on its write queue, it sends an M_IOCNAK message upstream.
It passes downstream the following ioctls after processing them: TCSETA, TCSETAW, TCSETAF, TCSETS, TCSETSW, TCSETSF, TCSBRK, and TIOCSWINSZ.
Frees any M_IOCNAK messages it receives on its read queue in case the pckt module (pckt(7M) (described in the section Packet Mode) is not on the pseudo-terminal subsystem and the TCSETA, TCSETAW, TCSETAF, TCSETS, TCSETSW, TCSETSF, TCSBRK, or TIOCSWINSZioctls get to the master's stream head which then sends an M_IOCNAK message.
In its open routine, ptem sends an M_SETOPTS message upstream requesting allocation of a controlling TTY.
When ptem receives an M_IOCTL message of type TCSBRK on its read queue, it sends an M_IOCACK message downstream and an M_BREAK message upstream.
When ptem receives an ioctl(2) message on its write queue to set the baud rate to 0 (TCSETAW with CBAUD set to B0), it sends an M_IOCACK message upstream and a zero-length message downstream.
When ptem receives an M_IOCTL of type TIOCSIGNAL on its read queue, it sends an M_IOCACK downstream and an M_PCSIG upstream, where the signal number is the same as in the M_IOCTL message.
When ptem receives an M_IOCTL of type TIOCREMOTE on its read queue, it sends an M_IOCACK message downstream and the appropriate M_CTL message upstream to enable or disable canonical processing.
When ptem receives an M_DELAY message on its read or write queue, it discards the message and does not act on it.
When ptem receives an M_IOCTL of type JWINSIZE on its write queue, and if the values in its jwinsize structure are not zero, it sends an M_IOCACK message upstream with the jwinsize structure. If the values are zero, it sends an M_IOCNAK message upstream.
When ptem receives an M_IOCTL message of type TIOCGWINSZ on its write queue and the values in the winsize structure are not zero, it sends an M_IOCACK message upstream with the winsize structure. If the values are zero, it sends an M_IOCNAK message upstream. It also saves the information passed to it in the winsize structure and sends a STREAMS signal message for signal SIGWINCH upstream to the slave process if the size changed.
When ptem(7M) receives an M_IOCTL message with type TIOCGWINSZ on its read queue and the values in the winsize structure are not zero, it sends an M_IOCACK message downstream with the winsize structure. If the values are zero, it sends an M_IOCNAK message downstream. It also saves the information passed to it in the winsize structure and sends a STREAMS signal message for signal SIGWINCH upstream to the slave process if the size changed.
All other messages are passed to the next module or driver.
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.
ptem(7M) internal implementation might change. This structure should be relevant only to people wanting to change the module.
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.
These default values are currently being examined and may change in the future.
The open routine fails if:
No free entries are found when the ptem(7M) structure is searched
sflag is not set to MODOPEN
A zero-length message cannot be allocated (no buffer is available)
A stroptions(9S) structure cannot be allocated
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 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).
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:
When a message is passed to this module on its write queue, the module does no processing and passes the message to the next module or driver.
pckt creates an M_PROTO message when one of the following messages is passed to it: M_DATA, M_IOCTL, M_PROTO/M_PCPROTO, M_FLUSH, M_READ, M_START/M_STOP, and M_STARTI/M_STOPI.
All other messages are passed through. The M_PROTO message is passed upstream and retrieved when the user issues getmsg(2).
If the message is an M_FLUSH message, pckt(7M) does the following:
If the flag is FLUSHW, it is changed to FLUSHR (because FLUSHR was the original flag before the pts(7D) driver changed it), changed into an M_PROTO message, and passed upstream. To prevent the stream head's read queue from being flushed, the original M_FLUSH message must not be passed upstream.
If the flag is FLUSHR, it is changed to FLUSHW, packetized into an M_PROTO message, and passed upstream. To flush both of the write queues properly, an M_FLUSH message with the FLUSHW flag set is also sent upstream.
If the flag is FLUSHRW, the message with both flags set is packetized and passed upstream. An M_FLUSH message with the FLUSHW flag set is also sent upstream.
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.
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 functions grantpt(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:
ioctl (fd, TIOCSIGNAL, SIGINT) |
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.
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.
This appendix provides answers to frequently asked questions (FAQs).
A source of information on STREAMS performance is the paper "The BSD Packet Filter: A New Architecture for User-level Packet Capture" by McCanne & Van Jacobson in the 1993 Winter USENIX proceedings (also available as ftp://ftp.ee.lbl.gov/papers/bpf-usenix93.ps.Z). It includes detailed NIT vs. in-kernel BPF performance measurements and some explanation of results obtained.
With decent code in the kernel (not STREAMS) an in-kernel filter is much faster.
The following discussion provides answers to frequently asked IP interface questions.
Naming of the NIC device driver has the following constraints:
The name can contain alphanumeric and underscore (_) characters only
The first and last characters of the name cannot be a number
The name length cannot exceed 16 characters. The recommended length is 3–8 characters
Yes. The Solaris operating environment Ethernet le(7D) drivers support Data Link Provider Interfaces (DLPI).
IP is a STREAMS module in the Solaris operating environment. Any module or driver interface with IP should follow the STREAMS mechanism. There are no specific requirements for the interface between IP and network drivers.
Look at the dl_info_ack_t struct in /usr/include/sys/dlpi.h.
Yes, it is possible for the driver to be a CLONE driver and also a DLPI Style 2 provider. The DL_ATTACH_REQ request assigns a physical point of attachment (PPA) to a stream. The DL_ATTACH_REQ request can be issued any time after a file or stream being opened. The DL_ATTACH_REQ request does not have anything to do with assigning, retrieving or mapping minor/instance number. Of course, you can issue a DL_ATTACH_REQ request for a file or stream with desired major/minor number. As for the question of mapping minor number to instance, usually the minor number (getminor(9F)) is the instance number.
For the clone driver, this might not be possible. A non-clone driver, it can use the bits information in a particular minor number, for example FF, to map all other minor nodes.
Do you mean DLPI (Data Link Provider interfaces) ? The Solaris 2.1 ethernet drivers, le and ie. both support DLPI. See the le(7D) man page.
Yes and yes. Please see the dlpi(7P) man page. TCP and IP are STREAMS modules in the Solaris operating environment. The command strconf < /dev/tcp lists all the modules. STREAMS is not supported in SunOS 4 system TCP/IP.
IP multicast is a standard supported feature in the Solaris operating environment, but it is not supported in the SunOS 4 environment. If customers want to run an unsupported IP multicast on their SunOS 4 machines, it is available via anonymous FTP from gregorio.stanford.edu in the file vmtp-ip/ipmulti-sunos41x.tar.Z.