Skip to content

Commit

Permalink
devicetree: Add DT_FOREACH_ANCESTOR macro
Browse files Browse the repository at this point in the history
Add 'DT_FOREACH_ANCESTOR' macro to get a list of
ancestor node of a given node_id.

Signed-off-by: James Roy <[email protected]>
  • Loading branch information
rruuaanng authored and kartben committed Dec 18, 2024
1 parent a782ec1 commit 4553a21
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 0 deletions.
3 changes: 3 additions & 0 deletions doc/build/dts/api/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@ does not apply to macros which take cell names as arguments.
For-each macros
===============

The :c:macro:`DT_FOREACH_CHILD` macro allows iterating over the ancestor node
of a devicetree node.

There is currently only one "generic" for-each macro,
:c:macro:`DT_FOREACH_CHILD`, which allows iterating over the children of a
devicetree node.
Expand Down
49 changes: 49 additions & 0 deletions include/zephyr/devicetree.h
Original file line number Diff line number Diff line change
Expand Up @@ -2975,6 +2975,55 @@
*/
#define DT_FOREACH_STATUS_OKAY_NODE_VARGS(fn, ...) DT_FOREACH_OKAY_VARGS_HELPER(fn, __VA_ARGS__)

/**
* @brief Invokes @p fn for each ancestor of @p node_id
*
* The macro @p fn must take one parameter, which will be the identifier
* of a child node of @p node_id to enable traversal of all ancestor nodes.
*
* The ancestor will be iterated over in the same order as they
* appear in the final devicetree.
*
* Example devicetree fragment:
*
* @code{.dts}
* n: node1 {
* foobar = "foo1";
*
* n_2: node2 {
* foobar = "foo2";
*
* n_3: node3 {
* foobar = "foo3";
* };
* };
* };
* @endcode
*
* Example usage:
*
* @code{.c}
* #define GET_PROP(n) DT_PROP(n, foobar),
*
* const char *ancestor_names[] = {
* DT_FOREACH_ANCESTOR(DT_NODELABEL(n_3), GET_PROP)
* };
* @endcode
*
* This expands to:
*
* @code{.c}
* const char *ancestor_names[] = {
* "foo2", "foo1",
* };
* @endcode
*
* @param node_id node identifier
* @param fn macro to invoke
*/
#define DT_FOREACH_ANCESTOR(node_id, fn) \
DT_CAT(node_id, _FOREACH_ANCESTOR)(fn)

/**
* @brief Invokes @p fn for each child of @p node_id
*
Expand Down
12 changes: 12 additions & 0 deletions scripts/dts/gen_defines.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ def main():
out_dt_define(f"{node.z_path_id}_FOREACH_NODELABEL_VARGS(fn, ...)",
" ".join(f"fn({nodelabel}, __VA_ARGS__)" for nodelabel in node.labels))

write_parent(node)
write_children(node)
write_dep_info(node)
write_idents_and_existence(node)
Expand Down Expand Up @@ -457,6 +458,17 @@ def write_compatibles(node: edtlib.Node) -> None:
out_dt_define(f"{node.z_path_id}_COMPAT_MODEL_IDX_{i}",
quote_str(node.edt.compat2model[compat]))

def write_parent(node: edtlib.Node) -> None:
# Visit all parent nodes.
def _visit_parent_node(node: edtlib.Node):
while node is not None:
yield node.parent
node = node.parent

# Writes helper macros for dealing with node's parent.
out_dt_define(f"{node.z_path_id}_FOREACH_ANCESTOR(fn)",
" ".join(f"fn(DT_{parent.z_path_id})" for parent in
_visit_parent_node(node) if parent is not None))

def write_children(node: edtlib.Node) -> None:
# Writes helper macros for dealing with node's children.
Expand Down
12 changes: 12 additions & 0 deletions tests/lib/devicetree/api/app.overlay
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,18 @@
phys = <&test_transceiver1>;
};

test_parent: test-parent {
compatible = "vnd,parent-bindings";

test_parent_a: parent-a {
val = <0>;

test_parent_b: parent-b {
val = <0>;
};
};
};

/* there should only be one of these */
test_children: test-children {
compatible = "vnd,child-bindings";
Expand Down
25 changes: 25 additions & 0 deletions tests/lib/devicetree/api/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -2344,6 +2344,31 @@ ZTEST(devicetree_api, test_parent)
TEST_SPI_BUS_0), "");
}

#undef DT_DRV_COMPAT
#define DT_DRV_COMPAT vnd_parent_bindings
ZTEST(devicetree_api, test_parent_nodes_list)
{
/* When traversing upwards, there are no fixed attributes and labels */
#define TEST_FUNC(parent) { /* No operation */ }
#define TEST_FUNC_AND_COMMA(parent) TEST_FUNC(parent),

struct vnd_parent_binding {
int val;
};

struct vnd_parent_binding vals_a[] = {
DT_FOREACH_ANCESTOR(DT_NODELABEL(test_parent_a), TEST_FUNC_AND_COMMA)};

struct vnd_parent_binding vals_b[] = {
DT_FOREACH_ANCESTOR(DT_NODELABEL(test_parent_b), TEST_FUNC_AND_COMMA)};

zassert_equal(ARRAY_SIZE(vals_a), 3, "");
zassert_equal(ARRAY_SIZE(vals_b), 4, "");

#undef TEST_FUNC_AND_COMMA
#undef TEST_FUNC
}

#undef DT_DRV_COMPAT
#define DT_DRV_COMPAT vnd_i2c_mux_controller
ZTEST(devicetree_api, test_gparent)
Expand Down

0 comments on commit 4553a21

Please sign in to comment.