Go to main content

STREAMS Programming Guide

Exit Print View

Updated: November 2020
 
 

Module and Driver ioctl Calls

STREAMS is a special type of character device driver that is different from the character input/output (I/O) mechanism. In this section, the phrases character I/O mechanism and I/O mechanism refer only to the mechanism that existed before STREAMS.

The character I/O mechanism handles all ioctl() calls transparently. That is, the kernel expects all ioctl() to be handled by the device driver associated with the character special file on which the call is sent. All ioctl() calls are sent to the driver, which is expected to perform all validation and processing other than file descriptor validity checking. The operation of any specific ioctl() is dependent on the device driver. If the driver requires data to be transferred in from user space, it will use the kernel ddi_copyin function. It may also use ddi_copyout to transfer any data results to user space. For more information, see the ioctl(2) man page.

    The character I/O mechanism and STREAMS process the ioctl() calls in different ways.

  • There is a set of generic STREAMS ioctl() command values recognized and processed by the stream head. The generic STREAMS ioctl() operation is independent of the presence of any specific module or driver on the stream. For more information about the command values, see the streamio(4I) man page.

  • The user context is not present in the module and driver, when the information associated with the ioctl() is received. Absence of user context prevents the use of ddi_copyin() or ddi_copyout() by the module. This also prevents the module and driver from associating any kernel data with the running process. By the time the module or driver receives the ioctl(), the process generating it probably will not be running. For more information, see the ddi_copyin(9F) and ddi_copyout(9F) man pages.

  • In the character I/O mechanism, all ioctl() are handled by the single driver associated with the file. In STREAMS, there can be multiple modules on a stream and each module can have its own set of ioctl() calls. The ioctl() calls that can be used on a stream can change as modules are pushed and popped.

STREAMS provides the capability for user processes to perform control functions on specific modules and drivers in a stream with ioctl() calls. Most of the streamio ioctl commands are processed at the stream head and no related messages are sent downstream. However, certain commands and all unrecognized commands cause the stream head to create an M_IOCTL </function> message, which includes the ioctl() arguments, and send the message downstream to be received and processed by a specific module or driver. The M_IOCTL </function> message is the initial message type that carries ioctl() information to modules. Other message types are used to complete the ioctl() processing in the stream. In general, each module must recognize and act on specific M_IOCTL </function> messages.

STREAMS ioctl() handling is equivalent to the transparent processing of the character I/O mechanism. STREAMS modules and drivers can process ioctl() generated by applications that are implemented for a non-STREAMS environment. For more information, see the ioctl(2) man page.

ioctl Processing

STREAMS blocks a user process that issues an ioctl() and causes the stream head to generate an M_IOCTL </function> message. The process remains blocked until one of the following occurs:

  • A module or a driver responds with an M_IOCACK (ack, positive acknowledgement) message or an M_IOCNAK (nak, negative acknowledgement) message.

  • No message is received and the request times out.

  • The ioctl() is interrupted by the user process.

  • An error condition occurs. For the I_STR ioctl(), the timeout period can be a user-specified interval or a default. For the other ioctl() calls, the default value, infinite is used.

For an I_STR, the STREAMS module or driver that generates a positive acknowledgement message can also return data to the process in the message. The data can also be returned using transparent ioctl(). If the stream head does not receive a positive or negative acknowledgement message in the specified time, the ioctl() call fails.

A module that receives an unrecognized M_IOCTL message must pass it on unchanged. A driver that receives an unrecognized M_IOCTL must produce a negative acknowledgement.

The two STREAMS ioctl() mechanisms, I_STR and transparent, are described in the later sections. Here, I_STR means the streamio I_STR command and implies the related STREAMS processing unless specified. I_STR has a restricted format and restricted addressing for transferring ioctl()-related data between user and kernel space. It requires a single pair of message to complete ioctl() processing. The transparent mechanism has no restrictions on ioctl() data format and addressing. The transparent mechanism requires that multiple pairs of messages be exchanged between the stream head and module to complete the processing.

Nothing prevents a given ioctl() from being issued either directly (transparent) or by means of I_STR. Further, ioctl() calls issued through I_STR may require further processing of the form typically associated with transparent ioctl().

