STREAMS Programming Guide

Upper Write Put Procedure

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


static int
/*
* This is our callback routine usesd 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 bottom 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);
 	}
}

First, there is a check to enforce that the stream associated with minor device 0 will be the single, controlling stream. The ioctl(2)s are only accepted on this stream. As described previously, a controlling stream is the one that issues the I_LINK. Having a single control stream is a recommended practice. I_LINK and I_UNLINK include a linkblk structure containing:

l_qtop

The upper write queue from which the ioctl(2) is coming. It always equals q for an I_LINK, and NULL for I_PLINK.

l_qbot

The new lower write queue. It is the former stream head write queue. It is of most interest since that is where the multiplexer gets and puts its data.

l_index

A unique (system-wide) identifier for the link. It can be used for routing or during selective unlinks. Since the example only supports a single link, l_index is not used.

For I_LINK, l_qbot is saved in muxbot and a positive acknowledgment is generated. From this point on, until an I_UNLINK occurs, data from upper queues will be routed through muxbot. Note that when an I_LINK, is received, the lower stream has already been connected. This allows the driver to send messages downstream to perform any initialization functions. Returning an M_IOCNAK message (negative acknowledgment) in response to an I_LINK causes the lower stream to be disconnected.

The I_UNLINK handling code nulls out muxbot and generates a positive acknowledgment. A negative acknowledgment should not be returned to an I_UNLINK. The stream head ensures that the lower stream is connected to a multiplexer before sending an I_UNLINK M_IOCTL.

Drivers can handle the persistent link requests--I_PLINK and I_PUNLINK ioctl(2) (see "Persistent Links") in the same manner, except that l_qtop in the linkblk structure passed to the put routine is NULL instead of identifying the controlling stream.

muxuwput handles M_FLUSH messages as a normal driver does, except that there are no messages queued on the upper read queue, so there is no need to call flushq if FLUSHR is set.

M_DATA messages are not placed on the lower write message queue. They are queued on the upper write message queue. When flow control subsides on the lower stream, the lower service procedure, muxlwsrv, is scheduled to start output. This is similar to starting output on a device driver.