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.
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;
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.
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;
On receipt of the M_IOCDATA message for the SET_ADDR command, xxioc() checks cp_rval. If an error occurred during the copyin operation, cp_rval is set. The mblk is freed and, if necessary, xxioc() cleans up from previous M_IOCTL requests, freeing memory, resetting state variables, and so on. The stream head returns the appropriate error to the user.
if (csp->cp_rval){ /* GETSTRUCT or GETADDR fail */ freemsg(mp); return;
If no error occurred during the copyin operation, the switch statement determines whether to process the user structure, GETSTRUCT, or user address, GETADDR.
switch ((int)csp->cp_private){ /*determine state*/
The cp_private field set to GETSTRUCT indicates that the linked b_cont mblk contains a copy of the user's address structure. The example then copies the actual address specified in address.ad_addr. The program issues another M_COPYIN request to the stream head, but this time cq_private contains GETADDR to indicate that the M_IOCDATA response will contain a copy of address.ad_addr. The stream head copies the information at the requested user address and sends it downstream in another, final M_IOCDATA message.
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;
The final M_IOCDATA message arrives from the stream head. cp_private contains GETADDR. The ad_addr data is contained in the b_cont link of the mblk. If the address is successfully processed by xx_set_addr() (not shown here), the message is acknowledged with an M_IOCACK message. If xx_set_addr() fails, the message is rejected with an M_IOCNAK message]. xx_set_addr() processes the user address from the ioctl(2).
After the final M_IOCDATA message is processed, the module acknowledges the ioctl(2) to let the stream head know that processing is complete. This is done by sending an M_IOCACK message upstream if the request was successfully processed. Always set ioc_error to zero, otherwise an error code could be passed to the user application. Set ioc_rval and ioc_count to zero to reflect that a return value of 0 and no data is to be passed upstream. If the request cannot be processed, either an M_IOCNAK or M_IOCACK can be sent upstream with an appropriate error number. When sending an M_IOCNAK or M_IOCACK, freeing the linked M_DATA block is not mandatory. It is more efficient to use the stream head handle to free the linked M_DATA block.
If ioc_error is set in an M_IOCNAK or M_IOCNACK message, this error code will be returned to the user. If no error code is set in an M_IOCNAK message, EINVAL will be returned to the user.
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;