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