Sun Microsystems, Inc.
spacerspacer
spacer www.sun.com docs.sun.com |
spacer
black dot
 
 
9.  STREAMS Drivers STREAMS Driver Code Samples Printer Driver Example  Previous   Contents   Next 
   
 

The STREAMS mechanism allows only one stream per minor device. The driver open routine is called whenever a STREAMS device is opened. open matches the correct private data structure with the stream using ddi_get_soft_state(9F). The driver open, lpopen in Example 9-4, has the same interface as the module open.

The stream flag, sflag, must have the value 0, indicating a normal driver open. devp pointers to the major/minor device number for the port. After checking sflag, lpopen uses devp to find the correct soft-state structure.

The next check, if (q->q_ptr)..., determines if the printer is already open. q_ptr is a driver or module private data pointer. It can be used by the driver for any purpose and is initialized to zero by STREAMS before the first open. In this example, the driver sets the value of q_ptr, in both the read and write queue structures, to point to the device's per-instance data structure. If the pointer is non-NULL, it means the printer is already open, so lpopen returns EBUSY to avoid merging printouts from multiple users.

The driver close routine is called by the stream head. Any messages left in the queue are automatically removed by STREAMS. The stream is dismantled and data structures are released.


Example 9-4 Queue Processing Entry Points

/*ARGSUSED*/
static int
lpopen(
	queue_t *q,	/* read queue */
	dev_t *devp,
	int flag,
	int sflag,
	cred_t *credp)

{

	struct lp *lp;

	if (sflag)	/* driver refuses to do module or clone open */
			return (ENXIO);

	if ((lp = ddi_get_soft_state(lp_state, getminor(*devp))) == NULL)
			return (ENXIO);

	/* Check if open already. Can't have multiple opens */

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

	lp->qptr = WR(q);
	q->q_ptr = (char *) lp;
	WR(q)->q_ptr = (char *) lp;
	qprocson(q);
	return (0);
}

/*ARGSUSED*/
static int
lpclose(
	queue_t *q,		/* read queue */
	int flag,
	cred_t *credp)
{

	struct lp *lp;

	qprocsoff(q);
	lp = (struct lp *) q->q_ptr;

	/*
	 * Free message, queue is automatically
	 * flushed by STREAMS
	 */
	mutex_enter(&lp->lp_lock);

	if (lp->msg) {
			freemsg(lp->msg);
			lp->msg = NULL;
	}

	lp->flags = 0;
	mutex_exit(&lp->lp_lock);

	return (0);
}

There are no physical pointers between the read and write queue of a pair. WR(9F) is a queue pointer function. WR(9F) generates the write pointer from the read pointer. RD(9F) and OTHERQ(9F) are additional queue pointer functions. RD(9F) generates the read pointer from the write pointer, and OTHERQ(9F) generates the mate pointer from either.

Driver Flush Handling

The write put procedure in Example 9-5, lpwput, illustrates driver M_FLUSH handling. Note that all drivers are expected to incorporate flush handling.

If FLUSHW is set, the write message queue is flushed, and (in this example) the leading message (lp->msg) is also flushed. lp_lock protects the driver's per-instance data structure.

In most drivers, if FLUSHR is set, the read queue is flushed. However, in this example, no messages are ever placed on the read queue, so flushing it is not necessary. The FLUSHW bit is cleared and the message is sent upstream using qreply(9F). If FLUSHR is not set, the message is discarded.

The stream head always performs the following actions on flush requests received on the read side from downstream. If FLUSHR is set, messages waiting to be sent to user space are flushed. If FLUSHW is set, the stream head clears the FLUSHR bit and sends the M_FLUSH message downstream. In this manner, a single M_FLUSH message sent from the driver can reach all queues in a stream. A module must send two M_FLUSH messages to have the same effect.

lpwput queues M_DATA and M_IOCTL messages and if the device is not busy, starts output by calling lpout. Message types that are not recognized are discarded (in the default case of the switch).


Example 9-5 Driver Flush Handling

static int lpwput(
	queue_t *q,	/* write queue */
	mblk_t *mp)	/* message pointer */
{
	struct lp *lp;

	lp = (struct lp *)q->q_ptr;

	switch (mp->b_datap->db_type) {
	default:
		freemsg(mp);
		break;

	case M_FLUSH: /* Canonical flush handling */
		if (*mp->b_rptr & FLUSHW) {
			flushq(q, FLUSHDATA);
			mutex_enter(&lp->lp_lock); /* lock any access to lp */

			if (lp->msg) {
				freemsg(lp->msg);
				lp->msg = NULL;
			}

			mutex_exit(&lp->lp_lock);

		}

		if (*mp->b_rptr & FLUSHR) {
			*mp->b_rptr &= ~FLUSHW;
			qreply(q, mp);
		} else
			freemsg(mp);

		break;

	case M_IOCTL:
	case M_DATA:
		(void) putq(q, mp);
		mutex_enter(&lp->lp_lock);

		if (!(lp->flags & BUSY))
			lpout(lp);

		mutex_exit(&lp->lp_lock);

	}
	return (0);
}

 
 
 
  Previous   Contents   Next