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 <arvinf@ip-logix.com>
This commit is contained in:
Arvin Farahmand 2021-05-06 11:19:29 -04:00 committed by Christopher Friedt
commit d0b9c03154
4 changed files with 210 additions and 0 deletions

View file

@ -51,9 +51,11 @@ node-macro =/ %s"DT_N" path-id %s"_PARENT"
; These are used internally by DT_FOREACH_CHILD, which iterates over ; These are used internally by DT_FOREACH_CHILD, which iterates over
; each child node. ; each child node.
node-macro =/ %s"DT_N" path-id %s"_FOREACH_CHILD" node-macro =/ %s"DT_N" path-id %s"_FOREACH_CHILD"
node-macro =/ %s"DT_N" path-id %s"_FOREACH_CHILD_VARGS"
; These are used internally by DT_FOREACH_CHILD_STATUS_OKAY, which iterates ; These are used internally by DT_FOREACH_CHILD_STATUS_OKAY, which iterates
; over each child node with status "okay". ; over each child node with status "okay".
node-macro =/ %s"DT_N" path-id %s"_FOREACH_CHILD_STATUS_OKAY" node-macro =/ %s"DT_N" path-id %s"_FOREACH_CHILD_STATUS_OKAY"
node-macro =/ %s"DT_N" path-id %s"_FOREACH_CHILD_STATUS_OKAY_VARGS"
; The node's status macro; dt-name in this case is something like "okay" ; The node's status macro; dt-name in this case is something like "okay"
; or "disabled". ; or "disabled".
node-macro =/ %s"DT_N" path-id %s"_STATUS_" dt-name 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"
; These are used internally by DT_INST_FOREACH, which iterates over ; These are used internally by DT_INST_FOREACH, which iterates over
; each enabled instance of a compatible. ; each enabled instance of a compatible.
other-macro =/ %s"DT_FOREACH_OKAY_INST_" dt-name other-macro =/ %s"DT_FOREACH_OKAY_INST_" dt-name
other-macro =/ %s"DT_FOREACH_OKAY_INST_VARGS_" dt-name
; E.g.: #define DT_CHOSEN_zephyr_flash ; E.g.: #define DT_CHOSEN_zephyr_flash
other-macro =/ %s"DT_CHOSEN_" dt-name other-macro =/ %s"DT_CHOSEN_" dt-name
; Declares that a compatible has at least one node on a bus. ; Declares that a compatible has at least one node on a bus.

View file

