HDK Technical Reference

STREAMS ioctls

ioctl calls to a STREAMS driver or module are processed with the messages documented in Section D7str manual pages rather than with the ioctl(D2) entry point that is used in non-STREAMS drivers.

Most streamio ioctl commands go no further than the Stream head. They are fully processed there and no related messages are sent downstream. However, certain commands and all unrecognized commands cause the Stream head to create an M_IOCTL(D7str) message that includes the ioctl arguments, and send the message downstream to be received and processed by a specific module or driver. The M_IOCTL 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 uniquely recognize and take action on specific M_IOCTL messages.

Note the following about STREAMS ioctl processing:

STREAMS supports two types of ioctls:

General ioctl processing

STREAMS blocks a user process that issues an ioctl and causes the Stream head to generate an M_IOCTL message. The process remains blocked until one of the following occurs:

For the I_STR ioctl, the timeout period can be a user-specified interval or a default. For the other M_IOCTL ioctls, 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 that message. An alternate means to return data is provided with transparent ioctls. 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 should pass it on unchanged. A driver that receives an unrecognized M_IOCTL should produce a negative acknowledgement.

The form of an M_IOCTL message is a single M_IOCTL message block followed by zero or more M_DATA(D7str) blocks. The M_IOCTL message block contains an iocblk(D4str) structure.

For an I_STR ioctl, the ioc_cmd member of the iocblk structure contains the command supplied by the user in the strioctl structure defined in streamio.

If a module or driver determines an M_IOCTL message is in error for any reason, it must produce the negative acknowledgement message by setting the message type to M_IOCNAK and sending the message upstream. No data or a return value can be sent to a user in this case. If ioc_error (in iocblk) is set to 0, the Stream head causes the ioctl call to fail with EINVAL. The driver has the option of setting ioc_error to an alternate error number if desired.

NOTE: ioc_error can be set to a nonzero value in both M_IOCACK and M_IOCNAK. This causes that value to be returned as an error number to the process that sent the ioctl.

If a module wants to look at what ioctls of other modules are doing, the module should not look for a specific M_IOCTL on the write-side but look for M_IOCACK or M_IOCNAK on the read-side. For example, the module sees TCSETS (see termios) going down and wants to know what is being set. The module should look at it and save the data but not use it. The read-side processing knows that the module is waiting for an answer for the ioctl. When the read-side processing sees an ack or nak next time, it checks if it is the same ioctl (here TCSETS) and if it is, the module may use the data previously saved.

I_STR ioctl processing

The I_STR ioctl provides a capability for user applications to do module and driver control functions on STREAMS files. I_STR allows an application to specify the ioctl timeout. It requires 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.

If the module is looking at, for example, the TCSETS/TCGETS group of ioctl calls as they pass up or down a Stream, it must never assume that because TCSETS comes down that it actually has a data buffer attached to it. The user may have formed TCSETS as an I_STR call and accidentally given a null data buffer pointer. You should always check b_cont to see if it is NULL before using it as an index to the data block that goes with M_IOCTL messages.

The TCGETA call, if formed as an I_STR call with a data buffer pointer set to a value by the user, always has a data buffer attached to b_cont from the main message block. Check to see that the ioctl message does not have a buffer attached before allocating a new buffer and assigning b_cont to point at it. If you do not, the original buffer will be lost.

The following illustration illustrates processing associated with an I_STR ioctl. lpdoioctl is called to process trapped M_IOCTL messages:

I_STR ioctl processing

   lpdoioctl(struct lp *lp, mblk_t *mp)
   	struct iocblk *iocp;
   	queue_t *q;

q = lp->qptr; /*its own write queue*/

/* 1st block contains iocblk structure */ iocp = (struct iocblk *)mp->b_rptr;

switch (iocp->ioc_cmd) { case SET_OPTIONS: /* Count should be exactly one short's worth (for this example) */ if (iocp->ioc_count != sizeof(short)) goto iocnak; if (mp->b_cont == NULL) goto lognak; /* not shown in this example */ /* Actual data is in 2nd message block */ lpsetopt(lp, *(short *)mp->b_cont->b_rptr); /*hypothetical routine*/

/* ACK the ioctl */ mp->b_datap->db_type = M_IOCACK; iocp->ioc_count = 0; qreply(q, mp); break; default: iocnak: /* NAK the ioctl */ mp->b_datap->db_type = M_IOCNAK; qreply(q, mp); } }

lpdoioctl illustrates driver M_IOCTL processing, which also applies to modules. However, at case default, a module would not nak an unrecognized command, but would pass the message on. In this example, only one command is recognized, SET_OPTIONS. ioc_count contains the number of user-supplied data bytes. For this example, it must equal the size of a short. The user data is sent directly to the printer interface using lpsetopt. Next, the M_IOCTL message is changed to type M_IOCACK and the ioc_count field is set to zero to show that no data is to be returned to the user. Finally, the message is sent upstream using qreply. If ioc_count was left nonzero, the Stream head copies that many bytes from the 2nd through Nth message blocks into the user buffer.

Transparent ioctl processing

The transparent STREAMS ioctl mechanism allows application programs to perform module and driver control functions with ioctls other than I_STR. It is used to transparently support applications that were developed before the introduction of STREAMS, and alleviates the need to recode and recompile the user level software to run over STREAMS files.

The mechanism extends the data transfer capability for STREAMS ioctl calls beyond that 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 ioctls are known as transparent ioctls to differentiate them from the I_STR form. Transparent processing support is necessary when existing user level applications perform ioctls on a non-STREAMS device and the device driver is converted to STREAMS. The ioctl data can be in any format that is mutually understood by the user-level application and module.

