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