Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /******************************************************************************* |
2 | * | |
3 | * Module Name: nseval - Object evaluation interfaces -- includes control | |
4 | * method lookup and execution. | |
5 | * | |
6 | ******************************************************************************/ | |
7 | ||
8 | /* | |
4a90c7e8 | 9 | * Copyright (C) 2000 - 2006, R. Byron Moore |
1da177e4 LT |
10 | * All rights reserved. |
11 | * | |
12 | * Redistribution and use in source and binary forms, with or without | |
13 | * modification, are permitted provided that the following conditions | |
14 | * are met: | |
15 | * 1. Redistributions of source code must retain the above copyright | |
16 | * notice, this list of conditions, and the following disclaimer, | |
17 | * without modification. | |
18 | * 2. Redistributions in binary form must reproduce at minimum a disclaimer | |
19 | * substantially similar to the "NO WARRANTY" disclaimer below | |
20 | * ("Disclaimer") and any redistribution must be conditioned upon | |
21 | * including a substantially similar Disclaimer requirement for further | |
22 | * binary redistribution. | |
23 | * 3. Neither the names of the above-listed copyright holders nor the names | |
24 | * of any contributors may be used to endorse or promote products derived | |
25 | * from this software without specific prior written permission. | |
26 | * | |
27 | * Alternatively, this software may be distributed under the terms of the | |
28 | * GNU General Public License ("GPL") version 2 as published by the Free | |
29 | * Software Foundation. | |
30 | * | |
31 | * NO WARRANTY | |
32 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
33 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
34 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR | |
35 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
36 | * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
37 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
38 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
39 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | |
40 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING | |
41 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
42 | * POSSIBILITY OF SUCH DAMAGES. | |
43 | */ | |
44 | ||
1da177e4 LT |
45 | #include <acpi/acpi.h> |
46 | #include <acpi/acparser.h> | |
47 | #include <acpi/acinterp.h> | |
48 | #include <acpi/acnamesp.h> | |
49 | ||
1da177e4 | 50 | #define _COMPONENT ACPI_NAMESPACE |
4be44fcd | 51 | ACPI_MODULE_NAME("nseval") |
1da177e4 | 52 | |
44f6c012 | 53 | /* Local prototypes */ |
44f6c012 | 54 | static acpi_status |
4be44fcd | 55 | acpi_ns_execute_control_method(struct acpi_parameter_info *info); |
44f6c012 | 56 | |
4be44fcd | 57 | static acpi_status acpi_ns_get_object_value(struct acpi_parameter_info *info); |
1da177e4 LT |
58 | |
59 | /******************************************************************************* | |
60 | * | |
61 | * FUNCTION: acpi_ns_evaluate_relative | |
62 | * | |
44f6c012 RM |
63 | * PARAMETERS: Pathname - Name of method to execute, If NULL, the |
64 | * handle is the object to execute | |
65 | * Info - Method info block, contains: | |
66 | * return_object - Where to put method's return value (if | |
67 | * any). If NULL, no value is returned. | |
68 | * Params - List of parameters to pass to the method, | |
69 | * terminated by NULL. Params itself may be | |
70 | * NULL if no parameters are being passed. | |
1da177e4 LT |
71 | * |
72 | * RETURN: Status | |
73 | * | |
44f6c012 | 74 | * DESCRIPTION: Evaluate the object or find and execute the requested method |
1da177e4 LT |
75 | * |
76 | * MUTEX: Locks Namespace | |
77 | * | |
78 | ******************************************************************************/ | |
79 | ||
80 | acpi_status | |
4be44fcd | 81 | acpi_ns_evaluate_relative(char *pathname, struct acpi_parameter_info *info) |
1da177e4 | 82 | { |
4be44fcd LB |
83 | acpi_status status; |
84 | struct acpi_namespace_node *node = NULL; | |
85 | union acpi_generic_state *scope_info; | |
86 | char *internal_path = NULL; | |
1da177e4 | 87 | |
4be44fcd | 88 | ACPI_FUNCTION_TRACE("ns_evaluate_relative"); |
1da177e4 LT |
89 | |
90 | /* | |
91 | * Must have a valid object handle | |
92 | */ | |
93 | if (!info || !info->node) { | |
4be44fcd | 94 | return_ACPI_STATUS(AE_BAD_PARAMETER); |
1da177e4 LT |
95 | } |
96 | ||
97 | /* Build an internal name string for the method */ | |
98 | ||
4be44fcd LB |
99 | status = acpi_ns_internalize_name(pathname, &internal_path); |
100 | if (ACPI_FAILURE(status)) { | |
101 | return_ACPI_STATUS(status); | |
1da177e4 LT |
102 | } |
103 | ||
4be44fcd | 104 | scope_info = acpi_ut_create_generic_state(); |
1da177e4 LT |
105 | if (!scope_info) { |
106 | goto cleanup1; | |
107 | } | |
108 | ||
109 | /* Get the prefix handle and Node */ | |
110 | ||
4be44fcd LB |
111 | status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); |
112 | if (ACPI_FAILURE(status)) { | |
1da177e4 LT |
113 | goto cleanup; |
114 | } | |
115 | ||
4be44fcd | 116 | info->node = acpi_ns_map_handle_to_node(info->node); |
1da177e4 | 117 | if (!info->node) { |
4be44fcd | 118 | (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); |
1da177e4 LT |
119 | status = AE_BAD_PARAMETER; |
120 | goto cleanup; | |
121 | } | |
122 | ||
123 | /* Lookup the name in the namespace */ | |
124 | ||
125 | scope_info->scope.node = info->node; | |
4be44fcd LB |
126 | status = acpi_ns_lookup(scope_info, internal_path, ACPI_TYPE_ANY, |
127 | ACPI_IMODE_EXECUTE, ACPI_NS_NO_UPSEARCH, NULL, | |
128 | &node); | |
1da177e4 | 129 | |
4be44fcd | 130 | (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); |
1da177e4 | 131 | |
4be44fcd LB |
132 | if (ACPI_FAILURE(status)) { |
133 | ACPI_DEBUG_PRINT((ACPI_DB_NAMES, "Object [%s] not found [%s]\n", | |
134 | pathname, acpi_format_exception(status))); | |
1da177e4 LT |
135 | goto cleanup; |
136 | } | |
137 | ||
138 | /* | |
139 | * Now that we have a handle to the object, we can attempt to evaluate it. | |
140 | */ | |
4be44fcd LB |
141 | ACPI_DEBUG_PRINT((ACPI_DB_NAMES, "%s [%p] Value %p\n", |
142 | pathname, node, acpi_ns_get_attached_object(node))); | |
1da177e4 LT |
143 | |
144 | info->node = node; | |
4be44fcd | 145 | status = acpi_ns_evaluate_by_handle(info); |
1da177e4 | 146 | |
4be44fcd LB |
147 | ACPI_DEBUG_PRINT((ACPI_DB_NAMES, |
148 | "*** Completed eval of object %s ***\n", pathname)); | |
1da177e4 | 149 | |
4be44fcd LB |
150 | cleanup: |
151 | acpi_ut_delete_generic_state(scope_info); | |
1da177e4 | 152 | |
4be44fcd LB |
153 | cleanup1: |
154 | ACPI_MEM_FREE(internal_path); | |
155 | return_ACPI_STATUS(status); | |
1da177e4 LT |
156 | } |
157 | ||
1da177e4 LT |
158 | /******************************************************************************* |
159 | * | |
160 | * FUNCTION: acpi_ns_evaluate_by_name | |
161 | * | |
44f6c012 RM |
162 | * PARAMETERS: Pathname - Fully qualified pathname to the object |
163 | * Info - Method info block, contains: | |
1da177e4 LT |
164 | * return_object - Where to put method's return value (if |
165 | * any). If NULL, no value is returned. | |
166 | * Params - List of parameters to pass to the method, | |
167 | * terminated by NULL. Params itself may be | |
168 | * NULL if no parameters are being passed. | |
169 | * | |
170 | * RETURN: Status | |
171 | * | |
44f6c012 RM |
172 | * DESCRIPTION: Evaluate the object or rind and execute the requested method |
173 | * passing the given parameters | |
1da177e4 LT |
174 | * |
175 | * MUTEX: Locks Namespace | |
176 | * | |
177 | ******************************************************************************/ | |
178 | ||
179 | acpi_status | |
4be44fcd | 180 | acpi_ns_evaluate_by_name(char *pathname, struct acpi_parameter_info *info) |
1da177e4 | 181 | { |
4be44fcd LB |
182 | acpi_status status; |
183 | char *internal_path = NULL; | |
1da177e4 | 184 | |
4be44fcd | 185 | ACPI_FUNCTION_TRACE("ns_evaluate_by_name"); |
1da177e4 LT |
186 | |
187 | /* Build an internal name string for the method */ | |
188 | ||
4be44fcd LB |
189 | status = acpi_ns_internalize_name(pathname, &internal_path); |
190 | if (ACPI_FAILURE(status)) { | |
191 | return_ACPI_STATUS(status); | |
1da177e4 LT |
192 | } |
193 | ||
4be44fcd LB |
194 | status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); |
195 | if (ACPI_FAILURE(status)) { | |
1da177e4 LT |
196 | goto cleanup; |
197 | } | |
198 | ||
199 | /* Lookup the name in the namespace */ | |
200 | ||
4be44fcd LB |
201 | status = acpi_ns_lookup(NULL, internal_path, ACPI_TYPE_ANY, |
202 | ACPI_IMODE_EXECUTE, ACPI_NS_NO_UPSEARCH, NULL, | |
203 | &info->node); | |
1da177e4 | 204 | |
4be44fcd | 205 | (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); |
1da177e4 | 206 | |
4be44fcd LB |
207 | if (ACPI_FAILURE(status)) { |
208 | ACPI_DEBUG_PRINT((ACPI_DB_NAMES, | |
209 | "Object at [%s] was not found, status=%.4X\n", | |
210 | pathname, status)); | |
1da177e4 LT |
211 | goto cleanup; |
212 | } | |
213 | ||
214 | /* | |
215 | * Now that we have a handle to the object, we can attempt to evaluate it. | |
216 | */ | |
4be44fcd LB |
217 | ACPI_DEBUG_PRINT((ACPI_DB_NAMES, "%s [%p] Value %p\n", |
218 | pathname, info->node, | |
219 | acpi_ns_get_attached_object(info->node))); | |
1da177e4 | 220 | |
4be44fcd | 221 | status = acpi_ns_evaluate_by_handle(info); |
1da177e4 | 222 | |
4be44fcd LB |
223 | ACPI_DEBUG_PRINT((ACPI_DB_NAMES, |
224 | "*** Completed eval of object %s ***\n", pathname)); | |
1da177e4 | 225 | |
4be44fcd | 226 | cleanup: |
1da177e4 LT |
227 | |
228 | /* Cleanup */ | |
229 | ||
230 | if (internal_path) { | |
4be44fcd | 231 | ACPI_MEM_FREE(internal_path); |
1da177e4 LT |
232 | } |
233 | ||
4be44fcd | 234 | return_ACPI_STATUS(status); |
1da177e4 LT |
235 | } |
236 | ||
1da177e4 LT |
237 | /******************************************************************************* |
238 | * | |
239 | * FUNCTION: acpi_ns_evaluate_by_handle | |
240 | * | |
44f6c012 RM |
241 | * PARAMETERS: Info - Method info block, contains: |
242 | * Node - Method/Object Node to execute | |
243 | * Parameters - List of parameters to pass to the method, | |
244 | * terminated by NULL. Params itself may be | |
1da177e4 | 245 | * NULL if no parameters are being passed. |
44f6c012 RM |
246 | * return_object - Where to put method's return value (if |
247 | * any). If NULL, no value is returned. | |
248 | * parameter_type - Type of Parameter list | |
249 | * return_object - Where to put method's return value (if | |
250 | * any). If NULL, no value is returned. | |
1da177e4 LT |
251 | * |
252 | * RETURN: Status | |
253 | * | |
44f6c012 RM |
254 | * DESCRIPTION: Evaluate object or execute the requested method passing the |
255 | * given parameters | |
1da177e4 LT |
256 | * |
257 | * MUTEX: Locks Namespace | |
258 | * | |
259 | ******************************************************************************/ | |
260 | ||
4be44fcd | 261 | acpi_status acpi_ns_evaluate_by_handle(struct acpi_parameter_info *info) |
1da177e4 | 262 | { |
4be44fcd | 263 | acpi_status status; |
1da177e4 | 264 | |
4be44fcd | 265 | ACPI_FUNCTION_TRACE("ns_evaluate_by_handle"); |
1da177e4 LT |
266 | |
267 | /* Check if namespace has been initialized */ | |
268 | ||
269 | if (!acpi_gbl_root_node) { | |
4be44fcd | 270 | return_ACPI_STATUS(AE_NO_NAMESPACE); |
1da177e4 LT |
271 | } |
272 | ||
273 | /* Parameter Validation */ | |
274 | ||
275 | if (!info) { | |
4be44fcd | 276 | return_ACPI_STATUS(AE_BAD_PARAMETER); |
1da177e4 LT |
277 | } |
278 | ||
279 | /* Initialize the return value to an invalid object */ | |
280 | ||
281 | info->return_object = NULL; | |
282 | ||
283 | /* Get the prefix handle and Node */ | |
284 | ||
4be44fcd LB |
285 | status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); |
286 | if (ACPI_FAILURE(status)) { | |
287 | return_ACPI_STATUS(status); | |
1da177e4 LT |
288 | } |
289 | ||
4be44fcd | 290 | info->node = acpi_ns_map_handle_to_node(info->node); |
1da177e4 | 291 | if (!info->node) { |
4be44fcd LB |
292 | (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); |
293 | return_ACPI_STATUS(AE_BAD_PARAMETER); | |
1da177e4 LT |
294 | } |
295 | ||
296 | /* | |
297 | * For a method alias, we must grab the actual method node so that proper | |
298 | * scoping context will be established before execution. | |
299 | */ | |
4be44fcd LB |
300 | if (acpi_ns_get_type(info->node) == ACPI_TYPE_LOCAL_METHOD_ALIAS) { |
301 | info->node = | |
302 | ACPI_CAST_PTR(struct acpi_namespace_node, | |
303 | info->node->object); | |
1da177e4 LT |
304 | } |
305 | ||
306 | /* | |
307 | * Two major cases here: | |
308 | * 1) The object is an actual control method -- execute it. | |
309 | * 2) The object is not a method -- just return it's current value | |
310 | * | |
311 | * In both cases, the namespace is unlocked by the acpi_ns* procedure | |
312 | */ | |
4be44fcd | 313 | if (acpi_ns_get_type(info->node) == ACPI_TYPE_METHOD) { |
1da177e4 LT |
314 | /* |
315 | * Case 1) We have an actual control method to execute | |
316 | */ | |
4be44fcd LB |
317 | status = acpi_ns_execute_control_method(info); |
318 | } else { | |
1da177e4 LT |
319 | /* |
320 | * Case 2) Object is NOT a method, just return its current value | |
321 | */ | |
4be44fcd | 322 | status = acpi_ns_get_object_value(info); |
1da177e4 LT |
323 | } |
324 | ||
325 | /* | |
326 | * Check if there is a return value on the stack that must be dealt with | |
327 | */ | |
328 | if (status == AE_CTRL_RETURN_VALUE) { | |
329 | /* Map AE_CTRL_RETURN_VALUE to AE_OK, we are done with it */ | |
330 | ||
331 | status = AE_OK; | |
332 | } | |
333 | ||
334 | /* | |
335 | * Namespace was unlocked by the handling acpi_ns* function, so we | |
336 | * just return | |
337 | */ | |
4be44fcd | 338 | return_ACPI_STATUS(status); |
1da177e4 LT |
339 | } |
340 | ||
1da177e4 LT |
341 | /******************************************************************************* |
342 | * | |
343 | * FUNCTION: acpi_ns_execute_control_method | |
344 | * | |
44f6c012 RM |
345 | * PARAMETERS: Info - Method info block, contains: |
346 | * Node - Method Node to execute | |
0c9938cc | 347 | * obj_desc - Method object |
44f6c012 RM |
348 | * Parameters - List of parameters to pass to the method, |
349 | * terminated by NULL. Params itself may be | |
350 | * NULL if no parameters are being passed. | |
351 | * return_object - Where to put method's return value (if | |
352 | * any). If NULL, no value is returned. | |
353 | * parameter_type - Type of Parameter list | |
354 | * return_object - Where to put method's return value (if | |
355 | * any). If NULL, no value is returned. | |
1da177e4 LT |
356 | * |
357 | * RETURN: Status | |
358 | * | |
359 | * DESCRIPTION: Execute the requested method passing the given parameters | |
360 | * | |
361 | * MUTEX: Assumes namespace is locked | |
362 | * | |
363 | ******************************************************************************/ | |
364 | ||
44f6c012 | 365 | static acpi_status |
4be44fcd | 366 | acpi_ns_execute_control_method(struct acpi_parameter_info *info) |
1da177e4 | 367 | { |
4be44fcd | 368 | acpi_status status; |
1da177e4 | 369 | |
4be44fcd | 370 | ACPI_FUNCTION_TRACE("ns_execute_control_method"); |
1da177e4 LT |
371 | |
372 | /* Verify that there is a method associated with this object */ | |
373 | ||
4be44fcd | 374 | info->obj_desc = acpi_ns_get_attached_object(info->node); |
0c9938cc | 375 | if (!info->obj_desc) { |
b8e4d893 | 376 | ACPI_ERROR((AE_INFO, "No attached method object")); |
1da177e4 | 377 | |
4be44fcd LB |
378 | (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); |
379 | return_ACPI_STATUS(AE_NULL_OBJECT); | |
1da177e4 LT |
380 | } |
381 | ||
4be44fcd LB |
382 | ACPI_DUMP_PATHNAME(info->node, "Execute Method:", |
383 | ACPI_LV_INFO, _COMPONENT); | |
1da177e4 | 384 | |
4be44fcd LB |
385 | ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Method at AML address %p Length %X\n", |
386 | info->obj_desc->method.aml_start + 1, | |
387 | info->obj_desc->method.aml_length - 1)); | |
1da177e4 LT |
388 | |
389 | /* | |
390 | * Unlock the namespace before execution. This allows namespace access | |
391 | * via the external Acpi* interfaces while a method is being executed. | |
392 | * However, any namespace deletion must acquire both the namespace and | |
393 | * interpreter locks to ensure that no thread is using the portion of the | |
394 | * namespace that is being deleted. | |
395 | */ | |
4be44fcd LB |
396 | status = acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); |
397 | if (ACPI_FAILURE(status)) { | |
398 | return_ACPI_STATUS(status); | |
1da177e4 LT |
399 | } |
400 | ||
401 | /* | |
402 | * Execute the method via the interpreter. The interpreter is locked | |
403 | * here before calling into the AML parser | |
404 | */ | |
4be44fcd LB |
405 | status = acpi_ex_enter_interpreter(); |
406 | if (ACPI_FAILURE(status)) { | |
407 | return_ACPI_STATUS(status); | |
1da177e4 LT |
408 | } |
409 | ||
4be44fcd LB |
410 | status = acpi_ps_execute_method(info); |
411 | acpi_ex_exit_interpreter(); | |
1da177e4 | 412 | |
4be44fcd | 413 | return_ACPI_STATUS(status); |
1da177e4 LT |
414 | } |
415 | ||
1da177e4 LT |
416 | /******************************************************************************* |
417 | * | |
418 | * FUNCTION: acpi_ns_get_object_value | |
419 | * | |
44f6c012 RM |
420 | * PARAMETERS: Info - Method info block, contains: |
421 | * Node - Object's NS node | |
422 | * return_object - Where to put object value (if | |
423 | * any). If NULL, no value is returned. | |
1da177e4 LT |
424 | * |
425 | * RETURN: Status | |
426 | * | |
427 | * DESCRIPTION: Return the current value of the object | |
428 | * | |
429 | * MUTEX: Assumes namespace is locked, leaves namespace unlocked | |
430 | * | |
431 | ******************************************************************************/ | |
432 | ||
4be44fcd | 433 | static acpi_status acpi_ns_get_object_value(struct acpi_parameter_info *info) |
1da177e4 | 434 | { |
4be44fcd LB |
435 | acpi_status status = AE_OK; |
436 | struct acpi_namespace_node *resolved_node = info->node; | |
1da177e4 | 437 | |
4be44fcd | 438 | ACPI_FUNCTION_TRACE("ns_get_object_value"); |
1da177e4 LT |
439 | |
440 | /* | |
441 | * Objects require additional resolution steps (e.g., the Node may be a | |
442 | * field that must be read, etc.) -- we can't just grab the object out of | |
443 | * the node. | |
444 | */ | |
445 | ||
446 | /* | |
447 | * Use resolve_node_to_value() to get the associated value. This call always | |
448 | * deletes obj_desc (allocated above). | |
449 | * | |
450 | * NOTE: we can get away with passing in NULL for a walk state because | |
451 | * obj_desc is guaranteed to not be a reference to either a method local or | |
452 | * a method argument (because this interface can only be called from the | |
453 | * acpi_evaluate external interface, never called from a running method.) | |
454 | * | |
455 | * Even though we do not directly invoke the interpreter for this, we must | |
456 | * enter it because we could access an opregion. The opregion access code | |
457 | * assumes that the interpreter is locked. | |
458 | * | |
459 | * We must release the namespace lock before entering the intepreter. | |
460 | */ | |
4be44fcd LB |
461 | status = acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); |
462 | if (ACPI_FAILURE(status)) { | |
463 | return_ACPI_STATUS(status); | |
1da177e4 LT |
464 | } |
465 | ||
4be44fcd LB |
466 | status = acpi_ex_enter_interpreter(); |
467 | if (ACPI_SUCCESS(status)) { | |
468 | status = acpi_ex_resolve_node_to_value(&resolved_node, NULL); | |
1da177e4 LT |
469 | /* |
470 | * If acpi_ex_resolve_node_to_value() succeeded, the return value was placed | |
471 | * in resolved_node. | |
472 | */ | |
4be44fcd | 473 | acpi_ex_exit_interpreter(); |
1da177e4 | 474 | |
4be44fcd | 475 | if (ACPI_SUCCESS(status)) { |
1da177e4 LT |
476 | status = AE_CTRL_RETURN_VALUE; |
477 | info->return_object = ACPI_CAST_PTR | |
4be44fcd LB |
478 | (union acpi_operand_object, resolved_node); |
479 | ACPI_DEBUG_PRINT((ACPI_DB_NAMES, | |
480 | "Returning object %p [%s]\n", | |
481 | info->return_object, | |
482 | acpi_ut_get_object_type_name(info-> | |
483 | return_object))); | |
1da177e4 LT |
484 | } |
485 | } | |
486 | ||
487 | /* Namespace is unlocked */ | |
488 | ||
4be44fcd | 489 | return_ACPI_STATUS(status); |
1da177e4 | 490 | } |