The transparent mechanism also supports STREAMS applications that want to 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, different user space buffers, and so forth.

This mechanism is needed because user context does not exist in modules and drivers when ioctl processing occurs. This prevents them from using the kernel copyin(D3) and copyout(D3) functions. For example, consider the following ioctl call:

ioctl (stream_fildes, user_command, &ioctl_struct);
where ioctl_struct is a structure containing the members:
   int stringlen;  /* string length */
   char *string;
   struct other_struct *other1;
To read (or write) the elements of ioctl_struct, a module would have to do a series of copyin( ) and copyout( ) calls using pointer information from a prior copyin( ) to transfer additional data. A non-STREAMS character driver could directly execute these copy functions because user context exists during all calls to the driver. However, in STREAMS, user context is only available to modules and drivers in their open( ) and close( ) routines.

The transparent mechanism enables modules and drivers to request that the Stream head do a copyin( ) or copyout( ) operation on their behalf to transfer ioctl data between their kernel space and various user space locations. The related data is sent in message pairs exchanged between the Stream head and the module. A pair of messages is required so that each transfer can be acknowledged. In addition to M_IOCTL(D7str), M_IOCACK(D7str), and M_IOCNAK(D7str) messages, the transparent mechanism also uses M_COPYIN(D7str), M_COPYOUT(D7str), and M_IOCDATA(D7str) messages.

The general processing by which a module or a driver reads data from user space for the transparent case involves pairs of request/response messages, as follows:

  1. The Stream head does not recognize the command argument of an ioctl call and creates a transparent M_IOCTL message. The iocblk structure has a TRANSPARENT indicator containing the value of the arg argument in the call. It sends the M_IOCTL message downstream. See ``Transparent ioctl messages'' for more details.

  2. A module receives the M_IOCTL message, recognizes the ioc_cmd, and determines that it is TRANSPARENT.

  3. If the module requires user data, it creates an M_COPYIN message to request a copyin of user data. The message contains the address of user data to copy in and how much data to transfer. It sends the message upstream.

  4. The Stream head receives the M_COPYIN message and uses the contents to copyin the data from user space into an M_IOCDATA response message that it sends downstream. The message also contains an indicator of whether the data transfer succeeded. The copyin might fail, for instance, because of an EFAULT condition. See errnos(D5).

  5. The module receives the M_IOCDATA message and processes its contents.

    The module may use the message contents to generate another M_COPYIN. Steps 3 through 5 may be repeated until the module has requested and received all the user data to be transferred.

  6. When the module completes its data transfer, it does the ioctl processing and sends an M_IOCACK message upstream to notify the Stream head that ioctl processing has successfully completed.

Writing data from a module to user space is similar except that the module uses an M_COPYOUT message to request the Stream head to write data into user space. In addition to length and user address, the message includes the data to be copied out. In this case, the M_IOCDATA response does not contain user data, only shows success or failure.

The module may intermix M_COPYIN and M_COPYOUT messages in any order. However, each message must be sent one at a time; the module must receive the associated M_IOCDATA response before any subsequent M_COPYIN/M_COPYOUT request or ack/nak message is sent upstream. After the last M_COPYIN/M_COPYOUT message, the module must send an M_IOCACK message (or M_IOCNAK for a detected error condition).

NOTE: For a transparent M_IOCTL, user data cannot be returned with an M_IOCACK message. The data must have been sent with a preceding M_COPYOUT message.

Transparent ioctl messages

The form of the M_IOCTL message generated by the Stream head for a transparent ioctl is a single M_IOCTL message block followed by one M_DATA block. The form of the iocblk structure in the M_IOCTL block is the same as described under ``General ioctl Processing.'' However, ioc_cmd is set to the value of the command argument in the ioctl system call and ioc_count is set to TRANSPARENT, defined in <sys/stream.h>. TRANSPARENT distinguishes the case where an I_STR ioctl may specify a value of ioc_cmd equivalent to the command argument of a transparent ioctl. The M_DATA block of the message contains the value of the arg parameter in the call.

NOTE: Modules that process a specific ioc_cmd that did not validate the ioc_count field of the M_IOCTL message will break if transparent ioctls with the same command are done from user space.

I_LIST ioctl

The ioctl I_LIST supports the strconf and strchg commands that are used to query or change the configuration of a Stream. Only the superuser or an owner of a STREAMS device may alter the configuration of that Stream. See strchg for more information.

The strchg command does the following:

The strconf command does the following:

If the Stream contains a multiplexing driver, the strchg and strconf commands will not recognize any modules below that driver.

The ioctl I_LIST performs two functions. When the third argument of the ioctl call is set to NULL, the return value of the call shows the number of modules, including 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_list, which is defined in the following illustration:

str_list Structure

   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;/* address of buffer for names */

where 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. FMNAMESZ is defined in the <sys/conf.h> header file.

The user can find out how much space to allocate by first invoking the ioctl I_LIST with arg set to NULL. The I_LIST call with arg pointing to the str_list structure returns, in the sl_nmods member, the number of entries that have been filled into the sl_modlist array. Note that the number of entries includes the driver. If there is not enough space in the sl_modlist array (see note) 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.

NOTE: It is possible, but unlikely, that another module was pushed on the Stream after the user invoked the I_LIST ioctl with the NULL argument and before the I_LIST ioctl with the structure argument was invoked.

© 2005 The SCO Group, Inc. All rights reserved.
OpenServer 6 and UnixWare (SVR5) HDK - June 2005