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 43int acpinex_event_support_remove = 0; 44 45static volatile uint_t acpinex_dr_event_cnt = 0; 46static 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 */ 52static int 53acpinex_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 */ 156static void 157acpinex_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 205struct 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 213static ACPI_STATUS 214acpinex_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 */ 310static void 311acpinex_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 377static void 378acpinex_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 */ 486static int 487acpinex_event_install_handler(ACPI_HANDLE hdl, void *arg, 488 ACPI_DEVICE_INFO *infop, acpidev_data_handle_t dhdl) 489{
| 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 44int acpinex_event_support_remove = 0; 45 46static volatile uint_t acpinex_dr_event_cnt = 0; 47static 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 */ 53static int 54acpinex_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 */ 157static void 158acpinex_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 206struct 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 214static ACPI_STATUS 215acpinex_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 */ 311static void 312acpinex_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 378static void 379acpinex_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 */ 487static int 488acpinex_event_install_handler(ACPI_HANDLE hdl, void *arg, 489 ACPI_DEVICE_INFO *infop, acpidev_data_handle_t dhdl) 490{
|
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 */ 529static int 530acpinex_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 */ 560static int 561acpinex_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 659int 660acpinex_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 696void 697acpinex_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 708void 709acpinex_event_fini(void) 710{ 711 bzero(acpinex_object_type_mask, sizeof (acpinex_object_type_mask)); 712}
| 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 */ 532static int 533acpinex_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 */ 563static int 564acpinex_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 662int 663acpinex_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 699void 700acpinex_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 711void 712acpinex_event_fini(void) 713{ 714 bzero(acpinex_object_type_mask, sizeof (acpinex_object_type_mask)); 715}
|