:pserver:cvsanon@mok.lvcm.com:/CVS/ReactOS reactos
[reactos.git] / drivers / bus / acpi / dispatcher / dsmethod.c
1 /******************************************************************************
2  *
3  * Module Name: dsmethod - Parser/Interpreter interface - control method parsing
4  *              $Revision$
5  *
6  *****************************************************************************/
7
8 /*
9  *  Copyright (C) 2000, 2001 R. Byron Moore
10  *
11  *  This program is free software; you can redistribute it and/or modify
12  *  it under the terms of the GNU General Public License as published by
13  *  the Free Software Foundation; either version 2 of the License, or
14  *  (at your option) any later version.
15  *
16  *  This program is distributed in the hope that it will be useful,
17  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
18  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  *  GNU General Public License for more details.
20  *
21  *  You should have received a copy of the GNU General Public License
22  *  along with this program; if not, write to the Free Software
23  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24  */
25
26
27 #include "acpi.h"
28 #include "acparser.h"
29 #include "amlcode.h"
30 #include "acdispat.h"
31 #include "acinterp.h"
32 #include "acnamesp.h"
33 #include "actables.h"
34 #include "acdebug.h"
35
36
37 #define _COMPONENT          ACPI_DISPATCHER
38          MODULE_NAME         ("dsmethod")
39
40
41 /*******************************************************************************
42  *
43  * FUNCTION:    Acpi_ds_parse_method
44  *
45  * PARAMETERS:  Obj_handle      - Node of the method
46  *              Level           - Current nesting level
47  *              Context         - Points to a method counter
48  *              Return_value    - Not used
49  *
50  * RETURN:      Status
51  *
52  * DESCRIPTION: Call the parser and parse the AML that is
53  *              associated with the method.
54  *
55  * MUTEX:       Assumes parser is locked
56  *
57  ******************************************************************************/
58
59 ACPI_STATUS
60 acpi_ds_parse_method (
61         ACPI_HANDLE             obj_handle)
62 {
63         ACPI_STATUS             status;
64         ACPI_OPERAND_OBJECT     *obj_desc;
65         ACPI_PARSE_OBJECT       *op;
66         ACPI_NAMESPACE_NODE     *node;
67         ACPI_OWNER_ID           owner_id;
68
69
70         /* Parameter Validation */
71
72         if (!obj_handle) {
73                 return (AE_NULL_ENTRY);
74         }
75
76
77         /* Extract the method object from the method Node */
78
79         node = (ACPI_NAMESPACE_NODE *) obj_handle;
80         obj_desc = node->object;
81         if (!obj_desc) {
82                 return (AE_NULL_OBJECT);
83         }
84
85          /* Create a mutex for the method if there is a concurrency limit */
86
87         if ((obj_desc->method.concurrency != INFINITE_CONCURRENCY) &&
88                 (!obj_desc->method.semaphore)) {
89                 status = acpi_os_create_semaphore (obj_desc->method.concurrency,
90                                    obj_desc->method.concurrency,
91                                    &obj_desc->method.semaphore);
92                 if (ACPI_FAILURE (status)) {
93                         return (status);
94                 }
95         }
96
97         /*
98          * Allocate a new parser op to be the root of the parsed
99          * method tree
100          */
101         op = acpi_ps_alloc_op (AML_METHOD_OP);
102         if (!op) {
103                 return (AE_NO_MEMORY);
104         }
105
106         /* Init new op with the method name and pointer back to the Node */
107
108         acpi_ps_set_name (op, node->name);
109         op->node = node;
110
111
112         /*
113          * Parse the method, first pass
114          *
115          * The first pass load is
116          * where newly declared named objects are
117          * added into the namespace.  Actual evaluation of
118          * the named objects (what would be called a "second
119          * pass") happens during the actual execution of the
120          * method so that operands to the named objects can
121          * take on dynamic run-time values.
122          */
123         status = acpi_ps_parse_aml (op, obj_desc->method.pcode,
124                            obj_desc->method.pcode_length,
125                            ACPI_PARSE_LOAD_PASS1 | ACPI_PARSE_DELETE_TREE,
126                            node, NULL, NULL,
127                            acpi_ds_load1_begin_op, acpi_ds_load1_end_op);
128
129         if (ACPI_FAILURE (status)) {
130                 return (status);
131         }
132
133         /* Get a new Owner_id for objects created by this method */
134
135         owner_id = acpi_cm_allocate_owner_id (OWNER_TYPE_METHOD);
136         obj_desc->method.owning_id = owner_id;
137
138         /* Install the parsed tree in the method object */
139         /* TBD: [Restructure] Obsolete field? */
140
141         acpi_ps_delete_parse_tree (op);
142
143
144         return (status);
145 }
146
147
148 /*******************************************************************************
149  *
150  * FUNCTION:    Acpi_ds_begin_method_execution
151  *
152  * PARAMETERS:  Method_node         - Node of the method
153  *              Obj_desc            - The method object
154  *              Calling_method_node - Caller of this method (if non-null)
155  *
156  * RETURN:      Status
157  *
158  * DESCRIPTION: Prepare a method for execution.  Parses the method if necessary,
159  *              increments the thread count, and waits at the method semaphore
160  *              for clearance to execute.
161  *
162  * MUTEX:       Locks/unlocks parser.
163  *
164  ******************************************************************************/
165
166 ACPI_STATUS
167 acpi_ds_begin_method_execution (
168         ACPI_NAMESPACE_NODE     *method_node,
169         ACPI_OPERAND_OBJECT     *obj_desc,
170         ACPI_NAMESPACE_NODE     *calling_method_node)
171 {
172         ACPI_STATUS             status = AE_OK;
173
174
175         if (!method_node) {
176                 return (AE_NULL_ENTRY);
177         }
178
179
180         /*
181          * If there is a concurrency limit on this method, we need to
182          * obtain a unit from the method semaphore.
183          */
184         if (obj_desc->method.semaphore) {
185                 /*
186                  * Allow recursive method calls, up to the reentrancy/concurrency
187                  * limit imposed by the SERIALIZED rule and the Sync_level method
188                  * parameter.
189                  *
190                  * The point of this code is to avoid permanently blocking a
191                  * thread that is making recursive method calls.
192                  */
193                 if (method_node == calling_method_node) {
194                         if (obj_desc->method.thread_count >= obj_desc->method.concurrency) {
195                                 return (AE_AML_METHOD_LIMIT);
196                         }
197                 }
198
199                 /*
200                  * Get a unit from the method semaphore. This releases the
201                  * interpreter if we block
202                  */
203                 status = acpi_aml_system_wait_semaphore (obj_desc->method.semaphore,
204                                  WAIT_FOREVER);
205         }
206
207
208         /*
209          * Increment the method parse tree thread count since it has been
210          * reentered one more time (even if it is the same thread)
211          */
212         obj_desc->method.thread_count++;
213
214         return (status);
215 }
216
217
218 /*******************************************************************************
219  *
220  * FUNCTION:    Acpi_ds_call_control_method
221  *
222  * PARAMETERS:  Walk_state          - Current state of the walk
223  *              Op                  - Current Op to be walked
224  *
225  * RETURN:      Status
226  *
227  * DESCRIPTION: Transfer execution to a called control method
228  *
229  ******************************************************************************/
230
231 ACPI_STATUS
232 acpi_ds_call_control_method (
233         ACPI_WALK_LIST          *walk_list,
234         ACPI_WALK_STATE         *this_walk_state,
235         ACPI_PARSE_OBJECT       *op)
236 {
237         ACPI_STATUS             status;
238         ACPI_NAMESPACE_NODE     *method_node;
239         ACPI_OPERAND_OBJECT     *obj_desc;
240         ACPI_WALK_STATE         *next_walk_state;
241         ACPI_PARSE_STATE        *parser_state;
242         u32                     i;
243
244
245         /*
246          * Get the namespace entry for the control method we are about to call
247          */
248         method_node = this_walk_state->method_call_node;
249         if (!method_node) {
250                 return (AE_NULL_ENTRY);
251         }
252
253         obj_desc = acpi_ns_get_attached_object (method_node);
254         if (!obj_desc) {
255                 return (AE_NULL_OBJECT);
256         }
257
258
259         /* Init for new method, wait on concurrency semaphore */
260
261         status = acpi_ds_begin_method_execution (method_node, obj_desc,
262                           this_walk_state->method_node);
263         if (ACPI_FAILURE (status)) {
264                 return (status);
265         }
266
267         /* Create and initialize a new parser state */
268
269         parser_state = acpi_ps_create_state (obj_desc->method.pcode,
270                            obj_desc->method.pcode_length);
271         if (!parser_state) {
272                 return (AE_NO_MEMORY);
273         }
274
275         acpi_ps_init_scope (parser_state, NULL);
276         parser_state->start_node = method_node;
277
278
279         /* Create a new state for the preempting walk */
280
281         next_walk_state = acpi_ds_create_walk_state (obj_desc->method.owning_id,
282                           NULL, obj_desc, walk_list);
283         if (!next_walk_state) {
284                 /* TBD: delete parser state */
285
286                 return (AE_NO_MEMORY);
287         }
288
289         next_walk_state->walk_type          = WALK_METHOD;
290         next_walk_state->method_node        = method_node;
291         next_walk_state->parser_state       = parser_state;
292         next_walk_state->parse_flags        = this_walk_state->parse_flags;
293         next_walk_state->descending_callback = this_walk_state->descending_callback;
294         next_walk_state->ascending_callback = this_walk_state->ascending_callback;
295
296         /* The Next_op of the Next_walk will be the beginning of the method */
297         /* TBD: [Restructure] -- obsolete? */
298
299         next_walk_state->next_op = NULL;
300
301         /* Open a new scope */
302
303         status = acpi_ds_scope_stack_push (method_node,
304                            ACPI_TYPE_METHOD, next_walk_state);
305         if (ACPI_FAILURE (status)) {
306                 goto cleanup;
307         }
308
309
310         /*
311          * Initialize the arguments for the method.  The resolved
312          * arguments were put on the previous walk state's operand
313          * stack.  Operands on the previous walk state stack always
314          * start at index 0.
315          */
316         status = acpi_ds_method_data_init_args (&this_walk_state->operands[0],
317                          this_walk_state->num_operands,
318                          next_walk_state);
319         if (ACPI_FAILURE (status)) {
320                 goto cleanup;
321         }
322
323
324         /* Create and init a Root Node */
325
326         op = acpi_ps_alloc_op (AML_SCOPE_OP);
327         if (!op) {
328                 return (AE_NO_MEMORY);
329         }
330
331         status = acpi_ps_parse_aml (op, obj_desc->method.pcode,
332                           obj_desc->method.pcode_length,
333                           ACPI_PARSE_LOAD_PASS1 | ACPI_PARSE_DELETE_TREE,
334                           method_node, NULL, NULL,
335                           acpi_ds_load1_begin_op, acpi_ds_load1_end_op);
336         acpi_ps_delete_parse_tree (op);
337
338
339         /*
340          * Delete the operands on the previous walkstate operand stack
341          * (they were copied to new objects)
342          */
343         for (i = 0; i < obj_desc->method.param_count; i++) {
344                 acpi_cm_remove_reference (this_walk_state->operands [i]);
345                 this_walk_state->operands [i] = NULL;
346         }
347
348         /* Clear the operand stack */
349
350         this_walk_state->num_operands = 0;
351
352
353         return (AE_OK);
354
355
356         /* On error, we must delete the new walk state */
357
358 cleanup:
359         acpi_ds_terminate_control_method (next_walk_state);
360         acpi_ds_delete_walk_state (next_walk_state);
361         return (status);
362
363 }
364
365
366 /*******************************************************************************
367  *
368  * FUNCTION:    Acpi_ds_restart_control_method
369  *
370  * PARAMETERS:  Walk_state          - State of the method when it was preempted
371  *              Op                  - Pointer to new current op
372  *
373  * RETURN:      Status
374  *
375  * DESCRIPTION: Restart a method that was preempted
376  *
377  ******************************************************************************/
378
379 ACPI_STATUS
380 acpi_ds_restart_control_method (
381         ACPI_WALK_STATE         *walk_state,
382         ACPI_OPERAND_OBJECT     *return_desc)
383 {
384         ACPI_STATUS             status;
385
386
387         if (return_desc) {
388                 if (walk_state->return_used) {
389                         /*
390                          * Get the return value (if any) from the previous method.
391                          * NULL if no return value
392                          */
393                         status = acpi_ds_result_push (return_desc, walk_state);
394                         if (ACPI_FAILURE (status)) {
395                                 acpi_cm_remove_reference (return_desc);
396                                 return (status);
397                         }
398                 }
399
400                 else {
401                         /*
402                          * Delete the return value if it will not be used by the
403                          * calling method
404                          */
405                         acpi_cm_remove_reference (return_desc);
406                 }
407
408         }
409
410
411         return (AE_OK);
412 }
413
414
415 /*******************************************************************************
416  *
417  * FUNCTION:    Acpi_ds_terminate_control_method
418  *
419  * PARAMETERS:  Walk_state          - State of the method
420  *
421  * RETURN:      Status
422  *
423  * DESCRIPTION: Terminate a control method.  Delete everything that the method
424  *              created, delete all locals and arguments, and delete the parse
425  *              tree if requested.
426  *
427  ******************************************************************************/
428
429 ACPI_STATUS
430 acpi_ds_terminate_control_method (
431         ACPI_WALK_STATE         *walk_state)
432 {
433         ACPI_STATUS             status;
434         ACPI_OPERAND_OBJECT     *obj_desc;
435         ACPI_NAMESPACE_NODE     *method_node;
436
437
438         /* The method object should be stored in the walk state */
439
440         obj_desc = walk_state->method_desc;
441         if (!obj_desc) {
442                 return (AE_OK);
443         }
444
445         /* Delete all arguments and locals */
446
447         acpi_ds_method_data_delete_all (walk_state);
448
449         /*
450          * Lock the parser while we terminate this method.
451          * If this is the last thread executing the method,
452          * we have additional cleanup to perform
453          */
454         acpi_cm_acquire_mutex (ACPI_MTX_PARSER);
455
456
457         /* Signal completion of the execution of this method if necessary */
458
459         if (walk_state->method_desc->method.semaphore) {
460                 status = acpi_os_signal_semaphore (
461                                  walk_state->method_desc->method.semaphore, 1);
462         }
463
464         /* Decrement the thread count on the method parse tree */
465
466         walk_state->method_desc->method.thread_count--;
467         if (!walk_state->method_desc->method.thread_count) {
468                 /*
469                  * There are no more threads executing this method.  Perform
470                  * additional cleanup.
471                  *
472                  * The method Node is stored in the walk state
473                  */
474                 method_node = walk_state->method_node;
475
476                 /*
477                  * Delete any namespace entries created immediately underneath
478                  * the method
479                  */
480                 acpi_cm_acquire_mutex (ACPI_MTX_NAMESPACE);
481                 if (method_node->child) {
482                         acpi_ns_delete_namespace_subtree (method_node);
483                 }
484
485                 /*
486                  * Delete any namespace entries created anywhere else within
487                  * the namespace
488                  */
489                 acpi_ns_delete_namespace_by_owner (walk_state->method_desc->method.owning_id);
490                 acpi_cm_release_mutex (ACPI_MTX_NAMESPACE);
491         }
492
493         acpi_cm_release_mutex (ACPI_MTX_PARSER);
494         return (AE_OK);
495 }
496
497