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 } 716