Example 9-10 contains the initialization routines.
Example 9-10 Initialization Routines for the Loop-around Driver
static int
loop_identify(dev_info_t *devi)
{
if (strcmp(ddi_get_name(devi), "loop") == 0)
return (DDI_IDENTIFIED);
else
return (DDI_NOT_IDENTIFIED);
}
static int
loop_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
{
if (cmd != DDI_ATTACH)
return (DDI_FAILURE);
if (ddi_create_minor_node(devi, "loopmajor", S_IFCHR, 0, NULL, 0)
== DDI_FAILURE) {
ddi_remove_minor_node(devi, NULL);
return (DDI_FAILURE);
}
if (ddi_create_minor_node(devi, "loopx", S_IFCHR, 0, NULL, CLONE_DEV)
== DDI_FAILURE) {
ddi_remove_minor_node(devi, NULL);
return (DDI_FAILURE);
}
loop_dip = devi;
return (DDI_SUCCESS);
}
static int
loop_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
{
if (cmd != DDI_DETACH)
return (DDI_FAILURE);
ddi_remove_minor_node(devi, NULL);
return (DDI_SUCCESS);
}
/*ARGSUSED*/
static int
loop_devinfo(
dev_info_t *dip,
ddi_info_cmd_t infocmd,
void *arg,
void **result)
{
int error;
switch (infocmd) {
case DDI_INFO_DEVT2DEVINFO:
if (loop_dip == NULL) {
error = DDI_FAILURE;
} else {
*result = (void *) loop_dip;
error = DDI_SUCCESS;
}
break;
case DDI_INFO_DEVT2INSTANCE:
*result = (void *)0;
error = DDI_SUCCESS;
break;
default:
error = DDI_FAILURE;
}
return (error);
}
|
The open procedure (in Example 9-11)
includes canonical clone processing that enables a single file system node
to yield a new minor device/vnode each time the driver is opened. In loopopen, sflag can be CLONEOPEN,
indicating that the driver picks an unused minor device. In this case, the
driver scans its private loop_loop data structure to find
an unused minor device number. If sflag is not set to CLONEOPEN, the passed-in minor device specified by getminor(*devp) is used.
Example 9-11 Opening the Loop-Around Driver
/*ARGSUSED*/
static int loopopen(
queue_t *q,
dev_t *devp,
int flag,
int sflag,
cred_t *credp)
{
struct loop *loop;
minor_t newminor;
if (q->q_ptr) /* already open */
return(0);
/*
* If CLONEOPEN, pick a minor device number to use.
* Otherwise, check the minor device range.
*/
if (sflag == CLONEOPEN) {
for (newminor = 0; newminor < loop_cnt; newminor++ ) {
if (loop_loop[newminor].qptr == NULL)
break;
}
} else
newminor = getminor(*devp);
if (newminor >= loop_cnt)
return(ENXIO);
/*
* construct new device number and reset devp
* getmajor gets the major number
*/
*devp = makedevice(getmajor(*devp), newminor);
loop = &loop_loop[newminor];
WR(q)->q_ptr = (char *) loop;
q->q_ptr = (char *) loop;
loop->qptr = WR(q);
loop->oqptr = NULL;
qprocson(q);
return(0);
}
|
Because the messages are switched to the read queue following the other
stream's read side, the driver needs a put procedure only
on its write side. loopwput (in Example 9-12)
shows another use of an ioctl(2). The driver supports the ioc_cmd value LOOP_SET in the iocblk(9S) of the M_IOCTL
message. LOOP_SET makes the driver connect the current open stream to the stream indicated in the message. The second block
of the M_IOCTL message holds an integer that specifies
the minor device number of the stream to which to connect.
The LOOP_SET ioctl(2) processing involves several checks: