STREAMS Programming Guide

STREAMS Driver Entry Points

As described in Chapter 9, STREAMS Drivers and as seen in the previous data structures, there are four STREAMS driver entry points:

load kernel module

_init(9E), _fini(9E), _info(9E)

dev_ops

identify(9E), attach(9E), getinfo(9E)

cb_ops

open(9E), close(9E), read(9E), write(9E), ioctl(9E)

streamtab

put(9E), srv(9E)

pts Example

The following real example was taken from the Solaris operating environment. The driver pts(7D) is the pseudo terminal slave driver.


Example 11–1 Stream Pseudo Terminal Module

/*
 * Slave Stream Pseudo Terminal Module
 */

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

static int ptsopen (queue_t*, dev_t*, int, int, cred_t);
static int ptsclose (queue_t*, int, cred_t*);
static int ptswput (queue_t*, mblk_t*);
static int ptsrsrv (queue_t*);
static int ptswsrv (queue_t*);

static int pts_devinfo(dev_info_t *dip, ddi_info_cmd_t infocmd,
      void *arg,void **result);

static struct module_info pts_info = {
		0xface,
		"pts",
		0,
		512,
		512,
		128
};

static struct qinit ptsrint = {
		NULL,
		ptsrsrv,
		ptsopen,
		ptsclose,
		NULL,
		&pts_info,
		NULL
};

static struct qinit ptswint = {
		ptswput,
		ptswsrv,
		NULL,
		NULL,
		NULL,
		&pts_info,
		NULL
};

static struct streamtab ptsinfo = {
		&ptsrint,
		&ptswint,
		NULL,
		NULL
};

static int pts_identify(dev_info_t *devi);
static int pts_attach(dev_info_t *devi, ddi_attach_cmd_t cmd);
static int pts_detach(dev_info_t *devi, ddi_detach_cmd_t cmd);
static dev_info_t *pts_dip;				/* private copy of devinfo ptr */

extern kmutex_t pt_lock;
extern pt_cnt;
static struct cb_ops cb_pts_ops = {
   nulldev,       /* cb_open */ 
   nulldev,       /* 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 */
   &ptsinfo,      /* cb_stream */
   D_MP           /* cb_flag */
};

static struct dev_ops pts_ops = {
   DEVO_REV,      /* devo_rev */
   0,             /* devo_refcnt */   
   pts_devinfo,   /* devo_getinfo */ 
   pts_identify,  /* devo_identify */
   nulldev,       /* devo_probe */
   pts_attach,    /* devo_attach */
   pts_detach,    /* devo_detach */
   nodev,         /* devo_reset */
   &cb_pts_ops,   /* devo_cb_ops */
   (struct bus_ops*) NULL   /* devo_bus_ops */
};

/*
 * Module linkage information for the kernel.
 */

static struct modldrv modldrv = {
			&mod_driverops, 		/* Type of module: a pseudo driver */
			"Slave Stream Pseudo Terminal driver'pts'",
			&pts_ops,				/* driver ops */
};

static struct modlinkage modlinkage = {
		MODREV_1,
		(void *)&modldrv,
		NULL
};

int
_init(void)
{
		return (mod_install(&modlinkage));
}

int
_fini(void)
{
		return (mod_remove(&modlinkage));
}

int
_info(struct modinfo *modinfop)
{
		return (mod_info(&modlinkage, modinfop));
}

static int
pts_identify(dev_info_t *devi)
{
		if (strcmp(ddi_get_name(devi), "pts") == 0)
			return (DDI_IDENTIFIED);
		else
			return (DDI_NOT_IDENTIFIED);
}

static int
pts_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
{
		int i;
		char name[5];

		if (cmd != DDI_ATTACH)
			return (DDI_FAILURE);
	
		for (i = 0; i < pt_cnt; i++) {
			(void) sprintf(name, "%d", i);
			if (ddi_create_minor_node(devi, name, S_IFCHR, i, NULL, 0) 
						== DDI_FAILURE) {
				ddi_remove_minor_node(devi, NULL);
				return (DDI_FAILURE);
			}
		}
		return (DDI_SUCCESS);
}

static int
pts_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
{
		ddi_remove_minor_node(devi, NULL);
		return (DDI_SUCCESS);
}

static int
pts_devinfo (dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
					 void **result)
{
		int error;

		switch (infocmd)   {
			case DDI_INFO_DEVT2DEVINFO:
				if (pts_dip == NULL) {
					error = DDI_FAILURE;
				} else {
					*result = (void *) pts_dip;
					error = DDI_SUCCESS;
				}
				break;
			case DDI_INFO_DEVT2INSTANCE:
				*result = (void *) 0;
				error = DDI_SUCCESS;
				break;
			default:
				error = DDI_FAILURE;
		}
		return (error);
}

/* the open, close, wput, rsrv, and wsrv routines are presented
 * here solely for the sake of showing how they interact with the
 * configuration data structures and routines. Therefore, the 
 * bulk of their code is not included.
 */
static int
ptsopen(rqp, devp, oflag, sflag, credp)
		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 */
{
		qprocson(rqp);
		return (0);
}

static int
ptsclose(rqp, flag, credp)
		queue_t		*rqp;
		int			flag;
		cred_t		*credp;
{
		qprocsoff(rqp);
		return (0);
}

static int
ptswput(qp, mp)
		queue_t		*qp;
		mblk_t		*mp;
{
		return (0);
}

static int
ptsrsrv(qp)
		queue_t *qp;
{
		return (0);
}

static int
ptswsrv(qp)
		queue_t 	*qp;
{
		return (0);
}