Skip to content

Commit d0b9c03

Browse files
arvinfcfriedt
authored andcommitted
devicetree: Add _VARGS variants to _FOREACH_ marcos
`_FOREACH_` macros do not allow the caller to pass additional arguments to the `fn`. A series of `_VARGS` variants have been added that allow the caller to pass arbitrary number of arguments to the `fn`: ``` DT_FOREACH_CHILD_VARGS DT_FOREACH_CHILD_STATUS_OKAY_VARGS DT_FOREACH_PROP_ELEM_VARGS DT_INST_FOREACH_CHILD_VARGS DT_INST_FOREACH_STATUS_OKAY_VARGS DT_INST_FOREACH_PROP_ELEM_VARGS ``` Signed-off-by: Arvin Farahmand <[email protected]>
1 parent 2486b45 commit d0b9c03

File tree

4 files changed

+210
-0
lines changed

4 files changed

+210
-0
lines changed

doc/guides/dts/macros.bnf

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,11 @@ node-macro =/ %s"DT_N" path-id %s"_PARENT"
5151
; These are used internally by DT_FOREACH_CHILD, which iterates over
5252
; each child node.
5353
node-macro =/ %s"DT_N" path-id %s"_FOREACH_CHILD"
54+
node-macro =/ %s"DT_N" path-id %s"_FOREACH_CHILD_VARGS"
5455
; These are used internally by DT_FOREACH_CHILD_STATUS_OKAY, which iterates
5556
; over each child node with status "okay".
5657
node-macro =/ %s"DT_N" path-id %s"_FOREACH_CHILD_STATUS_OKAY"
58+
node-macro =/ %s"DT_N" path-id %s"_FOREACH_CHILD_STATUS_OKAY_VARGS"
5759
; The node's status macro; dt-name in this case is something like "okay"
5860
; or "disabled".
5961
node-macro =/ %s"DT_N" path-id %s"_STATUS_" dt-name
@@ -168,6 +170,7 @@ other-macro =/ %s"DT_N_INST_" dt-name %s"_NUM_OKAY"
168170
; These are used internally by DT_INST_FOREACH, which iterates over
169171
; each enabled instance of a compatible.
170172
other-macro =/ %s"DT_FOREACH_OKAY_INST_" dt-name
173+
other-macro =/ %s"DT_FOREACH_OKAY_INST_VARGS_" dt-name
171174
; E.g.: #define DT_CHOSEN_zephyr_flash
172175
other-macro =/ %s"DT_CHOSEN_" dt-name
173176
; Declares that a compatible has at least one node on a bus.

include/devicetree.h

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
* _ENUM_UPPER_TOKEN: like _ENUM_TOKEN, but uppercased
4646
* _EXISTS: property is defined
4747
* _FOREACH_PROP_ELEM: helper for "iterating" over values in the property
48+
* _FOREACH_PROP_ELEM_VARGS: foreach functions with variable number of arguments
4849
* _IDX_<i>: logical index into property
4950
* _IDX_<i>_EXISTS: logical index into property is defined
5051
* _IDX_<i>_PH: phandle array's phandle by index (or phandle, phandles)
@@ -1485,6 +1486,21 @@
14851486
#define DT_FOREACH_CHILD(node_id, fn) \
14861487
DT_CAT(node_id, _FOREACH_CHILD)(fn)
14871488

