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 /*
  23  * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
  24  * Copyright (c) 2010, Intel Corporation.
  25  * All rights reserved.
  26  */
  27 
  28 #include <sys/types.h>
  29 #include <sys/atomic.h>
  30 #include <sys/bitmap.h>
  31 #include <sys/cmn_err.h>
  32 #include <sys/note.h>
  33 #include <sys/sunndi.h>
  34 #include <sys/fastboot_impl.h>
  35 #include <sys/sysevent.h>
  36 #include <sys/sysevent/dr.h>
  37 #include <sys/sysevent/eventdefs.h>
  38 #include <sys/acpi/acpi.h>
  39 #include <sys/acpica.h>
  40 #include <sys/acpidev.h>
  41 #include <sys/acpidev_dr.h>
  42 #include <sys/acpinex.h>
  43 
  44 int acpinex_event_support_remove = 0;
  45 
  46 static volatile uint_t acpinex_dr_event_cnt = 0;
  47 static ulong_t acpinex_object_type_mask[BT_BITOUL(ACPI_TYPE_NS_NODE_MAX + 1)];
  48 
  49 /*
  50  * Generate DR_REQ event to syseventd.
  51  * Please refer to sys/sysevent/dr.h for message definition.
  52  */
  53 static int
  54 acpinex_event_generate_event(dev_info_t *dip, ACPI_HANDLE hdl, int req,
  55     int event, char *objname)
  56 {
  57         int rv = 0;
  58         sysevent_id_t eid;
  59         sysevent_value_t evnt_val;
  60         sysevent_attr_list_t *evnt_attr_list = NULL;
  61         char *attach_pnt;
  62         char event_type[32];
  63 
  64         /* Add "attachment point" attribute. */
  65         attach_pnt = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
  66         if (ACPI_FAILURE(acpidev_dr_get_attachment_point(hdl,
  67             attach_pnt, MAXPATHLEN))) {
  68                 cmn_err(CE_WARN,
  69                     "!acpinex: failed to generate AP name for %s.", objname);
  70                 kmem_free(attach_pnt, MAXPATHLEN);
  71                 return (-1);
  72         }
  73         ASSERT(attach_pnt[0] != '\0');
  74         evnt_val.value_type = SE_DATA_TYPE_STRING;
  75         evnt_val.value.sv_string = attach_pnt;
  76         rv = sysevent_add_attr(&evnt_attr_list, DR_AP_ID, &evnt_val, KM_SLEEP);
  77         if (rv != 0) {
  78                 cmn_err(CE_WARN,
  79                     "!acpinex: failed to add attr [%s] for %s event.",
  80                     DR_AP_ID, EC_DR);
  81                 kmem_free(attach_pnt, MAXPATHLEN);
  82                 return (rv);
  83         }
  84 
  85         /* Add "request type" attribute. */
  86         evnt_val.value_type = SE_DATA_TYPE_STRING;
  87         evnt_val.value.sv_string = SE_REQ2STR(req);
  88         rv = sysevent_add_attr(&evnt_attr_list, DR_REQ_TYPE, &evnt_val,
  89             KM_SLEEP);
  90         if (rv != 0) {
  91                 cmn_err(CE_WARN,
  92                     "!acpinex: failed to add attr [%s] for %s event.",
  93                     DR_REQ_TYPE, EC_DR);
  94                 sysevent_free_attr(evnt_attr_list);
  95                 kmem_free(attach_pnt, MAXPATHLEN);
  96                 return (rv);
  97         }
  98 
  99         /* Add "acpi-event-type" attribute. */
 100         switch (event) {
 101         case ACPI_NOTIFY_BUS_CHECK:
 102                 (void) snprintf(event_type, sizeof (event_type),
 103                     ACPIDEV_EVENT_TYPE_BUS_CHECK);
 104                 break;
 105         case ACPI_NOTIFY_DEVICE_CHECK:
 106                 (void) snprintf(event_type, sizeof (event_type),
 107                     ACPIDEV_EVENT_TYPE_DEVICE_CHECK);
 108                 break;
 109         case ACPI_NOTIFY_DEVICE_CHECK_LIGHT:
 110                 (void) snprintf(event_type, sizeof (event_type),
 111                     ACPIDEV_EVENT_TYPE_DEVICE_CHECK_LIGHT);
 112                 break;
 113         case ACPI_NOTIFY_EJECT_REQUEST:
 114                 (void) snprintf(event_type, sizeof (event_type),
 115                     ACPIDEV_EVENT_TYPE_EJECT_REQUEST);
 116                 break;
 117         default:
 118                 cmn_err(CE_WARN,
 119                     "!acpinex: unknown ACPI event type %d.", event);
 120                 sysevent_free_attr(evnt_attr_list);
 121                 kmem_free(attach_pnt, MAXPATHLEN);
 122                 return (-1);
 123         }
 124         evnt_val.value_type = SE_DATA_TYPE_STRING;
 125         evnt_val.value.sv_string = event_type;
 126         rv = sysevent_add_attr(&evnt_attr_list, ACPIDEV_EVENT_TYPE_ATTR_NAME,
 127             &evnt_val, KM_SLEEP);
 128         if (rv != 0) {
 129                 cmn_err(CE_WARN,
 130                     "!acpinex: failed to add attr [%s] for %s event.",
 131                     ACPIDEV_EVENT_TYPE_ATTR_NAME, EC_DR);
 132                 sysevent_free_attr(evnt_attr_list);
 133                 kmem_free(attach_pnt, MAXPATHLEN);
 134                 return (rv);
 135         }
 136 
 137         rv = ddi_log_sysevent(dip, DDI_VENDOR_SUNW, EC_DR, ESC_DR_REQ,
 138             evnt_attr_list, &eid, KM_SLEEP);
 139         if (rv != DDI_SUCCESS) {
 140                 cmn_err(CE_WARN,
 141                     "!acpinex: failed to log DR_REQ event for %s.", objname);
 142                 rv = -1;
 143         }
 144 
 145         nvlist_free(evnt_attr_list);
 146         kmem_free(attach_pnt, MAXPATHLEN);
 147 
 148         return (rv);
 149 }
 150 
 151 /*
 152  * Event handler for ACPI EJECT_REQUEST notifications.
 153  * EJECT_REQUEST notifications should be generated on the device to be ejected,
 154  * so no need to scan subtree of it.
 155  * It also invokes ACPI _OST method to update event status if call_ost is true.
 156  */
 157 static void
 158 acpinex_event_handle_eject_request(ACPI_HANDLE hdl, acpinex_softstate_t *sp,
 159     boolean_t call_ost)
 160 {
 161         int code;
 162         char *objname;
 163 
 164         ASSERT(hdl != NULL);
 165         objname = acpidev_get_object_name(hdl);
 166 
 167         ASSERT(sp != NULL);
 168         ASSERT(sp->ans_dip != NULL && sp->ans_hdl != NULL);
 169         if (sp == NULL || sp->ans_dip == NULL || sp->ans_hdl == NULL) {
 170                 if (call_ost) {
 171                         (void) acpidev_eval_ost(hdl, ACPI_NOTIFY_EJECT_REQUEST,
 172                             ACPI_OST_STA_FAILURE, NULL, 0);
 173                 }
 174                 ACPINEX_DEBUG(CE_WARN,
 175                     "!acpinex: softstate data structure is invalid.");
 176                 cmn_err(CE_WARN,
 177                     "!acpinex: failed to handle EJECT_REQUEST event from %s.",
 178                     objname);
 179                 acpidev_free_object_name(objname);
 180                 return;
 181         }
 182 
 183         if (acpinex_event_support_remove == 0) {
 184                 cmn_err(CE_WARN,
 185                     "!acpinex: hot-removing of device %s is unsupported.",
 186                     objname);
 187                 code = ACPI_OST_STA_EJECT_NOT_SUPPORT;
 188         } else if (acpinex_event_generate_event(sp->ans_dip, hdl,
 189             SE_OUTGOING_RES, ACPI_NOTIFY_EJECT_REQUEST, objname) != 0) {
 190                 cmn_err(CE_WARN, "!acpinex: failed to generate ESC_DR_REQ "
 191                     "event for device eject request from %s.", objname);
 192                 code = ACPI_OST_STA_FAILURE;
 193         } else {
 194                 cmn_err(CE_NOTE, "!acpinex: generate ESC_DR_REQ event for "
 195                     "device eject request from %s.", objname);
 196                 code = ACPI_OST_STA_EJECT_IN_PROGRESS;
 197         }
 198         if (call_ost) {
 199                 (void) acpidev_eval_ost(hdl, ACPI_NOTIFY_EJECT_REQUEST,
 200                     code, NULL, 0);
 201         }
 202 
 203         acpidev_free_object_name(objname);
 204 }
 205 
 206 struct acpinex_event_check_arg {
 207         acpinex_softstate_t     *softstatep;
 208         int                     event_type;
 209         uint32_t                device_insert;
 210         uint32_t                device_remove;
 211         uint32_t                device_fail;
 212 };
 213 
 214 static ACPI_STATUS
 215 acpinex_event_handle_check_one(ACPI_HANDLE hdl, UINT32 lvl, void *ctx,
 216     void **retval)
 217 {
 218         _NOTE(ARGUNUSED(lvl, retval));
 219 
 220         char *objname;
 221         int status, psta, csta;
 222         acpidev_data_handle_t dhdl;
 223         struct acpinex_event_check_arg *argp;
 224 
 225         ASSERT(hdl != NULL);
 226         ASSERT(ctx != NULL);
 227         argp = (struct acpinex_event_check_arg *)ctx;
 228 
 229         dhdl = acpidev_data_get_handle(hdl);
 230         if (dhdl == NULL) {
 231                 /* Skip subtree if failed to get the data handle. */
 232                 ACPINEX_DEBUG(CE_NOTE,
 233                     "!acpinex: failed to get data associated with %p.", hdl);
 234                 return (AE_CTRL_DEPTH);
 235         } else if (!acpidev_data_dr_capable(dhdl)) {
 236                 return (AE_OK);
 237         }
 238 
 239         objname = acpidev_get_object_name(hdl);
 240 
 241         status = 0;
 242         /* Query previous device status. */
 243         psta = acpidev_data_get_status(dhdl);
 244         if (acpidev_check_device_enabled(psta)) {
 245                 status |= 0x1;
 246         }
 247         /* Query current device status. */
 248         csta = acpidev_query_device_status(hdl);
 249         if (acpidev_check_device_enabled(csta)) {
 250                 status |= 0x2;
 251         }
 252 
 253         switch (status) {
 254         case 0x0:
 255                 /*FALLTHROUGH*/
 256         case 0x3:
 257                 /* No status changes, keep on walking. */
 258                 acpidev_free_object_name(objname);
 259                 return (AE_OK);
 260 
 261         case 0x1:
 262                 /* Surprising removal. */
 263                 cmn_err(CE_WARN,
 264                     "!acpinex: device %s has been surprisingly removed.",
 265                     objname);
 266                 if (argp->event_type == ACPI_NOTIFY_BUS_CHECK) {
 267                         /*
 268                          * According to ACPI spec, BUS_CHECK notification
 269                          * should be triggered for hot-adding events only.
 270                          */
 271                         ACPINEX_DEBUG(CE_WARN,
 272                             "!acpinex: device %s has been surprisingly removed "
 273                             "when handling BUS_CHECK event.", objname);
 274                 }
 275                 acpidev_free_object_name(objname);
 276                 argp->device_remove++;
 277                 return (AE_CTRL_DEPTH);
 278 
 279         case 0x2:
 280                 /* Hot-adding. */
 281                 ACPINEX_DEBUG(CE_NOTE,
 282                     "!acpinex: device %s has been inserted.", objname);
 283                 argp->device_insert++;
 284                 if (acpinex_event_generate_event(argp->softstatep->ans_dip, hdl,
 285                     SE_INCOMING_RES, argp->event_type, objname) != 0) {
 286                         cmn_err(CE_WARN,
 287                             "!acpinex: failed to generate ESC_DR_REQ event for "
 288                             "device insert request from %s.", objname);
 289                         argp->device_fail++;
 290                 } else {
 291                         cmn_err(CE_NOTE, "!acpinex: generate ESC_DR_REQ event "
 292                             "for device insert request from %s.", objname);
 293                 }
 294                 acpidev_free_object_name(objname);
 295                 return (AE_OK);
 296 
 297         default:
 298                 ASSERT(0);
 299                 break;
 300         }
 301 
 302         return (AE_ERROR);
 303 }
 304 
 305 /*
 306  * Event handler for BUS_CHECK/DEVICE_CHECK/DEVICE_CHECK_LIGHT notifications.
 307  * These events may be signaled on parent/ancestor of devices to be hot-added,
 308  * so need to scan ACPI namespace to figure out devices in question.
 309  * It also invokes ACPI _OST method to update event status if call_ost is true.
 310  */
 311 static void
 312 acpinex_event_handle_check_request(int event, ACPI_HANDLE hdl,
 313     acpinex_softstate_t *sp, boolean_t call_ost)
 314 {
 315         ACPI_STATUS rv;
 316         int code;
 317         char *objname;
 318         struct acpinex_event_check_arg arg;
 319 
 320         ASSERT(hdl != NULL);
 321         objname = acpidev_get_object_name(hdl);
 322 
 323         ASSERT(sp != NULL);
 324         ASSERT(sp->ans_dip != NULL && sp->ans_hdl != NULL);
 325         if (sp == NULL || sp->ans_dip == NULL || sp->ans_hdl == NULL) {
 326                 if (call_ost) {
 327                         (void) acpidev_eval_ost(hdl, event,
 328                             ACPI_OST_STA_FAILURE, NULL, 0);
 329                 }
 330                 ACPINEX_DEBUG(CE_WARN,
 331                     "!acpinex: softstate data structure is invalid.");
 332                 cmn_err(CE_WARN, "!acpinex: failed to handle "
 333                     "BUS/DEVICE_CHECK event from %s.", objname);
 334                 acpidev_free_object_name(objname);
 335                 return;
 336         }
 337 
 338         bzero(&arg, sizeof (arg));
 339         arg.event_type = event;
 340         arg.softstatep = sp;
 341         rv = acpinex_event_handle_check_one(hdl, 0, &arg, NULL);
 342         if (ACPI_SUCCESS(rv)) {
 343                 rv = AcpiWalkNamespace(ACPI_TYPE_DEVICE, hdl,
 344                     ACPIDEV_MAX_ENUM_LEVELS,
 345                     &acpinex_event_handle_check_one, NULL, &arg, NULL);
 346         }
 347 
 348         if (ACPI_FAILURE(rv)) {
 349                 /* Failed to scan the ACPI namespace. */
 350                 cmn_err(CE_WARN, "!acpinex: failed to handle event %d from %s.",
 351                     event, objname);
 352                 code = ACPI_OST_STA_FAILURE;
 353         } else if (arg.device_remove != 0) {
 354                 /* Surprising removal happened. */
 355                 ACPINEX_DEBUG(CE_WARN,
 356                     "!acpinex: some devices have been surprisingly removed.");
 357                 code = ACPI_OST_STA_NOT_SUPPORT;
 358         } else if (arg.device_fail != 0) {
 359                 /* Failed to handle some devices. */
 360                 ACPINEX_DEBUG(CE_WARN,
 361                     "!acpinex: failed to check status of some devices.");
 362                 code = ACPI_OST_STA_FAILURE;
 363         } else if (arg.device_insert == 0) {
 364                 /* No hot-added devices found. */
 365                 cmn_err(CE_WARN,
 366                     "!acpinex: no hot-added devices under %s found.", objname);
 367                 code = ACPI_OST_STA_FAILURE;
 368         } else {
 369                 code = ACPI_OST_STA_INSERT_IN_PROGRESS;
 370         }
 371         if (call_ost) {
 372                 (void) acpidev_eval_ost(hdl, event, code, NULL, 0);
 373         }
 374 
 375         acpidev_free_object_name(objname);
 376 }
 377 
 378 static void
 379 acpinex_event_system_handler(ACPI_HANDLE hdl, UINT32 type, void *arg)
 380 {
 381         acpinex_softstate_t *sp;
 382 
 383         ASSERT(hdl != NULL);
 384         ASSERT(arg != NULL);
 385         sp = (acpinex_softstate_t *)arg;
 386 
 387         acpidev_dr_lock_all();
 388         mutex_enter(&sp->ans_lock);
 389 
 390         switch (type) {
 391         case ACPI_NOTIFY_BUS_CHECK:
 392                 /*
 393                  * Bus Check. This notification is performed on a device object
 394                  * to indicate to OSPM that it needs to perform the Plug and
 395                  * Play re-enumeration operation on the device tree starting
 396                  * from the point where it has been notified. OSPM will only
 397                  * perform this operation at boot, and when notified. It is
 398                  * the responsibility of the ACPI AML code to notify OSPM at
 399                  * any other times that this operation is required. The more
 400                  * accurately and closer to the actual device tree change the
 401                  * notification can be done, the more efficient the operating
 402                  * system response will be; however, it can also be an issue
 403                  * when a device change cannot be confirmed. For example, if
 404                  * the hardware cannot notice a device change for a particular
 405                  * location during a system sleeping state, it issues a Bus
 406                  * Check notification on wake to inform OSPM that it needs to
 407                  * check the configuration for a device change.
 408                  */
 409                 /*FALLTHROUGH*/
 410         case ACPI_NOTIFY_DEVICE_CHECK:
 411                 /*
 412                  * Device Check. Used to notify OSPM that the device either
 413                  * appeared or disappeared. If the device has appeared, OSPM
 414                  * will re-enumerate from the parent. If the device has
 415                  * disappeared, OSPM will invalidate the state of the device.
 416                  * OSPM may optimize out re-enumeration. If _DCK is present,
 417                  * then Notify(object,1) is assumed to indicate an undock
 418                  * request.
 419                  */
 420                 /*FALLTHROUGH*/
 421         case ACPI_NOTIFY_DEVICE_CHECK_LIGHT:
 422                 /*
 423                  * Device Check Light. Used to notify OSPM that the device
 424                  * either appeared or disappeared. If the device has appeared,
 425                  * OSPM will re-enumerate from the device itself, not the
 426                  * parent. If the device has disappeared, OSPM will invalidate
 427                  * the state of the device.
 428                  */
 429                 atomic_inc_uint(&acpinex_dr_event_cnt);
 430                 acpinex_event_handle_check_request(type, hdl, sp, B_TRUE);
 431                 break;
 432 
 433         case ACPI_NOTIFY_EJECT_REQUEST:
 434                 /*
 435                  * Eject Request. Used to notify OSPM that the device should
 436                  * be ejected, and that OSPM needs to perform the Plug and Play
 437                  * ejection operation. OSPM will run the _EJx method.
 438                  */
 439                 atomic_inc_uint(&acpinex_dr_event_cnt);
 440                 acpinex_event_handle_eject_request(hdl, sp, B_TRUE);
 441                 break;
 442 
 443         default:
 444                 ACPINEX_DEBUG(CE_NOTE,
 445                     "!acpinex: unhandled event(%d) on hdl %p under %s.",
 446                     type, hdl, sp->ans_path);
 447                 (void) acpidev_eval_ost(hdl, type, ACPI_OST_STA_NOT_SUPPORT,
 448                     NULL, 0);
 449                 break;
 450         }
 451 
 452         if (acpinex_dr_event_cnt != 0) {
 453                 /*
 454                  * Disable fast reboot if a CPU/MEM/IOH hotplug event happens.
 455                  * Note: this is a temporary solution and will be revised when
 456                  * fast reboot can support CPU/MEM/IOH DR operations in the
 457                  * future.
 458                  *
 459                  * ACPI BIOS generates some static ACPI tables, such as MADT,
 460                  * SRAT and SLIT, to describe the system hardware configuration
 461                  * on power-on. When a CPU/MEM/IOH hotplug event happens, those
 462                  * static tables won't be updated and will become stale.
 463                  *
 464                  * If we reset the system by fast reboot, BIOS will have no
 465                  * chance to regenerate those staled static tables. Fast reboot
 466                  * can't tolerate such inconsistency between staled ACPI tables
 467                  * and real hardware configuration yet.
 468                  *
 469                  * A temporary solution is introduced to disable fast reboot if
 470                  * CPU/MEM/IOH hotplug event happens. This solution should be
 471                  * revised when fast reboot is enhanced to support CPU/MEM/IOH
 472                  * DR operations.
 473                  */
 474                 fastreboot_disable(FBNS_HOTPLUG);
 475         }
 476 
 477         mutex_exit(&sp->ans_lock);
 478         acpidev_dr_unlock_all();
 479 }
 480 
 481 /*
 482  * Install event handler for ACPI system events.
 483  * Acpinex driver handles ACPI system events for its children,
 484  * device specific events will be handled by device drivers.
 485  * Return DDI_SUCCESS on success, and DDI_FAILURE on failure.
 486  */
 487 static int
 488 acpinex_event_install_handler(ACPI_HANDLE hdl, void *arg,
 489     ACPI_DEVICE_INFO *infop, acpidev_data_handle_t dhdl)
 490 {
 491         ACPI_STATUS status;
 492         int rc = DDI_SUCCESS;
 493 
 494         ASSERT(hdl != NULL);
 495         ASSERT(dhdl != NULL);
 496         ASSERT(infop != NULL);
 497 
 498         /*
 499          * Check whether the event handler has already been installed on the
 500          * device object. With the introduction of ACPI Alias objects, which are
 501          * similar to symlinks in file systems, there may be multiple name
 502          * objects in the ACPI namespace pointing to the same underlying device
 503          * object. Those Alias objects need to be filtered out, otherwise
 504          * it will attempt to install the event handler multiple times on the
 505          * same device object which will fail.
 506          */
 507         if (acpidev_data_get_flag(dhdl, ACPIDEV_DATA_HANDLER_READY)) {
 508                 return (DDI_SUCCESS);
 509         }
 510         status = AcpiInstallNotifyHandler(hdl, ACPI_SYSTEM_NOTIFY,
 511             acpinex_event_system_handler, arg);
 512         if (status == AE_OK || status == AE_ALREADY_EXISTS) {
 513                 acpidev_data_set_flag(dhdl, ACPIDEV_DATA_HANDLER_READY);
 514         } else {
 515                 char *objname;
 516 
 517                 objname = acpidev_get_object_name(hdl);
 518                 cmn_err(CE_WARN,
 519                     "!acpinex: failed to install system event handler for %s.",
 520                     objname);
 521                 acpidev_free_object_name(objname);
 522                 rc = DDI_FAILURE;
 523         }
 524 
 525         return (rc);
 526 }
 527 
 528 /*
 529  * Uninstall event handler for ACPI system events.
 530  * Return DDI_SUCCESS on success, and DDI_FAILURE on failure.
 531  */
 532 static int
 533 acpinex_event_uninstall_handler(ACPI_HANDLE hdl, ACPI_DEVICE_INFO *infop,
 534     acpidev_data_handle_t dhdl)
 535 {
 536         ASSERT(hdl != NULL);
 537         ASSERT(dhdl != NULL);
 538         ASSERT(infop != NULL);
 539 
 540         if (!acpidev_data_get_flag(dhdl, ACPIDEV_DATA_HANDLER_READY)) {
 541                 return (DDI_SUCCESS);
 542         }
 543         if (ACPI_SUCCESS(AcpiRemoveNotifyHandler(hdl, ACPI_SYSTEM_NOTIFY,
 544             acpinex_event_system_handler))) {
 545                 acpidev_data_clear_flag(dhdl, ACPIDEV_DATA_HANDLER_READY);
 546         } else {
 547                 char *objname;
 548 
 549                 objname = acpidev_get_object_name(hdl);
 550                 cmn_err(CE_WARN, "!acpinex: failed to uninstall system event "
 551                     "handler for %s.", objname);
 552                 acpidev_free_object_name(objname);
 553                 return (DDI_FAILURE);
 554         }
 555 
 556         return (DDI_SUCCESS);
 557 }
 558 
 559 /*
 560  * Install/uninstall ACPI system event handler for child objects of hdl.
 561  * Return DDI_SUCCESS on success, and DDI_FAILURE on failure.
 562  */
 563 static int
 564 acpinex_event_walk(boolean_t init, acpinex_softstate_t *sp, ACPI_HANDLE hdl)
 565 {
 566         int rc;
 567         int retval = DDI_SUCCESS;
 568         dev_info_t *dip;
 569         ACPI_HANDLE child = NULL;
 570         ACPI_OBJECT_TYPE type;
 571         ACPI_DEVICE_INFO *infop;
 572         acpidev_data_handle_t dhdl;
 573 
 574         /* Walk all child objects. */
 575         ASSERT(hdl != NULL);
 576         while (ACPI_SUCCESS(AcpiGetNextObject(ACPI_TYPE_ANY, hdl, child,
 577             &child))) {
 578                 /* Skip unwanted object types. */
 579                 if (ACPI_FAILURE(AcpiGetType(child, &type)) ||
 580                     type > ACPI_TYPE_NS_NODE_MAX ||
 581                     BT_TEST(acpinex_object_type_mask, type) == 0) {
 582                         continue;
 583                 }
 584 
 585                 /* Get data associated with the object. Skip it if fails. */
 586                 dhdl = acpidev_data_get_handle(child);
 587                 if (dhdl == NULL) {
 588                         ACPINEX_DEBUG(CE_NOTE, "!acpinex: failed to get data "
 589                             "associated with %p, skip.", child);
 590                         continue;
 591                 }
 592 
 593                 /* Query ACPI object info for the object. */
 594                 if (ACPI_FAILURE(AcpiGetObjectInfo(child, &infop))) {
 595                         cmn_err(CE_WARN,
 596                             "!acpidnex: failed to get object info for %p.",
 597                             child);
 598                         continue;
 599                 }
 600 
 601                 if (init) {
 602                         rc = acpinex_event_install_handler(child, sp, infop,
 603                             dhdl);
 604                         if (rc != DDI_SUCCESS) {
 605                                 ACPINEX_DEBUG(CE_WARN, "!acpinex: failed to "
 606                                     "install handler for child %p of %s.",
 607                                     child, sp->ans_path);
 608                                 retval = DDI_FAILURE;
 609                         /*
 610                          * Try to handle descendants if both of the
 611                          * following two conditions are true:
 612                          * 1) Device corresponding to the current object is
 613                          *    enabled. If the device is absent/disabled,
 614                          *    no notification should be generated from
 615                          *    descendant objects of it.
 616                          * 2) No Solaris device node has been created for the
 617                          *    current object yet. If the device node has been
 618                          *    created for the current object, notification
 619                          *    events from child objects should be handled by
 620                          *    the corresponding driver.
 621                          */
 622                         } else if (acpidev_check_device_enabled(
 623                             acpidev_data_get_status(dhdl)) &&
 624                             ACPI_FAILURE(acpica_get_devinfo(child, &dip))) {
 625                                 rc = acpinex_event_walk(B_TRUE, sp, child);
 626                                 if (rc != DDI_SUCCESS) {
 627                                         ACPINEX_DEBUG(CE_WARN,
 628                                             "!acpinex: failed to install "
 629                                             "handler for descendants of %s.",
 630                                             sp->ans_path);
 631                                         retval = DDI_FAILURE;
 632                                 }
 633                         }
 634                 } else {
 635                         rc = DDI_SUCCESS;
 636                         /* Uninstall handler for descendants if needed. */
 637                         if (ACPI_FAILURE(acpica_get_devinfo(child, &dip))) {
 638                                 rc = acpinex_event_walk(B_FALSE, sp, child);
 639                         }
 640                         if (rc == DDI_SUCCESS) {
 641                                 rc = acpinex_event_uninstall_handler(child,
 642                                     infop, dhdl);
 643                         }
 644                         /* Undo will be done by caller in case of failure. */
 645                         if (rc != DDI_SUCCESS) {
 646                                 ACPINEX_DEBUG(CE_WARN, "!acpinex: failed to "
 647                                     "uninstall handler for descendants of %s.",
 648                                     sp->ans_path);
 649                                 AcpiOsFree(infop);
 650                                 retval = DDI_FAILURE;
 651                                 break;
 652                         }
 653                 }
 654 
 655                 /* Release cached resources. */
 656                 AcpiOsFree(infop);
 657         }
 658 
 659         return (retval);
 660 }
 661 
 662 int
 663 acpinex_event_scan(acpinex_softstate_t *sp, boolean_t init)
 664 {
 665         int rc;
 666 
 667         ASSERT(sp != NULL);
 668         ASSERT(sp->ans_hdl != NULL);
 669         ASSERT(sp->ans_dip != NULL);
 670         if (sp == NULL || sp->ans_hdl == NULL || sp->ans_dip == NULL) {
 671                 ACPINEX_DEBUG(CE_WARN,
 672                     "!acpinex: invalid parameter to acpinex_event_scan().");
 673                 return (DDI_FAILURE);
 674         }
 675 
 676         /* Lock current device node and walk all child device nodes of it. */
 677         mutex_enter(&sp->ans_lock);
 678 
 679         rc = acpinex_event_walk(init, sp, sp->ans_hdl);
 680         if (rc != DDI_SUCCESS) {
 681                 if (init) {
 682                         ACPINEX_DEBUG(CE_WARN, "!acpinex: failed to "
 683                             "configure child objects of %s.", sp->ans_path);
 684                         rc = DDI_FAILURE;
 685                 } else {
 686                         ACPINEX_DEBUG(CE_WARN, "!acpinex: failed to "
 687                             "unconfigure child objects of %s.", sp->ans_path);
 688                         /* Undo in case of errors */
 689                         (void) acpinex_event_walk(B_TRUE, sp, sp->ans_hdl);
 690                         rc = DDI_FAILURE;
 691                 }
 692         }
 693 
 694         mutex_exit(&sp->ans_lock);
 695 
 696         return (rc);
 697 }
 698 
 699 void
 700 acpinex_event_init(void)
 701 {
 702         /*
 703          * According to ACPI specifications, notification is only supported on
 704          * Device, Processor and ThermalZone. Currently we only need to handle
 705          * Device and Processor objects.
 706          */
 707         BT_SET(acpinex_object_type_mask, ACPI_TYPE_PROCESSOR);
 708         BT_SET(acpinex_object_type_mask, ACPI_TYPE_DEVICE);
 709 }
 710 
 711 void
 712 acpinex_event_fini(void)
 713 {
 714         bzero(acpinex_object_type_mask, sizeof (acpinex_object_type_mask));
 715 }