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