Sun Microsystems, Inc.
spacerspacer
spacer www.sun.com docs.sun.com |
spacer
black dot
 
 
13.  STREAMS Multiplex Drivers Multiplexing Driver Example  Previous   Contents   Next 
   
 

The four streamtab entries correspond to the upper read, upper write, lower read, and lower write qinit structures. The multiplexing qinit structures replace those in each lower stream head (in this case there is only one) after the I_LINK has concluded successfully. In a multiplexing configuration, the processing performed by the multiplexing driver can be partitioned between the upper and lower queues. There must be an upper-stream write put procedure and lower-stream read put procedure. If the queue procedures of the opposite upper/lower queue are not needed, the queue can be skipped, and the message put to the following queue.

In the example, the upper read-side procedures are not used. The lower-stream read queue put procedure transfers the message directly to the read queue upstream from the multiplexer. There is no lower write put procedure because the upper write put procedure directly feeds the lower write queue downstream from the multiplexer.

The driver uses a private data structure, mux. mux_mux[dev] points back to the opened upper read queue. This is used to route messages coming upstream from the driver to the appropriate upper queue. It is also used to find a free major or minor device for a CLONEOPEN driver open case.

Example 13-3, the upper queue open, contains the canonical driver open code.


Example 13-3 Upper Queue Open

static int
muxopen(queue_t *q, dev_t *devp, int flag,
				 int sflag, cred_t *credp)
{
	struct mux *mux;
	minor_t device;

	if (q->q_ptr)
			return(EBUSY);

	if (sflag == CLONEOPEN) {
			for (device = 0; device < mux_cnt; device++)
				if (mux_mux[device].qptr == 0)
						break;

			*devp=makedevice(getmajor(*devp), device);
	}
	else {
			device = getminor(*devp);
			if (device >= mux_cnt)
				return ENXIO;
	}

	mux = &mux_mux[device];
	mux->qptr = q;
	q->q_ptr = (char *) mux;
	WR(q)->q_ptr = (char *) mux;
	qprocson(q);
	return (0);
}

muxopen checks for a clone or ordinary open call. It initializes q_ptr to point at the mux_mux[] structure.

The core multiplexer processing is as follows: downstream data written to an upper stream is queued on the corresponding upper write message queue if the lower stream is flow controlled. This allows flow control to propagate toward the stream head for each upper stream. A lower write service procedure, rather than a write put procedure, is used so that flow control, coming up from the driver below, may be handled.

On the lower read side, data coming up the lower stream are passed to the lower read put procedure. The procedure routes the data to an upper stream based on the first byte of the message. This byte holds the minor device number of an upper stream. The put procedure handles flow control by testing the upper stream at the first upper read queue beyond the driver.

Upper Write put Procedure Sample

muxuwput, the upper-queue write put procedure, traps ioctl calls, in particular I_LINK and I_UNLINK:


Example 13-4 bufcall Callback Routine

static int
/*
* This is our callback routine used by bufcall() to inform us
* when buffers become available
*/
static void mux_qenable(long ql)
{
		queue_t *q = (queue_t *ql);
		struct mux *mux;

		mux = (struct mux *)(q->q_ptr);
		mux->bufcid = 0;
		qenable(q);
}
muxuwput(queue_t *q, mblk_t *mp)
{
	struct mux *mux;

	mux = (struct mux *)q->q_ptr;
	switch (mp->b_datap->db_type) {
	case M_IOCTL: {
			struct iocblk *iocp;
			struct linkblk *linkp;
			/*
			 * ioctl. Only channel 0 can do ioctls. Two
			 * calls are recognized: LINK, and UNLINK
			 */
			if (mux != mux_mux)
				goto iocnak;

			iocp = (struct iocblk *) mp->b_rptr;
			switch (iocp->ioc_cmd) {
			case I_LINK:
				/*
				 *Link. The data contains a linkblk structure
				 *Remember the bottom queue in muxbot.
				 */
				if (muxbot != NULL)
						goto iocnak;

				linkp=(struct linkblk *) mp->b_cont->b_rptr;
				muxbot = linkp->l_qbot;
				muxerr = 0;

				mp->b_datap->db_type = M_IOCACK;
				iocp->ioc_count = 0;
				qreply(q, mp);
				break;
			case I_UNLINK:
				/*
				 * Unlink. The data contains a linkblk struct.
				 * Should not fail an unlink. Null out muxbot.
				 */
				linkp=(struct linkblk *) mp->b_cont->b_rptr;
				muxbot = NULL;
				mp->b_datap->db_type = M_IOCACK;
				iocp->ioc_count = 0;
				qreply(q, mp);
				break;
			default:
			iocnak:
				/* fail ioctl */
				mp->b_datap->db_type = M_IOCNAK;
				qreply(q, mp);
			}
			break;
	}
	case M_FLUSH:
			if (*mp->b_rptr & FLUSHW)
				flushq(q, FLUSHDATA);
			if (*mp->b_rptr & FLUSHR) {
				*mp->b_rptr &= ~FLUSHW;
				qreply(q, mp);
			} else
				freemsg(mp);
			break;





	case M_DATA:{
			 */
			 * Data. If we have no lower queue --> fail
			 * Otherwise, queue the data and invoke the lower
			 * service procedure.
		mblk_t *bp;
			if (muxerr || muxbot == NULL)
				goto bad;
		if ((bp = allocb(1, BPRI_MED)) == NULL) {
				putbq(q, mp);
				mux->bufcid = bufcall(1, BPRI_MED,
					mux_qenable, (long)q);
				break;
			}
			*bp->b_wptr++ = (struct mux *)q->ptr - mux_mux;
			bp->b_cont = mp;
			putq(q, bp);
			break;
}
	default:
	bad:
			/*
			 * Send an error message upstream.
			 */
			mp->b_datap->db_type = M_ERROR;
			mp->b_rptr = mp->b_wptr = mp->b_datap->db_base;
			*mp->b_wptr++ = EINVAL;
			qreply(q, mp);
 	}
}

 
 
 
  Previous   Contents   Next