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 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <sys/types.h> 30 #include <sys/param.h> 31 #include <sys/systm.h> 32 #include <sys/buf.h> 33 #include <sys/uio.h> 34 #include <sys/cred.h> 35 #include <sys/poll.h> 36 #include <sys/mman.h> 37 #include <sys/kmem.h> 38 #include <sys/model.h> 39 #include <sys/file.h> 40 #include <sys/proc.h> 41 #include <sys/open.h> 42 #include <sys/user.h> 43 #include <sys/t_lock.h> 44 #include <sys/vm.h> 45 #include <sys/stat.h> 46 #include <vm/hat.h> 47 #include <vm/seg.h> 48 #include <vm/as.h> 49 #include <sys/cmn_err.h> 50 #include <sys/debug.h> 51 #include <sys/avintr.h> 52 #include <sys/autoconf.h> 53 #include <sys/sunddi.h> 54 #include <sys/esunddi.h> 55 #include <sys/sunndi.h> 56 #include <sys/ddi.h> 57 #include <sys/kstat.h> 58 #include <sys/conf.h> 59 #include <sys/ddi_impldefs.h> /* include implementation structure defs */ 60 #include <sys/ndi_impldefs.h> 61 #include <sys/hwconf.h> 62 #include <sys/pathname.h> 63 #include <sys/modctl.h> 64 #include <sys/epm.h> 65 #include <sys/devctl.h> 66 #include <sys/callb.h> 67 #include <sys/bootconf.h> 68 #include <sys/dacf_impl.h> 69 #include <sys/nvpair.h> 70 #include <sys/sunmdi.h> 71 #include <sys/fs/dv_node.h> 72 73 #ifdef __sparc 74 #include <sys/archsystm.h> /* getpil/setpil */ 75 #include <sys/membar.h> /* membar_sync */ 76 #endif 77 78 /* 79 * ndi property handling 80 */ 81 int 82 ndi_prop_update_int(dev_t match_dev, dev_info_t *dip, 83 char *name, int data) 84 { 85 return (ddi_prop_update_common(match_dev, dip, 86 DDI_PROP_HW_DEF | DDI_PROP_TYPE_INT | DDI_PROP_DONTSLEEP, 87 name, &data, 1, ddi_prop_fm_encode_ints)); 88 } 89 90 int 91 ndi_prop_update_int64(dev_t match_dev, dev_info_t *dip, 92 char *name, int64_t data) 93 { 94 return (ddi_prop_update_common(match_dev, dip, 95 DDI_PROP_HW_DEF | DDI_PROP_TYPE_INT64 | DDI_PROP_DONTSLEEP, 96 name, &data, 1, ddi_prop_fm_encode_int64)); 97 } 98 99 int 100 ndi_prop_create_boolean(dev_t match_dev, dev_info_t *dip, 101 char *name) 102 { 103 return (ddi_prop_update_common(match_dev, dip, 104 DDI_PROP_HW_DEF | DDI_PROP_TYPE_ANY | DDI_PROP_DONTSLEEP, 105 name, NULL, 0, ddi_prop_fm_encode_bytes)); 106 } 107 108 int 109 ndi_prop_update_int_array(dev_t match_dev, dev_info_t *dip, 110 char *name, int *data, uint_t nelements) 111 { 112 return (ddi_prop_update_common(match_dev, dip, 113 DDI_PROP_HW_DEF | DDI_PROP_TYPE_INT | DDI_PROP_DONTSLEEP, 114 name, data, nelements, ddi_prop_fm_encode_ints)); 115 } 116 117 int 118 ndi_prop_update_int64_array(dev_t match_dev, dev_info_t *dip, 119 char *name, int64_t *data, uint_t nelements) 120 { 121 return (ddi_prop_update_common(match_dev, dip, 122 DDI_PROP_HW_DEF | DDI_PROP_TYPE_INT64 | DDI_PROP_DONTSLEEP, 123 name, data, nelements, ddi_prop_fm_encode_int64)); 124 } 125 126 int 127 ndi_prop_update_string(dev_t match_dev, dev_info_t *dip, 128 char *name, char *data) 129 { 130 return (ddi_prop_update_common(match_dev, dip, 131 DDI_PROP_HW_DEF | DDI_PROP_TYPE_STRING | DDI_PROP_DONTSLEEP, 132 name, &data, 1, ddi_prop_fm_encode_string)); 133 } 134 135 int 136 ndi_prop_update_string_array(dev_t match_dev, dev_info_t *dip, 137 char *name, char **data, uint_t nelements) 138 { 139 return (ddi_prop_update_common(match_dev, dip, 140 DDI_PROP_HW_DEF | DDI_PROP_TYPE_STRING | DDI_PROP_DONTSLEEP, 141 name, data, nelements, 142 ddi_prop_fm_encode_strings)); 143 } 144 145 int 146 ndi_prop_update_byte_array(dev_t match_dev, dev_info_t *dip, 147 char *name, uchar_t *data, uint_t nelements) 148 { 149 if (nelements == 0) 150 return (DDI_PROP_INVAL_ARG); 151 152 return (ddi_prop_update_common(match_dev, dip, 153 DDI_PROP_HW_DEF | DDI_PROP_TYPE_BYTE | DDI_PROP_DONTSLEEP, 154 name, data, nelements, ddi_prop_fm_encode_bytes)); 155 } 156 157 int 158 ndi_prop_remove(dev_t dev, dev_info_t *dip, char *name) 159 { 160 return (ddi_prop_remove_common(dev, dip, name, DDI_PROP_HW_DEF)); 161 } 162 163 void 164 ndi_prop_remove_all(dev_info_t *dip) 165 { 166 ddi_prop_remove_all_common(dip, (int)DDI_PROP_HW_DEF); 167 } 168 169 /* 170 * Post an event notification to nexus driver responsible for handling 171 * the event. The responsible nexus is defined in the cookie passed in as 172 * the third parameter. 173 * The dip parameter is an artifact of an older implementation in which all 174 * requests to remove an eventcall would bubble up the tree. Today, this 175 * parameter is ignored. 176 * Input Parameters: 177 * dip - Ignored. 178 * rdip - device driver posting the event 179 * cookie - valid ddi_eventcookie_t, obtained by caller prior to 180 * invocation of this routine 181 * impl_data - used by framework 182 */ 183 /*ARGSUSED*/ 184 int 185 ndi_post_event(dev_info_t *dip, dev_info_t *rdip, 186 ddi_eventcookie_t cookie, void *impl_data) 187 { 188 dev_info_t *ddip; 189 190 ASSERT(cookie); 191 ddip = NDI_EVENT_DDIP(cookie); 192 193 /* 194 * perform sanity checks. These conditions should never be true. 195 */ 196 197 ASSERT(DEVI(ddip)->devi_ops->devo_bus_ops != NULL); 198 ASSERT(DEVI(ddip)->devi_ops->devo_bus_ops->busops_rev >= BUSO_REV_6); 199 ASSERT(DEVI(ddip)->devi_ops->devo_bus_ops->bus_post_event != NULL); 200 201 /* 202 * post the event to the responsible ancestor 203 */ 204 return ((*(DEVI(ddip)->devi_ops->devo_bus_ops->bus_post_event)) 205 (ddip, rdip, cookie, impl_data)); 206 } 207 208 /* 209 * Calls the bus nexus driver's implementation of the 210 * (*bus_remove_eventcall)() interface. 211 */ 212 int 213 ndi_busop_remove_eventcall(dev_info_t *ddip, ddi_callback_id_t id) 214 { 215 216 ASSERT(id); 217 /* check for a correct revno before calling up the device tree. */ 218 ASSERT(DEVI(ddip)->devi_ops->devo_bus_ops != NULL); 219 ASSERT(DEVI(ddip)->devi_ops->devo_bus_ops->busops_rev >= BUSO_REV_6); 220 221 if (DEVI(ddip)->devi_ops->devo_bus_ops->bus_remove_eventcall == NULL) 222 return (DDI_FAILURE); 223 224 /* 225 * request responsible nexus to remove the eventcall 226 */ 227 return ((*(DEVI(ddip)->devi_ops->devo_bus_ops->bus_remove_eventcall)) 228 (ddip, id)); 229 } 230 231 /* 232 * Calls the bus nexus driver's implementation of the 233 * (*bus_add_eventcall)() interface. The dip parameter is an 234 * artifact of an older implementation in which all requests to 235 * add an eventcall would bubble up the tree. Today, this parameter is 236 * ignored. 237 */ 238 /*ARGSUSED*/ 239 int 240 ndi_busop_add_eventcall(dev_info_t *dip, dev_info_t *rdip, 241 ddi_eventcookie_t cookie, void (*callback)(), void *arg, 242 ddi_callback_id_t *cb_id) 243 { 244 dev_info_t *ddip = (dev_info_t *)NDI_EVENT_DDIP(cookie); 245 246 /* 247 * check for a correct revno before calling up the device tree. 248 */ 249 ASSERT(DEVI(ddip)->devi_ops->devo_bus_ops != NULL); 250 ASSERT(DEVI(ddip)->devi_ops->devo_bus_ops->busops_rev >= BUSO_REV_6); 251 252 if (DEVI(ddip)->devi_ops->devo_bus_ops->bus_add_eventcall == NULL) 253 return (DDI_FAILURE); 254 255 /* 256 * request responsible ancestor to add the eventcall 257 */ 258 return ((*(DEVI(ddip)->devi_ops->devo_bus_ops->bus_add_eventcall)) 259 (ddip, rdip, cookie, callback, arg, cb_id)); 260 } 261 262 /* 263 * Calls the bus nexus driver's implementation of the 264 * (*bus_get_eventcookie)() interface up the device tree hierarchy. 265 */ 266 int 267 ndi_busop_get_eventcookie(dev_info_t *dip, dev_info_t *rdip, char *name, 268 ddi_eventcookie_t *event_cookiep) 269 { 270 dev_info_t *pdip = (dev_info_t *)DEVI(dip)->devi_parent; 271 272 /* Can not be called from rootnex. */ 273 ASSERT(pdip); 274 275 /* 276 * check for a correct revno before calling up the device tree. 277 */ 278 ASSERT(DEVI(pdip)->devi_ops->devo_bus_ops != NULL); 279 280 if ((DEVI(pdip)->devi_ops->devo_bus_ops->busops_rev < BUSO_REV_6) || 281 (DEVI(pdip)->devi_ops->devo_bus_ops->bus_get_eventcookie == NULL)) { 282 #ifdef DEBUG 283 if ((DEVI(pdip)->devi_ops->devo_bus_ops->busops_rev >= 284 BUSO_REV_3) && 285 (DEVI(pdip)->devi_ops->devo_bus_ops->bus_get_eventcookie)) { 286 cmn_err(CE_WARN, 287 "Warning: %s%d busops_rev=%d no longer supported" 288 " by the NDI event framework.\nBUSO_REV_6 or " 289 "greater must be used.", 290 DEVI(pdip)->devi_binding_name, 291 DEVI(pdip)->devi_instance, 292 DEVI(pdip)->devi_ops->devo_bus_ops->busops_rev); 293 } 294 #endif /* DEBUG */ 295 296 return (ndi_busop_get_eventcookie(pdip, rdip, name, 297 event_cookiep)); 298 } 299 300 return ((*(DEVI(pdip)->devi_ops->devo_bus_ops->bus_get_eventcookie)) 301 (pdip, rdip, name, event_cookiep)); 302 } 303 304 /* 305 * Copy in the devctl IOCTL data and return a handle to 306 * the data. 307 */ 308 int 309 ndi_dc_allochdl(void *iocarg, struct devctl_iocdata **rdcp) 310 { 311 struct devctl_iocdata *dcp; 312 char *cpybuf; 313 314 ASSERT(rdcp != NULL); 315 316 dcp = kmem_zalloc(sizeof (*dcp), KM_SLEEP); 317 318 if (get_udatamodel() == DATAMODEL_NATIVE) { 319 if (copyin(iocarg, dcp, sizeof (*dcp)) != 0) { 320 kmem_free(dcp, sizeof (*dcp)); 321 return (NDI_FAULT); 322 } 323 } 324 #ifdef _SYSCALL32_IMPL 325 else { 326 struct devctl_iocdata32 dcp32; 327 328 if (copyin(iocarg, &dcp32, sizeof (dcp32)) != 0) { 329 kmem_free(dcp, sizeof (*dcp)); 330 return (NDI_FAULT); 331 } 332 dcp->cmd = (uint_t)dcp32.cmd; 333 dcp->flags = (uint_t)dcp32.flags; 334 dcp->cpyout_buf = (uint_t *)(uintptr_t)dcp32.cpyout_buf; 335 dcp->nvl_user = (nvlist_t *)(uintptr_t)dcp32.nvl_user; 336 dcp->nvl_usersz = (size_t)dcp32.nvl_usersz; 337 dcp->c_nodename = (char *)(uintptr_t)dcp32.c_nodename; 338 dcp->c_unitaddr = (char *)(uintptr_t)dcp32.c_unitaddr; 339 } 340 #endif 341 if (dcp->c_nodename != NULL) { 342 cpybuf = kmem_alloc(MAXNAMELEN, KM_SLEEP); 343 if (copyinstr(dcp->c_nodename, cpybuf, MAXNAMELEN, 0) != 0) { 344 kmem_free(cpybuf, MAXNAMELEN); 345 kmem_free(dcp, sizeof (*dcp)); 346 return (NDI_FAULT); 347 } 348 cpybuf[MAXNAMELEN - 1] = '\0'; 349 dcp->c_nodename = cpybuf; 350 } 351 352 if (dcp->c_unitaddr != NULL) { 353 cpybuf = kmem_alloc(MAXNAMELEN, KM_SLEEP); 354 if (copyinstr(dcp->c_unitaddr, cpybuf, MAXNAMELEN, 0) != 0) { 355 kmem_free(cpybuf, MAXNAMELEN); 356 if (dcp->c_nodename != NULL) 357 kmem_free(dcp->c_nodename, MAXNAMELEN); 358 kmem_free(dcp, sizeof (*dcp)); 359 return (NDI_FAULT); 360 } 361 cpybuf[MAXNAMELEN - 1] = '\0'; 362 dcp->c_unitaddr = cpybuf; 363 } 364 365 /* 366 * copyin and unpack a user defined nvlist if one was passed 367 */ 368 if (dcp->nvl_user != NULL) { 369 if (dcp->nvl_usersz == 0) { 370 if (dcp->c_nodename != NULL) 371 kmem_free(dcp->c_nodename, MAXNAMELEN); 372 if (dcp->c_unitaddr != NULL) 373 kmem_free(dcp->c_unitaddr, MAXNAMELEN); 374 kmem_free(dcp, sizeof (*dcp)); 375 return (NDI_FAILURE); 376 } 377 cpybuf = kmem_alloc(dcp->nvl_usersz, KM_SLEEP); 378 if (copyin(dcp->nvl_user, cpybuf, dcp->nvl_usersz) != 0) { 379 kmem_free(cpybuf, dcp->nvl_usersz); 380 if (dcp->c_nodename != NULL) 381 kmem_free(dcp->c_nodename, MAXNAMELEN); 382 if (dcp->c_unitaddr != NULL) 383 kmem_free(dcp->c_unitaddr, MAXNAMELEN); 384 kmem_free(dcp, sizeof (*dcp)); 385 return (NDI_FAULT); 386 } 387 388 if (nvlist_unpack(cpybuf, dcp->nvl_usersz, &dcp->nvl_user, 389 KM_SLEEP)) { 390 kmem_free(cpybuf, dcp->nvl_usersz); 391 if (dcp->c_nodename != NULL) 392 kmem_free(dcp->c_nodename, MAXNAMELEN); 393 if (dcp->c_unitaddr != NULL) 394 kmem_free(dcp->c_unitaddr, MAXNAMELEN); 395 kmem_free(dcp, sizeof (*dcp)); 396 return (NDI_FAULT); 397 } 398 /* 399 * free the buffer containing the packed nvlist 400 */ 401 kmem_free(cpybuf, dcp->nvl_usersz); 402 403 } 404 405 *rdcp = dcp; 406 return (NDI_SUCCESS); 407 } 408 409 /* 410 * free all space allocated to a handle. 411 */ 412 void 413 ndi_dc_freehdl(struct devctl_iocdata *dcp) 414 { 415 ASSERT(dcp != NULL); 416 417 if (dcp->c_nodename != NULL) 418 kmem_free(dcp->c_nodename, MAXNAMELEN); 419 420 if (dcp->c_unitaddr != NULL) 421 kmem_free(dcp->c_unitaddr, MAXNAMELEN); 422 423 if (dcp->nvl_user != NULL) 424 nvlist_free(dcp->nvl_user); 425 426 kmem_free(dcp, sizeof (*dcp)); 427 } 428 429 char * 430 ndi_dc_getname(struct devctl_iocdata *dcp) 431 { 432 ASSERT(dcp != NULL); 433 return (dcp->c_nodename); 434 435 } 436 437 char * 438 ndi_dc_getaddr(struct devctl_iocdata *dcp) 439 { 440 ASSERT(dcp != NULL); 441 return (dcp->c_unitaddr); 442 } 443 444 nvlist_t * 445 ndi_dc_get_ap_data(struct devctl_iocdata *dcp) 446 { 447 ASSERT(dcp != NULL); 448 449 return (dcp->nvl_user); 450 } 451 452 /* 453 * Transition the child named by "devname@devaddr" to the online state. 454 * For use by a driver's DEVCTL_DEVICE_ONLINE handler. 455 */ 456 int 457 ndi_devctl_device_online(dev_info_t *dip, struct devctl_iocdata *dcp, 458 uint_t flags) 459 { 460 int rval; 461 char *name; 462 dev_info_t *rdip; 463 464 if (ndi_dc_getname(dcp) == NULL || ndi_dc_getaddr(dcp) == NULL) 465 return (EINVAL); 466 467 name = kmem_alloc(MAXNAMELEN, KM_SLEEP); 468 (void) snprintf(name, MAXNAMELEN, "%s@%s", 469 ndi_dc_getname(dcp), ndi_dc_getaddr(dcp)); 470 471 if ((rval = ndi_devi_config_one(dip, name, &rdip, 472 flags | NDI_DEVI_ONLINE | NDI_CONFIG)) == NDI_SUCCESS) { 473 ndi_rele_devi(rdip); 474 475 /* 476 * Invalidate devfs cached directory contents. For the checks 477 * in the "if" condition see the comment in ndi_devi_online(). 478 */ 479 if (i_ddi_node_state(dip) == DS_READY && !DEVI_BUSY_OWNED(dip)) 480 (void) devfs_clean(dip, NULL, 0); 481 482 } else if (rval == NDI_BUSY) { 483 rval = EBUSY; 484 } else if (rval == NDI_FAILURE) { 485 rval = EIO; 486 } 487 488 NDI_DEBUG(flags, (CE_CONT, "%s%d: online: %s: %s\n", 489 ddi_driver_name(dip), ddi_get_instance(dip), name, 490 ((rval == NDI_SUCCESS) ? "ok" : "failed"))); 491 492 kmem_free(name, MAXNAMELEN); 493 494 return (rval); 495 } 496 497 /* 498 * Transition the child named by "devname@devaddr" to the offline state. 499 * For use by a driver's DEVCTL_DEVICE_OFFLINE handler. 500 */ 501 int 502 ndi_devctl_device_offline(dev_info_t *dip, struct devctl_iocdata *dcp, 503 uint_t flags) 504 { 505 int rval; 506 char *name; 507 508 if (ndi_dc_getname(dcp) == NULL || ndi_dc_getaddr(dcp) == NULL) 509 return (EINVAL); 510 511 name = kmem_alloc(MAXNAMELEN, KM_SLEEP); 512 (void) snprintf(name, MAXNAMELEN, "%s@%s", 513 ndi_dc_getname(dcp), ndi_dc_getaddr(dcp)); 514 515 rval = devfs_clean(dip, name, DV_CLEAN_FORCE); 516 if (rval) { 517 rval = EBUSY; 518 } else { 519 rval = ndi_devi_unconfig_one(dip, name, NULL, 520 flags | NDI_DEVI_OFFLINE); 521 522 if (rval == NDI_BUSY) { 523 rval = EBUSY; 524 } else if (rval == NDI_FAILURE) { 525 rval = EIO; 526 } 527 } 528 529 NDI_DEBUG(flags, (CE_CONT, "%s%d: offline: %s: %s\n", 530 ddi_driver_name(dip), ddi_get_instance(dip), name, 531 (rval == NDI_SUCCESS) ? "ok" : "failed")); 532 533 kmem_free(name, MAXNAMELEN); 534 535 return (rval); 536 } 537 538 /* 539 * Remove the child named by "devname@devaddr". 540 * For use by a driver's DEVCTL_DEVICE_REMOVE handler. 541 */ 542 int 543 ndi_devctl_device_remove(dev_info_t *dip, struct devctl_iocdata *dcp, 544 uint_t flags) 545 { 546 int rval; 547 char *name; 548 549 if (ndi_dc_getname(dcp) == NULL || ndi_dc_getaddr(dcp) == NULL) 550 return (EINVAL); 551 552 name = kmem_alloc(MAXNAMELEN, KM_SLEEP); 553 (void) snprintf(name, MAXNAMELEN, "%s@%s", 554 ndi_dc_getname(dcp), ndi_dc_getaddr(dcp)); 555 556 (void) devfs_clean(dip, name, DV_CLEAN_FORCE); 557 558 rval = ndi_devi_unconfig_one(dip, name, NULL, flags | NDI_DEVI_REMOVE); 559 560 if (rval == NDI_BUSY) { 561 rval = EBUSY; 562 } else if (rval == NDI_FAILURE) { 563 rval = EIO; 564 } 565 566 NDI_DEBUG(flags, (CE_CONT, "%s%d: remove: %s: %s\n", 567 ddi_driver_name(dip), ddi_get_instance(dip), name, 568 (rval == NDI_SUCCESS) ? "ok" : "failed")); 569 570 kmem_free(name, MAXNAMELEN); 571 572 return (rval); 573 } 574 575 /* 576 * Return devctl state of the child named by "name@addr". 577 * For use by a driver's DEVCTL_DEVICE_GETSTATE handler. 578 */ 579 int 580 ndi_devctl_device_getstate(dev_info_t *parent, struct devctl_iocdata *dcp, 581 uint_t *state) 582 { 583 dev_info_t *dip; 584 char *name, *addr; 585 char *devname; 586 int devnamelen; 587 int circ; 588 589 if (parent == NULL || 590 ((name = ndi_dc_getname(dcp)) == NULL) || 591 ((addr = ndi_dc_getaddr(dcp)) == NULL)) 592 return (NDI_FAILURE); 593 594 devnamelen = strlen(name) + strlen(addr) + 2; 595 devname = kmem_alloc(devnamelen, KM_SLEEP); 596 if (strlen(addr) > 0) { 597 (void) snprintf(devname, devnamelen, "%s@%s", name, addr); 598 } else { 599 (void) snprintf(devname, devnamelen, "%s", name); 600 } 601 602 ndi_devi_enter(parent, &circ); 603 604 dip = ndi_devi_findchild(parent, devname); 605 kmem_free(devname, devnamelen); 606 607 if (dip == NULL) { 608 ndi_devi_exit(parent, circ); 609 return (NDI_FAILURE); 610 } 611 612 mutex_enter(&(DEVI(dip)->devi_lock)); 613 if (DEVI_IS_DEVICE_OFFLINE(dip)) { 614 *state = DEVICE_OFFLINE; 615 } else if (DEVI_IS_DEVICE_DOWN(dip)) { 616 *state = DEVICE_DOWN; 617 } else { 618 *state = DEVICE_ONLINE; 619 if (devi_stillreferenced(dip) == DEVI_REFERENCED) 620 *state |= DEVICE_BUSY; 621 } 622 623 mutex_exit(&(DEVI(dip)->devi_lock)); 624 ndi_devi_exit(parent, circ); 625 626 return (NDI_SUCCESS); 627 } 628 629 /* 630 * return the current state of the device "dip" 631 * 632 * recommend using ndi_devctl_ioctl() or 633 * ndi_devctl_device_getstate() instead 634 */ 635 int 636 ndi_dc_return_dev_state(dev_info_t *dip, struct devctl_iocdata *dcp) 637 { 638 dev_info_t *pdip; 639 uint_t devstate = 0; 640 int circ; 641 642 if ((dip == NULL) || (dcp == NULL)) 643 return (NDI_FAILURE); 644 645 pdip = ddi_get_parent(dip); 646 647 ndi_devi_enter(pdip, &circ); 648 mutex_enter(&(DEVI(dip)->devi_lock)); 649 if (DEVI_IS_DEVICE_OFFLINE(dip)) { 650 devstate = DEVICE_OFFLINE; 651 } else if (DEVI_IS_DEVICE_DOWN(dip)) { 652 devstate = DEVICE_DOWN; 653 } else { 654 devstate = DEVICE_ONLINE; 655 if (devi_stillreferenced(dip) == DEVI_REFERENCED) 656 devstate |= DEVICE_BUSY; 657 } 658 659 mutex_exit(&(DEVI(dip)->devi_lock)); 660 ndi_devi_exit(pdip, circ); 661 662 if (copyout(&devstate, dcp->cpyout_buf, sizeof (uint_t)) != 0) 663 return (NDI_FAULT); 664 665 return (NDI_SUCCESS); 666 } 667 668 /* 669 * Return device's bus state 670 * For use by a driver's DEVCTL_BUS_GETSTATE handler. 671 */ 672 int 673 ndi_devctl_bus_getstate(dev_info_t *dip, struct devctl_iocdata *dcp, 674 uint_t *state) 675 { 676 if ((dip == NULL) || (dcp == NULL)) 677 return (NDI_FAILURE); 678 679 return (ndi_get_bus_state(dip, state)); 680 } 681 682 /* 683 * Generic devctl ioctl handler 684 */ 685 int 686 ndi_devctl_ioctl(dev_info_t *dip, int cmd, intptr_t arg, int mode, uint_t flags) 687 { 688 _NOTE(ARGUNUSED(mode)) 689 struct devctl_iocdata *dcp; 690 uint_t state; 691 int rval = ENOTTY; 692 693 /* 694 * read devctl ioctl data 695 */ 696 if (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS) 697 return (EFAULT); 698 699 switch (cmd) { 700 701 case DEVCTL_BUS_GETSTATE: 702 rval = ndi_devctl_bus_getstate(dip, dcp, &state); 703 if (rval == NDI_SUCCESS) { 704 if (copyout(&state, dcp->cpyout_buf, 705 sizeof (uint_t)) != 0) 706 rval = NDI_FAULT; 707 } 708 break; 709 710 case DEVCTL_DEVICE_ONLINE: 711 rval = ndi_devctl_device_online(dip, dcp, flags); 712 break; 713 714 case DEVCTL_DEVICE_OFFLINE: 715 rval = ndi_devctl_device_offline(dip, dcp, flags); 716 break; 717 718 case DEVCTL_DEVICE_GETSTATE: 719 rval = ndi_devctl_device_getstate(dip, dcp, &state); 720 if (rval == NDI_SUCCESS) { 721 if (copyout(&state, dcp->cpyout_buf, 722 sizeof (uint_t)) != 0) 723 rval = NDI_FAULT; 724 } 725 break; 726 727 case DEVCTL_DEVICE_REMOVE: 728 rval = ndi_devctl_device_remove(dip, dcp, flags); 729 break; 730 731 case DEVCTL_BUS_DEV_CREATE: 732 rval = ndi_dc_devi_create(dcp, dip, 0, NULL); 733 break; 734 735 /* 736 * ioctls for which a generic implementation makes no sense 737 */ 738 case DEVCTL_BUS_RESET: 739 case DEVCTL_BUS_RESETALL: 740 case DEVCTL_DEVICE_RESET: 741 case DEVCTL_AP_CONNECT: 742 case DEVCTL_AP_DISCONNECT: 743 case DEVCTL_AP_INSERT: 744 case DEVCTL_AP_REMOVE: 745 case DEVCTL_AP_CONFIGURE: 746 case DEVCTL_AP_UNCONFIGURE: 747 case DEVCTL_AP_GETSTATE: 748 case DEVCTL_AP_CONTROL: 749 case DEVCTL_BUS_QUIESCE: 750 case DEVCTL_BUS_UNQUIESCE: 751 rval = ENOTSUP; 752 break; 753 } 754 755 ndi_dc_freehdl(dcp); 756 return (rval); 757 } 758 759 /* 760 * Copyout the state of the Attachment Point "ap" to the requesting 761 * user process. 762 */ 763 int 764 ndi_dc_return_ap_state(devctl_ap_state_t *ap, struct devctl_iocdata *dcp) 765 { 766 if ((ap == NULL) || (dcp == NULL)) 767 return (NDI_FAILURE); 768 769 770 if (get_udatamodel() == DATAMODEL_NATIVE) { 771 if (copyout(ap, dcp->cpyout_buf, 772 sizeof (devctl_ap_state_t)) != 0) 773 return (NDI_FAULT); 774 } 775 #ifdef _SYSCALL32_IMPL 776 else { 777 struct devctl_ap_state32 ap_state32; 778 779 ap_state32.ap_rstate = ap->ap_rstate; 780 ap_state32.ap_ostate = ap->ap_ostate; 781 ap_state32.ap_condition = ap->ap_condition; 782 ap_state32.ap_error_code = ap->ap_error_code; 783 ap_state32.ap_in_transition = ap->ap_in_transition; 784 ap_state32.ap_last_change = (time32_t)ap->ap_last_change; 785 if (copyout(&ap_state32, dcp->cpyout_buf, 786 sizeof (devctl_ap_state32_t)) != 0) 787 return (NDI_FAULT); 788 } 789 #endif 790 791 return (NDI_SUCCESS); 792 } 793 794 /* 795 * Copyout the bus state of the bus nexus device "dip" to the requesting 796 * user process. 797 */ 798 int 799 ndi_dc_return_bus_state(dev_info_t *dip, struct devctl_iocdata *dcp) 800 { 801 uint_t devstate = 0; 802 803 if ((dip == NULL) || (dcp == NULL)) 804 return (NDI_FAILURE); 805 806 if (ndi_get_bus_state(dip, &devstate) != NDI_SUCCESS) 807 return (NDI_FAILURE); 808 809 if (copyout(&devstate, dcp->cpyout_buf, sizeof (uint_t)) != 0) 810 return (NDI_FAULT); 811 812 return (NDI_SUCCESS); 813 } 814 815 static int 816 i_dc_devi_create(struct devctl_iocdata *, dev_info_t *, dev_info_t **); 817 818 /* 819 * create a child device node given the property definitions 820 * supplied by the userland process 821 */ 822 int 823 ndi_dc_devi_create(struct devctl_iocdata *dcp, dev_info_t *pdip, int flags, 824 dev_info_t **rdip) 825 { 826 dev_info_t *cdip; 827 int rv, circular = 0; 828 char devnm[MAXNAMELEN]; 829 int nmlen; 830 831 /* 832 * The child device may have been pre-constructed by an earlier 833 * call to this function with the flag DEVCTL_CONSTRUCT set. 834 */ 835 836 if ((cdip = (rdip != NULL) ? *rdip : NULL) == NULL) 837 if ((rv = i_dc_devi_create(dcp, pdip, &cdip)) != 0) 838 return (rv); 839 840 ASSERT(cdip != NULL); 841 842 /* 843 * Return the device node partially constructed if the 844 * DEVCTL_CONSTRUCT flag is set. 845 */ 846 if (flags & DEVCTL_CONSTRUCT) { 847 if (rdip == NULL) { 848 (void) ndi_devi_free(cdip); 849 return (EINVAL); 850 } 851 *rdip = cdip; 852 return (0); 853 } 854 855 /* 856 * Bring the node up to a named but OFFLINE state. The calling 857 * application will need to manage the node from here on. 858 */ 859 if (dcp->flags & DEVCTL_OFFLINE) { 860 /* 861 * hand set the OFFLINE flag to prevent any asynchronous 862 * autoconfiguration operations from attaching this node. 863 */ 864 DEVI(cdip)->devi_state |= DEVI_DEVICE_OFFLINE; 865 rv = ndi_devi_bind_driver(cdip, flags); 866 if (rv != NDI_SUCCESS) { 867 (void) ndi_devi_offline(cdip, NDI_DEVI_REMOVE); 868 return (ENXIO); 869 } 870 871 /* 872 * remove the dev_info node if it failed to bind to a 873 * driver above. 874 */ 875 if (i_ddi_node_state(cdip) < DS_BOUND) { 876 (void) ndi_devi_offline(cdip, NDI_DEVI_REMOVE); 877 return (ENXIO); 878 } 879 880 /* 881 * add the node to the per-driver list and INITCHILD it 882 * to give it a name. 883 */ 884 ndi_devi_enter(pdip, &circular); 885 if ((rv = ddi_initchild(pdip, cdip)) != DDI_SUCCESS) { 886 (void) ndi_devi_offline(cdip, NDI_DEVI_REMOVE); 887 ndi_devi_exit(pdip, circular); 888 return (EINVAL); 889 } 890 ndi_devi_exit(pdip, circular); 891 892 } else { 893 /* 894 * Attempt to bring the device ONLINE. If the request to 895 * fails, remove the dev_info node. 896 */ 897 if (ndi_devi_online(cdip, NDI_ONLINE_ATTACH) != NDI_SUCCESS) { 898 (void) ndi_devi_offline(cdip, NDI_DEVI_REMOVE); 899 return (ENXIO); 900 } 901 902 /* 903 * if the node was successfully added but there was 904 * no driver available for the device, remove the node 905 */ 906 if (i_ddi_node_state(cdip) < DS_BOUND) { 907 (void) ndi_devi_offline(cdip, NDI_DEVI_REMOVE); 908 return (ENODEV); 909 } 910 } 911 912 /* 913 * return a handle to the child device 914 * copy out the name of the newly attached child device if 915 * the IOCTL request has provided a copyout buffer. 916 */ 917 if (rdip != NULL) 918 *rdip = cdip; 919 920 if (dcp->cpyout_buf == NULL) 921 return (0); 922 923 ASSERT(ddi_node_name(cdip) != NULL); 924 ASSERT(ddi_get_name_addr(cdip) != NULL); 925 926 nmlen = snprintf(devnm, MAXNAMELEN, "%s@%s", 927 ddi_node_name(cdip), ddi_get_name_addr(cdip)); 928 929 if (copyout(&devnm, dcp->cpyout_buf, nmlen) != 0) { 930 (void) ndi_devi_offline(cdip, NDI_DEVI_REMOVE); 931 return (EFAULT); 932 } 933 return (0); 934 } 935 936 static int 937 i_dc_devi_create(struct devctl_iocdata *dcp, dev_info_t *pdip, 938 dev_info_t **rdip) 939 { 940 941 dev_info_t *cdip; 942 char *cname = NULL; 943 nvlist_t *nvlp = dcp->nvl_user; 944 nvpair_t *npp; 945 char *np; 946 int rv = 0; 947 948 ASSERT(rdip != NULL && *rdip == NULL); 949 950 if ((nvlp == NULL) || 951 (nvlist_lookup_string(nvlp, DC_DEVI_NODENAME, &cname) != 0)) 952 return (EINVAL); 953 954 /* 955 * construct a new dev_info node with a user-provided nodename 956 */ 957 ndi_devi_alloc_sleep(pdip, cname, (dnode_t)DEVI_SID_NODEID, &cdip); 958 959 /* 960 * create hardware properties for each member in the property 961 * list. 962 */ 963 for (npp = nvlist_next_nvpair(nvlp, NULL); (npp != NULL && !rv); 964 npp = nvlist_next_nvpair(nvlp, npp)) { 965 966 np = nvpair_name(npp); 967 968 /* 969 * skip the nodename property 970 */ 971 if (strcmp(np, DC_DEVI_NODENAME) == 0) 972 continue; 973 974 switch (nvpair_type(npp)) { 975 976 case DATA_TYPE_INT32: { 977 int32_t prop_val; 978 979 if ((rv = nvpair_value_int32(npp, &prop_val)) != 0) 980 break; 981 982 (void) ndi_prop_update_int(DDI_DEV_T_NONE, cdip, np, 983 (int)prop_val); 984 break; 985 } 986 987 case DATA_TYPE_STRING: { 988 char *prop_val; 989 990 if ((rv = nvpair_value_string(npp, &prop_val)) != 0) 991 break; 992 993 (void) ndi_prop_update_string(DDI_DEV_T_NONE, cdip, 994 np, prop_val); 995 break; 996 } 997 998 case DATA_TYPE_BYTE_ARRAY: { 999 uchar_t *val; 1000 uint_t nelms; 1001 1002 if ((rv = nvpair_value_byte_array(npp, &val, 1003 &nelms)) != 0) 1004 break; 1005 1006 (void) ndi_prop_update_byte_array(DDI_DEV_T_NONE, 1007 cdip, np, (uchar_t *)val, nelms); 1008 break; 1009 } 1010 1011 case DATA_TYPE_INT32_ARRAY: { 1012 int32_t *val; 1013 uint_t nelms; 1014 1015 if ((rv = nvpair_value_int32_array(npp, &val, 1016 &nelms)) != 0) 1017 break; 1018 1019 (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, 1020 cdip, np, val, nelms); 1021 break; 1022 } 1023 1024 case DATA_TYPE_STRING_ARRAY: { 1025 char **val; 1026 uint_t nelms; 1027 1028 if ((rv = nvpair_value_string_array(npp, &val, 1029 &nelms)) != 0) 1030 break; 1031 1032 (void) ndi_prop_update_string_array(DDI_DEV_T_NONE, 1033 cdip, np, val, nelms); 1034 break; 1035 } 1036 1037 /* 1038 * unsupported property data type 1039 */ 1040 default: 1041 rv = EINVAL; 1042 } 1043 } 1044 1045 /* 1046 * something above failed 1047 * destroy the partially child device and abort the request 1048 */ 1049 if (rv != 0) { 1050 (void) ndi_devi_free(cdip); 1051 return (rv); 1052 } 1053 1054 *rdip = cdip; 1055 return (0); 1056 } 1057 1058 /* 1059 * return current soft bus state of bus nexus "dip" 1060 */ 1061 int 1062 ndi_get_bus_state(dev_info_t *dip, uint_t *rstate) 1063 { 1064 if (dip == NULL || rstate == NULL) 1065 return (NDI_FAILURE); 1066 1067 if (DEVI(dip)->devi_ops->devo_bus_ops == NULL) 1068 return (NDI_FAILURE); 1069 1070 mutex_enter(&(DEVI(dip)->devi_lock)); 1071 if (DEVI_IS_BUS_QUIESCED(dip)) 1072 *rstate = BUS_QUIESCED; 1073 else if (DEVI_IS_BUS_DOWN(dip)) 1074 *rstate = BUS_SHUTDOWN; 1075 else 1076 *rstate = BUS_ACTIVE; 1077 mutex_exit(&(DEVI(dip)->devi_lock)); 1078 return (NDI_SUCCESS); 1079 } 1080 1081 /* 1082 * Set the soft state of bus nexus "dip" 1083 */ 1084 int 1085 ndi_set_bus_state(dev_info_t *dip, uint_t state) 1086 { 1087 int rv = NDI_SUCCESS; 1088 1089 if (dip == NULL) 1090 return (NDI_FAILURE); 1091 1092 mutex_enter(&(DEVI(dip)->devi_lock)); 1093 1094 switch (state) { 1095 case BUS_QUIESCED: 1096 DEVI_SET_BUS_QUIESCE(dip); 1097 break; 1098 1099 case BUS_ACTIVE: 1100 DEVI_SET_BUS_ACTIVE(dip); 1101 DEVI_SET_BUS_UP(dip); 1102 break; 1103 1104 case BUS_SHUTDOWN: 1105 DEVI_SET_BUS_DOWN(dip); 1106 break; 1107 1108 default: 1109 rv = NDI_FAILURE; 1110 } 1111 1112 mutex_exit(&(DEVI(dip)->devi_lock)); 1113 return (rv); 1114 } 1115 1116 /* 1117 * These dummy functions are obsolete and may be removed. 1118 * Retained for existing driver compatibility only. 1119 * Drivers should be fixed not to use these functions. 1120 * Don't write new code using these obsolete interfaces. 1121 */ 1122 /*ARGSUSED*/ 1123 void 1124 i_ndi_block_device_tree_changes(uint_t *lkcnt) /* obsolete */ 1125 { 1126 /* obsolete dummy function */ 1127 } 1128 1129 /*ARGSUSED*/ 1130 void 1131 i_ndi_allow_device_tree_changes(uint_t lkcnt) /* obsolete */ 1132 { 1133 /* obsolete dummy function */ 1134 } 1135 1136 /* 1137 * Single thread entry into per-driver list 1138 */ 1139 /*ARGSUSED*/ 1140 void 1141 e_ddi_enter_driver_list(struct devnames *dnp, int *listcnt) /* obsolete */ 1142 { 1143 /* obsolete dummy function */ 1144 } 1145 1146 /* 1147 * release the per-driver list 1148 */ 1149 /*ARGSUSED*/ 1150 void 1151 e_ddi_exit_driver_list(struct devnames *dnp, int listcnt) /* obsolete */ 1152 { 1153 /* obsolete dummy function */ 1154 } 1155 1156 /* 1157 * Attempt to enter driver list 1158 */ 1159 /*ARGSUSED*/ 1160 int 1161 e_ddi_tryenter_driver_list(struct devnames *dnp, int *listcnt) /* obsolete */ 1162 { 1163 return (1); /* obsolete dummy function */ 1164 } 1165 1166 /* 1167 * ndi event handling support functions: 1168 * The NDI event support model is as follows: 1169 * 1170 * The nexus driver defines a set of events using some static structures (so 1171 * these structures can be shared by all instances of the nexus driver). 1172 * The nexus driver allocates an event handle and binds the event set 1173 * to this handle. The nexus driver's event busop functions can just 1174 * call the appropriate NDI event support function using this handle 1175 * as the first argument. 1176 * 1177 * The reasoning for tying events to the device tree is that the entity 1178 * generating the callback will typically be one of the device driver's 1179 * ancestors in the tree. 1180 */ 1181 static int ndi_event_debug = 0; 1182 1183 #ifdef DEBUG 1184 #define NDI_EVENT_DEBUG ndi_event_debug 1185 #endif /* DEBUG */ 1186 1187 /* 1188 * allocate a new ndi event handle 1189 */ 1190 int 1191 ndi_event_alloc_hdl(dev_info_t *dip, ddi_iblock_cookie_t cookie, 1192 ndi_event_hdl_t *handle, uint_t flag) 1193 { 1194 struct ndi_event_hdl *ndi_event_hdl; 1195 1196 ndi_event_hdl = kmem_zalloc(sizeof (struct ndi_event_hdl), 1197 ((flag & NDI_NOSLEEP) ? KM_NOSLEEP : KM_SLEEP)); 1198 1199 if (!ndi_event_hdl) { 1200 return (NDI_FAILURE); 1201 } 1202 1203 ndi_event_hdl->ndi_evthdl_dip = dip; 1204 ndi_event_hdl->ndi_evthdl_iblock_cookie = cookie; 1205 mutex_init(&ndi_event_hdl->ndi_evthdl_mutex, NULL, 1206 MUTEX_DRIVER, (void *)cookie); 1207 1208 mutex_init(&ndi_event_hdl->ndi_evthdl_cb_mutex, NULL, 1209 MUTEX_DRIVER, (void *)cookie); 1210 1211 *handle = (ndi_event_hdl_t)ndi_event_hdl; 1212 1213 return (NDI_SUCCESS); 1214 } 1215 1216 /* 1217 * free the ndi event handle 1218 */ 1219 int 1220 ndi_event_free_hdl(ndi_event_hdl_t handle) 1221 { 1222 struct ndi_event_hdl *ndi_event_hdl = (struct ndi_event_hdl *)handle; 1223 ndi_event_cookie_t *cookie; 1224 ndi_event_cookie_t *free; 1225 1226 ASSERT(handle); 1227 1228 mutex_enter(&ndi_event_hdl->ndi_evthdl_mutex); 1229 mutex_enter(&ndi_event_hdl->ndi_evthdl_cb_mutex); 1230 1231 cookie = ndi_event_hdl->ndi_evthdl_cookie_list; 1232 1233 /* deallocate all defined cookies */ 1234 while (cookie != NULL) { 1235 ASSERT(cookie->callback_list == NULL); 1236 free = cookie; 1237 cookie = cookie->next_cookie; 1238 1239 kmem_free(free, sizeof (ndi_event_cookie_t)); 1240 } 1241 1242 1243 mutex_exit(&ndi_event_hdl->ndi_evthdl_cb_mutex); 1244 mutex_exit(&ndi_event_hdl->ndi_evthdl_mutex); 1245 1246 /* destroy mutexes */ 1247 mutex_destroy(&ndi_event_hdl->ndi_evthdl_mutex); 1248 mutex_destroy(&ndi_event_hdl->ndi_evthdl_cb_mutex); 1249 1250 /* free event handle */ 1251 kmem_free(ndi_event_hdl, sizeof (struct ndi_event_hdl)); 1252 1253 return (NDI_SUCCESS); 1254 } 1255 1256 1257 /* 1258 * ndi_event_bind_set() adds a set of events to the NDI event 1259 * handle. 1260 * 1261 * Events generated by high level interrupts should not 1262 * be mixed in the same event set with events generated by 1263 * normal interrupts or kernel events. 1264 * 1265 * This function can be called multiple times to bind 1266 * additional sets to the event handle. 1267 * However, events generated by high level interrupts cannot 1268 * be bound to a handle that already has bound events generated 1269 * by normal interrupts or from kernel context and vice versa. 1270 */ 1271 int 1272 ndi_event_bind_set(ndi_event_hdl_t handle, 1273 ndi_event_set_t *ndi_events, 1274 uint_t flag) 1275 { 1276 struct ndi_event_hdl *ndi_event_hdl; 1277 ndi_event_cookie_t *next, *prev, *new_cookie; 1278 uint_t i, len; 1279 uint_t dup = 0; 1280 uint_t high_plevels, other_plevels; 1281 ndi_event_definition_t *ndi_event_defs; 1282 1283 int km_flag = ((flag & NDI_NOSLEEP) ? KM_NOSLEEP : KM_SLEEP); 1284 1285 ASSERT(handle); 1286 ASSERT(ndi_events); 1287 1288 /* 1289 * binding must be performed during attach/detach 1290 */ 1291 if (!DEVI_IS_ATTACHING(handle->ndi_evthdl_dip) && 1292 !DEVI_IS_DETACHING(handle->ndi_evthdl_dip)) { 1293 cmn_err(CE_WARN, "ndi_event_bind_set must be called within " 1294 "attach or detach"); 1295 return (NDI_FAILURE); 1296 } 1297 1298 /* 1299 * if it is not the correct version or the event set is 1300 * empty, bail out 1301 */ 1302 if (ndi_events->ndi_events_version != NDI_EVENTS_REV1) 1303 return (NDI_FAILURE); 1304 1305 ndi_event_hdl = (struct ndi_event_hdl *)handle; 1306 ndi_event_defs = ndi_events->ndi_event_defs; 1307 high_plevels = other_plevels = 0; 1308 1309 mutex_enter(&ndi_event_hdl->ndi_evthdl_mutex); 1310 1311 /* check for mixing events at high level with the other types */ 1312 for (i = 0; i < ndi_events->ndi_n_events; i++) { 1313 if (ndi_event_defs[i].ndi_event_plevel == EPL_HIGHLEVEL) { 1314 high_plevels++; 1315 } else { 1316 other_plevels++; 1317 } 1318 } 1319 1320 /* 1321 * bail out if high level events are mixed with other types in this 1322 * event set or the set is incompatible with the set in the handle 1323 */ 1324 if ((high_plevels && other_plevels) || 1325 (other_plevels && ndi_event_hdl->ndi_evthdl_high_plevels) || 1326 (high_plevels && ndi_event_hdl->ndi_evthdl_other_plevels)) { 1327 mutex_exit(&ndi_event_hdl->ndi_evthdl_mutex); 1328 1329 return (NDI_FAILURE); 1330 } 1331 1332 /* 1333 * check for duplicate events in both the existing handle 1334 * and the event set, add events if not duplicates 1335 */ 1336 next = ndi_event_hdl->ndi_evthdl_cookie_list; 1337 for (i = 0; i < ndi_events->ndi_n_events; i++) { 1338 while (next != NULL) { 1339 len = strlen(NDI_EVENT_NAME(next)) + 1; 1340 if (strncmp(NDI_EVENT_NAME(next), 1341 ndi_event_defs[i].ndi_event_name, len) == 0) { 1342 dup = 1; 1343 break; 1344 } 1345 1346 prev = next; 1347 next = next->next_cookie; 1348 } 1349 1350 if (dup == 0) { 1351 new_cookie = kmem_zalloc(sizeof (ndi_event_cookie_t), 1352 km_flag); 1353 1354 if (!new_cookie) 1355 return (NDI_FAILURE); 1356 1357 if (ndi_event_hdl->ndi_evthdl_n_events == 0) { 1358 ndi_event_hdl->ndi_evthdl_cookie_list = 1359 new_cookie; 1360 } else { 1361 prev->next_cookie = new_cookie; 1362 } 1363 1364 ndi_event_hdl->ndi_evthdl_n_events++; 1365 1366 /* 1367 * set up new cookie 1368 */ 1369 new_cookie->definition = &ndi_event_defs[i]; 1370 new_cookie->ddip = ndi_event_hdl->ndi_evthdl_dip; 1371 1372 } else { 1373 /* 1374 * event not added, must correct plevel numbers 1375 */ 1376 if (ndi_event_defs[i].ndi_event_plevel == 1377 EPL_HIGHLEVEL) { 1378 high_plevels--; 1379 } else { 1380 other_plevels--; 1381 } 1382 } 1383 1384 dup = 0; 1385 next = ndi_event_hdl->ndi_evthdl_cookie_list; 1386 prev = NULL; 1387 1388 } 1389 1390 ndi_event_hdl->ndi_evthdl_high_plevels += high_plevels; 1391 ndi_event_hdl->ndi_evthdl_other_plevels += other_plevels; 1392 1393 ASSERT((ndi_event_hdl->ndi_evthdl_high_plevels == 0) || 1394 (ndi_event_hdl->ndi_evthdl_other_plevels == 0)); 1395 1396 #ifdef NDI_EVENT_DEBUG 1397 if (ndi_event_debug) { 1398 ndi_event_dump_hdl(ndi_event_hdl, "ndi_event_bind_set"); 1399 } 1400 #endif /* NDI_EVENT_DEBUG */ 1401 1402 mutex_exit(&ndi_event_hdl->ndi_evthdl_mutex); 1403 1404 return (NDI_SUCCESS); 1405 } 1406 1407 /* 1408 * ndi_event_unbind_set() unbinds a set of events, previously 1409 * bound using ndi_event_bind_set(), from the NDI event 1410 * handle. 1411 * 1412 * This routine will unbind all events in the event set. If an event, 1413 * specified in the event set, is not found in the handle, this 1414 * routine will proceed onto the next member of the set as if the event 1415 * was never specified. 1416 * 1417 * The event set may be a subset of the set of events that 1418 * was previously bound to the handle. For example, events 1419 * can be individually unbound. 1420 * 1421 * An event cannot be unbound if callbacks are still 1422 * registered against the event. 1423 */ 1424 /*ARGSUSED*/ 1425 int 1426 ndi_event_unbind_set(ndi_event_hdl_t handle, ndi_event_set_t *ndi_events, 1427 uint_t flag) 1428 { 1429 ndi_event_definition_t *ndi_event_defs; 1430 int len; 1431 uint_t i; 1432 int rval; 1433 ndi_event_cookie_t *cookie_list; 1434 ndi_event_cookie_t *prev = NULL; 1435 1436 ASSERT(ndi_events); 1437 ASSERT(handle); 1438 1439 /* 1440 * binding must be performed during attach/detac 1441 */ 1442 if (!DEVI_IS_ATTACHING(handle->ndi_evthdl_dip) && 1443 !DEVI_IS_DETACHING(handle->ndi_evthdl_dip)) { 1444 cmn_err(CE_WARN, "ndi_event_bind_set must be called within " 1445 "attach or detach"); 1446 return (NDI_FAILURE); 1447 } 1448 1449 /* bail out if ndi_event_set is outdated */ 1450 if (ndi_events->ndi_events_version != NDI_EVENTS_REV1) { 1451 return (NDI_FAILURE); 1452 } 1453 1454 ASSERT(ndi_events->ndi_event_defs); 1455 1456 ndi_event_defs = ndi_events->ndi_event_defs; 1457 1458 mutex_enter(&handle->ndi_evthdl_mutex); 1459 mutex_enter(&handle->ndi_evthdl_cb_mutex); 1460 1461 /* 1462 * Verify that all events in the event set are eligible 1463 * for unbinding(ie. there are no outstanding callbacks). 1464 * If any one of the events are ineligible, fail entire 1465 * operation. 1466 */ 1467 1468 for (i = 0; i < ndi_events->ndi_n_events; i++) { 1469 cookie_list = handle->ndi_evthdl_cookie_list; 1470 while (cookie_list != NULL) { 1471 len = strlen(NDI_EVENT_NAME(cookie_list)) + 1; 1472 if (strncmp(NDI_EVENT_NAME(cookie_list), 1473 ndi_event_defs[i].ndi_event_name, len) == 0) { 1474 1475 ASSERT(cookie_list->callback_list == NULL); 1476 if (cookie_list->callback_list) { 1477 rval = NDI_FAILURE; 1478 goto done; 1479 } 1480 break; 1481 } else { 1482 cookie_list = cookie_list->next_cookie; 1483 } 1484 } 1485 } 1486 1487 /* 1488 * remove all events found within the handle 1489 * If an event is not found, this function will proceed as if the event 1490 * was never specified. 1491 */ 1492 1493 for (i = 0; i < ndi_events->ndi_n_events; i++) { 1494 cookie_list = handle->ndi_evthdl_cookie_list; 1495 prev = NULL; 1496 while (cookie_list != NULL) { 1497 len = strlen(NDI_EVENT_NAME(cookie_list)) + 1; 1498 if (strncmp(NDI_EVENT_NAME(cookie_list), 1499 ndi_event_defs[i].ndi_event_name, len) == 0) { 1500 1501 /* 1502 * can not unbind an event definition with 1503 * outstanding callbacks 1504 */ 1505 if (cookie_list->callback_list) { 1506 rval = NDI_FAILURE; 1507 goto done; 1508 } 1509 1510 /* remove this cookie from the list */ 1511 if (prev != NULL) { 1512 prev->next_cookie = 1513 cookie_list->next_cookie; 1514 } else { 1515 handle->ndi_evthdl_cookie_list = 1516 cookie_list->next_cookie; 1517 } 1518 1519 /* adjust plevel counts */ 1520 if (NDI_EVENT_PLEVEL(cookie_list) == 1521 EPL_HIGHLEVEL) { 1522 handle->ndi_evthdl_high_plevels--; 1523 } else { 1524 handle->ndi_evthdl_other_plevels--; 1525 } 1526 1527 /* adjust cookie count */ 1528 handle->ndi_evthdl_n_events--; 1529 1530 /* free the cookie */ 1531 kmem_free(cookie_list, 1532 sizeof (ndi_event_cookie_t)); 1533 1534 cookie_list = handle->ndi_evthdl_cookie_list; 1535 break; 1536 1537 } else { 1538 prev = cookie_list; 1539 cookie_list = cookie_list->next_cookie; 1540 } 1541 1542 } 1543 1544 } 1545 1546 #ifdef NDI_EVENT_DEBUG 1547 if (ndi_event_debug) { 1548 ndi_event_dump_hdl(handle, "ndi_event_unbind_set"); 1549 } 1550 #endif /* NDI_EVENT_DEBUG */ 1551 1552 rval = NDI_SUCCESS; 1553 1554 done: 1555 mutex_exit(&handle->ndi_evthdl_cb_mutex); 1556 mutex_exit(&handle->ndi_evthdl_mutex); 1557 1558 return (rval); 1559 } 1560 1561 /* 1562 * ndi_event_retrieve_cookie(): 1563 * Return an event cookie for eventname if this nexus driver 1564 * has defined the named event. The event cookie returned 1565 * by this function is used to register callback handlers 1566 * for the event. 1567 * 1568 * ndi_event_retrieve_cookie() is intended to be used in the 1569 * nexus driver's bus_get_eventcookie busop routine. 1570 * 1571 * If the event is not defined by this bus nexus driver, and flag 1572 * does not include NDI_EVENT_NOPASS, then ndi_event_retrieve_cookie() 1573 * will pass the request up the device tree hierarchy by calling 1574 * ndi_busop_get_eventcookie(9N). 1575 * If the event is not defined by this bus nexus driver, and flag 1576 * does include NDI_EVENT_NOPASS, ndi_event_retrieve_cookie() 1577 * will return NDI_FAILURE. The caller may then determine what further 1578 * action to take, such as using a different handle, passing the 1579 * request up the device tree using ndi_busop_get_eventcookie(9N), 1580 * or returning the failure to the caller, thus blocking the 1581 * progress of the request up the tree. 1582 */ 1583 int 1584 ndi_event_retrieve_cookie(ndi_event_hdl_t handle, 1585 dev_info_t *rdip, 1586 char *eventname, 1587 ddi_eventcookie_t *cookiep, 1588 uint_t flag) 1589 { 1590 struct ndi_event_hdl *ndi_event_hdl = (struct ndi_event_hdl *)handle; 1591 int len; 1592 ndi_event_cookie_t *cookie_list; 1593 1594 mutex_enter(&ndi_event_hdl->ndi_evthdl_mutex); 1595 1596 cookie_list = ndi_event_hdl->ndi_evthdl_cookie_list; 1597 /* 1598 * search the cookie list for the event name and return 1599 * cookie if found. 1600 */ 1601 while (cookie_list != NULL) { 1602 1603 len = strlen(NDI_EVENT_NAME(cookie_list)) + 1; 1604 if (strncmp(NDI_EVENT_NAME(cookie_list), eventname, 1605 len) == 0) { 1606 *cookiep = (ddi_eventcookie_t)cookie_list; 1607 1608 mutex_exit(&ndi_event_hdl->ndi_evthdl_mutex); 1609 return (NDI_SUCCESS); 1610 } 1611 1612 cookie_list = cookie_list->next_cookie; 1613 } 1614 1615 mutex_exit(&ndi_event_hdl->ndi_evthdl_mutex); 1616 /* 1617 * event was not found, pass up or return failure 1618 */ 1619 if ((flag & NDI_EVENT_NOPASS) == 0) { 1620 return (ndi_busop_get_eventcookie( 1621 ndi_event_hdl->ndi_evthdl_dip, rdip, 1622 eventname, cookiep)); 1623 } else { 1624 return (NDI_FAILURE); 1625 } 1626 } 1627 1628 /* 1629 * check whether this nexus defined this event and look up attributes 1630 */ 1631 static int 1632 ndi_event_is_defined(ndi_event_hdl_t handle, 1633 ddi_eventcookie_t cookie, int *attributes) 1634 { 1635 1636 struct ndi_event_hdl *ndi_event_hdl = (struct ndi_event_hdl *)handle; 1637 ndi_event_cookie_t *cookie_list; 1638 1639 ASSERT(mutex_owned(&handle->ndi_evthdl_mutex)); 1640 1641 cookie_list = ndi_event_hdl->ndi_evthdl_cookie_list; 1642 while (cookie_list != NULL) { 1643 if (cookie_list == NDI_EVENT(cookie)) { 1644 if (attributes) 1645 *attributes = 1646 NDI_EVENT_ATTRIBUTES(cookie_list); 1647 1648 return (NDI_SUCCESS); 1649 } 1650 1651 cookie_list = cookie_list->next_cookie; 1652 } 1653 1654 return (NDI_FAILURE); 1655 } 1656 1657 /* 1658 * ndi_event_add_callback(): adds an event callback registration 1659 * to the event cookie defining this event. 1660 * 1661 * Refer also to bus_add_eventcall(9n) and ndi_busop_add_eventcall(9n). 1662 * 1663 * ndi_event_add_callback(9n) is intended to be used in 1664 * the nexus driver's bus_add_eventcall(9n) busop function. 1665 * 1666 * If the event is not defined by this bus nexus driver, 1667 * ndi_event_add_callback() will return NDI_FAILURE. 1668 */ 1669 int 1670 ndi_event_add_callback(ndi_event_hdl_t handle, dev_info_t *child_dip, 1671 ddi_eventcookie_t cookie, 1672 void (*event_callback)(dev_info_t *, 1673 ddi_eventcookie_t, void *arg, void *impldata), 1674 void *arg, 1675 uint_t flag, 1676 ddi_callback_id_t *cb_id) 1677 { 1678 struct ndi_event_hdl *ndi_event_hdl = (struct ndi_event_hdl *)handle; 1679 int km_flag = ((flag & NDI_NOSLEEP) ? KM_NOSLEEP : KM_SLEEP); 1680 ndi_event_callbacks_t *cb; 1681 1682 mutex_enter(&ndi_event_hdl->ndi_evthdl_mutex); 1683 1684 /* 1685 * if the event was not bound to this handle, return failure 1686 */ 1687 if (ndi_event_is_defined(handle, cookie, NULL) != NDI_SUCCESS) { 1688 1689 mutex_exit(&ndi_event_hdl->ndi_evthdl_mutex); 1690 return (NDI_FAILURE); 1691 1692 } 1693 1694 mutex_exit(&ndi_event_hdl->ndi_evthdl_mutex); 1695 1696 /* 1697 * allocate space for a callback structure 1698 */ 1699 cb = kmem_zalloc(sizeof (ndi_event_callbacks_t), km_flag); 1700 if (cb == NULL) { 1701 return (NDI_FAILURE); 1702 } 1703 1704 mutex_enter(&ndi_event_hdl->ndi_evthdl_mutex); 1705 1706 /* initialize callback structure */ 1707 cb->ndi_evtcb_dip = child_dip; 1708 cb->ndi_evtcb_callback = event_callback; 1709 cb->ndi_evtcb_arg = arg; 1710 cb->ndi_evtcb_cookie = cookie; 1711 cb->devname = (char *)ddi_driver_name(child_dip); 1712 1713 *cb_id = (ddi_callback_id_t)cb; 1714 mutex_enter(&ndi_event_hdl->ndi_evthdl_cb_mutex); 1715 1716 /* add this callback structure to the list */ 1717 if (NDI_EVENT(cookie)->callback_list) { 1718 cb->ndi_evtcb_next = NDI_EVENT(cookie)->callback_list; 1719 NDI_EVENT(cookie)->callback_list->ndi_evtcb_prev = cb; 1720 NDI_EVENT(cookie)->callback_list = cb; 1721 } else { 1722 NDI_EVENT(cookie)->callback_list = cb; 1723 } 1724 #ifdef NDI_EVENT_DEBUG 1725 if (ndi_event_debug) { 1726 ndi_event_dump_hdl(ndi_event_hdl, "ndi_event_add_callback"); 1727 } 1728 #endif /* NDI_EVENT_DEBUG */ 1729 1730 mutex_exit(&ndi_event_hdl->ndi_evthdl_cb_mutex); 1731 mutex_exit(&ndi_event_hdl->ndi_evthdl_mutex); 1732 1733 return (NDI_SUCCESS); 1734 } 1735 1736 /* 1737 * ndi_event_remove_callback(): 1738 * 1739 * ndi_event_remove_callback() removes a callback that was 1740 * previously registered using ndi_event_add_callback(9N). 1741 * Refer also to bus_remove_eventcall(9n) and 1742 * ndi_busop_remove_eventcall(9n). 1743 * ndi_event_remove_callback(9n) is intended to be used in 1744 * the nexus driver's bus_remove_eventcall (9n) busop function. 1745 * If the event is not defined by this bus nexus driver, 1746 * ndi_event_remove_callback() will return NDI_FAILURE. 1747 */ 1748 static void do_ndi_event_remove_callback(struct ndi_event_hdl *ndi_event_hdl, 1749 ddi_callback_id_t cb_id); 1750 1751 int 1752 ndi_event_remove_callback(ndi_event_hdl_t handle, ddi_callback_id_t cb_id) 1753 { 1754 struct ndi_event_hdl *ndi_event_hdl = (struct ndi_event_hdl *)handle; 1755 1756 ASSERT(cb_id); 1757 1758 mutex_enter(&ndi_event_hdl->ndi_evthdl_mutex); 1759 mutex_enter(&ndi_event_hdl->ndi_evthdl_cb_mutex); 1760 1761 do_ndi_event_remove_callback(ndi_event_hdl, cb_id); 1762 1763 mutex_exit(&ndi_event_hdl->ndi_evthdl_cb_mutex); 1764 mutex_exit(&ndi_event_hdl->ndi_evthdl_mutex); 1765 1766 return (NDI_SUCCESS); 1767 } 1768 1769 /*ARGSUSED*/ 1770 static void 1771 do_ndi_event_remove_callback(struct ndi_event_hdl *ndi_event_hdl, 1772 ddi_callback_id_t cb_id) 1773 { 1774 ndi_event_callbacks_t *cb = (ndi_event_callbacks_t *)cb_id; 1775 ASSERT(cb); 1776 1777 ASSERT(mutex_owned(&ndi_event_hdl->ndi_evthdl_mutex)); 1778 ASSERT(mutex_owned(&ndi_event_hdl->ndi_evthdl_cb_mutex)); 1779 1780 /* remove from callback linked list */ 1781 if (cb->ndi_evtcb_prev) { 1782 cb->ndi_evtcb_prev->ndi_evtcb_next = cb->ndi_evtcb_next; 1783 } 1784 1785 if (cb->ndi_evtcb_next) { 1786 cb->ndi_evtcb_next->ndi_evtcb_prev = cb->ndi_evtcb_prev; 1787 } 1788 1789 if (NDI_EVENT(cb->ndi_evtcb_cookie)->callback_list == cb) { 1790 NDI_EVENT(cb->ndi_evtcb_cookie)->callback_list = 1791 cb->ndi_evtcb_next; 1792 } 1793 1794 kmem_free(cb, sizeof (ndi_event_callbacks_t)); 1795 } 1796 1797 /* 1798 * ndi_event_run_callbacks() performs event callbacks for the event 1799 * specified by cookie, if this is among those bound to the 1800 * supplied handle. 1801 * If the event is among those bound to the handle, none, 1802 * some, or all of the handlers registered for the event 1803 * will be called, according to the delivery attributes of 1804 * the event. 1805 * If the event attributes include NDI_EVENT_POST_TO_ALL 1806 * (the default), all the handlers for the event will be 1807 * called in an unspecified order. 1808 * If the event attributes include NDI_EVENT_POST_TO_TGT, only 1809 * the handlers (if any) registered by the driver identified by 1810 * rdip will be called. 1811 * If the event identified by cookie is not bound to the handle, 1812 * NDI_FAILURE will be returned. 1813 */ 1814 int 1815 ndi_event_run_callbacks(ndi_event_hdl_t handle, dev_info_t *child_dip, 1816 ddi_eventcookie_t cookie, void *bus_impldata) 1817 { 1818 struct ndi_event_hdl *ndi_event_hdl = (struct ndi_event_hdl *)handle; 1819 ndi_event_callbacks_t *next, *cb; 1820 int attributes; 1821 1822 mutex_enter(&ndi_event_hdl->ndi_evthdl_mutex); 1823 1824 /* if this is not our event, fail */ 1825 if (ndi_event_is_defined(handle, cookie, &attributes) != 1826 NDI_SUCCESS) { 1827 1828 mutex_exit(&ndi_event_hdl->ndi_evthdl_mutex); 1829 return (NDI_FAILURE); 1830 } 1831 1832 mutex_exit(&ndi_event_hdl->ndi_evthdl_mutex); 1833 1834 #ifdef NDI_EVENT_DEBUG 1835 if (ndi_event_debug) { 1836 cmn_err(CE_CONT, "ndi_event_run_callbacks:\n\t" 1837 "producer dip=%p (%s%d): cookie = %p, name = %s\n", 1838 (void *)ndi_event_hdl->ndi_evthdl_dip, 1839 ddi_node_name(ndi_event_hdl->ndi_evthdl_dip), 1840 ddi_get_instance(ndi_event_hdl->ndi_evthdl_dip), 1841 (void *)cookie, 1842 ndi_event_cookie_to_name(handle, cookie)); 1843 } 1844 #endif /* #ifdef NDI_EVENT_DEBUG */ 1845 1846 1847 /* 1848 * The callback handlers may call conversion functions. The conversion 1849 * functions may hold the ndi_evthdl_mutex during execution. Thus, to 1850 * avoid a recursive mutex problem, only the ndi_evthdl_cb_mutex is 1851 * held. The ndi_evthdl_mutex is not held when running the callbacks. 1852 */ 1853 mutex_enter(&ndi_event_hdl->ndi_evthdl_cb_mutex); 1854 1855 /* perform callbacks */ 1856 next = NDI_EVENT(cookie)->callback_list; 1857 while (next != NULL) { 1858 1859 cb = next; 1860 next = next->ndi_evtcb_next; 1861 1862 ASSERT(cb->ndi_evtcb_cookie == cookie); 1863 1864 if (attributes == NDI_EVENT_POST_TO_TGT && 1865 child_dip != cb->ndi_evtcb_dip) { 1866 continue; 1867 } 1868 1869 cb->ndi_evtcb_callback(cb->ndi_evtcb_dip, cb->ndi_evtcb_cookie, 1870 cb->ndi_evtcb_arg, bus_impldata); 1871 1872 #ifdef NDI_EVENT_DEBUG 1873 if (ndi_event_debug) { 1874 cmn_err(CE_CONT, 1875 "\t\tconsumer dip=%p (%s%d)\n", 1876 (void *)cb->ndi_evtcb_dip, 1877 ddi_node_name(cb->ndi_evtcb_dip), 1878 ddi_get_instance(cb->ndi_evtcb_dip)); 1879 } 1880 #endif 1881 1882 } 1883 1884 mutex_exit(&ndi_event_hdl->ndi_evthdl_cb_mutex); 1885 1886 #ifdef NDI_EVENT_DEBUG 1887 if (ndi_event_debug) { 1888 mutex_enter(&ndi_event_hdl->ndi_evthdl_mutex); 1889 ndi_event_dump_hdl(ndi_event_hdl, "ndi_event_run_callbacks"); 1890 mutex_exit(&ndi_event_hdl->ndi_evthdl_mutex); 1891 } 1892 #endif /* NDI_EVENT_DEBUG */ 1893 1894 return (NDI_SUCCESS); 1895 } 1896 1897 1898 /* 1899 * perform one callback for a specified cookie and just one target 1900 */ 1901 int 1902 ndi_event_do_callback(ndi_event_hdl_t handle, dev_info_t *child_dip, 1903 ddi_eventcookie_t cookie, void *bus_impldata) 1904 { 1905 struct ndi_event_hdl *ndi_event_hdl = (struct ndi_event_hdl *)handle; 1906 ndi_event_callbacks_t *next, *cb; 1907 int attributes; 1908 1909 mutex_enter(&ndi_event_hdl->ndi_evthdl_mutex); 1910 1911 /* if this is not our event, fail */ 1912 if (ndi_event_is_defined(handle, cookie, &attributes) != 1913 NDI_SUCCESS) { 1914 1915 mutex_exit(&ndi_event_hdl->ndi_evthdl_mutex); 1916 1917 return (NDI_FAILURE); 1918 } 1919 1920 mutex_exit(&ndi_event_hdl->ndi_evthdl_mutex); 1921 1922 #ifdef NDI_EVENT_DEBUG 1923 if (ndi_event_debug) { 1924 cmn_err(CE_CONT, "ndi_event_run_callbacks:\n\t" 1925 "producer dip=%p (%s%d): cookie = %p, name = %s\n", 1926 (void *)ndi_event_hdl->ndi_evthdl_dip, 1927 ddi_node_name(ndi_event_hdl->ndi_evthdl_dip), 1928 ddi_get_instance(ndi_event_hdl->ndi_evthdl_dip), 1929 (void *)cookie, 1930 ndi_event_cookie_to_name(handle, cookie)); 1931 } 1932 #endif 1933 1934 1935 /* 1936 * we only grab the cb mutex because the callback handlers 1937 * may call the conversion functions which would cause a recursive 1938 * mutex problem 1939 */ 1940 mutex_enter(&ndi_event_hdl->ndi_evthdl_cb_mutex); 1941 1942 /* perform callbacks */ 1943 for (next = NDI_EVENT(cookie)->callback_list; next != NULL; ) { 1944 cb = next; 1945 next = next->ndi_evtcb_next; 1946 1947 if (cb->ndi_evtcb_dip == child_dip) { 1948 cb->ndi_evtcb_callback(cb->ndi_evtcb_dip, 1949 cb->ndi_evtcb_cookie, cb->ndi_evtcb_arg, 1950 bus_impldata); 1951 1952 #ifdef NDI_EVENT_DEBUG 1953 if (ndi_event_debug) { 1954 cmn_err(CE_CONT, 1955 "\t\tconsumer dip=%p (%s%d)\n", 1956 (void *)cb->ndi_evtcb_dip, 1957 ddi_node_name(cb->ndi_evtcb_dip), 1958 ddi_get_instance(cb->ndi_evtcb_dip)); 1959 } 1960 #endif 1961 break; 1962 } 1963 } 1964 1965 mutex_exit(&ndi_event_hdl->ndi_evthdl_cb_mutex); 1966 1967 #ifdef NDI_EVENT_DEBUG 1968 if (ndi_event_debug) { 1969 mutex_enter(&ndi_event_hdl->ndi_evthdl_mutex); 1970 ndi_event_dump_hdl(ndi_event_hdl, "ndi_event_run_callbacks"); 1971 mutex_exit(&ndi_event_hdl->ndi_evthdl_mutex); 1972 } 1973 #endif /* NDI_EVENT_DEBUG */ 1974 1975 return (NDI_SUCCESS); 1976 } 1977 1978 1979 /* 1980 * ndi_event_tag_to_cookie: utility function to find an event cookie 1981 * given an event tag 1982 */ 1983 ddi_eventcookie_t 1984 ndi_event_tag_to_cookie(ndi_event_hdl_t handle, int event_tag) 1985 { 1986 struct ndi_event_hdl *ndi_event_hdl = (struct ndi_event_hdl *)handle; 1987 ndi_event_cookie_t *list; 1988 1989 mutex_enter(&ndi_event_hdl->ndi_evthdl_mutex); 1990 1991 list = ndi_event_hdl->ndi_evthdl_cookie_list; 1992 while (list != NULL) { 1993 if (NDI_EVENT_TAG(list) == event_tag) { 1994 mutex_exit(&ndi_event_hdl->ndi_evthdl_mutex); 1995 return ((ddi_eventcookie_t)list); 1996 } 1997 1998 list = list->next_cookie; 1999 } 2000 2001 mutex_exit(&ndi_event_hdl->ndi_evthdl_mutex); 2002 return (NULL); 2003 } 2004 2005 /* 2006 * ndi_event_cookie_to_tag: utility function to find a event tag 2007 * given an event_cookie 2008 */ 2009 int 2010 ndi_event_cookie_to_tag(ndi_event_hdl_t handle, ddi_eventcookie_t cookie) 2011 { 2012 struct ndi_event_hdl *ndi_event_hdl = (struct ndi_event_hdl *)handle; 2013 ndi_event_cookie_t *list; 2014 2015 mutex_enter(&ndi_event_hdl->ndi_evthdl_mutex); 2016 2017 list = ndi_event_hdl->ndi_evthdl_cookie_list; 2018 2019 while (list != NULL) { 2020 if ((ddi_eventcookie_t)list == cookie) { 2021 mutex_exit(&ndi_event_hdl->ndi_evthdl_mutex); 2022 return (NDI_EVENT_TAG(list)); 2023 } 2024 2025 list = list->next_cookie; 2026 } 2027 2028 mutex_exit(&ndi_event_hdl->ndi_evthdl_mutex); 2029 return (NDI_FAILURE); 2030 2031 } 2032 2033 /* 2034 * ndi_event_cookie_to_name: utility function to find an event name 2035 * given an event_cookie 2036 */ 2037 char * 2038 ndi_event_cookie_to_name(ndi_event_hdl_t handle, ddi_eventcookie_t cookie) 2039 { 2040 struct ndi_event_hdl *ndi_event_hdl = (struct ndi_event_hdl *)handle; 2041 ndi_event_cookie_t *list; 2042 2043 mutex_enter(&ndi_event_hdl->ndi_evthdl_mutex); 2044 2045 list = ndi_event_hdl->ndi_evthdl_cookie_list; 2046 2047 while (list != NULL) { 2048 if (list == NDI_EVENT(cookie)) { 2049 mutex_exit(&ndi_event_hdl->ndi_evthdl_mutex); 2050 return (NDI_EVENT_NAME(list)); 2051 } 2052 2053 list = list->next_cookie; 2054 } 2055 2056 mutex_exit(&ndi_event_hdl->ndi_evthdl_mutex); 2057 return (NULL); 2058 } 2059 2060 /* 2061 * ndi_event_tag_to_name: utility function to find an event name 2062 * given an event tag 2063 */ 2064 char * 2065 ndi_event_tag_to_name(ndi_event_hdl_t handle, int event_tag) 2066 { 2067 struct ndi_event_hdl *ndi_event_hdl = (struct ndi_event_hdl *)handle; 2068 ndi_event_cookie_t *list; 2069 2070 mutex_enter(&ndi_event_hdl->ndi_evthdl_mutex); 2071 2072 list = ndi_event_hdl->ndi_evthdl_cookie_list; 2073 2074 while (list) { 2075 if (NDI_EVENT_TAG(list) == event_tag) { 2076 mutex_exit(&ndi_event_hdl->ndi_evthdl_mutex); 2077 return (NDI_EVENT_NAME(list)); 2078 } 2079 2080 list = list->next_cookie; 2081 } 2082 2083 mutex_exit(&ndi_event_hdl->ndi_evthdl_mutex); 2084 2085 return (NULL); 2086 } 2087 2088 #ifdef NDI_EVENT_DEBUG 2089 void 2090 ndi_event_dump_hdl(struct ndi_event_hdl *hdl, char *location) 2091 { 2092 2093 2094 ndi_event_callbacks_t *next; 2095 ndi_event_cookie_t *list; 2096 2097 ASSERT(mutex_owned(&hdl->ndi_evthdl_mutex)); 2098 list = hdl->ndi_evthdl_cookie_list; 2099 2100 cmn_err(CE_CONT, "%s: event handle (%p): dip = %p (%s%d)\n", 2101 location, (void *)hdl, 2102 (void *)hdl->ndi_evthdl_dip, 2103 ddi_node_name(hdl->ndi_evthdl_dip), 2104 ddi_get_instance(hdl->ndi_evthdl_dip)); 2105 cmn_err(CE_CONT, "\thigh=%d other=%d n=%d\n", 2106 hdl->ndi_evthdl_high_plevels, 2107 hdl->ndi_evthdl_other_plevels, 2108 hdl->ndi_evthdl_n_events); 2109 2110 2111 cmn_err(CE_CONT, "\tevent cookies:\n"); 2112 while (list) { 2113 cmn_err(CE_CONT, 2114 "\t\ttag=%d name=%s p=%d a=%x dd=%p\n", 2115 NDI_EVENT_TAG(list), 2116 NDI_EVENT_NAME(list), 2117 NDI_EVENT_PLEVEL(list), 2118 NDI_EVENT_ATTRIBUTES(list), 2119 (void *)NDI_EVENT_DDIP(list)); 2120 cmn_err(CE_CONT, "\t\tcallbacks:\n"); 2121 for (next = list->callback_list; next != NULL; 2122 next = next->ndi_evtcb_next) { 2123 cmn_err(CE_CONT, 2124 "\t\t dip=%p (%s%d) cookie=%p arg=%p\n", 2125 (void*)next->ndi_evtcb_dip, 2126 ddi_driver_name(next->ndi_evtcb_dip), 2127 ddi_get_instance(next->ndi_evtcb_dip), 2128 (void *)next->ndi_evtcb_cookie, 2129 next->ndi_evtcb_arg); 2130 } 2131 2132 list = list->next_cookie; 2133 } 2134 2135 cmn_err(CE_CONT, "\n"); 2136 } 2137 #endif 2138 2139 int 2140 ndi_dev_is_prom_node(dev_info_t *dip) 2141 { 2142 return (DEVI(dip)->devi_node_class == DDI_NC_PROM); 2143 } 2144 2145 int 2146 ndi_dev_is_pseudo_node(dev_info_t *dip) 2147 { 2148 /* 2149 * NOTE: this does NOT mean the pseudo branch of the device tree, 2150 * it means the node was created by software (DEVI_SID_NODEID | 2151 * DEVI_PSEUDO_NODEID) instead of being generated from a PROM node. 2152 */ 2153 return (DEVI(dip)->devi_node_class == DDI_NC_PSEUDO); 2154 } 2155 2156 int 2157 ndi_dev_is_persistent_node(dev_info_t *dip) 2158 { 2159 return ((DEVI(dip)->devi_node_attributes & DDI_PERSISTENT) != 0); 2160 } 2161 2162 int 2163 i_ndi_dev_is_auto_assigned_node(dev_info_t *dip) 2164 { 2165 return ((DEVI(dip)->devi_node_attributes & 2166 DDI_AUTO_ASSIGNED_NODEID) != 0); 2167 } 2168 2169 void 2170 i_ndi_set_node_class(dev_info_t *dip, ddi_node_class_t c) 2171 { 2172 DEVI(dip)->devi_node_class = c; 2173 } 2174 2175 ddi_node_class_t 2176 i_ndi_get_node_class(dev_info_t *dip) 2177 { 2178 return (DEVI(dip)->devi_node_class); 2179 } 2180 2181 void 2182 i_ndi_set_node_attributes(dev_info_t *dip, int p) 2183 { 2184 DEVI(dip)->devi_node_attributes = p; 2185 } 2186 2187 int 2188 i_ndi_get_node_attributes(dev_info_t *dip) 2189 { 2190 return (DEVI(dip)->devi_node_attributes); 2191 } 2192 2193 void 2194 i_ndi_set_nodeid(dev_info_t *dip, int n) 2195 { 2196 DEVI(dip)->devi_nodeid = n; 2197 } 2198 2199 void 2200 ndi_set_acc_fault(ddi_acc_handle_t ah) 2201 { 2202 i_ddi_acc_set_fault(ah); 2203 } 2204 2205 void 2206 ndi_clr_acc_fault(ddi_acc_handle_t ah) 2207 { 2208 i_ddi_acc_clr_fault(ah); 2209 } 2210 2211 void 2212 ndi_set_dma_fault(ddi_dma_handle_t dh) 2213 { 2214 i_ddi_dma_set_fault(dh); 2215 } 2216 2217 void 2218 ndi_clr_dma_fault(ddi_dma_handle_t dh) 2219 { 2220 i_ddi_dma_clr_fault(dh); 2221 } 2222 2223 /* 2224 * The default fault-handler, called when the event posted by 2225 * ddi_dev_report_fault() reaches rootnex. 2226 */ 2227 static void 2228 i_ddi_fault_handler(dev_info_t *dip, struct ddi_fault_event_data *fedp) 2229 { 2230 ASSERT(fedp); 2231 2232 mutex_enter(&(DEVI(dip)->devi_lock)); 2233 if (!DEVI_IS_DEVICE_OFFLINE(dip)) { 2234 switch (fedp->f_impact) { 2235 case DDI_SERVICE_LOST: 2236 DEVI_SET_DEVICE_DOWN(dip); 2237 break; 2238 2239 case DDI_SERVICE_DEGRADED: 2240 DEVI_SET_DEVICE_DEGRADED(dip); 2241 break; 2242 2243 case DDI_SERVICE_UNAFFECTED: 2244 default: 2245 break; 2246 2247 case DDI_SERVICE_RESTORED: 2248 DEVI_SET_DEVICE_UP(dip); 2249 break; 2250 } 2251 } 2252 mutex_exit(&(DEVI(dip)->devi_lock)); 2253 } 2254 2255 /* 2256 * The default fault-logger, called when the event posted by 2257 * ddi_dev_report_fault() reaches rootnex. 2258 */ 2259 /*ARGSUSED*/ 2260 static void 2261 i_ddi_fault_logger(dev_info_t *rdip, struct ddi_fault_event_data *fedp) 2262 { 2263 ddi_devstate_t newstate; 2264 const char *action; 2265 const char *servstate; 2266 const char *location; 2267 int bad; 2268 int changed; 2269 int level; 2270 int still; 2271 2272 ASSERT(fedp); 2273 2274 bad = 0; 2275 switch (fedp->f_location) { 2276 case DDI_DATAPATH_FAULT: 2277 location = "in datapath to"; 2278 break; 2279 case DDI_DEVICE_FAULT: 2280 location = "in"; 2281 break; 2282 case DDI_EXTERNAL_FAULT: 2283 location = "external to"; 2284 break; 2285 default: 2286 location = "somewhere near"; 2287 bad = 1; 2288 break; 2289 } 2290 2291 newstate = ddi_get_devstate(fedp->f_dip); 2292 switch (newstate) { 2293 case DDI_DEVSTATE_OFFLINE: 2294 servstate = "unavailable"; 2295 break; 2296 case DDI_DEVSTATE_DOWN: 2297 servstate = "unavailable"; 2298 break; 2299 case DDI_DEVSTATE_QUIESCED: 2300 servstate = "suspended"; 2301 break; 2302 case DDI_DEVSTATE_DEGRADED: 2303 servstate = "degraded"; 2304 break; 2305 default: 2306 servstate = "available"; 2307 break; 2308 } 2309 2310 changed = (newstate != fedp->f_oldstate); 2311 level = (newstate < fedp->f_oldstate) ? CE_WARN : CE_NOTE; 2312 switch (fedp->f_impact) { 2313 case DDI_SERVICE_LOST: 2314 case DDI_SERVICE_DEGRADED: 2315 case DDI_SERVICE_UNAFFECTED: 2316 /* fault detected; service [still] <servstate> */ 2317 action = "fault detected"; 2318 still = !changed; 2319 break; 2320 2321 case DDI_SERVICE_RESTORED: 2322 if (newstate != DDI_DEVSTATE_UP) { 2323 /* fault cleared; service still <servstate> */ 2324 action = "fault cleared"; 2325 still = 1; 2326 } else if (changed) { 2327 /* fault cleared; service <servstate> */ 2328 action = "fault cleared"; 2329 still = 0; 2330 } else { 2331 /* no fault; service <servstate> */ 2332 action = "no fault"; 2333 still = 0; 2334 } 2335 break; 2336 2337 default: 2338 bad = 1; 2339 break; 2340 } 2341 2342 cmn_err(level, "!%s%d: %s %s device; service %s%s"+(bad|changed), 2343 ddi_driver_name(fedp->f_dip), 2344 ddi_get_instance(fedp->f_dip), 2345 bad ? "invalid report of fault" : action, 2346 location, still ? "still " : "", servstate); 2347 2348 cmn_err(level, "!%s%d: %s"+(bad|changed), 2349 ddi_driver_name(fedp->f_dip), 2350 ddi_get_instance(fedp->f_dip), 2351 fedp->f_message); 2352 } 2353 2354 /* 2355 * Platform-settable pointers to fault handler and logger functions. 2356 * These are called by the default rootnex event-posting code when 2357 * a fault event reaches rootnex. 2358 */ 2359 void (*plat_fault_handler)(dev_info_t *, struct ddi_fault_event_data *) = 2360 i_ddi_fault_handler; 2361 void (*plat_fault_logger)(dev_info_t *, struct ddi_fault_event_data *) = 2362 i_ddi_fault_logger; 2363 2364 /* 2365 * Rootnex event definitions ... 2366 */ 2367 enum rootnex_event_tags { 2368 ROOTNEX_FAULT_EVENT 2369 }; 2370 static ndi_event_hdl_t rootnex_event_hdl; 2371 static ndi_event_definition_t rootnex_event_set[] = { 2372 { 2373 ROOTNEX_FAULT_EVENT, 2374 DDI_DEVI_FAULT_EVENT, 2375 EPL_INTERRUPT, 2376 NDI_EVENT_POST_TO_ALL 2377 } 2378 }; 2379 static ndi_event_set_t rootnex_events = { 2380 NDI_EVENTS_REV1, 2381 sizeof (rootnex_event_set) / sizeof (rootnex_event_set[0]), 2382 rootnex_event_set 2383 }; 2384 2385 /* 2386 * Initialize rootnex event handle 2387 */ 2388 void 2389 i_ddi_rootnex_init_events(dev_info_t *dip) 2390 { 2391 if (ndi_event_alloc_hdl(dip, (ddi_iblock_cookie_t)(LOCK_LEVEL-1), 2392 &rootnex_event_hdl, NDI_SLEEP) == NDI_SUCCESS) { 2393 if (ndi_event_bind_set(rootnex_event_hdl, 2394 &rootnex_events, NDI_SLEEP) != NDI_SUCCESS) { 2395 (void) ndi_event_free_hdl(rootnex_event_hdl); 2396 rootnex_event_hdl = NULL; 2397 } 2398 } 2399 } 2400 2401 /* 2402 * Event-handling functions for rootnex 2403 * These provide the standard implementation of fault handling 2404 */ 2405 /*ARGSUSED*/ 2406 int 2407 i_ddi_rootnex_get_eventcookie(dev_info_t *dip, dev_info_t *rdip, 2408 char *eventname, ddi_eventcookie_t *cookiep) 2409 { 2410 if (rootnex_event_hdl == NULL) 2411 return (NDI_FAILURE); 2412 return (ndi_event_retrieve_cookie(rootnex_event_hdl, rdip, eventname, 2413 cookiep, NDI_EVENT_NOPASS)); 2414 } 2415 2416 /*ARGSUSED*/ 2417 int 2418 i_ddi_rootnex_add_eventcall(dev_info_t *dip, dev_info_t *rdip, 2419 ddi_eventcookie_t eventid, void (*handler)(dev_info_t *dip, 2420 ddi_eventcookie_t event, void *arg, void *impl_data), void *arg, 2421 ddi_callback_id_t *cb_id) 2422 { 2423 if (rootnex_event_hdl == NULL) 2424 return (NDI_FAILURE); 2425 return (ndi_event_add_callback(rootnex_event_hdl, rdip, 2426 eventid, handler, arg, NDI_SLEEP, cb_id)); 2427 } 2428 2429 /*ARGSUSED*/ 2430 int 2431 i_ddi_rootnex_remove_eventcall(dev_info_t *dip, ddi_callback_id_t cb_id) 2432 { 2433 if (rootnex_event_hdl == NULL) 2434 return (NDI_FAILURE); 2435 2436 return (ndi_event_remove_callback(rootnex_event_hdl, cb_id)); 2437 } 2438 2439 /*ARGSUSED*/ 2440 int 2441 i_ddi_rootnex_post_event(dev_info_t *dip, dev_info_t *rdip, 2442 ddi_eventcookie_t eventid, void *impl_data) 2443 { 2444 int tag; 2445 2446 if (rootnex_event_hdl == NULL) 2447 return (NDI_FAILURE); 2448 2449 tag = ndi_event_cookie_to_tag(rootnex_event_hdl, eventid); 2450 if (tag == ROOTNEX_FAULT_EVENT) { 2451 (*plat_fault_handler)(rdip, impl_data); 2452 (*plat_fault_logger)(rdip, impl_data); 2453 } 2454 return (ndi_event_run_callbacks(rootnex_event_hdl, rdip, 2455 eventid, impl_data)); 2456 } 2457