diff --git a/include/zephyr/devicetree.h b/include/zephyr/devicetree.h index 705396a050e..75af47f53df 100644 --- a/include/zephyr/devicetree.h +++ b/include/zephyr/devicetree.h @@ -1712,6 +1712,94 @@ * @} */ +/** + * @defgroup devicetree-generic-vendor Vendor name helpers + * @ingroup devicetree + * @{ + */ + +/** + * @brief Get the vendor at index "idx" as a string literal + * + * The vendor is a string extracted from vendor prefixes if an entry exists + * that matches the node's compatible prefix. There may be as many as one + * vendor prefixes file per directory in DTS_ROOT. + * + * Example vendor-prefixes.txt: + * + * vnd A stand-in for a real vendor + * zephyr Zephyr-specific binding + * + * Example devicetree fragment: + * + * n1: node-1 { + * compatible = "vnd,model1", "gpio", "zephyr,model2"; + * }; + * + * Example usage: + * + * DT_NODE_VENDOR_BY_IDX(DT_NODELABEL(n1), 0) // "A stand-in for a real vendor" + * DT_NODE_VENDOR_BY_IDX(DT_NODELABEL(n1), 2) // "Zephyr-specific binding" + * + * Notice that the compatible at index 1 doesn't match any entries in the + * vendor prefix file and therefore index 1 is not a valid vendor index. Use + * DT_NODE_VENDOR_HAS_IDX(node_id, idx) to determine if an index is valid. + * + * @param node_id node identifier + * @param idx index of the vendor to return + * @return string literal of the idx-th vendor + */ +#define DT_NODE_VENDOR_BY_IDX(node_id, idx) \ + DT_CAT(node_id, _COMPAT_VENDOR_IDX_##idx) + +/** + * @brief Does a node's compatible property have a vendor at an index? + * + * If this returns 1, then DT_NODE_VENDOR_BY_IDX(node_id, idx) is valid. If it + * returns 0, it is an error to use DT_NODE_VENDOR_BY_IDX(node_id, idx) with + * index "idx". + * + * @param node_id node identifier + * @param idx index of the vendor to check + * @return 1 if "idx" is a valid vendor index, + * 0 otherwise. + */ +#define DT_NODE_VENDOR_HAS_IDX(node_id, idx) \ + IS_ENABLED(DT_CAT4(node_id, _COMPAT_VENDOR_IDX_, idx, _EXISTS)) + +/** + * @brief Like DT_NODE_VENDOR_BY_IDX(), but with a fallback to default_value. + * + * If the value exists, this expands to DT_NODE_VENDOR_BY_IDX(node_id, idx). + * The default_value parameter is not expanded in this case. + * + * Otherwise, this expands to default_value. + * + * @param node_id node identifier + * @param idx index of the vendor to return + * @return string literal of the idx-th vendor + * @param default_value a fallback value to expand to + * @return string literal of the idx-th vendor or "default_value" + */ +#define DT_NODE_VENDOR_BY_IDX_OR(node_id, idx, default_value) \ + COND_CODE_1(DT_NODE_VENDOR_HAS_IDX(node_id, idx), \ + (DT_NODE_VENDOR_BY_IDX(node_id, idx)), (default_value)) + +/** + * @brief Get the node's (only) vendor as a string literal + * + * Equivalent to DT_NODE_VENDOR_BY_IDX_OR(node_id, 0, default_value). + * + * @param node_id node identifier + * @param default_value a fallback value to expand to + */ +#define DT_NODE_VENDOR_OR(node_id, default_value) \ + DT_NODE_VENDOR_BY_IDX_OR(node_id, 0, default_value) + +/** + * @} + */ + /** * @defgroup devicetree-reg-prop reg property * @ingroup devicetree diff --git a/tests/lib/devicetree/api/app.overlay b/tests/lib/devicetree/api/app.overlay index 50655eb4ddd..3f143794ed3 100644 --- a/tests/lib/devicetree/api/app.overlay +++ b/tests/lib/devicetree/api/app.overlay @@ -344,6 +344,11 @@ misc-prop = <1234>; }; + test_vendor: vendor { + compatible = "vnd,model1", "gpio", "zephyr,model2"; + status = "okay"; + }; + test_intc: interrupt-controller@bbbbcccc { compatible = "vnd,intc"; reg = <0xbbbbcccc 0x1000>; diff --git a/tests/lib/devicetree/api/src/main.c b/tests/lib/devicetree/api/src/main.c index b9512177106..bcf7f55e349 100644 --- a/tests/lib/devicetree/api/src/main.c +++ b/tests/lib/devicetree/api/src/main.c @@ -36,6 +36,7 @@ #define TEST_IRQ DT_NODELABEL(test_irq) #define TEST_TEMP DT_NODELABEL(test_temp_sensor) #define TEST_REG DT_NODELABEL(test_reg) +#define TEST_VENDOR DT_NODELABEL(test_vendor) #define TEST_ENUM_0 DT_NODELABEL(test_enum_0) #define TEST_I2C DT_NODELABEL(test_i2c) @@ -398,6 +399,34 @@ ZTEST(devicetree_api, test_bus) NULL); } +#undef DT_DRV_COMPAT +#define DT_DRV_COMPAT vnd_vendor + +#define VND_VENDOR "A stand-in for a real vendor which can be used in examples and tests" +#define ZEP_VENDOR "Zephyr-specific binding" + +ZTEST(devicetree_api, test_vendor) +{ + /* DT_NODE_VENDOR_HAS_IDX */ + zassert_true(DT_NODE_VENDOR_HAS_IDX(TEST_VENDOR, 0), ""); + zassert_false(DT_NODE_VENDOR_HAS_IDX(TEST_VENDOR, 1), ""); + zassert_true(DT_NODE_VENDOR_HAS_IDX(TEST_VENDOR, 2), ""); + zassert_false(DT_NODE_VENDOR_HAS_IDX(TEST_VENDOR, 3), ""); + + /* DT_NODE_VENDOR_BY_IDX */ + zassert_true(!strcmp(DT_NODE_VENDOR_BY_IDX(TEST_VENDOR, 0), VND_VENDOR), ""); + zassert_true(!strcmp(DT_NODE_VENDOR_BY_IDX(TEST_VENDOR, 2), ZEP_VENDOR), ""); + + /* DT_NODE_VENDOR_BY_IDX_OR */ + zassert_true(!strcmp(DT_NODE_VENDOR_BY_IDX_OR(TEST_VENDOR, 0, NULL), VND_VENDOR), ""); + zassert_is_null(DT_NODE_VENDOR_BY_IDX_OR(TEST_VENDOR, 1, NULL), ""); + zassert_true(!strcmp(DT_NODE_VENDOR_BY_IDX_OR(TEST_VENDOR, 2, NULL), ZEP_VENDOR), ""); + zassert_is_null(DT_NODE_VENDOR_BY_IDX_OR(TEST_VENDOR, 3, NULL), ""); + + /* DT_NODE_VENDOR_OR */ + zassert_true(!strcmp(DT_NODE_VENDOR_OR(TEST_VENDOR, NULL), VND_VENDOR), ""); +} + #undef DT_DRV_COMPAT #define DT_DRV_COMPAT vnd_reg_holder ZTEST(devicetree_api, test_reg)