scroll mode for very long start menus
[reactos.git] / reactos / drivers / bus / acpi / dispatcher / dsmethod.c
1 /******************************************************************************
2 *
3 * Module Name: dsmethod - Parser/Interpreter interface - control method parsing
4 * $Revision: 1.1 $
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