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

First, there is a check to enforce that the stream associated with minor device 0 will be the single, controlling stream. The ioctls are only accepted on this stream. As described previously, a controlling stream is the one that issues the I_LINK. There should be only a single control stream. I_LINK and I_UNLINK include a linkblk structure containing the following fields:

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

l_qbot is the new lower write queue. It is the former stream head write queue and is where the multiplexer gets and puts its data.

l_index is 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 acknowledgement 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 enables the driver to send messages downstream to perform any initialization functions. Returning an M_IOCNAK message (negative acknowledgement) 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 acknowledgement. A negative acknowledgement 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) 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.

Upper Write service Procedure Sample

The following example shows the code for the upper multiplexer write service procedure:


Example 13-5 Upper Multiplexer Write Service Procedure

static int muxuwsrv(queue_t *q)
{
	mblk_t *mp;
	struct mux *muxp;
	muxp = (struct mux *)q->q_ptr;

	if (!muxbot) {
			flushq(q, FLUSHALL);
			return (0);
	}
	if (muxerr) {
			flushq(q, FLUSHALL);
			return (0);
	}
	while (mp = getq(q)) {
			if (canputnext(muxbot))
				putnext(muxbot, mp);
			else {
				putbq(q, mp);
				return(0);
			}
	}
	return (0);
}

As long as there is a stream still linked under the multiplexer and there are no errors, the service procedure will take a message off the queue and send it downstream, if flow control allows.

Lower Write service Procedure

muxlwsrv, the lower (linked) queue write service procedure is scheduled as a result of flow control subsiding downstream (it is back-enabled).

static int muxlwsrv(queue_t *q)
{
	int i;

	for (i = 0; i < mux_cnt; i++)
			if (mux_mux[i].qptr && mux_mux[i].qptr->q_first)
				qenable(mux_mux[i].qptr);
	return (0);
}

muxlwsrv steps through all possible upper queues. If a queue is active and there are messages on the queue, then its upper write service procedure is enabled through qenable.

Lower Read put Procedure

The lower (linked) queue read put procedure is shown in the following example:


Example 13-6 Lower Read put Procedure

static int
muxlrput(queue_t *q, mblk_t *mp)
{
	queue_t *uq;
	int device;

	if(muxerr) {
			freemsg(mp);
			return (0);
	}

	switch(mp->b_datap->db_type) {
	case M_FLUSH:
			/*
			 * Flush queues. NOTE: sense of tests is reversed
			 * since we are acting like a "stream head"
			 */
			if (*mp->b_rptr & FLUSHW) {
				*mp->b_rptr &= ~FLUSHR;
				qreply(q, mp);
			} else
				freemsg(mp);
			break;
	case M_ERROR:
	case M_HANGUP:
			muxerr = 1;
			freemsg(mp);
			break;
	case M_DATA:
			/*
			 * Route message. First byte indicates
			 * device to send to. No flow control.
			 *
			 * Extract and delete device number. If the
			 * leading block is now empty and more blocks
			 * follow, strip the leading block.
			 */
			device = *mp->b_rptr++;

			/* Sanity check. Device must be in range */
			if (device < 0 || device >= mux_cnt) {
				freemsg(mp);
				break;
			}
			/*
			 * If upper stream is open and not backed up,
			 * send the message there, otherwise discard it.
			 */
			uq = mux_mux[device].qptr;
			if (uq != NULL && canputnext(uq))
				putnext(uq, mp);
			else
				freemsg(mp);
			break;
	default:
			freemsg(mp);
	}
	return (0);
}

 
 
 
  Previous   Contents   Next