For more information, see the ioctl(2) and streamio(4I) man pages.

I_STR ioctl Processing

The I_STR ioctl() allows user applications to perform module and driver control functions on STREAMS files. I_STR allows an application to specify the ioctl() timeout. It encourages that all user ioctl() data (to be received by the destination module) be placed in a single block that is pointed to from the user strioctl structure. The module can also return data to this block. For more information, see the ioctl(2) man page.

Transparent ioctl Processing

The transparent STREAMS ioctl() mechanism enables application programs to perform module and driver control functions with ioctl() calls other than I_STR. It transparently supports applications developed prior to the introduction of STREAMS. It reduces the need to recode and recompile the user-level software to run over STREAMS files. More importantly, applications do not have to package their ioctl() requests into the form demanded by I_STR.

The transparent STREAMS mechanism extends the data transfer capability for STREAMS ioctl() calls beyond those provided in the I_STR form. Modules and drivers can transfer data between their kernel space and user space in any ioctl() that has a value of the command argument not defined in streamio. These ioctl() are known as transparent ioctl() to differentiate them from the I_STR form. Existing applications that use non-STREAMS character devices require transparent processing for ioctl() if the device driver is converted to STREAMS. The ioctl() data can be in any format mutually understood by the user application and module.

The transparent mechanism also supports STREAMS applications that send ioctl() data to a driver or module in a single call, where the data may not be in a form readily embedded in a single user block. For example, the data may be contained in nested structures and different user space buffers. For more information, see the ioctl(2) and streamio(4I) man pages.

I_LIST ioctl

The I_LIST ioctl() supports the strconf and strchg commands that are used to query or change the configuration of a stream. Only the root user or an owner of a STREAMS device can alter the configuration of that stream. For more information, see the ioctl(2) and strchg(1) man pages.

    strchg performs the following actions:

  • Pushes one or more modules on the stream.

  • Pops the topmost module off the stream.

  • Pops all the modules off the stream.

  • Pops all modules up to but not including a specified module.

    strconf performs the following actions:

  • Checks if the specified module is present on the stream.

  • Prints the topmost module of the stream.

  • Prints a list of all modules and the topmost driver on the stream. If the stream contains a multiplexing driver, the strchg and strconf commands will not recognize any modules below that driver.

The I_LIST ioctl(), illustrated in Example 12, I_LIST ioctl, performs two functions. When the third argument of the ioctl() call is NULL,

