xref: /openbmc/linux/drivers/acpi/acpica/nswalk.c (revision c45b5c097001480e66d4c523eb715ad317a4ef77)
195b482a8SLen Brown /******************************************************************************
295b482a8SLen Brown  *
395b482a8SLen Brown  * Module Name: nswalk - Functions for walking the ACPI namespace
495b482a8SLen Brown  *
595b482a8SLen Brown  *****************************************************************************/
695b482a8SLen Brown 
795b482a8SLen Brown /*
8a8357b0cSBob Moore  * Copyright (C) 2000 - 2010, Intel Corp.
995b482a8SLen Brown  * All rights reserved.
1095b482a8SLen Brown  *
1195b482a8SLen Brown  * Redistribution and use in source and binary forms, with or without
1295b482a8SLen Brown  * modification, are permitted provided that the following conditions
1395b482a8SLen Brown  * are met:
1495b482a8SLen Brown  * 1. Redistributions of source code must retain the above copyright
1595b482a8SLen Brown  *    notice, this list of conditions, and the following disclaimer,
1695b482a8SLen Brown  *    without modification.
1795b482a8SLen Brown  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
1895b482a8SLen Brown  *    substantially similar to the "NO WARRANTY" disclaimer below
1995b482a8SLen Brown  *    ("Disclaimer") and any redistribution must be conditioned upon
2095b482a8SLen Brown  *    including a substantially similar Disclaimer requirement for further
2195b482a8SLen Brown  *    binary redistribution.
2295b482a8SLen Brown  * 3. Neither the names of the above-listed copyright holders nor the names
2395b482a8SLen Brown  *    of any contributors may be used to endorse or promote products derived
2495b482a8SLen Brown  *    from this software without specific prior written permission.
2595b482a8SLen Brown  *
2695b482a8SLen Brown  * Alternatively, this software may be distributed under the terms of the
2795b482a8SLen Brown  * GNU General Public License ("GPL") version 2 as published by the Free
2895b482a8SLen Brown  * Software Foundation.
2995b482a8SLen Brown  *
3095b482a8SLen Brown  * NO WARRANTY
3195b482a8SLen Brown  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
3295b482a8SLen Brown  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
3395b482a8SLen Brown  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
3495b482a8SLen Brown  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
3595b482a8SLen Brown  * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3695b482a8SLen Brown  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3795b482a8SLen Brown  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3895b482a8SLen Brown  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
3995b482a8SLen Brown  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
4095b482a8SLen Brown  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
4195b482a8SLen Brown  * POSSIBILITY OF SUCH DAMAGES.
4295b482a8SLen Brown  */
4395b482a8SLen Brown 
4495b482a8SLen Brown #include <acpi/acpi.h>
45e2f7a777SLen Brown #include "accommon.h"
46e2f7a777SLen Brown #include "acnamesp.h"
4795b482a8SLen Brown 
4895b482a8SLen Brown #define _COMPONENT          ACPI_NAMESPACE
4995b482a8SLen Brown ACPI_MODULE_NAME("nswalk")
5095b482a8SLen Brown 
5195b482a8SLen Brown /*******************************************************************************
5295b482a8SLen Brown  *
5395b482a8SLen Brown  * FUNCTION:    acpi_ns_get_next_node
5495b482a8SLen Brown  *
558c725bf9SBob Moore  * PARAMETERS:  parent_node         - Parent node whose children we are
568c725bf9SBob Moore  *                                    getting
578c725bf9SBob Moore  *              child_node          - Previous child that was found.
588c725bf9SBob Moore  *                                    The NEXT child will be returned
598c725bf9SBob Moore  *
608c725bf9SBob Moore  * RETURN:      struct acpi_namespace_node - Pointer to the NEXT child or NULL if
618c725bf9SBob Moore  *                                    none is found.
628c725bf9SBob Moore  *
638c725bf9SBob Moore  * DESCRIPTION: Return the next peer node within the namespace.  If Handle
648c725bf9SBob Moore  *              is valid, Scope is ignored.  Otherwise, the first node
658c725bf9SBob Moore  *              within Scope is returned.
668c725bf9SBob Moore  *
678c725bf9SBob Moore  ******************************************************************************/
688c725bf9SBob Moore struct acpi_namespace_node *acpi_ns_get_next_node(struct acpi_namespace_node
698c725bf9SBob Moore 						  *parent_node,
708c725bf9SBob Moore 						  struct acpi_namespace_node
718c725bf9SBob Moore 						  *child_node)
728c725bf9SBob Moore {
738c725bf9SBob Moore 	ACPI_FUNCTION_ENTRY();
748c725bf9SBob Moore 
758c725bf9SBob Moore 	if (!child_node) {
768c725bf9SBob Moore 
778c725bf9SBob Moore 		/* It's really the parent's _scope_ that we want */
788c725bf9SBob Moore 
798c725bf9SBob Moore 		return parent_node->child;
808c725bf9SBob Moore 	}
818c725bf9SBob Moore 
828c725bf9SBob Moore 	/* Otherwise just return the next peer */
838c725bf9SBob Moore 
848c725bf9SBob Moore 	return child_node->peer;
858c725bf9SBob Moore }
868c725bf9SBob Moore 
878c725bf9SBob Moore /*******************************************************************************
888c725bf9SBob Moore  *
898c725bf9SBob Moore  * FUNCTION:    acpi_ns_get_next_node_typed
908c725bf9SBob Moore  *
9195b482a8SLen Brown  * PARAMETERS:  Type                - Type of node to be searched for
9295b482a8SLen Brown  *              parent_node         - Parent node whose children we are
9395b482a8SLen Brown  *                                    getting
9495b482a8SLen Brown  *              child_node          - Previous child that was found.
9595b482a8SLen Brown  *                                    The NEXT child will be returned
9695b482a8SLen Brown  *
9795b482a8SLen Brown  * RETURN:      struct acpi_namespace_node - Pointer to the NEXT child or NULL if
9895b482a8SLen Brown  *                                    none is found.
9995b482a8SLen Brown  *
10095b482a8SLen Brown  * DESCRIPTION: Return the next peer node within the namespace.  If Handle
10195b482a8SLen Brown  *              is valid, Scope is ignored.  Otherwise, the first node
10295b482a8SLen Brown  *              within Scope is returned.
10395b482a8SLen Brown  *
10495b482a8SLen Brown  ******************************************************************************/
1058c725bf9SBob Moore 
1068c725bf9SBob Moore struct acpi_namespace_node *acpi_ns_get_next_node_typed(acpi_object_type type,
1078c725bf9SBob Moore 							struct
1088c725bf9SBob Moore 							acpi_namespace_node
1098c725bf9SBob Moore 							*parent_node,
1108c725bf9SBob Moore 							struct
1118c725bf9SBob Moore 							acpi_namespace_node
11295b482a8SLen Brown 							*child_node)
11395b482a8SLen Brown {
11495b482a8SLen Brown 	struct acpi_namespace_node *next_node = NULL;
11595b482a8SLen Brown 
11695b482a8SLen Brown 	ACPI_FUNCTION_ENTRY();
11795b482a8SLen Brown 
1188c725bf9SBob Moore 	next_node = acpi_ns_get_next_node(parent_node, child_node);
11995b482a8SLen Brown 
12095b482a8SLen Brown 
12195b482a8SLen Brown 	/* If any type is OK, we are done */
12295b482a8SLen Brown 
12395b482a8SLen Brown 	if (type == ACPI_TYPE_ANY) {
12495b482a8SLen Brown 
12595b482a8SLen Brown 		/* next_node is NULL if we are at the end-of-list */
12695b482a8SLen Brown 
12795b482a8SLen Brown 		return (next_node);
12895b482a8SLen Brown 	}
12995b482a8SLen Brown 
13095b482a8SLen Brown 	/* Must search for the node -- but within this scope only */
13195b482a8SLen Brown 
13295b482a8SLen Brown 	while (next_node) {
13395b482a8SLen Brown 
13495b482a8SLen Brown 		/* If type matches, we are done */
13595b482a8SLen Brown 
13695b482a8SLen Brown 		if (next_node->type == type) {
13795b482a8SLen Brown 			return (next_node);
13895b482a8SLen Brown 		}
13995b482a8SLen Brown 
140*c45b5c09SAlexey Starikovskiy 		/* Otherwise, move on to the next peer node */
14195b482a8SLen Brown 
142*c45b5c09SAlexey Starikovskiy 		next_node = next_node->peer;
14395b482a8SLen Brown 	}
14495b482a8SLen Brown 
14595b482a8SLen Brown 	/* Not found */
14695b482a8SLen Brown 
14795b482a8SLen Brown 	return (NULL);
14895b482a8SLen Brown }
14995b482a8SLen Brown 
15095b482a8SLen Brown /*******************************************************************************
15195b482a8SLen Brown  *
15295b482a8SLen Brown  * FUNCTION:    acpi_ns_walk_namespace
15395b482a8SLen Brown  *
15495b482a8SLen Brown  * PARAMETERS:  Type                - acpi_object_type to search for
15595b482a8SLen Brown  *              start_node          - Handle in namespace where search begins
15695b482a8SLen Brown  *              max_depth           - Depth to which search is to reach
15795b482a8SLen Brown  *              Flags               - Whether to unlock the NS before invoking
15895b482a8SLen Brown  *                                    the callback routine
1592263576cSLin Ming  *              pre_order_visit     - Called during tree pre-order visit
1602263576cSLin Ming  *                                    when an object of "Type" is found
1612263576cSLin Ming  *              post_order_visit    - Called during tree post-order visit
1622263576cSLin Ming  *                                    when an object of "Type" is found
1632263576cSLin Ming  *              Context             - Passed to user function(s) above
1642263576cSLin Ming  *              return_value        - from the user_function if terminated
1652263576cSLin Ming  *                                    early. Otherwise, returns NULL.
16695b482a8SLen Brown  * RETURNS:     Status
16795b482a8SLen Brown  *
16895b482a8SLen Brown  * DESCRIPTION: Performs a modified depth-first walk of the namespace tree,
16995b482a8SLen Brown  *              starting (and ending) at the node specified by start_handle.
1702263576cSLin Ming  *              The callback function is called whenever a node that matches
1712263576cSLin Ming  *              the type parameter is found. If the callback function returns
172d4913dc6SBob Moore  *              a non-zero value, the search is terminated immediately and
173d4913dc6SBob Moore  *              this value is returned to the caller.
17495b482a8SLen Brown  *
17595b482a8SLen Brown  *              The point of this procedure is to provide a generic namespace
17695b482a8SLen Brown  *              walk routine that can be called from multiple places to
1772263576cSLin Ming  *              provide multiple services; the callback function(s) can be
1782263576cSLin Ming  *              tailored to each task, whether it is a print function,
1792263576cSLin Ming  *              a compare function, etc.
18095b482a8SLen Brown  *
18195b482a8SLen Brown  ******************************************************************************/
18295b482a8SLen Brown 
18395b482a8SLen Brown acpi_status
18495b482a8SLen Brown acpi_ns_walk_namespace(acpi_object_type type,
18595b482a8SLen Brown 		       acpi_handle start_node,
18695b482a8SLen Brown 		       u32 max_depth,
18795b482a8SLen Brown 		       u32 flags,
1882263576cSLin Ming 		       acpi_walk_callback pre_order_visit,
1892263576cSLin Ming 		       acpi_walk_callback post_order_visit,
19095b482a8SLen Brown 		       void *context, void **return_value)
19195b482a8SLen Brown {
19295b482a8SLen Brown 	acpi_status status;
19395b482a8SLen Brown 	acpi_status mutex_status;
19495b482a8SLen Brown 	struct acpi_namespace_node *child_node;
19595b482a8SLen Brown 	struct acpi_namespace_node *parent_node;
19695b482a8SLen Brown 	acpi_object_type child_type;
19795b482a8SLen Brown 	u32 level;
1982263576cSLin Ming 	u8 node_previously_visited = FALSE;
19995b482a8SLen Brown 
20095b482a8SLen Brown 	ACPI_FUNCTION_TRACE(ns_walk_namespace);
20195b482a8SLen Brown 
20295b482a8SLen Brown 	/* Special case for the namespace Root Node */
20395b482a8SLen Brown 
20495b482a8SLen Brown 	if (start_node == ACPI_ROOT_OBJECT) {
20595b482a8SLen Brown 		start_node = acpi_gbl_root_node;
20695b482a8SLen Brown 	}
20795b482a8SLen Brown 
20895b482a8SLen Brown 	/* Null child means "get first node" */
20995b482a8SLen Brown 
21095b482a8SLen Brown 	parent_node = start_node;
2112263576cSLin Ming 	child_node = acpi_ns_get_next_node(parent_node, NULL);
21295b482a8SLen Brown 	child_type = ACPI_TYPE_ANY;
21395b482a8SLen Brown 	level = 1;
21495b482a8SLen Brown 
21595b482a8SLen Brown 	/*
21695b482a8SLen Brown 	 * Traverse the tree of nodes until we bubble back up to where we
21795b482a8SLen Brown 	 * started. When Level is zero, the loop is done because we have
21895b482a8SLen Brown 	 * bubbled up to (and passed) the original parent handle (start_entry)
21995b482a8SLen Brown 	 */
2202263576cSLin Ming 	while (level > 0 && child_node) {
22195b482a8SLen Brown 		status = AE_OK;
22295b482a8SLen Brown 
22395b482a8SLen Brown 		/* Found next child, get the type if we are not searching for ANY */
22495b482a8SLen Brown 
22595b482a8SLen Brown 		if (type != ACPI_TYPE_ANY) {
22695b482a8SLen Brown 			child_type = child_node->type;
22795b482a8SLen Brown 		}
22895b482a8SLen Brown 
22995b482a8SLen Brown 		/*
23095b482a8SLen Brown 		 * Ignore all temporary namespace nodes (created during control
23195b482a8SLen Brown 		 * method execution) unless told otherwise. These temporary nodes
232d4913dc6SBob Moore 		 * can cause a race condition because they can be deleted during
233d4913dc6SBob Moore 		 * the execution of the user function (if the namespace is
234d4913dc6SBob Moore 		 * unlocked before invocation of the user function.) Only the
235d4913dc6SBob Moore 		 * debugger namespace dump will examine the temporary nodes.
23695b482a8SLen Brown 		 */
23795b482a8SLen Brown 		if ((child_node->flags & ANOBJ_TEMPORARY) &&
23895b482a8SLen Brown 		    !(flags & ACPI_NS_WALK_TEMP_NODES)) {
23995b482a8SLen Brown 			status = AE_CTRL_DEPTH;
24095b482a8SLen Brown 		}
24195b482a8SLen Brown 
24295b482a8SLen Brown 		/* Type must match requested type */
24395b482a8SLen Brown 
24495b482a8SLen Brown 		else if (child_type == type) {
24595b482a8SLen Brown 			/*
24695b482a8SLen Brown 			 * Found a matching node, invoke the user callback function.
24795b482a8SLen Brown 			 * Unlock the namespace if flag is set.
24895b482a8SLen Brown 			 */
24995b482a8SLen Brown 			if (flags & ACPI_NS_WALK_UNLOCK) {
25095b482a8SLen Brown 				mutex_status =
2512263576cSLin Ming 				    acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
25295b482a8SLen Brown 				if (ACPI_FAILURE(mutex_status)) {
2532263576cSLin Ming 					return_ACPI_STATUS(mutex_status);
25495b482a8SLen Brown 				}
25595b482a8SLen Brown 			}
25695b482a8SLen Brown 
2572263576cSLin Ming 			/*
2582263576cSLin Ming 			 * Invoke the user function, either pre-order or post-order
2592263576cSLin Ming 			 * or both.
2602263576cSLin Ming 			 */
2612263576cSLin Ming 			if (!node_previously_visited) {
2622263576cSLin Ming 				if (pre_order_visit) {
26395b482a8SLen Brown 					status =
2642263576cSLin Ming 					    pre_order_visit(child_node, level,
2652263576cSLin Ming 							    context,
26695b482a8SLen Brown 							    return_value);
2672263576cSLin Ming 				}
2682263576cSLin Ming 			} else {
2692263576cSLin Ming 				if (post_order_visit) {
2702263576cSLin Ming 					status =
2712263576cSLin Ming 					    post_order_visit(child_node, level,
2722263576cSLin Ming 							     context,
2732263576cSLin Ming 							     return_value);
2742263576cSLin Ming 				}
2752263576cSLin Ming 			}
27695b482a8SLen Brown 
27795b482a8SLen Brown 			if (flags & ACPI_NS_WALK_UNLOCK) {
27895b482a8SLen Brown 				mutex_status =
2792263576cSLin Ming 				    acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
28095b482a8SLen Brown 				if (ACPI_FAILURE(mutex_status)) {
2812263576cSLin Ming 					return_ACPI_STATUS(mutex_status);
28295b482a8SLen Brown 				}
28395b482a8SLen Brown 			}
28495b482a8SLen Brown 
28595b482a8SLen Brown 			switch (status) {
28695b482a8SLen Brown 			case AE_OK:
28795b482a8SLen Brown 			case AE_CTRL_DEPTH:
28895b482a8SLen Brown 
28995b482a8SLen Brown 				/* Just keep going */
29095b482a8SLen Brown 				break;
29195b482a8SLen Brown 
29295b482a8SLen Brown 			case AE_CTRL_TERMINATE:
29395b482a8SLen Brown 
29495b482a8SLen Brown 				/* Exit now, with OK status */
29595b482a8SLen Brown 
29695b482a8SLen Brown 				return_ACPI_STATUS(AE_OK);
29795b482a8SLen Brown 
29895b482a8SLen Brown 			default:
29995b482a8SLen Brown 
30095b482a8SLen Brown 				/* All others are valid exceptions */
30195b482a8SLen Brown 
30295b482a8SLen Brown 				return_ACPI_STATUS(status);
30395b482a8SLen Brown 			}
30495b482a8SLen Brown 		}
30595b482a8SLen Brown 
30695b482a8SLen Brown 		/*
30795b482a8SLen Brown 		 * Depth first search: Attempt to go down another level in the
30895b482a8SLen Brown 		 * namespace if we are allowed to.  Don't go any further if we have
30995b482a8SLen Brown 		 * reached the caller specified maximum depth or if the user
31095b482a8SLen Brown 		 * function has specified that the maximum depth has been reached.
31195b482a8SLen Brown 		 */
3122263576cSLin Ming 		if (!node_previously_visited &&
3132263576cSLin Ming 		    (level < max_depth) && (status != AE_CTRL_DEPTH)) {
3148c725bf9SBob Moore 			if (child_node->child) {
31595b482a8SLen Brown 
31695b482a8SLen Brown 				/* There is at least one child of this node, visit it */
31795b482a8SLen Brown 
31895b482a8SLen Brown 				level++;
31995b482a8SLen Brown 				parent_node = child_node;
3202263576cSLin Ming 				child_node =
3212263576cSLin Ming 				    acpi_ns_get_next_node(parent_node, NULL);
3222263576cSLin Ming 				continue;
32395b482a8SLen Brown 			}
32495b482a8SLen Brown 		}
3252263576cSLin Ming 
3262263576cSLin Ming 		/* No more children, re-visit this node */
3272263576cSLin Ming 
3282263576cSLin Ming 		if (!node_previously_visited) {
3292263576cSLin Ming 			node_previously_visited = TRUE;
3302263576cSLin Ming 			continue;
3312263576cSLin Ming 		}
3322263576cSLin Ming 
3332263576cSLin Ming 		/* No more children, visit peers */
3342263576cSLin Ming 
3352263576cSLin Ming 		child_node = acpi_ns_get_next_node(parent_node, child_node);
3362263576cSLin Ming 		if (child_node) {
3372263576cSLin Ming 			node_previously_visited = FALSE;
3382263576cSLin Ming 		}
3392263576cSLin Ming 
3402263576cSLin Ming 		/* No peers, re-visit parent */
3412263576cSLin Ming 
3422263576cSLin Ming 		else {
34395b482a8SLen Brown 			/*
34495b482a8SLen Brown 			 * No more children of this node (acpi_ns_get_next_node failed), go
34595b482a8SLen Brown 			 * back upwards in the namespace tree to the node's parent.
34695b482a8SLen Brown 			 */
34795b482a8SLen Brown 			level--;
34895b482a8SLen Brown 			child_node = parent_node;
349*c45b5c09SAlexey Starikovskiy 			parent_node = parent_node->parent;
3502263576cSLin Ming 
3512263576cSLin Ming 			node_previously_visited = TRUE;
35295b482a8SLen Brown 		}
35395b482a8SLen Brown 	}
35495b482a8SLen Brown 
35595b482a8SLen Brown 	/* Complete walk, not terminated by user function */
35695b482a8SLen Brown 
35795b482a8SLen Brown 	return_ACPI_STATUS(AE_OK);
35895b482a8SLen Brown }
359