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_ctl(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *, 87 void *); 88 89 static void *pseudonex_state; 90 91 typedef struct pseudonex_state { 92 dev_info_t *pnx_devi; 93 } pseudonex_state_t; 94 95 static struct bus_ops pseudonex_bus_ops = { 96 BUSO_REV, 97 nullbusmap, /* bus_map */ 98 NULL, /* bus_get_intrspec */ 99 NULL, /* bus_add_intrspec */ 100 NULL, /* bus_remove_intrspec */ 101 i_ddi_map_fault, /* bus_map_fault */ 102 ddi_no_dma_map, /* bus_dma_map */ 103 ddi_no_dma_allochdl, /* bus_dma_allochdl */ 104 NULL, /* bus_dma_freehdl */ 105 NULL, /* bus_dma_bindhdl */ 106 NULL, /* bus_dma_unbindhdl */ 107 NULL, /* bus_dma_flush */ 108 NULL, /* bus_dma_win */ 109 NULL, /* bus_dma_ctl */ 110 pseudonex_ctl, /* bus_ctl */ 111 ddi_bus_prop_op, /* bus_prop_op */ 112 0, /* bus_get_eventcookie */ 113 0, /* bus_add_eventcall */ 114 0, /* bus_remove_eventcall */ 115 0, /* bus_post_event */ 116 NULL, /* bus_intr_ctl */ 117 NULL, /* bus_config */ 118 NULL, /* bus_unconfig */ 119 NULL, /* bus_fm_init */ 120 NULL, /* bus_fm_fini */ 121 NULL, /* bus_fm_access_enter */ 122 NULL, /* bus_fm_access_exit */ 123 NULL, /* bus_power */ 124 pseudonex_intr_op /* bus_intr_op */ 125 }; 126 127 static struct cb_ops pseudonex_cb_ops = { 128 pseudonex_open, /* open */ 129 pseudonex_close, /* close */ 130 nodev, /* strategy */ 131 nodev, /* print */ 132 nodev, /* dump */ 133 nodev, /* read */ 134 nodev, /* write */ 135 pseudonex_ioctl, /* ioctl */ 136 nodev, /* devmap */ 137 nodev, /* mmap */ 138 nodev, /* segmap */ 139 nochpoll, /* poll */ 140 ddi_prop_op, /* cb_prop_op */ 141 0, /* streamtab */ 142 D_MP | D_NEW | D_HOTPLUG /* Driver compatibility flag */ 143 }; 144 145 static struct dev_ops pseudo_ops = { 146 DEVO_REV, /* devo_rev, */ 147 0, /* refcnt */ 148 ddi_getinfo_1to1, /* info */ 149 nulldev, /* identify */ 150 nulldev, /* probe */ 151 pseudonex_attach, /* attach */ 152 pseudonex_detach, /* detach */ 153 nodev, /* reset */ 154 &pseudonex_cb_ops, /* driver operations */ 155 &pseudonex_bus_ops, /* bus operations */ 156 nulldev, /* power */ 157 ddi_quiesce_not_needed, /* quiesce */ 158 }; 159 160 /* 161 * Module linkage information for the kernel. 162 */ 163 static struct modldrv modldrv = { 164 &mod_driverops, 165 "nexus driver for 'pseudo' 1.31", 166 &pseudo_ops, 167 }; 168 169 static struct modlinkage modlinkage = { 170 MODREV_1, (void *)&modldrv, NULL 171 }; 172 173 int 174 _init(void) 175 { 176 int err; 177 178 if ((err = ddi_soft_state_init(&pseudonex_state, 179 sizeof (pseudonex_state_t), 0)) != 0) { 180 return (err); 181 } 182 if ((err = mod_install(&modlinkage)) != 0) { 183 ddi_soft_state_fini(&pseudonex_state); 184 return (err); 185 } 186 return (0); 187 } 188 189 int 190 _fini(void) 191 { 192 int err; 193 194 if ((err = mod_remove(&modlinkage)) != 0) 195 return (err); 196 ddi_soft_state_fini(&pseudonex_state); 197 return (0); 198 } 199 200 int 201 _info(struct modinfo *modinfop) 202 { 203 return (mod_info(&modlinkage, modinfop)); 204 } 205 206 /*ARGSUSED*/ 207 static int 208 pseudonex_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 209 { 210 int instance; 211 pseudonex_state_t *pnx_state; 212 213 switch (cmd) { 214 case DDI_ATTACH: 215 break; 216 case DDI_RESUME: 217 return (DDI_SUCCESS); 218 default: 219 return (DDI_FAILURE); 220 } 221 222 /* 223 * Save the devi for this instance in the soft_state data. 224 */ 225 instance = ddi_get_instance(devi); 226 if (ddi_soft_state_zalloc(pseudonex_state, instance) != DDI_SUCCESS) 227 return (DDI_FAILURE); 228 pnx_state = ddi_get_soft_state(pseudonex_state, instance); 229 pnx_state->pnx_devi = devi; 230 231 if (ddi_create_minor_node(devi, "devctl", S_IFCHR, instance, 232 DDI_NT_NEXUS, 0) != DDI_SUCCESS) { 233 ddi_remove_minor_node(devi, NULL); 234 ddi_soft_state_free(pseudonex_state, instance); 235 return (DDI_FAILURE); 236 } 237 ddi_report_dev(devi); 238 return (DDI_SUCCESS); 239 } 240 241 /*ARGSUSED*/ 242 static int 243 pseudonex_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) 244 { 245 int instance = ddi_get_instance(devi); 246 247 if (cmd == DDI_SUSPEND) 248 return (DDI_SUCCESS); 249 250 ddi_remove_minor_node(devi, NULL); 251 ddi_soft_state_free(pseudonex_state, instance); 252 return (DDI_SUCCESS); 253 } 254 255 /*ARGSUSED*/ 256 static int 257 pseudonex_open(dev_t *devp, int flags, int otyp, cred_t *credp) 258 { 259 int instance; 260 261 if (otyp != OTYP_CHR) 262 return (EINVAL); 263 264 instance = getminor(*devp); 265 if (ddi_get_soft_state(pseudonex_state, instance) == NULL) 266 return (ENXIO); 267 268 return (0); 269 } 270 271 /*ARGSUSED*/ 272 static int 273 pseudonex_close(dev_t dev, int flags, int otyp, cred_t *credp) 274 { 275 int instance; 276 277 if (otyp != OTYP_CHR) 278 return (EINVAL); 279 280 instance = getminor(dev); 281 if (ddi_get_soft_state(pseudonex_state, instance) == NULL) 282 return (ENXIO); 283 284 return (0); 285 } 286 287 /*ARGSUSED*/ 288 static int 289 pseudonex_ioctl(dev_t dev, 290 int cmd, intptr_t arg, int mode, cred_t *cred_p, int *rval_p) 291 { 292 int instance; 293 pseudonex_state_t *pnx_state; 294 295 instance = getminor(dev); 296 if ((pnx_state = ddi_get_soft_state(pseudonex_state, instance)) == NULL) 297 return (ENXIO); 298 ASSERT(pnx_state->pnx_devi); 299 return (ndi_devctl_ioctl(pnx_state->pnx_devi, cmd, arg, mode, 0)); 300 } 301 302 /* 303 * pseudonex_intr_op: pseudonex convert an interrupt number to an 304 * interrupt. NO OP for pseudo drivers. 305 */ 306 /*ARGSUSED*/ 307 static int 308 pseudonex_intr_op(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t op, 309 ddi_intr_handle_impl_t *hdlp, void *result) 310 { 311 return (DDI_FAILURE); 312 } 313 314 static int 315 pseudonex_check_assignment(dev_info_t *child, int test_inst) 316 { 317 dev_info_t *tdip; 318 kmutex_t *dmp; 319 const char *childname = ddi_driver_name(child); 320 major_t childmaj = ddi_name_to_major((char *)childname); 321 322 dmp = &devnamesp[childmaj].dn_lock; 323 LOCK_DEV_OPS(dmp); 324 for (tdip = devnamesp[childmaj].dn_head; 325 tdip != NULL; tdip = ddi_get_next(tdip)) { 326 /* is this the current node? */ 327 if (tdip == child) 328 continue; 329 /* is this a duplicate instance? */ 330 if (test_inst == ddi_get_instance(tdip)) { 331 UNLOCK_DEV_OPS(dmp); 332 return (DDI_FAILURE); 333 } 334 } 335 UNLOCK_DEV_OPS(dmp); 336 return (DDI_SUCCESS); 337 } 338 339 /* 340 * This is a nasty, slow hack. But we're stuck with it until we do some 341 * major surgery on the instance assignment subsystem, to allow pseudonode 342 * instance assignment to be tracked there. 343 * 344 * To auto-assign an instance number, we exhaustively search the instance 345 * list for each possible instance number until we find one which is unused. 346 */ 347 static int 348 pseudonex_auto_assign(dev_info_t *child) 349 { 350 dev_info_t *tdip; 351 kmutex_t *dmp; 352 const char *childname = ddi_driver_name(child); 353 major_t childmaj = ddi_name_to_major((char *)childname); 354 int inst = 0; 355 356 dmp = &devnamesp[childmaj].dn_lock; 357 LOCK_DEV_OPS(dmp); 358 for (inst = 0; inst <= MAXMIN32; inst++) { 359 for (tdip = devnamesp[childmaj].dn_head; tdip != NULL; 360 tdip = ddi_get_next(tdip)) { 361 /* is this the current node? */ 362 if (tdip == child) 363 continue; 364 if (inst == ddi_get_instance(tdip)) { 365 break; 366 } 367 } 368 if (tdip == NULL) { 369 UNLOCK_DEV_OPS(dmp); 370 return (inst); 371 } 372 } 373 UNLOCK_DEV_OPS(dmp); 374 return (-1); 375 } 376 377 static int 378 pseudonex_ctl(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t ctlop, 379 void *arg, void *result) 380 { 381 switch (ctlop) { 382 case DDI_CTLOPS_REPORTDEV: 383 if (rdip == NULL) 384 return (DDI_FAILURE); 385 cmn_err(CE_CONT, "?pseudo-device: %s%d\n", 386 ddi_driver_name(rdip), ddi_get_instance(rdip)); 387 return (DDI_SUCCESS); 388 389 case DDI_CTLOPS_INITCHILD: 390 { 391 char name[12]; /* enough for a decimal integer */ 392 int instance = -1; 393 dev_info_t *child = (dev_info_t *)arg; 394 const char *childname = ddi_driver_name(child); 395 char **childlist; 396 uint_t nelems; 397 int auto_assign = 0; 398 399 /* 400 * If this pseudonex node has a valid-children property, 401 * then that acts as an access control list for children 402 * allowed to attach beneath this node. Honor it. 403 */ 404 if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, dip, 405 DDI_PROP_DONTPASS, "valid-children", &childlist, 406 &nelems) == DDI_PROP_SUCCESS) { 407 int i, ok = 0; 408 for (i = 0; i < nelems; i++) { 409 if (strcmp(childlist[i], childname) == 0) { 410 ok = 1; 411 break; 412 } 413 } 414 ddi_prop_free(childlist); 415 if (!ok) 416 return (DDI_FAILURE); 417 } 418 419 /* 420 * Look up the "instance" property. If it does not exist, 421 * check to see if the "auto-assign-instance" property is set. 422 * If not, default to using instance 0; while not ideal, this 423 * is a legacy behavior we must continue to support. 424 */ 425 instance = ddi_prop_get_int(DDI_DEV_T_ANY, child, 426 DDI_PROP_DONTPASS, "instance", -1); 427 auto_assign = ddi_prop_exists(DDI_DEV_T_ANY, child, 428 DDI_PROP_DONTPASS, "auto-assign-instance"); 429 NDI_CONFIG_DEBUG((CE_NOTE, 430 "pseudonex: DDI_CTLOPS_INITCHILD(instance=%d, " 431 "auto-assign=%d)", instance, auto_assign)); 432 433 if (instance != -1 && auto_assign != 0) { 434 NDI_CONFIG_DEBUG((CE_NOTE, "both instance and " 435 "auto-assign-instance properties specified. " 436 "Node rejected.")); 437 return (DDI_FAILURE); 438 } 439 440 if (instance == -1 && auto_assign == 0) { 441 /* default to instance 0 if not specified */ 442 NDI_CONFIG_DEBUG((CE_NOTE, "defaulting to 0")); 443 instance = 0; 444 } 445 446 /* 447 * If an instance has been specified, determine if this 448 * instance is already in use; if we need to pick an instance, 449 * we do it here. 450 */ 451 if (auto_assign) { 452 if ((instance = pseudonex_auto_assign(child)) == -1) { 453 NDI_CONFIG_DEBUG((CE_NOTE, "failed to " 454 "auto-select instance for %s", childname)); 455 return (DDI_FAILURE); 456 } 457 NDI_CONFIG_DEBUG((CE_NOTE, 458 "auto-selected instance for %s: %d", 459 childname, instance)); 460 } else { 461 if (pseudonex_check_assignment(child, instance) == 462 DDI_FAILURE) { 463 NDI_CONFIG_DEBUG((CE_WARN, 464 "Duplicate instance %d of node \"%s\" " 465 "ignored.", instance, childname)); 466 return (DDI_FAILURE); 467 } 468 NDI_CONFIG_DEBUG((CE_NOTE, 469 "using fixed-assignment instance for %s: %d", 470 childname, instance)); 471 } 472 473 /* 474 * Attach the instance number to the node. This allows 475 * us to have multiple instances of the same pseudo 476 * device, they will be named 'device@instance'. If this 477 * breaks programs, we may need to special-case instance 0 478 * into 'device'. Ick. devlinks appears to handle the 479 * new names ok, so if only names in /dev are used 480 * this may not be necessary. 481 */ 482 (void) snprintf(name, sizeof (name), "%d", instance); 483 DEVI(child)->devi_instance = instance; 484 ddi_set_name_addr(child, name); 485 return (DDI_SUCCESS); 486 } 487 488 case DDI_CTLOPS_UNINITCHILD: 489 { 490 dev_info_t *child = (dev_info_t *)arg; 491 492 NDI_CONFIG_DEBUG((CE_NOTE, 493 "DDI_CTLOPS_UNINITCHILD(%s, instance=%d)", 494 ddi_driver_name(child), DEVI(child)->devi_instance)); 495 496 ddi_set_name_addr(child, NULL); 497 498 return (DDI_SUCCESS); 499 } 500 501 case DDI_CTLOPS_DMAPMAPC: 502 case DDI_CTLOPS_REPORTINT: 503 case DDI_CTLOPS_REGSIZE: 504 case DDI_CTLOPS_NREGS: 505 case DDI_CTLOPS_SIDDEV: 506 case DDI_CTLOPS_SLAVEONLY: 507 case DDI_CTLOPS_AFFINITY: 508 case DDI_CTLOPS_POKE: 509 case DDI_CTLOPS_PEEK: 510 /* 511 * These ops correspond to functions that "shouldn't" be called 512 * by a pseudo driver. So we whine when we're called. 513 */ 514 cmn_err(CE_CONT, "%s%d: invalid op (%d) from %s%d\n", 515 ddi_driver_name(dip), ddi_get_instance(dip), ctlop, 516 ddi_driver_name(rdip), ddi_get_instance(rdip)); 517 return (DDI_FAILURE); 518 519 case DDI_CTLOPS_ATTACH: 520 case DDI_CTLOPS_BTOP: 521 case DDI_CTLOPS_BTOPR: 522 case DDI_CTLOPS_DETACH: 523 case DDI_CTLOPS_DVMAPAGESIZE: 524 case DDI_CTLOPS_IOMIN: 525 case DDI_CTLOPS_POWER: 526 case DDI_CTLOPS_PTOB: 527 default: 528 /* 529 * The ops that we pass up (default). We pass up memory 530 * allocation oriented ops that we receive - these may be 531 * associated with pseudo HBA drivers below us with target 532 * drivers below them that use ddi memory allocation 533 * interfaces like scsi_alloc_consistent_buf. 534 */ 535 return (ddi_ctlops(dip, rdip, ctlop, arg, result)); 536 } 537 } 538