Sun Microsystems Logo
Products and Services
 
Support and Training
 
 

Previous Previous     Contents     Index     Next Next

I_STR ioctl Processing

The transparent and nontransparent methods implement ioctl(2) in the STREAMS driver or module itself, rather than in the stream head. I_STR ioctl(2) (also referred to as nontransparent ioctl(2)) is created when a user requests an I_STR ioctl(2) and specifies a pointer to a strioctl structure as the argument. For example, assuming that fd is an open lp STREAMS device and LP_CRLF is a valid option, the user could make a request by issuing the struct in the following example:


Example 8-6 Struct for Nontransparent ioctl

struct strioctl *str;
short lp_opt = LP_CRLF;

str.ic_cmd = SET_OPTIONS;
str.ic_timout = -1;
str.ic_dp = (char *)&lp_opt;
str.ic_len = sizeof (lp_opt)

ioctl(fd, I_STR, &str);


On receipt of the I_STR ioctl(2) request, the stream head creates an M_IOCTL message. ioc_cmd is set to SET_OPTIONS, ioc_count is set to the value contained in ic_len (in this example sizeof (short)). An M_DATA mblk is linked to the M_IOCTL mblk and the data pointed to by ic_dp is copied into it (in this case LP_CRLF).

Example 8-7 illustrates processing associated with an I_STR ioctl(2). lpdoioctl illustrates driver M_IOCTL processing, which also applies to modules. lpdoioctl is called by lp write-side put or service procedure to process M_IOCTL messages. This example is for a driver.


Example 8-7 I_STR ioctl(2) Driver

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

		lp = (struct lp *)q->q_ptr;

		/* 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 */
			  iocp->ioc_error = lpsetopt (lp, *(short *)mp->b_cont->b_rptr)

			  /* 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);
		}
	}


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! 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! 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.

Previous Previous     Contents     Index     Next Next