Patchwork [RFC,V1,02/14] device_tree: Extended interface for fdt_generic

login
register
mail settings
Submitter Peter A. G. Crosthwaite
Date Aug. 25, 2011, 6:41 a.m.
Message ID <1314254480-22438-3-git-send-email-peter.crosthwaite@petalogix.com>
Download mbox | patch
Permalink /patch/111468/
State New
Headers show

Comments

Peter A. G. Crosthwaite - Aug. 25, 2011, 6:41 a.m.
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.crosthwaite@petalogix.com>
---
 device_tree.c |  202 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 device_tree.h |   30 +++++++++
 2 files changed, 232 insertions(+), 0 deletions(-)

Patch

diff --git a/device_tree.c b/device_tree.c
index 3a224d1..c48c90a 100644
--- a/device_tree.c
+++ b/device_tree.c
@@ -107,3 +107,205 @@  int qemu_devtree_setprop_string(void *fdt, const char *node_path,
 
     return fdt_setprop_string(fdt, offset, property, string);
 }
+
+/* FIXME: guard against move argument running off the end of the parameter */
+
+int qemu_devtree_getprop(void *fdt, int *err, const char *node_path,
+        const char *type, int move, int inherit)
+{
+    int offset = fdt_path_offset(fdt, node_path);
+    unsigned int *temp;
+
+    if (err) {
+        *err = 0;
+    }
+
+    temp = (unsigned int *)fdt_getprop(fdt, offset, type, NULL);
+
+    if (temp) {
+        return be32_to_cpu(*(temp + move));
+    } else if (inherit) {
+        char parent [DT_PATH_LENGTH];
+        if (!qemu_devtree_getparent(fdt, parent, node_path)) {
+            return qemu_devtree_getprop(fdt, err, parent, type, move, 1);
+        }
+    }
+    if (err) {
+        *err = 1;
+    }
+    return 0;
+}
+
+char *qemu_devtree_getprop_string(void *fdt, const char *node_path,
+        const char *type, int move, int *len, int inherit)
+{
+    int offset = fdt_path_offset(fdt, node_path);
+    int llen;
+    const char *temp;
+    if (!len) {
+        len = &llen;
+    }
+    temp = fdt_getprop(fdt, offset, type, len);
+
+    if (temp) {
+        char *ret;
+        while (move) {
+            char *next = memchr(temp, '\0', *len) + 1;
+            *len -= (next - temp);
+            temp = next;
+            if (*len <= 0) {
+                printf("Can't get %s property - not enough elements\n", type);
+                return NULL;
+            }
+            move--;
+        }
+        ret = g_malloc0(*len);
+        memcpy(ret, temp, *len);
+        return ret;
+    } else if (inherit) {
+        char parent [DT_PATH_LENGTH];
+        if (!qemu_devtree_getparent(fdt, parent, node_path)) {
+            return qemu_devtree_getprop_string(fdt, parent, type, move, len,
+                1);
+        }
+    }
+    return NULL;
+}
+
+char *qemu_devtree_get_node_name(void *fdt, const char *node_path) {
+    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;
+}
+
+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 = 0;
+            int compat_len;
+            if (fdt_get_path(fdt, offset, node_path, DT_PATH_LENGTH))
+                sprintf(node_path, "(none)");
+            else
+                all_compats = qemu_devtree_getprop_string(fdt, node_path,
+                    "compatible", 0, &compat_len, 0);
+            if (all_compats) {
+                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 cecd98f..e4f2d8a 100644
--- a/device_tree.h
+++ b/device_tree.h
@@ -16,6 +16,8 @@ 
 
 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, void *val_array, int size);
 int qemu_devtree_setprop_cell(void *fdt, const char *node_path,
@@ -23,4 +25,32 @@  int qemu_devtree_setprop_cell(void *fdt, const char *node_path,
 int qemu_devtree_setprop_string(void *fdt, const char *node_path,
                                 const char *property, const char *string);
 
+/* property getters */
+
+int qemu_devtree_getprop(void *fdt, int *err, const char *node_path,
+                        const char *type, int move, int inherit);
+char *qemu_devtree_getprop_string(void *fdt, const char *node_path,
+                        const char *type, int move, int *len, int inherit);
+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);
+
+/* misc */
+
+int devtree_get_num_nodes(void *fdt);
+void devtree_info_dump(void *fdt);
+
+#define DT_PATH_LENGTH 1024
+
 #endif /* __DEVICE_TREE_H__ */