if ((mods = ioctl(s, I_LIST, 0)) < 0) {

The return value of the call indicates the number of modules, and the driver present on the stream. For example, if there are two modules above the driver, 3 is returned. On failure, errno may be set to a value specified in streamio . The second function of the I_LIST ioctl() is to copy the module names found on the stream to the user-supplied buffer. The address of the buffer in user space and the size of the buffer are passed to the ioctl() through a structure str_mlist that is defined as:

struct str_mlist {
		char l_name[FMNAMESZ+1]; /* space for holding a module name*/
};
	
	struct str_list {
		int sl_nmods;		/* #of modules for which space is allocated */
		struct str_mlist *sl_modlist;	/*addr of buf for names*/
};

Here sl_nmods is the number of modules in the sl_modlist array that the user has allocated. Each element in the array must be at least FMNAMESZ+1 bytes long. The array is FMNAMESZ+1 so the extra byte can hold the NULL character at the end of the string. FMNAMESZ is defined by <sys/conf.h>.

The amount of space to allocate for module names is indicated by the number of modules in the STREAM. This is not completely reliable because another module might be pushed onto the stream after the application invokes the I_LIST ioctl() with the NULL argument and before it invokes the I_LIST ioctl() with the structure argument. For more information, see the ioctl(2) and streamio(4I) man pages.

The I_LIST call with arg pointing to the str_list structure returns the number of entries that have been filled into the sl_modlist array (the number represents the number of modules including the driver). If there is not enough space in the sl_modlist array or sl_nmods is less than 1, the I_LIST call fails and errno is set to EINVAL. If arg or the sl_modlist array points outside the allocated address space, EFAULT is returned.

Example 12  I_LIST ioctl
#include <stdio.h>
#include <string.h>
#include <stropts.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/socket.h>

main(int argc, const char **argv)
{
		int	s, i;
		unsigned int	mods;
		struct str_list	mod_list;struct str_mlist *mlist;
		/* Get a socket... */

		if ((s = socket(AF_INET, SOCK_STREAM, 0)) <= 0) {
			perror("socket: ");
			exit(1);
		}

		/* Determine the number of modules in the stream */	
		if ((mods = ioctl(s, I_LIST, 0)) < 0) {
			perror("I_LIST ioctl");
		}
		if (mods == 0) {
			printf("No modules\n");
			exit(1);
		} else {
			printf("%d modules\n", mods);
		}

		/* Allocate memory for all of the module names */
		mlist = (struct str_mlist *)
		calloc(mods, sizeof (struct str_mlist));
		if (mlist == 0) {
			perror("malloc failure");
			exit(1);
		}
		mod_list.sl_modlist = mlist;
		mod_list.sl_nmods = mods;
		/* Do the ioctl and get the module names... */
		if (ioctl(s, I_LIST, &mod_list) < 0) {
			exit(1);
		}

		/* Print out the name of the modules... */
		for (i = 0; i < mods; i++) {
			printf("s: %s\n", mod_list.sl_modlist[i].l_name);
		}

		/* Free the calloc'd structures... */
		free(mlist);
		return(0);
}

Other ioctl Commands

streamio details the following ioctl() calls:

I_ANCHOR

Prevents the removal of a STREAMS module with an I_POP call. Any process can place an anchor on a stream, but once placed, an anchor can only be removed by a privileged process.

I_LOOK

Retrieves the name of the module just below the stream head

I_FLUSH

Flushes all input or output queues

I_FLUSHBAND

Flushes a band of messages

I_FIND

Compares the names of all modules present in the stream to a specific name

I_PEEK

Enables the user to look at information in the first message on the stream head read queue without taking the message off the queue

I_SRDOPT

Sets the read mode using a series of flag options in the argument

I_GRDOPT

Indicates the read mode in an int

I_NREAD

Counts the data bytes in data blocks in the first message on the stream head read queue

I_FDINSERT

Creates a message from a user buffer, adds information about another stream, and sends the message downstream

I_SWROPT

Sets the write mode using the value of the argument

I_GWROPT

Returns the current write mode setting

I_SENDFD

Requests that the stream send a message with file pointer to the stream head at the other end of a STREAMS pipe

I_RECVFD

Retrieves the file descriptor of the message sent by an I_SENDFD ioctl() over a STREAMS pipe

I_ATMARK

Lets the application see if the current message on the stream head read queue is marked by a module downstream

I_CKBAND

Checks if the message of a given priority band exists on the stream head read queue

I_GETBAND

Returns the priority band of the first message on the stream head read queue

I_CANPUT

Checks if a certain band is writable

I_SETCLTIME

Enables the user to set the time the stream head will delay when a stream is closing if data exists on the write queues

I_GETCLTIME

Returns the close time delay

I_LINK

Connects two streams

I_UNLINK

Disconnects two streams

I_PLINK

Connects two streams with a persistent link below a multiplexing driver

I_PUNLINK

Disconnects two streams that have been connected with a persistent link

Message Direction

Various system calls let the user create messages and send them downstream and prioritize the messages.

getmsg()

Retrieves M_DATA, M_PROTO, or M_PCPROTO or high priority messages from the stream head, and places the contents into two user buffers. For more information, see the getmsg(2) man page.

getpmsg()

Does the same as getmsg() and enables the caller to specify a priority band for the message. For more information, see the getpmsg(2) man page.

putmsg()

Creates a message from the caller supplied control and data buffers and sends the message downstream. For more information, see the putmsg(2) man page.

putpmsg()

Does the same as putmsg() and enables the caller to specify a priority band for the message. For more information, see the putpmsg(2) and putmsg(2) man pages.

The stream head guarantees that the control part of a message generated by putmsg() is at least 64 bytes long. This promotes reusability of the buffer. When the buffer is a reasonable size, modules and drivers may reuse the buffer for other headers.

stropts.h contains the specification of strbuf, which describes the control and data buffers.