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 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * pseudo bus nexus driver 28 * hotplug framework test facility 29 */ 30 31 /* 32 * The pshot driver can be used to exercise the i/o framework together 33 * with devfs by configuring an arbitrarily complex device tree. 34 * 35 * The pshot driver is rooted at /devices/pshot. The following commands 36 * illustrate the operation of devfs together with pshot's bus_config. 37 * The first command demonstrates that, like the magician showing there's 38 * nothing up his sleeve, /devices/pshot is empty. The second command 39 * conjures up a branch of pshot nodes. Note that pshot's bus_config is 40 * called sequentially by devfs for each node, as part of the pathname 41 * resolution, and that each pshot node is fully configured and 42 * attached before that node's bus_config is called to configure the 43 * next child down the tree. The final result is a "disk" node configured 44 * at the bottom of the named hierarchy of pshot nodes. 45 * 46 * # 47 * # ls /devices/pshot 48 * # 49 * # ls -ld /devices/pshot/pshot@0/pshot@1/pshot@2/disk@3,0 50 * drwxr-xr-x 2 root sys 512 Feb 6 15:10 51 * /devices/pshot/pshot@0/pshot@1/pshot@2/disk@3,0 52 * 53 * pshot supports some unique behaviors as aids for test error cases. 54 * 55 * Match these special address formats to behavior: 56 * 57 * err.* - induce bus_config error 58 * delay - induce 1 second of bus_config delay time 59 * delay,n - induce n seconds of bus_config delay time 60 * wait - induce 1 second of bus_config wait time 61 * wait,n - induce n seconds of bus_config wait time 62 * failinit.* - induce error at INITCHILD 63 * failprobe.* - induce error at probe 64 * failattach.* - induce error at attach 65 */ 66 67 #if defined(lint) && !defined(DEBUG) 68 #define DEBUG 1 69 #endif 70 71 #include <sys/types.h> 72 #include <sys/cmn_err.h> 73 #include <sys/conf.h> 74 #include <sys/ddi_impldefs.h> 75 #include <sys/autoconf.h> 76 #include <sys/open.h> 77 #include <sys/stat.h> 78 #include <sys/file.h> 79 #include <sys/errno.h> 80 #include <sys/systm.h> 81 #include <sys/modctl.h> 82 #include <sys/kmem.h> 83 #include <sys/ddi.h> 84 #include <sys/sunddi.h> 85 #include <sys/sunndi.h> 86 #include <sys/devctl.h> 87 #include <sys/disp.h> 88 #include <sys/utsname.h> 89 #include <sys/pshot.h> 90 #include <sys/debug.h> 91 92 static int pshot_log = 0; 93 static int pshot_devctl_debug = 0; 94 static int pshot_debug_busy = 0; 95 96 static void *pshot_softstatep; 97 98 static int pshot_prop_autoattach; 99 100 #define MAXPWR 3 101 102 103 /* 104 * device configuration data 105 */ 106 107 /* should keep in sync with current release */ 108 static struct { 109 char *name; 110 char *val; 111 } pshot_nodetypes[] = { 112 {"DDI_NT_SERIAL", DDI_NT_SERIAL}, 113 {"DDI_NT_SERIAL_MB", DDI_NT_SERIAL_MB}, 114 {"DDI_NT_SERIAL_DO", DDI_NT_SERIAL_DO}, 115 {"DDI_NT_SERIAL_MB_DO", DDI_NT_SERIAL_MB_DO}, 116 {"DDI_NT_SERIAL_LOMCON", DDI_NT_SERIAL_LOMCON}, 117 {"DDI_NT_BLOCK", DDI_NT_BLOCK}, 118 {"DDI_NT_BLOCK_CHAN", DDI_NT_BLOCK_CHAN}, 119 {"DDI_NT_BLOCK_WWN", DDI_NT_BLOCK_WWN}, 120 {"DDI_NT_BLOCK_SAS", DDI_NT_BLOCK_SAS}, 121 {"DDI_NT_CD", DDI_NT_CD}, 122 {"DDI_NT_CD_CHAN", DDI_NT_CD_CHAN}, 123 {"DDI_NT_FD", DDI_NT_FD}, 124 {"DDI_NT_ENCLOSURE", DDI_NT_ENCLOSURE}, 125 {"DDI_NT_SCSI_ENCLOSURE", DDI_NT_SCSI_ENCLOSURE}, 126 {"DDI_NT_TAPE", DDI_NT_TAPE}, 127 {"DDI_NT_NET", DDI_NT_NET}, 128 {"DDI_NT_DISPLAY", DDI_NT_DISPLAY}, 129 {"DDI_PSEUDO", DDI_PSEUDO}, 130 {"DDI_NT_AUDIO", DDI_NT_AUDIO}, 131 {"DDI_NT_MOUSE", DDI_NT_MOUSE}, 132 {"DDI_NT_KEYBOARD", DDI_NT_KEYBOARD}, 133 {"DDI_NT_PARALLEL", DDI_NT_PARALLEL}, 134 {"DDI_NT_PRINTER", DDI_NT_PRINTER}, 135 {"DDI_NT_UGEN", DDI_NT_UGEN}, 136 {"DDI_NT_NEXUS", DDI_NT_NEXUS}, 137 {"DDI_NT_SCSI_NEXUS", DDI_NT_SCSI_NEXUS}, 138 {"DDI_NT_ATTACHMENT_POINT", DDI_NT_ATTACHMENT_POINT}, 139 {"DDI_NT_SCSI_ATTACHMENT_POINT", DDI_NT_SCSI_ATTACHMENT_POINT}, 140 {"DDI_NT_PCI_ATTACHMENT_POINT", DDI_NT_PCI_ATTACHMENT_POINT}, 141 {"DDI_NT_SBD_ATTACHMENT_POINT", DDI_NT_SBD_ATTACHMENT_POINT}, 142 {"DDI_NT_FC_ATTACHMENT_POINT", DDI_NT_FC_ATTACHMENT_POINT}, 143 {"DDI_NT_USB_ATTACHMENT_POINT", DDI_NT_USB_ATTACHMENT_POINT}, 144 {"DDI_NT_BLOCK_FABRIC", DDI_NT_BLOCK_FABRIC}, 145 {"DDI_NT_SMARTCARD_READER", DDI_NT_SMARTCARD_READER}, 146 {"DDI_NT_AV_ASYNC", DDI_NT_AV_ASYNC}, 147 {"DDI_NT_AV_ISOCH", DDI_NT_AV_ISOCH}, 148 { NULL, NULL } 149 }; 150 151 /* Node name */ 152 static char *pshot_compat_diskname = "cdisk"; 153 154 /* Compatible names... */ 155 static char *pshot_compat_psramdisks[] = { 156 "psramhead", 157 "psramrom", 158 "psramdisk", 159 "psramd", 160 "psramwhat" 161 }; 162 163 /* 164 * devices "natively" supported by pshot (i.e. included with SUNWiotu) 165 * used to initialize pshot_devices with 166 */ 167 static pshot_device_t pshot_stock_devices[] = { 168 {"disk", DDI_NT_BLOCK, "gen_drv"}, 169 {"disk_chan", DDI_NT_BLOCK_CHAN, "gen_drv"}, 170 {"disk_wwn", DDI_NT_BLOCK_WWN, "gen_drv"}, 171 {"disk_cdrom", DDI_NT_CD, "gen_drv"}, 172 {"disk_cdrom.chan", DDI_NT_CD_CHAN, "gen_drv"}, 173 /* Note: use bad_drv to force attach errors */ 174 {"disk_fd", DDI_NT_FD, "bad_drv"}, 175 {"tape", DDI_NT_TAPE, "gen_drv"}, 176 {"net", DDI_NT_NET, "gen_drv"}, 177 {"display", DDI_NT_DISPLAY, "gen_drv"}, 178 {"pseudo", DDI_PSEUDO, "gen_drv"}, 179 {"audio", DDI_NT_AUDIO, "gen_drv"}, 180 {"mouse", DDI_NT_MOUSE, "gen_drv"}, 181 {"keyboard", DDI_NT_KEYBOARD, "gen_drv"}, 182 {"nexus", DDI_NT_NEXUS, "pshot"} 183 }; 184 #define PSHOT_N_STOCK_DEVICES \ 185 (sizeof (pshot_stock_devices) / sizeof (pshot_device_t)) 186 187 static pshot_device_t *pshot_devices = NULL; 188 static size_t pshot_devices_len = 0; 189 190 /* protects <pshot_devices>, <pshot_devices_len> */ 191 static kmutex_t pshot_devices_lock; 192 193 194 /* 195 * event testing 196 */ 197 198 static ndi_event_definition_t pshot_ndi_event_defs[] = { 199 { PSHOT_EVENT_TAG_OFFLINE, PSHOT_EVENT_NAME_DEV_OFFLINE, 200 EPL_INTERRUPT, NDI_EVENT_POST_TO_ALL }, 201 202 { PSHOT_EVENT_TAG_DEV_RESET, PSHOT_EVENT_NAME_DEV_RESET, 203 EPL_INTERRUPT, NDI_EVENT_POST_TO_TGT }, 204 205 { PSHOT_EVENT_TAG_BUS_RESET, PSHOT_EVENT_NAME_BUS_RESET, 206 EPL_INTERRUPT, NDI_EVENT_POST_TO_ALL }, 207 208 { PSHOT_EVENT_TAG_BUS_QUIESCE, PSHOT_EVENT_NAME_BUS_QUIESCE, 209 EPL_INTERRUPT, NDI_EVENT_POST_TO_ALL }, 210 211 { PSHOT_EVENT_TAG_BUS_UNQUIESCE, PSHOT_EVENT_NAME_BUS_UNQUIESCE, 212 EPL_INTERRUPT, NDI_EVENT_POST_TO_ALL }, 213 214 { PSHOT_EVENT_TAG_TEST_POST, PSHOT_EVENT_NAME_BUS_TEST_POST, 215 EPL_INTERRUPT, NDI_EVENT_POST_TO_TGT } 216 }; 217 218 219 #define PSHOT_N_NDI_EVENTS \ 220 (sizeof (pshot_ndi_event_defs) / sizeof (ndi_event_definition_t)) 221 222 #ifdef DEBUG 223 224 static ndi_event_definition_t pshot_test_events[] = { 225 { 10, "test event 0", EPL_INTERRUPT, NDI_EVENT_POST_TO_ALL }, 226 { 11, "test event 1", EPL_KERNEL, NDI_EVENT_POST_TO_TGT }, 227 { 12, "test event 2", EPL_INTERRUPT, NDI_EVENT_POST_TO_TGT }, 228 { 13, "test event 3", EPL_INTERRUPT, NDI_EVENT_POST_TO_ALL }, 229 { 14, "test event 4", EPL_KERNEL, NDI_EVENT_POST_TO_ALL}, 230 { 15, "test event 5", EPL_INTERRUPT, NDI_EVENT_POST_TO_ALL }, 231 { 16, "test event 6", EPL_KERNEL, NDI_EVENT_POST_TO_ALL }, 232 { 17, "test event 7", EPL_INTERRUPT, NDI_EVENT_POST_TO_ALL } 233 }; 234 235 static ndi_event_definition_t pshot_test_events_high[] = { 236 { 20, "test event high 0", EPL_HIGHLEVEL, NDI_EVENT_POST_TO_ALL} 237 }; 238 239 #define PSHOT_N_TEST_EVENTS \ 240 (sizeof (pshot_test_events)/sizeof (ndi_event_definition_t)) 241 #endif 242 243 struct register_events { 244 char *event_name; 245 ddi_eventcookie_t event_cookie; 246 void (*event_callback) 247 (dev_info_t *, 248 ddi_eventcookie_t, 249 void *arg, 250 void *impldata); 251 ddi_callback_id_t cb_id; 252 }; 253 254 struct register_events pshot_register_events[] = { 255 { PSHOT_EVENT_NAME_DEV_OFFLINE, 0, pshot_event_cb, 0 }, 256 { PSHOT_EVENT_NAME_DEV_RESET, 0, pshot_event_cb, 0 }, 257 { PSHOT_EVENT_NAME_BUS_RESET, 0, pshot_event_cb, 0 }, 258 { PSHOT_EVENT_NAME_BUS_QUIESCE, 0, pshot_event_cb, 0 }, 259 { PSHOT_EVENT_NAME_BUS_UNQUIESCE, 0, pshot_event_cb, 0 }, 260 { PSHOT_EVENT_NAME_BUS_TEST_POST, 0, pshot_event_cb, 0 } 261 }; 262 263 #define PSHOT_N_DDI_EVENTS \ 264 (sizeof (pshot_register_events) / sizeof (struct register_events)) 265 266 267 #ifdef DEBUG 268 269 static struct register_events pshot_register_test[] = { 270 { "test event 0", 0, pshot_event_cb_test, 0}, 271 { "test event 1", 0, pshot_event_cb_test, 0}, 272 { "test event 2", 0, pshot_event_cb_test, 0}, 273 { "test event 3", 0, pshot_event_cb_test, 0}, 274 { "test event 4", 0, pshot_event_cb_test, 0}, 275 { "test event 5", 0, pshot_event_cb_test, 0}, 276 { "test event 6", 0, pshot_event_cb_test, 0}, 277 { "test event 7", 0, pshot_event_cb_test, 0} 278 }; 279 280 281 static struct register_events pshot_register_high_test[] = { 282 {"test event high 0", 0, pshot_event_cb_test, 0} 283 }; 284 285 #endif /* DEBUG */ 286 287 static struct { 288 int ioctl_int; 289 char *ioctl_char; 290 } pshot_devctls[] = { 291 {DEVCTL_DEVICE_GETSTATE, "DEVCTL_DEVICE_GETSTATE"}, 292 {DEVCTL_DEVICE_ONLINE, "DEVCTL_DEVICE_ONLINE"}, 293 {DEVCTL_DEVICE_OFFLINE, "DEVCTL_DEVICE_OFFLINE"}, 294 {DEVCTL_DEVICE_REMOVE, "DEVCTL_DEVICE_REMOVE"}, 295 {DEVCTL_BUS_GETSTATE, "DEVCTL_BUS_GETSTATE"}, 296 {DEVCTL_BUS_DEV_CREATE, "DEVCTL_BUS_DEV_CREATE"}, 297 {DEVCTL_BUS_RESET, "DEVCTL_BUS_RESET"}, 298 {DEVCTL_BUS_RESETALL, "DEVCTL_BUS_RESETALL"}, 299 {0, NULL} 300 }; 301 302 static struct bus_ops pshot_bus_ops = { 303 BUSO_REV, /* busops_rev */ 304 nullbusmap, /* bus_map */ 305 NULL, /* bus_get_intrspec */ 306 NULL, /* bus_add_interspec */ 307 NULL, /* bus_remove_interspec */ 308 i_ddi_map_fault, /* bus_map_fault */ 309 ddi_dma_map, /* bus_dma_map */ 310 ddi_dma_allochdl, /* bus_dma_allochdl */ 311 ddi_dma_freehdl, /* bus_dma_freehdl */ 312 ddi_dma_bindhdl, /* bus_dma_bindhdl */ 313 ddi_dma_unbindhdl, /* bus_dma_unbindhdl */ 314 ddi_dma_flush, /* bus_dma_flush */ 315 ddi_dma_win, /* bus_dma_win */ 316 ddi_dma_mctl, /* bus_dma_ctl */ 317 pshot_ctl, /* bus_ctl */ 318 ddi_bus_prop_op, /* bus_prop_op */ 319 pshot_get_eventcookie, /* bus_get_eventcookie */ 320 pshot_add_eventcall, /* bus_add_eventcall */ 321 pshot_remove_eventcall, /* bus_remove_event */ 322 pshot_post_event, /* bus_post_event */ 323 NULL, /* bus_intr_ctl */ 324 pshot_bus_config, /* bus_config */ 325 pshot_bus_unconfig, /* bus_unconfig */ 326 NULL, /* bus_fm_init */ 327 NULL, /* bus_fm_fini */ 328 NULL, /* bus_fm_access_enter */ 329 NULL, /* bus_fm_access_exit */ 330 pshot_bus_power, /* bus_power */ 331 pshot_bus_introp /* bus_intr_op */ 332 }; 333 334 static struct cb_ops pshot_cb_ops = { 335 pshot_open, /* open */ 336 pshot_close, /* close */ 337 nodev, /* strategy */ 338 nodev, /* print */ 339 nodev, /* dump */ 340 nodev, /* read */ 341 nodev, /* write */ 342 pshot_ioctl, /* ioctl */ 343 nodev, /* devmap */ 344 nodev, /* mmap */ 345 nodev, /* segmap */ 346 nochpoll, /* poll */ 347 ddi_prop_op, /* prop_op */ 348 NULL, /* streamtab */ 349 D_NEW | D_MP | D_HOTPLUG, /* flags */ 350 CB_REV, /* cb_rev */ 351 nodev, /* aread */ 352 nodev, /* awrite */ 353 }; 354 355 static struct dev_ops pshot_ops = { 356 DEVO_REV, /* devo_rev, */ 357 0, /* refcnt */ 358 pshot_info, /* getinfo */ 359 nulldev, /* identify */ 360 pshot_probe, /* probe */ 361 pshot_attach, /* attach */ 362 pshot_detach, /* detach */ 363 nodev, /* reset */ 364 &pshot_cb_ops, /* driver operations */ 365 &pshot_bus_ops, /* bus operations */ 366 pshot_power, /* power */ 367 ddi_quiesce_not_supported, /* devo_quiesce */ 368 369 }; 370 371 372 /* 373 * Module linkage information for the kernel. 374 */ 375 static struct modldrv modldrv = { 376 &mod_driverops, 377 "pshotnex", 378 &pshot_ops, 379 }; 380 381 static struct modlinkage modlinkage = { 382 MODREV_1, &modldrv, NULL 383 }; 384 385 386 /* 387 * pshot_devices is set up on the first attach and destroyed on fini 388 * 389 * therefore PSHOT_PROP_DEV* properties may be set just for the root device, 390 * instead of being set globably, in pshot.conf by specifying the properties 391 * on a single line in the form: 392 * name="pshot" parent="/" <dev props ..> 393 * to unclutter a device tree snapshot. 394 * this of course produces a long single line that may wrap around several 395 * times on screen 396 */ 397 398 int 399 _init(void) 400 { 401 int rv; 402 403 rv = ddi_soft_state_init(&pshot_softstatep, sizeof (pshot_t), 0); 404 405 if (rv != DDI_SUCCESS) 406 return (rv); 407 408 mutex_init(&pshot_devices_lock, NULL, MUTEX_DRIVER, NULL); 409 pshot_devices = NULL; 410 pshot_devices_len = 0; 411 412 if ((rv = mod_install(&modlinkage)) != 0) { 413 ddi_soft_state_fini(&pshot_softstatep); 414 mutex_destroy(&pshot_devices_lock); 415 } 416 return (rv); 417 } 418 419 int 420 _fini(void) 421 { 422 int rv; 423 424 if ((rv = mod_remove(&modlinkage)) != 0) 425 return (rv); 426 427 ddi_soft_state_fini(&pshot_softstatep); 428 mutex_destroy(&pshot_devices_lock); 429 if (pshot_devices) 430 pshot_devices_free(pshot_devices, pshot_devices_len); 431 return (0); 432 } 433 434 int 435 _info(struct modinfo *modinfop) 436 { 437 return (mod_info(&modlinkage, modinfop)); 438 } 439 440 441 /*ARGSUSED*/ 442 static int 443 pshot_probe(dev_info_t *devi) 444 { 445 int instance = ddi_get_instance(devi); 446 char *bus_addr; 447 448 /* 449 * Hook for tests to force probe fail 450 */ 451 if (ddi_prop_lookup_string(DDI_DEV_T_ANY, devi, 0, "bus-addr", 452 &bus_addr) == DDI_PROP_SUCCESS) { 453 if (strncmp(bus_addr, "failprobe", 9) == 0) { 454 if (pshot_debug) 455 cmn_err(CE_CONT, "pshot%d: " 456 "%s forced probe failure\n", 457 instance, bus_addr); 458 ddi_prop_free(bus_addr); 459 return (DDI_PROBE_FAILURE); 460 } 461 ddi_prop_free(bus_addr); 462 } 463 464 return (DDI_PROBE_SUCCESS); 465 } 466 467 468 /*ARGSUSED*/ 469 static int 470 pshot_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 471 { 472 int instance; 473 minor_t minor; 474 pshot_t *pshot; 475 476 minor = getminor((dev_t)arg); 477 instance = pshot_minor_decode_inst(minor); 478 switch (infocmd) { 479 case DDI_INFO_DEVT2DEVINFO: 480 pshot = ddi_get_soft_state(pshot_softstatep, instance); 481 if (pshot == NULL) { 482 cmn_err(CE_WARN, "pshot_info: get soft state failed " 483 "on minor %u, instance %d", minor, instance); 484 return (DDI_FAILURE); 485 } 486 *result = (void *)pshot->dip; 487 break; 488 case DDI_INFO_DEVT2INSTANCE: 489 *result = (void *)(uintptr_t)instance; 490 break; 491 default: 492 cmn_err(CE_WARN, "pshot_info: unrecognized cmd 0x%x on " 493 "minor %u, instance %d", infocmd, minor, instance); 494 return (DDI_FAILURE); 495 } 496 497 return (DDI_SUCCESS); 498 } 499 500 501 static int 502 pshot_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 503 { 504 int instance = ddi_get_instance(devi); 505 pshot_t *pshot; 506 int rval, i; 507 int prop_flags = DDI_PROP_DONTPASS | DDI_PROP_NOTPROM; 508 char *bus_addr; 509 char *pm_comp[] = { 510 "NAME=bus", 511 "0=B3", 512 "1=B2", 513 "2=B1", 514 "3=B0"}; 515 char *pm_hw_state = {"needs-suspend-resume"}; 516 517 pshot_prop_autoattach = ddi_prop_get_int(DDI_DEV_T_ANY, devi, 518 prop_flags, "autoattach", 0); 519 520 switch (cmd) { 521 522 case DDI_ATTACH: 523 if (pshot_debug) 524 cmn_err(CE_CONT, "attach: %s%d/pshot%d\n", 525 ddi_get_name(ddi_get_parent(devi)), 526 ddi_get_instance(ddi_get_parent(devi)), 527 instance); 528 529 /* 530 * Hook for tests to force attach fail 531 */ 532 if ((ddi_prop_lookup_string(DDI_DEV_T_ANY, devi, 0, "bus-addr", 533 &bus_addr) == DDI_PROP_SUCCESS) && bus_addr != NULL) { 534 if (strncmp(bus_addr, "failattach", 10) == 0) { 535 if (pshot_debug) 536 cmn_err(CE_CONT, "pshot%d: " 537 "%s forced attach failure\n", 538 instance, bus_addr); 539 ddi_prop_free(bus_addr); 540 return (DDI_FAILURE); 541 } 542 ddi_prop_free(bus_addr); 543 } 544 545 /* 546 * minor nodes setup 547 */ 548 if (ddi_soft_state_zalloc(pshot_softstatep, instance) != 549 DDI_SUCCESS) { 550 return (DDI_FAILURE); 551 } 552 pshot = ddi_get_soft_state(pshot_softstatep, instance); 553 pshot->dip = devi; 554 pshot->instance = instance; 555 mutex_init(&pshot->lock, NULL, MUTEX_DRIVER, NULL); 556 557 /* set each minor, then create on dip all together */ 558 559 i = PSHOT_NODENUM_DEVCTL; 560 pshot->nodes[i].pshot = pshot; 561 pshot->nodes[i].minor = pshot_minor_encode(instance, i); 562 (void) strncpy(pshot->nodes[i].name, PSHOT_NODENAME_DEVCTL, 563 PSHOT_MAX_MINOR_NAMELEN); 564 565 i = PSHOT_NODENUM_TESTCTL; 566 pshot->nodes[i].pshot = pshot; 567 pshot->nodes[i].minor = pshot_minor_encode(instance, i); 568 (void) strncpy(pshot->nodes[i].name, PSHOT_NODENAME_TESTCTL, 569 PSHOT_MAX_MINOR_NAMELEN); 570 571 /* this assumes contiguous a filling */ 572 for (i = 0; i <= PSHOT_MAX_NODENUM; i++) { 573 if (ddi_create_minor_node(devi, pshot->nodes[i].name, 574 S_IFCHR, pshot->nodes[i].minor, DDI_NT_NEXUS, 0) != 575 DDI_SUCCESS) { 576 cmn_err(CE_WARN, "attach: cannot create " 577 "minor %s", pshot->nodes[i].name); 578 goto FAIL_ATTACH; 579 } 580 } 581 582 /* 583 * pshot_devices setup 584 */ 585 if (pshot_devices_setup(devi)) { 586 cmn_err(CE_WARN, "attach: pshot devices setup " 587 "failed"); 588 goto FAIL_ATTACH; 589 } 590 591 /* 592 * events setup 593 */ 594 for (i = 0; i < PSHOT_N_DDI_EVENTS; i++) { 595 rval = ddi_get_eventcookie(devi, 596 pshot_register_events[i].event_name, 597 &pshot_register_events[i].event_cookie); 598 599 if (pshot_debug) 600 cmn_err(CE_CONT, "pshot%d: event=%s:" 601 "ddi_get_eventcookie rval=%d\n", 602 instance, 603 pshot_register_events[i].event_name, rval); 604 605 if (rval == DDI_SUCCESS) { 606 rval = ddi_add_event_handler(devi, 607 pshot_register_events[i].event_cookie, 608 pshot_register_events[i].event_callback, 609 (void *)pshot, 610 &pshot->callback_cache[i]); 611 612 if (pshot_debug) 613 cmn_err(CE_CONT, "pshot%d: event=%s: " 614 "ddi_add_event_handler rval=%d\n", 615 instance, 616 pshot_register_events[i].event_name, 617 rval); 618 } 619 } 620 621 #ifdef DEBUG 622 if (pshot_event_test_enable) { 623 pshot_event_test((void *)pshot); 624 (void) timeout(pshot_event_test_post_one, (void *)pshot, 625 instance * drv_usectohz(60000000)); 626 } 627 #endif 628 629 /* 630 * allocate an ndi event handle 631 */ 632 if (ndi_event_alloc_hdl(devi, NULL, &pshot->ndi_event_hdl, 633 NDI_SLEEP) != NDI_SUCCESS) { 634 goto FAIL_ATTACH; 635 } 636 637 pshot->ndi_events.ndi_events_version = NDI_EVENTS_REV1; 638 pshot->ndi_events.ndi_n_events = PSHOT_N_NDI_EVENTS; 639 pshot->ndi_events.ndi_event_defs = pshot_ndi_event_defs; 640 641 if (ndi_event_bind_set(pshot->ndi_event_hdl, &pshot->ndi_events, 642 NDI_SLEEP) != NDI_SUCCESS) { 643 cmn_err(CE_CONT, "pshot%d bind set failed\n", 644 instance); 645 } 646 647 /* 648 * setup a test for nexus auto-attach iff we are 649 * a second level pshot node (parent == /SUNW,pshot) 650 * enable by setting "autoattach=1" in pshot.conf 651 */ 652 if ((PARENT_IS_PSHOT(devi)) && (pshot_prop_autoattach != 0) && 653 (ddi_get_instance(ddi_get_parent(devi))) == 0) 654 pshot_setup_autoattach(devi); 655 656 /* 657 * initialize internal state to idle: busy = 0, 658 * power level = -1 659 */ 660 mutex_enter(&pshot->lock); 661 pshot->busy = 0; 662 pshot->busy_ioctl = 0; 663 pshot->level = -1; 664 pshot->state &= ~STRICT_PARENT; 665 pshot->state |= PM_SUPPORTED; 666 mutex_exit(&pshot->lock); 667 668 /* 669 * Create the "pm-want-child-notification?" property 670 * for the root node /devices/pshot 671 */ 672 if (instance == 0) { 673 if (pshot_debug) { 674 cmn_err(CE_CONT, "pshot%d: DDI_ATTACH:\n\t" 675 " create the" 676 " \"pm-want-child-notification?\" property" 677 " for the root node\n", instance); 678 } 679 if (ddi_prop_create(DDI_DEV_T_NONE, devi, 0, 680 "pm-want-child-notification?", NULL, 0) 681 != DDI_PROP_SUCCESS) { 682 cmn_err(CE_WARN, "%s%d:\n\t" 683 " unable to create the" 684 " \"pm-want-child-notification?\"" 685 " property", ddi_get_name(devi), 686 ddi_get_instance(devi)); 687 688 goto FAIL_ATTACH; 689 } 690 } 691 692 /* 693 * Check if the pm-want-child-notification? property was 694 * created in pshot_bus_config_setup_nexus() by the parent. 695 * Set the STRICT_PARENT flag if not. 696 */ 697 if (ddi_prop_exists(DDI_DEV_T_ANY, devi, 698 (DDI_PROP_DONTPASS | DDI_PROP_NOTPROM), 699 "pm-want-child-notification?") != 1) { 700 if (pshot_debug) { 701 cmn_err(CE_CONT, "pshot%d: DDI_ATTACH:" 702 " STRICT PARENT\n", instance); 703 } 704 mutex_enter(&pshot->lock); 705 pshot->state |= STRICT_PARENT; 706 mutex_exit(&pshot->lock); 707 } else { 708 if (pshot_debug) { 709 cmn_err(CE_CONT, "pshot%d: DDI_ATTACH:" 710 " INVOLVED PARENT\n", instance); 711 } 712 mutex_enter(&pshot->lock); 713 pshot->state &= ~STRICT_PARENT; 714 mutex_exit(&pshot->lock); 715 } 716 717 /* 718 * create the pm-components property: one component 719 * with 4 power levels. 720 * - skip for pshot@XXX,nopm and pshot@XXX,nopm_strict: 721 * "no-pm-components" property 722 */ 723 if (ddi_prop_exists(DDI_DEV_T_ANY, devi, 724 (DDI_PROP_DONTPASS | DDI_PROP_NOTPROM), 725 "no-pm-components") == 0) { 726 if (pshot_debug) { 727 cmn_err(CE_CONT, "pshot%d: DDI_ATTACH:" 728 " create the \"pm_components\" property\n", 729 instance); 730 } 731 if (ddi_prop_update_string_array(DDI_DEV_T_NONE, devi, 732 "pm-components", pm_comp, 5) != DDI_PROP_SUCCESS) { 733 cmn_err(CE_WARN, "%s%d: DDI_ATTACH:\n\t" 734 " unable to create the \"pm-components\"" 735 " property", ddi_get_name(devi), 736 ddi_get_instance(devi)); 737 738 goto FAIL_ATTACH; 739 } 740 } else { 741 if (pshot_debug) { 742 cmn_err(CE_CONT, "pshot%d: DDI_ATTACH:" 743 " NO-PM_COMPONENTS PARENT\n", instance); 744 } 745 mutex_enter(&pshot->lock); 746 pshot->state &= ~PM_SUPPORTED; 747 mutex_exit(&pshot->lock); 748 } 749 750 /* 751 * create the property needed to get DDI_SUSPEND 752 * and DDI_RESUME calls 753 */ 754 if (pshot_debug) { 755 cmn_err(CE_CONT, "pshot%d: DDI_ATTACH:" 756 " create pm-hardware-state property\n", 757 instance); 758 } 759 if (ddi_prop_update_string(DDI_DEV_T_NONE, devi, 760 "pm-hardware-state", pm_hw_state) != DDI_PROP_SUCCESS) { 761 cmn_err(CE_WARN, "%s%d: DDI_ATTACH:\n\t" 762 " unable to create the \"pm-hardware-state\"" 763 " property", ddi_get_name(devi), 764 ddi_get_instance(devi)); 765 766 goto FAIL_ATTACH; 767 } 768 769 /* 770 * set power level to max via pm_raise_power(), 771 * - skip if PM_SUPPORTED is not set (pshot@XXX,nopm) 772 */ 773 if (pshot->state & PM_SUPPORTED) { 774 if (pshot_debug) { 775 cmn_err(CE_CONT, "pshot%d: DDI_ATTACH:" 776 " raise power to MAXPWR\n", instance); 777 } 778 if (pm_raise_power(pshot->dip, 0, MAXPWR) != 779 DDI_SUCCESS) { 780 cmn_err(CE_WARN, "%s%d: DDI_ATTACH:" 781 " pm_raise_power failed", 782 ddi_get_name(devi), 783 ddi_get_instance(devi)); 784 785 goto FAIL_ATTACH; 786 787 } 788 } 789 790 if (pshot_log) 791 cmn_err(CE_CONT, "pshot%d attached\n", instance); 792 ddi_report_dev(devi); 793 794 return (DDI_SUCCESS); 795 /*NOTREACHED*/ 796 FAIL_ATTACH: 797 ddi_remove_minor_node(devi, NULL); 798 mutex_destroy(&pshot->lock); 799 ddi_soft_state_free(pshot_softstatep, instance); 800 return (DDI_FAILURE); 801 802 case DDI_RESUME: 803 if (pshot_debug) { 804 cmn_err(CE_CONT, "pshot%d: DDI_RESUME: resuming\n", 805 instance); 806 } 807 pshot = ddi_get_soft_state(pshot_softstatep, instance); 808 809 /* 810 * set power level to max via pm_raise_power(), 811 * - skip if PM_SUPPORTED is not set (pshot@XXX,nopm) 812 */ 813 if (pshot->state & PM_SUPPORTED) { 814 if (pshot_debug) { 815 cmn_err(CE_CONT, "pshot%d: DDI_RESUME:" 816 " raise power to MAXPWR\n", instance); 817 } 818 if (pm_raise_power(pshot->dip, 0, MAXPWR) != 819 DDI_SUCCESS) { 820 cmn_err(CE_WARN, "%s%d: DDI_RESUME:" 821 " pm_raise_power failed", 822 ddi_get_name(devi), 823 ddi_get_instance(devi)); 824 } 825 } 826 827 if (pshot_debug) { 828 cmn_err(CE_CONT, "pshot%d: DDI_RESUME: resumed\n", 829 instance); 830 } 831 return (DDI_SUCCESS); 832 833 default: 834 return (DDI_FAILURE); 835 } 836 } 837 838 static int 839 pshot_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) 840 { 841 int instance = ddi_get_instance(devi); 842 int i, rval; 843 pshot_t *pshot = ddi_get_soft_state(pshot_softstatep, instance); 844 int level_tmp; 845 846 if (pshot == NULL) 847 return (DDI_FAILURE); 848 849 switch (cmd) { 850 851 case DDI_DETACH: 852 if (pshot_debug) 853 cmn_err(CE_CONT, "pshot%d: DDI_DETACH\n", instance); 854 /* 855 * power off component 0 856 * - skip if PM_SUPPORTED is not set (pshot@XXX,nopm) 857 */ 858 if (pshot->state & PM_SUPPORTED) { 859 if (pshot_debug) { 860 cmn_err(CE_CONT, "pshot%d: DDI_DETACH:" 861 " power off\n", instance); 862 } 863 if (pm_lower_power(pshot->dip, 0, 0) != DDI_SUCCESS) { 864 cmn_err(CE_WARN, "%s%d: DDI_DETACH:\n\t" 865 "pm_lower_power failed for comp 0 to" 866 " level 0", ddi_get_name(devi), 867 ddi_get_instance(devi)); 868 869 return (DDI_FAILURE); 870 } 871 872 /* 873 * Check if the power level is actually OFF. 874 * Issue pm_power_has_changed if not. 875 */ 876 mutex_enter(&pshot->lock); 877 if (pshot->level != 0) { 878 if (pshot_debug) { 879 cmn_err(CE_NOTE, "pshot%d:" 880 " DDI_DETACH: power off via" 881 " pm_power_has_changed instead\n", 882 instance); 883 } 884 level_tmp = pshot->level; 885 pshot->level = 0; 886 if (pm_power_has_changed(pshot->dip, 0, 0) != 887 DDI_SUCCESS) { 888 if (pshot_debug) { 889 cmn_err(CE_NOTE, "pshot%d:" 890 " DDI_DETACH:" 891 " pm_power_has_changed" 892 " failed\n", instance); 893 } 894 pshot->level = level_tmp; 895 mutex_exit(&pshot->lock); 896 897 return (DDI_FAILURE); 898 } 899 } 900 mutex_exit(&pshot->lock); 901 } 902 903 for (i = 0; i < PSHOT_N_DDI_EVENTS; i++) { 904 if (pshot->callback_cache[i] != NULL) { 905 rval = ddi_remove_event_handler( 906 pshot->callback_cache[i]); 907 ASSERT(rval == DDI_SUCCESS); 908 } 909 } 910 911 #ifdef DEBUG 912 for (i = 0; i < PSHOT_N_TEST_EVENTS; i++) { 913 if (pshot->test_callback_cache[i] != NULL) { 914 rval = ddi_remove_event_handler( 915 pshot->test_callback_cache[i]); 916 ASSERT(rval == DDI_SUCCESS); 917 } 918 } 919 #endif 920 rval = ndi_event_free_hdl(pshot->ndi_event_hdl); 921 ASSERT(rval == DDI_SUCCESS); 922 923 if (pshot_log) 924 cmn_err(CE_CONT, "pshot%d detached\n", instance); 925 926 ddi_remove_minor_node(devi, NULL); 927 mutex_destroy(&pshot->lock); 928 ddi_soft_state_free(pshot_softstatep, instance); 929 break; 930 931 case DDI_SUSPEND: 932 if (pshot_debug) 933 cmn_err(CE_CONT, "pshot%d: DDI_SUSPEND\n", instance); 934 /* 935 * fail the suspend if FAIL_SUSPEND_FLAG is set. 936 * clear the FAIL_SUSPEND_FLAG flag 937 */ 938 mutex_enter(&pshot->lock); 939 if (pshot->state & FAIL_SUSPEND_FLAG) { 940 if (pshot_debug) { 941 cmn_err(CE_CONT, "pshot%d:" 942 " FAIL_SUSPEND_FLAG set, fail suspend\n", 943 ddi_get_instance(devi)); 944 } 945 pshot->state &= ~FAIL_SUSPEND_FLAG; 946 rval = DDI_FAILURE; 947 } else { 948 rval = DDI_SUCCESS; 949 } 950 mutex_exit(&pshot->lock); 951 952 /* 953 * power OFF via pm_power_has_changed 954 */ 955 mutex_enter(&pshot->lock); 956 if (pshot->state & PM_SUPPORTED) { 957 if (pshot_debug) { 958 cmn_err(CE_CONT, "pshot%d: DDI_SUSPEND:" 959 " power off via pm_power_has_changed\n", 960 instance); 961 } 962 level_tmp = pshot->level; 963 pshot->level = 0; 964 if (pm_power_has_changed(pshot->dip, 0, 0) != 965 DDI_SUCCESS) { 966 if (pshot_debug) { 967 cmn_err(CE_NOTE, "pshot%d:" 968 " DDI_SUSPEND:" 969 " pm_power_has_changed failed\n", 970 instance); 971 } 972 pshot->level = level_tmp; 973 rval = DDI_FAILURE; 974 } 975 } 976 mutex_exit(&pshot->lock); 977 return (rval); 978 979 default: 980 break; 981 } 982 983 return (DDI_SUCCESS); 984 } 985 986 987 /* 988 * returns number of bits to represent <val> 989 */ 990 static size_t 991 pshot_numbits(size_t val) 992 { 993 size_t bitcnt; 994 995 if (val == 0) 996 return (0); 997 for (bitcnt = 1; 1 << bitcnt < val; bitcnt++) 998 ; 999 return (bitcnt); 1000 } 1001 1002 /* 1003 * returns a minor number encoded with instance <inst> and an index <nodenum> 1004 * that identifies the minor node for this instance 1005 */ 1006 static minor_t 1007 pshot_minor_encode(int inst, minor_t nodenum) 1008 { 1009 return (((minor_t)inst << PSHOT_NODENUM_BITS()) | 1010 (((1 << PSHOT_NODENUM_BITS()) - 1) & nodenum)); 1011 } 1012 1013 /* 1014 * returns instance of <minor> 1015 */ 1016 static int 1017 pshot_minor_decode_inst(minor_t minor) 1018 { 1019 return (minor >> PSHOT_NODENUM_BITS()); 1020 } 1021 1022 /* 1023 * returns node number indexing a minor node for the instance in <minor> 1024 */ 1025 static minor_t 1026 pshot_minor_decode_nodenum(minor_t minor) 1027 { 1028 return (minor & ((1 << PSHOT_NODENUM_BITS()) - 1)); 1029 } 1030 1031 1032 /* 1033 * pshot_bus_introp: pshot convert an interrupt number to an 1034 * interrupt. NO OP for pseudo drivers. 1035 */ 1036 /*ARGSUSED*/ 1037 static int 1038 pshot_bus_introp(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op, 1039 ddi_intr_handle_impl_t *hdlp, void *result) 1040 { 1041 return (DDI_FAILURE); 1042 } 1043 static int 1044 pshot_ctl(dev_info_t *dip, dev_info_t *rdip, 1045 ddi_ctl_enum_t ctlop, void *arg, void *result) 1046 { 1047 int instance; 1048 pshot_t *pshot; 1049 char *childname; 1050 int childinstance; 1051 char *name; 1052 int circ; 1053 struct attachspec *as; 1054 struct detachspec *ds; 1055 int rval = DDI_SUCCESS; 1056 int no_pm_components_child; 1057 1058 name = ddi_get_name(dip); 1059 instance = ddi_get_instance(dip); 1060 pshot = ddi_get_soft_state(pshot_softstatep, instance); 1061 if (pshot == NULL) { 1062 return (ENXIO); 1063 } 1064 1065 switch (ctlop) { 1066 case DDI_CTLOPS_REPORTDEV: 1067 if (rdip == (dev_info_t *)0) 1068 return (DDI_FAILURE); 1069 cmn_err(CE_CONT, "?pshot-device: %s%d\n", 1070 ddi_get_name(rdip), ddi_get_instance(rdip)); 1071 return (DDI_SUCCESS); 1072 1073 case DDI_CTLOPS_INITCHILD: 1074 { 1075 dev_info_t *child = (dev_info_t *)arg; 1076 1077 if (pshot_debug) { 1078 cmn_err(CE_CONT, "initchild %s%d/%s%d state 0x%x\n", 1079 ddi_get_name(dip), ddi_get_instance(dip), 1080 ddi_node_name(child), ddi_get_instance(child), 1081 DEVI(child)->devi_state); 1082 } 1083 1084 return (pshot_initchild(dip, child)); 1085 } 1086 1087 case DDI_CTLOPS_UNINITCHILD: 1088 { 1089 dev_info_t *child = (dev_info_t *)arg; 1090 1091 if (pshot_debug) { 1092 cmn_err(CE_CONT, "uninitchild %s%d/%s%d state 0x%x\n", 1093 ddi_get_name(dip), ddi_get_instance(dip), 1094 ddi_node_name(child), ddi_get_instance(child), 1095 DEVI(child)->devi_state); 1096 } 1097 1098 return (pshot_uninitchild(dip, child)); 1099 } 1100 1101 case DDI_CTLOPS_DMAPMAPC: 1102 case DDI_CTLOPS_REPORTINT: 1103 case DDI_CTLOPS_REGSIZE: 1104 case DDI_CTLOPS_NREGS: 1105 case DDI_CTLOPS_SIDDEV: 1106 case DDI_CTLOPS_SLAVEONLY: 1107 case DDI_CTLOPS_AFFINITY: 1108 case DDI_CTLOPS_POKE: 1109 case DDI_CTLOPS_PEEK: 1110 /* 1111 * These ops correspond to functions that "shouldn't" be called 1112 * by a pseudo driver. So we whine when we're called. 1113 */ 1114 cmn_err(CE_CONT, "%s%d: invalid op (%d) from %s%d\n", 1115 ddi_get_name(dip), ddi_get_instance(dip), 1116 ctlop, ddi_get_name(rdip), ddi_get_instance(rdip)); 1117 return (DDI_FAILURE); 1118 1119 case DDI_CTLOPS_ATTACH: 1120 { 1121 dev_info_t *child = (dev_info_t *)rdip; 1122 childname = ddi_node_name(child); 1123 childinstance = ddi_get_instance(child); 1124 as = (struct attachspec *)arg; 1125 1126 no_pm_components_child = 0; 1127 if (ddi_prop_exists(DDI_DEV_T_ANY, child, 1128 (DDI_PROP_DONTPASS | DDI_PROP_NOTPROM), 1129 "no-pm-components") == 1) { 1130 no_pm_components_child = 1; 1131 } 1132 if (pshot_debug) { 1133 cmn_err(CE_CONT, "%s%d: ctl_attach %s%d [%d]\n", 1134 name, instance, childname, childinstance, 1135 no_pm_components_child); 1136 } 1137 1138 ndi_devi_enter(dip, &circ); 1139 1140 switch (as->when) { 1141 case DDI_PRE: 1142 /* 1143 * Mark nexus busy before a child attaches. 1144 * - skip if PM_SUPPORTED is not set (pshot@XXX,nopm 1145 * - pshot@XXX,nopm_strict) 1146 */ 1147 if (!(pshot->state & PM_SUPPORTED)) 1148 break; 1149 mutex_enter(&pshot->lock); 1150 ++(pshot->busy); 1151 if (pshot_debug_busy) { 1152 cmn_err(CE_CONT, "%s%d:" 1153 " ctl_attach_pre: busy for %s%d:" 1154 " busy = %d\n", name, instance, 1155 childname, childinstance, 1156 pshot->busy); 1157 } 1158 mutex_exit(&pshot->lock); 1159 rval = pm_busy_component(dip, 0); 1160 ASSERT(rval == DDI_SUCCESS); 1161 break; 1162 case DDI_POST: 1163 /* 1164 * Mark nexus idle after a child attaches. 1165 * - skip if PM_SUPPORTED is not set (pshot@XXX,nopm). 1166 * - also skip if this is not a stict parent and 1167 * - the child is a tape device or a no-pm-components 1168 * - nexus node. 1169 */ 1170 if (!(pshot->state & PM_SUPPORTED) || 1171 (strcmp(childname, "tape") == 0 && 1172 !(pshot->state & STRICT_PARENT)) || 1173 no_pm_components_child) 1174 break; 1175 mutex_enter(&pshot->lock); 1176 ASSERT(pshot->busy > 0); 1177 --pshot->busy; 1178 if (pshot_debug_busy) { 1179 cmn_err(CE_CONT, "%s%d:" 1180 " ctl_attach_post: idle for %s%d:" 1181 " busy = %d\n", name, instance, 1182 childname, childinstance, 1183 pshot->busy); 1184 } 1185 mutex_exit(&pshot->lock); 1186 rval = pm_idle_component(dip, 0); 1187 ASSERT(rval == DDI_SUCCESS); 1188 break; 1189 } 1190 1191 ndi_devi_exit(dip, circ); 1192 1193 return (rval); 1194 } 1195 case DDI_CTLOPS_DETACH: 1196 { 1197 dev_info_t *child = (dev_info_t *)rdip; 1198 childname = ddi_node_name(child); 1199 childinstance = ddi_get_instance(child); 1200 ds = (struct detachspec *)arg; 1201 1202 no_pm_components_child = 0; 1203 if (ddi_prop_exists(DDI_DEV_T_ANY, child, 1204 (DDI_PROP_DONTPASS | DDI_PROP_NOTPROM), 1205 "no-pm-components") == 1) { 1206 no_pm_components_child = 1; 1207 } 1208 if (pshot_debug) { 1209 cmn_err(CE_CONT, 1210 "%s%d: ctl_detach %s%d [%d]\n", 1211 name, instance, childname, childinstance, 1212 no_pm_components_child); 1213 } 1214 1215 ndi_devi_enter(dip, &circ); 1216 1217 switch (ds->when) { 1218 case DDI_PRE: 1219 /* 1220 * Mark nexus busy before a child detaches. 1221 * - skip if PM_SUPPORTED is not set (pshot@XXX,nopm 1222 * - pshot@XXX,nopm_strict), or if the child is a 1223 * - no-pm-components nexus node. 1224 */ 1225 if (!(pshot->state & PM_SUPPORTED) || 1226 (strcmp(childname, "tape") == 0 && 1227 !(pshot->state & STRICT_PARENT)) || 1228 no_pm_components_child) 1229 break; 1230 mutex_enter(&pshot->lock); 1231 ++(pshot->busy); 1232 if (pshot_debug_busy) { 1233 cmn_err(CE_CONT, "%s%d:" 1234 " ctl_detach_pre: busy for %s%d:" 1235 " busy = %d\n", name, instance, 1236 childname, childinstance, 1237 pshot->busy); 1238 } 1239 mutex_exit(&pshot->lock); 1240 rval = pm_busy_component(dip, 0); 1241 ASSERT(rval == DDI_SUCCESS); 1242 1243 break; 1244 case DDI_POST: 1245 /* 1246 * Mark nexus idle after a child detaches. 1247 * - skip if PM_SUPPORTED is not set (pshot@XXX,nopm) 1248 */ 1249 if (!(pshot->state & PM_SUPPORTED)) 1250 break; 1251 mutex_enter(&pshot->lock); 1252 ASSERT(pshot->busy > 0); 1253 --pshot->busy; 1254 if (pshot_debug_busy) { 1255 cmn_err(CE_CONT, "%s%d:" 1256 " ctl_detach_post: idle for %s%d:" 1257 " busy = %d\n", name, instance, 1258 childname, childinstance, 1259 pshot->busy); 1260 } 1261 mutex_exit(&pshot->lock); 1262 rval = pm_idle_component(dip, 0); 1263 ASSERT(rval == DDI_SUCCESS); 1264 1265 /* 1266 * Mark the driver idle if the NO_INVOL_FLAG 1267 * is set. This is needed to make sure the 1268 * parent is idle after the child detaches 1269 * without calling pm_lower_power(). 1270 * Clear the NO_INVOL_FLAG. 1271 * - also mark idle if a tape device has detached 1272 */ 1273 if (!(pshot->state & NO_INVOL_FLAG)) 1274 break; 1275 mutex_enter(&pshot->lock); 1276 ASSERT(pshot->busy > 0); 1277 --pshot->busy; 1278 if (pshot_debug_busy) { 1279 cmn_err(CE_CONT, "%s%d:" 1280 " ctl_detach_post: NO_INVOL:" 1281 " idle for %s%d: busy = %d\n", 1282 name, instance, childname, 1283 childinstance, pshot->busy); 1284 } 1285 pshot->state &= ~NO_INVOL_FLAG; 1286 mutex_exit(&pshot->lock); 1287 rval = pm_idle_component(dip, 0); 1288 ASSERT(rval == DDI_SUCCESS); 1289 1290 break; 1291 } 1292 1293 ndi_devi_exit(dip, circ); 1294 1295 return (rval); 1296 } 1297 1298 case DDI_CTLOPS_BTOP: 1299 case DDI_CTLOPS_BTOPR: 1300 case DDI_CTLOPS_DVMAPAGESIZE: 1301 case DDI_CTLOPS_IOMIN: 1302 case DDI_CTLOPS_PTOB: 1303 default: 1304 /* 1305 * The ops that we pass up (default). We pass up memory 1306 * allocation oriented ops that we receive - these may be 1307 * associated with pseudo HBA drivers below us with target 1308 * drivers below them that use ddi memory allocation 1309 * interfaces like scsi_alloc_consistent_buf. 1310 */ 1311 return (ddi_ctlops(dip, rdip, ctlop, arg, result)); 1312 } 1313 } 1314 1315 /*ARGSUSED0*/ 1316 static int 1317 pshot_power(dev_info_t *dip, int cmpt, int level) 1318 { 1319 pshot_t *pshot; 1320 int instance = ddi_get_instance(dip); 1321 char *name = ddi_node_name(dip); 1322 int circ; 1323 int rv; 1324 1325 pshot = ddi_get_soft_state(pshot_softstatep, instance); 1326 if (pshot == NULL) { 1327 1328 return (DDI_FAILURE); 1329 } 1330 1331 ndi_devi_enter(dip, &circ); 1332 1333 /* 1334 * set POWER_FLAG when power() is called. 1335 * ioctl(DEVCT_PM_POWER) is a clear on read call. 1336 */ 1337 mutex_enter(&pshot->lock); 1338 pshot->state |= POWER_FLAG; 1339 /* 1340 * refuse to power OFF if the component is busy 1341 */ 1342 if (pshot->busy != 0 && pshot->level > level) { 1343 cmn_err(CE_WARN, "%s%d: power: REFUSING POWER LEVEL CHANGE" 1344 " (%d->%d), DEVICE NOT IDLE: busy = %d", 1345 name, instance, pshot->level, level, pshot->busy); 1346 rv = DDI_FAILURE; 1347 } else { 1348 if (pshot_debug) { 1349 cmn_err(CE_CONT, "%s%d: power: comp %d (%d->%d)\n", 1350 name, instance, cmpt, pshot->level, level); 1351 } 1352 pshot->level = level; 1353 rv = DDI_SUCCESS; 1354 } 1355 mutex_exit(&pshot->lock); 1356 1357 ndi_devi_exit(dip, circ); 1358 1359 return (rv); 1360 } 1361 1362 /*ARGSUSED0*/ 1363 static int 1364 pshot_bus_power(dev_info_t *dip, void *impl_arg, pm_bus_power_op_t op, 1365 void *arg, void *result) 1366 1367 { 1368 int ret; 1369 int instance = ddi_get_instance(dip); 1370 char *name = ddi_node_name(dip); 1371 pshot_t *pshot; 1372 pm_bp_child_pwrchg_t *bpc; 1373 pm_bp_nexus_pwrup_t bpn; 1374 pm_bp_has_changed_t *bphc; 1375 int pwrup_res; 1376 int ret_failed = 0; 1377 int pwrup_res_failed = 0; 1378 1379 pshot = ddi_get_soft_state(pshot_softstatep, instance); 1380 if (pshot == NULL) { 1381 1382 return (DDI_FAILURE); 1383 } 1384 1385 switch (op) { 1386 case BUS_POWER_PRE_NOTIFICATION: 1387 bpc = (pm_bp_child_pwrchg_t *)arg; 1388 if (pshot_debug) { 1389 cmn_err(CE_CONT, "%s%d: pre_bus_power:" 1390 " %s%d comp %d (%d->%d)\n", 1391 name, instance, ddi_node_name(bpc->bpc_dip), 1392 ddi_get_instance(bpc->bpc_dip), 1393 bpc->bpc_comp, bpc->bpc_olevel, 1394 bpc->bpc_nlevel); 1395 } 1396 1397 /* 1398 * mark parent busy if old_level is either -1 or 0, 1399 * and new level is == MAXPWR 1400 * - skip if PM_SUPPORTED is not set (pshot@XXX,nopm) 1401 */ 1402 if ((bpc->bpc_comp == 0 && bpc->bpc_nlevel == MAXPWR && 1403 bpc->bpc_olevel <= 0) && (pshot->state & PM_SUPPORTED)) { 1404 mutex_enter(&pshot->lock); 1405 ++(pshot->busy); 1406 if (pshot_debug_busy) { 1407 cmn_err(CE_CONT, 1408 "%s%d: pre_bus_power:" 1409 " busy parent for %s%d (%d->%d): " 1410 " busy = %d\n", 1411 name, instance, 1412 ddi_node_name(bpc->bpc_dip), 1413 ddi_get_instance(bpc->bpc_dip), 1414 bpc->bpc_olevel, bpc->bpc_nlevel, 1415 pshot->busy); 1416 } 1417 mutex_exit(&pshot->lock); 1418 ret = pm_busy_component(dip, 0); 1419 ASSERT(ret == DDI_SUCCESS); 1420 } 1421 1422 /* 1423 * if new_level > 0, power up parent, if not already at 1424 * MAXPWR, via pm_busop_bus_power 1425 * - skip for the no-pm nexus (pshot@XXX,nopm) 1426 */ 1427 if (bpc->bpc_comp == 0 && bpc->bpc_nlevel > 0 && 1428 pshot->level < MAXPWR && (pshot->state & PM_SUPPORTED)) { 1429 /* 1430 * stuff the bpn struct 1431 */ 1432 bpn.bpn_comp = 0; 1433 bpn.bpn_level = MAXPWR; 1434 bpn.bpn_private = bpc->bpc_private; 1435 bpn.bpn_dip = dip; 1436 1437 /* 1438 * ask pm to power parent up 1439 */ 1440 if (pshot_debug) { 1441 cmn_err(CE_CONT, "%s%d: pre_bus_power:" 1442 " pm_busop_bus_power on parent for %s%d" 1443 " (%d->%d): enter", name, instance, 1444 ddi_node_name(bpc->bpc_dip), 1445 ddi_get_instance(bpc->bpc_dip), 1446 bpc->bpc_olevel, bpc->bpc_nlevel); 1447 } 1448 ret = pm_busop_bus_power(dip, impl_arg, 1449 BUS_POWER_NEXUS_PWRUP, (void *)&bpn, 1450 (void *)&pwrup_res); 1451 1452 /* 1453 * check the return status individually, 1454 * idle parent and exit if either failed. 1455 */ 1456 if (ret != DDI_SUCCESS) { 1457 cmn_err(CE_WARN, 1458 "%s%d: pre_bus_power:" 1459 " pm_busop_bus_power FAILED (ret) FOR" 1460 " %s%d (%d->%d)", 1461 name, instance, 1462 ddi_node_name(bpc->bpc_dip), 1463 ddi_get_instance(bpc->bpc_dip), 1464 bpc->bpc_olevel, bpc->bpc_nlevel); 1465 ret_failed = 1; 1466 } 1467 if (pwrup_res != DDI_SUCCESS) { 1468 cmn_err(CE_WARN, 1469 "%s%d: pre_bus_power:" 1470 " pm_busop_bus_power FAILED (pwrup_res)" 1471 " FOR %s%d (%d->%d)", 1472 name, instance, 1473 ddi_node_name(bpc->bpc_dip), 1474 ddi_get_instance(bpc->bpc_dip), 1475 bpc->bpc_olevel, bpc->bpc_nlevel); 1476 pwrup_res_failed = 1; 1477 } 1478 if (ret_failed || pwrup_res_failed) { 1479 /* 1480 * decrement the busy count if it 1481 * had been incremented. 1482 */ 1483 if ((bpc->bpc_comp == 0 && 1484 bpc->bpc_nlevel == MAXPWR && 1485 bpc->bpc_olevel <= 0) && 1486 (pshot->state & PM_SUPPORTED)) { 1487 mutex_enter(&pshot->lock); 1488 ASSERT(pshot->busy > 0); 1489 --(pshot->busy); 1490 if (pshot_debug_busy) { 1491 cmn_err(CE_CONT, "%s%d:" 1492 " pm_busop_bus_power" 1493 " failed: idle parent for" 1494 " %s%d (%d->%d):" 1495 " busy = %d\n", 1496 name, instance, 1497 ddi_node_name( 1498 bpc->bpc_dip), 1499 ddi_get_instance( 1500 bpc->bpc_dip), 1501 bpc->bpc_olevel, 1502 bpc->bpc_nlevel, 1503 pshot->busy); 1504 } 1505 mutex_exit(&pshot->lock); 1506 ret = pm_idle_component(dip, 0); 1507 ASSERT(ret == DDI_SUCCESS); 1508 } 1509 return (DDI_FAILURE); 1510 1511 } else { 1512 if (pshot_debug) { 1513 cmn_err(CE_CONT, 1514 "%s%d: pre_bus_power:" 1515 " pm_busop_bus_power on parent" 1516 " for %s%d (%d->%d)\n", 1517 name, instance, 1518 ddi_node_name(bpc->bpc_dip), 1519 ddi_get_instance(bpc->bpc_dip), 1520 bpc->bpc_olevel, bpc->bpc_nlevel); 1521 } 1522 } 1523 } 1524 break; 1525 1526 case BUS_POWER_POST_NOTIFICATION: 1527 bpc = (pm_bp_child_pwrchg_t *)arg; 1528 if (pshot_debug) { 1529 cmn_err(CE_CONT, "%s%d: post_bus_power:" 1530 " %s%d comp %d (%d->%d) result %d\n", 1531 name, instance, ddi_node_name(bpc->bpc_dip), 1532 ddi_get_instance(bpc->bpc_dip), 1533 bpc->bpc_comp, bpc->bpc_olevel, 1534 bpc->bpc_nlevel, *(int *)result); 1535 } 1536 1537 /* 1538 * handle pm_busop_bus_power() failure case. 1539 * mark parent idle if had been marked busy. 1540 * - skip if PM_SUPPORTED is not set (pshot@XXX,nopm) 1541 */ 1542 if (*(int *)result != DDI_SUCCESS) { 1543 cmn_err(CE_WARN, 1544 "pshot%d: post_bus_power_failed:" 1545 " pm_busop_bus_power FAILED FOR %s%d (%d->%d)", 1546 instance, ddi_node_name(bpc->bpc_dip), 1547 ddi_get_instance(bpc->bpc_dip), 1548 bpc->bpc_olevel, bpc->bpc_nlevel); 1549 1550 if ((bpc->bpc_comp == 0 && bpc->bpc_nlevel == MAXPWR && 1551 bpc->bpc_olevel <= 0) && 1552 (pshot->state & PM_SUPPORTED)) { 1553 mutex_enter(&pshot->lock); 1554 ASSERT(pshot->busy > 0); 1555 --(pshot->busy); 1556 if (pshot_debug_busy) { 1557 cmn_err(CE_CONT, "%s%d:" 1558 " post_bus_power_failed:" 1559 " idle parent for %s%d" 1560 " (%d->%d): busy = %d\n", 1561 name, instance, 1562 ddi_node_name(bpc->bpc_dip), 1563 ddi_get_instance(bpc->bpc_dip), 1564 bpc->bpc_olevel, bpc->bpc_nlevel, 1565 pshot->busy); 1566 } 1567 mutex_exit(&pshot->lock); 1568 ret = pm_idle_component(dip, 0); 1569 ASSERT(ret == DDI_SUCCESS); 1570 } 1571 } 1572 1573 /* 1574 * Mark nexus idle when a child's comp 0 1575 * is set to level 0 from level 1, 2, or 3 only. 1576 * And only if result arg == DDI_SUCCESS. 1577 * This will leave the parent busy when the child 1578 * does not call pm_lower_power() on detach after 1579 * unsetting the NO_LOWER_POWER flag. 1580 * If so, need to notify the parent to mark itself 1581 * idle anyway, else the no-involumtary-power-cycles 1582 * test cases will report false passes! 1583 * - skip if PM_SUPPORTED is not set (pshot@XXX,nopm) 1584 */ 1585 if ((bpc->bpc_comp == 0 && bpc->bpc_nlevel == 0 && 1586 !(bpc->bpc_olevel <= 0) && 1587 *(int *)result == DDI_SUCCESS) && 1588 (pshot->state & PM_SUPPORTED)) { 1589 mutex_enter(&pshot->lock); 1590 ASSERT(pshot->busy > 0); 1591 --(pshot->busy); 1592 if (pshot_debug_busy) { 1593 cmn_err(CE_CONT, 1594 "%s%d: post_bus_power:" 1595 " idle parent for %s%d (%d->%d):" 1596 " busy = %d\n", name, instance, 1597 ddi_node_name(bpc->bpc_dip), 1598 ddi_get_instance(bpc->bpc_dip), 1599 bpc->bpc_olevel, bpc->bpc_nlevel, 1600 pshot->busy); 1601 } 1602 mutex_exit(&pshot->lock); 1603 ret = pm_idle_component(dip, 0); 1604 ASSERT(ret == DDI_SUCCESS); 1605 } 1606 break; 1607 1608 case BUS_POWER_HAS_CHANGED: 1609 bphc = (pm_bp_has_changed_t *)arg; 1610 if (pshot_debug) { 1611 cmn_err(CE_CONT, "%s%d: has_changed_bus_power:" 1612 " %s%d comp %d (%d->%d) result %d\n", 1613 name, instance, ddi_node_name(bphc->bphc_dip), 1614 ddi_get_instance(bphc->bphc_dip), 1615 bphc->bphc_comp, bphc->bphc_olevel, 1616 bphc->bphc_nlevel, *(int *)result); 1617 } 1618 1619 /* 1620 * Mark nexus idle when a child's comp 0 1621 * is set to level 0 from levels 1, 2, or 3 only. 1622 * 1623 * If powering up child leaf/nexus nodes via 1624 * pm_power_has_changed() calls, first issue 1625 * DEVCTL_PM_BUSY_COMP ioctl to mark parent busy 1626 * before powering the parent up, then power up the 1627 * child node. 1628 * - skip if PM_SUPPORTED is not set (pshot@XXX,nopm) 1629 */ 1630 if ((bphc->bphc_comp == 0 && bphc->bphc_nlevel == 0 && 1631 !(bphc->bphc_olevel <= 0)) && 1632 pshot->state & PM_SUPPORTED) { 1633 mutex_enter(&pshot->lock); 1634 ASSERT(pshot->busy > 0); 1635 --(pshot->busy); 1636 if (pshot_debug_busy) { 1637 cmn_err(CE_CONT, 1638 "%s%d: has_changed_bus_power:" 1639 " idle parent for %s%d (%d->%d):" 1640 " busy = %d\n", name, instance, 1641 ddi_node_name(bphc->bphc_dip), 1642 ddi_get_instance(bphc->bphc_dip), 1643 bphc->bphc_olevel, 1644 bphc->bphc_nlevel, pshot->busy); 1645 } 1646 mutex_exit(&pshot->lock); 1647 ret = pm_idle_component(dip, 0); 1648 ASSERT(ret == DDI_SUCCESS); 1649 } 1650 break; 1651 1652 default: 1653 return (pm_busop_bus_power(dip, impl_arg, op, arg, result)); 1654 1655 } 1656 1657 return (DDI_SUCCESS); 1658 } 1659 1660 static int 1661 pshot_initchild(dev_info_t *dip, dev_info_t *child) 1662 { 1663 char name[64]; 1664 char *bus_addr; 1665 char *c_nodename; 1666 int bus_id; 1667 dev_info_t *enum_child; 1668 int enum_base; 1669 int enum_extent; 1670 1671 1672 /* check for bus_enum node */ 1673 1674 #ifdef NOT_USED 1675 if (impl_ddi_merge_child(child) != DDI_SUCCESS) 1676 return (DDI_FAILURE); 1677 #endif 1678 1679 enum_base = ddi_prop_get_int(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, 1680 "busid_ebase", 0); 1681 1682 enum_extent = ddi_prop_get_int(DDI_DEV_T_ANY, child, 1683 DDI_PROP_DONTPASS, "busid_range", 0); 1684 1685 /* 1686 * bus enumeration node 1687 */ 1688 if ((enum_base != 0) && (enum_extent != 0)) { 1689 c_nodename = ddi_node_name(child); 1690 bus_id = enum_base; 1691 for (; bus_id < enum_extent; bus_id++) { 1692 if (ndi_devi_alloc(dip, c_nodename, DEVI_PSEUDO_NODEID, 1693 &enum_child) != NDI_SUCCESS) 1694 return (DDI_FAILURE); 1695 1696 (void) sprintf(name, "%d", bus_id); 1697 if (ndi_prop_update_string(DDI_DEV_T_NONE, enum_child, 1698 "bus-addr", name) != DDI_PROP_SUCCESS) { 1699 (void) ndi_devi_free(enum_child); 1700 return (DDI_FAILURE); 1701 } 1702 1703 if (ndi_devi_online(enum_child, 0) != 1704 DDI_SUCCESS) { 1705 (void) ndi_devi_free(enum_child); 1706 return (DDI_FAILURE); 1707 } 1708 } 1709 /* 1710 * fail the enumeration node itself 1711 */ 1712 return (DDI_FAILURE); 1713 } 1714 1715 if (ddi_prop_lookup_string(DDI_DEV_T_ANY, child, 0, "bus-addr", 1716 &bus_addr) != DDI_PROP_SUCCESS) { 1717 cmn_err(CE_WARN, "pshot_initchild: bus-addr not defined (%s)", 1718 ddi_node_name(child)); 1719 return (DDI_NOT_WELL_FORMED); 1720 } 1721 1722 if (strlen(bus_addr) == 0) { 1723 cmn_err(CE_WARN, "pshot_initchild: NULL bus-addr (%s)", 1724 ddi_node_name(child)); 1725 ddi_prop_free(bus_addr); 1726 return (DDI_FAILURE); 1727 } 1728 1729 if (strncmp(bus_addr, "failinit", 8) == 0) { 1730 if (pshot_debug) 1731 cmn_err(CE_CONT, 1732 "pshot%d: %s forced INITCHILD failure\n", 1733 ddi_get_instance(dip), bus_addr); 1734 ddi_prop_free(bus_addr); 1735 return (DDI_FAILURE); 1736 } 1737 1738 if (pshot_log) { 1739 cmn_err(CE_CONT, "initchild %s%d/%s@%s\n", 1740 ddi_get_name(dip), ddi_get_instance(dip), 1741 ddi_node_name(child), bus_addr); 1742 } 1743 1744 ddi_set_name_addr(child, bus_addr); 1745 ddi_prop_free(bus_addr); 1746 return (DDI_SUCCESS); 1747 } 1748 1749 /*ARGSUSED*/ 1750 static int 1751 pshot_uninitchild(dev_info_t *dip, dev_info_t *child) 1752 { 1753 ddi_set_name_addr(child, NULL); 1754 return (DDI_SUCCESS); 1755 } 1756 1757 1758 /* 1759 * devctl IOCTL support 1760 */ 1761 /* ARGSUSED */ 1762 static int 1763 pshot_open(dev_t *devp, int flags, int otyp, cred_t *credp) 1764 { 1765 int instance; 1766 pshot_t *pshot; 1767 1768 if (otyp != OTYP_CHR) 1769 return (EINVAL); 1770 1771 instance = pshot_minor_decode_inst(getminor(*devp)); 1772 if ((pshot = ddi_get_soft_state(pshot_softstatep, instance)) == NULL) 1773 return (ENXIO); 1774 1775 /* 1776 * Access is currently determined on a per-instance basis. 1777 * If we want per-node, then need to add state and lock members to 1778 * pshot_minor_t 1779 */ 1780 mutex_enter(&pshot->lock); 1781 if (((flags & FEXCL) && (pshot->state & IS_OPEN)) || 1782 (!(flags & FEXCL) && (pshot->state & IS_OPEN_EXCL))) { 1783 mutex_exit(&pshot->lock); 1784 return (EBUSY); 1785 } 1786 pshot->state |= IS_OPEN; 1787 if (flags & FEXCL) 1788 pshot->state |= IS_OPEN_EXCL; 1789 1790 if (pshot_debug) 1791 cmn_err(CE_CONT, "pshot%d open\n", instance); 1792 1793 mutex_exit(&pshot->lock); 1794 return (0); 1795 } 1796 1797 /* 1798 * pshot_close 1799 */ 1800 /* ARGSUSED */ 1801 static int 1802 pshot_close(dev_t dev, int flag, int otyp, cred_t *credp) 1803 { 1804 int instance; 1805 pshot_t *pshot; 1806 1807 if (otyp != OTYP_CHR) 1808 return (EINVAL); 1809 1810 instance = pshot_minor_decode_inst(getminor(dev)); 1811 if ((pshot = ddi_get_soft_state(pshot_softstatep, instance)) == NULL) 1812 return (ENXIO); 1813 1814 mutex_enter(&pshot->lock); 1815 pshot->state &= ~(IS_OPEN | IS_OPEN_EXCL); 1816 mutex_exit(&pshot->lock); 1817 if (pshot_debug) 1818 cmn_err(CE_CONT, "pshot%d closed\n", instance); 1819 return (0); 1820 } 1821 1822 1823 /* 1824 * pshot_ioctl: redirects to appropriate command handler based on various 1825 * criteria 1826 */ 1827 /* ARGSUSED */ 1828 static int 1829 pshot_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, 1830 int *rvalp) 1831 { 1832 pshot_t *pshot; 1833 int instance; 1834 minor_t nodenum; 1835 char *nodename; 1836 1837 instance = pshot_minor_decode_inst(getminor(dev)); 1838 if ((pshot = ddi_get_soft_state(pshot_softstatep, instance)) == NULL) 1839 return (ENXIO); 1840 1841 nodenum = pshot_minor_decode_nodenum(getminor(dev)); 1842 nodename = pshot->nodes[nodenum].name; 1843 1844 if (pshot_debug) 1845 cmn_err(CE_CONT, 1846 "pshot%d ioctl: dev=%p, cmd=%x, arg=%p, mode=%x\n", 1847 instance, (void *)dev, cmd, (void *)arg, mode); 1848 1849 if (strcmp(nodename, PSHOT_NODENAME_DEVCTL) == 0) 1850 return (pshot_devctl(pshot, nodenum, cmd, arg, mode, credp, 1851 rvalp)); 1852 1853 if (strcmp(nodename, PSHOT_NODENAME_TESTCTL) == 0) 1854 return (pshot_testctl(pshot, nodenum, cmd, arg, mode, credp, 1855 rvalp)); 1856 1857 cmn_err(CE_WARN, "pshot_ioctl: unmatched nodename on minor %u", 1858 pshot->nodes[nodenum].minor); 1859 return (ENXIO); 1860 } 1861 1862 1863 /* 1864 * pshot_devctl: handle DEVCTL operations 1865 */ 1866 /* ARGSUSED */ 1867 static int 1868 pshot_devctl(pshot_t *pshot, minor_t nodenum, int cmd, intptr_t arg, int mode, 1869 cred_t *credp, int *rvalp) 1870 { 1871 dev_info_t *self; 1872 dev_info_t *child = NULL; 1873 struct devctl_iocdata *dcp; 1874 uint_t state; 1875 int rv = 0; 1876 uint_t flags; 1877 int instance; 1878 int i; 1879 int ret; 1880 1881 self = pshot->dip; 1882 1883 flags = (pshot_devctl_debug) ? NDI_DEVI_DEBUG : 0; 1884 instance = pshot->instance; 1885 1886 /* 1887 * We can use the generic implementation for these ioctls 1888 */ 1889 for (i = 0; pshot_devctls[i].ioctl_int != 0; i++) { 1890 if (pshot_devctls[i].ioctl_int == cmd) { 1891 if (pshot_debug) 1892 cmn_err(CE_CONT, "pshot%d devctl: %s", 1893 instance, pshot_devctls[i].ioctl_char); 1894 } 1895 } 1896 switch (cmd) { 1897 case DEVCTL_DEVICE_GETSTATE: 1898 case DEVCTL_DEVICE_ONLINE: 1899 case DEVCTL_DEVICE_OFFLINE: 1900 case DEVCTL_DEVICE_REMOVE: 1901 case DEVCTL_BUS_GETSTATE: 1902 case DEVCTL_BUS_DEV_CREATE: 1903 rv = ndi_devctl_ioctl(self, cmd, arg, mode, flags); 1904 if (pshot_debug && rv != 0) { 1905 cmn_err(CE_CONT, "pshot%d ndi_devctl_ioctl:" 1906 " failed, rv = %d", instance, rv); 1907 } 1908 1909 return (rv); 1910 } 1911 1912 /* 1913 * read devctl ioctl data 1914 */ 1915 if (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS) 1916 return (EFAULT); 1917 1918 switch (cmd) { 1919 1920 case DEVCTL_DEVICE_RESET: 1921 if (pshot_debug) 1922 cmn_err(CE_CONT, "pshot%d devctl:" 1923 " DEVCTL_DEVICE_RESET\n", instance); 1924 rv = pshot_event(pshot, PSHOT_EVENT_TAG_DEV_RESET, 1925 child, (void *)self); 1926 ASSERT(rv == NDI_SUCCESS); 1927 break; 1928 1929 case DEVCTL_BUS_QUIESCE: 1930 if (pshot_debug) 1931 cmn_err(CE_CONT, "pshot%d devctl:" 1932 " DEVCTL_BUS_QUIESCE\n", instance); 1933 if (ndi_get_bus_state(self, &state) == NDI_SUCCESS) { 1934 if (state == BUS_QUIESCED) { 1935 break; 1936 } 1937 (void) ndi_set_bus_state(self, BUS_QUIESCED); 1938 } 1939 rv = pshot_event(pshot, PSHOT_EVENT_TAG_BUS_QUIESCE, 1940 child, (void *)self); 1941 ASSERT(rv == NDI_SUCCESS); 1942 1943 break; 1944 1945 case DEVCTL_BUS_UNQUIESCE: 1946 if (pshot_debug) 1947 cmn_err(CE_CONT, "pshot%d devctl:" 1948 " DEVCTL_BUS_UNQUIESCE\n", instance); 1949 if (ndi_get_bus_state(self, &state) == NDI_SUCCESS) { 1950 if (state == BUS_ACTIVE) { 1951 break; 1952 } 1953 } 1954 1955 /* 1956 * quiesce the bus through bus-specific means 1957 */ 1958 (void) ndi_set_bus_state(self, BUS_ACTIVE); 1959 rv = pshot_event(pshot, PSHOT_EVENT_TAG_BUS_UNQUIESCE, 1960 child, (void *)self); 1961 ASSERT(rv == NDI_SUCCESS); 1962 break; 1963 1964 case DEVCTL_BUS_RESET: 1965 case DEVCTL_BUS_RESETALL: 1966 /* 1967 * no reset support for the pseudo bus 1968 * but if there were.... 1969 */ 1970 rv = pshot_event(pshot, PSHOT_EVENT_TAG_BUS_RESET, 1971 child, (void *)self); 1972 ASSERT(rv == NDI_SUCCESS); 1973 break; 1974 1975 /* 1976 * PM related ioctls 1977 */ 1978 case DEVCTL_PM_BUSY_COMP: 1979 /* 1980 * mark component 0 busy. 1981 * Keep track of ioctl updates to the busy count 1982 * via pshot->busy_ioctl. 1983 */ 1984 if (pshot_debug) { 1985 cmn_err(CE_CONT, "pshot%d devctl:" 1986 " DEVCTL_PM_BUSY_COMP\n", instance); 1987 } 1988 mutex_enter(&pshot->lock); 1989 ++(pshot->busy); 1990 ++(pshot->busy_ioctl); 1991 if (pshot_debug_busy) { 1992 cmn_err(CE_CONT, "pshot%d:" 1993 " DEVCTL_PM_BUSY_COMP comp 0 busy" 1994 " %d busy_ioctl %d\n", instance, pshot->busy, 1995 pshot->busy_ioctl); 1996 } 1997 mutex_exit(&pshot->lock); 1998 ret = pm_busy_component(pshot->dip, 0); 1999 ASSERT(ret == DDI_SUCCESS); 2000 2001 break; 2002 2003 case DEVCTL_PM_BUSY_COMP_TEST: 2004 /* 2005 * test bus's busy state 2006 */ 2007 if (pshot_debug) { 2008 cmn_err(CE_CONT, "pshot%d devctl:" 2009 " DEVCTL_PM_BUSY_COMP_TEST\n", instance); 2010 } 2011 mutex_enter(&pshot->lock); 2012 state = pshot->busy; 2013 if (copyout(&state, dcp->cpyout_buf, 2014 sizeof (uint_t)) != 0) { 2015 cmn_err(CE_WARN, "pshot%d devctl:" 2016 " DEVCTL_PM_BUSY_COMP_TEST: copyout failed", 2017 instance); 2018 rv = EINVAL; 2019 } 2020 if (pshot_debug_busy) { 2021 cmn_err(CE_CONT, "pshot%d: DEVCTL_PM_BUSY_COMP_TEST:" 2022 " comp 0 busy %d busy_ioctl %d\n", instance, 2023 state, pshot->busy_ioctl); 2024 } 2025 mutex_exit(&pshot->lock); 2026 break; 2027 2028 case DEVCTL_PM_IDLE_COMP: 2029 /* 2030 * mark component 0 idle. 2031 * NOP if pshot->busy_ioctl <= 0. 2032 */ 2033 if (pshot_debug) { 2034 cmn_err(CE_CONT, "pshot%d devctl:" 2035 " DEVCTL_PM_IDLE_COMP\n", instance); 2036 } 2037 mutex_enter(&pshot->lock); 2038 if (pshot->busy_ioctl > 0) { 2039 ASSERT(pshot->busy > 0); 2040 --(pshot->busy); 2041 --(pshot->busy_ioctl); 2042 if (pshot_debug_busy) { 2043 cmn_err(CE_CONT, "pshot%d:" 2044 " DEVCTL_PM_IDLE_COM: comp 0" 2045 " busy %d busy_ioctl %d\n", instance, 2046 pshot->busy, pshot->busy_ioctl); 2047 } 2048 mutex_exit(&pshot->lock); 2049 ret = pm_idle_component(pshot->dip, 0); 2050 ASSERT(ret == DDI_SUCCESS); 2051 2052 } else { 2053 mutex_exit(&pshot->lock); 2054 } 2055 break; 2056 2057 case DEVCTL_PM_RAISE_PWR: 2058 /* 2059 * raise component 0 to full power level MAXPWR via a 2060 * pm_raise_power() call 2061 */ 2062 if (pshot_debug) { 2063 cmn_err(CE_CONT, "pshot%d devctl:" 2064 " DEVCTL_PM_RAISE_PWR\n", instance); 2065 } 2066 if (pm_raise_power(pshot->dip, 0, MAXPWR) != DDI_SUCCESS) { 2067 rv = EINVAL; 2068 } else { 2069 mutex_enter(&pshot->lock); 2070 if (pshot_debug) { 2071 cmn_err(CE_CONT, "pshot%d:" 2072 " DEVCTL_PM_RAISE_POWER: comp 0" 2073 " to level %d\n", instance, pshot->level); 2074 } 2075 mutex_exit(&pshot->lock); 2076 } 2077 break; 2078 2079 case DEVCTL_PM_LOWER_PWR: 2080 /* 2081 * pm_lower_power() call for negative testing 2082 * expected to fail. 2083 */ 2084 if (pshot_debug) { 2085 cmn_err(CE_CONT, "pshot%d devctl:" 2086 " DEVCTL_PM_LOWER_PWR\n", instance); 2087 } 2088 if (pm_lower_power(pshot->dip, 0, 0) != DDI_SUCCESS) { 2089 rv = EINVAL; 2090 } else { 2091 mutex_enter(&pshot->lock); 2092 if (pshot_debug) { 2093 cmn_err(CE_CONT, "pshot%d:" 2094 " DEVCTL_PM_LOWER_POWER comp 0" 2095 " to level %d\n", instance, pshot->level); 2096 } 2097 mutex_exit(&pshot->lock); 2098 } 2099 break; 2100 2101 case DEVCTL_PM_CHANGE_PWR_LOW: 2102 /* 2103 * inform the PM framework that component 0 has changed 2104 * power level to 0 via a pm_power_has_changed() call 2105 */ 2106 if (pshot_debug) { 2107 cmn_err(CE_CONT, "pshot%d devctl:" 2108 " DEVCTL_PM_CHANGE_PWR_LOW\n", instance); 2109 } 2110 mutex_enter(&pshot->lock); 2111 pshot->level = 0; 2112 if (pm_power_has_changed(pshot->dip, 0, 0) != DDI_SUCCESS) { 2113 rv = EINVAL; 2114 } else { 2115 if (pshot_debug) { 2116 cmn_err(CE_CONT, "pshot%d:" 2117 " DEVCTL_PM_CHANGE_PWR_LOW comp 0 to" 2118 " level %d\n", instance, pshot->level); 2119 } 2120 } 2121 mutex_exit(&pshot->lock); 2122 break; 2123 2124 case DEVCTL_PM_CHANGE_PWR_HIGH: 2125 /* 2126 * inform the PM framework that component 0 has changed 2127 * power level to MAXPWR via a pm_power_has_changed() call 2128 */ 2129 if (pshot_debug) { 2130 cmn_err(CE_CONT, "pshot%d devctl:" 2131 " DEVCTL_PM_CHANGE_PWR_HIGH\n", instance); 2132 } 2133 mutex_enter(&pshot->lock); 2134 pshot->level = MAXPWR; 2135 if (pm_power_has_changed(pshot->dip, 0, MAXPWR) 2136 != DDI_SUCCESS) { 2137 rv = EINVAL; 2138 } else { 2139 if (pshot_debug) { 2140 cmn_err(CE_CONT, "pshot%d:" 2141 " DEVCTL_PM_CHANGE_PWR_HIGH comp 0 to" 2142 " level %d\n", instance, pshot->level); 2143 } 2144 } 2145 mutex_exit(&pshot->lock); 2146 break; 2147 2148 case DEVCTL_PM_POWER: 2149 /* 2150 * test if the pshot_power() routine has been called, 2151 * then clear 2152 */ 2153 if (pshot_debug) { 2154 cmn_err(CE_CONT, "pshot%d devctl:" 2155 " DEVCTL_PM_POWER\n", instance); 2156 } 2157 mutex_enter(&pshot->lock); 2158 state = (pshot->state & POWER_FLAG) ? 1 : 0; 2159 if (copyout(&state, dcp->cpyout_buf, 2160 sizeof (uint_t)) != 0) { 2161 cmn_err(CE_WARN, "pshot%d devctl:" 2162 " DEVCTL_PM_POWER: copyout failed", 2163 instance); 2164 rv = EINVAL; 2165 } 2166 if (pshot_debug) { 2167 cmn_err(CE_CONT, "pshot%d: DEVCTL_PM_POWER:" 2168 " POWER_FLAG = %d\n", instance, state); 2169 } 2170 pshot->state &= ~POWER_FLAG; 2171 mutex_exit(&pshot->lock); 2172 break; 2173 2174 case DEVCTL_PM_FAIL_SUSPEND: 2175 /* 2176 * fail DDI_SUSPEND 2177 */ 2178 if (pshot_debug) { 2179 cmn_err(CE_CONT, "pshot%d devctl:" 2180 " DEVCTL_PM_FAIL_SUSPEND\n", instance); 2181 } 2182 mutex_enter(&pshot->lock); 2183 pshot->state |= FAIL_SUSPEND_FLAG; 2184 mutex_exit(&pshot->lock); 2185 if (pshot_debug) { 2186 cmn_err(CE_CONT, "pshot%d: DEVCTL_PM_FAIL_SUSPEND\n", 2187 instance); 2188 } 2189 break; 2190 2191 case DEVCTL_PM_BUS_STRICT_TEST: 2192 /* 2193 * test the STRICT_PARENT flag: 2194 * set => STRICT PARENT 2195 * not set => INVOLVED PARENT 2196 */ 2197 mutex_enter(&pshot->lock); 2198 state = (pshot->state & STRICT_PARENT) ? 1 : 0; 2199 if (copyout(&state, dcp->cpyout_buf, 2200 sizeof (uint_t)) != 0) { 2201 cmn_err(CE_WARN, "pshot%d devctl:" 2202 " DEVCTL_PM_BUS_STRICT_TEST: copyout failed", 2203 instance); 2204 rv = EINVAL; 2205 } 2206 if (pshot_debug) { 2207 cmn_err(CE_CONT, "pshot%d devctl:" 2208 " DEVCTL_PM_BUS_STRICT_TEST: type = %s\n", 2209 instance, ((state == 0) ? "INVOLVED" : "STRICT")); 2210 } 2211 mutex_exit(&pshot->lock); 2212 break; 2213 2214 case DEVCTL_PM_BUS_NO_INVOL: 2215 /* 2216 * Set the NO_INVOL_FLAG flag to 2217 * notify the driver that the child will not 2218 * call pm_lower_power() on detach. 2219 * The driver needs to mark itself idle twice 2220 * during DDI_CTLOPS_DETACH (post). 2221 */ 2222 if (pshot_debug) { 2223 cmn_err(CE_CONT, "pshot%d devctl:" 2224 " DEVCTL_PM_BUS_NO_INVOL\n", instance); 2225 } 2226 mutex_enter(&pshot->lock); 2227 pshot->state |= NO_INVOL_FLAG; 2228 mutex_exit(&pshot->lock); 2229 break; 2230 2231 default: 2232 rv = ENOTTY; 2233 } 2234 2235 ndi_dc_freehdl(dcp); 2236 return (rv); 2237 } 2238 2239 2240 /* 2241 * pshot_testctl: handle other test operations 2242 * - If <cmd> is a DEVCTL cmd, then <arg> is a dev_t indicating which 2243 * child to direct the DEVCTL to, if applicable; 2244 * furthermore, any cmd here can be sent by layered ioctls (unlike 2245 * those to pshot_devctl() which must come from userland) 2246 */ 2247 /* ARGSUSED */ 2248 static int 2249 pshot_testctl(pshot_t *pshot, minor_t nodenum, int cmd, intptr_t arg, int mode, 2250 cred_t *credp, int *rvalp) 2251 { 2252 dev_info_t *self; 2253 dev_info_t *child = NULL; 2254 uint_t state; 2255 int rv = 0; 2256 int instance; 2257 int i; 2258 2259 /* uint_t flags; */ 2260 2261 /* flags = (pshot_devctl_debug) ? NDI_DEVI_DEBUG : 0; */ 2262 self = pshot->dip; 2263 instance = pshot->instance; 2264 2265 if (cmd & DEVCTL_IOC) { 2266 child = e_ddi_hold_devi_by_dev((dev_t)arg, 0); 2267 } 2268 2269 for (i = 0; pshot_devctls[i].ioctl_int != 0; i++) { 2270 if (pshot_devctls[i].ioctl_int == cmd) { 2271 if (pshot_debug) 2272 cmn_err(CE_CONT, "pshot%d devctl: %s", 2273 instance, pshot_devctls[i].ioctl_char); 2274 } 2275 } 2276 switch (cmd) { 2277 case DEVCTL_DEVICE_RESET: 2278 if (pshot_debug) 2279 cmn_err(CE_CONT, "pshot%d testctl:" 2280 " DEVCTL_PM_POWER\n", instance); 2281 rv = pshot_event(pshot, PSHOT_EVENT_TAG_DEV_RESET, 2282 child, (void *)self); 2283 ASSERT(rv == NDI_SUCCESS); 2284 break; 2285 2286 case DEVCTL_BUS_QUIESCE: 2287 if (pshot_debug) 2288 cmn_err(CE_CONT, "pshot%d testctl:" 2289 " DEVCTL_PM_POWER\n", instance); 2290 if (ndi_get_bus_state(self, &state) == NDI_SUCCESS) { 2291 if (state == BUS_QUIESCED) { 2292 break; 2293 } 2294 (void) ndi_set_bus_state(self, BUS_QUIESCED); 2295 } 2296 rv = pshot_event(pshot, PSHOT_EVENT_TAG_BUS_QUIESCE, 2297 child, (void *)self); 2298 ASSERT(rv == NDI_SUCCESS); 2299 2300 break; 2301 2302 case DEVCTL_BUS_UNQUIESCE: 2303 if (pshot_debug) 2304 cmn_err(CE_CONT, "pshot%d testctl:" 2305 " DEVCTL_PM_POWER\n", instance); 2306 if (ndi_get_bus_state(self, &state) == NDI_SUCCESS) { 2307 if (state == BUS_ACTIVE) { 2308 break; 2309 } 2310 } 2311 2312 /* 2313 * quiesce the bus through bus-specific means 2314 */ 2315 (void) ndi_set_bus_state(self, BUS_ACTIVE); 2316 rv = pshot_event(pshot, PSHOT_EVENT_TAG_BUS_UNQUIESCE, 2317 child, (void *)self); 2318 ASSERT(rv == NDI_SUCCESS); 2319 break; 2320 2321 case DEVCTL_BUS_RESET: 2322 case DEVCTL_BUS_RESETALL: 2323 /* 2324 * no reset support for the pseudo bus 2325 * but if there were.... 2326 */ 2327 rv = pshot_event(pshot, PSHOT_EVENT_TAG_BUS_RESET, 2328 child, (void *)self); 2329 ASSERT(rv == NDI_SUCCESS); 2330 break; 2331 2332 default: 2333 rv = ENOTTY; 2334 } 2335 2336 if (child != NULL) 2337 ddi_release_devi(child); 2338 return (rv); 2339 } 2340 2341 2342 static int 2343 pshot_get_eventcookie(dev_info_t *dip, dev_info_t *rdip, 2344 char *eventname, ddi_eventcookie_t *event_cookiep) 2345 { 2346 int instance = ddi_get_instance(dip); 2347 pshot_t *pshot = ddi_get_soft_state(pshot_softstatep, instance); 2348 2349 if (pshot_debug) 2350 cmn_err(CE_CONT, "pshot%d: " 2351 "pshot_get_eventcookie:\n\t" 2352 "dip = 0x%p rdip = 0x%p (%s/%d) eventname = %s\n", 2353 instance, (void *)dip, (void *)rdip, 2354 ddi_node_name(rdip), ddi_get_instance(rdip), 2355 eventname); 2356 2357 2358 return (ndi_event_retrieve_cookie(pshot->ndi_event_hdl, 2359 rdip, eventname, event_cookiep, NDI_EVENT_NOPASS)); 2360 } 2361 2362 static int 2363 pshot_add_eventcall(dev_info_t *dip, dev_info_t *rdip, 2364 ddi_eventcookie_t cookie, 2365 void (*callback)(), void *arg, ddi_callback_id_t *cb_id) 2366 { 2367 int instance = ddi_get_instance(dip); 2368 pshot_t *pshot = ddi_get_soft_state(pshot_softstatep, instance); 2369 2370 if (pshot_debug) 2371 cmn_err(CE_CONT, "pshot%d: " 2372 "pshot_add_eventcall:\n\t" 2373 "dip = 0x%p rdip = 0x%p (%s%d)\n\tcookie = 0x%p (%s)\n\t" 2374 "cb = 0x%p, arg = 0x%p\n", 2375 instance, (void *)dip, (void *)rdip, 2376 ddi_node_name(rdip), ddi_get_instance(rdip), (void *)cookie, 2377 NDI_EVENT_NAME(cookie), (void *)callback, arg); 2378 2379 /* add callback to our event handle */ 2380 return (ndi_event_add_callback(pshot->ndi_event_hdl, rdip, 2381 cookie, callback, arg, NDI_SLEEP, cb_id)); 2382 } 2383 2384 static int 2385 pshot_remove_eventcall(dev_info_t *dip, ddi_callback_id_t cb_id) 2386 { 2387 2388 ndi_event_callbacks_t *cb = (ndi_event_callbacks_t *)cb_id; 2389 2390 int instance = ddi_get_instance(dip); 2391 pshot_t *pshot = ddi_get_soft_state(pshot_softstatep, instance); 2392 2393 ASSERT(cb); 2394 2395 if (pshot_debug) 2396 cmn_err(CE_CONT, "pshot%d: " 2397 "pshot_remove_eventcall:\n\t" 2398 "dip = 0x%p rdip = 0x%p (%s%d)\n\tcookie = 0x%p (%s)\n", 2399 instance, (void *)dip, (void *)cb->ndi_evtcb_dip, 2400 ddi_node_name(cb->ndi_evtcb_dip), 2401 ddi_get_instance(cb->ndi_evtcb_dip), 2402 (void *)cb->ndi_evtcb_cookie, 2403 NDI_EVENT_NAME(cb->ndi_evtcb_cookie)); 2404 2405 return (ndi_event_remove_callback(pshot->ndi_event_hdl, cb_id)); 2406 } 2407 2408 static int 2409 pshot_post_event(dev_info_t *dip, dev_info_t *rdip, 2410 ddi_eventcookie_t cookie, void *impl_data) 2411 { 2412 int instance = ddi_get_instance(dip); 2413 pshot_t *pshot = ddi_get_soft_state(pshot_softstatep, instance); 2414 2415 if (pshot_debug) { 2416 if (rdip) { 2417 cmn_err(CE_CONT, "pshot%d: " 2418 "pshot_post_event:\n\t" 2419 "dip = 0x%p rdip = 0x%p (%s%d\n\t" 2420 "cookie = 0x%p (%s)\n\tbus_impl = 0x%p\n", 2421 instance, (void *)dip, (void *)rdip, 2422 ddi_node_name(rdip), ddi_get_instance(rdip), 2423 (void *)cookie, 2424 NDI_EVENT_NAME(cookie), impl_data); 2425 } else { 2426 cmn_err(CE_CONT, "pshot%d: " 2427 "pshot_post_event:\n\t" 2428 "dip = 0x%p cookie = 0x%p (%s) bus_impl = 0x%p\n", 2429 instance, (void *)dip, (void *)cookie, 2430 NDI_EVENT_NAME(cookie), impl_data); 2431 } 2432 } 2433 2434 /* run callbacks for this event */ 2435 return (ndi_event_run_callbacks(pshot->ndi_event_hdl, rdip, 2436 cookie, impl_data)); 2437 } 2438 2439 /* 2440 * the nexus driver will generate events 2441 * that need to go to children 2442 */ 2443 static int 2444 pshot_event(pshot_t *pshot, int event_tag, dev_info_t *child, 2445 void *bus_impldata) 2446 { 2447 ddi_eventcookie_t cookie = ndi_event_tag_to_cookie( 2448 pshot->ndi_event_hdl, event_tag); 2449 2450 if (pshot_debug) { 2451 if (child) { 2452 cmn_err(CE_CONT, "pshot%d: " 2453 "pshot_event: event_tag = 0x%x (%s)\n\t" 2454 "child = 0x%p (%s%d) bus_impl = 0x%p (%s%d)\n", 2455 pshot->instance, event_tag, 2456 ndi_event_tag_to_name(pshot->ndi_event_hdl, 2457 event_tag), 2458 (void *)child, ddi_node_name(child), 2459 ddi_get_instance(child), bus_impldata, 2460 ddi_node_name((dev_info_t *)bus_impldata), 2461 ddi_get_instance((dev_info_t *)bus_impldata)); 2462 } else { 2463 cmn_err(CE_CONT, "pshot%d: " 2464 "pshot_event: event_tag = 0x%x (%s)\n\t" 2465 "child = NULL, bus_impl = 0x%p (%s%d)\n", 2466 pshot->instance, event_tag, 2467 ndi_event_tag_to_name(pshot->ndi_event_hdl, 2468 event_tag), 2469 bus_impldata, 2470 ddi_node_name((dev_info_t *)bus_impldata), 2471 ddi_get_instance((dev_info_t *)bus_impldata)); 2472 } 2473 } 2474 2475 return (ndi_event_run_callbacks(pshot->ndi_event_hdl, 2476 child, cookie, bus_impldata)); 2477 } 2478 2479 2480 /* 2481 * the pshot driver event notification callback 2482 */ 2483 static void 2484 pshot_event_cb(dev_info_t *dip, ddi_eventcookie_t cookie, 2485 void *arg, void *bus_impldata) 2486 { 2487 pshot_t *pshot = (pshot_t *)arg; 2488 int event_tag; 2489 2490 /* look up the event */ 2491 event_tag = NDI_EVENT_TAG(cookie); 2492 2493 if (pshot_debug) { 2494 cmn_err(CE_CONT, "pshot%d: " 2495 "pshot_event_cb:\n\t" 2496 "dip = 0x%p cookie = 0x%p (%s), tag = 0x%x\n\t" 2497 "arg = 0x%p bus_impl = 0x%p (%s%d)\n", 2498 pshot->instance, (void *)dip, (void *)cookie, 2499 NDI_EVENT_NAME(cookie), event_tag, arg, bus_impldata, 2500 ddi_node_name((dev_info_t *)bus_impldata), 2501 ddi_get_instance((dev_info_t *)bus_impldata)); 2502 } 2503 2504 switch (event_tag) { 2505 case PSHOT_EVENT_TAG_OFFLINE: 2506 case PSHOT_EVENT_TAG_BUS_RESET: 2507 case PSHOT_EVENT_TAG_BUS_QUIESCE: 2508 case PSHOT_EVENT_TAG_BUS_UNQUIESCE: 2509 /* notify all subscribers of the this event */ 2510 (void) ndi_event_run_callbacks(pshot->ndi_event_hdl, 2511 NULL, cookie, bus_impldata); 2512 if (pshot_debug) { 2513 cmn_err(CE_CONT, "pshot%d: event=%s\n\t" 2514 "pshot_event_cb\n", pshot->instance, 2515 NDI_EVENT_NAME(cookie)); 2516 } 2517 /*FALLTHRU*/ 2518 case PSHOT_EVENT_TAG_TEST_POST: 2519 case PSHOT_EVENT_TAG_DEV_RESET: 2520 default: 2521 return; 2522 } 2523 } 2524 2525 static int 2526 pshot_bus_config(dev_info_t *parent, uint_t flags, 2527 ddi_bus_config_op_t op, void *arg, dev_info_t **childp) 2528 { 2529 int rval; 2530 char *devname; 2531 char *devstr, *cname, *caddr; 2532 int devstrlen; 2533 int circ; 2534 pshot_t *pshot; 2535 int instance = ddi_get_instance(parent); 2536 2537 if (pshot_debug) { 2538 flags |= NDI_DEVI_DEBUG; 2539 cmn_err(CE_CONT, 2540 "pshot%d: bus_config %s flags=0x%x\n", 2541 ddi_get_instance(parent), 2542 (op == BUS_CONFIG_ONE) ? (char *)arg : "", flags); 2543 } 2544 2545 pshot = ddi_get_soft_state(pshot_softstatep, instance); 2546 if (pshot == NULL) { 2547 2548 return (NDI_FAILURE); 2549 } 2550 2551 /* 2552 * Hold the nexus across the bus_config 2553 */ 2554 ndi_devi_enter(parent, &circ); 2555 2556 switch (op) { 2557 case BUS_CONFIG_ONE: 2558 2559 /* 2560 * lookup and hold child device, create if not found 2561 */ 2562 devname = (char *)arg; 2563 devstrlen = strlen(devname) + 1; 2564 devstr = i_ddi_strdup(devname, KM_SLEEP); 2565 i_ddi_parse_name(devstr, &cname, &caddr, NULL); 2566 2567 /* 2568 * The framework ensures that the node has 2569 * a name but each nexus is responsible for 2570 * the bus address name space. This driver 2571 * requires that a bus address be specified, 2572 * as will most nexus drivers. 2573 */ 2574 ASSERT(cname && strlen(cname) > 0); 2575 if (caddr == NULL || strlen(caddr) == 0) { 2576 cmn_err(CE_WARN, 2577 "pshot%d: malformed name %s (no bus address)", 2578 ddi_get_instance(parent), devname); 2579 kmem_free(devstr, devstrlen); 2580 ndi_devi_exit(parent, circ); 2581 return (NDI_FAILURE); 2582 } 2583 2584 /* 2585 * Handle a few special cases for testing purposes 2586 */ 2587 rval = pshot_bus_config_test_specials(parent, 2588 devname, cname, caddr); 2589 2590 if (rval == NDI_SUCCESS) { 2591 /* 2592 * Set up either a leaf or nexus device 2593 */ 2594 if (strcmp(cname, "pshot") == 0) { 2595 rval = pshot_bus_config_setup_nexus(parent, 2596 cname, caddr); 2597 } else { 2598 rval = pshot_bus_config_setup_leaf(parent, 2599 cname, caddr); 2600 } 2601 } 2602 2603 kmem_free(devstr, devstrlen); 2604 break; 2605 2606 case BUS_CONFIG_DRIVER: 2607 case BUS_CONFIG_ALL: 2608 rval = NDI_SUCCESS; 2609 break; 2610 2611 default: 2612 rval = NDI_FAILURE; 2613 break; 2614 } 2615 2616 if (rval == NDI_SUCCESS) 2617 rval = ndi_busop_bus_config(parent, flags, op, arg, childp, 0); 2618 2619 ndi_devi_exit(parent, circ); 2620 2621 if (pshot_debug) 2622 cmn_err(CE_CONT, "pshot%d: bus_config %s\n", 2623 ddi_get_instance(parent), 2624 (rval == NDI_SUCCESS) ? "ok" : "failed"); 2625 2626 return (rval); 2627 } 2628 2629 static int 2630 pshot_bus_unconfig(dev_info_t *parent, uint_t flags, 2631 ddi_bus_config_op_t op, void *arg) 2632 { 2633 major_t major; 2634 int rval = NDI_SUCCESS; 2635 int circ; 2636 2637 if (pshot_debug) { 2638 flags |= NDI_DEVI_DEBUG; 2639 cmn_err(CE_CONT, 2640 "pshot%d: bus_unconfig %s flags=0x%x\n", 2641 ddi_get_instance(parent), 2642 (op == BUS_UNCONFIG_ONE) ? (char *)arg : "", flags); 2643 } 2644 2645 /* 2646 * Hold the nexus across the bus_unconfig 2647 */ 2648 ndi_devi_enter(parent, &circ); 2649 2650 switch (op) { 2651 case BUS_UNCONFIG_ONE: 2652 /* 2653 * Nothing special required here 2654 */ 2655 if (pshot_debug) { 2656 cmn_err(CE_CONT, "pshot%d: bus_unconfig:" 2657 " BUS_UNCONFIG_ONE\n", ddi_get_instance(parent)); 2658 } 2659 break; 2660 2661 case BUS_UNCONFIG_DRIVER: 2662 if (pshot_debug > 0) { 2663 major = (major_t)(uintptr_t)arg; 2664 cmn_err(CE_CONT, 2665 "pshot%d: BUS_UNCONFIG_DRIVER: %s\n", 2666 ddi_get_instance(parent), 2667 ddi_major_to_name(major)); 2668 } 2669 break; 2670 2671 case BUS_UNCONFIG_ALL: 2672 if (pshot_debug) { 2673 cmn_err(CE_CONT, "pshot%d: bus_unconfig:" 2674 " BUS_UNCONFIG_ALL\n", ddi_get_instance(parent)); 2675 } 2676 break; 2677 2678 default: 2679 if (pshot_debug) { 2680 cmn_err(CE_CONT, "pshot%d: bus_unconfig: DEFAULT\n", 2681 ddi_get_instance(parent)); 2682 } 2683 rval = NDI_FAILURE; 2684 } 2685 2686 if (rval == NDI_SUCCESS) 2687 rval = ndi_busop_bus_unconfig(parent, flags, op, arg); 2688 2689 ndi_devi_exit(parent, circ); 2690 2691 if (pshot_debug) 2692 cmn_err(CE_CONT, "pshot%d: bus_unconfig %s\n", 2693 ddi_get_instance(parent), 2694 (rval == NDI_SUCCESS) ? "ok" : "failed"); 2695 2696 return (rval); 2697 } 2698 2699 static dev_info_t * 2700 pshot_findchild(dev_info_t *pdip, char *cname, char *caddr) 2701 { 2702 dev_info_t *dip; 2703 char *addr; 2704 2705 ASSERT(cname != NULL && caddr != NULL); 2706 ASSERT(DEVI_BUSY_OWNED(pdip)); 2707 2708 for (dip = ddi_get_child(pdip); dip != NULL; 2709 dip = ddi_get_next_sibling(dip)) { 2710 if (strcmp(cname, ddi_node_name(dip)) != 0) 2711 continue; 2712 2713 if ((addr = ddi_get_name_addr(dip)) == NULL) { 2714 if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, 0, 2715 "bus-addr", &addr) == DDI_PROP_SUCCESS) { 2716 if (strcmp(caddr, addr) == 0) { 2717 ddi_prop_free(addr); 2718 return (dip); 2719 } 2720 ddi_prop_free(addr); 2721 } 2722 } else { 2723 if (strcmp(caddr, addr) == 0) 2724 return (dip); 2725 } 2726 } 2727 2728 return (NULL); 2729 } 2730 2731 static void 2732 pshot_nexus_properties(dev_info_t *parent, dev_info_t *child, char *cname, 2733 char *caddr) 2734 { 2735 char *extension; 2736 2737 /* 2738 * extract the address extension 2739 */ 2740 extension = strstr(caddr, ","); 2741 if (extension != NULL) { 2742 ++extension; 2743 } else { 2744 extension = "null"; 2745 } 2746 2747 /* 2748 * Create the "pm-want-child-notification?" property for all 2749 * nodes that do not have the "pm_strict" or "nopm_strict" 2750 * extension 2751 */ 2752 if (strcmp(extension, "pm_strict") != 0 && 2753 strcmp(extension, "nopm_strict") != 0) { 2754 if (ddi_prop_exists(DDI_DEV_T_ANY, child, 2755 (DDI_PROP_DONTPASS | DDI_PROP_NOTPROM), 2756 "pm-want-child-notification?") == 0) { 2757 if (pshot_debug) { 2758 cmn_err(CE_CONT, "pshot%d:" 2759 " nexus_properties:\n\tcreate the" 2760 " \"pm-want-child-notification?\"" 2761 " property for %s@%s\n", 2762 ddi_get_instance(parent), cname, caddr); 2763 } 2764 if (ddi_prop_create(DDI_DEV_T_NONE, child, 0, 2765 "pm-want-child-notification?", NULL, 0) 2766 != DDI_PROP_SUCCESS) { 2767 cmn_err(CE_WARN, "pshot%d:" 2768 " nexus_properties:\n\tunable to create" 2769 " the \"pm-want-child-notification?\"" 2770 " property for %s@%s", 2771 ddi_get_instance(parent), cname, caddr); 2772 } 2773 } 2774 } 2775 2776 /* 2777 * Create the "no-pm-components" property for all nodes 2778 * with extension "nopm" or "nopm_strict" 2779 */ 2780 if (strcmp(extension, "nopm") == 0 || 2781 strcmp(extension, "nopm_strict") == 0) { 2782 if (ddi_prop_exists(DDI_DEV_T_ANY, child, 2783 (DDI_PROP_DONTPASS | DDI_PROP_NOTPROM), 2784 "no-pm-components") == 0) { 2785 if (pshot_debug) { 2786 cmn_err(CE_CONT, "pshot%d:" 2787 " nexus_properties:\n\tcreate the" 2788 " \"no-pm-components\"" 2789 " property for %s@%s\n", 2790 ddi_get_instance(parent), cname, caddr); 2791 } 2792 if (ddi_prop_create(DDI_DEV_T_NONE, child, 0, 2793 "no-pm-components", NULL, 0) 2794 != DDI_PROP_SUCCESS) { 2795 cmn_err(CE_WARN, "pshot%d:" 2796 " nexus_properties:\n\tunable to create" 2797 " the \"no-pm-components\"" 2798 " property for %s@%s", 2799 ddi_get_instance(parent), cname, caddr); 2800 } 2801 } 2802 } 2803 } 2804 2805 static void 2806 pshot_leaf_properties(dev_info_t *parent, dev_info_t *child, char *cname, 2807 char *caddr) 2808 { 2809 char *extension; 2810 2811 /* 2812 * extract the address extension 2813 */ 2814 extension = strstr(caddr, ","); 2815 if (extension != NULL) { 2816 ++extension; 2817 } else { 2818 extension = "null"; 2819 } 2820 2821 /* 2822 * Create the "no-involuntary-power-cycles" property for 2823 * all leaf nodes with extension "no_invol" 2824 */ 2825 if (strcmp(extension, "no_invol") == 0) { 2826 if (ddi_prop_exists(DDI_DEV_T_ANY, child, 2827 (DDI_PROP_DONTPASS | DDI_PROP_NOTPROM), 2828 "no-involuntary-power-cycles") == 0) { 2829 if (pshot_debug) { 2830 cmn_err(CE_CONT, "pshot%d:" 2831 " leaf_properties:\n\tcreate the" 2832 " \"no-involuntary-power-cycles\"" 2833 " property for %s@%s\n", 2834 ddi_get_instance(parent), cname, caddr); 2835 } 2836 if (ddi_prop_create(DDI_DEV_T_NONE, child, 2837 DDI_PROP_CANSLEEP, 2838 "no-involuntary-power-cycles", NULL, 0) 2839 != DDI_PROP_SUCCESS) { 2840 cmn_err(CE_WARN, "pshot%d:" 2841 " leaf_properties:\n\tunable to create the" 2842 " \"no-involuntary-power-cycles\"" 2843 " property for %s@%s", 2844 ddi_get_instance(parent), cname, caddr); 2845 } 2846 } 2847 } 2848 2849 /* 2850 * Create the "dependency-property" property for all leaf 2851 * nodes with extension "dep_prop" 2852 * to be used with the PM_ADD_DEPENDENT_PROPERTY ioctl 2853 */ 2854 if (strcmp(extension, "dep_prop") == 0) { 2855 if (ddi_prop_exists(DDI_DEV_T_ANY, child, 2856 (DDI_PROP_DONTPASS | DDI_PROP_NOTPROM), 2857 "dependency-property") == 0) { 2858 if (pshot_debug) { 2859 cmn_err(CE_CONT, "pshot%d:" 2860 " leaf_properties:\n\tcreate the" 2861 " \"dependency-property\"" 2862 " property for %s@%s\n", 2863 ddi_get_instance(parent), cname, caddr); 2864 } 2865 if (ddi_prop_create(DDI_DEV_T_NONE, child, 2866 DDI_PROP_CANSLEEP, "dependency-property", NULL, 0) 2867 != DDI_PROP_SUCCESS) { 2868 cmn_err(CE_WARN, "pshot%d:" 2869 " leaf_properties:\n\tunable to create the" 2870 " \"dependency-property\" property for" 2871 " %s@%s", ddi_get_instance(parent), 2872 cname, caddr); 2873 } 2874 } 2875 } 2876 } 2877 2878 /* 2879 * BUS_CONFIG_ONE: setup a child nexus instance. 2880 */ 2881 static int 2882 pshot_bus_config_setup_nexus(dev_info_t *parent, char *cname, char *caddr) 2883 { 2884 dev_info_t *child; 2885 int rval; 2886 2887 ASSERT(parent != 0); 2888 ASSERT(cname != NULL); 2889 ASSERT(caddr != NULL); 2890 2891 child = pshot_findchild(parent, cname, caddr); 2892 if (child) { 2893 if (pshot_debug) { 2894 cmn_err(CE_CONT, 2895 "pshot%d: bus_config one %s@%s found\n", 2896 ddi_get_instance(parent), cname, caddr); 2897 } 2898 2899 /* 2900 * create the "pm-want-child-notification?" property 2901 * for this child, if it doesn't already exist 2902 */ 2903 (void) pshot_nexus_properties(parent, child, cname, caddr); 2904 2905 return (NDI_SUCCESS); 2906 } 2907 2908 ndi_devi_alloc_sleep(parent, cname, DEVI_SID_NODEID, &child); 2909 ASSERT(child != NULL); 2910 2911 if (ndi_prop_update_string(DDI_DEV_T_NONE, child, 2912 "bus-addr", caddr) != DDI_PROP_SUCCESS) { 2913 cmn_err(CE_WARN, "pshot%d: _prop_update %s@%s failed", 2914 ddi_get_instance(parent), cname, caddr); 2915 (void) ndi_devi_free(child); 2916 return (NDI_FAILURE); 2917 } 2918 2919 rval = ndi_devi_bind_driver(child, 0); 2920 if (rval != NDI_SUCCESS) { 2921 cmn_err(CE_WARN, "pshot%d: bind_driver %s failed", 2922 ddi_get_instance(parent), cname); 2923 (void) ndi_devi_free(child); 2924 return (NDI_FAILURE); 2925 } 2926 2927 /* 2928 * create the "pm-want-child-notification?" property 2929 */ 2930 (void) pshot_nexus_properties(parent, child, cname, caddr); 2931 2932 return (NDI_SUCCESS); 2933 } 2934 2935 /* 2936 * BUS_CONFIG_ONE: setup a child leaf device instance. 2937 * for testing purposes, we will create nodes of a variety of types. 2938 */ 2939 static int 2940 pshot_bus_config_setup_leaf(dev_info_t *parent, char *cname, char *caddr) 2941 { 2942 dev_info_t *child; 2943 char *compat_name; 2944 char *nodetype; 2945 int rval; 2946 int i; 2947 2948 ASSERT(parent != 0); 2949 ASSERT(cname != NULL); 2950 ASSERT(caddr != NULL); 2951 2952 /* 2953 * if we already have a node with this name, return it 2954 */ 2955 if ((child = pshot_findchild(parent, cname, caddr)) != NULL) { 2956 /* 2957 * create the "no-involuntary-power-cycles" or 2958 * the "dependency-property" property, if they 2959 * don't already exit 2960 */ 2961 (void) pshot_leaf_properties(parent, child, cname, caddr); 2962 2963 return (NDI_SUCCESS); 2964 } 2965 2966 ndi_devi_alloc_sleep(parent, cname, DEVI_SID_NODEID, &child); 2967 ASSERT(child != NULL); 2968 2969 if (ndi_prop_update_string(DDI_DEV_T_NONE, child, "bus-addr", 2970 caddr) != DDI_PROP_SUCCESS) { 2971 (void) ndi_devi_free(child); 2972 return (NDI_FAILURE); 2973 } 2974 2975 /* 2976 * test compatible naming 2977 * if the child nodename is "cdisk", attach the list of compatible 2978 * named disks 2979 */ 2980 if (strcmp(cname, pshot_compat_diskname) == 0) { 2981 if ((ndi_prop_update_string_array(DDI_DEV_T_NONE, 2982 child, "compatible", (char **)pshot_compat_psramdisks, 2983 5)) != DDI_PROP_SUCCESS) { 2984 (void) ndi_devi_free(child); 2985 return (NDI_FAILURE); 2986 } 2987 } else { 2988 for (i = 0; i < pshot_devices_len && pshot_devices[i].name; 2989 i++) { 2990 if (strcmp(cname, pshot_devices[i].name) == 0) { 2991 compat_name = pshot_devices[i].compat; 2992 nodetype = pshot_devices[i].nodetype; 2993 if (pshot_debug) { 2994 cmn_err(CE_CONT, "pshot%d: %s %s %s\n", 2995 ddi_get_instance(parent), cname, 2996 compat_name, nodetype); 2997 } 2998 if ((ndi_prop_update_string_array( 2999 DDI_DEV_T_NONE, child, "compatible", 3000 &compat_name, 1)) != DDI_PROP_SUCCESS) { 3001 (void) ndi_devi_free(child); 3002 return (NDI_FAILURE); 3003 } 3004 if ((ndi_prop_update_string( 3005 DDI_DEV_T_NONE, child, "node-type", 3006 nodetype)) != DDI_PROP_SUCCESS) { 3007 (void) ndi_devi_free(child); 3008 return (NDI_FAILURE); 3009 } 3010 } 3011 } 3012 } 3013 3014 rval = ndi_devi_bind_driver(child, 0); 3015 if (rval != NDI_SUCCESS) { 3016 cmn_err(CE_WARN, "pshot%d: bind_driver %s failed", 3017 ddi_get_instance(parent), cname); 3018 (void) ndi_devi_free(child); 3019 return (NDI_FAILURE); 3020 } 3021 3022 /* 3023 * create the "no-involuntary-power-cycles" or 3024 * the "dependency-property" property 3025 */ 3026 (void) pshot_leaf_properties(parent, child, cname, caddr); 3027 3028 return (NDI_SUCCESS); 3029 } 3030 3031 /* 3032 * Handle some special cases for testing bus_config via pshot 3033 * 3034 * Match these special address formats to behavior: 3035 * 3036 * err.* - induce bus_config error 3037 * delay - induce 1 second of bus_config delay time 3038 * delay,n - induce n seconds of bus_config delay time 3039 * wait - induce 1 second of bus_config wait time 3040 * wait,n - induce n seconds of bus_config wait time 3041 * failinit.* - induce error at INITCHILD 3042 * failprobe.* - induce error at probe 3043 * failattach.* - induce error at attach 3044 */ 3045 /*ARGSUSED*/ 3046 static int 3047 pshot_bus_config_test_specials(dev_info_t *parent, char *devname, 3048 char *cname, char *caddr) 3049 { 3050 char *p; 3051 int n; 3052 3053 if (strncmp(caddr, "err", 3) == 0) { 3054 if (pshot_debug) 3055 cmn_err(CE_CONT, 3056 "pshot%d: %s forced failure\n", 3057 ddi_get_instance(parent), devname); 3058 return (NDI_FAILURE); 3059 } 3060 3061 /* 3062 * The delay and wait strings have the same effect. 3063 * The "wait[,]" support should be removed once the 3064 * devfs test suites are fixed. 3065 * NOTE: delay should not be called from interrupt context 3066 */ 3067 ASSERT(!servicing_interrupt()); 3068 3069 if (strncmp(caddr, "delay,", 6) == 0) { 3070 p = caddr+6; 3071 n = stoi(&p); 3072 if (*p != 0) 3073 n = 1; 3074 if (pshot_debug) 3075 cmn_err(CE_CONT, 3076 "pshot%d: %s delay %d second\n", 3077 ddi_get_instance(parent), devname, n); 3078 delay(n * drv_usectohz(1000000)); 3079 } else if (strncmp(caddr, "delay", 5) == 0) { 3080 if (pshot_debug) 3081 cmn_err(CE_CONT, 3082 "pshot%d: %s delay 1 second\n", 3083 ddi_get_instance(parent), devname); 3084 delay(drv_usectohz(1000000)); 3085 } else if (strncmp(caddr, "wait,", 5) == 0) { 3086 p = caddr+5; 3087 n = stoi(&p); 3088 if (*p != 0) 3089 n = 1; 3090 if (pshot_debug) 3091 cmn_err(CE_CONT, 3092 "pshot%d: %s wait %d second\n", 3093 ddi_get_instance(parent), devname, n); 3094 delay(n * drv_usectohz(1000000)); 3095 } else if (strncmp(caddr, "wait", 4) == 0) { 3096 if (pshot_debug) 3097 cmn_err(CE_CONT, 3098 "pshot%d: %s wait 1 second\n", 3099 ddi_get_instance(parent), devname); 3100 delay(drv_usectohz(1000000)); 3101 } 3102 3103 return (NDI_SUCCESS); 3104 } 3105 3106 /* 3107 * translate nodetype name to actual value 3108 */ 3109 static char * 3110 pshot_str2nt(char *str) 3111 { 3112 int i; 3113 3114 for (i = 0; pshot_nodetypes[i].name; i++) { 3115 if (strcmp(pshot_nodetypes[i].name, str) == 0) 3116 return (pshot_nodetypes[i].val); 3117 } 3118 return (NULL); 3119 } 3120 3121 /* 3122 * grows array pointed to by <dstp>, with <src> data 3123 * <dstlen> = # elements of the original <*dstp> 3124 * <srclen> = # elements of <src> 3125 * 3126 * on success, returns 0 and a pointer to the new array through <dstp> with 3127 * <srclen> + <dstlen> number of elements; 3128 * else returns non-zero 3129 * 3130 * a NULL <*dstp> is OK (a NULL <dstp> is not) and so is a zero <dstlen> 3131 */ 3132 static int 3133 pshot_devices_grow(pshot_device_t **dstp, size_t dstlen, 3134 const pshot_device_t *src, size_t srclen) 3135 { 3136 size_t i; 3137 pshot_device_t *newdst; 3138 3139 newdst = kmem_alloc((srclen + dstlen) * sizeof (*src), 3140 KM_SLEEP); 3141 3142 /* keep old pointers and dup new ones */ 3143 if (*dstp) 3144 bcopy(*dstp, newdst, dstlen * sizeof (*src)); 3145 for (i = 0; i < srclen; i++) { 3146 newdst[i + dstlen].name = 3147 i_ddi_strdup(src[i].name, KM_SLEEP); 3148 3149 newdst[i + dstlen].nodetype = 3150 i_ddi_strdup(src[i].nodetype, KM_SLEEP); 3151 3152 newdst[i + dstlen].compat = 3153 i_ddi_strdup(src[i].compat, KM_SLEEP); 3154 } 3155 3156 /* do last */ 3157 if (*dstp) 3158 kmem_free(*dstp, dstlen * sizeof (*src)); 3159 *dstp = newdst; 3160 return (0); 3161 } 3162 3163 /* 3164 * free a pshot_device_t array <dp> with <len> elements 3165 * null pointers within the elements are ok 3166 */ 3167 static void 3168 pshot_devices_free(pshot_device_t *dp, size_t len) 3169 { 3170 size_t i; 3171 3172 for (i = 0; i < len; i++) { 3173 if (dp[i].name) 3174 kmem_free(dp[i].name, strlen(dp[i].name) + 1); 3175 if (dp[i].nodetype) 3176 kmem_free(dp[i].nodetype, strlen(dp[i].nodetype) + 1); 3177 if (dp[i].compat) 3178 kmem_free(dp[i].compat, strlen(dp[i].compat) + 1); 3179 } 3180 kmem_free(dp, len * sizeof (*dp)); 3181 } 3182 3183 /* 3184 * returns an array of pshot_device_t parsed from <dip>'s properties 3185 * 3186 * property structure (i.e. pshot.conf) for pshot: 3187 * 3188 * corresponding | pshot_device_t array elements 3189 * pshot_device_t | 3190 * member by prop name | [0] [1] [2] 3191 * ----------------------|--------------|-------------|----------------------- 3192 * <PSHOT_PROP_DEVNAME> ="disk", "tape", "testdev"; 3193 * <PSHOT_PROP_DEVNT> ="DDI_NT_BLOCK","DDI_NT_TAPE","ddi_testdev_nodetype"; 3194 * <PSHOT_PROP_DEVCOMPAT>="testdrv", "testdrv", "testdrv"; 3195 * 3196 * 3197 * if any of these properties are specified, then: 3198 * - all the members must be specified 3199 * - the number of elements for each string array property must be the same 3200 * - no empty strings allowed 3201 * - nodetypes (PSHOT_PROP_DEVNT) must be the nodetype name as specified in 3202 * sys/sunddi.h 3203 * 3204 * NOTE: the pshot_nodetypes[] table should be kept in sync with the list 3205 * of ddi nodetypes. It's not normally critical to always be in sync so 3206 * keeping this up-to-date can usually be done "on-demand". 3207 * 3208 * if <flags> & PSHOT_DEV_ANYNT, then custom nodetype strings are allowed. 3209 * these will be duplicated verbatim 3210 */ 3211 static pshot_device_t * 3212 pshot_devices_from_props(dev_info_t *dip, size_t *lenp, int flags) 3213 { 3214 pshot_device_t *devarr = NULL; 3215 char **name_arr = NULL, **nt_arr = NULL, **compat_arr = NULL; 3216 uint_t name_arr_len, nt_arr_len, compat_arr_len; 3217 uint_t i; 3218 char *str; 3219 3220 if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, dip, 0, 3221 PSHOT_PROP_DEVNAME, &name_arr, &name_arr_len) != 3222 DDI_PROP_SUCCESS) 3223 name_arr = NULL; 3224 3225 if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, dip, 0, 3226 PSHOT_PROP_DEVNT, &nt_arr, &nt_arr_len) != 3227 DDI_PROP_SUCCESS) 3228 nt_arr = NULL; 3229 3230 if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, dip, 0, 3231 PSHOT_PROP_DEVCOMPAT, &compat_arr, &compat_arr_len) != 3232 DDI_PROP_SUCCESS) 3233 compat_arr = NULL; 3234 3235 /* 3236 * warn about any incorrect usage, if specified 3237 */ 3238 if (!(name_arr || nt_arr || compat_arr)) 3239 return (NULL); 3240 3241 if (!(name_arr && nt_arr && compat_arr) || 3242 (name_arr_len != nt_arr_len) || 3243 (name_arr_len != compat_arr_len)) 3244 goto FAIL; 3245 3246 for (i = 0; i < name_arr_len; i++) { 3247 if (*name_arr[i] == '\0' || 3248 *nt_arr[i] == '\0' || 3249 *compat_arr[i] == '\0') 3250 goto FAIL; 3251 } 3252 3253 devarr = kmem_zalloc(name_arr_len * sizeof (*devarr), KM_SLEEP); 3254 for (i = 0; i < name_arr_len; i++) { 3255 devarr[i].name = i_ddi_strdup(name_arr[i], KM_SLEEP); 3256 devarr[i].compat = i_ddi_strdup(compat_arr[i], KM_SLEEP); 3257 3258 if ((str = pshot_str2nt(nt_arr[i])) == NULL) 3259 if (flags & PSHOT_DEV_ANYNT) 3260 str = nt_arr[i]; 3261 else 3262 goto FAIL; 3263 devarr[i].nodetype = i_ddi_strdup(str, KM_SLEEP); 3264 } 3265 ddi_prop_free(name_arr); 3266 ddi_prop_free(nt_arr); 3267 ddi_prop_free(compat_arr); 3268 3269 /* set <*lenp> ONLY on success */ 3270 *lenp = name_arr_len; 3271 3272 return (devarr); 3273 /*NOTREACHED*/ 3274 FAIL: 3275 cmn_err(CE_WARN, "malformed device specification property"); 3276 if (name_arr) 3277 ddi_prop_free(name_arr); 3278 if (nt_arr) 3279 ddi_prop_free(nt_arr); 3280 if (compat_arr) 3281 ddi_prop_free(compat_arr); 3282 if (devarr) 3283 pshot_devices_free(devarr, name_arr_len); 3284 return (NULL); 3285 } 3286 3287 /* 3288 * if global <pshot_devices> was not set up already (i.e. is NULL): 3289 * sets up global <pshot_devices> and <pshot_devices_len>, 3290 * using device properties from <dip> and global <pshot_stock_devices>. 3291 * device properties, if any, overrides pshot_stock_devices. 3292 * 3293 * returns 0 on success (or if pshot_devices already set up) 3294 * 3295 * INTERNAL LOCKING: <pshot_devices_lock> 3296 */ 3297 static int 3298 pshot_devices_setup(dev_info_t *dip) 3299 { 3300 pshot_device_t *newdevs = NULL; 3301 size_t newdevs_len = 0; 3302 int rv = 0; 3303 3304 mutex_enter(&pshot_devices_lock); 3305 if (pshot_devices != NULL) 3306 goto FAIL; 3307 3308 ASSERT(pshot_devices_len == 0); 3309 3310 newdevs = pshot_devices_from_props(dip, &newdevs_len, PSHOT_DEV_ANYNT); 3311 rv = pshot_devices_grow(&newdevs, newdevs_len, pshot_stock_devices, 3312 PSHOT_N_STOCK_DEVICES); 3313 if (rv != 0) { 3314 cmn_err(CE_WARN, "pshot_devices_setup: pshot_devices_grow " 3315 "failed"); 3316 goto FAIL; 3317 } 3318 newdevs_len += PSHOT_N_STOCK_DEVICES; 3319 3320 pshot_devices = newdevs; 3321 pshot_devices_len = newdevs_len; 3322 rv = 0; 3323 FAIL: 3324 if (rv && newdevs) 3325 pshot_devices_free(newdevs, newdevs_len); 3326 mutex_exit(&pshot_devices_lock); 3327 return (rv); 3328 } 3329 3330 3331 #ifdef NOTNEEDED 3332 /* ARGSUSED */ 3333 static int 3334 pshot_probe_family(dev_info_t *self, ddi_probe_method_t probe_how, 3335 dev_info_t **return_dip) 3336 { 3337 char name[64]; 3338 uint_t bus_id; 3339 dev_info_t *child; 3340 3341 for (bus_id = 10; bus_id < 20; bus_id++) { 3342 (void) sprintf(name, "%d", bus_id); 3343 if ((ndi_devi_alloc(self, "psramd", DEVI_SID_NODEID, 3344 &child)) != NDI_SUCCESS) { 3345 return (DDI_FAILURE); 3346 } 3347 3348 if (ndi_prop_update_string(DDI_DEV_T_NONE, child, 3349 "bus-addr", name) != DDI_PROP_SUCCESS) { 3350 (void) ndi_devi_free(child); 3351 if (return_dip != NULL) 3352 *return_dip = (dev_info_t *)NULL; 3353 return (DDI_FAILURE); 3354 } 3355 3356 if (ndi_devi_online(child, 0) != NDI_SUCCESS) { 3357 return (DDI_FAILURE); 3358 } 3359 } 3360 return (DDI_SUCCESS); 3361 } 3362 3363 static int 3364 strtoi(char *str) 3365 { 3366 int c; 3367 int val; 3368 3369 for (val = 0, c = *str++; c >= '0' && c <= '9'; c = *str++) { 3370 val *= 10; 3371 val += c - '0'; 3372 } 3373 return (val); 3374 } 3375 3376 #endif 3377 3378 static void 3379 pshot_setup_autoattach(dev_info_t *devi) 3380 { 3381 dev_info_t *l1child, *l2child; 3382 int rv; 3383 3384 rv = ndi_devi_alloc(devi, "pshot", DEVI_SID_NODEID, &l1child); 3385 if (rv == NDI_SUCCESS) { 3386 (void) ndi_prop_update_string(DDI_DEV_T_NONE, l1child, 3387 "bus-addr", "0"); 3388 rv = ndi_devi_alloc(l1child, "port", DEVI_SID_NODEID, 3389 &l2child); 3390 if (rv == NDI_SUCCESS) 3391 (void) ndi_prop_update_string(DDI_DEV_T_NONE, 3392 l2child, "bus-addr", "99"); 3393 } 3394 3395 rv = ndi_devi_alloc(devi, "port", DEVI_SID_NODEID, &l1child); 3396 if (rv == NDI_SUCCESS) 3397 (void) ndi_prop_update_string(DDI_DEV_T_NONE, l1child, 3398 "bus-addr", "99"); 3399 3400 rv = ndi_devi_alloc(devi, "gen_drv", DEVI_SID_NODEID, &l1child); 3401 if (rv == NDI_SUCCESS) 3402 (void) ndi_prop_update_string(DDI_DEV_T_NONE, l1child, 3403 "bus-addr", "99"); 3404 3405 rv = ndi_devi_alloc(devi, "no_driver", DEVI_SID_NODEID, &l1child); 3406 if (rv == NDI_SUCCESS) 3407 (void) ndi_devi_alloc(l1child, "no_driver", DEVI_SID_NODEID, 3408 &l2child); 3409 } 3410 3411 #ifdef PRUNE_SNUBS 3412 3413 #define PRUNE_THIS_NODE(d) (((d)->devi_node_name != NULL) && \ 3414 (DEVI_PROM_NODE((d)->devi_nodeid)) && \ 3415 ((d)->devi_addr == NULL)) 3416 /* 3417 * test code to remove OBP nodes that have not attached 3418 */ 3419 static void 3420 prune_snubs(const char *name) 3421 { 3422 struct dev_info *nex_dip, *cdip, *cndip; 3423 int maj; 3424 int rv; 3425 3426 maj = ddi_name_to_major((char *)name); 3427 if (maj != -1) { 3428 nex_dip = (struct dev_info *)devnamesp[maj].dn_head; 3429 while (nex_dip != NULL) { 3430 cndip = ddi_get_child(nex_dip); 3431 while ((cdip = cndip) != NULL) { 3432 cndip = cdip->devi_sibling; 3433 if (PRUNE_THIS_NODE(cdip)) { 3434 cmn_err(CE_NOTE, 3435 "parent %s@%s pruning node %s", 3436 nex_dip->devi_node_name, 3437 nex_dip->devi_addr, 3438 cdip->devi_node_name); 3439 rv = ndi_devi_offline(cdip, 3440 NDI_DEVI_REMOVE); 3441 if (rv != NDI_SUCCESS) 3442 cmn_err(CE_NOTE, 3443 "failed to prune node, " 3444 "err %d", rv); 3445 } 3446 } 3447 nex_dip = nex_dip->devi_next; 3448 } 3449 } 3450 } 3451 3452 #endif /* PRUBE_SNUBS */ 3453 3454 #ifdef KERNEL_DEVICE_TREE_WALKER 3455 static kthread_id_t pwt; 3456 static kmutex_t pwl; 3457 static kcondvar_t pwcv; 3458 3459 static void 3460 pshot_walk_tree() 3461 { 3462 static int pshot_devnode(dev_info_t *dip, void * arg); 3463 3464 dev_info_t *root = ddi_root_node(); 3465 ddi_walk_devs(root, pshot_devnode, NULL); 3466 } 3467 3468 static void 3469 pshot_walk_thread() 3470 { 3471 static void pshot_timeout(void *arg); 3472 static kthread_id_t pwt; 3473 3474 pwt = curthread; 3475 mutex_init(&pwl, NULL, MUTEX_DRIVER, NULL); 3476 cv_init(&pwcv, NULL, CV_DRIVER, NULL); 3477 3478 while (1) { 3479 pshot_walk_tree(); 3480 mutex_enter(&pwl); 3481 (void) timeout(pshot_timeout, NULL, 5 * drv_usectohz(1000000)); 3482 cv_wait(&pwcv, &pwl); 3483 mutex_exit(&pwl); 3484 } 3485 } 3486 3487 static void 3488 pshot_timeout(void *arg) 3489 { 3490 mutex_enter(&pwl); 3491 cv_signal(&pwcv); 3492 mutex_exit(&pwl); 3493 } 3494 3495 static int 3496 pshot_devnode(dev_info_t *dip, void *arg) 3497 { 3498 dev_info_t *f_dip; 3499 3500 if (dip != ddi_root_node()) { 3501 f_dip = ndi_devi_find((dev_info_t *)DEVI(dip)->devi_parent, 3502 DEVI(dip)->devi_node_name, DEVI(dip)->devi_addr); 3503 if (f_dip != dip) { 3504 cmn_err(CE_NOTE, "!pshot_devnode: failed lookup" 3505 "node (%s/%s@%s)\n", 3506 DEVI(DEVI(dip)->devi_parent)->devi_node_name, 3507 (DEVI(dip)->devi_node_name ? 3508 DEVI(dip)->devi_node_name : "NULL"), 3509 (DEVI(dip)->devi_addr ? DEVI(dip)->devi_addr : 3510 "NULL")); 3511 } 3512 } 3513 return (DDI_WALK_CONTINUE); 3514 } 3515 #endif /* KERNEL_DEVICE_TREE_WALKER */ 3516 3517 #ifdef DEBUG 3518 static void 3519 pshot_event_cb_test(dev_info_t *dip, ddi_eventcookie_t cookie, 3520 void *arg, void *bus_impldata) 3521 { 3522 pshot_t *softstate = (pshot_t *)arg; 3523 int event_tag; 3524 3525 /* look up the event */ 3526 event_tag = NDI_EVENT_TAG(cookie); 3527 cmn_err(CE_CONT, "pshot_event_cb_test:\n\t" 3528 "dip = 0x%p cookie = 0x%p (%s), tag = %d\n\t" 3529 "arg = 0x%p bus_impl = 0x%p\n", 3530 (void *)dip, (void *)cookie, NDI_EVENT_NAME(cookie), 3531 event_tag, (void *)softstate, (void *)bus_impldata); 3532 3533 } 3534 3535 static void 3536 pshot_event_test(void *arg) 3537 { 3538 pshot_t *pshot = (pshot_t *)arg; 3539 ndi_event_hdl_t hdl; 3540 ndi_event_set_t events; 3541 int i, rval; 3542 3543 (void) ndi_event_alloc_hdl(pshot->dip, NULL, &hdl, NDI_SLEEP); 3544 3545 events.ndi_events_version = NDI_EVENTS_REV1; 3546 events.ndi_n_events = PSHOT_N_TEST_EVENTS; 3547 events.ndi_event_defs = pshot_test_events; 3548 3549 cmn_err(CE_CONT, "pshot: binding set of 8 events\n"); 3550 delay(drv_usectohz(1000000)); 3551 rval = ndi_event_bind_set(hdl, &events, NDI_SLEEP); 3552 cmn_err(CE_CONT, "pshot: ndi_event_bind_set rval = %d\n", rval); 3553 3554 cmn_err(CE_CONT, "pshot: binding the same set of 8 events\n"); 3555 delay(drv_usectohz(1000000)); 3556 rval = ndi_event_bind_set(hdl, &events, NDI_SLEEP); 3557 cmn_err(CE_CONT, "pshot: ndi_event_bind_set rval = %d\n", rval); 3558 3559 cmn_err(CE_CONT, "pshot: unbinding all events\n"); 3560 delay(drv_usectohz(1000000)); 3561 rval = ndi_event_unbind_set(hdl, &events, NDI_SLEEP); 3562 cmn_err(CE_CONT, "pshot: ndi_event_unbind_set rval = %d\n", rval); 3563 3564 3565 cmn_err(CE_CONT, "pshot: binding one highlevel event\n"); 3566 delay(drv_usectohz(1000000)); 3567 events.ndi_n_events = 1; 3568 events.ndi_event_defs = pshot_test_events_high; 3569 rval = ndi_event_bind_set(hdl, &events, NDI_SLEEP); 3570 cmn_err(CE_CONT, "pshot: ndi_event_bind_set rval = %d\n", rval); 3571 3572 cmn_err(CE_CONT, "pshot: binding the same set of 8 events\n"); 3573 delay(drv_usectohz(1000000)); 3574 events.ndi_n_events = PSHOT_N_TEST_EVENTS; 3575 events.ndi_event_defs = pshot_test_events; 3576 rval = ndi_event_bind_set(hdl, &events, NDI_SLEEP); 3577 cmn_err(CE_CONT, "pshot: ndi_event_bind_set rval = %d\n", rval); 3578 3579 cmn_err(CE_CONT, "pshot: unbinding one highlevel event\n"); 3580 delay(drv_usectohz(1000000)); 3581 events.ndi_n_events = 1; 3582 events.ndi_event_defs = pshot_test_events_high; 3583 rval = ndi_event_unbind_set(hdl, &events, NDI_SLEEP); 3584 cmn_err(CE_CONT, "pshot: ndi_event_bind_set rval = %d\n", rval); 3585 3586 cmn_err(CE_CONT, "pshot: binding one highlevel event\n"); 3587 delay(drv_usectohz(1000000)); 3588 events.ndi_n_events = 1; 3589 events.ndi_event_defs = pshot_test_events_high; 3590 rval = ndi_event_bind_set(hdl, &events, NDI_SLEEP); 3591 cmn_err(CE_CONT, "pshot: ndi_event_bind_set rval = %d\n", rval); 3592 3593 cmn_err(CE_CONT, "pshot: unbinding one highlevel event\n"); 3594 delay(drv_usectohz(1000000)); 3595 events.ndi_n_events = 1; 3596 events.ndi_event_defs = pshot_test_events_high; 3597 rval = ndi_event_unbind_set(hdl, &events, NDI_SLEEP); 3598 cmn_err(CE_CONT, "pshot: ndi_event_bind_set rval = %d\n", rval); 3599 3600 cmn_err(CE_CONT, "pshot: binding the same set of 8 events\n"); 3601 delay(drv_usectohz(1000000)); 3602 events.ndi_n_events = PSHOT_N_TEST_EVENTS; 3603 events.ndi_event_defs = pshot_test_events; 3604 rval = ndi_event_bind_set(hdl, &events, NDI_SLEEP); 3605 cmn_err(CE_CONT, "pshot: ndi_event_bind_set rval = %d\n", rval); 3606 3607 cmn_err(CE_CONT, "pshot: unbinding first 2 events\n"); 3608 delay(drv_usectohz(1000000)); 3609 events.ndi_n_events = 2; 3610 events.ndi_event_defs = pshot_test_events; 3611 rval = ndi_event_unbind_set(hdl, &events, NDI_SLEEP); 3612 cmn_err(CE_CONT, "pshot: ndi_event_unbind_set rval = %d\n", rval); 3613 3614 cmn_err(CE_CONT, "pshot: unbinding first 2 events again\n"); 3615 delay(drv_usectohz(1000000)); 3616 events.ndi_n_events = 2; 3617 events.ndi_event_defs = pshot_test_events; 3618 rval = ndi_event_unbind_set(hdl, &events, NDI_SLEEP); 3619 cmn_err(CE_CONT, "pshot: ndi_event_unbind_set rval = %d\n", rval); 3620 3621 cmn_err(CE_CONT, "pshot: unbinding middle 2 events\n"); 3622 delay(drv_usectohz(1000000)); 3623 events.ndi_n_events = 2; 3624 events.ndi_event_defs = &pshot_test_events[4]; 3625 rval = ndi_event_unbind_set(hdl, &events, NDI_SLEEP); 3626 cmn_err(CE_CONT, "pshot: ndi_event_unbind_set rval = %d\n", rval); 3627 3628 cmn_err(CE_CONT, "pshot: binding those 2 events back\n"); 3629 delay(drv_usectohz(1000000)); 3630 events.ndi_n_events = 2; 3631 events.ndi_event_defs = &pshot_test_events[4]; 3632 rval = ndi_event_bind_set(hdl, &events, NDI_SLEEP); 3633 cmn_err(CE_CONT, "pshot: ndi_event_bind_set rval = %d\n", rval); 3634 3635 cmn_err(CE_CONT, "pshot: unbinding 2 events\n"); 3636 delay(drv_usectohz(1000000)); 3637 events.ndi_n_events = 2; 3638 events.ndi_event_defs = &pshot_test_events[4]; 3639 rval = ndi_event_unbind_set(hdl, &events, NDI_SLEEP); 3640 cmn_err(CE_CONT, "pshot: ndi_event_unbind_set rval = %d\n", rval); 3641 3642 cmn_err(CE_CONT, "pshot: unbinding all events\n"); 3643 delay(drv_usectohz(1000000)); 3644 events.ndi_n_events = PSHOT_N_TEST_EVENTS; 3645 events.ndi_event_defs = pshot_test_events; 3646 rval = ndi_event_unbind_set(hdl, &events, NDI_SLEEP); 3647 cmn_err(CE_CONT, "pshot: ndi_event_unbind_set rval = %d\n", rval); 3648 3649 cmn_err(CE_CONT, "pshot: unbinding 1 event\n"); 3650 delay(drv_usectohz(1000000)); 3651 events.ndi_n_events = 1; 3652 events.ndi_event_defs = &pshot_test_events[2]; 3653 rval = ndi_event_unbind_set(hdl, &events, NDI_SLEEP); 3654 cmn_err(CE_CONT, "pshot: ndi_event_unbind_set rval = %d\n", rval); 3655 3656 cmn_err(CE_CONT, "pshot: unbinding 1 event\n"); 3657 delay(drv_usectohz(1000000)); 3658 events.ndi_n_events = 1; 3659 events.ndi_event_defs = &pshot_test_events[3]; 3660 rval = ndi_event_unbind_set(hdl, &events, NDI_SLEEP); 3661 cmn_err(CE_CONT, "pshot: ndi_event_unbind_set rval = %d\n", rval); 3662 3663 cmn_err(CE_CONT, "pshot: unbinding 1 event\n"); 3664 delay(drv_usectohz(1000000)); 3665 events.ndi_n_events = 1; 3666 events.ndi_event_defs = &pshot_test_events[6]; 3667 rval = ndi_event_unbind_set(hdl, &events, NDI_SLEEP); 3668 cmn_err(CE_CONT, "pshot: ndi_event_unbind_set rval = %d\n", rval); 3669 3670 cmn_err(CE_CONT, "pshot: unbinding 1 event\n"); 3671 delay(drv_usectohz(1000000)); 3672 events.ndi_n_events = 1; 3673 events.ndi_event_defs = &pshot_test_events[7]; 3674 rval = ndi_event_unbind_set(hdl, &events, NDI_SLEEP); 3675 cmn_err(CE_CONT, "pshot: ndi_event_unbind_set rval = %d\n", rval); 3676 3677 events.ndi_n_events = PSHOT_N_TEST_EVENTS; 3678 events.ndi_event_defs = pshot_test_events; 3679 3680 cmn_err(CE_CONT, "pshot: binding set of 8 events\n"); 3681 delay(drv_usectohz(1000000)); 3682 rval = ndi_event_bind_set(hdl, &events, NDI_SLEEP); 3683 cmn_err(CE_CONT, "pshot: ndi_event_bind_set rval = %d\n", rval); 3684 3685 cmn_err(CE_CONT, "pshot: adding 8 callbacks\n"); 3686 delay(drv_usectohz(1000000)); 3687 for (i = 0; i < 8; i++) { 3688 rval = ndi_event_add_callback(hdl, pshot->dip, 3689 ndi_event_tag_to_cookie(hdl, 3690 pshot_test_events[i].ndi_event_tag), 3691 pshot_event_cb_test, 3692 (void *)(uintptr_t)pshot_test_events[i].ndi_event_tag, 3693 NDI_SLEEP, &pshot->test_callback_cache[i]); 3694 ASSERT(rval == NDI_SUCCESS); 3695 } 3696 3697 cmn_err(CE_CONT, "pshot: event callbacks\n"); 3698 3699 for (i = 10; i < 18; i++) { 3700 ddi_eventcookie_t cookie = ndi_event_tag_to_cookie(hdl, i); 3701 3702 rval = ndi_event_run_callbacks(hdl, pshot->dip, cookie, 3703 (void *)hdl); 3704 3705 cmn_err(CE_CONT, "pshot: callback, tag=%d rval=%d\n", 3706 i, rval); 3707 delay(drv_usectohz(1000000)); 3708 } 3709 3710 cmn_err(CE_CONT, "pshot: redo event callbacks\n"); 3711 3712 for (i = 10; i < 18; i++) { 3713 ddi_eventcookie_t cookie = ndi_event_tag_to_cookie(hdl, i); 3714 3715 rval = ndi_event_run_callbacks(hdl, 3716 pshot->dip, cookie, (void *)hdl); 3717 3718 cmn_err(CE_CONT, "pshot: callback, tag=%d rval=%d\n", 3719 i, rval); 3720 delay(drv_usectohz(1000000)); 3721 } 3722 3723 cmn_err(CE_CONT, "pshot: removing 8 callbacks\n"); 3724 delay(drv_usectohz(1000000)); 3725 3726 for (i = 0; i < 8; i++) { 3727 (void) ndi_event_remove_callback(hdl, 3728 pshot->test_callback_cache[i]); 3729 3730 pshot->test_callback_cache[i] = 0; 3731 } 3732 3733 cmn_err(CE_CONT, "pshot: freeing handle with bound set\n"); 3734 delay(drv_usectohz(1000000)); 3735 3736 rval = ndi_event_free_hdl(hdl); 3737 3738 ASSERT(rval == NDI_SUCCESS); 3739 3740 } 3741 3742 void 3743 pshot_event_test_post_one(void *arg) 3744 { 3745 pshot_t *pshot = (pshot_t *)arg; 3746 int rval; 3747 ddi_eventcookie_t cookie; 3748 3749 cmn_err(CE_CONT, "pshot%d: pshot_event_post_one event\n", 3750 pshot->instance); 3751 3752 if (ddi_get_eventcookie(pshot->dip, PSHOT_EVENT_NAME_BUS_TEST_POST, 3753 &cookie) != DDI_SUCCESS) { 3754 cmn_err(CE_NOTE, "pshot_bus_test_post cookie not found"); 3755 return; 3756 } 3757 3758 rval = ndi_post_event(pshot->dip, pshot->dip, cookie, NULL); 3759 3760 cmn_err(CE_CONT, "pshot%d: pshot_event_post_one rval=%d\n", 3761 pshot->instance, rval); 3762 3763 (void) timeout(pshot_event_test_post_one, (void *)pshot, 3764 pshot->instance * drv_usectohz(60000000)); 3765 3766 } 3767 #endif /* DEBUG */ 3768