The log(7D)
driver uses the second method; it clones itself without intervention from
the system clone device. The log(7D) driver's attach routine
(in Example 9-8) is similar to the one in ptm(7D). It creates two nodes using ddi_create_minor_node(9F), but neither specifies CLONE_DEV
as the last parameter. Instead, one of the devices has minor 0, the other
minor CLONEMIN. These two devices provide log(7D) two interfaces: the first
write-only, the second read-write (see the man page log(7D) for more information). Users
open one node or the other. If they open the CONSWMIN (clonable,
read-write) node, the open routine checks its table of
minor devices for an unused device. If it is successful, it (like the ptm(7D) open routine) returns the new dev_t to its caller
in devp.
Example 9-8 Opening the log Driver
static int
log_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
{
if (ddi_create_minor_node(devi, "conslog", S_IFCHR, 0, NULL, NULL)
== DDI_FAILURE ||
ddi_create_minor_node(devi, "log", S_IFCHR, 5, NULL, NULL)
== DDI_FAILURE) {
ddi_remove_minor_node(devi, NULL);
return (-1);
}
log_dip = devi;
return (DDI_SUCCESS);
}
static int
logopen(
queue_t *q,
dev_t *devp,
int flag,
int sflag,
cred_t *cr)
{
int i;
struct log *lp;
/*
* A MODOPEN is invalid and so is a CLONEOPEN.
* This is because a clone open comes in as a CLONEMIN device open!!
*/
if (sflag)
return (ENXIO);
mutex_enter(&log_lock);
switch (getminor(*devp)) {
case CONSWMIN:
if (flag & FREAD) { /* you can only write to this minor */
mutex_exit(&log_lock);
return (EINVAL);
}
if (q->q_ptr) { /* already open */
mutex_exit(&log_lock);
return (0);
}
lp = &log_log[CONSWMIN];
break;
case CLONEMIN:
/*
* Find an unused minor > CLONEMIN.
*/
i = CLONEMIN + 1;
for (lp = &log_log[i]; i < log_cnt; i++, lp++) {
if (!(lp->log_state & LOGOPEN))
break;
}
if (i >= log_cnt) {
mutex_exit(&log_lock);
return (ENXIO);
}
*devp = makedevice(getmajor(*devp), i); /* clone it */
break;
default:
mutex_exit(&log_lock);
return (ENXIO);
}
/*
* Finish device initialization.
*/
lp->log_state = LOGOPEN;
lp->log_rdq = q;
q->q_ptr = (void *)lp;
WR(q)->q_ptr = (void *)lp;
mutex_exit(&log_lock);
qprocson(q);
return (0);
}
|
Loop-Around Driver
The loop-around driver is a pseudo-driver that loops data from one open
stream to another open stream. The associated files are
almost like a full-duplex pipe to user processes. The streams are not physically
linked. The driver is a simple multiplexer that passes messages from one stream's
write queue to the other stream's read queue.
To create a connection, a process opens two streams, obtains the minor
device number associated with one of the returned file descriptors, and sends
the device number in an ioctl(2) to the other stream. For each open, the
driver open places the passed queue pointer in a driver
interconnection table, indexed by the device number. When the driver later
receives an M_IOCTL message, it uses the device number
to locate the other stream's interconnection table entry, and stores the appropriate
queue pointers in both of the streams' interconnection table entries.
Subsequently, when messages other than M_IOCTL or M_FLUSH are received by the driver on either stream's write side,
the messages are switched to the read queue following the driver on the other
stream's read side. The resultant logical connection is shown in Figure 9-2.
Flow control between the two streams must be handled explicitly, since STREAMS
do not automatically propagate flow control information between two streams
that are not physically connected.
Figure 9-2 Loop-Around Streams
Example 9-9 shows the loop-around driver code. The loop structure contains the interconnection information for a pair
of streams. loop_loop is indexed by the minor device number.
When a stream is opened to the driver, the driver places the address of the
corresponding loop_loop element in the q_ptr
(private data structure pointer) of the read-side and write-side queues. Since
STREAMS clears q_ptr when the queue is allocated, a NULL
value of q_ptr indicates an initial open. loop_loop verifies that this stream is connected to another open stream.
The code presented here for the loop-around driver represents a single-threaded,
uniprocessor implementation. Chapter 12, Multithreaded STREAMS presents
multiprocessor and multithreading issues such as locking to prevent race conditions
and data corruption.
Example 9-9 contains the declarations for the driver.
Example 9-9 Declarations for the Loop-Around Driver
/* Loop-around 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>
static int loop_identify(dev_info_t *);
static int loop_attach(dev_info_t *, ddi_attach_cmd_t);
static int loop_detach(dev_info_t *, ddi_detach_cmd_t);
static int loop_devinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
static int loopopen (queue_t*, dev_t*, int, int, cred_t*);
static int loopclose (queue_t*, int, cred_t*);
static int loopwput (queue_t*, mblk_t*);
static int loopwsrv (queue_t*);
static int looprsrv (queue_t*);
static dev_info_t *loop_dip; /* private devinfo pointer */
static struct module_info minfo = {
0xee12,
"loop",
0,
INFPSZ,
512,
128
};
static struct qinit rinit = {
(int (*)()) NULL,
looprsrv,
loopopen,
loopclose,
(int (*)()) NULL,
&minfo,
NULL
};
static struct qinit winit = {
loopwput,
loopwsrv,
(int (*)()) NULL,
(int (*)()) NULL,
(int (*)()) NULL,
&minfo,
NULL
};
static struct streamtab loopinfo= {
&rinit,
&winit,
NULL,
NULL
};
struct loop {
queue_t *qptr; /* back pointer to write queue */
queue_t *oqptr; /* pointer to connected read queue */
};
#define LOOP_CONF_FLAG (D_NEW | D_MP)
static struct cb_ops cb_loop_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 */
( &loopinfo), /* cb_stream */
(int)(LOOP_CONF_FLAG) /* cb_flag */
};
static struct dev_ops loop_ops = {
DEVO_REV, /* devo_rev */
0, /* devo_refcnt */
(loop_devinfo), /* devo_getinfo */
(loop_identify), /* devo_identify */
(nulldev), /* devo_probe */
(loop_attach), /* devo_attach */
(loop_detach), /* devo_detach */
(nodev), /* devo_reset */
&(cb_loop_ops), /* devo_cb_ops */
(struct bus_ops *)NULL, /* devo_bus_ops */
(int (*)()) NULL /* devo_power */
};
#define LOOP_SET ((`l'<<8)|1) /* in a .h file */
#define NLOOP 64
static struct loop loop_loop[NLOOP];
static int loop_cnt = NLOOP;
/*
* Module linkage information for the kernel.
*/
extern struct mod_ops mod_strmodops;
static struct modldrv modldrv = {
&mod_driverops, "STREAMS loop driver", &loop_ops
};
static struct modlinkage modlinkage = {
MODREV_1, &modldrv, NULL
};
_init()
{
return (mod_install(&modlinkage));
}
_info(modinfop)
struct modinfo *modinfop;
{
return (mod_info(&modlinkage, modinfop));
}
_fini(void)
{
return (mod_remove(&modlinkage));
}
|