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 committed Nov 14, 2024
1 parent 2f23313 commit a7ce143
Show file tree
Hide file tree
Showing 5 changed files with 98 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:func:`DT_FOREACH_CHILD`, which allows iterating over the children of a
devicetree node.
Expand Down
46 changes: 46 additions & 0 deletions include/zephyr/devicetree.h
Original file line number Diff line number Diff line change
Expand Up @@ -2961,6 +2961,52 @@
*/
#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 argument which will be the identifier
* of an ancestor node to traverse from @p node_id towards the root node.
*
* 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

Check notice on line 3005 in include/zephyr/devicetree.h

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

include/zephyr/devicetree.h:3005 -#define DT_FOREACH_ANCESTOR(node_id, fn) \ - DT_CAT(node_id, _FOREACH_ANCESTOR)(fn) +#define DT_FOREACH_ANCESTOR(node_id, fn) DT_CAT(node_id, _FOREACH_ANCESTOR)(fn)
*/
#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 @@ -570,6 +570,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 @@ -2225,6 +2225,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),

Check notice on line 2235 in tests/lib/devicetree/api/src/main.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

tests/lib/devicetree/api/src/main.c:2235 - /* 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), +/* 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
}

Check notice on line 2251 in tests/lib/devicetree/api/src/main.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

tests/lib/devicetree/api/src/main.c:2251 - #undef TEST_FUNC_AND_COMMA - #undef TEST_FUNC +#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 a7ce143

Please sign in to comment.