devicetree: better DT_PROP_BY_IDX()/DT_FOREACH_PROP_ELEM() support

Support use of these macros with properties of type phandle and
string by allowing iterating over:

- a phandle as if it were a phandles of length 1, for convenience and
  consistency with our ability to take its length (and getting 1)

- the non-null characters in a string: we exclude the null for
  consistency with the return value of DT_PROP_LEN() on string
  properties, which, like strlen(), does not include the null

With this and a previous patch expanding the usage of DT_PROP_LEN(),
there is now a relationship between being able to take a property's
logical length with DT_PROP_LEN() and being able to iterate over its
logical elements with DT_FOREACH_PROP_ELEM(). Explain this in the
documentation.

Signed-off-by: Martí Bolívar <marti.bolivar@nordicsemi.no>
This commit is contained in:
Martí Bolívar 2023-05-06 14:42:15 -07:00 committed by Carles Cufí
parent 8aa83f6ae8
commit 0c29e07e30
2 changed files with 47 additions and 26 deletions

View file

@ -725,16 +725,28 @@
* It might help to read the argument order as being similar to
* `node->property[index]`.
*
* When the property's binding has type array, string-array,
* uint8-array, or phandles, this expands to the idx-th array element
* as an integer, string literal, or node identifier respectively.
* The return value depends on the property's type:
*
* - for types array, string-array, uint8-array, and phandles,
* this expands to the idx-th array element as an
* integer, string literal, integer, and node identifier
* respectively
*
* - for type phandle, idx must be 0 and the expansion is a node
* identifier (this treats phandle like a phandles of length 1)
*
* - for type string, idx must be 0 and the expansion is the the
* entire string (this treats string like string-array of length 1)
*
* These properties are handled as special cases:
*
* - `reg` property: use DT_REG_ADDR_BY_IDX() or DT_REG_SIZE_BY_IDX() instead
* - `interrupts` property: use DT_IRQ_BY_IDX() instead
* - `reg`: use DT_REG_ADDR_BY_IDX() or DT_REG_SIZE_BY_IDX() instead
* - `interrupts`: use DT_IRQ_BY_IDX()
* - `ranges`: use DT_NUM_RANGES()
* - `dma-ranges`: it is an error to use this property with
* DT_PROP_BY_IDX()
*
* For non-array properties, behavior is undefined.
* For properties of other types, behavior is undefined.
*
* @param node_id node identifier
* @param prop lowercase-and-underscores property name
@ -2653,6 +2665,9 @@
* DT_FOREACH_PROP_ELEM(), and @p idx is the current index into the array.
* The @p idx values are integer literals starting from 0.
*
* The @p prop argument must refer to a property that can be passed to
* DT_PROP_LEN().
*
* Example devicetree fragment:
*
* @code{.dts}
@ -2687,13 +2702,10 @@
* where `n` is the number of elements in @p prop, as it would be
* returned by `DT_PROP_LEN(node_id, prop)`.
*
* The @p 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
* @see DT_PROP_LEN
*/
#define DT_FOREACH_PROP_ELEM(node_id, prop, fn) \
DT_CAT4(node_id, _P_, prop, _FOREACH_PROP_ELEM)(fn)
@ -2730,9 +2742,8 @@
* };
* @endcode
*
* 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.
* The @p prop parameter has the same restrictions as the same parameter
* given to DT_FOREACH_PROP_ELEM().
*
* @param node_id node identifier
* @param prop lowercase-and-underscores property name
@ -2755,6 +2766,9 @@
* the array. The @p idx values are integer literals starting from 0. The
* remaining arguments are passed-in by the caller.
*
* The @p prop parameter has the same restrictions as the same parameter
* given to DT_FOREACH_PROP_ELEM().
*
* @param node_id node identifier
* @param prop lowercase-and-underscores property name
* @param fn macro to invoke
@ -2769,6 +2783,9 @@
* @brief Invokes @p fn for each element in the value of property @p prop with
* multiple arguments and a separator.
*
* The @p prop parameter has the same restrictions as the same parameter
* given to DT_FOREACH_PROP_ELEM().
*
* @param node_id node identifier
* @param prop lowercase-and-underscores property name
* @param fn macro to invoke

View file

@ -32,12 +32,6 @@ sys.path.insert(0, 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.'''
@ -651,6 +645,12 @@ def write_vanilla_props(node):
macro2val[macro + "_STRING_TOKEN"] = prop.val_as_token
# DT_N_<node-id>_P_<prop-id>_IDX_<i>_STRING_UPPER_TOKEN
macro2val[macro + "_STRING_UPPER_TOKEN"] = prop.val_as_token.upper()
# DT_N_<node-id>_P_<prop-id>_IDX_0:
# DT_N_<node-id>_P_<prop-id>_IDX_0_EXISTS:
# Allows treating the string like a degenerate case of a
# string-array of length 1.
macro2val[macro + "_IDX_0"] = quote_str(prop.val)
macro2val[macro + "_IDX_0_EXISTS"] = 1
if prop.enum_index is not None:
# DT_N_<node-id>_P_<prop-id>_ENUM_IDX
@ -692,33 +692,32 @@ 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:
plen = prop_len(prop)
if plen is not None:
# 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)))
for i in range(plen))
# DT_N_<node-id>_P_<prop-id>_FOREACH_PROP_ELEM_SEP
macro2val[f"{macro}_FOREACH_PROP_ELEM_SEP(fn, sep)"] = \
' DT_DEBRACKET_INTERNAL sep \\\n\t'.join(
f'fn(DT_{node.z_path_id}, {prop_id}, {i})'
for i in range(len(prop.val)))
for i in range(plen))
# DT_N_<node-id>_P_<prop-id>_FOREACH_PROP_ELEM_VARGS
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)))
for i in range(plen))
# DT_N_<node-id>_P_<prop-id>_FOREACH_PROP_ELEM_SEP_VARGS
macro2val[f"{macro}_FOREACH_PROP_ELEM_SEP_VARGS(fn, sep, ...)"] = \
' DT_DEBRACKET_INTERNAL sep \\\n\t'.join(
f'fn(DT_{node.z_path_id}, {prop_id}, {i}, __VA_ARGS__)'
for i in range(len(prop.val)))
for i in range(plen))
plen = prop_len(prop)
if plen is not None:
# DT_N_<node-id>_P_<prop-id>_LEN
macro2val[macro + "_LEN"] = plen
@ -786,6 +785,11 @@ def prop_len(prop):
# Returns the property's length if and only if we should generate
# a _LEN macro for the property. Otherwise, returns None.
#
# The set of types handled here coincides with the allowable types
# that can be used with DT_PROP_LEN(). If you change this set,
# make sure to update the doxygen string for that macro, and make
# sure that DT_FOREACH_PROP_ELEM() works for the new types too.
#
# This deliberately excludes ranges, dma-ranges, reg and interrupts.
# While they have array type, their lengths as arrays are
# basically nonsense semantically due to #address-cells and