@ -45,6 +45,7 @@
* _ENUM_UPPER_TOKEN: like _ENUM_TOKEN, but uppercased * _ENUM_UPPER_TOKEN: like _ENUM_TOKEN, but uppercased
* _EXISTS: property is defined * _EXISTS: property is defined
* _FOREACH_PROP_ELEM: helper for "iterating" over values in the property * _FOREACH_PROP_ELEM: helper for "iterating" over values in the property
* _FOREACH_PROP_ELEM_VARGS: foreach functions with variable number of arguments
* _IDX_<i>: logical index into property * _IDX_<i>: logical index into property
* _IDX_<i>_EXISTS: logical index into property is defined * _IDX_<i>_EXISTS: logical index into property is defined
* _IDX_<i>_PH: phandle array's phandle by index (or phandle, phandles) * _IDX_<i>_PH: phandle array's phandle by index (or phandle, phandles)
@ -1485,6 +1486,21 @@
#define DT_FOREACH_CHILD(node_id, fn) \ #define DT_FOREACH_CHILD(node_id, fn) \
DT_CAT(node_id, _FOREACH_CHILD)(fn) DT_CAT(node_id, _FOREACH_CHILD)(fn)
/**
* @brief Invokes "fn" for each child of "node_id" with multiple arguments
*
* The macro "fn" takes multiple arguments. The first should be the node
* identifier for the child node. The remaining are passed-in by the caller.
*
* @param node_id node identifier
* @param fn macro to invoke
* @param ... variable number of arguments to pass to fn
*
* @see DT_FOREACH_CHILD
*/
#define DT_FOREACH_CHILD_VARGS(node_id, fn, ...) \
DT_CAT(node_id, _FOREACH_CHILD_VARGS)(fn, __VA_ARGS__)
/** /**
* @brief Call "fn" on the child nodes with status "okay" * @brief Call "fn" on the child nodes with status "okay"
* *
@ -1500,6 +1516,25 @@
#define DT_FOREACH_CHILD_STATUS_OKAY(node_id, fn) \ #define DT_FOREACH_CHILD_STATUS_OKAY(node_id, fn) \
DT_CAT(node_id, _FOREACH_CHILD_STATUS_OKAY)(fn) DT_CAT(node_id, _FOREACH_CHILD_STATUS_OKAY)(fn)
/**
* @brief Call "fn" on the child nodes with status "okay" with multiple
* arguments
*
* The macro "fn" takes multiple arguments. The first should be the node
* identifier for the child node. The remaining are passed-in by the caller.
*
* As usual, both a missing status and an "ok" status are
* treated as "okay".
*
* @param node_id node identifier
* @param fn macro to invoke
* @param ... variable number of arguments to pass to fn
*
* @see DT_FOREACH_CHILD_STATUS_OKAY
*/
#define DT_FOREACH_CHILD_STATUS_OKAY_VARGS(node_id, fn, ...) \
DT_CAT(node_id, _FOREACH_CHILD_STATUS_OKAY_VARGS)(fn, __VA_ARGS__)
/** /**
* @brief Invokes "fn" for each element in the value of property "prop". * @brief Invokes "fn" for each element in the value of property "prop".
* *
@ -1547,6 +1582,26 @@
#define DT_FOREACH_PROP_ELEM(node_id, prop, fn) \ #define DT_FOREACH_PROP_ELEM(node_id, prop, fn) \
DT_CAT4(node_id, _P_, prop, _FOREACH_PROP_ELEM)(fn) DT_CAT4(node_id, _P_, prop, _FOREACH_PROP_ELEM)(fn)
/**
* @brief Invokes "fn" for each element in the value of property "prop" with
* multiple arguments.
*
* The macro "fn" must take multiple parameters: fn(node_id, prop, idx, ...).
* "node_id" and "prop" are the same as what is passed to
* DT_FOREACH_PROP_ELEM, and "idx" is the current index into the array.
* The "idx" values are integer literals starting from 0. The remaining
* arguments are passed-in by the caller.
*
* @param node_id node identifier
* @param prop lowercase-and-underscores property name
* @param fn macro to invoke
* @param ... variable number of arguments to pass to fn
*
* @see DT_FOREACH_PROP_ELEM
*/
#define DT_FOREACH_PROP_ELEM_VARGS(node_id, prop, fn, ...) \
DT_CAT4(node_id, _P_, prop, _FOREACH_PROP_ELEM_VARGS)(fn, __VA_ARGS__)
/** /**
* @} * @}
*/ */
@ -1820,6 +1875,21 @@
#define DT_INST_FOREACH_CHILD(inst, fn) \ #define DT_INST_FOREACH_CHILD(inst, fn) \
DT_FOREACH_CHILD(DT_DRV_INST(inst), fn) DT_FOREACH_CHILD(DT_DRV_INST(inst), fn)
/**
* @brief Call "fn" on all child nodes of DT_DRV_INST(inst).
*
* The macro "fn" takes multiple arguments. The first should be the node
* identifier for the child node. The remaining are passed-in by the caller.
*
* @param inst instance number
* @param fn macro to invoke on each child node identifier
* @param ... variable number of arguments to pass to fn
*
* @see DT_FOREACH_CHILD
*/
#define DT_INST_FOREACH_CHILD_VARGS(inst, fn, ...) \
DT_FOREACH_CHILD_VARGS(DT_DRV_INST(inst), fn, __VA_ARGS__)
/** /**
* @brief Get a DT_DRV_COMPAT instance property * @brief Get a DT_DRV_COMPAT instance property
* @param inst instance number * @param inst instance number
@ -2213,6 +2283,23 @@
DT_DRV_COMPAT)(fn)), \ DT_DRV_COMPAT)(fn)), \
()) ())
/**
* @brief Call "fn" on all nodes with compatible DT_DRV_COMPAT
* and status "okay" with multiple arguments
*
*
* @param fn Macro to call for each enabled node. Must accept an
* instance number as its only parameter.
* @param ... variable number of arguments to pass to fn
*
* @see DT_INST_FOREACH_STATUS_OKAY
*/
#define DT_INST_FOREACH_STATUS_OKAY_VARGS(fn, ...) \
COND_CODE_1(DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT), \
(UTIL_CAT(DT_FOREACH_OKAY_INST_VARGS_, \
DT_DRV_COMPAT)(fn, __VA_ARGS__)), \
())
/** /**
* @brief Invokes "fn" for each element of property "prop" for * @brief Invokes "fn" for each element of property "prop" for
* a DT_DRV_COMPAT instance. * a DT_DRV_COMPAT instance.
@ -2226,6 +2313,23 @@
#define DT_INST_FOREACH_PROP_ELEM(inst, prop, fn) \ #define DT_INST_FOREACH_PROP_ELEM(inst, prop, fn) \
DT_FOREACH_PROP_ELEM(DT_DRV_INST(inst), prop, fn) DT_FOREACH_PROP_ELEM(DT_DRV_INST(inst), prop, fn)
/**
* @brief Invokes "fn" for each element of property "prop" for
* a DT_DRV_COMPAT instance with multiple arguments.
*
* Equivalent to
* DT_FOREACH_PROP_ELEM_VARGS(DT_DRV_INST(inst), prop, fn, __VA_ARGS__)
*
* @param inst instance number
* @param prop lowercase-and-underscores property name
* @param fn macro to invoke
* @param ... variable number of arguments to pass to fn
*
* @see DT_INST_FOREACH_PROP_ELEM
*/
#define DT_INST_FOREACH_PROP_ELEM_VARGS(inst, prop, fn, ...) \
DT_FOREACH_PROP_ELEM_VARGS(DT_DRV_INST(inst), prop, fn, __VA_ARGS__)
/** /**
* @brief Does a DT_DRV_COMPAT instance have a property? * @brief Does a DT_DRV_COMPAT instance have a property?
* @param inst instance number * @param inst instance number

View file

@ -479,17 +479,25 @@ def write_child_functions(node):
" ".join(f"fn(DT_{child.z_path_id})" for child in " ".join(f"fn(DT_{child.z_path_id})" for child in
node.children.values())) node.children.values()))
out_dt_define(f"{node.z_path_id}_FOREACH_CHILD_VARGS(fn, ...)",
" ".join(f"fn(DT_{child.z_path_id}, __VA_ARGS__)" for child in
node.children.values()))
def write_child_functions_status_okay(node): def write_child_functions_status_okay(node):
# Writes macro that are helpers that will call a macro/function # Writes macro that are helpers that will call a macro/function
# for each child node with status "okay". # for each child node with status "okay".
functions = '' functions = ''
functions_args = ''
for child in node.children.values(): for child in node.children.values():
if child.status == "okay": if child.status == "okay":
functions = functions + f"fn(DT_{child.z_path_id}) " functions = functions + f"fn(DT_{child.z_path_id}) "
functions_args = functions_args + f"fn(DT_{child.z_path_id}, " \
"__VA_ARGS__) "
out_dt_define(f"{node.z_path_id}_FOREACH_CHILD_STATUS_OKAY(fn)", functions) out_dt_define(f"{node.z_path_id}_FOREACH_CHILD_STATUS_OKAY(fn)", functions)
out_dt_define(f"{node.z_path_id}_FOREACH_CHILD_STATUS_OKAY_VARGS(fn, ...)",
functions_args)
def write_status(node): def write_status(node):
@ -549,6 +557,11 @@ def write_vanilla_props(node):
' \\\n\t'.join(f'fn(DT_{node.z_path_id}, {prop_id}, {i})' ' \\\n\t'.join(f'fn(DT_{node.z_path_id}, {prop_id}, {i})'
for i in range(len(prop.val))) for i in range(len(prop.val)))
macro2val[f"{macro}_FOREACH_PROP_ELEM_VARGS(fn, ...)"] = \
' \\\n\t'.join(f'fn(DT_{node.z_path_id}, {prop_id}, {i},'
' __VA_ARGS__)'
for i in range(len(prop.val)))
plen = prop_len(prop) plen = prop_len(prop)
if plen is not None: if plen is not None:
# DT_N_<node-id>_P_<prop-id>_LEN # DT_N_<node-id>_P_<prop-id>_LEN
@ -749,6 +762,10 @@ def write_global_compat_info(edt):
" ".join(f"fn({edt.compat2nodes[compat].index(node)})" " ".join(f"fn({edt.compat2nodes[compat].index(node)})"
for node in okay_nodes) for node in okay_nodes)
for_each_macros[f"DT_FOREACH_OKAY_INST_VARGS_{ident}(fn, ...)"] = \
" ".join(f"fn({edt.compat2nodes[compat].index(node)}, __VA_ARGS__)"
for node in okay_nodes)
for compat, nodes in edt.compat2nodes.items(): for compat, nodes in edt.compat2nodes.items():
for node in nodes: for node in nodes:
if compat == "fixed-partitions": if compat == "fixed-partitions":

View file

@ -1280,6 +1280,34 @@ static void test_foreach_prop_elem(void)
#undef TIMES_TWO #undef TIMES_TWO
} }
static void test_foreach_prop_elem_varg(void)
{
#define TIMES_TWO_ADD(node_id, prop, idx, arg) \
((2 * DT_PROP_BY_IDX(node_id, prop, idx)) + arg),
int array[] = {
DT_FOREACH_PROP_ELEM_VARGS(TEST_ARRAYS, a, TIMES_TWO_ADD, 3)
};
zassert_equal(ARRAY_SIZE(array), 3, "");
zassert_equal(array[0], 2003, "");
zassert_equal(array[1], 4003, "");
zassert_equal(array[2], 6003, "");
#undef DT_DRV_COMPAT
#define DT_DRV_COMPAT vnd_array_holder
int inst_array[] = {
DT_INST_FOREACH_PROP_ELEM_VARGS(0, a, TIMES_TWO_ADD, 3)
};
zassert_equal(ARRAY_SIZE(inst_array), ARRAY_SIZE(array), "");
zassert_equal(inst_array[0], array[0], "");
zassert_equal(inst_array[1], array[1], "");
zassert_equal(inst_array[2], array[2], "");
#undef TIMES_TWO
}
struct test_gpio_info { struct test_gpio_info {
uint32_t reg_addr; uint32_t reg_addr;
uint32_t reg_len; uint32_t reg_len;
@ -1378,6 +1406,15 @@ static void test_devices(void)
#define INC(inst_ignored) do { val++; } while (0); #define INC(inst_ignored) do { val++; } while (0);
DT_INST_FOREACH_STATUS_OKAY(INC) DT_INST_FOREACH_STATUS_OKAY(INC)
zassert_equal(val, 2, ""); zassert_equal(val, 2, "");
#undef INC
val = 0;
#define INC_ARG(arg) do { val++; val += arg; } while (0)
#define INC(inst_ignored, arg) INC_ARG(arg);
DT_INST_FOREACH_STATUS_OKAY_VARGS(INC, 1)
zassert_equal(val, 4, "");
#undef INC_ARG
#undef INC
/* /*
* Make sure DT_INST_FOREACH_STATUS_OKAY works with 0 instances, and does * Make sure DT_INST_FOREACH_STATUS_OKAY works with 0 instances, and does
@ -1387,6 +1424,13 @@ static void test_devices(void)
#define DT_DRV_COMPAT xxxx #define DT_DRV_COMPAT xxxx
#define BUILD_BUG_ON_EXPANSION (there is a bug in devicetree.h) #define BUILD_BUG_ON_EXPANSION (there is a bug in devicetree.h)
DT_INST_FOREACH_STATUS_OKAY(BUILD_BUG_ON_EXPANSION) DT_INST_FOREACH_STATUS_OKAY(BUILD_BUG_ON_EXPANSION)
#undef BUILD_BUG_ON_EXPANSION
#undef DT_DRV_COMPAT
#define DT_DRV_COMPAT xxxx
#define BUILD_BUG_ON_EXPANSION(arg) (there is a bug in devicetree.h)
DT_INST_FOREACH_STATUS_OKAY_VARGS(BUILD_BUG_ON_EXPANSION, 1)
#undef BUILD_BUG_ON_EXPANSION
} }
static void test_cs_gpios(void) static void test_cs_gpios(void)
@ -1589,6 +1633,46 @@ static void test_child_nodes_list(void)
#undef TEST_FUNC #undef TEST_FUNC
} }
#undef DT_DRV_COMPAT
#define DT_DRV_COMPAT vnd_child_bindings
static void test_child_nodes_list_varg(void)
{
#define TEST_FUNC(child, arg) { DT_PROP(child, val) + arg },
#define TEST_PARENT DT_PARENT(DT_NODELABEL(test_child_a))
struct vnd_child_binding {
int val;
};
struct vnd_child_binding vals[] = {
DT_FOREACH_CHILD_VARGS(TEST_PARENT, TEST_FUNC, 1)
};
struct vnd_child_binding vals_inst[] = {
DT_INST_FOREACH_CHILD_VARGS(0, TEST_FUNC, 1)
};
struct vnd_child_binding vals_status_okay[] = {
DT_FOREACH_CHILD_STATUS_OKAY_VARGS(TEST_PARENT, TEST_FUNC, 1)
};
zassert_equal(ARRAY_SIZE(vals), 3, "");
zassert_equal(ARRAY_SIZE(vals_inst), 3, "");
zassert_equal(ARRAY_SIZE(vals_status_okay), 2, "");
zassert_equal(vals[0].val, 1, "");
zassert_equal(vals[1].val, 2, "");
zassert_equal(vals[2].val, 3, "");
zassert_equal(vals_inst[0].val, 1, "");
zassert_equal(vals_inst[1].val, 2, "");
zassert_equal(vals_inst[2].val, 3, "");
zassert_equal(vals_status_okay[0].val, 1, "");
zassert_equal(vals_status_okay[1].val, 2, "");
#undef TEST_PARENT
#undef TEST_FUNC
}
static void test_great_grandchild(void) static void test_great_grandchild(void)
{ {
zassert_equal(DT_PROP(DT_NODELABEL(test_ggc), ggc_prop), 42, ""); zassert_equal(DT_PROP(DT_NODELABEL(test_ggc), ggc_prop), 42, "");
@ -1816,6 +1900,7 @@ void test_main(void)
ztest_unit_test(test_macro_names), ztest_unit_test(test_macro_names),
ztest_unit_test(test_arrays), ztest_unit_test(test_arrays),
ztest_unit_test(test_foreach_prop_elem), ztest_unit_test(test_foreach_prop_elem),
ztest_unit_test(test_foreach_prop_elem_varg),
ztest_unit_test(test_devices), ztest_unit_test(test_devices),
ztest_unit_test(test_cs_gpios), ztest_unit_test(test_cs_gpios),
ztest_unit_test(test_chosen), ztest_unit_test(test_chosen),
@ -1824,6 +1909,7 @@ void test_main(void)
ztest_unit_test(test_clocks), ztest_unit_test(test_clocks),
ztest_unit_test(test_parent), ztest_unit_test(test_parent),
ztest_unit_test(test_child_nodes_list), ztest_unit_test(test_child_nodes_list),
ztest_unit_test(test_child_nodes_list_varg),
ztest_unit_test(test_great_grandchild), ztest_unit_test(test_great_grandchild),
ztest_unit_test(test_compat_get_any_status_okay), ztest_unit_test(test_compat_get_any_status_okay),
ztest_unit_test(test_dep_ord), ztest_unit_test(test_dep_ord),