1*0Sstevel@tonic-gate /* 2*0Sstevel@tonic-gate * CDDL HEADER START 3*0Sstevel@tonic-gate * 4*0Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*0Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*0Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*0Sstevel@tonic-gate * with the License. 8*0Sstevel@tonic-gate * 9*0Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*0Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*0Sstevel@tonic-gate * See the License for the specific language governing permissions 12*0Sstevel@tonic-gate * and limitations under the License. 13*0Sstevel@tonic-gate * 14*0Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*0Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*0Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*0Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*0Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*0Sstevel@tonic-gate * 20*0Sstevel@tonic-gate * CDDL HEADER END 21*0Sstevel@tonic-gate */ 22*0Sstevel@tonic-gate /* 23*0Sstevel@tonic-gate * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24*0Sstevel@tonic-gate * Use is subject to license terms. 25*0Sstevel@tonic-gate */ 26*0Sstevel@tonic-gate 27*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 28*0Sstevel@tonic-gate 29*0Sstevel@tonic-gate /* 30*0Sstevel@tonic-gate * Pseudo devices are devices implemented entirely in software; pseudonex 31*0Sstevel@tonic-gate * (pseudo) is the traditional nexus for pseudodevices. Instances are 32*0Sstevel@tonic-gate * typically specified via driver.conf files; e.g. a leaf device which 33*0Sstevel@tonic-gate * should be attached below pseudonex will have an entry like: 34*0Sstevel@tonic-gate * 35*0Sstevel@tonic-gate * name="foo" parent="/pseudo" instance=0; 36*0Sstevel@tonic-gate * 37*0Sstevel@tonic-gate * pseudonex also supports the devctl (see <sys/devctl.h>) interface via 38*0Sstevel@tonic-gate * its :devctl minor node. This allows priveleged userland applications to 39*0Sstevel@tonic-gate * online/offline children of pseudo as needed. 40*0Sstevel@tonic-gate * 41*0Sstevel@tonic-gate * In general, we discourage widespread use of this tactic, as it may lead to a 42*0Sstevel@tonic-gate * proliferation of nodes in /pseudo. It is preferred that implementors update 43*0Sstevel@tonic-gate * pseudo.conf, adding another 'pseudo' nexus child of /pseudo, and then use 44*0Sstevel@tonic-gate * that for their collection of device nodes. To do so, add a driver alias 45*0Sstevel@tonic-gate * for the name of the nexus child and a line in pseudo.conf such as: 46*0Sstevel@tonic-gate * 47*0Sstevel@tonic-gate * name="foo" parent="/pseudo" instance=<n> valid-children="bar","baz"; 48*0Sstevel@tonic-gate * 49*0Sstevel@tonic-gate * Setting 'valid-children' is important because we have an annoying problem; 50*0Sstevel@tonic-gate * we need to prevent pseudo devices with 'parent="pseudo"' set from binding 51*0Sstevel@tonic-gate * to our new pseudonex child node. A better way might be to teach the 52*0Sstevel@tonic-gate * spec-node code to understand that parent="pseudo" really means 53*0Sstevel@tonic-gate * parent="/pseudo". 54*0Sstevel@tonic-gate * 55*0Sstevel@tonic-gate * At some point in the future, it would be desirable to extend the instance 56*0Sstevel@tonic-gate * database to include nexus children of pseudo. Then we could use devctl 57*0Sstevel@tonic-gate * or devfs to online nexus children of pseudo, auto-selecting an instance #, 58*0Sstevel@tonic-gate * and the instance number selected would be preserved across reboot in 59*0Sstevel@tonic-gate * path_to_inst. 60*0Sstevel@tonic-gate */ 61*0Sstevel@tonic-gate 62*0Sstevel@tonic-gate #include <sys/types.h> 63*0Sstevel@tonic-gate #include <sys/cmn_err.h> 64*0Sstevel@tonic-gate #include <sys/conf.h> 65*0Sstevel@tonic-gate #include <sys/ddi.h> 66*0Sstevel@tonic-gate #include <sys/ddi_impldefs.h> 67*0Sstevel@tonic-gate #include <sys/devops.h> 68*0Sstevel@tonic-gate #include <sys/instance.h> 69*0Sstevel@tonic-gate #include <sys/modctl.h> 70*0Sstevel@tonic-gate #include <sys/open.h> 71*0Sstevel@tonic-gate #include <sys/stat.h> 72*0Sstevel@tonic-gate #include <sys/sunddi.h> 73*0Sstevel@tonic-gate #include <sys/sunndi.h> 74*0Sstevel@tonic-gate #include <sys/systm.h> 75*0Sstevel@tonic-gate #include <sys/mkdev.h> 76*0Sstevel@tonic-gate 77*0Sstevel@tonic-gate /* 78*0Sstevel@tonic-gate * Config information 79*0Sstevel@tonic-gate */ 80*0Sstevel@tonic-gate static int pseudonex_intr_op(dev_info_t *dip, dev_info_t *rdip, 81*0Sstevel@tonic-gate ddi_intr_op_t op, ddi_intr_handle_impl_t *hdlp, void *result); 82*0Sstevel@tonic-gate 83*0Sstevel@tonic-gate static int pseudonex_attach(dev_info_t *, ddi_attach_cmd_t); 84*0Sstevel@tonic-gate static int pseudonex_detach(dev_info_t *, ddi_detach_cmd_t); 85*0Sstevel@tonic-gate static int pseudonex_open(dev_t *, int, int, cred_t *); 86*0Sstevel@tonic-gate static int pseudonex_close(dev_t, int, int, cred_t *); 87*0Sstevel@tonic-gate static int pseudonex_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 88*0Sstevel@tonic-gate static int pseudonex_ctl(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *, 89*0Sstevel@tonic-gate void *); 90*0Sstevel@tonic-gate 91*0Sstevel@tonic-gate static void *pseudonex_state; 92*0Sstevel@tonic-gate 93*0Sstevel@tonic-gate typedef struct pseudonex_state { 94*0Sstevel@tonic-gate dev_info_t *pnx_devi; 95*0Sstevel@tonic-gate } pseudonex_state_t; 96*0Sstevel@tonic-gate 97*0Sstevel@tonic-gate static struct bus_ops pseudonex_bus_ops = { 98*0Sstevel@tonic-gate BUSO_REV, 99*0Sstevel@tonic-gate nullbusmap, /* bus_map */ 100*0Sstevel@tonic-gate NULL, /* bus_get_intrspec */ 101*0Sstevel@tonic-gate NULL, /* bus_add_intrspec */ 102*0Sstevel@tonic-gate NULL, /* bus_remove_intrspec */ 103*0Sstevel@tonic-gate i_ddi_map_fault, /* bus_map_fault */ 104*0Sstevel@tonic-gate ddi_no_dma_map, /* bus_dma_map */ 105*0Sstevel@tonic-gate ddi_no_dma_allochdl, /* bus_dma_allochdl */ 106*0Sstevel@tonic-gate NULL, /* bus_dma_freehdl */ 107*0Sstevel@tonic-gate NULL, /* bus_dma_bindhdl */ 108*0Sstevel@tonic-gate NULL, /* bus_dma_unbindhdl */ 109*0Sstevel@tonic-gate NULL, /* bus_dma_flush */ 110*0Sstevel@tonic-gate NULL, /* bus_dma_win */ 111*0Sstevel@tonic-gate NULL, /* bus_dma_ctl */ 112*0Sstevel@tonic-gate pseudonex_ctl, /* bus_ctl */ 113*0Sstevel@tonic-gate ddi_bus_prop_op, /* bus_prop_op */ 114*0Sstevel@tonic-gate 0, /* bus_get_eventcookie */ 115*0Sstevel@tonic-gate 0, /* bus_add_eventcall */ 116*0Sstevel@tonic-gate 0, /* bus_remove_eventcall */ 117*0Sstevel@tonic-gate 0, /* bus_post_event */ 118*0Sstevel@tonic-gate NULL, /* bus_intr_ctl */ 119*0Sstevel@tonic-gate NULL, /* bus_config */ 120*0Sstevel@tonic-gate NULL, /* bus_unconfig */ 121*0Sstevel@tonic-gate NULL, /* bus_fm_init */ 122*0Sstevel@tonic-gate NULL, /* bus_fm_fini */ 123*0Sstevel@tonic-gate NULL, /* bus_fm_access_enter */ 124*0Sstevel@tonic-gate NULL, /* bus_fm_access_exit */ 125*0Sstevel@tonic-gate NULL, /* bus_power */ 126*0Sstevel@tonic-gate pseudonex_intr_op /* bus_intr_op */ 127*0Sstevel@tonic-gate }; 128*0Sstevel@tonic-gate 129*0Sstevel@tonic-gate static struct cb_ops pseudonex_cb_ops = { 130*0Sstevel@tonic-gate pseudonex_open, /* open */ 131*0Sstevel@tonic-gate pseudonex_close, /* close */ 132*0Sstevel@tonic-gate nodev, /* strategy */ 133*0Sstevel@tonic-gate nodev, /* print */ 134*0Sstevel@tonic-gate nodev, /* dump */ 135*0Sstevel@tonic-gate nodev, /* read */ 136*0Sstevel@tonic-gate nodev, /* write */ 137*0Sstevel@tonic-gate pseudonex_ioctl, /* ioctl */ 138*0Sstevel@tonic-gate nodev, /* devmap */ 139*0Sstevel@tonic-gate nodev, /* mmap */ 140*0Sstevel@tonic-gate nodev, /* segmap */ 141*0Sstevel@tonic-gate nochpoll, /* poll */ 142*0Sstevel@tonic-gate ddi_prop_op, /* cb_prop_op */ 143*0Sstevel@tonic-gate 0, /* streamtab */ 144*0Sstevel@tonic-gate D_MP | D_NEW | D_HOTPLUG /* Driver compatibility flag */ 145*0Sstevel@tonic-gate }; 146*0Sstevel@tonic-gate 147*0Sstevel@tonic-gate static struct dev_ops pseudo_ops = { 148*0Sstevel@tonic-gate DEVO_REV, /* devo_rev, */ 149*0Sstevel@tonic-gate 0, /* refcnt */ 150*0Sstevel@tonic-gate ddi_getinfo_1to1, /* info */ 151*0Sstevel@tonic-gate nulldev, /* identify */ 152*0Sstevel@tonic-gate nulldev, /* probe */ 153*0Sstevel@tonic-gate pseudonex_attach, /* attach */ 154*0Sstevel@tonic-gate pseudonex_detach, /* detach */ 155*0Sstevel@tonic-gate nodev, /* reset */ 156*0Sstevel@tonic-gate &pseudonex_cb_ops, /* driver operations */ 157*0Sstevel@tonic-gate &pseudonex_bus_ops, /* bus operations */ 158*0Sstevel@tonic-gate nulldev /* power */ 159*0Sstevel@tonic-gate }; 160*0Sstevel@tonic-gate 161*0Sstevel@tonic-gate /* 162*0Sstevel@tonic-gate * Module linkage information for the kernel. 163*0Sstevel@tonic-gate */ 164*0Sstevel@tonic-gate static struct modldrv modldrv = { 165*0Sstevel@tonic-gate &mod_driverops, 166*0Sstevel@tonic-gate "nexus driver for 'pseudo' %I%", 167*0Sstevel@tonic-gate &pseudo_ops, 168*0Sstevel@tonic-gate }; 169*0Sstevel@tonic-gate 170*0Sstevel@tonic-gate static struct modlinkage modlinkage = { 171*0Sstevel@tonic-gate MODREV_1, (void *)&modldrv, NULL 172*0Sstevel@tonic-gate }; 173*0Sstevel@tonic-gate 174*0Sstevel@tonic-gate int 175*0Sstevel@tonic-gate _init(void) 176*0Sstevel@tonic-gate { 177*0Sstevel@tonic-gate int err; 178*0Sstevel@tonic-gate 179*0Sstevel@tonic-gate if ((err = ddi_soft_state_init(&pseudonex_state, 180*0Sstevel@tonic-gate sizeof (pseudonex_state_t), 0)) != 0) { 181*0Sstevel@tonic-gate return (err); 182*0Sstevel@tonic-gate } 183*0Sstevel@tonic-gate if ((err = mod_install(&modlinkage)) != 0) { 184*0Sstevel@tonic-gate ddi_soft_state_fini(&pseudonex_state); 185*0Sstevel@tonic-gate return (err); 186*0Sstevel@tonic-gate } 187*0Sstevel@tonic-gate return (0); 188*0Sstevel@tonic-gate } 189*0Sstevel@tonic-gate 190*0Sstevel@tonic-gate int 191*0Sstevel@tonic-gate _fini(void) 192*0Sstevel@tonic-gate { 193*0Sstevel@tonic-gate int err; 194*0Sstevel@tonic-gate 195*0Sstevel@tonic-gate if ((err = mod_remove(&modlinkage)) != 0) 196*0Sstevel@tonic-gate return (err); 197*0Sstevel@tonic-gate ddi_soft_state_fini(&pseudonex_state); 198*0Sstevel@tonic-gate return (0); 199*0Sstevel@tonic-gate } 200*0Sstevel@tonic-gate 201*0Sstevel@tonic-gate int 202*0Sstevel@tonic-gate _info(struct modinfo *modinfop) 203*0Sstevel@tonic-gate { 204*0Sstevel@tonic-gate return (mod_info(&modlinkage, modinfop)); 205*0Sstevel@tonic-gate } 206*0Sstevel@tonic-gate 207*0Sstevel@tonic-gate /*ARGSUSED*/ 208*0Sstevel@tonic-gate static int 209*0Sstevel@tonic-gate pseudonex_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 210*0Sstevel@tonic-gate { 211*0Sstevel@tonic-gate int instance; 212*0Sstevel@tonic-gate pseudonex_state_t *pnx_state; 213*0Sstevel@tonic-gate 214*0Sstevel@tonic-gate switch (cmd) { 215*0Sstevel@tonic-gate case DDI_ATTACH: 216*0Sstevel@tonic-gate break; 217*0Sstevel@tonic-gate case DDI_RESUME: 218*0Sstevel@tonic-gate return (DDI_SUCCESS); 219*0Sstevel@tonic-gate default: 220*0Sstevel@tonic-gate return (DDI_FAILURE); 221*0Sstevel@tonic-gate } 222*0Sstevel@tonic-gate 223*0Sstevel@tonic-gate /* 224*0Sstevel@tonic-gate * Save the devi for this instance in the soft_state data. 225*0Sstevel@tonic-gate */ 226*0Sstevel@tonic-gate instance = ddi_get_instance(devi); 227*0Sstevel@tonic-gate if (ddi_soft_state_zalloc(pseudonex_state, instance) != DDI_SUCCESS) 228*0Sstevel@tonic-gate return (DDI_FAILURE); 229*0Sstevel@tonic-gate pnx_state = ddi_get_soft_state(pseudonex_state, instance); 230*0Sstevel@tonic-gate pnx_state->pnx_devi = devi; 231*0Sstevel@tonic-gate 232*0Sstevel@tonic-gate if (ddi_create_minor_node(devi, "devctl", S_IFCHR, instance, 233*0Sstevel@tonic-gate DDI_NT_NEXUS, 0) != DDI_SUCCESS) { 234*0Sstevel@tonic-gate ddi_remove_minor_node(devi, NULL); 235*0Sstevel@tonic-gate ddi_soft_state_free(pseudonex_state, instance); 236*0Sstevel@tonic-gate return (DDI_FAILURE); 237*0Sstevel@tonic-gate } 238*0Sstevel@tonic-gate ddi_report_dev(devi); 239*0Sstevel@tonic-gate return (DDI_SUCCESS); 240*0Sstevel@tonic-gate } 241*0Sstevel@tonic-gate 242*0Sstevel@tonic-gate /*ARGSUSED*/ 243*0Sstevel@tonic-gate static int 244*0Sstevel@tonic-gate pseudonex_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) 245*0Sstevel@tonic-gate { 246*0Sstevel@tonic-gate int instance = ddi_get_instance(devi); 247*0Sstevel@tonic-gate 248*0Sstevel@tonic-gate if (cmd == DDI_SUSPEND) 249*0Sstevel@tonic-gate return (DDI_SUCCESS); 250*0Sstevel@tonic-gate 251*0Sstevel@tonic-gate ddi_remove_minor_node(devi, NULL); 252*0Sstevel@tonic-gate ddi_soft_state_free(pseudonex_state, instance); 253*0Sstevel@tonic-gate return (DDI_SUCCESS); 254*0Sstevel@tonic-gate } 255*0Sstevel@tonic-gate 256*0Sstevel@tonic-gate /*ARGSUSED*/ 257*0Sstevel@tonic-gate static int 258*0Sstevel@tonic-gate pseudonex_open(dev_t *devp, int flags, int otyp, cred_t *credp) 259*0Sstevel@tonic-gate { 260*0Sstevel@tonic-gate int instance; 261*0Sstevel@tonic-gate 262*0Sstevel@tonic-gate if (otyp != OTYP_CHR) 263*0Sstevel@tonic-gate return (EINVAL); 264*0Sstevel@tonic-gate 265*0Sstevel@tonic-gate instance = getminor(*devp); 266*0Sstevel@tonic-gate if (ddi_get_soft_state(pseudonex_state, instance) == NULL) 267*0Sstevel@tonic-gate return (ENXIO); 268*0Sstevel@tonic-gate 269*0Sstevel@tonic-gate return (0); 270*0Sstevel@tonic-gate } 271*0Sstevel@tonic-gate 272*0Sstevel@tonic-gate /*ARGSUSED*/ 273*0Sstevel@tonic-gate static int 274*0Sstevel@tonic-gate pseudonex_close(dev_t dev, int flags, int otyp, cred_t *credp) 275*0Sstevel@tonic-gate { 276*0Sstevel@tonic-gate int instance; 277*0Sstevel@tonic-gate 278*0Sstevel@tonic-gate if (otyp != OTYP_CHR) 279*0Sstevel@tonic-gate return (EINVAL); 280*0Sstevel@tonic-gate 281*0Sstevel@tonic-gate instance = getminor(dev); 282*0Sstevel@tonic-gate if (ddi_get_soft_state(pseudonex_state, instance) == NULL) 283*0Sstevel@tonic-gate return (ENXIO); 284*0Sstevel@tonic-gate 285*0Sstevel@tonic-gate return (0); 286*0Sstevel@tonic-gate } 287*0Sstevel@tonic-gate 288*0Sstevel@tonic-gate /*ARGSUSED*/ 289*0Sstevel@tonic-gate static int 290*0Sstevel@tonic-gate pseudonex_ioctl(dev_t dev, 291*0Sstevel@tonic-gate int cmd, intptr_t arg, int mode, cred_t *cred_p, int *rval_p) 292*0Sstevel@tonic-gate { 293*0Sstevel@tonic-gate int instance; 294*0Sstevel@tonic-gate pseudonex_state_t *pnx_state; 295*0Sstevel@tonic-gate 296*0Sstevel@tonic-gate instance = getminor(dev); 297*0Sstevel@tonic-gate if ((pnx_state = ddi_get_soft_state(pseudonex_state, instance)) == NULL) 298*0Sstevel@tonic-gate return (ENXIO); 299*0Sstevel@tonic-gate ASSERT(pnx_state->pnx_devi); 300*0Sstevel@tonic-gate return (ndi_devctl_ioctl(pnx_state->pnx_devi, cmd, arg, mode, 0)); 301*0Sstevel@tonic-gate } 302*0Sstevel@tonic-gate 303*0Sstevel@tonic-gate /* 304*0Sstevel@tonic-gate * pseudonex_intr_op: pseudonex convert an interrupt number to an 305*0Sstevel@tonic-gate * interrupt. NO OP for pseudo drivers. 306*0Sstevel@tonic-gate */ 307*0Sstevel@tonic-gate /*ARGSUSED*/ 308*0Sstevel@tonic-gate static int 309*0Sstevel@tonic-gate pseudonex_intr_op(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t op, 310*0Sstevel@tonic-gate ddi_intr_handle_impl_t *hdlp, void *result) 311*0Sstevel@tonic-gate { 312*0Sstevel@tonic-gate return (DDI_FAILURE); 313*0Sstevel@tonic-gate } 314*0Sstevel@tonic-gate 315*0Sstevel@tonic-gate static int 316*0Sstevel@tonic-gate pseudonex_check_assignment(dev_info_t *child, int test_inst) 317*0Sstevel@tonic-gate { 318*0Sstevel@tonic-gate dev_info_t *tdip; 319*0Sstevel@tonic-gate kmutex_t *dmp; 320*0Sstevel@tonic-gate const char *childname = ddi_driver_name(child); 321*0Sstevel@tonic-gate major_t childmaj = ddi_name_to_major((char *)childname); 322*0Sstevel@tonic-gate 323*0Sstevel@tonic-gate dmp = &devnamesp[childmaj].dn_lock; 324*0Sstevel@tonic-gate LOCK_DEV_OPS(dmp); 325*0Sstevel@tonic-gate for (tdip = devnamesp[childmaj].dn_head; 326*0Sstevel@tonic-gate tdip != NULL; tdip = ddi_get_next(tdip)) { 327*0Sstevel@tonic-gate /* is this the current node? */ 328*0Sstevel@tonic-gate if (tdip == child) 329*0Sstevel@tonic-gate continue; 330*0Sstevel@tonic-gate /* is this a duplicate instance? */ 331*0Sstevel@tonic-gate if (test_inst == ddi_get_instance(tdip)) { 332*0Sstevel@tonic-gate UNLOCK_DEV_OPS(dmp); 333*0Sstevel@tonic-gate return (DDI_FAILURE); 334*0Sstevel@tonic-gate } 335*0Sstevel@tonic-gate } 336*0Sstevel@tonic-gate UNLOCK_DEV_OPS(dmp); 337*0Sstevel@tonic-gate return (DDI_SUCCESS); 338*0Sstevel@tonic-gate } 339*0Sstevel@tonic-gate 340*0Sstevel@tonic-gate /* 341*0Sstevel@tonic-gate * This is a nasty, slow hack. But we're stuck with it until we do some 342*0Sstevel@tonic-gate * major surgery on the instance assignment subsystem, to allow pseudonode 343*0Sstevel@tonic-gate * instance assignment to be tracked there. 344*0Sstevel@tonic-gate * 345*0Sstevel@tonic-gate * To auto-assign an instance number, we exhaustively search the instance 346*0Sstevel@tonic-gate * list for each possible instance number until we find one which is unused. 347*0Sstevel@tonic-gate */ 348*0Sstevel@tonic-gate static int 349*0Sstevel@tonic-gate pseudonex_auto_assign(dev_info_t *child) 350*0Sstevel@tonic-gate { 351*0Sstevel@tonic-gate dev_info_t *tdip; 352*0Sstevel@tonic-gate kmutex_t *dmp; 353*0Sstevel@tonic-gate const char *childname = ddi_driver_name(child); 354*0Sstevel@tonic-gate major_t childmaj = ddi_name_to_major((char *)childname); 355*0Sstevel@tonic-gate int inst = 0; 356*0Sstevel@tonic-gate 357*0Sstevel@tonic-gate dmp = &devnamesp[childmaj].dn_lock; 358*0Sstevel@tonic-gate LOCK_DEV_OPS(dmp); 359*0Sstevel@tonic-gate for (inst = 0; inst <= MAXMIN32; inst++) { 360*0Sstevel@tonic-gate for (tdip = devnamesp[childmaj].dn_head; tdip != NULL; 361*0Sstevel@tonic-gate tdip = ddi_get_next(tdip)) { 362*0Sstevel@tonic-gate /* is this the current node? */ 363*0Sstevel@tonic-gate if (tdip == child) 364*0Sstevel@tonic-gate continue; 365*0Sstevel@tonic-gate if (inst == ddi_get_instance(tdip)) { 366*0Sstevel@tonic-gate break; 367*0Sstevel@tonic-gate } 368*0Sstevel@tonic-gate } 369*0Sstevel@tonic-gate if (tdip == NULL) { 370*0Sstevel@tonic-gate UNLOCK_DEV_OPS(dmp); 371*0Sstevel@tonic-gate return (inst); 372*0Sstevel@tonic-gate } 373*0Sstevel@tonic-gate } 374*0Sstevel@tonic-gate UNLOCK_DEV_OPS(dmp); 375*0Sstevel@tonic-gate return (-1); 376*0Sstevel@tonic-gate } 377*0Sstevel@tonic-gate 378*0Sstevel@tonic-gate static int 379*0Sstevel@tonic-gate pseudonex_ctl(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t ctlop, 380*0Sstevel@tonic-gate void *arg, void *result) 381*0Sstevel@tonic-gate { 382*0Sstevel@tonic-gate switch (ctlop) { 383*0Sstevel@tonic-gate case DDI_CTLOPS_REPORTDEV: 384*0Sstevel@tonic-gate if (rdip == NULL) 385*0Sstevel@tonic-gate return (DDI_FAILURE); 386*0Sstevel@tonic-gate cmn_err(CE_CONT, "?pseudo-device: %s%d\n", 387*0Sstevel@tonic-gate ddi_driver_name(rdip), ddi_get_instance(rdip)); 388*0Sstevel@tonic-gate return (DDI_SUCCESS); 389*0Sstevel@tonic-gate 390*0Sstevel@tonic-gate case DDI_CTLOPS_INITCHILD: 391*0Sstevel@tonic-gate { 392*0Sstevel@tonic-gate char name[12]; /* enough for a decimal integer */ 393*0Sstevel@tonic-gate int instance = -1; 394*0Sstevel@tonic-gate dev_info_t *child = (dev_info_t *)arg; 395*0Sstevel@tonic-gate const char *childname = ddi_driver_name(child); 396*0Sstevel@tonic-gate char **childlist; 397*0Sstevel@tonic-gate uint_t nelems; 398*0Sstevel@tonic-gate int auto_assign = 0; 399*0Sstevel@tonic-gate 400*0Sstevel@tonic-gate /* 401*0Sstevel@tonic-gate * If this pseudonex node has a valid-children property, 402*0Sstevel@tonic-gate * then that acts as an access control list for children 403*0Sstevel@tonic-gate * allowed to attach beneath this node. Honor it. 404*0Sstevel@tonic-gate */ 405*0Sstevel@tonic-gate if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, dip, 406*0Sstevel@tonic-gate DDI_PROP_DONTPASS, "valid-children", &childlist, 407*0Sstevel@tonic-gate &nelems) == DDI_PROP_SUCCESS) { 408*0Sstevel@tonic-gate int i, ok = 0; 409*0Sstevel@tonic-gate for (i = 0; i < nelems; i++) { 410*0Sstevel@tonic-gate if (strcmp(childlist[i], childname) == 0) { 411*0Sstevel@tonic-gate ok = 1; 412*0Sstevel@tonic-gate break; 413*0Sstevel@tonic-gate } 414*0Sstevel@tonic-gate } 415*0Sstevel@tonic-gate ddi_prop_free(childlist); 416*0Sstevel@tonic-gate if (!ok) 417*0Sstevel@tonic-gate return (DDI_FAILURE); 418*0Sstevel@tonic-gate } 419*0Sstevel@tonic-gate 420*0Sstevel@tonic-gate /* 421*0Sstevel@tonic-gate * Look up the "instance" property. If it does not exist, 422*0Sstevel@tonic-gate * check to see if the "auto-assign-instance" property is set. 423*0Sstevel@tonic-gate * If not, default to using instance 0; while not ideal, this 424*0Sstevel@tonic-gate * is a legacy behavior we must continue to support. 425*0Sstevel@tonic-gate */ 426*0Sstevel@tonic-gate instance = ddi_prop_get_int(DDI_DEV_T_ANY, child, 427*0Sstevel@tonic-gate DDI_PROP_DONTPASS, "instance", -1); 428*0Sstevel@tonic-gate auto_assign = ddi_prop_exists(DDI_DEV_T_ANY, child, 429*0Sstevel@tonic-gate DDI_PROP_DONTPASS, "auto-assign-instance"); 430*0Sstevel@tonic-gate NDI_CONFIG_DEBUG((CE_NOTE, 431*0Sstevel@tonic-gate "pseudonex: DDI_CTLOPS_INITCHILD(instance=%d, " 432*0Sstevel@tonic-gate "auto-assign=%d)", instance, auto_assign)); 433*0Sstevel@tonic-gate 434*0Sstevel@tonic-gate if (instance != -1 && auto_assign != 0) { 435*0Sstevel@tonic-gate NDI_CONFIG_DEBUG((CE_NOTE, "both instance and " 436*0Sstevel@tonic-gate "auto-assign-instance properties specified. " 437*0Sstevel@tonic-gate "Node rejected.")); 438*0Sstevel@tonic-gate return (DDI_FAILURE); 439*0Sstevel@tonic-gate } 440*0Sstevel@tonic-gate 441*0Sstevel@tonic-gate if (instance == -1 && auto_assign == 0) { 442*0Sstevel@tonic-gate /* default to instance 0 if not specified */ 443*0Sstevel@tonic-gate NDI_CONFIG_DEBUG((CE_NOTE, "defaulting to 0")); 444*0Sstevel@tonic-gate instance = 0; 445*0Sstevel@tonic-gate } 446*0Sstevel@tonic-gate 447*0Sstevel@tonic-gate /* 448*0Sstevel@tonic-gate * If an instance has been specified, determine if this 449*0Sstevel@tonic-gate * instance is already in use; if we need to pick an instance, 450*0Sstevel@tonic-gate * we do it here. 451*0Sstevel@tonic-gate */ 452*0Sstevel@tonic-gate if (auto_assign) { 453*0Sstevel@tonic-gate if ((instance = pseudonex_auto_assign(child)) == -1) { 454*0Sstevel@tonic-gate NDI_CONFIG_DEBUG((CE_NOTE, "failed to " 455*0Sstevel@tonic-gate "auto-select instance for %s", childname)); 456*0Sstevel@tonic-gate return (DDI_FAILURE); 457*0Sstevel@tonic-gate } 458*0Sstevel@tonic-gate NDI_CONFIG_DEBUG((CE_NOTE, 459*0Sstevel@tonic-gate "auto-selected instance for %s: %d", 460*0Sstevel@tonic-gate childname, instance)); 461*0Sstevel@tonic-gate } else { 462*0Sstevel@tonic-gate if (pseudonex_check_assignment(child, instance) == 463*0Sstevel@tonic-gate DDI_FAILURE) { 464*0Sstevel@tonic-gate NDI_CONFIG_DEBUG((CE_WARN, 465*0Sstevel@tonic-gate "Duplicate instance %d of node \"%s\" " 466*0Sstevel@tonic-gate "ignored.", instance, childname)); 467*0Sstevel@tonic-gate return (DDI_FAILURE); 468*0Sstevel@tonic-gate } 469*0Sstevel@tonic-gate NDI_CONFIG_DEBUG((CE_NOTE, 470*0Sstevel@tonic-gate "using fixed-assignment instance for %s: %d", 471*0Sstevel@tonic-gate childname, instance)); 472*0Sstevel@tonic-gate } 473*0Sstevel@tonic-gate 474*0Sstevel@tonic-gate /* 475*0Sstevel@tonic-gate * Attach the instance number to the node. This allows 476*0Sstevel@tonic-gate * us to have multiple instances of the same pseudo 477*0Sstevel@tonic-gate * device, they will be named 'device@instance'. If this 478*0Sstevel@tonic-gate * breaks programs, we may need to special-case instance 0 479*0Sstevel@tonic-gate * into 'device'. Ick. devlinks appears to handle the 480*0Sstevel@tonic-gate * new names ok, so if only names in /dev are used 481*0Sstevel@tonic-gate * this may not be necessary. 482*0Sstevel@tonic-gate */ 483*0Sstevel@tonic-gate (void) snprintf(name, sizeof (name), "%d", instance); 484*0Sstevel@tonic-gate DEVI(child)->devi_instance = instance; 485*0Sstevel@tonic-gate ddi_set_name_addr(child, name); 486*0Sstevel@tonic-gate return (DDI_SUCCESS); 487*0Sstevel@tonic-gate } 488*0Sstevel@tonic-gate 489*0Sstevel@tonic-gate case DDI_CTLOPS_UNINITCHILD: 490*0Sstevel@tonic-gate { 491*0Sstevel@tonic-gate dev_info_t *child = (dev_info_t *)arg; 492*0Sstevel@tonic-gate 493*0Sstevel@tonic-gate NDI_CONFIG_DEBUG((CE_NOTE, 494*0Sstevel@tonic-gate "DDI_CTLOPS_UNINITCHILD(%s, instance=%d)", 495*0Sstevel@tonic-gate ddi_driver_name(child), DEVI(child)->devi_instance)); 496*0Sstevel@tonic-gate 497*0Sstevel@tonic-gate ddi_set_name_addr(child, NULL); 498*0Sstevel@tonic-gate 499*0Sstevel@tonic-gate return (DDI_SUCCESS); 500*0Sstevel@tonic-gate } 501*0Sstevel@tonic-gate 502*0Sstevel@tonic-gate case DDI_CTLOPS_DMAPMAPC: 503*0Sstevel@tonic-gate case DDI_CTLOPS_REPORTINT: 504*0Sstevel@tonic-gate case DDI_CTLOPS_REGSIZE: 505*0Sstevel@tonic-gate case DDI_CTLOPS_NREGS: 506*0Sstevel@tonic-gate case DDI_CTLOPS_NINTRS: 507*0Sstevel@tonic-gate case DDI_CTLOPS_SIDDEV: 508*0Sstevel@tonic-gate case DDI_CTLOPS_SLAVEONLY: 509*0Sstevel@tonic-gate case DDI_CTLOPS_AFFINITY: 510*0Sstevel@tonic-gate case DDI_CTLOPS_INTR_HILEVEL: 511*0Sstevel@tonic-gate case DDI_CTLOPS_XLATE_INTRS: 512*0Sstevel@tonic-gate case DDI_CTLOPS_POKE: 513*0Sstevel@tonic-gate case DDI_CTLOPS_PEEK: 514*0Sstevel@tonic-gate /* 515*0Sstevel@tonic-gate * These ops correspond to functions that "shouldn't" be called 516*0Sstevel@tonic-gate * by a pseudo driver. So we whine when we're called. 517*0Sstevel@tonic-gate */ 518*0Sstevel@tonic-gate cmn_err(CE_CONT, "%s%d: invalid op (%d) from %s%d\n", 519*0Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip), ctlop, 520*0Sstevel@tonic-gate ddi_driver_name(rdip), ddi_get_instance(rdip)); 521*0Sstevel@tonic-gate return (DDI_FAILURE); 522*0Sstevel@tonic-gate 523*0Sstevel@tonic-gate case DDI_CTLOPS_ATTACH: 524*0Sstevel@tonic-gate case DDI_CTLOPS_BTOP: 525*0Sstevel@tonic-gate case DDI_CTLOPS_BTOPR: 526*0Sstevel@tonic-gate case DDI_CTLOPS_DETACH: 527*0Sstevel@tonic-gate case DDI_CTLOPS_DVMAPAGESIZE: 528*0Sstevel@tonic-gate case DDI_CTLOPS_IOMIN: 529*0Sstevel@tonic-gate case DDI_CTLOPS_POWER: 530*0Sstevel@tonic-gate case DDI_CTLOPS_PTOB: 531*0Sstevel@tonic-gate default: 532*0Sstevel@tonic-gate /* 533*0Sstevel@tonic-gate * The ops that we pass up (default). We pass up memory 534*0Sstevel@tonic-gate * allocation oriented ops that we receive - these may be 535*0Sstevel@tonic-gate * associated with pseudo HBA drivers below us with target 536*0Sstevel@tonic-gate * drivers below them that use ddi memory allocation 537*0Sstevel@tonic-gate * interfaces like scsi_alloc_consistent_buf. 538*0Sstevel@tonic-gate */ 539*0Sstevel@tonic-gate return (ddi_ctlops(dip, rdip, ctlop, arg, result)); 540*0Sstevel@tonic-gate } 541*0Sstevel@tonic-gate } 542