Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

devicetree: Add DT_FOREACH_ANCESTOR macro #81338

Merged
merged 1 commit into from
Dec 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
49 changes: 49 additions & 0 deletions include/zephyr/devicetree.h
Original file line number Diff line number Diff line change
Expand Up @@ -2961,6 +2961,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

Check notice on line 3008 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:3008 -#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))
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The if here cannot be omitted. The generator will traverse to the root node so that it has a 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 @@
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 */ }
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't want to add too many properties for testing in the test dts, so I chose to do this. Make it only used to fill in the macro parameters, and then calculate whether the number of nodes it traverses matches.

#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, "");
Comment on lines +2246 to +2247
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is here.


#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
Loading