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

Print Driver Interrupt

Example 9-6 shows the interrupt handling for the printer driver.

lpintr is the driver-interrupt handler registered by the attach routine.

lpout takes a single character from the queue and sends it to the printer. For convenience, the message currently being output is stored in lp->msg in the per-instance structure. This assumes that the message is called with the mutex held.

lpoutchar sends a single character to the printer (in this case the system console using cmn_err(9F)) and interrupts when complete. Of course, hardware would generate a hard interrupt, so the call to ddi_trigger_softintr(9F) would be unnecessary.


Example 9-6 Driver Interrupt Handling

/* Device interrupt routine */static uint
lpintr(caddr_t lp)	 /* minor device number of lp */
{

	struct lp *lpp = (struct lp *)lp;
	
	mutex_enter(&lpp->lp_lock);

	if (!(lpp->flags & BUSY)) {
			mutex_exit(&lpp->lp_lock);
			return (DDI_INTR_UNCLAIMED);
	}

	lpp->flags &= ~BUSY;
	lpout(lpp);
	mutex_exit(&lpp->lp_lock);

	return (DDI_INTR_CLAIMED);
}

/* Start output to device - used by put procedure and driver */

static void
lpout(
	struct lp *lp)
{

	mblk_t *bp;
	queue_t *q;

	q = lp->qptr;

 loop:
	if ((bp = lp->msg) == NULL) { /*no current message*/
			if ((bp = getq(q)) == NULL) {
				lp->flags &= ~BUSY;	
				return;
			}
			if (bp->b_datap->db_type == M_IOCTL) {
				/* lpdoioctl(lp, bp); */
				goto loop;
			}

			lp->msg = bp; /* new message */

		}

	if (bp->b_rptr >= bp->b_wptr) { /* validate message */

			bp = lp->msg->b_cont;
			lp->msg->b_cont = NULL;
			freeb(lp->msg);
			lp->msg = bp;
			goto loop;
	}

	lpoutchar(lp, *bp->b_rptr++); /*output one character*/
	lp->flags |= BUSY;
}

static void
lpoutchar(
	struct lp *lp,
	char c)
{
	cmn_err(CE_CONT, "%c", c);
	ddi_trigger_softintr(lp->siid);
}

Driver Flow Control

The same utilities (described in Chapter 10, STREAMS Modules) and mechanisms used for module flow control are used by drivers.

When the message is queued, putq(9F) increments the value of q_count by the size of the message and compares the result to the driver's write high-watermark (q_hiwat) value. If the count reaches q_hiwat, putq(9F) sets the internal FULL indicator for the driver write queue. This causes messages from upstream to be halted (canputnext(9F) returns FALSE) until the write queue count drops below q_lowat. The driver messages waiting to be output through lpout are dequeued by the driver output interrupt routine with getq(9F), which decrements the count. If the resulting count is below q_lowat, getq(9F) back-enables any upstream queue that had been blocked.

For priority band data, qb_count, qb_hiwat, and qb_lowat are used.

STREAMS with flow control can be used on the driver read side to handle temporary upstream blocks.

To some extent, a driver or a module can control when its upstream transmission becomes blocked. Control is available through the M_SETOPTS message (see Appendix A, Message Types) to modify the stream head read-side flow control limits.

Cloning STREAMS Drivers

To eliminate polling, STREAMS drivers can be made clonable. If a STREAMS driver is implemented as a clonable device, a single node in the file system can be opened to access any unused device that the driver controls. This special node guarantees that each user is allocated a separate stream to the driver for each open call. Each stream is associated with an unused minor device, so the total number of streams that may be connected to a particular clonable driver is limited only by the number of minor devices configured for that driver.

In previous examples, each user process connected a stream to a driver by explicitly opening a particular minor device of the driver. Each minor device had its own node in the device tree file system. Often, there is a need for a user process to connect a new stream to a driver regardless of which minor device is used to access the driver. In the past, this forced the user process to poll the various minor device nodes of the driver for an available minor device.

