:pserver:cvsanon@mok.lvcm.com:/CVS/ReactOS reactos
[reactos.git] / 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$
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 }