Ported ACPI CA (from the nice guys at Intel) to ReactOS (ACPI bus driver).
[reactos.git] / reactos / drivers / bus / acpi / events / evmisc.c
1 /******************************************************************************
2 *
3 * Module Name: evmisc - ACPI device notification handler dispatch
4 * and ACPI Global Lock support
5 * $Revision: 1.1 $
6 *
7 *****************************************************************************/
8
9 /*
10 * Copyright (C) 2000, 2001 R. Byron Moore
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 */
26
27 #include "acpi.h"
28 #include "acevents.h"
29 #include "acnamesp.h"
30 #include "acinterp.h"
31 #include "achware.h"
32
33 #define _COMPONENT ACPI_EVENTS
34 MODULE_NAME ("evmisc")
35
36
37 /**************************************************************************
38 *
39 * FUNCTION: Acpi_ev_queue_notify_request
40 *
41 * PARAMETERS:
42 *
43 * RETURN: None.
44 *
45 * DESCRIPTION: Dispatch a device notification event to a previously
46 * installed handler.
47 *
48 *************************************************************************/
49
50 ACPI_STATUS
51 acpi_ev_queue_notify_request (
52 ACPI_NAMESPACE_NODE *node,
53 u32 notify_value)
54 {
55 ACPI_OPERAND_OBJECT *obj_desc;
56 ACPI_OPERAND_OBJECT *handler_obj = NULL;
57 ACPI_GENERIC_STATE *notify_info;
58 ACPI_STATUS status = AE_OK;
59
60
61 /*
62 * For value 1 (Ejection Request), some device method may need to be run.
63 * For value 2 (Device Wake) if _PRW exists, the _PS0 method may need to be run.
64 * For value 0x80 (Status Change) on the power button or sleep button,
65 * initiate soft-off or sleep operation?
66 */
67
68 switch (notify_value) {
69 case 0:
70 break;
71
72 case 1:
73 break;
74
75 case 2:
76 break;
77
78 case 0x80:
79 break;
80
81 default:
82 break;
83 }
84
85
86 /*
87 * Get the notify object attached to the device Node
88 */
89
90 obj_desc = acpi_ns_get_attached_object ((ACPI_HANDLE) node);
91 if (obj_desc) {
92
93 /* We have the notify object, Get the right handler */
94
95 switch (node->type) {
96 case ACPI_TYPE_DEVICE:
97 if (notify_value <= MAX_SYS_NOTIFY) {
98 handler_obj = obj_desc->device.sys_handler;
99 }
100 else {
101 handler_obj = obj_desc->device.drv_handler;
102 }
103 break;
104
105 case ACPI_TYPE_THERMAL:
106 if (notify_value <= MAX_SYS_NOTIFY) {
107 handler_obj = obj_desc->thermal_zone.sys_handler;
108 }
109 else {
110 handler_obj = obj_desc->thermal_zone.drv_handler;
111 }
112 break;
113 }
114 }
115
116
117 /* If there is any handler to run, schedule the dispatcher */
118
119 if ((acpi_gbl_sys_notify.handler && (notify_value <= MAX_SYS_NOTIFY)) ||
120 (acpi_gbl_drv_notify.handler && (notify_value > MAX_SYS_NOTIFY)) ||
121 handler_obj) {
122
123 notify_info = acpi_cm_create_generic_state ();
124 if (!notify_info) {
125 return (AE_NO_MEMORY);
126 }
127
128 notify_info->notify.node = node;
129 notify_info->notify.value = (u16) notify_value;
130 notify_info->notify.handler_obj = handler_obj;
131
132 status = acpi_os_queue_for_execution (OSD_PRIORITY_HIGH,
133 acpi_ev_notify_dispatch, notify_info);
134 if (ACPI_FAILURE (status)) {
135 acpi_cm_delete_generic_state (notify_info);
136 }
137 }
138
139 if (!handler_obj) {
140 /* There is no per-device notify handler for this device */
141
142 }
143
144
145 return (status);
146 }
147
148
149 /**************************************************************************
150 *
151 * FUNCTION: Acpi_ev_notify_dispatch
152 *
153 * PARAMETERS:
154 *
155 * RETURN: None.
156 *
157 * DESCRIPTION: Dispatch a device notification event to a previously
158 * installed handler.
159 *
160 *************************************************************************/
161
162 void
163 acpi_ev_notify_dispatch (
164 void *context)
165 {
166 ACPI_GENERIC_STATE *notify_info = (ACPI_GENERIC_STATE *) context;
167 NOTIFY_HANDLER global_handler = NULL;
168 void *global_context = NULL;
169 ACPI_OPERAND_OBJECT *handler_obj;
170
171
172 /*
173 * We will invoke a global notify handler if installed.
174 * This is done _before_ we invoke the per-device handler attached to the device.
175 */
176
177 if (notify_info->notify.value <= MAX_SYS_NOTIFY) {
178 /* Global system notification handler */
179
180 if (acpi_gbl_sys_notify.handler) {
181 global_handler = acpi_gbl_sys_notify.handler;
182 global_context = acpi_gbl_sys_notify.context;
183 }
184 }
185
186 else {
187 /* Global driver notification handler */
188
189 if (acpi_gbl_drv_notify.handler) {
190 global_handler = acpi_gbl_drv_notify.handler;
191 global_context = acpi_gbl_drv_notify.context;
192 }
193 }
194
195
196 /* Invoke the system handler first, if present */
197
198 if (global_handler) {
199 global_handler (notify_info->notify.node, notify_info->notify.value, global_context);
200 }
201
202 /* Now invoke the per-device handler, if present */
203
204 handler_obj = notify_info->notify.handler_obj;
205 if (handler_obj) {
206 handler_obj->notify_handler.handler (notify_info->notify.node, notify_info->notify.value,
207 handler_obj->notify_handler.context);
208 }
209
210
211 /* All done with the info object */
212
213 acpi_cm_delete_generic_state (notify_info);
214 }
215
216
217 /***************************************************************************
218 *
219 * FUNCTION: Acpi_ev_global_lock_thread
220 *
221 * RETURN: None
222 *
223 * DESCRIPTION: Invoked by SCI interrupt handler upon acquisition of the
224 * Global Lock. Simply signal all threads that are waiting
225 * for the lock.
226 *
227 **************************************************************************/
228
229 static void
230 acpi_ev_global_lock_thread (
231 void *context)
232 {
233
234 /* Signal threads that are waiting for the lock */
235
236 if (acpi_gbl_global_lock_thread_count) {
237 /* Send sufficient units to the semaphore */
238
239 acpi_os_signal_semaphore (acpi_gbl_global_lock_semaphore,
240 acpi_gbl_global_lock_thread_count);
241 }
242 }
243
244
245 /***************************************************************************
246 *
247 * FUNCTION: Acpi_ev_global_lock_handler
248 *
249 * RETURN: Status
250 *
251 * DESCRIPTION: Invoked directly from the SCI handler when a global lock
252 * release interrupt occurs. Grab the global lock and queue
253 * the global lock thread for execution
254 *
255 **************************************************************************/
256
257 static u32
258 acpi_ev_global_lock_handler (
259 void *context)
260 {
261 u8 acquired = FALSE;
262 void *global_lock;
263
264
265 /*
266 * Attempt to get the lock
267 * If we don't get it now, it will be marked pending and we will
268 * take another interrupt when it becomes free.
269 */
270
271 global_lock = acpi_gbl_FACS->global_lock;
272 ACPI_ACQUIRE_GLOBAL_LOCK (global_lock, acquired);
273 if (acquired) {
274 /* Got the lock, now wake all threads waiting for it */
275
276 acpi_gbl_global_lock_acquired = TRUE;
277
278 /* Run the Global Lock thread which will signal all waiting threads */
279
280 acpi_os_queue_for_execution (OSD_PRIORITY_HIGH, acpi_ev_global_lock_thread,
281 context);
282 }
283
284 return (INTERRUPT_HANDLED);
285 }
286
287
288 /***************************************************************************
289 *
290 * FUNCTION: Acpi_ev_init_global_lock_handler
291 *
292 * RETURN: Status
293 *
294 * DESCRIPTION: Install a handler for the global lock release event
295 *
296 **************************************************************************/
297
298 ACPI_STATUS
299 acpi_ev_init_global_lock_handler (void)
300 {
301 ACPI_STATUS status;
302
303
304 acpi_gbl_global_lock_present = TRUE;
305 status = acpi_install_fixed_event_handler (ACPI_EVENT_GLOBAL,
306 acpi_ev_global_lock_handler, NULL);
307
308 /*
309 * If the global lock does not exist on this platform, the attempt
310 * to enable GBL_STS will fail (the GBL_EN bit will not stick)
311 * Map to AE_OK, but mark global lock as not present.
312 * Any attempt to actually use the global lock will be flagged
313 * with an error.
314 */
315 if (status == AE_NO_HARDWARE_RESPONSE) {
316 acpi_gbl_global_lock_present = FALSE;
317 status = AE_OK;
318 }
319
320 return (status);
321 }
322
323
324 /***************************************************************************
325 *
326 * FUNCTION: Acpi_ev_acquire_global_lock
327 *
328 * RETURN: Status
329 *
330 * DESCRIPTION: Attempt to gain ownership of the Global Lock.
331 *
332 **************************************************************************/
333
334 ACPI_STATUS
335 acpi_ev_acquire_global_lock(void)
336 {
337 ACPI_STATUS status = AE_OK;
338 u8 acquired = FALSE;
339 void *global_lock;
340
341
342 /* Make sure that we actually have a global lock */
343
344 if (!acpi_gbl_global_lock_present) {
345 return (AE_NO_GLOBAL_LOCK);
346 }
347
348 /* One more thread wants the global lock */
349
350 acpi_gbl_global_lock_thread_count++;
351
352
353 /* If we (OS side) have the hardware lock already, we are done */
354
355 if (acpi_gbl_global_lock_acquired) {
356 return (AE_OK);
357 }
358
359 /* Only if the FACS is valid */
360
361 if (!acpi_gbl_FACS) {
362 return (AE_OK);
363 }
364
365
366 /* We must acquire the actual hardware lock */
367
368 global_lock = acpi_gbl_FACS->global_lock;
369 ACPI_ACQUIRE_GLOBAL_LOCK (global_lock, acquired);
370 if (acquired) {
371 /* We got the lock */
372
373 acpi_gbl_global_lock_acquired = TRUE;
374
375 return (AE_OK);
376 }
377
378
379 /*
380 * Did not get the lock. The pending bit was set above, and we must now
381 * wait until we get the global lock released interrupt.
382 */
383
384 /*
385 * Acquire the global lock semaphore first.
386 * Since this wait will block, we must release the interpreter
387 */
388
389 status = acpi_aml_system_wait_semaphore (acpi_gbl_global_lock_semaphore,
390 ACPI_UINT32_MAX);
391
392 return (status);
393 }
394
395
396 /***************************************************************************
397 *
398 * FUNCTION: Acpi_ev_release_global_lock
399 *
400 * DESCRIPTION: Releases ownership of the Global Lock.
401 *
402 **************************************************************************/
403
404 void
405 acpi_ev_release_global_lock (void)
406 {
407 u8 pending = FALSE;
408 void *global_lock;
409
410
411 if (!acpi_gbl_global_lock_thread_count) {
412 REPORT_WARNING(("Global Lock has not be acquired, cannot release\n"));
413 return;
414 }
415
416 /* One fewer thread has the global lock */
417
418 acpi_gbl_global_lock_thread_count--;
419
420 /* Have all threads released the lock? */
421
422 if (!acpi_gbl_global_lock_thread_count) {
423 /*
424 * No more threads holding lock, we can do the actual hardware
425 * release
426 */
427
428 global_lock = acpi_gbl_FACS->global_lock;
429 ACPI_RELEASE_GLOBAL_LOCK (global_lock, pending);
430 acpi_gbl_global_lock_acquired = FALSE;
431
432 /*
433 * If the pending bit was set, we must write GBL_RLS to the control
434 * register
435 */
436 if (pending) {
437 acpi_hw_register_bit_access (ACPI_WRITE, ACPI_MTX_LOCK,
438 GBL_RLS, 1);
439 }
440 }
441
442 return;
443 }