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