devicetree: add DT_(INST_)FOREACH_CHILD(_STATUS_OKAY)_SEP(_VARGS)
It is frequent to see in Devicetree code constructs like:
```c
#define NAME_AND_COMMA(node_id) DT_NODE_FULL_NAME(node_id),
const char *child_names[] = {
DT_FOREACH_CHILD(DT_NODELABEL(n), NAME_AND_COMMA)
};
```
That is, an auxiliary macro to append a separator character in
DT_FOREACH* macros. Non-DT API, e.g. FOR_EACH(), takes a separator
argument to avoid such intermediate macros.
This patch adds DT_FOREACH_CHILD_SEP (and instance/status okay/vargs
versions of it). They all take an extra argument: a separator. With this
change, the example above can be simplified to:
```c
const char *child_labels[] = {
DT_FOREACH_CHILD(DT_NODELABEL(n), DT_NODE_FULL_NAME, (,))
};
```
Notes:
- Other DT_FOREACH* macros could/should be extended as well
Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
This commit is contained in:
parent
d77f4e61de
commit
fff9ecbc7f
4
doc/build/dts/macros.bnf
vendored
4
doc/build/dts/macros.bnf
vendored
@ -61,11 +61,15 @@ node-macro =/ %s"DT_N" path-id %s"_PARENT"
|
||||
; These are used internally by DT_FOREACH_CHILD, which iterates over
|
||||
; each child node.
|
||||
node-macro =/ %s"DT_N" path-id %s"_FOREACH_CHILD"
|
||||
node-macro =/ %s"DT_N" path-id %s"_FOREACH_CHILD_SEP"
|
||||
node-macro =/ %s"DT_N" path-id %s"_FOREACH_CHILD_VARGS"
|
||||
node-macro =/ %s"DT_N" path-id %s"_FOREACH_CHILD_SEP_VARGS"
|
||||
; These are used internally by DT_FOREACH_CHILD_STATUS_OKAY, which iterates
|
||||
; 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_SEP"
|
||||
node-macro =/ %s"DT_N" path-id %s"_FOREACH_CHILD_STATUS_OKAY_VARGS"
|
||||
node-macro =/ %s"DT_N" path-id %s"_FOREACH_CHILD_STATUS_OKAY_SEP_VARGS"
|
||||
; The node's zero-based index in the list of it's parent's child nodes.
|
||||
node-macro =/ %s"DT_N" path-id %s"_CHILD_IDX"
|
||||
; The node's status macro; dt-name in this case is something like "okay"
|
||||
|
||||
@ -2044,6 +2044,43 @@
|
||||
#define DT_FOREACH_CHILD(node_id, fn) \
|
||||
DT_CAT(node_id, _FOREACH_CHILD)(fn)
|
||||
|
||||
/**
|
||||
* @brief Invokes "fn" for each child of "node_id" with a separator
|
||||
*
|
||||
* The macro "fn" must take one parameter, which will be the node
|
||||
* identifier of a child node of "node_id".
|
||||
*
|
||||
* Example devicetree fragment:
|
||||
*
|
||||
* n: node {
|
||||
* child-1 {
|
||||
* ...
|
||||
* };
|
||||
* child-2 {
|
||||
* ...
|
||||
* };
|
||||
* };
|
||||
*
|
||||
* Example usage:
|
||||
*
|
||||
* const char *child_names[] = {
|
||||
* DT_FOREACH_CHILD_SEP(DT_NODELABEL(n), DT_NODE_FULL_NAME, (,))
|
||||
* };
|
||||
*
|
||||
* This expands to:
|
||||
*
|
||||
* const char *child_names[] = {
|
||||
* "child-1", "child-2"
|
||||
* };
|
||||
*
|
||||
* @param node_id node identifier
|
||||
* @param fn macro to invoke
|
||||
* @param sep Separator (e.g. comma or semicolon). Must be in parentheses;
|
||||
* this is required to enable providing a comma as separator.
|
||||
*/
|
||||
#define DT_FOREACH_CHILD_SEP(node_id, fn, sep) \
|
||||
DT_CAT(node_id, _FOREACH_CHILD_SEP)(fn, sep)
|
||||
|
||||
/**
|
||||
* @brief Invokes "fn" for each child of "node_id" with multiple arguments
|
||||
*
|
||||
@ -2062,6 +2099,24 @@
|
||||
#define DT_FOREACH_CHILD_VARGS(node_id, fn, ...) \
|
||||
DT_CAT(node_id, _FOREACH_CHILD_VARGS)(fn, __VA_ARGS__)
|
||||
|
||||
/**
|
||||
* @brief Invokes "fn" for each child of "node_id" with separator and 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 sep Separator (e.g. comma or semicolon). Must be in parentheses;
|
||||
* this is required to enable providing a comma as separator.
|
||||
* @param ... variable number of arguments to pass to fn
|
||||
*
|
||||
* @see DT_FOREACH_CHILD_VARGS
|
||||
*/
|
||||
#define DT_FOREACH_CHILD_SEP_VARGS(node_id, fn, sep, ...) \
|
||||
DT_CAT(node_id, _FOREACH_CHILD_SEP_VARGS)(fn, sep, __VA_ARGS__)
|
||||
|
||||
/**
|
||||
* @brief Call "fn" on the child nodes with status "okay"
|
||||
*
|
||||
@ -2080,6 +2135,25 @@
|
||||
#define DT_FOREACH_CHILD_STATUS_OKAY(node_id, fn) \
|
||||
DT_CAT(node_id, _FOREACH_CHILD_STATUS_OKAY)(fn)
|
||||
|
||||
/**
|
||||
* @brief Call "fn" on the child nodes with status "okay" with separator
|
||||
*
|
||||
* The macro "fn" should take one argument, which is the node
|
||||
* identifier for the child node.
|
||||
*
|
||||
* 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 sep Separator (e.g. comma or semicolon). Must be in parentheses;
|
||||
* this is required to enable providing a comma as separator.
|
||||
*
|
||||
* @see DT_FOREACH_CHILD_STATUS_OKAY
|
||||
*/
|
||||
#define DT_FOREACH_CHILD_STATUS_OKAY_SEP(node_id, fn, sep) \
|
||||
DT_CAT(node_id, _FOREACH_CHILD_STATUS_OKAY_SEP)(fn, sep)
|
||||
|
||||
/**
|
||||
* @brief Call "fn" on the child nodes with status "okay" with multiple
|
||||
* arguments
|
||||
@ -2102,6 +2176,27 @@
|
||||
#define DT_FOREACH_CHILD_STATUS_OKAY_VARGS(node_id, fn, ...) \
|
||||
DT_CAT(node_id, _FOREACH_CHILD_STATUS_OKAY_VARGS)(fn, __VA_ARGS__)
|
||||
|
||||
/**
|
||||
* @brief Call "fn" on the child nodes with status "okay" with separator and
|
||||
* 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 sep Separator (e.g. comma or semicolon). Must be in parentheses;
|
||||
* this is required to enable providing a comma as separator.
|
||||
* @param ... variable number of arguments to pass to fn
|
||||
*
|
||||
* @see DT_FOREACH_CHILD_SEP_STATUS_OKAY
|
||||
*/
|
||||
#define DT_FOREACH_CHILD_STATUS_OKAY_SEP_VARGS(node_id, fn, sep, ...) \
|
||||
DT_CAT(node_id, _FOREACH_CHILD_STATUS_OKAY_SEP_VARGS)(fn, sep, __VA_ARGS__)
|
||||
|
||||
/**
|
||||
* @brief Invokes "fn" for each element in the value of property "prop".
|
||||
*
|
||||
@ -2545,6 +2640,22 @@
|
||||
#define DT_INST_FOREACH_CHILD(inst, fn) \
|
||||
DT_FOREACH_CHILD(DT_DRV_INST(inst), fn)
|
||||
|
||||
/**
|
||||
* @brief Call "fn" on all child nodes of DT_DRV_INST(inst) with a separator
|
||||
*
|
||||
* The macro "fn" should take one argument, which is the node
|
||||
* identifier for the child node.
|
||||
*
|
||||
* @param inst instance number
|
||||
* @param fn macro to invoke on each child node identifier
|
||||
* @param sep Separator (e.g. comma or semicolon). Must be in parentheses;
|
||||
* this is required to enable providing a comma as separator.
|
||||
*
|
||||
* @see DT_FOREACH_CHILD_SEP
|
||||
*/
|
||||
#define DT_INST_FOREACH_CHILD_SEP(inst, fn, sep) \
|
||||
DT_FOREACH_CHILD_SEP(DT_DRV_INST(inst), fn, sep)
|
||||
|
||||
/**
|
||||
* @brief Call "fn" on all child nodes of DT_DRV_INST(inst).
|
||||
*
|
||||
@ -2563,6 +2674,23 @@
|
||||
#define DT_INST_FOREACH_CHILD_VARGS(inst, fn, ...) \
|
||||
DT_FOREACH_CHILD_VARGS(DT_DRV_INST(inst), fn, __VA_ARGS__)
|
||||
|
||||
/**
|
||||
* @brief Call "fn" on all child nodes of DT_DRV_INST(inst) with separator.
|
||||
*
|
||||
* 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 sep Separator (e.g. comma or semicolon). Must be in parentheses;
|
||||
* this is required to enable providing a comma as separator.
|
||||
* @param ... variable number of arguments to pass to fn
|
||||
*
|
||||
* @see DT_FOREACH_CHILD_SEP_VARGS
|
||||
*/
|
||||
#define DT_INST_FOREACH_CHILD_SEP_VARGS(inst, fn, sep, ...) \
|
||||
DT_FOREACH_CHILD_SEP_VARGS(DT_DRV_INST(inst), fn, sep, __VA_ARGS__)
|
||||
|
||||
/**
|
||||
* @brief Get a DT_DRV_COMPAT value's index into its enumeration values
|
||||
* @param inst instance number
|
||||
|
||||
@ -521,21 +521,33 @@ def write_children(node):
|
||||
" ".join(f"fn(DT_{child.z_path_id})" for child in
|
||||
node.children.values()))
|
||||
|
||||
out_dt_define(f"{node.z_path_id}_FOREACH_CHILD_SEP(fn, sep)",
|
||||
" DT_DEBRACKET_INTERNAL sep ".join(f"fn(DT_{child.z_path_id})"
|
||||
for child in 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()))
|
||||
" ".join(f"fn(DT_{child.z_path_id}, __VA_ARGS__)"
|
||||
for child in node.children.values()))
|
||||
|
||||
functions = ''
|
||||
functions_args = ''
|
||||
for child in node.children.values():
|
||||
if child.status == "okay":
|
||||
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_SEP_VARGS(fn, sep, ...)",
|
||||
" DT_DEBRACKET_INTERNAL sep ".join(f"fn(DT_{child.z_path_id}, __VA_ARGS__)"
|
||||
for child in node.children.values()))
|
||||
|
||||
out_dt_define(f"{node.z_path_id}_FOREACH_CHILD_STATUS_OKAY(fn)",
|
||||
" ".join(f"fn(DT_{child.z_path_id})"
|
||||
for child in node.children.values() if child.status == "okay"))
|
||||
|
||||
out_dt_define(f"{node.z_path_id}_FOREACH_CHILD_STATUS_OKAY_SEP(fn, sep)",
|
||||
" DT_DEBRACKET_INTERNAL sep ".join(f"fn(DT_{child.z_path_id})"
|
||||
for child in node.children.values() if child.status == "okay"))
|
||||
|
||||
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)
|
||||
" ".join(f"fn(DT_{child.z_path_id}, __VA_ARGS__)"
|
||||
for child in node.children.values() if child.status == "okay"))
|
||||
|
||||
out_dt_define(f"{node.z_path_id}_FOREACH_CHILD_STATUS_OKAY_SEP_VARGS(fn, sep, ...)",
|
||||
" DT_DEBRACKET_INTERNAL sep ".join(f"fn(DT_{child.z_path_id}, __VA_ARGS__)"
|
||||
for child in node.children.values() if child.status == "okay"))
|
||||
|
||||
|
||||
def write_status(node):
|
||||
|
||||
@ -1630,7 +1630,8 @@ ZTEST(devicetree_api, test_parent)
|
||||
#define DT_DRV_COMPAT vnd_child_bindings
|
||||
ZTEST(devicetree_api, test_child_nodes_list)
|
||||
{
|
||||
#define TEST_FUNC(child) { DT_PROP(child, val) },
|
||||
#define TEST_FUNC(child) { DT_PROP(child, val) }
|
||||
#define TEST_FUNC_AND_COMMA(child) TEST_FUNC(child),
|
||||
#define TEST_PARENT DT_PARENT(DT_NODELABEL(test_child_a))
|
||||
|
||||
struct vnd_child_binding {
|
||||
@ -1638,31 +1639,55 @@ ZTEST(devicetree_api, test_child_nodes_list)
|
||||
};
|
||||
|
||||
struct vnd_child_binding vals[] = {
|
||||
DT_FOREACH_CHILD(TEST_PARENT, TEST_FUNC)
|
||||
DT_FOREACH_CHILD(TEST_PARENT, TEST_FUNC_AND_COMMA)
|
||||
};
|
||||
|
||||
struct vnd_child_binding vals_sep[] = {
|
||||
DT_FOREACH_CHILD_SEP(TEST_PARENT, TEST_FUNC, (,))
|
||||
};
|
||||
|
||||
struct vnd_child_binding vals_inst[] = {
|
||||
DT_INST_FOREACH_CHILD(0, TEST_FUNC)
|
||||
DT_INST_FOREACH_CHILD(0, TEST_FUNC_AND_COMMA)
|
||||
};
|
||||
|
||||
struct vnd_child_binding vals_inst_sep[] = {
|
||||
DT_INST_FOREACH_CHILD_SEP(0, TEST_FUNC, (,))
|
||||
};
|
||||
|
||||
struct vnd_child_binding vals_status_okay[] = {
|
||||
DT_FOREACH_CHILD_STATUS_OKAY(TEST_PARENT, TEST_FUNC)
|
||||
DT_FOREACH_CHILD_STATUS_OKAY(TEST_PARENT, TEST_FUNC_AND_COMMA)
|
||||
};
|
||||
|
||||
struct vnd_child_binding vals_status_okay_sep[] = {
|
||||
DT_FOREACH_CHILD_STATUS_OKAY_SEP(TEST_PARENT, TEST_FUNC, (,))
|
||||
};
|
||||
|
||||
zassert_equal(ARRAY_SIZE(vals), 3, "");
|
||||
zassert_equal(ARRAY_SIZE(vals_sep), 3, "");
|
||||
zassert_equal(ARRAY_SIZE(vals_inst), 3, "");
|
||||
zassert_equal(ARRAY_SIZE(vals_inst_sep), 3, "");
|
||||
zassert_equal(ARRAY_SIZE(vals_status_okay), 2, "");
|
||||
zassert_equal(ARRAY_SIZE(vals_status_okay_sep), 2, "");
|
||||
|
||||
zassert_equal(vals[0].val, 0, "");
|
||||
zassert_equal(vals[1].val, 1, "");
|
||||
zassert_equal(vals[2].val, 2, "");
|
||||
zassert_equal(vals_sep[0].val, 0, "");
|
||||
zassert_equal(vals_sep[1].val, 1, "");
|
||||
zassert_equal(vals_sep[2].val, 2, "");
|
||||
zassert_equal(vals_inst[0].val, 0, "");
|
||||
zassert_equal(vals_inst[1].val, 1, "");
|
||||
zassert_equal(vals_inst[2].val, 2, "");
|
||||
zassert_equal(vals_inst_sep[0].val, 0, "");
|
||||
zassert_equal(vals_inst_sep[1].val, 1, "");
|
||||
zassert_equal(vals_inst_sep[2].val, 2, "");
|
||||
zassert_equal(vals_status_okay[0].val, 0, "");
|
||||
zassert_equal(vals_status_okay[1].val, 1, "");
|
||||
zassert_equal(vals_status_okay_sep[0].val, 0, "");
|
||||
zassert_equal(vals_status_okay_sep[1].val, 1, "");
|
||||
|
||||
#undef TEST_PARENT
|
||||
#undef TEST_FUNC_AND_COMMA
|
||||
#undef TEST_FUNC
|
||||
}
|
||||
|
||||
@ -1670,7 +1695,8 @@ ZTEST(devicetree_api, test_child_nodes_list)
|
||||
#define DT_DRV_COMPAT vnd_child_bindings
|
||||
ZTEST(devicetree_api, test_child_nodes_list_varg)
|
||||
{
|
||||
#define TEST_FUNC(child, arg) { DT_PROP(child, val) + arg },
|
||||
#define TEST_FUNC(child, arg) { DT_PROP(child, val) + arg }
|
||||
#define TEST_FUNC_AND_COMMA(child, arg) TEST_FUNC(child, arg),
|
||||
#define TEST_PARENT DT_PARENT(DT_NODELABEL(test_child_a))
|
||||
|
||||
struct vnd_child_binding {
|
||||
@ -1678,31 +1704,55 @@ ZTEST(devicetree_api, test_child_nodes_list_varg)
|
||||
};
|
||||
|
||||
struct vnd_child_binding vals[] = {
|
||||
DT_FOREACH_CHILD_VARGS(TEST_PARENT, TEST_FUNC, 1)
|
||||
DT_FOREACH_CHILD_VARGS(TEST_PARENT, TEST_FUNC_AND_COMMA, 1)
|
||||
};
|
||||
|
||||
struct vnd_child_binding vals_sep[] = {
|
||||
DT_FOREACH_CHILD_SEP_VARGS(TEST_PARENT, TEST_FUNC, (,), 1)
|
||||
};
|
||||
|
||||
struct vnd_child_binding vals_inst[] = {
|
||||
DT_INST_FOREACH_CHILD_VARGS(0, TEST_FUNC, 1)
|
||||
DT_INST_FOREACH_CHILD_VARGS(0, TEST_FUNC_AND_COMMA, 1)
|
||||
};
|
||||
|
||||
struct vnd_child_binding vals_inst_sep[] = {
|
||||
DT_INST_FOREACH_CHILD_SEP_VARGS(0, TEST_FUNC, (,), 1)
|
||||
};
|
||||
|
||||
struct vnd_child_binding vals_status_okay[] = {
|
||||
DT_FOREACH_CHILD_STATUS_OKAY_VARGS(TEST_PARENT, TEST_FUNC, 1)
|
||||
DT_FOREACH_CHILD_STATUS_OKAY_VARGS(TEST_PARENT, TEST_FUNC_AND_COMMA, 1)
|
||||
};
|
||||
|
||||
struct vnd_child_binding vals_status_okay_sep[] = {
|
||||
DT_FOREACH_CHILD_STATUS_OKAY_SEP_VARGS(TEST_PARENT, TEST_FUNC, (,), 1)
|
||||
};
|
||||
|
||||
zassert_equal(ARRAY_SIZE(vals), 3, "");
|
||||
zassert_equal(ARRAY_SIZE(vals_sep), 3, "");
|
||||
zassert_equal(ARRAY_SIZE(vals_inst), 3, "");
|
||||
zassert_equal(ARRAY_SIZE(vals_inst_sep), 3, "");
|
||||
zassert_equal(ARRAY_SIZE(vals_status_okay), 2, "");
|
||||
zassert_equal(ARRAY_SIZE(vals_status_okay_sep), 2, "");
|
||||
|
||||
zassert_equal(vals[0].val, 1, "");
|
||||
zassert_equal(vals[1].val, 2, "");
|
||||
zassert_equal(vals[2].val, 3, "");
|
||||
zassert_equal(vals_sep[0].val, 1, "");
|
||||
zassert_equal(vals_sep[1].val, 2, "");
|
||||
zassert_equal(vals_sep[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_inst_sep[0].val, 1, "");
|
||||
zassert_equal(vals_inst_sep[1].val, 2, "");
|
||||
zassert_equal(vals_inst_sep[2].val, 3, "");
|
||||
zassert_equal(vals_status_okay[0].val, 1, "");
|
||||
zassert_equal(vals_status_okay[1].val, 2, "");
|
||||
zassert_equal(vals_status_okay_sep[0].val, 1, "");
|
||||
zassert_equal(vals_status_okay_sep[1].val, 2, "");
|
||||
|
||||
#undef TEST_PARENT
|
||||
#undef TEST_FUNC_AND_COMMA
|
||||
#undef TEST_FUNC
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user