This example recogizes only one command, SET_OPTIONS. The ioc_count contains the number of user-supplied data bytes. ioc_count must equal the size of a short.
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 */ |
Once the command has been verified, lpsetopt is called to process the request. lpsetopt returns 0 if the request is satisfied, otherwise an error number is returned. If ioc_error is nonzero, on receipt of the acknowledgement the stream head returns -1 to the application's ioctl(2) request and sets errno to the value of ioc_error.
/* Actual data is in 2nd message block */ iocp->ioc_error = lpsetopt (lp, *(short *)mp->b_cont->b_rptr) |
The ioctl(2) is acknowledged. This includes changing the M_IOCTL message type to M_IOCACK and setting the ioc_count field to zero to indicate that no data is to be returned to the user. Finally, the message is sent upstream using qreply(9F).
If ioc_count was left nonzero, the stream head would copy that many bytes from the second through the nth message blocks into the user buffer. You must set ioc_count if you want to pass any data back to the user.
/* ACK the ioctl */ mp->b_datap->db_type = M_IOCACK; iocp->ioc_count = 0; qreply(q, mp); break; |
In the default case for unrecognized commands or malformed requests, a nak is generated. This is done by changing the message type to an M_IOCNAK and sending it back upstream.
default: iocnak: /* NAK the ioctl */ mp->b_datap->db_type = M_IOCNAK; qreply(q, mp); |
A module does not acknowledge (nak) an unrecognized command, but passes the message on. A module does not acknowledge (nak) a malformed request.
Transparent ioctl
Transparent ioctls are used from within a module to tell the stream head to perform a copyin or copyout on behalf of the module. The stream head must have knowledge of the data model of the caller in order to process the copyin and copyout properly. The user should use the ioctl macros as described in Writing Device Drivers when coding a STREAMS module that uses Transparent ioctls.
Transparent ioctl Messages
The transparent STREAMS ioctl(2) mechanism is needed because user context does not exist in modules and drivers when an ioctl(2) is processed. This prevents them from using the kernel ddi_copyin/ddi_copyout functions.
Transparent ioctl(2) enable you to write an application using conventional ioctl(2) semantics instead of the I_STR ioctl(2) and an strioctl structure. The difference between transparent and nontransparent ioctl(2) processing in a STREAMS driver and module is the way data is transferred from user to kernel space.
The transparent ioctl(2) mechanism allows backward compatibility for older programs. This transparency only works for modules and drivers that support transparent ioctl(2). Trying to use transparent ioctl(2) on a stream that does not support them makes the driver send an error message upstream, causing the ioctl to fail.
The following example illustrates the semantic difference between a nontransparent and transparent ioctl(2). A module that translates arbitrary character is pushed on the stream The ioctl(2) specifies the translation to do (in this case all uppercase vowels are changed to lowercase). A transparent ioctl(2) uses XCASE instead of I_STR to inform the module directly.
Assume that fd points to a STREAMS device and that the conversion module has been pushed onto it. The semantics of a nontransparent I_STR command to inform the module to change the case of AEIOU are:
strioctl.ic_cmd = XCASE; strioctl.ic_timout = 0; strioctl.ic_dp = "AEIOU" strioctl.ic_len = strlen(strioctl.ic_dp); ioctl(fd,I_STR, &strioctl); |
When the stream head receives the I_STR ioctl(2) it creates an M_IOCTL message with the ioc_cmd set to XCASE and the data specified by ic_dp. AEIOU is copied into the first mblk following the M_IOCTL mblk.
The same ioctl(2) specified as a transparent ioctl(2) is called as follows:
ioctl(fd, XCASE, "AEIOU"); |
The stream head creates an M_IOCTL message with the ioc_cmd set to XCASE, but the data is not copied in. Instead, ioc_count is set to TRANSPARENT and the address of the user data is placed in the first mblk following the M_IOCTL mblk. The module then requests the stream head to copy in the data ("AEIOU") from user space.
Unlike the nontransparent ioctl(2), which can specify a timeout parameter, transparent ioctl(2)s block until processing is complete.
Caution - Incorrectly written drivers can cause applications using transparent ioctl(2) to block indefinitely.
Even though this process is simpler in the application, transparent ioctl adds considerable complexity to modules and drivers, and additional overhead to the time required to process the request.
The form of the M_IOCTL message generated by the stream head for a transparent ioctl(2) is a single M_IOCTL message block followed by one M_DATA block. The form of the iocblk(9S) structure in the M_IOCTL block is the same as described under general ioctl(2) processing. However, ioc_cmd is set to the value of the command argument in ioctl(2) and ioc_count is set to the special value of TRANSPARENT. The value TRANSPARENT distinguishes when an I_STR ioctl(2) can specify a value of ioc_cmd that is equivalent to the command argument of a transparent ioctl(2). The b_cont block of the message contains the value of the arg parameter in the call.
Caution - If a module processes a specific ioc_cmd and does not validate the ioc_count field of the M_IOCTL message, the module breaks when transparent ioctl(2) is performed with the same command.
Note - Write modules and drivers to support both transparent and I_STR ioctl(2).
All M_IOCTL message types (M_COPYIN, M_COPYOUT, M_IOCDATA,M_IOCACK and M_IOCNACK) have some similar data structures and sizes. You can reuse these structures instead of reallocating them. Note the similarities in the command type, credentials, and id.
The iocblk(9S) structure is contained in M_IOCTL, M_IOCACK and M_IOCNAK message types. For the transparent case, M_IOCTL has one M_DATA message linked to it. This message contains a copy of the argument passed to ioctl(2). Transparent processing of M_IOCACK and M_IONAK does not allow any messages to be linked to them.
The copyreq(9S) structure is contained in M_COPYIN and M_COPYOUT message types. The M_COPYIN message type must not have any other message linked to it (that is, b_cont == NULL). The M_COPYOUT message type must have one or more M_DATA messages linked to it. These messages contain the data to be copied into user space.
The copyresp(9S) structure is contained in M_IOCDATA response message types. These messages are generated by the stream head in response to an M_COPYIN or M_COPYOUT request. If the message is in response to an M_COPYOUT request, the message has no messages attached to it (b_cont is NULL). If the response is to an M_COPYIN, then zero or more M_DATA message types are attached to the M_IOCDATA message. These attached messages contain a copy of the user data requested by the M_COPYIN message.
The iocblk(9S), copyreq(9S), and copyresp(9S) structures contain a field indicating the type of ioctl(2) command, a pointer to the user's credentials, and a unique identifier for this ioctl(2). These fields must be preserved.
The structure member cq_private is reserved for use by the module. M_COPYIN and M_COPYOUT request messages contain a cq_private field that can be set to contain state information for ioctl(2) processing (which identifies what the subsequent M_IOCDATA response message contains). This state is returned in cp_private in the M_IOCDATA message. This state information determines the next step in processing the message. Keeping the state in the message makes the message self-describing and simplifies the ioctl(2) processing.
For each piece of data that the module copies from user space, an M_COPYIN message is sent to the stream head. The M_COPYIN message specifies the user address (cq_addr) and number of bytes (cq_size) to copy from user space. The stream head responds to the M_COPYIN request with a M_IOCDATA message. The b_cont field of the M_IOCDATA mblk contains the contents pointed to by the M_COPYIN request. Likewise, for each piece of data that the module copies to user space, an M_COPYOUT message is sent to the stream head. Specify the user address (cq_addr) and number of bytes to copy (cq_size). The data to be copied is linked to the M_COPYOUT message as one or more M_DATA messages. The stream head responds to M_COPYOUT requests with an M_IOCDATA message, but b_cont is null.
After the module has finished processing the ioctl (that is, all M_COPYIN and M_COPYOUT requests have been processed), the ioctl(2) must be acknowledged with an M_IOCACK to indicate successful completion of the command or an M_IOCNAK to indicate failure.
If an error occurs when attempting to copy data to or from user address space, the stream head will set cp_rval in the M_IOCDATA message to the error number. In the event of such an error, the M_IOCDATA message should be freed by the module or driver. No acknowledgement of the ioctl(2) is sent in this case.
Transparent ioctl Examples
Following are three examples of transparent ioctl(2) processing. Example 8-8 and Example 8-9 illustrate how to use M_COPYIN to copy data from user space. Example 8-10 illustrates how to use M_COPYOUT to copy data to user space. Example 8-11 is a more complex example showing state transitions that combine M_COPYIN and M_COPYOUT.
In these examples the message blocks are reused to avoid the overhead of allocating, copying, and releasing messages. This is standard practice.
The stream head guarantees that the size of the message block containing an iocblk(9S) structure is large enough to also hold the copyreq(9S) and copyresp(9S) structures.
M_COPYIN Example
Note - Please see the copyin section in Writing Device Drivers for information on the 64-bit data structure macros.
Example 8-8 illustrates the processing of a transparent ioctl(2) request only (nontransparent request processing is not shown). In this example, the contents of a user buffer are to be transferred into the kernel as part of an ioctl call of the form
ioctl(fd, SET_ADDR, (caddr_t) &bufadd); |
where bufadd is a struct address whose elements are:
struct address { int ad_len;; /* buffer length in bytes */ caddr_t ad_addr; /* buffer address */ }; |
This requires two pairs of messages (request and response) following receipt of the M_IOCTL message: the first copyin(9F), shown in Example 8-8, copies the structure (address) , and the second copyin(9F), shown in Example 8-9, copies the buffer (address.ad.addr). Two states are maintained and processed in this example: GETSTRUCT is for copying the address structure and GETADDR for copying the ad_addr of the structure.
The transparent part of the SET_ADDR M_IOCTL message processing requires that the address structure be copied from user address space. To accomplish this, the M_IOCTL message processing issues an M_COPYIN request to the stream head.
Example 8-8 M_COPYIN: Copy the address Structure