1489+
/**
1490+
* @brief Invokes "fn" for each child of "node_id" with multiple arguments
1491+
*
1492+
* The macro "fn" takes multiple arguments. The first should be the node
1493+
* identifier for the child node. The remaining are passed-in by the caller.
1494+
*
1495+
* @param node_id node identifier
1496+
* @param fn macro to invoke
1497+
* @param ... variable number of arguments to pass to fn
1498+
*
1499+
* @see DT_FOREACH_CHILD
1500+
*/
1501+
#define DT_FOREACH_CHILD_VARGS(node_id, fn, ...) \
1502+
DT_CAT(node_id, _FOREACH_CHILD_VARGS)(fn, __VA_ARGS__)
1503+
14881504
/**
14891505
* @brief Call "fn" on the child nodes with status "okay"
14901506
*
@@ -1500,6 +1516,25 @@
15001516
#define DT_FOREACH_CHILD_STATUS_OKAY(node_id, fn) \
15011517
DT_CAT(node_id, _FOREACH_CHILD_STATUS_OKAY)(fn)
15021518

1519+
/**
1520+
* @brief Call "fn" on the child nodes with status "okay" with multiple
1521+
* arguments
1522+
*
1523+
* The macro "fn" takes multiple arguments. The first should be the node
1524+
* identifier for the child node. The remaining are passed-in by the caller.
1525+
*
1526+
* As usual, both a missing status and an "ok" status are
1527+
* treated as "okay".
1528+
*
1529+
* @param node_id node identifier
1530+
* @param fn macro to invoke
1531+
* @param ... variable number of arguments to pass to fn
1532+
*
1533+
* @see DT_FOREACH_CHILD_STATUS_OKAY
1534+
*/
1535+
#define DT_FOREACH_CHILD_STATUS_OKAY_VARGS(node_id, fn, ...) \
1536+
DT_CAT(node_id, _FOREACH_CHILD_STATUS_OKAY_VARGS)(fn, __VA_ARGS__)
1537+
15031538
/**
15041539
* @brief Invokes "fn" for each element in the value of property "prop".
15051540
*
@@ -1547,6 +1582,26 @@
15471582
#define DT_FOREACH_PROP_ELEM(node_id, prop, fn) \
15481583
DT_CAT4(node_id, _P_, prop, _FOREACH_PROP_ELEM)(fn)
15491584

1585+
/**
1586+
* @brief Invokes "fn" for each element in the value of property "prop" with
1587+
* multiple arguments.
1588+
*
1589+
* The macro "fn" must take multiple parameters: fn(node_id, prop, idx, ...).
1590+
* "node_id" and "prop" are the same as what is passed to
1591+
* DT_FOREACH_PROP_ELEM, and "idx" is the current index into the array.
1592+
* The "idx" values are integer literals starting from 0. The remaining
1593+
* arguments are passed-in by the caller.
1594+
*
1595+
* @param node_id node identifier
1596+
* @param prop lowercase-and-underscores property name
1597+
* @param fn macro to invoke
1598+
* @param ... variable number of arguments to pass to fn
1599+
*
1600+
* @see DT_FOREACH_PROP_ELEM
1601+
*/
1602+
#define DT_FOREACH_PROP_ELEM_VARGS(node_id, prop, fn, ...) \
1603+
DT_CAT4(node_id, _P_, prop, _FOREACH_PROP_ELEM_VARGS)(fn, __VA_ARGS__)
1604+
15501605
/**
15511606
* @}
15521607
*/
@@ -1820,6 +1875,21 @@
18201875
#define DT_INST_FOREACH_CHILD(inst, fn) \
18211876
DT_FOREACH_CHILD(DT_DRV_INST(inst), fn)
18221877

