diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/acpi/executer/exresolv.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/acpi/executer/exresolv.c')
-rw-r--r-- | drivers/acpi/executer/exresolv.c | 546 |
1 files changed, 546 insertions, 0 deletions
diff --git a/drivers/acpi/executer/exresolv.c b/drivers/acpi/executer/exresolv.c new file mode 100644 index 000000000000..7be604911156 --- /dev/null +++ b/drivers/acpi/executer/exresolv.c | |||
@@ -0,0 +1,546 @@ | |||
1 | |||
2 | /****************************************************************************** | ||
3 | * | ||
4 | * Module Name: exresolv - AML Interpreter object resolution | ||
5 | * | ||
6 | *****************************************************************************/ | ||
7 | |||
8 | /* | ||
9 | * Copyright (C) 2000 - 2005, R. Byron Moore | ||
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 | |||
45 | |||
46 | #include <acpi/acpi.h> | ||
47 | #include <acpi/amlcode.h> | ||
48 | #include <acpi/acdispat.h> | ||
49 | #include <acpi/acinterp.h> | ||
50 | #include <acpi/acnamesp.h> | ||
51 | #include <acpi/acparser.h> | ||
52 | |||
53 | |||
54 | #define _COMPONENT ACPI_EXECUTER | ||
55 | ACPI_MODULE_NAME ("exresolv") | ||
56 | |||
57 | |||
58 | /******************************************************************************* | ||
59 | * | ||
60 | * FUNCTION: acpi_ex_resolve_to_value | ||
61 | * | ||
62 | * PARAMETERS: **stack_ptr - Points to entry on obj_stack, which can | ||
63 | * be either an (union acpi_operand_object *) | ||
64 | * or an acpi_handle. | ||
65 | * walk_state - Current method state | ||
66 | * | ||
67 | * RETURN: Status | ||
68 | * | ||
69 | * DESCRIPTION: Convert Reference objects to values | ||
70 | * | ||
71 | ******************************************************************************/ | ||
72 | |||
73 | acpi_status | ||
74 | acpi_ex_resolve_to_value ( | ||
75 | union acpi_operand_object **stack_ptr, | ||
76 | struct acpi_walk_state *walk_state) | ||
77 | { | ||
78 | acpi_status status; | ||
79 | |||
80 | |||
81 | ACPI_FUNCTION_TRACE_PTR ("ex_resolve_to_value", stack_ptr); | ||
82 | |||
83 | |||
84 | if (!stack_ptr || !*stack_ptr) { | ||
85 | ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Internal - null pointer\n")); | ||
86 | return_ACPI_STATUS (AE_AML_NO_OPERAND); | ||
87 | } | ||
88 | |||
89 | /* | ||
90 | * The entity pointed to by the stack_ptr can be either | ||
91 | * 1) A valid union acpi_operand_object, or | ||
92 | * 2) A struct acpi_namespace_node (named_obj) | ||
93 | */ | ||
94 | if (ACPI_GET_DESCRIPTOR_TYPE (*stack_ptr) == ACPI_DESC_TYPE_OPERAND) { | ||
95 | status = acpi_ex_resolve_object_to_value (stack_ptr, walk_state); | ||
96 | if (ACPI_FAILURE (status)) { | ||
97 | return_ACPI_STATUS (status); | ||
98 | } | ||
99 | } | ||
100 | |||
101 | /* | ||
102 | * Object on the stack may have changed if acpi_ex_resolve_object_to_value() | ||
103 | * was called (i.e., we can't use an _else_ here.) | ||
104 | */ | ||
105 | if (ACPI_GET_DESCRIPTOR_TYPE (*stack_ptr) == ACPI_DESC_TYPE_NAMED) { | ||
106 | status = acpi_ex_resolve_node_to_value ( | ||
107 | ACPI_CAST_INDIRECT_PTR (struct acpi_namespace_node, stack_ptr), | ||
108 | walk_state); | ||
109 | if (ACPI_FAILURE (status)) { | ||
110 | return_ACPI_STATUS (status); | ||
111 | } | ||
112 | } | ||
113 | |||
114 | ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Resolved object %p\n", *stack_ptr)); | ||
115 | return_ACPI_STATUS (AE_OK); | ||
116 | } | ||
117 | |||
118 | |||
119 | /******************************************************************************* | ||
120 | * | ||
121 | * FUNCTION: acpi_ex_resolve_object_to_value | ||
122 | * | ||
123 | * PARAMETERS: stack_ptr - Pointer to a stack location that contains a | ||
124 | * ptr to an internal object. | ||
125 | * walk_state - Current method state | ||
126 | * | ||
127 | * RETURN: Status | ||
128 | * | ||
129 | * DESCRIPTION: Retrieve the value from an internal object. The Reference type | ||
130 | * uses the associated AML opcode to determine the value. | ||
131 | * | ||
132 | ******************************************************************************/ | ||
133 | |||
134 | acpi_status | ||
135 | acpi_ex_resolve_object_to_value ( | ||
136 | union acpi_operand_object **stack_ptr, | ||
137 | struct acpi_walk_state *walk_state) | ||
138 | { | ||
139 | acpi_status status = AE_OK; | ||
140 | union acpi_operand_object *stack_desc; | ||
141 | void *temp_node; | ||
142 | union acpi_operand_object *obj_desc; | ||
143 | u16 opcode; | ||
144 | |||
145 | |||
146 | ACPI_FUNCTION_TRACE ("ex_resolve_object_to_value"); | ||
147 | |||
148 | |||
149 | stack_desc = *stack_ptr; | ||
150 | |||
151 | /* This is an union acpi_operand_object */ | ||
152 | |||
153 | switch (ACPI_GET_OBJECT_TYPE (stack_desc)) { | ||
154 | case ACPI_TYPE_LOCAL_REFERENCE: | ||
155 | |||
156 | opcode = stack_desc->reference.opcode; | ||
157 | |||
158 | switch (opcode) { | ||
159 | case AML_NAME_OP: | ||
160 | |||
161 | /* | ||
162 | * Convert indirect name ptr to a direct name ptr. | ||
163 | * Then, acpi_ex_resolve_node_to_value can be used to get the value | ||
164 | */ | ||
165 | temp_node = stack_desc->reference.object; | ||
166 | |||
167 | /* Delete the Reference Object */ | ||
168 | |||
169 | acpi_ut_remove_reference (stack_desc); | ||
170 | |||
171 | /* Put direct name pointer onto stack and exit */ | ||
172 | |||
173 | (*stack_ptr) = temp_node; | ||
174 | break; | ||
175 | |||
176 | |||
177 | case AML_LOCAL_OP: | ||
178 | case AML_ARG_OP: | ||
179 | |||
180 | /* | ||
181 | * Get the local from the method's state info | ||
182 | * Note: this increments the local's object reference count | ||
183 | */ | ||
184 | status = acpi_ds_method_data_get_value (opcode, | ||
185 | stack_desc->reference.offset, walk_state, &obj_desc); | ||
186 | if (ACPI_FAILURE (status)) { | ||
187 | return_ACPI_STATUS (status); | ||
188 | } | ||
189 | |||
190 | ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "[Arg/Local %X] value_obj is %p\n", | ||
191 | stack_desc->reference.offset, obj_desc)); | ||
192 | |||
193 | /* | ||
194 | * Now we can delete the original Reference Object and | ||
195 | * replace it with the resolved value | ||
196 | */ | ||
197 | acpi_ut_remove_reference (stack_desc); | ||
198 | *stack_ptr = obj_desc; | ||
199 | break; | ||
200 | |||
201 | |||
202 | case AML_INDEX_OP: | ||
203 | |||
204 | switch (stack_desc->reference.target_type) { | ||
205 | case ACPI_TYPE_BUFFER_FIELD: | ||
206 | |||
207 | /* Just return - leave the Reference on the stack */ | ||
208 | break; | ||
209 | |||
210 | |||
211 | case ACPI_TYPE_PACKAGE: | ||
212 | |||
213 | obj_desc = *stack_desc->reference.where; | ||
214 | if (obj_desc) { | ||
215 | /* | ||
216 | * Valid obj descriptor, copy pointer to return value | ||
217 | * (i.e., dereference the package index) | ||
218 | * Delete the ref object, increment the returned object | ||
219 | */ | ||
220 | acpi_ut_remove_reference (stack_desc); | ||
221 | acpi_ut_add_reference (obj_desc); | ||
222 | *stack_ptr = obj_desc; | ||
223 | } | ||
224 | else { | ||
225 | /* | ||
226 | * A NULL object descriptor means an unitialized element of | ||
227 | * the package, can't dereference it | ||
228 | */ | ||
229 | ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, | ||
230 | "Attempt to deref an Index to NULL pkg element Idx=%p\n", | ||
231 | stack_desc)); | ||
232 | status = AE_AML_UNINITIALIZED_ELEMENT; | ||
233 | } | ||
234 | break; | ||
235 | |||
236 | |||
237 | default: | ||
238 | |||
239 | /* Invalid reference object */ | ||
240 | |||
241 | ACPI_REPORT_ERROR (( | ||
242 | "During resolve, Unknown target_type %X in Index/Reference obj %p\n", | ||
243 | stack_desc->reference.target_type, stack_desc)); | ||
244 | status = AE_AML_INTERNAL; | ||
245 | break; | ||
246 | } | ||
247 | break; | ||
248 | |||
249 | |||
250 | case AML_REF_OF_OP: | ||
251 | case AML_DEBUG_OP: | ||
252 | case AML_LOAD_OP: | ||
253 | |||
254 | /* Just leave the object as-is */ | ||
255 | |||
256 | break; | ||
257 | |||
258 | |||
259 | default: | ||
260 | |||
261 | ACPI_REPORT_ERROR (("During resolve, Unknown Reference opcode %X (%s) in %p\n", | ||
262 | opcode, acpi_ps_get_opcode_name (opcode), stack_desc)); | ||
263 | status = AE_AML_INTERNAL; | ||
264 | break; | ||
265 | } | ||
266 | break; | ||
267 | |||
268 | |||
269 | case ACPI_TYPE_BUFFER: | ||
270 | |||
271 | status = acpi_ds_get_buffer_arguments (stack_desc); | ||
272 | break; | ||
273 | |||
274 | |||
275 | case ACPI_TYPE_PACKAGE: | ||
276 | |||
277 | status = acpi_ds_get_package_arguments (stack_desc); | ||
278 | break; | ||
279 | |||
280 | |||
281 | /* | ||
282 | * These cases may never happen here, but just in case.. | ||
283 | */ | ||
284 | case ACPI_TYPE_BUFFER_FIELD: | ||
285 | case ACPI_TYPE_LOCAL_REGION_FIELD: | ||
286 | case ACPI_TYPE_LOCAL_BANK_FIELD: | ||
287 | case ACPI_TYPE_LOCAL_INDEX_FIELD: | ||
288 | |||
289 | ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "field_read source_desc=%p Type=%X\n", | ||
290 | stack_desc, ACPI_GET_OBJECT_TYPE (stack_desc))); | ||
291 | |||
292 | status = acpi_ex_read_data_from_field (walk_state, stack_desc, &obj_desc); | ||
293 | *stack_ptr = (void *) obj_desc; | ||
294 | break; | ||
295 | |||
296 | default: | ||
297 | break; | ||
298 | } | ||
299 | |||
300 | return_ACPI_STATUS (status); | ||
301 | } | ||
302 | |||
303 | |||
304 | /******************************************************************************* | ||
305 | * | ||
306 | * FUNCTION: acpi_ex_resolve_multiple | ||
307 | * | ||
308 | * PARAMETERS: walk_state - Current state (contains AML opcode) | ||
309 | * Operand - Starting point for resolution | ||
310 | * return_type - Where the object type is returned | ||
311 | * return_desc - Where the resolved object is returned | ||
312 | * | ||
313 | * RETURN: Status | ||
314 | * | ||
315 | * DESCRIPTION: Return the base object and type. Traverse a reference list if | ||
316 | * necessary to get to the base object. | ||
317 | * | ||
318 | ******************************************************************************/ | ||
319 | |||
320 | acpi_status | ||
321 | acpi_ex_resolve_multiple ( | ||
322 | struct acpi_walk_state *walk_state, | ||
323 | union acpi_operand_object *operand, | ||
324 | acpi_object_type *return_type, | ||
325 | union acpi_operand_object **return_desc) | ||
326 | { | ||
327 | union acpi_operand_object *obj_desc = (void *) operand; | ||
328 | struct acpi_namespace_node *node; | ||
329 | acpi_object_type type; | ||
330 | acpi_status status; | ||
331 | |||
332 | |||
333 | ACPI_FUNCTION_TRACE ("acpi_ex_resolve_multiple"); | ||
334 | |||
335 | |||
336 | /* | ||
337 | * Operand can be either a namespace node or an operand descriptor | ||
338 | */ | ||
339 | switch (ACPI_GET_DESCRIPTOR_TYPE (obj_desc)) { | ||
340 | case ACPI_DESC_TYPE_OPERAND: | ||
341 | type = obj_desc->common.type; | ||
342 | break; | ||
343 | |||
344 | case ACPI_DESC_TYPE_NAMED: | ||
345 | type = ((struct acpi_namespace_node *) obj_desc)->type; | ||
346 | obj_desc = acpi_ns_get_attached_object ((struct acpi_namespace_node *) obj_desc); | ||
347 | |||
348 | /* If we had an Alias node, use the attached object for type info */ | ||
349 | |||
350 | if (type == ACPI_TYPE_LOCAL_ALIAS) { | ||
351 | type = ((struct acpi_namespace_node *) obj_desc)->type; | ||
352 | obj_desc = acpi_ns_get_attached_object ((struct acpi_namespace_node *) obj_desc); | ||
353 | } | ||
354 | break; | ||
355 | |||
356 | default: | ||
357 | return_ACPI_STATUS (AE_AML_OPERAND_TYPE); | ||
358 | } | ||
359 | |||
360 | |||
361 | /* | ||
362 | * If type is anything other than a reference, we are done | ||
363 | */ | ||
364 | if (type != ACPI_TYPE_LOCAL_REFERENCE) { | ||
365 | goto exit; | ||
366 | } | ||
367 | |||
368 | /* | ||
369 | * For reference objects created via the ref_of or Index operators, | ||
370 | * we need to get to the base object (as per the ACPI specification | ||
371 | * of the object_type and size_of operators). This means traversing | ||
372 | * the list of possibly many nested references. | ||
373 | */ | ||
374 | while (ACPI_GET_OBJECT_TYPE (obj_desc) == ACPI_TYPE_LOCAL_REFERENCE) { | ||
375 | switch (obj_desc->reference.opcode) { | ||
376 | case AML_REF_OF_OP: | ||
377 | |||
378 | /* Dereference the reference pointer */ | ||
379 | |||
380 | node = obj_desc->reference.object; | ||
381 | |||
382 | /* All "References" point to a NS node */ | ||
383 | |||
384 | if (ACPI_GET_DESCRIPTOR_TYPE (node) != ACPI_DESC_TYPE_NAMED) { | ||
385 | ACPI_REPORT_ERROR (("acpi_ex_resolve_multiple: Not a NS node %p [%s]\n", | ||
386 | node, acpi_ut_get_descriptor_name (node))); | ||
387 | return_ACPI_STATUS (AE_AML_INTERNAL); | ||
388 | } | ||
389 | |||
390 | /* Get the attached object */ | ||
391 | |||
392 | obj_desc = acpi_ns_get_attached_object (node); | ||
393 | if (!obj_desc) { | ||
394 | /* No object, use the NS node type */ | ||
395 | |||
396 | type = acpi_ns_get_type (node); | ||
397 | goto exit; | ||
398 | } | ||
399 | |||
400 | /* Check for circular references */ | ||
401 | |||
402 | if (obj_desc == operand) { | ||
403 | return_ACPI_STATUS (AE_AML_CIRCULAR_REFERENCE); | ||
404 | } | ||
405 | break; | ||
406 | |||
407 | |||
408 | case AML_INDEX_OP: | ||
409 | |||
410 | /* Get the type of this reference (index into another object) */ | ||
411 | |||
412 | type = obj_desc->reference.target_type; | ||
413 | if (type != ACPI_TYPE_PACKAGE) { | ||
414 | goto exit; | ||
415 | } | ||
416 | |||
417 | /* | ||
418 | * The main object is a package, we want to get the type | ||
419 | * of the individual package element that is referenced by | ||
420 | * the index. | ||
421 | * | ||
422 | * This could of course in turn be another reference object. | ||
423 | */ | ||
424 | obj_desc = *(obj_desc->reference.where); | ||
425 | if (!obj_desc) { | ||
426 | /* NULL package elements are allowed */ | ||
427 | |||
428 | type = 0; /* Uninitialized */ | ||
429 | goto exit; | ||
430 | } | ||
431 | break; | ||
432 | |||
433 | |||
434 | case AML_INT_NAMEPATH_OP: | ||
435 | |||
436 | /* Dereference the reference pointer */ | ||
437 | |||
438 | node = obj_desc->reference.node; | ||
439 | |||
440 | /* All "References" point to a NS node */ | ||
441 | |||
442 | if (ACPI_GET_DESCRIPTOR_TYPE (node) != ACPI_DESC_TYPE_NAMED) { | ||
443 | ACPI_REPORT_ERROR (("acpi_ex_resolve_multiple: Not a NS node %p [%s]\n", | ||
444 | node, acpi_ut_get_descriptor_name (node))); | ||
445 | return_ACPI_STATUS (AE_AML_INTERNAL); | ||
446 | } | ||
447 | |||
448 | /* Get the attached object */ | ||
449 | |||
450 | obj_desc = acpi_ns_get_attached_object (node); | ||
451 | if (!obj_desc) { | ||
452 | /* No object, use the NS node type */ | ||
453 | |||
454 | type = acpi_ns_get_type (node); | ||
455 | goto exit; | ||
456 | } | ||
457 | |||
458 | /* Check for circular references */ | ||
459 | |||
460 | if (obj_desc == operand) { | ||
461 | return_ACPI_STATUS (AE_AML_CIRCULAR_REFERENCE); | ||
462 | } | ||
463 | break; | ||
464 | |||
465 | |||
466 | case AML_LOCAL_OP: | ||
467 | case AML_ARG_OP: | ||
468 | |||
469 | if (return_desc) { | ||
470 | status = acpi_ds_method_data_get_value (obj_desc->reference.opcode, | ||
471 | obj_desc->reference.offset, walk_state, &obj_desc); | ||
472 | if (ACPI_FAILURE (status)) { | ||
473 | return_ACPI_STATUS (status); | ||
474 | } | ||
475 | acpi_ut_remove_reference (obj_desc); | ||
476 | } | ||
477 | else { | ||
478 | status = acpi_ds_method_data_get_node (obj_desc->reference.opcode, | ||
479 | obj_desc->reference.offset, walk_state, &node); | ||
480 | if (ACPI_FAILURE (status)) { | ||
481 | return_ACPI_STATUS (status); | ||
482 | } | ||
483 | |||
484 | obj_desc = acpi_ns_get_attached_object (node); | ||
485 | if (!obj_desc) { | ||
486 | type = ACPI_TYPE_ANY; | ||
487 | goto exit; | ||
488 | } | ||
489 | } | ||
490 | break; | ||
491 | |||
492 | |||
493 | case AML_DEBUG_OP: | ||
494 | |||
495 | /* The Debug Object is of type "debug_object" */ | ||
496 | |||
497 | type = ACPI_TYPE_DEBUG_OBJECT; | ||
498 | goto exit; | ||
499 | |||
500 | |||
501 | default: | ||
502 | |||
503 | ACPI_REPORT_ERROR (("acpi_ex_resolve_multiple: Unknown Reference subtype %X\n", | ||
504 | obj_desc->reference.opcode)); | ||
505 | return_ACPI_STATUS (AE_AML_INTERNAL); | ||
506 | } | ||
507 | } | ||
508 | |||
509 | /* | ||
510 | * Now we are guaranteed to have an object that has not been created | ||
511 | * via the ref_of or Index operators. | ||
512 | */ | ||
513 | type = ACPI_GET_OBJECT_TYPE (obj_desc); | ||
514 | |||
515 | |||
516 | exit: | ||
517 | /* Convert internal types to external types */ | ||
518 | |||
519 | switch (type) { | ||
520 | case ACPI_TYPE_LOCAL_REGION_FIELD: | ||
521 | case ACPI_TYPE_LOCAL_BANK_FIELD: | ||
522 | case ACPI_TYPE_LOCAL_INDEX_FIELD: | ||
523 | |||
524 | type = ACPI_TYPE_FIELD_UNIT; | ||
525 | break; | ||
526 | |||
527 | case ACPI_TYPE_LOCAL_SCOPE: | ||
528 | |||
529 | /* Per ACPI Specification, Scope is untyped */ | ||
530 | |||
531 | type = ACPI_TYPE_ANY; | ||
532 | break; | ||
533 | |||
534 | default: | ||
535 | /* No change to Type required */ | ||
536 | break; | ||
537 | } | ||
538 | |||
539 | *return_type = type; | ||
540 | if (return_desc) { | ||
541 | *return_desc = obj_desc; | ||
542 | } | ||
543 | return_ACPI_STATUS (AE_OK); | ||
544 | } | ||
545 | |||
546 | |||