162306a36Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 262306a36Sopenharmony_ci/****************************************************************************** 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Module Name: nswalk - Functions for walking the ACPI namespace 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (C) 2000 - 2023, Intel Corp. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci *****************************************************************************/ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <acpi/acpi.h> 1162306a36Sopenharmony_ci#include "accommon.h" 1262306a36Sopenharmony_ci#include "acnamesp.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#define _COMPONENT ACPI_NAMESPACE 1562306a36Sopenharmony_ciACPI_MODULE_NAME("nswalk") 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/******************************************************************************* 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * FUNCTION: acpi_ns_get_next_node 2062306a36Sopenharmony_ci * 2162306a36Sopenharmony_ci * PARAMETERS: parent_node - Parent node whose children we are 2262306a36Sopenharmony_ci * getting 2362306a36Sopenharmony_ci * child_node - Previous child that was found. 2462306a36Sopenharmony_ci * The NEXT child will be returned 2562306a36Sopenharmony_ci * 2662306a36Sopenharmony_ci * RETURN: struct acpi_namespace_node - Pointer to the NEXT child or NULL if 2762306a36Sopenharmony_ci * none is found. 2862306a36Sopenharmony_ci * 2962306a36Sopenharmony_ci * DESCRIPTION: Return the next peer node within the namespace. If Handle 3062306a36Sopenharmony_ci * is valid, Scope is ignored. Otherwise, the first node 3162306a36Sopenharmony_ci * within Scope is returned. 3262306a36Sopenharmony_ci * 3362306a36Sopenharmony_ci ******************************************************************************/ 3462306a36Sopenharmony_cistruct acpi_namespace_node *acpi_ns_get_next_node(struct acpi_namespace_node 3562306a36Sopenharmony_ci *parent_node, 3662306a36Sopenharmony_ci struct acpi_namespace_node 3762306a36Sopenharmony_ci *child_node) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci ACPI_FUNCTION_ENTRY(); 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci if (!child_node) { 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci /* It's really the parent's _scope_ that we want */ 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci return (parent_node->child); 4662306a36Sopenharmony_ci } 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci /* Otherwise just return the next peer */ 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci return (child_node->peer); 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci/******************************************************************************* 5462306a36Sopenharmony_ci * 5562306a36Sopenharmony_ci * FUNCTION: acpi_ns_get_next_node_typed 5662306a36Sopenharmony_ci * 5762306a36Sopenharmony_ci * PARAMETERS: type - Type of node to be searched for 5862306a36Sopenharmony_ci * parent_node - Parent node whose children we are 5962306a36Sopenharmony_ci * getting 6062306a36Sopenharmony_ci * child_node - Previous child that was found. 6162306a36Sopenharmony_ci * The NEXT child will be returned 6262306a36Sopenharmony_ci * 6362306a36Sopenharmony_ci * RETURN: struct acpi_namespace_node - Pointer to the NEXT child or NULL if 6462306a36Sopenharmony_ci * none is found. 6562306a36Sopenharmony_ci * 6662306a36Sopenharmony_ci * DESCRIPTION: Return the next peer node within the namespace. If Handle 6762306a36Sopenharmony_ci * is valid, Scope is ignored. Otherwise, the first node 6862306a36Sopenharmony_ci * within Scope is returned. 6962306a36Sopenharmony_ci * 7062306a36Sopenharmony_ci ******************************************************************************/ 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistruct acpi_namespace_node *acpi_ns_get_next_node_typed(acpi_object_type type, 7362306a36Sopenharmony_ci struct 7462306a36Sopenharmony_ci acpi_namespace_node 7562306a36Sopenharmony_ci *parent_node, 7662306a36Sopenharmony_ci struct 7762306a36Sopenharmony_ci acpi_namespace_node 7862306a36Sopenharmony_ci *child_node) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci struct acpi_namespace_node *next_node = NULL; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci ACPI_FUNCTION_ENTRY(); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci next_node = acpi_ns_get_next_node(parent_node, child_node); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci /* If any type is OK, we are done */ 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci if (type == ACPI_TYPE_ANY) { 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci /* next_node is NULL if we are at the end-of-list */ 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci return (next_node); 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci /* Must search for the node -- but within this scope only */ 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci while (next_node) { 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci /* If type matches, we are done */ 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci if (next_node->type == type) { 10362306a36Sopenharmony_ci return (next_node); 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci /* Otherwise, move on to the next peer node */ 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci next_node = next_node->peer; 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci /* Not found */ 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci return (NULL); 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci/******************************************************************************* 11762306a36Sopenharmony_ci * 11862306a36Sopenharmony_ci * FUNCTION: acpi_ns_walk_namespace 11962306a36Sopenharmony_ci * 12062306a36Sopenharmony_ci * PARAMETERS: type - acpi_object_type to search for 12162306a36Sopenharmony_ci * start_node - Handle in namespace where search begins 12262306a36Sopenharmony_ci * max_depth - Depth to which search is to reach 12362306a36Sopenharmony_ci * flags - Whether to unlock the NS before invoking 12462306a36Sopenharmony_ci * the callback routine 12562306a36Sopenharmony_ci * descending_callback - Called during tree descent 12662306a36Sopenharmony_ci * when an object of "Type" is found 12762306a36Sopenharmony_ci * ascending_callback - Called during tree ascent 12862306a36Sopenharmony_ci * when an object of "Type" is found 12962306a36Sopenharmony_ci * context - Passed to user function(s) above 13062306a36Sopenharmony_ci * return_value - from the user_function if terminated 13162306a36Sopenharmony_ci * early. Otherwise, returns NULL. 13262306a36Sopenharmony_ci * RETURNS: Status 13362306a36Sopenharmony_ci * 13462306a36Sopenharmony_ci * DESCRIPTION: Performs a modified depth-first walk of the namespace tree, 13562306a36Sopenharmony_ci * starting (and ending) at the node specified by start_handle. 13662306a36Sopenharmony_ci * The callback function is called whenever a node that matches 13762306a36Sopenharmony_ci * the type parameter is found. If the callback function returns 13862306a36Sopenharmony_ci * a non-zero value, the search is terminated immediately and 13962306a36Sopenharmony_ci * this value is returned to the caller. 14062306a36Sopenharmony_ci * 14162306a36Sopenharmony_ci * The point of this procedure is to provide a generic namespace 14262306a36Sopenharmony_ci * walk routine that can be called from multiple places to 14362306a36Sopenharmony_ci * provide multiple services; the callback function(s) can be 14462306a36Sopenharmony_ci * tailored to each task, whether it is a print function, 14562306a36Sopenharmony_ci * a compare function, etc. 14662306a36Sopenharmony_ci * 14762306a36Sopenharmony_ci ******************************************************************************/ 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ciacpi_status 15062306a36Sopenharmony_ciacpi_ns_walk_namespace(acpi_object_type type, 15162306a36Sopenharmony_ci acpi_handle start_node, 15262306a36Sopenharmony_ci u32 max_depth, 15362306a36Sopenharmony_ci u32 flags, 15462306a36Sopenharmony_ci acpi_walk_callback descending_callback, 15562306a36Sopenharmony_ci acpi_walk_callback ascending_callback, 15662306a36Sopenharmony_ci void *context, void **return_value) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci acpi_status status; 15962306a36Sopenharmony_ci acpi_status mutex_status; 16062306a36Sopenharmony_ci struct acpi_namespace_node *child_node; 16162306a36Sopenharmony_ci struct acpi_namespace_node *parent_node; 16262306a36Sopenharmony_ci acpi_object_type child_type; 16362306a36Sopenharmony_ci u32 level; 16462306a36Sopenharmony_ci u8 node_previously_visited = FALSE; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci ACPI_FUNCTION_TRACE(ns_walk_namespace); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci /* Special case for the namespace Root Node */ 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci if (start_node == ACPI_ROOT_OBJECT) { 17162306a36Sopenharmony_ci start_node = acpi_gbl_root_node; 17262306a36Sopenharmony_ci if (!start_node) { 17362306a36Sopenharmony_ci return_ACPI_STATUS(AE_NO_NAMESPACE); 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci /* Null child means "get first node" */ 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci parent_node = start_node; 18062306a36Sopenharmony_ci child_node = acpi_ns_get_next_node(parent_node, NULL); 18162306a36Sopenharmony_ci child_type = ACPI_TYPE_ANY; 18262306a36Sopenharmony_ci level = 1; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci /* 18562306a36Sopenharmony_ci * Traverse the tree of nodes until we bubble back up to where we 18662306a36Sopenharmony_ci * started. When Level is zero, the loop is done because we have 18762306a36Sopenharmony_ci * bubbled up to (and passed) the original parent handle (start_entry) 18862306a36Sopenharmony_ci */ 18962306a36Sopenharmony_ci while (level > 0 && child_node) { 19062306a36Sopenharmony_ci status = AE_OK; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci /* Found next child, get the type if we are not searching for ANY */ 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci if (type != ACPI_TYPE_ANY) { 19562306a36Sopenharmony_ci child_type = child_node->type; 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci /* 19962306a36Sopenharmony_ci * Ignore all temporary namespace nodes (created during control 20062306a36Sopenharmony_ci * method execution) unless told otherwise. These temporary nodes 20162306a36Sopenharmony_ci * can cause a race condition because they can be deleted during 20262306a36Sopenharmony_ci * the execution of the user function (if the namespace is 20362306a36Sopenharmony_ci * unlocked before invocation of the user function.) Only the 20462306a36Sopenharmony_ci * debugger namespace dump will examine the temporary nodes. 20562306a36Sopenharmony_ci */ 20662306a36Sopenharmony_ci if ((child_node->flags & ANOBJ_TEMPORARY) && 20762306a36Sopenharmony_ci !(flags & ACPI_NS_WALK_TEMP_NODES)) { 20862306a36Sopenharmony_ci status = AE_CTRL_DEPTH; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci /* Type must match requested type */ 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci else if (child_type == type) { 21462306a36Sopenharmony_ci /* 21562306a36Sopenharmony_ci * Found a matching node, invoke the user callback function. 21662306a36Sopenharmony_ci * Unlock the namespace if flag is set. 21762306a36Sopenharmony_ci */ 21862306a36Sopenharmony_ci if (flags & ACPI_NS_WALK_UNLOCK) { 21962306a36Sopenharmony_ci mutex_status = 22062306a36Sopenharmony_ci acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); 22162306a36Sopenharmony_ci if (ACPI_FAILURE(mutex_status)) { 22262306a36Sopenharmony_ci return_ACPI_STATUS(mutex_status); 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci /* 22762306a36Sopenharmony_ci * Invoke the user function, either descending, ascending, 22862306a36Sopenharmony_ci * or both. 22962306a36Sopenharmony_ci */ 23062306a36Sopenharmony_ci if (!node_previously_visited) { 23162306a36Sopenharmony_ci if (descending_callback) { 23262306a36Sopenharmony_ci status = 23362306a36Sopenharmony_ci descending_callback(child_node, 23462306a36Sopenharmony_ci level, context, 23562306a36Sopenharmony_ci return_value); 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci } else { 23862306a36Sopenharmony_ci if (ascending_callback) { 23962306a36Sopenharmony_ci status = 24062306a36Sopenharmony_ci ascending_callback(child_node, 24162306a36Sopenharmony_ci level, context, 24262306a36Sopenharmony_ci return_value); 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci if (flags & ACPI_NS_WALK_UNLOCK) { 24762306a36Sopenharmony_ci mutex_status = 24862306a36Sopenharmony_ci acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); 24962306a36Sopenharmony_ci if (ACPI_FAILURE(mutex_status)) { 25062306a36Sopenharmony_ci return_ACPI_STATUS(mutex_status); 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci switch (status) { 25562306a36Sopenharmony_ci case AE_OK: 25662306a36Sopenharmony_ci case AE_CTRL_DEPTH: 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* Just keep going */ 25962306a36Sopenharmony_ci break; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci case AE_CTRL_TERMINATE: 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci /* Exit now, with OK status */ 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci return_ACPI_STATUS(AE_OK); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci default: 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci /* All others are valid exceptions */ 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci return_ACPI_STATUS(status); 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci /* 27662306a36Sopenharmony_ci * Depth first search: Attempt to go down another level in the 27762306a36Sopenharmony_ci * namespace if we are allowed to. Don't go any further if we have 27862306a36Sopenharmony_ci * reached the caller specified maximum depth or if the user 27962306a36Sopenharmony_ci * function has specified that the maximum depth has been reached. 28062306a36Sopenharmony_ci */ 28162306a36Sopenharmony_ci if (!node_previously_visited && 28262306a36Sopenharmony_ci (level < max_depth) && (status != AE_CTRL_DEPTH)) { 28362306a36Sopenharmony_ci if (child_node->child) { 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci /* There is at least one child of this node, visit it */ 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci level++; 28862306a36Sopenharmony_ci parent_node = child_node; 28962306a36Sopenharmony_ci child_node = 29062306a36Sopenharmony_ci acpi_ns_get_next_node(parent_node, NULL); 29162306a36Sopenharmony_ci continue; 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci /* No more children, re-visit this node */ 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci if (!node_previously_visited) { 29862306a36Sopenharmony_ci node_previously_visited = TRUE; 29962306a36Sopenharmony_ci continue; 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci /* No more children, visit peers */ 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci child_node = acpi_ns_get_next_node(parent_node, child_node); 30562306a36Sopenharmony_ci if (child_node) { 30662306a36Sopenharmony_ci node_previously_visited = FALSE; 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci /* No peers, re-visit parent */ 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci else { 31262306a36Sopenharmony_ci /* 31362306a36Sopenharmony_ci * No more children of this node (acpi_ns_get_next_node failed), go 31462306a36Sopenharmony_ci * back upwards in the namespace tree to the node's parent. 31562306a36Sopenharmony_ci */ 31662306a36Sopenharmony_ci level--; 31762306a36Sopenharmony_ci child_node = parent_node; 31862306a36Sopenharmony_ci parent_node = parent_node->parent; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci node_previously_visited = TRUE; 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci /* Complete walk, not terminated by user function */ 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci return_ACPI_STATUS(AE_OK); 32762306a36Sopenharmony_ci} 328