STREAMS Programming Guide

Bidirectional Data Transfer Example

Example 8–11 illustrates bidirectional data transfer between the kernel and application during transparent ioctl(2) processing. It also shows how to use more complex state information.

The user wants to send and receive data from user buffers as part of a transparent ioctl(2) call of the form:



	ioctl(fd, XX_IOCTL, (caddr_t) &addr_xxdata) 


Example 8–11 Bidirectional Data Transfer

struct xxdata {             /* same members in user space */
   int         x_inlen;     /* number of bytes copied in */
   caddr_t     x_inaddr;    /* buf addr of data copied in */
   int         x_outlen;    /* number of bytes copied out */
   caddr_t     x_outaddr;   /* buf addr of data copied out */
};
/* State information for ioctl processing */
struct state {
		int         st_state;    /* see below */
		struct xxdata		st_data;		/* see above */
};
/* state values */

#define GETSTRUC     0   /* get xxdata structure */
#define GETINDATA    1   /* get data from x_inaddr */
#define PUTOUTDATA   2   /* get response from M_COPYOUT */

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

static int
xxwput (queue_t *q, 	mblk_t *mp) {
		struct iocblk *iocbp;
		struct copyreq *cqp;
		struct state *stp;
		mblk_t *tmp;

		switch (mp->b_datap->db_type) {
			.
			.
			.
			case M_IOCTL:
				iocbp = (struct iocblk *)mp->b_rptr;
				switch (iocbp->ioc_cmd) {
				case XX_IOCTL:
				/* do non-transparent processing. (See I_STR ioctl
				 * processing discussed in previous section.)
				 */
				/*Reuse M_IOCTL block for M_COPYIN request*/

				cqp = (struct copyreq *)mp->b_rptr;

				/* Get structure's user address from
				 * linked M_DATA block */

				cqp->cq_addr = (caddr_t)
				 *(long *)mp->b_cont->b_rptr;
				freemsg(mp->b_cont);
				mp->b_cont = NULL;

				/* Allocate state buffer */

				if ((tmp = allocb(sizeof(struct state),
				 BPRI_MED)) == NULL) {
						mp->b_datap->db_type = M_IOCNAK;
						iocbp->ioc_error = EAGAIN;
						qreply(q, mp);
						break;
				}
				tmp->b_wptr += sizeof(struct state);
				stp = (struct state *)tmp->b_rptr;
				stp->st_state = GETSTRUCT;
				cqp->cq_private = tmp;

				/* Finish describing M_COPYIN message */

				cqp->cq_size = sizeof(struct xxdata);
				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:
			xxioc(q, mp);   /*all M_IOCDATA processing here*/
			break;
			.
			.
			.
	} /* switch (mp->b_datap->db_type) */
}

Three pairs of messages are required following the M_IOCTL message:

  1. case GETSTRUCT copies the structure into the message buffer.

  2. case GETINDATA copies the user buffer into the message buffer.

  3. case PUTOUTDATA copies the second message buffer into the user buffer.

xxwput() is the write-side put procedure for module or driver xx. xxwput allocates a message block to contain the state structure and reuses the M_IOCTL to create an M_COPYIN message to read in the xxdata structure.

M_IOCDATA processing is done in xxioc() as shown in the following example:


Example 8–12 M_IOCDATA Processing

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

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

	case XX_IOCTL:
			if (csp->cp_rval) { /* failure */
				if (csp->cp_private) /* state structure */
						freemsg(csp->cp_private);
				freemsg(mp);
				return;
			 }
			stp = (struct state *)csp->cp_private->b_rptr;
			switch (stp->st_state) {

			case GETSTRUCT:			/* xxdata structure copied in */
										/* save structure */

				stp->st_data =
				 *(struct xxdata *)mp->b_cont->b_rptr;
				freemsg(mp->b_cont);
				mp->b_cont = NULL;
				/* Reuse M_IOCDATA to copyin data */
				mp->b_datap->db_type = M_COPYIN;
				cqp = (struct copyreq *)mp->b_rptr;
				cqp->cq_size = stp->st_data.x_inlen;
				cqp->cq_addr = stp->st_data.x_inaddr;
				cqp->cq_flag = 0;
				stp->st_state = GETINDATA; /* next state */
				qreply(q, mp);
				break;

			case GETINDATA: /* data successfully copied in */
				/* Process input, return output */
				if ((mp->b_cont = xx_indata(mp->b_cont))
				 == NULL) { /* hypothetical */
							/* fail xx_indata */
							mp->b_datap->db_type = M_IOCNAK;
							mp->b_wptr = mp->b_rptr +
								sizeof(struct iocblk);
						iocbp->ioc_error = EIO;
						qreply(q, mp);
						break;
				}
				mp->b_datap->db_type = M_COPYOUT;
				cqp = (struct copyreq *)mp->b_rptr;
				cqp->cq_size = min(msgdsize(mp->b_cont),
				 stp->st_data.x_outlen);
				cqp->cq_addr = stp->st_data.x_outaddr;
				cqp->cq_flag = 0;
				stp->st_state = PUTOUTDATA; /* next state */
				qreply(q, mp);
				break;

			case PUTOUTDATA: /* data copied out, ack ioctl */
				freemsg(csp->cp_private); /*state structure*/
				mp->b_datap->db_type = M_IOCACK;
				mp->b_wtpr = mp->b_rptr + sizeof (struct iocblk);
            /* can have been overwritten */
				iocbp->ioc_error = 0;
				iocbp->ioc_count = 0;
				iocbp->ioc_rval = 0;
				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->b_rptr + sizeof (struct iocblk);
				iocbp->ioc_error = EINVAL;
				qreply(q, mp);
				break;
			} /* switch (stp->st_state) */
			break;
	default: /* M_IOCDATA not for us */
			/* if module, pass message on */
			/* if driver, free message */
			break;
	} /* switch (csp->cp_cmd) */
}

At case GETSTRUCT, the user xxdata structure is copied into the module's state structure (pointed to by cp_private in the message) and the M_IOCDATA message is reused to create a second M_COPYIN message to read the user data.

At case GETINDATA, the input user data is processed by xx_indata (not supplied in the example), which frees the linked M_DATA block and returns the output data message block. The M_IOCDATA message is reused to create an M_COPYOUT message to write the user data.

At case PUTOUTDATA, the message block containing the state structure is freed and an acknowledgement is sent upstream.

Care must be taken at the “can't happen” default case since the message block containing the state structure (cp_private) is not returned to the pool because it might not be valid. This might result in a lost block. The ASSERT helps find errors in the module if a “can't happen” condition occurs.