linux/drivers/acpi/acpica/nswalk.c
<<
>>
Prefs
   1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
   2/******************************************************************************
   3 *
   4 * Module Name: nswalk - Functions for walking the ACPI namespace
   5 *
   6 * Copyright (C) 2000 - 2020, Intel Corp.
   7 *
   8 *****************************************************************************/
   9
  10#include <acpi/acpi.h>
  11#include "accommon.h"
  12#include "acnamesp.h"
  13
  14#define _COMPONENT          ACPI_NAMESPACE
  15ACPI_MODULE_NAME("nswalk")
  16
  17/*******************************************************************************
  18 *
  19 * FUNCTION:    acpi_ns_get_next_node
  20 *
  21 * PARAMETERS:  parent_node         - Parent node whose children we are
  22 *                                    getting
  23 *              child_node          - Previous child that was found.
  24 *                                    The NEXT child will be returned
  25 *
  26 * RETURN:      struct acpi_namespace_node - Pointer to the NEXT child or NULL if
  27 *                                    none is found.
  28 *
  29 * DESCRIPTION: Return the next peer node within the namespace. If Handle
  30 *              is valid, Scope is ignored. Otherwise, the first node
  31 *              within Scope is returned.
  32 *
  33 ******************************************************************************/
  34struct acpi_namespace_node *acpi_ns_get_next_node(struct acpi_namespace_node
  35                                                  *parent_node,
  36                                                  struct acpi_namespace_node
  37                                                  *child_node)
  38{
  39        ACPI_FUNCTION_ENTRY();
  40
  41        if (!child_node) {
  42
  43                /* It's really the parent's _scope_ that we want */
  44
  45                return (parent_node->child);
  46        }
  47
  48        /* Otherwise just return the next peer */
  49
  50        return (child_node->peer);
  51}
  52
  53/*******************************************************************************
  54 *
  55 * FUNCTION:    acpi_ns_get_next_node_typed
  56 *
  57 * PARAMETERS:  type                - Type of node to be searched for
  58 *              parent_node         - Parent node whose children we are
  59 *                                    getting
  60 *              child_node          - Previous child that was found.
  61 *                                    The NEXT child will be returned
  62 *
  63 * RETURN:      struct acpi_namespace_node - Pointer to the NEXT child or NULL if
  64 *                                    none is found.
  65 *
  66 * DESCRIPTION: Return the next peer node within the namespace. If Handle
  67 *              is valid, Scope is ignored. Otherwise, the first node
  68 *              within Scope is returned.
  69 *
  70 ******************************************************************************/
  71
  72struct acpi_namespace_node *acpi_ns_get_next_node_typed(acpi_object_type type,
  73                                                        struct
  74                                                        acpi_namespace_node
  75                                                        *parent_node,
  76                                                        struct
  77                                                        acpi_namespace_node
  78                                                        *child_node)
  79{
  80        struct acpi_namespace_node *next_node = NULL;
  81
  82        ACPI_FUNCTION_ENTRY();
  83
  84        next_node = acpi_ns_get_next_node(parent_node, child_node);
  85
  86
  87        /* If any type is OK, we are done */
  88
  89        if (type == ACPI_TYPE_ANY) {
  90
  91                /* next_node is NULL if we are at the end-of-list */
  92
  93                return (next_node);
  94        }
  95
  96        /* Must search for the node -- but within this scope only */
  97
  98        while (next_node) {
  99
 100                /* If type matches, we are done */
 101
 102                if (next_node->type == type) {
 103                        return (next_node);
 104                }
 105
 106                /* Otherwise, move on to the next peer node */
 107
 108                next_node = next_node->peer;
 109        }
 110
 111        /* Not found */
 112
 113        return (NULL);
 114}
 115
 116/*******************************************************************************
 117 *
 118 * FUNCTION:    acpi_ns_walk_namespace
 119 *
 120 * PARAMETERS:  type                - acpi_object_type to search for
 121 *              start_node          - Handle in namespace where search begins
 122 *              max_depth           - Depth to which search is to reach
 123 *              flags               - Whether to unlock the NS before invoking
 124 *                                    the callback routine
 125 *              descending_callback - Called during tree descent
 126 *                                    when an object of "Type" is found
 127 *              ascending_callback  - Called during tree ascent
 128 *                                    when an object of "Type" is found
 129 *              context             - Passed to user function(s) above
 130 *              return_value        - from the user_function if terminated
 131 *                                    early. Otherwise, returns NULL.
 132 * RETURNS:     Status
 133 *
 134 * DESCRIPTION: Performs a modified depth-first walk of the namespace tree,
 135 *              starting (and ending) at the node specified by start_handle.
 136 *              The callback function is called whenever a node that matches
 137 *              the type parameter is found. If the callback function returns
 138 *              a non-zero value, the search is terminated immediately and
 139 *              this value is returned to the caller.
 140 *
 141 *              The point of this procedure is to provide a generic namespace
 142 *              walk routine that can be called from multiple places to
 143 *              provide multiple services; the callback function(s) can be
 144 *              tailored to each task, whether it is a print function,
 145 *              a compare function, etc.
 146 *
 147 ******************************************************************************/
 148
 149acpi_status
 150acpi_ns_walk_namespace(acpi_object_type type,
 151                       acpi_handle start_node,
 152                       u32 max_depth,
 153                       u32 flags,
 154                       acpi_walk_callback descending_callback,
 155                       acpi_walk_callback ascending_callback,
 156                       void *context, void **return_value)
 157{
 158        acpi_status status;
 159        acpi_status mutex_status;
 160        struct acpi_namespace_node *child_node;
 161        struct acpi_namespace_node *parent_node;
 162        acpi_object_type child_type;
 163        u32 level;
 164        u8 node_previously_visited = FALSE;
 165
 166        ACPI_FUNCTION_TRACE(ns_walk_namespace);
 167
 168        /* Special case for the namespace Root Node */
 169
 170        if (start_node == ACPI_ROOT_OBJECT) {
 171                start_node = acpi_gbl_root_node;
 172        }
 173
 174        /* Null child means "get first node" */
 175
 176        parent_node = start_node;
 177        child_node = acpi_ns_get_next_node(parent_node, NULL);
 178        child_type = ACPI_TYPE_ANY;
 179        level = 1;
 180
 181        /*
 182         * Traverse the tree of nodes until we bubble back up to where we
 183         * started. When Level is zero, the loop is done because we have
 184         * bubbled up to (and passed) the original parent handle (start_entry)
 185         */
 186        while (level > 0 && child_node) {
 187                status = AE_OK;
 188
 189                /* Found next child, get the type if we are not searching for ANY */
 190
 191                if (type != ACPI_TYPE_ANY) {
 192                        child_type = child_node->type;
 193                }
 194
 195                /*
 196                 * Ignore all temporary namespace nodes (created during control
 197                 * method execution) unless told otherwise. These temporary nodes
 198                 * can cause a race condition because they can be deleted during
 199                 * the execution of the user function (if the namespace is
 200                 * unlocked before invocation of the user function.) Only the
 201                 * debugger namespace dump will examine the temporary nodes.
 202                 */
 203                if ((child_node->flags & ANOBJ_TEMPORARY) &&
 204                    !(flags & ACPI_NS_WALK_TEMP_NODES)) {
 205                        status = AE_CTRL_DEPTH;
 206                }
 207
 208                /* Type must match requested type */
 209
 210                else if (child_type == type) {
 211                        /*
 212                         * Found a matching node, invoke the user callback function.
 213                         * Unlock the namespace if flag is set.
 214                         */
 215                        if (flags & ACPI_NS_WALK_UNLOCK) {
 216                                mutex_status =
 217                                    acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
 218                                if (ACPI_FAILURE(mutex_status)) {
 219                                        return_ACPI_STATUS(mutex_status);
 220                                }
 221                        }
 222
 223                        /*
 224                         * Invoke the user function, either descending, ascending,
 225                         * or both.
 226                         */
 227                        if (!node_previously_visited) {
 228                                if (descending_callback) {
 229                                        status =
 230                                            descending_callback(child_node,
 231                                                                level, context,
 232                                                                return_value);
 233                                }
 234                        } else {
 235                                if (ascending_callback) {
 236                                        status =
 237                                            ascending_callback(child_node,
 238                                                               level, context,
 239                                                               return_value);
 240                                }
 241                        }
 242
 243                        if (flags & ACPI_NS_WALK_UNLOCK) {
 244                                mutex_status =
 245                                    acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
 246                                if (ACPI_FAILURE(mutex_status)) {
 247                                        return_ACPI_STATUS(mutex_status);
 248                                }
 249                        }
 250
 251                        switch (status) {
 252                        case AE_OK:
 253                        case AE_CTRL_DEPTH:
 254
 255                                /* Just keep going */
 256                                break;
 257
 258                        case AE_CTRL_TERMINATE:
 259
 260                                /* Exit now, with OK status */
 261
 262                                return_ACPI_STATUS(AE_OK);
 263
 264                        default:
 265
 266                                /* All others are valid exceptions */
 267
 268                                return_ACPI_STATUS(status);
 269                        }
 270                }
 271
 272                /*
 273                 * Depth first search: Attempt to go down another level in the
 274                 * namespace if we are allowed to. Don't go any further if we have
 275                 * reached the caller specified maximum depth or if the user
 276                 * function has specified that the maximum depth has been reached.
 277                 */
 278                if (!node_previously_visited &&
 279                    (level < max_depth) && (status != AE_CTRL_DEPTH)) {
 280                        if (child_node->child) {
 281
 282                                /* There is at least one child of this node, visit it */
 283
 284                                level++;
 285                                parent_node = child_node;
 286                                child_node =
 287                                    acpi_ns_get_next_node(parent_node, NULL);
 288                                continue;
 289                        }
 290                }
 291
 292                /* No more children, re-visit this node */
 293
 294                if (!node_previously_visited) {
 295                        node_previously_visited = TRUE;
 296                        continue;
 297                }
 298
 299                /* No more children, visit peers */
 300
 301                child_node = acpi_ns_get_next_node(parent_node, child_node);
 302                if (child_node) {
 303                        node_previously_visited = FALSE;
 304                }
 305
 306                /* No peers, re-visit parent */
 307
 308                else {
 309                        /*
 310                         * No more children of this node (acpi_ns_get_next_node failed), go
 311                         * back upwards in the namespace tree to the node's parent.
 312                         */
 313                        level--;
 314                        child_node = parent_node;
 315                        parent_node = parent_node->parent;
 316
 317                        node_previously_visited = TRUE;
 318                }
 319        }
 320
 321        /* Complete walk, not terminated by user function */
 322
 323        return_ACPI_STATUS(AE_OK);
 324}
 325