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