Extended the wrapper interface (around libfdt) for device tree. Node Property getters have been added (qemu_devtree_getprop*) as well as helpers to search/ navigate the nodes of a FDT blob.
Signed-off-by: Peter A. G. Crosthwaite <peter.crosthwa...@petalogix.com> --- device_tree.c | 158 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ device_tree.h | 27 ++++++++++ 2 files changed, 185 insertions(+), 0 deletions(-) diff --git a/device_tree.c b/device_tree.c index 3e22286..2c5c9ef 100644 --- a/device_tree.c +++ b/device_tree.c @@ -323,3 +323,161 @@ int qemu_devtree_add_subnode(void *fdt, const char *name) g_free(dupname); return retval; } + +char *qemu_devtree_get_node_name(void *fdt, const char *node_path) +{ + const char *ret = fdt_get_name(fdt, fdt_path_offset(fdt, node_path), NULL); + return ret ? strdup(ret) : NULL; +} + +int qemu_devtree_get_node_depth(void *fdt, const char *node_path) +{ + return fdt_node_depth(fdt, fdt_path_offset(fdt, node_path)); +} + +static void qemu_devtree_children_info(void *fdt, const char *node_path, + int depth, int *num, char **returned_paths) { + int offset = fdt_path_offset(fdt, node_path); + int root_depth = fdt_node_depth(fdt, offset); + int cur_depth = root_depth; + + *num = 0; + for (;;) { + offset = fdt_next_node(fdt, offset, &cur_depth); + if (cur_depth <= root_depth) { + break; + } + if (cur_depth <= root_depth + depth || depth == 0) { + if (returned_paths) { + returned_paths[*num] = g_malloc0(DT_PATH_LENGTH); + fdt_get_path(fdt, offset, returned_paths[*num], DT_PATH_LENGTH); + } + (*num)++; + } + } +} + +char **qemu_devtree_get_children(void *fdt, const char *node_path, int depth) +{ + int num_children = qemu_devtree_get_num_children(fdt, node_path, depth); + char **ret = g_malloc0(sizeof(*ret) * num_children); + + qemu_devtree_children_info(fdt, node_path, depth, &num_children, ret); + return ret; +} + +int qemu_devtree_get_num_children(void *fdt, const char *node_path, int depth) +{ + int ret; + + qemu_devtree_children_info(fdt, node_path, depth, &ret, NULL); + return ret; +} + +int qemu_devtree_node_by_compatible(void *fdt, char *node_path, + const char *compats) +{ + int offset = fdt_node_offset_by_compatible(fdt, 0, compats); + return offset > 0 ? + fdt_get_path(fdt, offset, node_path, DT_PATH_LENGTH) : 1; +} + +int qemu_devtree_get_node_by_name(void *fdt, char *node_path, + const char *cmpname) { + int offset = 0; + char *name = NULL; + + do { + offset = fdt_next_node(fdt, offset, NULL); + name = (void *)fdt_get_name(fdt, offset, NULL); + if (!name) { + continue; + } + if (!strncmp(name, cmpname, strlen(cmpname))) { + break; + } + } while (offset > 0); + return offset > 0 ? + fdt_get_path(fdt, offset, node_path, DT_PATH_LENGTH) : 1; +} + +int qemu_devtree_get_node_by_phandle(void *fdt, char *node_path, int phandle) +{ + return fdt_get_path(fdt, fdt_node_offset_by_phandle(fdt, phandle), + node_path, DT_PATH_LENGTH); +} + +int qemu_devtree_getparent(void *fdt, char *node_path, const char *current) +{ + int offset = fdt_path_offset(fdt, current); + int parent_offset = fdt_supernode_atdepth_offset(fdt, offset, + fdt_node_depth(fdt, offset) - 1, NULL); + + return parent_offset > 0 ? + fdt_get_path(fdt, parent_offset, node_path, DT_PATH_LENGTH) : 1; +} + +int qemu_devtree_get_root_node(void *fdt, char *node_path) +{ + return fdt_get_path(fdt, 0, node_path, DT_PATH_LENGTH); +} + +static void devtree_scan(void *fdt, int *num_nodes, int info_dump) +{ + int depth = 0, offset = 0; + + if (num_nodes) { + *num_nodes = 0; + } + for (;;) { + offset = fdt_next_node(fdt, offset, &depth); + if (num_nodes) { + (*num_nodes)++; + } + if (offset <= 0 || depth <= 0) { + break; + } + + if (info_dump) { + char node_path[DT_PATH_LENGTH]; + char *all_compats = NULL; + int compat_len; + Error *errp = NULL; + + if (fdt_get_path(fdt, offset, node_path, DT_PATH_LENGTH)) { + sprintf(node_path, "(none)"); + } else { + all_compats = qemu_devtree_getprop(fdt, node_path, "compatible", + &compat_len, false, &errp); + } + if (!errp) { + char *i = all_compats; + for (;;) { + char *j = rawmemchr(i, '\0'); + compat_len -= ((j+1)-i); + if (!compat_len) { + break; + } + *j = ' '; + i = j+1; + } + } + printf("OFFSET: %d, DEPTH: %d, PATH: %s, COMPATS: %s\n", + offset, depth, node_path, + all_compats ? all_compats : "(none)"); + } + } +} + +void devtree_info_dump(void *fdt) +{ + devtree_scan(fdt, NULL, 1); +} + +int devtree_get_num_nodes(void *fdt) +{ + int ret; + + devtree_scan(fdt, &ret, 0); + return ret; +} diff --git a/device_tree.h b/device_tree.h index 2bc188a..e741c2a 100644 --- a/device_tree.h +++ b/device_tree.h @@ -20,6 +20,8 @@ void *create_device_tree(int *sizep); void *load_device_tree(const char *filename_path, int *sizep); +/* property setters */ + int qemu_devtree_setprop(void *fdt, const char *node_path, const char *property, const void *val_array, int size); int qemu_devtree_setprop_cell(void *fdt, const char *node_path, @@ -54,4 +56,29 @@ int qemu_devtree_add_subnode(void *fdt, const char *name); sizeof(qdt_tmp)); \ } while (0) +/* node queries */ + +char *qemu_devtree_get_node_name(void *fdt, const char *node_path); +int qemu_devtree_get_node_depth(void *fdt, const char *node_path); +int qemu_devtree_get_num_children(void *fdt, const char *node_path, int depth); +char **qemu_devtree_get_children(void *fdt, const char *node_path, int depth); + +/* node getters */ + +int qemu_devtree_node_by_compatible(void *fdt, char *node_path, + const char *compats); +int qemu_devtree_get_node_by_name(void *fdt, char *node_path, + const char *cmpname); +int qemu_devtree_get_node_by_phandle(void *fdt, char *node_path, int phandle); +int qemu_devtree_getparent(void *fdt, char *node_path, + const char *current); +int qemu_devtree_get_root_node(void *fdt, char *node_path); + +/* misc */ + +int devtree_get_num_nodes(void *fdt); +void devtree_info_dump(void *fdt); + +#define DT_PATH_LENGTH 1024 + #endif /* __DEVICE_TREE_H__ */ -- 1.7.0.4