devicetree: add new for-each macros

Add two new for-each macros:

- DT_FOREACH_STATUS_OKAY(compat, fn)
- DT_FOREACH_STATUS_OKAY_VARGS(compat, fn, ...)

These can be used to expand "fn" once for every status "okay" node in
the devicetree which has a given compatible. The intended use case is
to allow doing something in C once for each node of a compatible,
but outside of a device driver.

E.g. an application might want to collect an array of structures for a
compatible, where each structure is initialized from a node.

In such cases we don't want people to be forced into using
DT_DRV_COMPAT and instance numbers, because that's in general a hint
that you're doing something driver-like.

Signed-off-by: Martí Bolívar <marti.bolivar@nordicsemi.no>
This commit is contained in:
Martí Bolívar 2021-08-05 15:42:23 -07:00 committed by Kumar Gala
commit 56da140791
2 changed files with 123 additions and 0 deletions

View file

@ -1714,6 +1714,105 @@
#define DT_FOREACH_PROP_ELEM_VARGS(node_id, prop, fn, ...) \
DT_CAT4(node_id, _P_, prop, _FOREACH_PROP_ELEM_VARGS)(fn, __VA_ARGS__)
/**
* @brief Call "fn" on all nodes with compatible DT_DRV_COMPAT
* and status "okay"
*
* This macro expands to:
*
* fn(node_id_1) fn(node_id_2) ... fn(node_id_n)
*
* where each "node_id_<i>" is a node identifier for some node with
* compatible "compat" and status "okay". Whitespace is added between
* expansions as shown above.
*
* Example devicetree fragment:
*
* / {
* a {
* compatible = "foo";
* status = "okay";
* };
* b {
* compatible = "foo";
* status = "disabled";
* };
* c {
* compatible = "foo";
* };
* };
*
* Example usage:
*
* DT_FOREACH_STATUS_OKAY(foo, DT_NODE_PATH)
*
* This expands to one of the following:
*
* "/a" "/c"
* "/c" "/a"
*
* "One of the following" is because no guarantees are made about the
* order that node identifiers are passed to "fn" in the expansion.
*
* (The "/c" string literal is present because a missing status
* property is always treated as if the status were set to "okay".)
*
* Note also that "fn" is responsible for adding commas, semicolons,
* or other terminators as needed.
*
* @param compat lowercase-and-underscores devicetree compatible
* @param fn Macro to call for each enabled node. Must accept a
* node_id as its only parameter.
*/
#define DT_FOREACH_STATUS_OKAY(compat, fn) \
COND_CODE_1(DT_HAS_COMPAT_STATUS_OKAY(compat), \
(UTIL_CAT(DT_FOREACH_OKAY_, compat)(fn)), \
())
/**
* @brief Invokes "fn" for each status "okay" node of a compatible
* with multiple arguments.
*
* This is like DT_FOREACH_STATUS_OKAY() except you can also pass
* additional arguments to "fn".
*
* Example devicetree fragment:
*
* / {
* a {
* compatible = "foo";
* val = <3>;
* };
* b {
* compatible = "foo";
* val = <4>;
* };
* };
*
* Example usage:
*
* #define MY_FN(node_id, operator) DT_PROP(node_id, val) operator
* x = DT_FOREACH_STATUS_OKAY_VARGS(foo, MY_FN, +) 0;
*
* This expands to one of the following:
*
* x = 3 + 4 + 0;
* x = 4 + 3 + 0;
*
* i.e. it sets x to 7. As with DT_FOREACH_STATUS_OKAY(), there are no
* guarantees about the order nodes appear in the expansion.
*
* @param compat lowercase-and-underscores devicetree compatible
* @param fn Macro to call for each enabled node. Must accept a
* node_id as its only parameter.
* @param ... Additional arguments to pass to "fn"
*/
#define DT_FOREACH_STATUS_OKAY_VARGS(compat, fn, ...) \
COND_CODE_1(DT_HAS_COMPAT_STATUS_OKAY(compat), \
(UTIL_CAT(DT_FOREACH_OKAY_VARGS_, \
compat)(fn, __VA_ARGS__)), \
())
/**
* @}
*/

View file

@ -1265,6 +1265,30 @@ static void test_foreach_status_okay(void)
* for-each-property type macros.
*/
unsigned int val;
const char *str;
/* This should expand to something like:
*
* "/test/enum-0" "/test/enum-1"
*
* but there is no guarantee about the order of nodes in the
* expansion, so we test both.
*/
str = DT_FOREACH_STATUS_OKAY(vnd_enum_holder, DT_NODE_PATH);
zassert_true(!strcmp(str, "/test/enum-0/test/enum-1") ||
!strcmp(str, "/test/enum-1/test/enum-0"), "");
#undef MY_FN
#define MY_FN(node_id, operator) DT_ENUM_IDX(node_id, val) operator
/* This should expand to something like:
*
* 0 + 2 + 3
*
* and order of expansion doesn't matter, since we're adding
* the values all up.
*/
val = DT_FOREACH_STATUS_OKAY_VARGS(vnd_enum_holder, MY_FN, +) 3;
zassert_equal(val, 5, "");
/*
* Make sure DT_INST_FOREACH_STATUS_OKAY can be called from functions