1878+
/**
1879+
* @brief Call "fn" on all child nodes of DT_DRV_INST(inst).
1880+
*
1881+
* The macro "fn" takes multiple arguments. The first should be the node
1882+
* identifier for the child node. The remaining are passed-in by the caller.
1883+
*
1884+
* @param inst instance number
1885+
* @param fn macro to invoke on each child node identifier
1886+
* @param ... variable number of arguments to pass to fn
1887+
*
1888+
* @see DT_FOREACH_CHILD
1889+
*/
1890+
#define DT_INST_FOREACH_CHILD_VARGS(inst, fn, ...) \
1891+
DT_FOREACH_CHILD_VARGS(DT_DRV_INST(inst), fn, __VA_ARGS__)
1892+
18231893
/**
18241894
* @brief Get a DT_DRV_COMPAT instance property
18251895
* @param inst instance number
@@ -2213,6 +2283,23 @@
22132283
DT_DRV_COMPAT)(fn)), \
22142284
())
22152285

2286+
/**
2287+
* @brief Call "fn" on all nodes with compatible DT_DRV_COMPAT
2288+
* and status "okay" with multiple arguments
2289+
*
2290+
*
2291+
* @param fn Macro to call for each enabled node. Must accept an
2292+
* instance number as its only parameter.
2293+
* @param ... variable number of arguments to pass to fn
2294+
*
2295+
* @see DT_INST_FOREACH_STATUS_OKAY
2296+
*/
2297+
#define DT_INST_FOREACH_STATUS_OKAY_VARGS(fn, ...) \
2298+
COND_CODE_1(DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT), \
2299+
(UTIL_CAT(DT_FOREACH_OKAY_INST_VARGS_, \
2300+
DT_DRV_COMPAT)(fn, __VA_ARGS__)), \
2301+
())
2302+
22162303
/**
22172304
* @brief Invokes "fn" for each element of property "prop" for
22182305
* a DT_DRV_COMPAT instance.
@@ -2226,6 +2313,23 @@
22262313
#define DT_INST_FOREACH_PROP_ELEM(inst, prop, fn) \
22272314
DT_FOREACH_PROP_ELEM(DT_DRV_INST(inst), prop, fn)
22282315

2316+
/**
2317+
* @brief Invokes "fn" for each element of property "prop" for
2318+
* a DT_DRV_COMPAT instance with multiple arguments.
2319+
*
2320+
* Equivalent to
2321+
* DT_FOREACH_PROP_ELEM_VARGS(DT_DRV_INST(inst), prop, fn, __VA_ARGS__)
2322+
*
2323+
* @param inst instance number
2324+
* @param prop lowercase-and-underscores property name
2325+
* @param fn macro to invoke
2326+
* @param ... variable number of arguments to pass to fn
2327+
*
2328+
* @see DT_INST_FOREACH_PROP_ELEM
2329+
*/
2330+
#define DT_INST_FOREACH_PROP_ELEM_VARGS(inst, prop, fn, ...) \
2331+
DT_FOREACH_PROP_ELEM_VARGS(DT_DRV_INST(inst), prop, fn, __VA_ARGS__)
2332+
22292333
/**
22302334
* @brief Does a DT_DRV_COMPAT instance have a property?
22312335
* @param inst instance number

scripts/dts/gen_defines.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,17 +479,25 @@ def write_child_functions(node):
479479
" ".join(f"fn(DT_{child.z_path_id})" for child in
480480
node.children.values()))
481481

482+
out_dt_define(f"{node.z_path_id}_FOREACH_CHILD_VARGS(fn, ...)",
483+
" ".join(f"fn(DT_{child.z_path_id}, __VA_ARGS__)" for child in
484+
node.children.values()))
482485

483486
def write_child_functions_status_okay(node):
484487
# Writes macro that are helpers that will call a macro/function
485488
# for each child node with status "okay".
486489

487490
functions = ''
491+
functions_args = ''
488492
for child in node.children.values():
489493
if child.status == "okay":
490494
functions = functions + f"fn(DT_{child.z_path_id}) "
495+
functions_args = functions_args + f"fn(DT_{child.z_path_id}, " \
496+
"__VA_ARGS__) "
491497

492498
out_dt_define(f"{node.z_path_id}_FOREACH_CHILD_STATUS_OKAY(fn)", functions)
499+
out_dt_define(f"{node.z_path_id}_FOREACH_CHILD_STATUS_OKAY_VARGS(fn, ...)",
500+
functions_args)
493501

494502

495503
def write_status(node):
@@ -549,6 +557,11 @@ def write_vanilla_props(node):
549557
' \\\n\t'.join(f'fn(DT_{node.z_path_id}, {prop_id}, {i})'
550558
for i in range(len(prop.val)))
551559

560+
macro2val[f"{macro}_FOREACH_PROP_ELEM_VARGS(fn, ...)"] = \
561+
' \\\n\t'.join(f'fn(DT_{node.z_path_id}, {prop_id}, {i},'
562+
' __VA_ARGS__)'
563+
for i in range(len(prop.val)))
564+
552565
plen = prop_len(prop)
553566
if plen is not None:
554567
# DT_N_<node-id>_P_<prop-id>_LEN
@@ -749,6 +762,10 @@ def write_global_compat_info(edt):
749762
" ".join(f"fn({edt.compat2nodes[compat].index(node)})"
750763
for node in okay_nodes)
751764

765+
for_each_macros[f"DT_FOREACH_OKAY_INST_VARGS_{ident}(fn, ...)"] = \
766+
" ".join(f"fn({edt.compat2nodes[compat].index(node)}, __VA_ARGS__)"
767+
for node in okay_nodes)
768+
752769
for compat, nodes in edt.compat2nodes.items():
753770
for node in nodes:
754771
if compat == "fixed-partitions":

tests/lib/devicetree/api/src/main.c

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1280,6 +1280,34 @@ static void test_foreach_prop_elem(void)
12801280
#undef TIMES_TWO
12811281
}
12821282

1283+
static void test_foreach_prop_elem_varg(void)
1284+
{
1285+
#define TIMES_TWO_ADD(node_id, prop, idx, arg) \
1286+
((2 * DT_PROP_BY_IDX(node_id, prop, idx)) + arg),
1287+
1288+
int array[] = {
1289+
DT_FOREACH_PROP_ELEM_VARGS(TEST_ARRAYS, a, TIMES_TWO_ADD, 3)
1290+
};
1291+
1292+
zassert_equal(ARRAY_SIZE(array), 3, "");
1293+
zassert_equal(array[0], 2003, "");
1294+
zassert_equal(array[1], 4003, "");
1295+
zassert_equal(array[2], 6003, "");
1296+
1297+
#undef DT_DRV_COMPAT
1298+
#define DT_DRV_COMPAT vnd_array_holder
1299+
1300+
int inst_array[] = {
1301+
DT_INST_FOREACH_PROP_ELEM_VARGS(0, a, TIMES_TWO_ADD, 3)
1302+
};
1303+
1304+
zassert_equal(ARRAY_SIZE(inst_array), ARRAY_SIZE(array), "");
1305+
zassert_equal(inst_array[0], array[0], "");
1306+
zassert_equal(inst_array[1], array[1], "");
1307+
zassert_equal(inst_array[2], array[2], "");
1308+
#undef TIMES_TWO
1309+
}
1310+
12831311
struct test_gpio_info {
12841312
uint32_t reg_addr;
12851313
uint32_t reg_len;
@@ -1378,6 +1406,15 @@ static void test_devices(void)
13781406
#define INC(inst_ignored) do { val++; } while (0);
13791407
DT_INST_FOREACH_STATUS_OKAY(INC)
13801408
zassert_equal(val, 2, "");
1409+
#undef INC
1410+
1411+
val = 0;
1412+
#define INC_ARG(arg) do { val++; val += arg; } while (0)
1413+
#define INC(inst_ignored, arg) INC_ARG(arg);
1414+
DT_INST_FOREACH_STATUS_OKAY_VARGS(INC, 1)
1415+
zassert_equal(val, 4, "");
1416+
#undef INC_ARG
1417+
#undef INC
13811418

13821419
/*
13831420
* Make sure DT_INST_FOREACH_STATUS_OKAY works with 0 instances, and does
@@ -1387,6 +1424,13 @@ static void test_devices(void)
13871424
#define DT_DRV_COMPAT xxxx
13881425
#define BUILD_BUG_ON_EXPANSION (there is a bug in devicetree.h)
13891426
DT_INST_FOREACH_STATUS_OKAY(BUILD_BUG_ON_EXPANSION)
1427+
#undef BUILD_BUG_ON_EXPANSION
1428+
1429+
#undef DT_DRV_COMPAT
1430+
#define DT_DRV_COMPAT xxxx
1431+
#define BUILD_BUG_ON_EXPANSION(arg) (there is a bug in devicetree.h)
1432+
DT_INST_FOREACH_STATUS_OKAY_VARGS(BUILD_BUG_ON_EXPANSION, 1)
1433+
#undef BUILD_BUG_ON_EXPANSION
13901434
}
13911435

13921436
static void test_cs_gpios(void)
@@ -1589,6 +1633,46 @@ static void test_child_nodes_list(void)
15891633
#undef TEST_FUNC
15901634
}
15911635

1636+
#undef DT_DRV_COMPAT
1637+
#define DT_DRV_COMPAT vnd_child_bindings
1638+
static void test_child_nodes_list_varg(void)
1639+
{
1640+
#define TEST_FUNC(child, arg) { DT_PROP(child, val) + arg },
1641+
#define TEST_PARENT DT_PARENT(DT_NODELABEL(test_child_a))
1642+
1643+
struct vnd_child_binding {
1644+
int val;
1645+
};
1646+
1647+
struct vnd_child_binding vals[] = {
1648+
DT_FOREACH_CHILD_VARGS(TEST_PARENT, TEST_FUNC, 1)
1649+
};
1650+
1651+
struct vnd_child_binding vals_inst[] = {
1652+
DT_INST_FOREACH_CHILD_VARGS(0, TEST_FUNC, 1)
1653+
};
1654+
1655+
struct vnd_child_binding vals_status_okay[] = {
1656+
DT_FOREACH_CHILD_STATUS_OKAY_VARGS(TEST_PARENT, TEST_FUNC, 1)
1657+
};
1658+
1659+
zassert_equal(ARRAY_SIZE(vals), 3, "");
1660+
zassert_equal(ARRAY_SIZE(vals_inst), 3, "");
1661+
zassert_equal(ARRAY_SIZE(vals_status_okay), 2, "");
1662+
1663+
zassert_equal(vals[0].val, 1, "");
1664+
zassert_equal(vals[1].val, 2, "");
1665+
zassert_equal(vals[2].val, 3, "");
1666+
zassert_equal(vals_inst[0].val, 1, "");
1667+
zassert_equal(vals_inst[1].val, 2, "");
1668+
zassert_equal(vals_inst[2].val, 3, "");
1669+
zassert_equal(vals_status_okay[0].val, 1, "");
1670+
zassert_equal(vals_status_okay[1].val, 2, "");
1671+
1672+
#undef TEST_PARENT
1673+
#undef TEST_FUNC
1674+
}
1675+
15921676
static void test_great_grandchild(void)
15931677
{
15941678
zassert_equal(DT_PROP(DT_NODELABEL(test_ggc), ggc_prop), 42, "");
@@ -1816,6 +1900,7 @@ void test_main(void)
18161900
ztest_unit_test(test_macro_names),
18171901
ztest_unit_test(test_arrays),
18181902
ztest_unit_test(test_foreach_prop_elem),
1903+
ztest_unit_test(test_foreach_prop_elem_varg),
18191904
ztest_unit_test(test_devices),
18201905
ztest_unit_test(test_cs_gpios),
18211906
ztest_unit_test(test_chosen),
@@ -1824,6 +1909,7 @@ void test_main(void)
18241909
ztest_unit_test(test_clocks),
18251910
ztest_unit_test(test_parent),
18261911
ztest_unit_test(test_child_nodes_list),
1912+
ztest_unit_test(test_child_nodes_list_varg),
18271913
ztest_unit_test(test_great_grandchild),
18281914
ztest_unit_test(test_compat_get_any_status_okay),
18291915
ztest_unit_test(test_dep_ord),

0 commit comments

Comments
 (0)