acpinex_event.c (a3114836) acpinex_event.c (26f3cdf0)
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/*
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.
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{
491 ACPI_STATUS status;
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 }
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 }
508 if (ACPI_SUCCESS(AcpiInstallNotifyHandler(hdl, ACPI_SYSTEM_NOTIFY,
509 acpinex_event_system_handler, arg))) {
510 status = AcpiInstallNotifyHandler(hdl, ACPI_SYSTEM_NOTIFY,
511 acpinex_event_system_handler, arg);
512 if (status == AE_OK || status == AE_ALREADY_EXISTS) {
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}