/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Pseudo devices are devices implemented entirely in software; pseudonex
* (pseudo) is the traditional nexus for pseudodevices. Instances are
* typically specified via driver.conf files; e.g. a leaf device which
* should be attached below pseudonex will have an entry like:
*
* name="foo" parent="/pseudo" instance=0;
*
* its :devctl minor node. This allows priveleged userland applications to
*
* In general, we discourage widespread use of this tactic, as it may lead to a
* proliferation of nodes in /pseudo. It is preferred that implementors update
* pseudo.conf, adding another 'pseudo' nexus child of /pseudo, and then use
* that for their collection of device nodes. To do so, add a driver alias
* for the name of the nexus child and a line in pseudo.conf such as:
*
* name="foo" parent="/pseudo" instance=<n> valid-children="bar","baz";
*
* Setting 'valid-children' is important because we have an annoying problem;
* we need to prevent pseudo devices with 'parent="pseudo"' set from binding
* to our new pseudonex child node. A better way might be to teach the
* spec-node code to understand that parent="pseudo" really means
* parent="/pseudo".
*
* At some point in the future, it would be desirable to extend the instance
* database to include nexus children of pseudo. Then we could use devctl
* or devfs to online nexus children of pseudo, auto-selecting an instance #,
* and the instance number selected would be preserved across reboot in
* path_to_inst.
*/
#include <sys/ddi_impldefs.h>
#include <sys/instance.h>
/*
* Config information
*/
void *);
static void *pseudonex_state;
typedef struct pseudonex_state {
nullbusmap, /* bus_map */
NULL, /* bus_get_intrspec */
NULL, /* bus_add_intrspec */
NULL, /* bus_remove_intrspec */
i_ddi_map_fault, /* bus_map_fault */
ddi_no_dma_map, /* bus_dma_map */
ddi_no_dma_allochdl, /* bus_dma_allochdl */
NULL, /* bus_dma_freehdl */
NULL, /* bus_dma_bindhdl */
NULL, /* bus_dma_unbindhdl */
NULL, /* bus_dma_flush */
NULL, /* bus_dma_win */
NULL, /* bus_dma_ctl */
pseudonex_ctl, /* bus_ctl */
ddi_bus_prop_op, /* bus_prop_op */
0, /* bus_get_eventcookie */
0, /* bus_add_eventcall */
0, /* bus_remove_eventcall */
0, /* bus_post_event */
NULL, /* bus_intr_ctl */
NULL, /* bus_config */
NULL, /* bus_unconfig */
NULL, /* bus_fm_init */
NULL, /* bus_fm_fini */
NULL, /* bus_fm_access_enter */
NULL, /* bus_fm_access_exit */
NULL, /* bus_power */
pseudonex_intr_op /* bus_intr_op */
};
pseudonex_open, /* open */
pseudonex_close, /* close */
nodev, /* strategy */
nodev, /* print */
nodev, /* dump */
nodev, /* read */
nodev, /* write */
pseudonex_ioctl, /* ioctl */
nodev, /* devmap */
nodev, /* mmap */
nodev, /* segmap */
nochpoll, /* poll */
ddi_prop_op, /* cb_prop_op */
0, /* streamtab */
};
DEVO_REV, /* devo_rev, */
0, /* refcnt */
ddi_getinfo_1to1, /* info */
nulldev, /* identify */
nulldev, /* probe */
pseudonex_attach, /* attach */
pseudonex_detach, /* detach */
nodev, /* reset */
&pseudonex_cb_ops, /* driver operations */
&pseudonex_bus_ops, /* bus operations */
nulldev, /* power */
ddi_quiesce_not_needed, /* quiesce */
};
/*
* Module linkage information for the kernel.
*/
"nexus driver for 'pseudo' 1.31",
};
};
int
_init(void)
{
int err;
sizeof (pseudonex_state_t), 0)) != 0) {
return (err);
}
return (err);
}
return (0);
}
int
_fini(void)
{
int err;
return (err);
return (0);
}
int
{
}
/*ARGSUSED*/
static int
{
int instance;
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
/*
* Save the devi for this instance in the soft_state data.
*/
return (DDI_FAILURE);
DDI_NT_NEXUS, 0) != DDI_SUCCESS) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*ARGSUSED*/
static int
{
if (cmd == DDI_SUSPEND)
return (DDI_SUCCESS);
return (DDI_SUCCESS);
}
/*ARGSUSED*/
static int
{
int instance;
return (EINVAL);
return (ENXIO);
return (0);
}
/*ARGSUSED*/
static int
{
int instance;
return (EINVAL);
return (ENXIO);
return (0);
}
/*ARGSUSED*/
static int
{
int instance;
return (ENXIO);
}
/*
* pseudonex_intr_op: pseudonex convert an interrupt number to an
* interrupt. NO OP for pseudo drivers.
*/
/*ARGSUSED*/
static int
{
return (DDI_FAILURE);
}
static int
{
/* is this the current node? */
continue;
/* is this a duplicate instance? */
return (DDI_FAILURE);
}
}
return (DDI_SUCCESS);
}
/*
* This is a nasty, slow hack. But we're stuck with it until we do some
* major surgery on the instance assignment subsystem, to allow pseudonode
* instance assignment to be tracked there.
*
* To auto-assign an instance number, we exhaustively search the instance
* list for each possible instance number until we find one which is unused.
*/
static int
{
int inst = 0;
/* is this the current node? */
continue;
break;
}
}
return (inst);
}
}
return (-1);
}
static int
{
switch (ctlop) {
case DDI_CTLOPS_REPORTDEV:
return (DDI_FAILURE);
return (DDI_SUCCESS);
case DDI_CTLOPS_INITCHILD:
{
char **childlist;
int auto_assign = 0;
/*
* If this pseudonex node has a valid-children property,
* then that acts as an access control list for children
* allowed to attach beneath this node. Honor it.
*/
&nelems) == DDI_PROP_SUCCESS) {
int i, ok = 0;
for (i = 0; i < nelems; i++) {
ok = 1;
break;
}
}
if (!ok)
return (DDI_FAILURE);
}
/*
* Look up the "instance" property. If it does not exist,
* check to see if the "auto-assign-instance" property is set.
* If not, default to using instance 0; while not ideal, this
* is a legacy behavior we must continue to support.
*/
DDI_PROP_DONTPASS, "auto-assign-instance");
"pseudonex: DDI_CTLOPS_INITCHILD(instance=%d, "
"auto-assign-instance properties specified. "
"Node rejected."));
return (DDI_FAILURE);
}
/* default to instance 0 if not specified */
instance = 0;
}
/*
* If an instance has been specified, determine if this
* instance is already in use; if we need to pick an instance,
* we do it here.
*/
if (auto_assign) {
"auto-select instance for %s", childname));
return (DDI_FAILURE);
}
"auto-selected instance for %s: %d",
} else {
DDI_FAILURE) {
"Duplicate instance %d of node \"%s\" "
return (DDI_FAILURE);
}
"using fixed-assignment instance for %s: %d",
}
/*
* Attach the instance number to the node. This allows
* us to have multiple instances of the same pseudo
* device, they will be named 'device@instance'. If this
* breaks programs, we may need to special-case instance 0
* into 'device'. Ick. devlinks appears to handle the
* new names ok, so if only names in /dev are used
* this may not be necessary.
*/
return (DDI_SUCCESS);
}
case DDI_CTLOPS_UNINITCHILD:
{
"DDI_CTLOPS_UNINITCHILD(%s, instance=%d)",
return (DDI_SUCCESS);
}
case DDI_CTLOPS_DMAPMAPC:
case DDI_CTLOPS_REPORTINT:
case DDI_CTLOPS_REGSIZE:
case DDI_CTLOPS_NREGS:
case DDI_CTLOPS_SIDDEV:
case DDI_CTLOPS_SLAVEONLY:
case DDI_CTLOPS_AFFINITY:
case DDI_CTLOPS_POKE:
case DDI_CTLOPS_PEEK:
/*
* These ops correspond to functions that "shouldn't" be called
* by a pseudo driver. So we whine when we're called.
*/
return (DDI_FAILURE);
case DDI_CTLOPS_ATTACH:
case DDI_CTLOPS_BTOP:
case DDI_CTLOPS_BTOPR:
case DDI_CTLOPS_DETACH:
case DDI_CTLOPS_DVMAPAGESIZE:
case DDI_CTLOPS_IOMIN:
case DDI_CTLOPS_POWER:
case DDI_CTLOPS_PTOB:
default:
/*
* The ops that we pass up (default). We pass up memory
* allocation oriented ops that we receive - these may be
* associated with pseudo HBA drivers below us with target
* drivers below them that use ddi memory allocation
* interfaces like scsi_alloc_consistent_buf.
*/
}
}