devicetree: add DT_FOREACH_PROP_ELEM(node_id, prop, fn)
It can be convenient to "iterate" over the elements of a property, in the same way it is convenient to "iterate" over enabled instances. Add a new macro for doing this, along with a DT_INST_FOREACH_PROP_ELEM variant. This is likely to be more convenient than UTIL_LISTIFY or FOR_EACH in some situations because: - it handles inputs of any length - compiler error messages will be shorter and more self-contained - it is easier to use with phandle-array type properties, which require more complicated macro boilerplate when used with util_macro.h APIs Signed-off-by: Martí Bolívar <marti.bolivar@nordicsemi.no>
This commit is contained in:
parent
62d8d1a4c6
commit
9c229a417c
4 changed files with 107 additions and 2 deletions
|
@ -44,6 +44,7 @@
|
|||
* enum values are identifiers)
|
||||
* _ENUM_UPPER_TOKEN: like _ENUM_TOKEN, but uppercased
|
||||
* _EXISTS: property is defined
|
||||
* _FOREACH_PROP_ELEM: helper for "iterating" over values in the property
|
||||
* _IDX_<i>: logical index into property
|
||||
* _IDX_<i>_EXISTS: logical index into property is defined
|
||||
* _IDX_<i>_PH: phandle array's phandle by index (or phandle, phandles)
|
||||
|
@ -1484,6 +1485,52 @@
|
|||
#define DT_FOREACH_CHILD(node_id, fn) \
|
||||
DT_CAT(node_id, _FOREACH_CHILD)(fn)
|
||||
|
||||
/**
|
||||
* @brief Invokes "fn" for each element in the value of property "prop".
|
||||
*
|
||||
* The macro "fn" must take three 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.
|
||||
*
|
||||
* Example devicetree fragment:
|
||||
*
|
||||
* n: node {
|
||||
* my-ints = <1 2 3>;
|
||||
* };
|
||||
*
|
||||
* Example usage:
|
||||
*
|
||||
* #define TIMES_TWO(node_id, prop, idx) \
|
||||
* (2 * DT_PROP_BY_IDX(node_id, prop, idx)),
|
||||
*
|
||||
* int array[] = {
|
||||
* DT_FOREACH_PROP_ELEM(DT_NODELABEL(n), my_ints, TIMES_TWO)
|
||||
* };
|
||||
*
|
||||
* This expands to:
|
||||
*
|
||||
* int array[] = {
|
||||
* (2 * 1), (2 * 2), (2 * 3),
|
||||
* };
|
||||
*
|
||||
* In general, this macro expands to:
|
||||
*
|
||||
* fn(node_id, prop, 0) fn(node_id, prop, 1) [...] fn(node_id, prop, n-1)
|
||||
*
|
||||
* where "n" is the number of elements in "prop", as it would be
|
||||
* returned by <tt>DT_PROP_LEN(node_id, prop)</tt>.
|
||||
*
|
||||
* The "prop" argument must refer to a property with type string,
|
||||
* array, uint8-array, string-array, phandles, or phandle-array. It is
|
||||
* an error to use this macro with properties of other types.
|
||||
*
|
||||
* @param node_id node identifier
|
||||
* @param prop lowercase-and-underscores property name
|
||||
* @param fn macro to invoke
|
||||
*/
|
||||
#define DT_FOREACH_PROP_ELEM(node_id, prop, fn) \
|
||||
DT_CAT4(node_id, _P_, prop, _FOREACH_PROP_ELEM)(fn)
|
||||
|
||||
/**
|
||||
* @}
|
||||
|
@ -2151,6 +2198,19 @@
|
|||
DT_DRV_COMPAT)(fn)), \
|
||||
())
|
||||
|
||||
/**
|
||||
* @brief Invokes "fn" for each element of property "prop" for
|
||||
* a DT_DRV_COMPAT instance.
|
||||
*
|
||||
* Equivalent to DT_FOREACH_PROP_ELEM(DT_DRV_INST(inst), prop, fn).
|
||||
*
|
||||
* @param inst instance number
|
||||
* @param prop lowercase-and-underscores property name
|
||||
* @param fn macro to invoke
|
||||
*/
|
||||
#define DT_INST_FOREACH_PROP_ELEM(inst, prop, fn) \
|
||||
DT_FOREACH_PROP_ELEM(DT_DRV_INST(inst), prop, fn)
|
||||
|
||||
/**
|
||||
* @brief Does a DT_DRV_COMPAT instance have a property?
|
||||
* @param inst instance number
|
||||
|
|
|
@ -32,6 +32,12 @@ sys.path.append(os.path.join(os.path.dirname(__file__), 'python-devicetree',
|
|||
|
||||
from devicetree import edtlib
|
||||
|
||||
# The set of binding types whose values can be iterated over with
|
||||
# DT_FOREACH_PROP_ELEM(). If you change this, make sure to update the
|
||||
# doxygen string for that macro.
|
||||
FOREACH_PROP_ELEM_TYPES = set(['string', 'array', 'uint8-array', 'string-array',
|
||||
'phandles', 'phandle-array'])
|
||||
|
||||
class LogFormatter(logging.Formatter):
|
||||
'''A log formatter that prints the level name in lower case,
|
||||
for compatibility with earlier versions of edtlib.'''
|
||||
|
@ -490,7 +496,8 @@ def write_vanilla_props(node):
|
|||
|
||||
macro2val = {}
|
||||
for prop_name, prop in node.props.items():
|
||||
macro = f"{node.z_path_id}_P_{str2ident(prop_name)}"
|
||||
prop_id = str2ident(prop_name)
|
||||
macro = f"{node.z_path_id}_P_{prop_id}"
|
||||
val = prop2value(prop)
|
||||
if val is not None:
|
||||
# DT_N_<node-id>_P_<prop-id>
|
||||
|
@ -523,6 +530,12 @@ def write_vanilla_props(node):
|
|||
macro2val[macro + f"_IDX_{i}"] = subval
|
||||
macro2val[macro + f"_IDX_{i}_EXISTS"] = 1
|
||||
|
||||
if prop.type in FOREACH_PROP_ELEM_TYPES:
|
||||
# DT_N_<node-id>_P_<prop-id>_FOREACH_PROP_ELEM
|
||||
macro2val[f"{macro}_FOREACH_PROP_ELEM(fn)"] = \
|
||||
' \\\n\t'.join(f'fn(DT_{node.z_path_id}, {prop_id}, {i})'
|
||||
for i in range(len(prop.val)))
|
||||
|
||||
plen = prop_len(prop)
|
||||
if plen is not None:
|
||||
# DT_N_<node-id>_P_<prop-id>_LEN
|
||||
|
|
|
@ -25,7 +25,10 @@
|
|||
interrupt-parent = <&test_intc>;
|
||||
|
||||
test_arrays: array-holder {
|
||||
/* vnd,undefined-compat is for DT_NODE_HAS_COMPAT_STATUS(..,okay) */
|
||||
/*
|
||||
* vnd,undefined-compat is for DT_NODE_HAS_COMPAT_STATUS(..,okay).
|
||||
* There should only be one vnd,array-holder in the entire DTS.
|
||||
*/
|
||||
compatible = "vnd,array-holder", "vnd,undefined-compat";
|
||||
a = <1000 2000 3000>;
|
||||
b = [aa bb cc dd];
|
||||
|
|
|
@ -1252,6 +1252,34 @@ static void test_arrays(void)
|
|||
zassert_equal(DT_PROP_LEN(TEST_ARRAYS, c), 2, "");
|
||||
}
|
||||
|
||||
static void test_foreach_prop_elem(void)
|
||||
{
|
||||
#define TIMES_TWO(node_id, prop, idx) \
|
||||
(2 * DT_PROP_BY_IDX(node_id, prop, idx)),
|
||||
|
||||
int array[] = {
|
||||
DT_FOREACH_PROP_ELEM(TEST_ARRAYS, a, TIMES_TWO)
|
||||
};
|
||||
|
||||
zassert_equal(ARRAY_SIZE(array), 3, "");
|
||||
zassert_equal(array[0], 2000, "");
|
||||
zassert_equal(array[1], 4000, "");
|
||||
zassert_equal(array[2], 6000, "");
|
||||
|
||||
#undef DT_DRV_COMPAT
|
||||
#define DT_DRV_COMPAT vnd_array_holder
|
||||
|
||||
int inst_array[] = {
|
||||
DT_INST_FOREACH_PROP_ELEM(0, a, TIMES_TWO)
|
||||
};
|
||||
|
||||
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 {
|
||||
uint32_t reg_addr;
|
||||
uint32_t reg_len;
|
||||
|
@ -1782,6 +1810,7 @@ void test_main(void)
|
|||
ztest_unit_test(test_pwms),
|
||||
ztest_unit_test(test_macro_names),
|
||||
ztest_unit_test(test_arrays),
|
||||
ztest_unit_test(test_foreach_prop_elem),
|
||||
ztest_unit_test(test_devices),
|
||||
ztest_unit_test(test_cs_gpios),
|
||||
ztest_unit_test(test_chosen),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue