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