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