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