Skip to content

Commit

Permalink
devicetree: make DT_PROP_HAS_IDX expand to 0 or 1
Browse files Browse the repository at this point in the history
We have a use case for checking the results of a DT_PROP_HAS_IDX()
call with COND_CODE_1(). That won't work because its expansion is an
integer comparison; COND_CODE_1() expects a literal 1 or 0.

Adjust the macro implementation so it expands to a literal 1 or 0.
Make this work even when the index argument needs an expansion while
we're at it.

Fixes: zephyrproject-rtos#29833
Signed-off-by: Martí Bolívar <[email protected]>
  • Loading branch information
mbolivar-nordic committed Nov 13, 2020
1 parent 6221439 commit 07faa4d
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 3 deletions.
34 changes: 31 additions & 3 deletions include/devicetree.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
* _ENUM_IDX: property's value as an index into bindings enum
* _EXISTS: property is defined
* _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)
* _IDX_<i>_VAL_<val>: phandle array's specifier value by index
* _IDX_<i>_VAL_<val>_EXISTS: cell value exists, by index
Expand Down Expand Up @@ -438,7 +439,7 @@
* into the given property, and 0 otherwise.
*/
#define DT_PROP_HAS_IDX(node_id, prop, idx) \
((idx) < DT_PROP_LEN(node_id, prop))
IS_ENABLED(DT_CAT6(node_id, _P_, prop, _IDX_, idx, _EXISTS))

/**
* @brief Get the value at index "idx" in an array type property
Expand Down Expand Up @@ -1921,8 +1922,35 @@
UTIL_CAT(DT_ROOT, MACRO_MAP_CAT(DT_S_PREFIX, __VA_ARGS__))
/** @internal helper for DT_PATH(): prepends _S_ to a node name */
#define DT_S_PREFIX(name) _S_##name
/** @internal concatenation helper, sometimes used to force expansion */
#define DT_CAT(node_id, prop_suffix) node_id##prop_suffix

/**
* @internal concatenation helper, 2 arguments
*
* This and the following macros are used to paste things together
* with "##" *after* forcing expansion on each argument.
*
* We could try to use something like UTIL_CAT(), but the compiler
* error messages from the util macros can be extremely long when they
* are misused. This unfortunately happens often with devicetree.h,
* since its macro-based API is fiddly and can be hard to get right.
*
* Keeping things brutally simple here hopefully makes some errors
* easier to read.
*/
#define DT_CAT(a1, a2) a1 ## a2
/** @internal concatenation helper, 3 arguments */
#define DT_CAT3(a1, a2, a3) a1 ## a2 ## a3
/** @internal concatenation helper, 4 arguments */
#define DT_CAT4(a1, a2, a3, a4) a1 ## a2 ## a3 ## a4
/** @internal concatenation helper, 5 arguments */
#define DT_CAT5(a1, a2, a3, a4, a5) a1 ## a2 ## a3 ## a4 ## a5
/** @internal concatenation helper, 6 arguments */
#define DT_CAT6(a1, a2, a3, a4, a5, a6) a1 ## a2 ## a3 ## a4 ## a5 ## a6
/*
* If you need to define a bigger DT_CATN(), do so here. Don't leave
* any "holes" of undefined macros, please.
*/

/** @internal helper for node identifier macros to expand args */
#define DT_DASH(...) MACRO_MAP_CAT(DT_DASH_PREFIX, __VA_ARGS__)
/** @internal helper for DT_DASH(): prepends _ to a name */
Expand Down
6 changes: 6 additions & 0 deletions scripts/dts/gen_defines.py
Original file line number Diff line number Diff line change
Expand Up @@ -428,11 +428,13 @@ def write_vanilla_props(node):
macro2val.update(phandle_macros(prop, macro))
elif "array" in prop.type:
# DT_N_<node-id>_P_<prop-id>_IDX_<i>
# DT_N_<node-id>_P_<prop-id>_IDX_<i>_EXISTS
for i, subval in enumerate(prop.val):
if isinstance(subval, str):
macro2val[macro + f"_IDX_{i}"] = quote_str(subval)
else:
macro2val[macro + f"_IDX_{i}"] = subval
macro2val[macro + f"_IDX_{i}_EXISTS"] = 1

plen = prop_len(prop)
if plen is not None:
Expand Down Expand Up @@ -542,9 +544,11 @@ def phandle_macros(prop, macro):
if prop.type == "phandle":
# A phandle is treated as a phandles with fixed length 1.
ret[f"{macro}_IDX_0_PH"] = f"DT_{prop.val.z_path_id}"
ret[f"{macro}_IDX_0_EXISTS"] = 1
elif prop.type == "phandles":
for i, node in enumerate(prop.val):
ret[f"{macro}_IDX_{i}_PH"] = f"DT_{node.z_path_id}"
ret[f"{macro}_IDX_{i}_EXISTS"] = 1
elif prop.type == "phandle-array":
for i, entry in enumerate(prop.val):
ret.update(controller_and_data_macros(entry, i, macro))
Expand All @@ -562,6 +566,8 @@ def controller_and_data_macros(entry, i, macro):
ret = {}
data = entry.data

# DT_N_<node-id>_P_<prop-id>_IDX_<i>_EXISTS
ret[f"{macro}_IDX_{i}_EXISTS"] = 1
# DT_N_<node-id>_P_<prop-id>_IDX_<i>_PH
ret[f"{macro}_IDX_{i}_PH"] = f"DT_{entry.controller.z_path_id}"
# DT_N_<node-id>_P_<prop-id>_IDX_<i>_VAL_<VAL>
Expand Down
14 changes: 14 additions & 0 deletions tests/lib/devicetree/api/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -1206,6 +1206,8 @@ static char *c[] = DT_PROP(TEST_ARRAYS, c);

static void test_arrays(void)
{
int ok;

zassert_equal(ARRAY_SIZE(a), 3, "a size");
zassert_equal(ARRAY_SIZE(b), 4, "b size");
zassert_equal(ARRAY_SIZE(c), 2, "c size");
Expand All @@ -1219,6 +1221,18 @@ static void test_arrays(void)
zassert_true(DT_PROP_HAS_IDX(TEST_ARRAYS, a, 2), "a idx 2");
zassert_false(DT_PROP_HAS_IDX(TEST_ARRAYS, a, 3), "!a idx 3");

/*
* Verify that DT_PROP_HAS_IDX can be used with COND_CODE_1()
* and COND_CODE_0(), i.e. its expansion is a literal 1 or 0,
* not an equivalent expression that evaluates to 1 or 0.
*/
ok = 0;
COND_CODE_1(DT_PROP_HAS_IDX(TEST_ARRAYS, a, 0), (ok = 1;), ());
zassert_equal(ok, 1, "a idx 0 as a literal 1");
ok = 0;
COND_CODE_0(DT_PROP_HAS_IDX(TEST_ARRAYS, a, 3), (ok = 1;), ());
zassert_equal(ok, 1, "a idx 3 as a literal 0");

zassert_equal(DT_PROP_BY_IDX(TEST_ARRAYS, a, 0), a[0], "a 0");
zassert_equal(DT_PROP_BY_IDX(TEST_ARRAYS, a, 1), a[1], "a 1");
zassert_equal(DT_PROP_BY_IDX(TEST_ARRAYS, a, 2), a[2], "a 2");
Expand Down

0 comments on commit 07faa4d

Please sign in to comment.