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