The clone model is useful, for example, in a networking environment where a protocol pseudo-device driver requires each user to open a separate stream over which it establishes communication. (The decision to implement a STREAMS driver as a clonable device is made by the designers of the device driver. Knowledge of the clone driver implementation is not required to use it.)

There are two ways to open as a clone device. The first is to use the STREAMS framework-provided clone device, which arranges to open the device with the CLONEOPEN flag passed in. This method is demonstrated in Example 9-7, which shows the attach and open routines for the pseudo-terminal master ptm(7D) driver. The second way is to have the driver open itself as a clone device, without intervention from the system clone device. This method is demonstrated in the attach and open routines for the log(7D) device in Example 9-8.

The ptm(7D) device, which uses the system-provided clone device, sets up two nodes in the device file system. One has a major number of 23 (ptm's assigned major number) and a minor number of 0. The other node has a major number of 11 (the clone device's assigned major number) and a minor number of 23 (ptm's assigned major number). The driver's attach routine (see Example 9-7) calls to ddi_create_minor_node(9F) twice. First, to set up the "normal" node (major number 23); second, to specify CLONE_DEV as the last parameter, making the system create the node with major 11.

crw-rw-rw-   1 sys       11, 23 Mar  6 02:05 clone:ptmx
crw-------   1 sys       23,  0 Mar  6 02:05 ptm:ptmajor

When the special file /devices/pseudo/clone@0:ptmx is opened, the clone driver code in the kernel (accessed by major 11) passes the CLONEOPEN flag in the sflag parameter to the ptm(7D) open routine. ptm's open routine checks sflag to make sure it is being called by the clone driver. The open routine next attempts to find an unused minor device for the open by searching its table of minor devices. (PT_ENTER_WRITE and PT_EXIT_WRITE are driver-defined macros for entering and exiting the driver's mutex.) If it succeeds (and following other open processing), the open routine constructs a new dev_t with the new minor number, which it passes back to its caller in the devp parameter. (The new minor number is available to the user program that opened the clonable device through an fstat(2) call.)


Example 9-7 Opening a System Clone Device

static int
ptm_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
{
	  if (cmd != DDI_ATTACH)
			return (DDI_FAILURE);

	  if (ddi_create_minor_node(devi, "ptmajor", S_IFCHR, 0, NULL, 0) 
				== DDI_FAILURE) {
			ddi_remove_minor_node(devi, NULL);
			return (DDI_FAILURE);
	  }
	  if (ddi_create_minor_node(devi, "ptmx", S_IFCHR, 0, NULL, CLONE_DEV) 
				== DDI_FAILURE) {
			ddi_remove_minor_node(devi, NULL);
			return (DDI_FAILURE);
	  }
	  ptm_dip = devi;
	  return (DDI_SUCCESS);
}

static int
ptmopen(
		queue_t	*rqp,		/* pointer to the read side queue */
		dev_t	*devp,		/* pointer to stream tail's dev */
		int		oflag,		/* the user open(2) supplied flags */
		int		sflag,		/* open state flag */
		cred_t	*credp)	/* credentials */
{
		struct pt_ttys	*ptmp;
		mblk_t	*mop;		/* ptr to a setopts message block */
		minor_t	dev;

		if (sflag != CLONEOPEN) {
			return (EINVAL);
		}

		for (dev = 0; dev < pt_cnt; dev++) {
			ptmp = &ptms_tty[dev];
			PT_ENTER_WRITE(ptmp);
			if (ptmp->pt_state & (PTMOPEN | PTSOPEN | PTLOCK)) {
				PT_EXIT_WRITE(ptmp);
			} else
				break;
		}

		if (dev >= pt_cnt) {
			return (ENODEV);
		}

		... <other open processing> ...

		/*
		 * The input, devp, is a major device number, the output is put into
		 * into the same parm as a major,minor pair.
		 */
		*devp = makedevice(getmajor(*devp), dev);
		return (0);
}

 
 
 
  Previous   Contents   Next