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
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