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