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