Sun Microsystems Logo
Products and Services
 
Support and Training
 
 

Previous Previous     Contents     Index     Next Next

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

	struct address {		/* same members as in user space */
		int		ad_len;	/* length in bytes */
		caddr_t	ad_addr;	/* buffer address */
	};

	/* state values (overloaded in private field) */
	#define GETSTRUCT		0		/* address structure */
	#define GETADDR		1		/* byte string from ad_addr */

	static void xxioc(queue_t *q, mblk_t *mp);

	static int
	xxwput(q, mp)
		queue_t *q;		/* write queue */
		mblk_t *mp;
	{
		struct iocblk *iocbp;
		struct copyreq *cqp;

		switch (mp->b_datap->db_type) {
			.
			.
			.
			case M_IOCTL:
				/* Process ioctl commands */
				iocbp = (struct iocblk *)mp->b_rptr;
				switch (iocbp->ioc_cmd) {
					case SET_ADDR;
						if (iocbp->ioc_count != TRANSPARENT) {
						  /* do non-transparent processing here
						   *       (not shown here) */
						} else {
						 /* ioctl command is transparent 
						  * Reuse M_IOCTL block for first M_COPYIN 
						  * request of address structure */
						 cqp = (struct copyreq *)mp->b_rptr;
						 /* Get user space structure address from linked 
						  * M_DATA block */
						 cqp->cq_addr = *(caddr_t *) mp->b_cont->b_rptr;
						 cqp->cq_size = sizeof(struct address);
						 /* MUST free linked blks */
						 freemsg(mp->b_cont);
						 mp->b_cont = NULL;

						 /* identify response */
						 cqp->cq_private = (mblk_t *)GETSTRUCT;

						 /* Finish describing M_COPYIN message */
						 cqp->cq_flag = 0;
						 mp->b_datap->db_type = M_COPYIN;
						 mp->b_wptr = mp->b_rptr + sizeof(struct copyreq);
						 qreply(q, mp);
					  break;
					default: /* M_IOCTL not for us */
					  /* if module, pass on */
					  /* if driver, nak ioctl */
					  break;
				} /* switch (iocbp->ioc_cmd) */
				break;
			case M_IOCDATA:
				/* all M_IOCDATA processing done here */
				xxioc(q, mp);
				break;
		}
		return (0);
	}


xxwput() verifies that the SET_ADDR is TRANSPARENT to avoid confusion with an I_STR ioctl(2), which uses a value of ioc_cmd equivalent to the command argument of a transparent ioctl(2).

The if else statement checks whether the size count is equal to TRANSPARENT. If it is equal, the message was not generated from an I_STR ioctl(2) and the else clause of the if else executes.

	if (iocbp->ioc_count != TRANSPARENT) {
			/* do non-transparent processing here (not shown here) */
	} else {

The mblk is reused and mapped into a copyreq(9S) structure. The user space address of bufadd is contained in the b_cont of the M_IOCTL mblk. This address and its size are copied into the copyreq(9S) message. The b_cont of the copy request mblk is not needed, so it is freed and then filled with NULL.

	cqp = (struct copyreq *)mp->b_rptr;
			/* Get user space structure address from linked M_DATA block */
			cqp->cq_addr = *(caddr_t *) mp->b_cont->b_rptr;
			cqp->cq_size = sizeof(struct address);
			/* MUST free linked blks */
			freemsg(mp->b_cont);
			mp->b_cont = NULL;


Caution! Caution - The layout of the iocblk, copyreq, and copyresp structures is different between 32-bit and 64-bit kernels. Be careful not to overload any data structure in the cp_private or the cq_filler fields because alignment has changed.



Example 8-9 M_COPYIN: Copy the Buffer Address

	xxioc(queue_t *q, mblk_t *mp)			/* M_IOCDATA processing */
	{
		struct iocblk *iocbp;
		struct copyreq *cqp;
		struct copyresp *csp;
		struct address *ap;

		csp = (struct copyresp *)mp->b_rptr;
		iocbp = (struct iocblk *)mp->b_rptr;

		/* validate this M_IOCDATA is for this module */
		switch (csp->cp_cmd) {
			case SET_ADDR:
				if (csp->cp_rval){ /* GETSTRUCT or GETADDR fail */
					freemsg(mp);
					return;
				}
				switch ((int)csp->cp_private){ /* determine state */
					case GETSTRUCT:		/* user structure has arrived */
					  /* reuse M_IOCDATA block */
					  mp->b_datap->db_type = M_COPYIN;
					  mp->b_wptr = mp->b_rptr + sizeof (struct copyreq);
					  cqp = (struct copyreq *)mp->b_rptr;
					  /* user structure */
					  ap = (struct address *)mp->b_cont->b_rptr;
					  /* buffer length */
					  cqp->cq_size = ap->ad_len;
					  /* user space buffer address */
					  cqp->cq_addr = ap->ad_addr;
					  freemsg(mp->b_cont);
					  mp->b_cont = NULL;
					  cqp->cq_flag = 0;
					  cqp->cp_private=(mblk_t *)GETADDR;  /*nxt st*/
					  qreply(q, mp);
					  break;

					case GETADDR:				/* user address is here */
					  /* hypothetical routine */
					  if (xx_set_addr(mp->b_cont) == FAILURE) {
						  mp->b_datap->db_type = M_IOCNAK;
						  iocbp->ioc_error = EIO;
					  } else {
						  mp->b_datap->db_type=M_IOCACK;/*success*/
						  /* can have been overwritten */
						  iocbp->ioc_error = 0;
						  iocbp->ioc_count = 0;
						  iocbp->ioc_rval = 0;
					  }
					  mp->b_wptr=mp->b_rptr + sizeof (struct ioclk);
					  freemsg(mp->b_cont);
					  mp->b_cont = NULL;
					  qreply(q, mp);
					  break;

					default: /* invalid state: can't happen */
					  freemsg(mp->b_cont);
					  mp->b_cont = NULL;
					  mp->b_datap->db_type = M_IOCNAK;
					  mp->b_wptr = mp->rptr + sizeof(struct iocblk);
					  /* can have been overwritten */
					  iocbp->ioc_error = EINVAL;
					  qreply(q, mp);
					  break;
				}
				break;						/* switch (cp_private) */

			default: /* M_IOCDATA not for us */
				/* if module, pass message on */
				/* if driver, free message */
				break;


Previous Previous     Contents     Index     Next Next