From 333542fabf8720b881e992a5abca32ef4bcb841a Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 16 Oct 2007 13:58:25 +1000 Subject: [PATCH] libfdt: Add functions for handling the "compatible" property This patch adds functions for dealing with the compatible property. fdt_node_check_compatible() can be used to determine whether a node is compatible with a given string and fdt_node_offset_by_compatible() locates nodes with a given compatible string. Testcases for these functions are also included. Signed-off-by: David Gibson --- libfdt/fdt_ro.c | 83 +++++++++++++++++++++++++++++ libfdt/libfdt.h | 5 ++ tests/Makefile.tests | 1 + tests/node_offset_by_compatible.c | 86 +++++++++++++++++++++++++++++++ tests/run_tests.sh | 2 + tests/rw_tree1.c | 7 ++- tests/sw_tree1.c | 7 +++ tests/test_tree1.dts | 4 ++ tests/trees.S | 5 ++ 9 files changed, 199 insertions(+), 1 deletion(-) create mode 100644 tests/node_offset_by_compatible.c diff --git a/libfdt/fdt_ro.c b/libfdt/fdt_ro.c index a5a15e3..49f2653 100644 --- a/libfdt/fdt_ro.c +++ b/libfdt/fdt_ro.c @@ -477,3 +477,86 @@ int fdt_node_offset_by_prop_value(const void *fdt, int startoffset, return -FDT_ERR_NOTFOUND; } + +int _stringlist_contains(const void *strlist, int listlen, const char *str) +{ + int len = strlen(str); + const void *p; + + while (listlen >= len) { + if (memcmp(str, strlist, len+1) == 0) + return 1; + p = memchr(strlist, '\0', listlen); + if (!p) + return 0; /* malformed strlist.. */ + listlen -= (p-strlist) + 1; + strlist = p + 1; + } + return 0; +} + +int fdt_node_check_compatible(const void *fdt, int nodeoffset, + const char *compatible) +{ + const void *prop; + int len; + + prop = fdt_getprop(fdt, nodeoffset, "compatible", &len); + if (!prop) + return len; + if (_stringlist_contains(prop, len, compatible)) + return 0; + else + return 1; +} + +int fdt_node_offset_by_compatible(const void *fdt, int startoffset, + const char *compatible) +{ + uint32_t tag; + int offset, nextoffset; + int err; + + CHECK_HEADER(fdt); + + if (startoffset >= 0) { + tag = _fdt_next_tag(fdt, startoffset, &nextoffset); + if (tag != FDT_BEGIN_NODE) + return -FDT_ERR_BADOFFSET; + } else { + nextoffset = 0; + } + + /* FIXME: The algorithm here is pretty horrible: we scan each + * property of a node in fdt_node_check_compatible(), then if + * that didn't find what we want, we scan over them again + * making our way to the next node. Still it's the easiest to + * implement approach; performance can come later. */ + do { + offset = nextoffset; + tag = _fdt_next_tag(fdt, offset, &nextoffset); + + switch (tag) { + case FDT_BEGIN_NODE: + err = fdt_node_check_compatible(fdt, offset, + compatible); + if ((err < 0) + && (err != -FDT_ERR_NOTFOUND)) + return err; + else if (err == 0) + return offset; + break; + + case FDT_PROP: + case FDT_END: + case FDT_END_NODE: + case FDT_NOP: + break; + + default: + return -FDT_ERR_BADSTRUCTURE; + } + } while (tag != FDT_END); + + return -FDT_ERR_NOTFOUND; +} diff --git a/libfdt/libfdt.h b/libfdt/libfdt.h index 8b7d501..9bb0fe3 100644 --- a/libfdt/libfdt.h +++ b/libfdt/libfdt.h @@ -153,6 +153,11 @@ int fdt_node_offset_by_prop_value(const void *fdt, int startoffset, const char *propname, const void *propval, int proplen); +int fdt_node_check_compatible(const void *fdt, int nodeoffset, + const char *compatible); +int fdt_node_offset_by_compatible(const void *fdt, int startoffset, + const char *compatible); + /* Write-in-place functions */ int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name, const void *val, int len); diff --git a/tests/Makefile.tests b/tests/Makefile.tests index c84b63d..3c3305b 100644 --- a/tests/Makefile.tests +++ b/tests/Makefile.tests @@ -2,6 +2,7 @@ LIB_TESTS_L = get_mem_rsv \ root_node find_property subnode_offset path_offset \ get_name getprop get_path supernode_atdepth_offset parent_offset \ node_offset_by_prop_value \ + node_check_compatible node_offset_by_compatible \ notfound \ setprop_inplace nop_property nop_node \ sw_tree1 \ diff --git a/tests/node_offset_by_compatible.c b/tests/node_offset_by_compatible.c new file mode 100644 index 0000000..02e9874 --- /dev/null +++ b/tests/node_offset_by_compatible.c @@ -0,0 +1,86 @@ +/* + * libfdt - Flat Device Tree manipulation + * Testcase for fdt_node_offset_by_compatible() + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include +#include +#include +#include +#include + +#include +#include + +#include "tests.h" +#include "testdata.h" + +void check_search(void *fdt, const char *compat, ...) +{ + va_list ap; + int offset = -1, target; + + va_start(ap, compat); + do { + target = va_arg(ap, int); + verbose_printf("Searching (target = %d): %d ->", + target, offset); + offset = fdt_node_offset_by_compatible(fdt, offset, compat); + verbose_printf("%d\n", offset); + + if (offset != target) + FAIL("fdt_node_offset_by_compatible(%s) returns %d " + "instead of %d", compat, offset, target); + } while (target >= 0); + + va_end(ap); +} + +int main(int argc, char *argv[]) +{ + void *fdt; + int subnode1_offset, subnode2_offset; + int subsubnode1_offset, subsubnode2_offset; + + test_init(argc, argv); + fdt = load_blob_arg(argc, argv); + + subnode1_offset = fdt_path_offset(fdt, "/subnode@1"); + subnode2_offset = fdt_path_offset(fdt, "/subnode@2"); + subsubnode1_offset = fdt_path_offset(fdt, "/subnode@1/subsubnode"); + subsubnode2_offset = fdt_path_offset(fdt, "/subnode@2/subsubnode@0"); + + if ((subnode1_offset < 0) || (subnode2_offset < 0) + || (subsubnode1_offset < 0) || (subsubnode2_offset < 0)) + FAIL("Can't find required nodes"); + + check_search(fdt, "test_tree1", 0, -FDT_ERR_NOTFOUND); + check_search(fdt, "subnode1", subnode1_offset, -FDT_ERR_NOTFOUND); + check_search(fdt, "subsubnode1", subsubnode1_offset, -FDT_ERR_NOTFOUND); + check_search(fdt, "subsubnode2", subsubnode2_offset, -FDT_ERR_NOTFOUND); + /* Eek.. HACK to make this work whatever the order in the + * example tree */ + if (subsubnode1_offset < subsubnode2_offset) + check_search(fdt, "subsubnode", subsubnode1_offset, + subsubnode2_offset, -FDT_ERR_NOTFOUND); + else + check_search(fdt, "subsubnode", subsubnode2_offset, + subsubnode1_offset, -FDT_ERR_NOTFOUND); + check_search(fdt, "nothing-like-this", -FDT_ERR_NOTFOUND); + + PASS(); +} diff --git a/tests/run_tests.sh b/tests/run_tests.sh index 6bc20ea..0c2142f 100755 --- a/tests/run_tests.sh +++ b/tests/run_tests.sh @@ -42,6 +42,8 @@ tree1_tests () { run_test supernode_atdepth_offset $TREE run_test parent_offset $TREE run_test node_offset_by_prop_value $TREE + run_test node_check_compatible $TREE + run_test node_offset_by_compatible $TREE run_test notfound $TREE # Write-in-place tests diff --git a/tests/rw_tree1.c b/tests/rw_tree1.c index 7835362..d063947 100644 --- a/tests/rw_tree1.c +++ b/tests/rw_tree1.c @@ -72,18 +72,23 @@ int main(int argc, char *argv[]) CHECK(fdt_add_mem_rsv(fdt, TEST_ADDR_1, TEST_SIZE_1)); CHECK(fdt_add_mem_rsv(fdt, TEST_ADDR_2, TEST_SIZE_2)); + CHECK(fdt_setprop_string(fdt, 0, "compatible", "test_tree1")); CHECK(fdt_setprop_typed(fdt, 0, "prop-int", TEST_VALUE_1)); CHECK(fdt_setprop_string(fdt, 0, "prop-str", TEST_STRING_1)); OFF_CHECK(offset, fdt_add_subnode(fdt, 0, "subnode@1")); + CHECK(fdt_setprop_string(fdt, offset, "compatible", "subnode1")); CHECK(fdt_setprop_typed(fdt, offset, "prop-int", TEST_VALUE_1)); OFF_CHECK(offset, fdt_add_subnode(fdt, offset, "subsubnode")); + CHECK(fdt_setprop(fdt, offset, "compatible", + "subsubnode1\0subsubnode", 23)); CHECK(fdt_setprop_typed(fdt, offset, "prop-int", TEST_VALUE_1)); OFF_CHECK(offset, fdt_add_subnode(fdt, 0, "subnode@2")); CHECK(fdt_setprop_typed(fdt, offset, "prop-int", TEST_VALUE_2)); OFF_CHECK(offset, fdt_add_subnode(fdt, offset, "subsubnode@0")); - + CHECK(fdt_setprop(fdt, offset, "compatible", + "subsubnode2\0subsubnode", 23)); CHECK(fdt_setprop_typed(fdt, offset, "prop-int", TEST_VALUE_2)); CHECK(fdt_pack(fdt)); diff --git a/tests/sw_tree1.c b/tests/sw_tree1.c index d4b960e..8784868 100644 --- a/tests/sw_tree1.c +++ b/tests/sw_tree1.c @@ -54,12 +54,17 @@ int main(int argc, char *argv[]) CHECK(fdt_finish_reservemap(fdt)); CHECK(fdt_begin_node(fdt, "")); + CHECK(fdt_property_string(fdt, "compatible", "test_tree1")); CHECK(fdt_property_typed(fdt, "prop-int", TEST_VALUE_1)); CHECK(fdt_property_string(fdt, "prop-str", TEST_STRING_1)); CHECK(fdt_begin_node(fdt, "subnode@1")); + CHECK(fdt_property_string(fdt, "compatible", "subnode1")); CHECK(fdt_property_typed(fdt, "prop-int", TEST_VALUE_1)); CHECK(fdt_begin_node(fdt, "subsubnode")); + CHECK(fdt_property(fdt, "compatible", "subsubnode1\0subsubnode", + 23)); + CHECK(fdt_property_string(fdt, "compatible", "subsubnode1\0")); CHECK(fdt_property_typed(fdt, "prop-int", TEST_VALUE_1)); CHECK(fdt_end_node(fdt)); CHECK(fdt_end_node(fdt)); @@ -67,6 +72,8 @@ int main(int argc, char *argv[]) CHECK(fdt_begin_node(fdt, "subnode@2")); CHECK(fdt_property_typed(fdt, "prop-int", TEST_VALUE_2)); CHECK(fdt_begin_node(fdt, "subsubnode@0")); + CHECK(fdt_property(fdt, "compatible", "subsubnode2\0subsubnode", + 23)); CHECK(fdt_property_typed(fdt, "prop-int", TEST_VALUE_2)); CHECK(fdt_end_node(fdt)); CHECK(fdt_end_node(fdt)); diff --git a/tests/test_tree1.dts b/tests/test_tree1.dts index 212ffa7..c5f8da3 100644 --- a/tests/test_tree1.dts +++ b/tests/test_tree1.dts @@ -2,13 +2,16 @@ /memreserve/ abcd1234 00001234; / { + compatible = "test_tree1"; prop-int = ; prop-str = "hello world"; subnode@1 { + compatible = "subnode1"; prop-int = ; subsubnode { + compatible = "subsubnode1", "subsubnode"; prop-int = ; }; }; @@ -17,6 +20,7 @@ prop-int = ; subsubnode@0 { + compatible = "subsubnode2", "subsubnode"; prop-int = ; }; }; diff --git a/tests/trees.S b/tests/trees.S index bb662a6..c0fbaf6 100644 --- a/tests/trees.S +++ b/tests/trees.S @@ -83,13 +83,16 @@ test_tree1_rsvmap: test_tree1_struct: BEGIN_NODE("") + PROP_STR(test_tree1, compatible, "test_tree1") PROP_INT(test_tree1, prop_int, TEST_VALUE_1) PROP_STR(test_tree1, prop_str, TEST_STRING_1) BEGIN_NODE("subnode@1") + PROP_STR(test_tree1, compatible, "subnode1") PROP_INT(test_tree1, prop_int, TEST_VALUE_1) BEGIN_NODE("subsubnode") + PROP_STR(test_tree1, compatible, "subsubnode1\0subsubnode") PROP_INT(test_tree1, prop_int, TEST_VALUE_1) END_NODE END_NODE @@ -98,6 +101,7 @@ test_tree1_struct: PROP_INT(test_tree1, prop_int, TEST_VALUE_2) BEGIN_NODE("subsubnode@0") + PROP_STR(test_tree1, compatible, "subsubnode2\0subsubnode") PROP_INT(test_tree1, prop_int, TEST_VALUE_2) END_NODE END_NODE @@ -106,6 +110,7 @@ test_tree1_struct: FDTLONG(FDT_END) test_tree1_strings: + STRING(test_tree1, compatible, "compatible") STRING(test_tree1, prop_int, "prop-int") STRING(test_tree1, prop_str, "prop-str") test_tree1_end: