devicetree: Add DT_FOREACH_ANCESTOR macro

Add 'DT_FOREACH_ANCESTOR' macro to get a list of
ancestor node of a given node_id.

Signed-off-by: James Roy <rruuaanng@outlook.com>
This commit is contained in:
James Roy 2024-11-14 21:10:00 +08:00 committed by Benjamin Cabé
parent a782ec14bc
commit 4553a21fe5
5 changed files with 101 additions and 0 deletions

View File

@ -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.

View File

@ -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
*

View File

@ -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)
@ -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.

View File

@ -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";

View File

@ -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)