diff options
Diffstat (limited to 'modules/devices')
| -rw-r--r-- | modules/devices/devicetree.c | 643 | ||||
| -rw-r--r-- | modules/devices/devicetree/dt_util.c | 569 | 
2 files changed, 620 insertions, 592 deletions
| diff --git a/modules/devices/devicetree.c b/modules/devices/devicetree.c index 6c2a6071..b3a9d621 100644 --- a/modules/devices/devicetree.c +++ b/modules/devices/devicetree.c @@ -24,56 +24,8 @@  #include <unistd.h>  #include <sys/types.h>  #include <stdint.h> -#include <endian.h>  #include "devices.h" - -/* some not-quite-working stuff that can be disabled */ -#define DTEX_INH_PROPS 0 -#define DTEX_PHREFS 0 -#define DTEX_GROUP_TUPS 0 - -enum { -    DT_NODE, -    DT_PROPERTY, -}; -enum { -    DTP_UNK, -    DTP_STR, /* null-delimited list of strings */ -    DTP_INT, -    DTP_UINT, -    DTP_HEX, /* list of 32-bit values displayed in hex */ -    DTP_PH,  /* phandle */ -    DTP_PH_REF,  /* reference to phandle */ -}; - -typedef struct { -    char *path; -    char *name; -    int type; -    unsigned long length; -    void *data; -} dt_raw; - -static struct { -    char *name; int type; -} prop_types[] = { -    { "name", DTP_STR }, -    { "compatible", DTP_STR }, -    { "model", DTP_STR }, -    { "reg", DTP_HEX }, -    { "clocks", DTP_HEX }, -    { "gpios", DTP_HEX }, -    { "phandle", DTP_PH }, -    { "interrupts", DTP_HEX }, -    { "interrupt-parent", DTP_PH_REF }, -    { "regulator-min-microvolt", DTP_UINT }, -    { "regulator-max-microvolt", DTP_UINT }, -    { "clock-frequency", DTP_UINT }, -    { NULL, 0 }, -}; - -static dt_raw *get_dt_raw(char *); -static void dt_raw_free(dt_raw *); +#include "dt_util.h"  /* Hardinfo labels that have # are truncated and/or hidden.   * Labels can't have $ because that is the delimiter in @@ -131,575 +83,84 @@ gchar *hardinfo_clean_value(const gchar *v, int replacing) {      return clean;  } -struct _dt_phandle { -    uint32_t v; -    char *path; -    struct _dt_phandle *next; -}; -typedef struct _dt_phandle dt_phandle; - -dt_phandle *phandles = NULL; - -dt_phandle *dt_phandle_add(uint32_t v, const char *path) { -    dt_phandle *phi = phandles; -    dt_phandle *ph = malloc(sizeof(dt_phandle)); -    memset(ph, 0, sizeof(dt_phandle)); -    ph->v = v; ph->path = strdup(path); -    if (phi == NULL) { -        phandles = ph; -    } else { -        while(phi->next != NULL) phi = phi->next; -        phi->next = ph; -    } -    return ph; -} - -void dt_phandle_free(dt_phandle *ph) { -    if (ph) { -        free(ph->path); -    } -    free(ph); -} - -void dt_phandles_free() { -    dt_phandle *phi; -    while(phandles != NULL) { -        phi = phandles->next; -        dt_phandle_free(phandles); -        phandles = phi; -    } -    phandles = NULL; -} - -char *dt_phandle_lookup(uint32_t v) { -    dt_phandle *phi = phandles; -    while(phi != NULL) { -        if (phi->v == v) -            return phi->path; -        phi = phi->next; -    } -    return NULL; -} - -struct _dt_alias { -    char *label; -    char *path; -    struct _dt_alias *next; -}; -typedef struct _dt_alias dt_alias; - -dt_alias *aliases; - -dt_alias *dt_alias_add(const char *label, const char *path) { -    dt_alias *ali = aliases; -    dt_alias *al = malloc(sizeof(dt_alias)); -    memset(al, 0, sizeof(dt_alias)); -    al->label = strdup(label); al->path = strdup(path); -    if (ali == NULL) { -        aliases = al; -    } else { -        while(ali->next != NULL) ali = ali->next; -        ali->next = al; -    } -    return al; -} - -void dt_alias_free(dt_alias *al) { -    if (al) { -        free(al->label); -        free(al->path); -    } -    free(al); -} -void dt_aliases_free() { -    dt_alias *ali; -    while(aliases != NULL) { -        ali = aliases->next; -        dt_alias_free(aliases); -        aliases = ali; -    } -    aliases = NULL; -} - -char *dt_alias_lookup_by_path(const char* path) { -    dt_alias *ali = aliases; -    while(ali != NULL) { -        if (strcmp(ali->path, path) == 0) -            return ali->label; -        ali = ali->next; -    } -    return NULL; -} - -void dt_map_phandles(char *np) { -    gchar *dir_path; -    gchar *ftmp, *ntmp, *ptmp; -    const gchar *fn; -    GDir *dir; -    dt_raw *prop, *ph_prop; -    uint32_t phandle; - -    if (np == NULL) np = ""; -    dir_path = g_strdup_printf("/proc/device-tree/%s", np); - -    prop = get_dt_raw(np); -    dir = g_dir_open(dir_path, 0 , NULL); -    if (dir) { -        while((fn = g_dir_read_name(dir)) != NULL) { -            ftmp = g_strdup_printf("%s/%s", dir_path, fn); -            if ( g_file_test(ftmp, G_FILE_TEST_IS_DIR) ) { -                ntmp = g_strdup_printf("%s/%s", np, fn); -                ptmp = g_strdup_printf("%s/phandle", ntmp); -                ph_prop = get_dt_raw(ptmp); -                if (ph_prop != NULL) { -                    phandle = be32toh(*(uint32_t*)ph_prop->data); -                    dt_phandle_add(phandle, ntmp); -                } -                dt_map_phandles(ntmp); -                g_free(ptmp); -                g_free(ntmp); -                dt_raw_free(ph_prop); -            } -            g_free(ftmp); -        } -    } -    g_dir_close(dir); -    dt_raw_free(prop); -} - -void dt_read_aliases() { -    gchar *dir_path = g_strdup_printf("/proc/device-tree/aliases"); -    gchar *ftmp, *ntmp, *ptmp; -    const gchar *fn; -    GDir *dir; -    dt_raw *prop; - -    dir = g_dir_open(dir_path, 0 , NULL); -    if (dir) { -        while((fn = g_dir_read_name(dir)) != NULL) { -            ftmp = g_strdup_printf("%s/%s", dir_path, fn); -            if ( g_file_test(ftmp, G_FILE_TEST_IS_DIR) ) -                continue; - -            ntmp = g_strdup_printf("aliases/%s", fn); -            prop = get_dt_raw(ntmp); -            dt_alias_add(prop->name, (char*)prop->data); -            g_free(ntmp); -            g_free(ftmp); -        } -    } -    g_dir_close(dir); -} - -#define DT_CHECK_NAME(prop, nm) (strcmp(prop->name, nm) == 0) - -/*cstd*/ -static int dt_guess_type(dt_raw *prop) { -    char *tmp, *dash; -    int i = 0, anc = 0, might_be_str = 1; - -    /* special #(.*)-cells names are UINT */ -    if (*prop->name == '#') { -        dash = strrchr(prop->name, '-'); -        if (dash != NULL) { -            if (strcmp(dash, "-cells") == 0) -                return DTP_UINT; -        } -    } - -    /* lookup known type */ -    while (prop_types[i].name != NULL) { -        if (strcmp(prop->name, prop_types[i].name) == 0) -            return prop_types[i].type; -        i++; -    } - -    /* maybe a string? */ -    for (i = 0; i < prop->length; i++) { -        tmp = (char*)prop->data + i; -        if ( isalnum(*tmp) ) anc++; /* count the alpha-nums */ -        if ( isprint(*tmp) || *tmp == 0 ) -            continue; -        might_be_str = 0; -        break; -    } -    if (might_be_str && -        ( anc >= prop->length - 2 /* all alpha-nums but ^/ and \0$ */ -          || anc >= 5 ) /*arbitrary*/) -        return DTP_STR; - -    if (!(prop->length % 4)) /* multiple of 4 bytes, try list of hex values */ -        return DTP_HEX; - -    return DTP_UNK; -} - -/*cstd*/ -static char* dt_hex_list(uint32_t *list, unsigned long count, unsigned long tup_len) { -    char *ret, *dest; -    char buff[15] = "";  /* max element: ">, <0x00000000\0" */ -    unsigned long i, l, tc; -    l = count * 15 + 1; -    dest = ret = malloc(l); -    memset(ret, 0, l); -    if (tup_len) { -        strcpy(dest, "<"); -        dest++; -    } -    tc = 0; -    for (i = 0; i < count; i++) { -        if (tup_len) { -            if (tc == tup_len) { -                sprintf(buff, ">, <0x%x", be32toh(list[i])); -                tc = 1; -            } else { -                sprintf(buff, "%s0x%x", (i) ? " " : "", be32toh(list[i])); -                tc++; -            } -        } else -            sprintf(buff, "%s0x%x", (i) ? " " : "", be32toh(list[i])); -        l = strlen(buff); -        strncpy(dest, buff, l); -        dest += l; -    } -    if (tup_len) -        strcpy(dest, ">"); -    return ret; -} - -/*cstd*/ -static char* dt_byte_list(uint8_t *bytes, unsigned long count) { -    char *ret, *dest; -    char buff[4] = "";  /* max element: " 00\0" */ -    uint32_t v; -    unsigned long i, l; -    l = count * 4 + 1; -    ret = malloc(l); -    memset(ret, 0, l); -    strcpy(ret, "["); -    dest = ret + 1; -    for (i = 0; i < count; i++) { -        v = bytes[i]; -        sprintf(buff, "%s%02x", (i) ? " " : "", v); -        l = strlen(buff); -        strncpy(dest, buff, l); -        dest += l; -    } -    strcpy(dest, "]"); -    return ret; -} - -struct { -    const char *name; -    uint32_t def_value; -} inherited_props[] = { -    { "#address-cells", 1 }, -    { "#size-cells",    0 }, -    { "#clock-cells",   1 }, -    { "#gpio-cells",    1 }, -    { NULL, 0 }, -}; -#define INHERITED_PROPS_N 5  /* including term null */ -/* requires an un-used int i */ -#define INHERITED_PROPS_I(pnm) \ -    { i = 0; while (inherited_props[i].name != NULL) { if (strcmp(inherited_props[i].name, pnm) == 0) break; i++; } } - -/* find an inherited property by climbing the path */ -/*cstd*/ -static uint32_t dt_inh_find(dt_raw *prop, const char *inh_prop) { -    char *slash, *tmp, *parent; -    char buff[1024] = ""; -    dt_raw *tprop; -    uint32_t ret = 0; -    int found = 0, i; - -    if (prop == NULL) -        return 0; - -    parent = strdup(prop->path); -    while ( slash = strrchr(parent, '/') ) { -        *slash = 0; -        sprintf(buff, "%s/%s", parent, inh_prop); -        tprop = get_dt_raw(buff); -        if (tprop != NULL) { -            ret = be32toh(*(uint32_t*)tprop->data); -            dt_raw_free(tprop); -            found = 1; -            break; -        } -    } - -    if (!found) { -        INHERITED_PROPS_I(inh_prop); /* sets i */ -        ret = inherited_props[i].def_value; -    } - -    free(parent); -    return ret; -} - -/*cstd*/ -static int dt_tup_len(dt_raw *prop) { -    uint32_t address_cells, size_cells, -        clock_cells, gpio_cells; - -#if !(DTEX_GROUP_TUPS) -    return 0; -#endif - -    if (prop == NULL) -        return 0; - -    if DT_CHECK_NAME(prop, "reg") { -        address_cells = dt_inh_find(prop, "#address-cells"); -        size_cells = dt_inh_find(prop, "#size-cells"); -        return address_cells + size_cells; -    } - -    if DT_CHECK_NAME(prop, "gpios") { -        gpio_cells = dt_inh_find(prop, "#gpio-cells"); -        if (gpio_cells == 0) gpio_cells = 1; -        return gpio_cells; -    } - -    if DT_CHECK_NAME(prop, "clocks") { -        clock_cells = dt_inh_find(prop, "#clock-cells"); -        return 1 + clock_cells; -    } - -    return 0; -} - -/*cstd, except for g_strescape()*/ -static char* dt_str(dt_raw *prop) { -    char *tmp, *esc, *next_str; -    char ret[1024] = ""; -    unsigned long i, l, tl; -    uint32_t phandle; -    char *ph_path, *al_label; - -    if (prop == NULL) return NULL; - -    if (prop->type == DT_NODE) -        strcpy(ret, "{node}"); -    else if (prop->data == NULL) -        strcpy(ret, "{null}"); -    else if (prop->length == 0) -        strcpy(ret, "{empty}"); -    else { -        i = dt_guess_type(prop); - -#if !(DTEX_PHREFS) -        if (i == DTP_PH_REF) i = DTP_HEX; -#endif - -        if (i == DTP_STR) { -            /* treat as null-separated string list */ -            tl = 0; -            strcpy(ret, ""); -            tmp = ret; -            next_str = prop->data; -            while(next_str != NULL) { -                l = strlen(next_str); -                esc = g_strescape(next_str, NULL); -                sprintf(tmp, "%s\"%s\"", -                        strlen(ret) ? ", " : "", esc); -                free(esc); -                tmp += strlen(tmp); -                tl += l + 1; next_str += l + 1; -                if (tl >= prop->length) break; -            } -        } else if (i == DTP_INT && prop->length == 4) { -            /* still use uint32_t for the byte-order conversion */ -            sprintf(ret, "%d", be32toh(*(uint32_t*)prop->data) ); -        } else if (i == DTP_UINT && prop->length == 4) { -            sprintf(ret, "%u", be32toh(*(uint32_t*)prop->data) ); -        } else if (i == DTP_HEX && !(prop->length % 4)) { -            l = prop->length / 4; -            tmp = dt_hex_list((uint32_t*)prop->data, l, dt_tup_len(prop)); -            strcpy(ret, tmp); -            free(tmp); -        } else if (i == DTP_PH && prop->length == 4) { -            phandle = be32toh(*(uint32_t*)prop->data); -            ph_path = dt_phandle_lookup(phandle); -            al_label = dt_alias_lookup_by_path(ph_path); -            if (al_label != NULL) -                sprintf(ret, "0x%x (&%s)", phandle, al_label); -            else -                sprintf(ret, "0x%x", phandle); -        } else if (i == DTP_PH_REF && prop->length == 4) { -            phandle = be32toh(*(uint32_t*)prop->data); -            ph_path = dt_phandle_lookup(phandle); -            if (ph_path != NULL) { -                al_label = dt_alias_lookup_by_path(ph_path); -                if (al_label != NULL) -                    sprintf(ret, "&%s (%s)", al_label, ph_path); -                else -                    sprintf(ret, "0x%x (%s)", phandle, ph_path); -            } else -                sprintf(ret, "<0x%x>", phandle); -        } else { -            if (prop->length > 64) { /* maybe should use #define at the top */ -                sprintf(ret, "{data} (%lu bytes)", prop->length); -            } else { -                tmp = dt_byte_list((uint8_t*)prop->data, prop->length); -                strcpy(ret, tmp); -                free(tmp); -            } -        } -    } -    return strdup(ret); -} - -/*glib, but dt_raw *prop uses malloc() and std types */ -static dt_raw *get_dt_raw(char *p) { -    gchar *full_path; -    dt_raw *prop; -    prop = malloc(sizeof(dt_raw)); -    if (prop != NULL) { -        memset(prop, 0, sizeof(dt_raw)); -        full_path = g_strdup_printf("/proc/device-tree/%s", p); -        prop->path = strdup( p != NULL && strcmp(p, "") ? p : "/" ); -        if ( g_file_test(full_path, G_FILE_TEST_IS_DIR) ) { -            prop->type = DT_NODE; -        } else { -            prop->type = DT_PROPERTY; -            if (!g_file_get_contents(full_path, (gchar**)&prop->data, (gsize*)&prop->length, NULL)) { -                dt_raw_free(prop); -                return NULL; -            } -        } - -        /* find name after last slash, or start */ -        char *slash = strrchr(prop->path, '/'); -        if (slash != NULL) -            prop->name = strdup(slash + 1); -        else -            prop->name = strdup(prop->path); - -        return prop; -    } -    return NULL; -} - -/*cstd*/ -void dt_raw_free(dt_raw *s) { -    if (s != NULL) { -        free(s->path); -        free(s->name); -        free(s->data); -    } -    free(s); -} - -/*cstd*/ -static char *get_dt_string(char *p, int decode) { -    dt_raw *prop; -    char *ret, *rep; -    prop = get_dt_raw(p); -    if (prop != NULL) { -        if (decode) -            ret = dt_str(prop); -        else { -            ret = strdup(prop->data); -            if (ret) -                while((rep = strchr(ret, '\n'))) *rep = ' '; -        } -        dt_raw_free(prop); -        return ret; -    } -    return NULL; -} -  #include "devicetree/rpi_data.c" +dtr *dt;  gchar *dtree_info = NULL;  gchar *get_node(char *np) { -    gchar *nodes = NULL, *props = NULL, *inh_props = NULL, *ret = NULL; +    gchar *nodes = NULL, *props = NULL, *ret = NULL;      gchar *tmp = NULL, *pstr = NULL, *lstr = NULL; -    gchar *dir_path = g_strdup_printf("/proc/device-tree/%s", np); +    gchar *dir_path;      gchar *node_path;      const gchar *fn;      GDir *dir; -    dt_raw *prop; -    int inh[INHERITED_PROPS_N]; -    memset(inh, 0, sizeof(int) * INHERITED_PROPS_N); -    int i; -    uint32_t v; +    dtr_obj *node, *child;      props = g_strdup_printf("[%s]\n", _("Properties") ); -    inh_props = g_strdup_printf("[%s]\n", _("Inherited Properties") );      nodes = g_strdup_printf("[%s]\n", _("Children") ); +    node = dtr_obj_read(dt, np); +    dir_path = dtr_obj_full_path(node);      dir = g_dir_open(dir_path, 0 , NULL);      if (dir) {          while((fn = g_dir_read_name(dir)) != NULL) { -            node_path = g_strdup_printf("%s/%s", np, fn); -            prop = get_dt_raw(node_path); -            pstr = hardinfo_clean_value(dt_str(prop), 1); +            child = dtr_get_prop_obj(dt, node, fn); +            pstr = hardinfo_clean_value(dtr_str(dt, child), 1);              lstr = hardinfo_clean_label(fn, 0); -            INHERITED_PROPS_I(prop->name); inh[i] = 1; -            if (prop->type == DT_NODE) { +            if (dtr_obj_type(child) == DT_NODE) {                  tmp = g_strdup_printf("%s%s=%s\n",                      nodes, lstr, pstr);                  g_free(nodes);                  nodes = tmp; -            } else if (prop->type == DT_PROPERTY) { +            } else {                  tmp = g_strdup_printf("%s%s=%s\n",                      props, lstr, pstr);                  g_free(props);                  props = tmp;              } -            dt_raw_free(prop); +            dtr_obj_free(child);              g_free(pstr);              g_free(lstr); -            g_free(node_path);          }      }      g_dir_close(dir);      g_free(dir_path); -    prop = get_dt_raw(np); -    for (i = 0; i < INHERITED_PROPS_N - 1; i++) -        if (!inh[i]) { -            v = dt_inh_find(prop, inherited_props[i].name); -            pstr = g_strdup_printf("%u", v); -            lstr = hardinfo_clean_label(inherited_props[i].name, 0); -            tmp = g_strdup_printf("%s%s=%s\n", -                inh_props, lstr, pstr); -            g_free(inh_props); -            inh_props = tmp; -            g_free(pstr); -            g_free(lstr); -        } - -#if !(DTEX_INH_PROPS) -    g_free(inh_props); inh_props = g_strdup(""); -#endif - -    lstr = dt_alias_lookup_by_path(prop->path); +    lstr = dtr_obj_alias(node);      ret = g_strdup_printf("[%s]\n"                      "%s=%s\n"                      "%s=%s\n" -                    "%s%s%s", +                    "%s%s",                      _("Node"), -                    _("Node Path"), prop->path, +                    _("Node Path"), dtr_obj_path(node),                      _("Alias"), (lstr != NULL) ? lstr : _("(None)"), -                    props, inh_props, nodes); +                    props, nodes); -    dt_raw_free(prop); +    dtr_obj_free(node);      g_free(props); -    g_free(inh_props);      g_free(nodes);      return ret;  } +char *get_dt_string(char *path, int decode) { +    dtr_obj *obj; +    char *ret = NULL; +    if (decode) { +        obj = dtr_get_prop_obj(dt, NULL, path); +        ret = dtr_str(dt, obj); +        dtr_obj_free(obj); +    } else +        ret = dtr_get_prop_str(dt, NULL, path); +    return ret; +} +  gchar *get_summary() {      char *model = NULL, *serial = NULL, *compat = NULL, *ret = NULL; -    model = get_dt_string("model", 0); -    serial = get_dt_string("serial-number", 0); -    compat = get_dt_string("compatible", 1); + +    model = get_dt_string("/model", 0); +    serial = get_dt_string("/serial-number", 0); +    compat = get_dt_string("/compatible", 1);      /* Expand on the DT information from known machines, like RPi.       * RPi stores a revision value in /proc/cpuinfo that can be used @@ -741,26 +202,29 @@ void mi_add(const char *key, const char *value) {  }  void add_keys(char *np) { -    gchar *dir_path = g_strdup_printf("/proc/device-tree/%s", np); -    gchar *ftmp, *ntmp, *ptmp; -    gchar *n_name, *n_info; +    gchar *dir_path, *dt_path; +    gchar *ftmp, *ntmp; +    gchar *n_info;      const gchar *fn;      GDir *dir; +    dtr_obj *obj; +    /* add self */ +    obj = dtr_obj_read(dt, np); +    dt_path = dtr_obj_path(obj); +    n_info = get_node(dt_path); +    mi_add(dt_path, n_info); + +    dir_path = g_strdup_printf("%s/%s", dtr_base_path(dt), np);      dir = g_dir_open(dir_path, 0 , NULL);      if (dir) {          while((fn = g_dir_read_name(dir)) != NULL) {              ftmp = g_strdup_printf("%s/%s", dir_path, fn);              if ( g_file_test(ftmp, G_FILE_TEST_IS_DIR) ) { -                ntmp = g_strdup_printf("%s/%s", np, fn); -                ptmp = g_strdup_printf("%s/name", ntmp); -                n_name = get_dt_string(ptmp, 0); -                n_info = get_node(ntmp); -                mi_add(ntmp, n_info); -                g_free(n_name); -                g_free(n_info); -                g_free(ptmp); - +                if (strcmp(np, "/") == 0) +                    ntmp = g_strdup_printf("/%s", fn); +                else +                    ntmp = g_strdup_printf("%s/%s", np, fn);                  add_keys(ntmp);                  g_free(ntmp);              } @@ -772,19 +236,14 @@ void add_keys(char *np) {  void __scan_dtree()  { +    dt = dtr_new(NULL);      gchar *summary = get_summary(); -    gchar *root_node = get_node(""); - -    dt_read_aliases(); -    dt_map_phandles(NULL);      dtree_info = g_strdup("[Device Tree]\n");      mi_add("Summary", summary); -    mi_add("/", root_node); -    add_keys(""); +    add_keys("/");      //printf("%s\n", dtree_info); -    dt_aliases_free(); -    dt_phandles_free(); +    dtr_free(dt);  } diff --git a/modules/devices/devicetree/dt_util.c b/modules/devices/devicetree/dt_util.c new file mode 100644 index 00000000..7c713c4f --- /dev/null +++ b/modules/devices/devicetree/dt_util.c @@ -0,0 +1,569 @@ +/* + *    HardInfo - Displays System Information + *    Copyright (C) 2003-2007 Leandro A. F. Pereira <leandro@hardinfo.org> + * + *    This program is free software; you can redistribute it and/or modify + *    it under the terms of the GNU General Public License as published by + *    the Free Software Foundation, version 2. + * + *    This program 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 General Public License for more details. + * + *    You should have received a copy of the GNU General Public License + *    along with this program; if not, write to the Free Software + *    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA + */ +/* + * Device Tree support by Burt P. <pburt0@gmail.com> + * Sources: + *   http://elinux.org/Device_Tree_Usage + *   http://elinux.org/Device_Tree_Mysteries + */ +#include <unistd.h> +#include <sys/types.h> +#include <endian.h> +#include "hardinfo.h" +#include "dt_util.h" + +static struct { +    char *name; int type; +} prop_types[] = { +    { "name", DTP_STR }, +    { "compatible", DTP_STR }, +    { "model", DTP_STR }, +    { "reg", DTP_HEX }, +    { "clocks", DTP_HEX }, +    { "gpios", DTP_HEX }, +    { "phandle", DTP_PH }, +    { "interrupts", DTP_HEX }, +    { "interrupt-parent", DTP_PH_REF }, +    { "regulator-min-microvolt", DTP_UINT }, +    { "regulator-max-microvolt", DTP_UINT }, +    { "clock-frequency", DTP_UINT }, +    { NULL, 0 }, +}; + +struct _dtr_map { +    uint32_t v;  /* phandle */ +    char *label;  /* alias */ +    char *path; +    struct _dtr_map *next; +}; +typedef struct _dtr_map dtr_map; + +struct _dtr { +    dtr_map *aliases; +    dtr_map *phandles; +    char *base_path; +}; + +struct _dtr_obj { +    char *path; +    union { +        void *data; +        char *data_str; +        dt_uint *data_int; +    }; +    char *name; +    uint32_t length; +    int type; +    const char *alias; /* null until first dtr_obj_alias(). do not free */ +    dtr *dt; +}; + +dtr_map *dtr_map_add(dtr_map *map, uint32_t v, const char *label, const char *path) { +    dtr_map *it; +    dtr_map *nmap = malloc(sizeof(dtr_map)); +    memset(nmap, 0, sizeof(dtr_map)); +    nmap->v = v; + +    //printf("dtr_map_add: 0x%x: %x - %s - %s\n", map, v, label, path); + +    if (label != NULL) nmap->label = strdup(label); +    if (path != NULL) nmap->path = strdup(path); +    if (map == NULL) +        return nmap; + +    it = map; +    while(it->next != NULL) +        it = it->next; +    it->next = nmap; + +    return nmap; +} + +void dtr_map_free(dtr_map *map) { +    dtr_map *it; +    while(map != NULL) { +        it = map->next; +        free(map->label); +        free(map->path); +        free(map); +        map = it; +    } +} + +const char *dtr_phandle_lookup(dtr *s, uint32_t v) { +    dtr_map *phi = s->phandles; +    while(phi != NULL) { +        if (phi->v == v) +            return phi->path; +        phi = phi->next; +    } +    return NULL; +} + +const char *dtr_alias_lookup(dtr *s, const char* label) { +    dtr_map *ali = s->aliases; +    while(ali != NULL) { +        if (strcmp(ali->label, label) == 0) +            return ali->path; +        ali = ali->next; +    } +    return NULL; +} + +const char *dtr_alias_lookup_by_path(dtr *s, const char* path) { +    dtr_map *ali = s->aliases; +    while(ali != NULL) { +        if (strcmp(ali->path, path) == 0) +            return ali->label; +        ali = ali->next; +    } +    return NULL; +} + +void _dtr_read_aliases(dtr *); +void _dtr_map_phandles(dtr *, char *np); + +dtr *dtr_new_x(char *base_path, int fast) { +    dtr *dt = malloc(sizeof(dtr)); +    if (dt != NULL) { +        memset(dt, 0, sizeof(dtr)); +        if (base_path != NULL) +            dt->base_path = strdup(base_path); +        else +            dt->base_path = strdup(DTR_ROOT); + +        /* build alias and phandle lists */ +        dt->aliases = NULL; +        dt->phandles = NULL; +        if (!fast) { +            _dtr_read_aliases(dt); +            _dtr_map_phandles(dt, ""); +        } +    } +    return dt; +} + +dtr *dtr_new(char *base_path) { +    return dtr_new_x(base_path, 0); +} + +void dtr_free(dtr *s) { +    if (s != NULL) { +        dtr_map_free(s->aliases); +        dtr_map_free(s->phandles); +        free(s->base_path); +        free(s); +    } +} + +const char *dtr_base_path(dtr *s) { +    if (s) +        return s->base_path; +    return NULL; +} + +/*glib, but _dt_obj *prop uses malloc() and std types */ +dtr_obj *dtr_obj_read(dtr *s, const char *dtp) { +    char *full_path; +    char *slash; +    dtr_obj *obj; + +    if (dtp == NULL) +        return NULL; + +    obj = malloc(sizeof(dtr_obj)); +    if (obj != NULL) { +        memset(obj, 0, sizeof(dtr_obj)); +        obj->dt = s; +        if (*dtp != '/') { +            obj->path = (char*)dtr_alias_lookup(s, dtp); +            if (obj->path != NULL) +                obj->path = strdup(obj->path); +            else { +                // obj->path = strdup( (dtp != NULL && strcmp(dtp, "")) ? dtp : "/" ); +                dtr_obj_free(obj); +                return NULL; +            } +        } else +            obj->path = strdup(dtp); + +        full_path = g_strdup_printf("%s%s", s->base_path, obj->path); + +        if ( g_file_test(full_path, G_FILE_TEST_IS_DIR) ) { +            obj->type = DT_NODE; +        } else { +            obj->type = DTP_UNK; +            if (!g_file_get_contents(full_path, (gchar**)&obj->data, (gsize*)&obj->length, NULL)) { +                dtr_obj_free(obj); +                g_free(full_path); +                return NULL; +            } +        } +        g_free(full_path); + +        /* find name after last slash, or start */ +        slash = strrchr(obj->path, '/'); +        if (slash != NULL) +            obj->name = strdup(slash + 1); +        else +            obj->name = strdup(obj->path); + +        if (obj->type == DTP_UNK) +            obj->type = dtr_guess_type(obj); + +        return obj; +    } +    return NULL; +} + +void dtr_obj_free(dtr_obj *s) { +    if (s != NULL) { +        free(s->path); +        free(s->name); +        free(s->data); +        free(s); +    } +} + +int dtr_obj_type(dtr_obj *s) { +    if (s) +        return s->type; +    return DT_TYPE_ERR; +} + +char *dtr_obj_alias(dtr_obj *s) { +    if (s) { +        if (s->alias != NULL) +            return (char*)s->alias; + +        s->alias = dtr_alias_lookup_by_path(s->dt, s->path); +        return (char*)s->alias; +    } +    return NULL; +} + +char *dtr_obj_path(dtr_obj *s) { +    if (s) +        return s->path; +    return NULL; +} + +char *dtr_obj_full_path(dtr_obj *s) { +    if (s) { +        if (strcmp(s->path, "/") == 0) +            return g_strdup_printf("%s", s->dt->base_path); +        else +            return g_strdup_printf("%s%s", s->dt->base_path, s->path); +    } +    return NULL; +} + +dtr_obj *dtr_get_prop_obj(dtr *s, dtr_obj *node, const char *name) { +    dtr_obj *prop; +    char *ptmp; +    ptmp = g_strdup_printf("%s/%s", (node == NULL) ? "" : node->path, name); +    prop = dtr_obj_read(s, ptmp); +    g_free(ptmp); +    return prop; +} + +char *dtr_get_prop_str(dtr *s, dtr_obj *node, const char *name) { +    dtr_obj *prop; +    char *ptmp; +    char *ret = NULL; + +    ptmp = g_strdup_printf("%s/%s", (node == NULL) ? "" : node->path, name); +    prop = dtr_obj_read(s, ptmp); +    if (prop != NULL && prop->data != NULL) { +        ret = strdup(prop->data_str); +        dtr_obj_free(prop); +    } +    g_free(ptmp); +    return ret; +} + +char *dtr_get_string(const char *p) { +    dtr *dt = dtr_new_x(NULL, 1); +    char *ret; +    ret = dtr_get_prop_str(dt, NULL, p); +    dtr_free(dt); +    return ret; +} + +uint32_t dtr_get_prop_u32(dtr *s, dtr_obj *node, const char *name) { +    dtr_obj *prop; +    char *ptmp; +    uint32_t ret = 0; + +    ptmp = g_strdup_printf("%s/%s", (node == NULL) ? "" : node->path, name); +    prop = dtr_obj_read(s, ptmp); +    if (prop != NULL && prop->data != NULL) { +        ret = be32toh(*prop->data_int); +        dtr_obj_free(prop); +    } +    g_free(ptmp); +    return ret; +} + +int dtr_guess_type(dtr_obj *obj) { +    char *tmp, *dash; +    int i = 0, anc = 0, might_be_str = 1; + +    if (obj->length == 0) +        return DTP_EMPTY; + +    /* special #(.*)-cells names are UINT */ +    if (*obj->name == '#') { +        dash = strrchr(obj->name, '-'); +        if (dash != NULL) { +            if (strcmp(dash, "-cells") == 0) +                return DTP_UINT; +        } +    } + +    /* lookup known type */ +    while (prop_types[i].name != NULL) { +        if (strcmp(obj->name, prop_types[i].name) == 0) +            return prop_types[i].type; +        i++; +    } + +    /* maybe a string? */ +    for (i = 0; i < obj->length; i++) { +        tmp = (char*)obj->data + i; +        if ( isalnum(*tmp) ) anc++; /* count the alpha-nums */ +        if ( isprint(*tmp) || *tmp == 0 ) +            continue; +        might_be_str = 0; +        break; +    } +    if (might_be_str && +        ( anc >= obj->length - 2 /* all alpha-nums but ^/ and \0$ */ +          || anc >= 5 ) /*arbitrary*/) +        return DTP_STR; + +    /* multiple of 4 bytes, try list of hex values */ +    if (!(obj->length % 4)) +        return DTP_HEX; + +    return DTP_UNK; +} + +char *dtr_elem_phref(dtr *s, dt_uint e) { +    char *ph_path, *al_label, *ret = NULL; +    ph_path = (char*)dtr_phandle_lookup(s, be32toh(e)); +    if (ph_path != NULL) { +        al_label = (char*)dtr_alias_lookup_by_path(s, ph_path); +        if (al_label != NULL) { +            ret = g_strdup_printf("&%s", al_label); +        } +    } +    free(ph_path); +    free(al_label); +    if (ret == NULL) +        ret = dtr_elem_hex(e); +    return ret; +} + +char *dtr_elem_hex(dt_uint e) { +    return g_strdup_printf("0x%x", be32toh(e) ); +} + +char *dtr_elem_byte(uint8_t e) { +    return g_strdup_printf("%x", e); +} + +char *dtr_elem_uint(dt_uint e) { +    return g_strdup_printf("%u", be32toh(e) ); +} + +char *dtr_list_byte(uint8_t *bytes, unsigned long count) { +    char *ret, *dest; +    char buff[4] = "";  /* max element: " 00\0" */ +    uint32_t v; +    unsigned long i, l; +    l = count * 4 + 1; +    ret = malloc(l); +    memset(ret, 0, l); +    strcpy(ret, "["); +    dest = ret + 1; +    for (i = 0; i < count; i++) { +        v = bytes[i]; +        sprintf(buff, "%s%02x", (i) ? " " : "", v); +        l = strlen(buff); +        strncpy(dest, buff, l); +        dest += l; +    } +    strcpy(dest, "]"); +    return ret; +} + +char *dtr_list_hex(dt_uint *list, unsigned long count) { +    char *ret, *dest; +    char buff[12] = "";  /* max element: " 0x00000000\0" */ +    unsigned long i, l; +    l = count * 12 + 1; +    dest = ret = malloc(l); +    memset(ret, 0, l); +    for (i = 0; i < count; i++) { +        sprintf(buff, "%s0x%x", (i) ? " " : "", be32toh(list[i])); +        l = strlen(buff); +        strncpy(dest, buff, l); +        dest += l; +    } +    return ret; +} + +/*cstd, except for g_strescape()*/ +char *dtr_list_str0(const char *data, uint32_t length) { +    char *tmp, *esc, *next_str; +    char ret[1024] = ""; +    uint32_t l, tl; + +    /* treat as null-separated string list */ +    tl = 0; +    strcpy(ret, ""); +    tmp = ret; +    next_str = (char*)data; +    while(next_str != NULL) { +        l = strlen(next_str); +        esc = g_strescape(next_str, NULL); +        sprintf(tmp, "%s\"%s\"", +                strlen(ret) ? ", " : "", esc); +        free(esc); +        tmp += strlen(tmp); +        tl += l + 1; next_str += l + 1; +        if (tl >= length) break; +    } + +    return strdup(ret); +} + +char* dtr_str(dtr *s, dtr_obj *obj) { +    char *ret; +    int type; + +    if (obj == NULL) return NULL; +    type = obj->type; + +    if (type == DTP_PH_REF) { +        if (!DTEX_PHREFS || obj->length != 4) +            type = DTP_HEX; +    } + +    switch(type) { +        case DT_NODE: +            ret = strdup("{node}"); +            break; +        case DTP_EMPTY: +            ret = strdup("{empty}"); +            break; +        case DTP_STR: +            ret = dtr_list_str0(obj->data_str, obj->length); +            break; +        case DTP_PH: +        case DTP_HEX: +            if (obj->length % 4) +                ret = dtr_list_byte((uint8_t*)obj->data, obj->length); +            else +                ret = dtr_list_hex(obj->data, obj->length / 4); +            break; +        case DTP_PH_REF: +            ret = dtr_elem_phref(s, *obj->data_int); +            break; +        case DTP_UINT: +            ret = dtr_elem_uint(*obj->data_int); +            break; +        case DTP_UNK: +        default: +            if (obj->length > 64) /* maybe should use #define at the top */ +                ret = g_strdup_printf(ret, "{data} (%lu bytes)", obj->length); +            else +                ret = dtr_list_byte((uint8_t*)obj->data, obj->length); +            break; +    } + +    return ret; +} + +void _dtr_read_aliases(dtr *s) { +    gchar *dir_path; +    GDir *dir; +    const gchar *fn; +    dtr_obj *anode, *prop; +    dtr_map *al; +    anode = dtr_obj_read(s, "/aliases"); + +    dir_path = g_strdup_printf("%s/aliases", s->base_path); +    dir = g_dir_open(dir_path, 0 , NULL); +    if (dir) { +        while((fn = g_dir_read_name(dir)) != NULL) { +            prop = dtr_get_prop_obj(s, anode, fn); +            if (prop->type == DTP_STR) { +                al = dtr_map_add(s->aliases, 0, prop->name, prop->data_str); +                if (s->aliases == NULL) +                    s->aliases = al; +            } +            dtr_obj_free(prop); +        } +    } +    g_dir_close(dir); +    g_free(dir_path); +    dtr_obj_free(anode); +} + +/* TODO: rewrite */ +void _dtr_map_phandles(dtr *s, char *np) { +    gchar *dir_path; +    gchar *ftmp, *ntmp, *ptmp; +    const gchar *fn; +    GDir *dir; +    dtr_obj *prop, *ph_prop; +    dtr_map *ph; +    uint32_t phandle; + +    if (np == NULL) np = ""; +    dir_path = g_strdup_printf("%s/%s", s->base_path, np); + +    prop = dtr_obj_read(s, np); +    dir = g_dir_open(dir_path, 0 , NULL); +    if (dir) { +        while((fn = g_dir_read_name(dir)) != NULL) { +            ftmp = g_strdup_printf("%s/%s", dir_path, fn); +            if ( g_file_test(ftmp, G_FILE_TEST_IS_DIR) ) { +                ntmp = g_strdup_printf("%s/%s", np, fn); +                ptmp = g_strdup_printf("%s/phandle", ntmp); +                ph_prop = dtr_obj_read(s, ptmp); +                if (ph_prop != NULL) { +                    ph = dtr_map_add(s->phandles, be32toh(*ph_prop->data_int), NULL, ntmp); +                    if (s->phandles == NULL) +                        s->phandles = ph; +                } +                _dtr_map_phandles(s, ntmp); +                g_free(ptmp); +                g_free(ntmp); +                dtr_obj_free(ph_prop); +            } +            g_free(ftmp); +        } +    } +    g_dir_close(dir); +    dtr_obj_free(prop); +} + + | 
