#include <sys/types.h>
#include <sys/cmn_err.h>
#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/ddi_impldefs.h>
#include <sys/devops.h>
#include <sys/instance.h>
#include <sys/modctl.h>
#include <sys/open.h>
#include <sys/stat.h>
#include <sys/sunddi.h>
#include <sys/sunndi.h>
#include <sys/systm.h>
#include <sys/mkdev.h>
static int pseudonex_intr_op(dev_info_t *dip, dev_info_t *rdip,
ddi_intr_op_t op, ddi_intr_handle_impl_t *hdlp, void *result);
static int pseudonex_attach(dev_info_t *, ddi_attach_cmd_t);
static int pseudonex_detach(dev_info_t *, ddi_detach_cmd_t);
static int pseudonex_open(dev_t *, int, int, cred_t *);
static int pseudonex_close(dev_t, int, int, cred_t *);
static int pseudonex_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
static int pseudonex_fm_init(dev_info_t *, dev_info_t *, int,
ddi_iblock_cookie_t *);
static int pseudonex_ctl(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *,
void *);
static void *pseudonex_state;
typedef struct pseudonex_state {
dev_info_t *pnx_devi;
int pnx_fmcap;
ddi_iblock_cookie_t pnx_fm_ibc;
} pseudonex_state_t;
static struct bus_ops pseudonex_bus_ops = {
BUSO_REV,
nullbusmap,
NULL,
NULL,
NULL,
i_ddi_map_fault,
ddi_no_dma_map,
ddi_no_dma_allochdl,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
pseudonex_ctl,
ddi_bus_prop_op,
0,
0,
0,
0,
NULL,
NULL,
NULL,
pseudonex_fm_init,
NULL,
NULL,
NULL,
NULL,
pseudonex_intr_op
};
static struct cb_ops pseudonex_cb_ops = {
pseudonex_open,
pseudonex_close,
nodev,
nodev,
nodev,
nodev,
nodev,
pseudonex_ioctl,
nodev,
nodev,
nodev,
nochpoll,
ddi_prop_op,
0,
D_MP | D_NEW | D_HOTPLUG
};
static struct dev_ops pseudo_ops = {
DEVO_REV,
0,
ddi_getinfo_1to1,
nulldev,
nulldev,
pseudonex_attach,
pseudonex_detach,
nodev,
&pseudonex_cb_ops,
&pseudonex_bus_ops,
nulldev,
ddi_quiesce_not_needed,
};
static struct modldrv modldrv = {
&mod_driverops,
"nexus driver for 'pseudo' 1.31",
&pseudo_ops,
};
static struct modlinkage modlinkage = {
MODREV_1, (void *)&modldrv, NULL
};
int
_init(void)
{
int err;
if ((err = ddi_soft_state_init(&pseudonex_state,
sizeof (pseudonex_state_t), 0)) != 0) {
return (err);
}
if ((err = mod_install(&modlinkage)) != 0) {
ddi_soft_state_fini(&pseudonex_state);
return (err);
}
return (0);
}
int
_fini(void)
{
int err;
if ((err = mod_remove(&modlinkage)) != 0)
return (err);
ddi_soft_state_fini(&pseudonex_state);
return (0);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
static int
pseudonex_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
{
int instance;
pseudonex_state_t *pnx_state;
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
instance = ddi_get_instance(devi);
if (ddi_soft_state_zalloc(pseudonex_state, instance) != DDI_SUCCESS)
return (DDI_FAILURE);
pnx_state = ddi_get_soft_state(pseudonex_state, instance);
pnx_state->pnx_devi = devi;
pnx_state->pnx_fmcap = DDI_FM_EREPORT_CAPABLE;
ddi_fm_init(devi, &pnx_state->pnx_fmcap, &pnx_state->pnx_fm_ibc);
if (ddi_create_minor_node(devi, "devctl", S_IFCHR, instance,
DDI_NT_NEXUS, 0) != DDI_SUCCESS) {
ddi_remove_minor_node(devi, NULL);
ddi_soft_state_free(pseudonex_state, instance);
return (DDI_FAILURE);
}
ddi_report_dev(devi);
return (DDI_SUCCESS);
}
static int
pseudonex_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
{
int instance = ddi_get_instance(devi);
if (cmd == DDI_SUSPEND)
return (DDI_SUCCESS);
if (cmd != DDI_DETACH)
return (DDI_FAILURE);
ddi_fm_fini(devi);
ddi_remove_minor_node(devi, NULL);
ddi_soft_state_free(pseudonex_state, instance);
return (DDI_SUCCESS);
}
static int
pseudonex_open(dev_t *devp, int flags, int otyp, cred_t *credp)
{
int instance;
if (otyp != OTYP_CHR)
return (EINVAL);
instance = getminor(*devp);
if (ddi_get_soft_state(pseudonex_state, instance) == NULL)
return (ENXIO);
return (0);
}
static int
pseudonex_close(dev_t dev, int flags, int otyp, cred_t *credp)
{
int instance;
if (otyp != OTYP_CHR)
return (EINVAL);
instance = getminor(dev);
if (ddi_get_soft_state(pseudonex_state, instance) == NULL)
return (ENXIO);
return (0);
}
static int
pseudonex_ioctl(dev_t dev,
int cmd, intptr_t arg, int mode, cred_t *cred_p, int *rval_p)
{
int instance;
pseudonex_state_t *pnx_state;
instance = getminor(dev);
if ((pnx_state = ddi_get_soft_state(pseudonex_state, instance)) == NULL)
return (ENXIO);
ASSERT(pnx_state->pnx_devi);
return (ndi_devctl_ioctl(pnx_state->pnx_devi, cmd, arg, mode, 0));
}
static int
pseudonex_intr_op(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t op,
ddi_intr_handle_impl_t *hdlp, void *result)
{
return (DDI_FAILURE);
}
static int
pseudonex_check_assignment(dev_info_t *child, int test_inst)
{
dev_info_t *tdip;
kmutex_t *dmp;
const char *childname = ddi_driver_name(child);
major_t childmaj = ddi_name_to_major((char *)childname);
dmp = &devnamesp[childmaj].dn_lock;
LOCK_DEV_OPS(dmp);
for (tdip = devnamesp[childmaj].dn_head;
tdip != NULL; tdip = ddi_get_next(tdip)) {
if (tdip == child)
continue;
if (test_inst == ddi_get_instance(tdip)) {
UNLOCK_DEV_OPS(dmp);
return (DDI_FAILURE);
}
}
UNLOCK_DEV_OPS(dmp);
return (DDI_SUCCESS);
}
static int
pseudonex_auto_assign(dev_info_t *child)
{
dev_info_t *tdip;
kmutex_t *dmp;
const char *childname = ddi_driver_name(child);
major_t childmaj = ddi_name_to_major((char *)childname);
int inst = 0;
dmp = &devnamesp[childmaj].dn_lock;
LOCK_DEV_OPS(dmp);
for (inst = 0; inst <= MAXMIN32; inst++) {
for (tdip = devnamesp[childmaj].dn_head; tdip != NULL;
tdip = ddi_get_next(tdip)) {
if (tdip == child)
continue;
if (inst == ddi_get_instance(tdip)) {
break;
}
}
if (tdip == NULL) {
UNLOCK_DEV_OPS(dmp);
return (inst);
}
}
UNLOCK_DEV_OPS(dmp);
return (-1);
}
static int
pseudonex_fm_init(dev_info_t *dip, dev_info_t *tdip, int cap,
ddi_iblock_cookie_t *ibc)
{
pseudonex_state_t *pnx_state;
pnx_state = ddi_get_soft_state(pseudonex_state, ddi_get_instance(dip));
ASSERT(pnx_state != NULL);
ASSERT(ibc != NULL);
*ibc = pnx_state->pnx_fm_ibc;
return (pnx_state->pnx_fmcap & cap);
}
static int
pseudonex_ctl(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t ctlop,
void *arg, void *result)
{
switch (ctlop) {
case DDI_CTLOPS_REPORTDEV:
if (rdip == NULL)
return (DDI_FAILURE);
cmn_err(CE_CONT, "?pseudo-device: %s%d\n",
ddi_driver_name(rdip), ddi_get_instance(rdip));
return (DDI_SUCCESS);
case DDI_CTLOPS_INITCHILD:
{
char name[12];
int instance = -1;
dev_info_t *child = (dev_info_t *)arg;
const char *childname = ddi_driver_name(child);
char **childlist;
uint_t nelems;
int auto_assign = 0;
if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, dip,
DDI_PROP_DONTPASS, "valid-children", &childlist,
&nelems) == DDI_PROP_SUCCESS) {
int i, ok = 0;
for (i = 0; i < nelems; i++) {
if (strcmp(childlist[i], childname) == 0) {
ok = 1;
break;
}
}
ddi_prop_free(childlist);
if (!ok)
return (DDI_FAILURE);
}
instance = ddi_prop_get_int(DDI_DEV_T_ANY, child,
DDI_PROP_DONTPASS, "instance", -1);
auto_assign = ddi_prop_exists(DDI_DEV_T_ANY, child,
DDI_PROP_DONTPASS, "auto-assign-instance");
NDI_CONFIG_DEBUG((CE_NOTE,
"pseudonex: DDI_CTLOPS_INITCHILD(instance=%d, "
"auto-assign=%d)", instance, auto_assign));
if (instance != -1 && auto_assign != 0) {
NDI_CONFIG_DEBUG((CE_NOTE, "both instance and "
"auto-assign-instance properties specified. "
"Node rejected."));
return (DDI_FAILURE);
}
if (instance == -1 && auto_assign == 0) {
NDI_CONFIG_DEBUG((CE_NOTE, "defaulting to 0"));
instance = 0;
}
if (auto_assign) {
if ((instance = pseudonex_auto_assign(child)) == -1) {
NDI_CONFIG_DEBUG((CE_NOTE, "failed to "
"auto-select instance for %s", childname));
return (DDI_FAILURE);
}
NDI_CONFIG_DEBUG((CE_NOTE,
"auto-selected instance for %s: %d",
childname, instance));
} else {
if (pseudonex_check_assignment(child, instance) ==
DDI_FAILURE) {
NDI_CONFIG_DEBUG((CE_WARN,
"Duplicate instance %d of node \"%s\" "
"ignored.", instance, childname));
return (DDI_FAILURE);
}
NDI_CONFIG_DEBUG((CE_NOTE,
"using fixed-assignment instance for %s: %d",
childname, instance));
}
(void) snprintf(name, sizeof (name), "%d", instance);
DEVI(child)->devi_instance = instance;
ddi_set_name_addr(child, name);
return (DDI_SUCCESS);
}
case DDI_CTLOPS_UNINITCHILD:
{
dev_info_t *child = (dev_info_t *)arg;
NDI_CONFIG_DEBUG((CE_NOTE,
"DDI_CTLOPS_UNINITCHILD(%s, instance=%d)",
ddi_driver_name(child), DEVI(child)->devi_instance));
ddi_set_name_addr(child, NULL);
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:
cmn_err(CE_CONT, "%s%d: invalid op (%d) from %s%d\n",
ddi_driver_name(dip), ddi_get_instance(dip), ctlop,
ddi_driver_name(rdip), ddi_get_instance(rdip));
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:
return (ddi_ctlops(dip, rdip, ctlop, arg, result));
}
}