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

Printer Driver Example

Example 9-1 is a sample print driver for an interrupt-per-character line printer. The driver is unidirectional--it has no read-side processing. It demonstrates some differences between module and driver programming, including the following:

  • Declarations for driver configuration

  • Open handling

  • A driver is passed a device number

  • Flush handling

  • A driver must loop M_FLUSH messages back upstream

  • Interrupt routine

  • A driver registers interrupt handler and processes interrupts

Most of the STREAMS processing in the driver is independent of the actual printer hardware; in this example, actual interaction with the printer is limited to the lpoutchar function, which prints one character at a time. For purposes of demonstration, the "printer hardware" is actually the system console, accessed through cmn_err(9F). Since there's no actual hardware to generate a genuine hardware interrupt, lpoutchar simulates interrupts using ddi_trigger_softintr(9F). For a real printer, the lpoutchar function is rewritten to send a character to the printer, which should generate a hardware interrupt.

The driver declarations follow. After specifying header files (include <sys/ddi.h> and <sys/sunddi.h> as the last two header files), the driver declares a per-printer structure, struct lp. This structure contains members that enable the driver to keep track of each instance of the driver, such as flags (what the driver is doing), msg (the current STREAMS print message), qptr (pointer to the stream's write queue), dip (the instance's device information handle), iblock_cookie (for registering an interrupt handler), siid (the handle of the soft interrupt), and lp_lock (a mutex to protect the data structure from multithreaded race conditions). The driver next defines the bits for the flags member of struct lp; the driver defines only one flag, BUSY.

Following function prototypes, the driver provides some standard STREAMS declarations: a module_info(9S) structure (minfo), a qinit(9S) structure for the read side (rinit) that is initialized by the driver's open and close entry points, a qinit(9S) structure for the write side (winit) that is initialized by the write put procedure, and a streamtab(9S) that points to rinit and winit. The values in the module name and ID fields in the module_info(9S) structure must be unique in the system. Because the driver is unidirectional, there is no read side put or service procedure. The flow control limits for use on the write side are 50 bytes for the low-watermark and 150 bytes for the high-watermark.

The driver next declares lp_state. This is an anchor on which the various "soft-state" functions provided by the DDK operate. The ddi_soft_state(9F) manual page describes how to maintain multiple instances of a driver.

The driver next declares acb_ops(9S) structure, which is required in all device drivers. In non-STREAMS device drivers, cb_ops(9S) contains vectors to the table-driven entry points. For STREAMS drivers, however, cb_ops(9S) contains mostly nodev entries. The cb_stream field, however, is initialized with a pointer to the driver's streamtab(9S) structure. This indicates to the kernel that this driver is a STREAMS driver.

Next, the driver declares a dev_ops(9S) structure, which points to the various initialization entry points as well as to the cb_ops(9S) structure. Finally, the driver declares a struct moldrv and a struct modlinkage for use by the kernel linker when the driver is dynamically loaded. struct moldrv contains a pointer to mod_driverops (a significant difference between a STREAMS driver and a STREAMS module--a STREAMS module would contain a pointer to mod_strops instead).


Example 9-1 Simple Line Printer Driver

#include <sys/types.h>
#include <sys/param.h>
#include <sys/stream.h>
#include <sys/stropts.h>
#include <sys/signal.h>
#include <sys/errno.h>
#include <sys/cred.h>
#include <sys/stat.h>
#include <sys/modctl.h>
#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>

/* This is a private data structure, one per minor device number */

struct lp {
	short flags; /* flags -- see below */
	mblk_t *msg; /* current message being output */
	queue_t *qptr; /* back pointer to write queue */
	dev_info_t *dip; /* devinfo handle */
	ddi_iblock_cookie_t iblock_cookie;
	ddi_softintr_t siid;
	kmutex_t lp_lock; /* sync lock */
};

/* flags bits */

#define BUSY 1 /* dev is running, int is forthcoming */

/*
 * Function prototypes.
 */
static int lpattach(dev_info_t *, ddi_attach_cmd_t);
static int lpdetach(dev_info_t *, ddi_detach_cmd_t);
static int lpgetinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
static int lpidentify(dev_info_t *);
static uint lpintr(caddr_t lp);
static void lpout(struct lp *lp);
static void lpoutchar(struct lp *lp, char c);
static int lpopen(queue_t*, dev_t*, int, int, cred_t*);
static int lpclose(queue_t*, int, cred_t*);
static int lpwput(queue_t*, mblk_t*);

/* Standard Streams declarations */

static struct module_info minfo = {
	0xaabb,
	"lp",
	0,
	INFPSZ,
	150,
	50
};

static struct qinit rinit = {
	(int (*)()) NULL,
	(int (*)()) NULL,
	lpopen,
	lpclose,
	(int (*)()) NULL,
	&minfo,
	NULL
};

static struct qinit winit = {
	lpwput,
	(int (*)()) NULL,
	(int (*)()) NULL,
	(int (*)()) NULL,
	(int (*)()) NULL,
	&minfo,
	NULL
};

static struct streamtab lpstrinfo = { &rinit, &winit, NULL, NULL };

/*
 * An opaque handle where our lp lives
 */
static void *lp_state;

/* Module Loading/Unloading and Autoconfiguration declarations */

static struct cb_ops lp_cb_ops = {
	nodev, /* cb_open */
	nodev, /* cb_close */
	nodev, /* cb_strategy */
	nodev, /* cb_print */
	nodev, /* cb_dump */
	nodev, /* cb_read */
	nodev, /* cb_write */
	nodev, /* cb_ioctl */
	nodev, /* cb_devmap */
	nodev, /* cb_mmap */
	nodev, /* cb_segmap */
	nochpoll, /* cb_chpoll */
	ddi_prop_op, /* cb_prop_op */
	&lpstrinfo, /* cb_stream */
	D_MP | D_NEW, /* cb_flag */
};

static struct dev_ops lp_ops = {
	DEVO_REV, /* devo_rev */
	0, /* devo_refcnt */
	lpgetinfo, /* devo_getinfo */
	lpidentify, /* devo_identify */
	nulldev, /* devo_probe */
	lpattach, /* devo_attach */
	lpdetach, /* devo_detach */
	nodev, /* devo_reset */
	&lp_cb_ops, /* devo_cb_ops */
	(struct bus_ops *)NULL /* devo_bus_ops */
};

/*
 * Module linkage information for the kernel.
 */
static struct modldrv modldrv = {
	&mod_driverops,
	"Simple Sample Printer Streams Driver", /* Description */
	&lp_ops, /* driver ops */
};

static struct modlinkage modlinkage = {
	MODREV_1, &modldrv, NULL
};

 
 
 
  Previous   Contents   Next