When the stream head is called to process an ioctl(2) that it does not recognize, it creates an M_IOCTL message and sends it down the stream. An M_IOCTL message is a single M_IOCTL message block followed by zero or more M_DATA blocks. The M_IOCTL message block has the form of an iocblk(9S) structure. This structure contains the following elements.
int ioc_cmd; /* ioctls command type */ cred_t *ioc_cr; /* full credentials */ uint ioc_id; /* ioctl id */ uint ioc_count; /* byte cnt in data field */ int ioc_error; /* error code */ int ioc_rval; /* return value */
For an I_STR ioctl(2), ioc_cmd contains the command supplied by the user in the ic_cmd member of the strioctl structure defined in streamio(7I). For others, ioc_cmd contains the value of the cmd argument in the call to ioctl(2). The ioc_cr field contains the credentials of the user process.
The ioc_id field is a unique identifier used by the stream head to identify the ioctl and its response messages.
The ioc_count field indicates the number of bytes of data associated with this ioctl request. If the value is greater than zero, there will be one or more M_DATA mblks linked to the M_IOCTL mblkb_cont field. If the value of the ioc_count field is zero, there will be no M_DATA mblk associated with the M_IOCTL mblk. If the value of ioc_count is equal to the special value TRANSPARENT, then there is one M_DATA mblk linked to this mblk and its contents will be the value of the argument passed to ioctl(2). This can be a user address or numeric value. (see Transparent ioctl Processing).
An M_IOCTL message is processed by the first module or driver that recognizes it. If a module does not recognize the command, it should pass it down. If a driver does not recognize the command, it should send a negative acknowledgement or M_IOCNAK message upstream. In all circumstances, a module or driver processing an M_IOCTL message must acknowledge it.
Modules must always pass unrecognized messages on. Drivers should negatively acknowledge unrecognized ioctl(2) messages and free any other unrecognized message.
If a module or driver finds an error in an M_IOCTL message for any reason, it must produce a negative acknowledgement message. To do this, set the message type to M_IOCNAK and send the message upstream. No data or return value can be sent. If ioc_error is set to 0, the stream head causes the ioctl(2) to fail with EINVAL. Optionally, the module can set ioc_error to an alternate error number.
ioc_error can be set to a nonzero value in both M_IOCACK and M_IOCNAK. This causes the value to be returned as an error number to the process that sent the ioctl(2).
If a module checks what the ioctl(2) of other modules below it are doing, the module should not just search for a specific M_IOCTL on the write side, but also look for M_IOCACK or M_IOCNAK on the read side. For example, suppose the module's write side sees TCSETA (see termio(7I)) and records what is being set. The read-side processing knows that the module is waiting for an answer for the ioctl(2). When the read-side processing sees an ack or nak, it checks for the same ioctl(2) by checking the command (here TCSETA) and the ioc_id. If these match, the module can use the information previously saved.
If you have the module check, for example, the TCSETA/TCGETA group of ioctl(2) calls as they pass up or down a stream, you must never assume that because TCSETA comes down it actually has a data buffer attached to it. The user can form TCSETA as an I_STR call and accidentally give a NULL data buffer pointer. 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. Do not assume that the data block is missing and allocate a new buffer, then assign b_cont to point to it, because the original buffer will be lost.