xref: /openbmc/linux/drivers/acpi/acpica/nswalk.c (revision e2f7a7772880458edff1b1cc5a988947229fac26)
195b482a8SLen Brown /******************************************************************************
295b482a8SLen Brown  *
395b482a8SLen Brown  * Module Name: nswalk - Functions for walking the ACPI namespace
495b482a8SLen Brown  *
595b482a8SLen Brown  *****************************************************************************/
695b482a8SLen Brown 
795b482a8SLen Brown /*
895b482a8SLen Brown  * Copyright (C) 2000 - 2008, 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>
45*e2f7a777SLen Brown #include "accommon.h"
46*e2f7a777SLen 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  *
5595b482a8SLen Brown  * PARAMETERS:  Type                - Type of node to be searched for
5695b482a8SLen Brown  *              parent_node         - Parent node whose children we are
5795b482a8SLen Brown  *                                    getting
5895b482a8SLen Brown  *              child_node          - Previous child that was found.
5995b482a8SLen Brown  *                                    The NEXT child will be returned
6095b482a8SLen Brown  *
6195b482a8SLen Brown  * RETURN:      struct acpi_namespace_node - Pointer to the NEXT child or NULL if
6295b482a8SLen Brown  *                                    none is found.
6395b482a8SLen Brown  *
6495b482a8SLen Brown  * DESCRIPTION: Return the next peer node within the namespace.  If Handle
6595b482a8SLen Brown  *              is valid, Scope is ignored.  Otherwise, the first node
6695b482a8SLen Brown  *              within Scope is returned.
6795b482a8SLen Brown  *
6895b482a8SLen Brown  ******************************************************************************/
6995b482a8SLen Brown struct acpi_namespace_node *acpi_ns_get_next_node(acpi_object_type type, struct acpi_namespace_node
7095b482a8SLen Brown 						  *parent_node, struct acpi_namespace_node
7195b482a8SLen Brown 						  *child_node)
7295b482a8SLen Brown {
7395b482a8SLen Brown 	struct acpi_namespace_node *next_node = NULL;
7495b482a8SLen Brown 
7595b482a8SLen Brown 	ACPI_FUNCTION_ENTRY();
7695b482a8SLen Brown 
7795b482a8SLen Brown 	if (!child_node) {
7895b482a8SLen Brown 
7995b482a8SLen Brown 		/* It's really the parent's _scope_ that we want */
8095b482a8SLen Brown 
8195b482a8SLen Brown 		next_node = parent_node->child;
8295b482a8SLen Brown 	}
8395b482a8SLen Brown 
8495b482a8SLen Brown 	else {
8595b482a8SLen Brown 		/* Start search at the NEXT node */
8695b482a8SLen Brown 
8795b482a8SLen Brown 		next_node = acpi_ns_get_next_valid_node(child_node);
8895b482a8SLen Brown 	}
8995b482a8SLen Brown 
9095b482a8SLen Brown 	/* If any type is OK, we are done */
9195b482a8SLen Brown 
9295b482a8SLen Brown 	if (type == ACPI_TYPE_ANY) {
9395b482a8SLen Brown 
9495b482a8SLen Brown 		/* next_node is NULL if we are at the end-of-list */
9595b482a8SLen Brown 
9695b482a8SLen Brown 		return (next_node);
9795b482a8SLen Brown 	}
9895b482a8SLen Brown 
9995b482a8SLen Brown 	/* Must search for the node -- but within this scope only */
10095b482a8SLen Brown 
10195b482a8SLen Brown 	while (next_node) {
10295b482a8SLen Brown 
10395b482a8SLen Brown 		/* If type matches, we are done */
10495b482a8SLen Brown 
10595b482a8SLen Brown 		if (next_node->type == type) {
10695b482a8SLen Brown 			return (next_node);
10795b482a8SLen Brown 		}
10895b482a8SLen Brown 
10995b482a8SLen Brown 		/* Otherwise, move on to the next node */
11095b482a8SLen Brown 
11195b482a8SLen Brown 		next_node = acpi_ns_get_next_valid_node(next_node);
11295b482a8SLen Brown 	}
11395b482a8SLen Brown 
11495b482a8SLen Brown 	/* Not found */
11595b482a8SLen Brown 
11695b482a8SLen Brown 	return (NULL);
11795b482a8SLen Brown }
11895b482a8SLen Brown 
11995b482a8SLen Brown /*******************************************************************************
12095b482a8SLen Brown  *
12195b482a8SLen Brown  * FUNCTION:    acpi_ns_walk_namespace
12295b482a8SLen Brown  *
12395b482a8SLen Brown  * PARAMETERS:  Type                - acpi_object_type to search for
12495b482a8SLen Brown  *              start_node          - Handle in namespace where search begins
12595b482a8SLen Brown  *              max_depth           - Depth to which search is to reach
12695b482a8SLen Brown  *              Flags               - Whether to unlock the NS before invoking
12795b482a8SLen Brown  *                                    the callback routine
12895b482a8SLen Brown  *              user_function       - Called when an object of "Type" is found
12995b482a8SLen Brown  *              Context             - Passed to user function
13095b482a8SLen Brown  *              return_value        - from the user_function if terminated early.
13195b482a8SLen Brown  *                                    Otherwise, returns NULL.
13295b482a8SLen Brown  * RETURNS:     Status
13395b482a8SLen Brown  *
13495b482a8SLen Brown  * DESCRIPTION: Performs a modified depth-first walk of the namespace tree,
13595b482a8SLen Brown  *              starting (and ending) at the node specified by start_handle.
13695b482a8SLen Brown  *              The user_function is called whenever a node that matches
13795b482a8SLen Brown  *              the type parameter is found.  If the user function returns
13895b482a8SLen Brown  *              a non-zero value, the search is terminated immediately and this
13995b482a8SLen Brown  *              value is returned to the caller.
14095b482a8SLen Brown  *
14195b482a8SLen Brown  *              The point of this procedure is to provide a generic namespace
14295b482a8SLen Brown  *              walk routine that can be called from multiple places to
14395b482a8SLen Brown  *              provide multiple services;  the User Function can be tailored
14495b482a8SLen Brown  *              to each task, whether it is a print function, a compare
14595b482a8SLen Brown  *              function, etc.
14695b482a8SLen Brown  *
14795b482a8SLen Brown  ******************************************************************************/
14895b482a8SLen Brown 
14995b482a8SLen Brown acpi_status
15095b482a8SLen Brown acpi_ns_walk_namespace(acpi_object_type type,
15195b482a8SLen Brown 		       acpi_handle start_node,
15295b482a8SLen Brown 		       u32 max_depth,
15395b482a8SLen Brown 		       u32 flags,
15495b482a8SLen Brown 		       acpi_walk_callback user_function,
15595b482a8SLen Brown 		       void *context, void **return_value)
15695b482a8SLen Brown {
15795b482a8SLen Brown 	acpi_status status;
15895b482a8SLen Brown 	acpi_status mutex_status;
15995b482a8SLen Brown 	struct acpi_namespace_node *child_node;
16095b482a8SLen Brown 	struct acpi_namespace_node *parent_node;
16195b482a8SLen Brown 	acpi_object_type child_type;
16295b482a8SLen Brown 	u32 level;
16395b482a8SLen Brown 
16495b482a8SLen Brown 	ACPI_FUNCTION_TRACE(ns_walk_namespace);
16595b482a8SLen Brown 
16695b482a8SLen Brown 	/* Special case for the namespace Root Node */
16795b482a8SLen Brown 
16895b482a8SLen Brown 	if (start_node == ACPI_ROOT_OBJECT) {
16995b482a8SLen Brown 		start_node = acpi_gbl_root_node;
17095b482a8SLen Brown 	}
17195b482a8SLen Brown 
17295b482a8SLen Brown 	/* Null child means "get first node" */
17395b482a8SLen Brown 
17495b482a8SLen Brown 	parent_node = start_node;
17595b482a8SLen Brown 	child_node = NULL;
17695b482a8SLen Brown 	child_type = ACPI_TYPE_ANY;
17795b482a8SLen Brown 	level = 1;
17895b482a8SLen Brown 
17995b482a8SLen Brown 	/*
18095b482a8SLen Brown 	 * Traverse the tree of nodes until we bubble back up to where we
18195b482a8SLen Brown 	 * started. When Level is zero, the loop is done because we have
18295b482a8SLen Brown 	 * bubbled up to (and passed) the original parent handle (start_entry)
18395b482a8SLen Brown 	 */
18495b482a8SLen Brown 	while (level > 0) {
18595b482a8SLen Brown 
18695b482a8SLen Brown 		/* Get the next node in this scope.  Null if not found */
18795b482a8SLen Brown 
18895b482a8SLen Brown 		status = AE_OK;
18995b482a8SLen Brown 		child_node =
19095b482a8SLen Brown 		    acpi_ns_get_next_node(ACPI_TYPE_ANY, parent_node,
19195b482a8SLen Brown 					  child_node);
19295b482a8SLen Brown 		if (child_node) {
19395b482a8SLen Brown 
19495b482a8SLen Brown 			/* Found next child, get the type if we are not searching for ANY */
19595b482a8SLen Brown 
19695b482a8SLen Brown 			if (type != ACPI_TYPE_ANY) {
19795b482a8SLen Brown 				child_type = child_node->type;
19895b482a8SLen Brown 			}
19995b482a8SLen Brown 
20095b482a8SLen Brown 			/*
20195b482a8SLen Brown 			 * Ignore all temporary namespace nodes (created during control
20295b482a8SLen Brown 			 * method execution) unless told otherwise. These temporary nodes
20395b482a8SLen Brown 			 * can cause a race condition because they can be deleted during the
20495b482a8SLen Brown 			 * execution of the user function (if the namespace is unlocked before
20595b482a8SLen Brown 			 * invocation of the user function.) Only the debugger namespace dump
20695b482a8SLen Brown 			 * will examine the temporary nodes.
20795b482a8SLen Brown 			 */
20895b482a8SLen Brown 			if ((child_node->flags & ANOBJ_TEMPORARY) &&
20995b482a8SLen Brown 			    !(flags & ACPI_NS_WALK_TEMP_NODES)) {
21095b482a8SLen Brown 				status = AE_CTRL_DEPTH;
21195b482a8SLen Brown 			}
21295b482a8SLen Brown 
21395b482a8SLen Brown 			/* Type must match requested type */
21495b482a8SLen Brown 
21595b482a8SLen Brown 			else if (child_type == type) {
21695b482a8SLen Brown 				/*
21795b482a8SLen Brown 				 * Found a matching node, invoke the user callback function.
21895b482a8SLen Brown 				 * Unlock the namespace if flag is set.
21995b482a8SLen Brown 				 */
22095b482a8SLen Brown 				if (flags & ACPI_NS_WALK_UNLOCK) {
22195b482a8SLen Brown 					mutex_status =
22295b482a8SLen Brown 					    acpi_ut_release_mutex
22395b482a8SLen Brown 					    (ACPI_MTX_NAMESPACE);
22495b482a8SLen Brown 					if (ACPI_FAILURE(mutex_status)) {
22595b482a8SLen Brown 						return_ACPI_STATUS
22695b482a8SLen Brown 						    (mutex_status);
22795b482a8SLen Brown 					}
22895b482a8SLen Brown 				}
22995b482a8SLen Brown 
23095b482a8SLen Brown 				status =
23195b482a8SLen Brown 				    user_function(child_node, level, context,
23295b482a8SLen Brown 						  return_value);
23395b482a8SLen Brown 
23495b482a8SLen Brown 				if (flags & ACPI_NS_WALK_UNLOCK) {
23595b482a8SLen Brown 					mutex_status =
23695b482a8SLen Brown 					    acpi_ut_acquire_mutex
23795b482a8SLen Brown 					    (ACPI_MTX_NAMESPACE);
23895b482a8SLen Brown 					if (ACPI_FAILURE(mutex_status)) {
23995b482a8SLen Brown 						return_ACPI_STATUS
24095b482a8SLen Brown 						    (mutex_status);
24195b482a8SLen Brown 					}
24295b482a8SLen Brown 				}
24395b482a8SLen Brown 
24495b482a8SLen Brown 				switch (status) {
24595b482a8SLen Brown 				case AE_OK:
24695b482a8SLen Brown 				case AE_CTRL_DEPTH:
24795b482a8SLen Brown 
24895b482a8SLen Brown 					/* Just keep going */
24995b482a8SLen Brown 					break;
25095b482a8SLen Brown 
25195b482a8SLen Brown 				case AE_CTRL_TERMINATE:
25295b482a8SLen Brown 
25395b482a8SLen Brown 					/* Exit now, with OK status */
25495b482a8SLen Brown 
25595b482a8SLen Brown 					return_ACPI_STATUS(AE_OK);
25695b482a8SLen Brown 
25795b482a8SLen Brown 				default:
25895b482a8SLen Brown 
25995b482a8SLen Brown 					/* All others are valid exceptions */
26095b482a8SLen Brown 
26195b482a8SLen Brown 					return_ACPI_STATUS(status);
26295b482a8SLen Brown 				}
26395b482a8SLen Brown 			}
26495b482a8SLen Brown 
26595b482a8SLen Brown 			/*
26695b482a8SLen Brown 			 * Depth first search: Attempt to go down another level in the
26795b482a8SLen Brown 			 * namespace if we are allowed to.  Don't go any further if we have
26895b482a8SLen Brown 			 * reached the caller specified maximum depth or if the user
26995b482a8SLen Brown 			 * function has specified that the maximum depth has been reached.
27095b482a8SLen Brown 			 */
27195b482a8SLen Brown 			if ((level < max_depth) && (status != AE_CTRL_DEPTH)) {
27295b482a8SLen Brown 				if (acpi_ns_get_next_node
27395b482a8SLen Brown 				    (ACPI_TYPE_ANY, child_node, NULL)) {
27495b482a8SLen Brown 
27595b482a8SLen Brown 					/* There is at least one child of this node, visit it */
27695b482a8SLen Brown 
27795b482a8SLen Brown 					level++;
27895b482a8SLen Brown 					parent_node = child_node;
27995b482a8SLen Brown 					child_node = NULL;
28095b482a8SLen Brown 				}
28195b482a8SLen Brown 			}
28295b482a8SLen Brown 		} else {
28395b482a8SLen Brown 			/*
28495b482a8SLen Brown 			 * No more children of this node (acpi_ns_get_next_node failed), go
28595b482a8SLen Brown 			 * back upwards in the namespace tree to the node's parent.
28695b482a8SLen Brown 			 */
28795b482a8SLen Brown 			level--;
28895b482a8SLen Brown 			child_node = parent_node;
28995b482a8SLen Brown 			parent_node = acpi_ns_get_parent_node(parent_node);
29095b482a8SLen Brown 		}
29195b482a8SLen Brown 	}
29295b482a8SLen Brown 
29395b482a8SLen Brown 	/* Complete walk, not terminated by user function */
29495b482a8SLen Brown 
29595b482a8SLen Brown 	return_ACPI_STATUS(AE_OK);
29695b482a8SLen Brown }
297