From 9b2982813bdb1241e2149bd48893d03e139d24bb Mon Sep 17 00:00:00 2001 From: Burt P Date: Wed, 19 Jul 2017 00:55:42 -0500 Subject: device tree: reworked and cleaned up * Moved device tree functions to modules/devices/devicetree/dt_util.c * The dtr_* functions usable from outside devicetree.c, for example in get_motherboard(). Must #include "dt_util.h" * Now possible to use an alternate device tree root for testing -DOVRDTRROOT=\"/some/path\" * Alternately, pass dtr_new() an alternate base path. * Abandoned the tuple grouping and inherited properties stuff for now. Signed-off-by: Burt P --- CMakeLists.txt | 6 + includes/dt_util.h | 61 ++++ modules/devices.c | 9 +- modules/devices/devicetree.c | 643 +++-------------------------------- modules/devices/devicetree/dt_util.c | 569 +++++++++++++++++++++++++++++++ 5 files changed, 692 insertions(+), 596 deletions(-) create mode 100644 includes/dt_util.h create mode 100644 modules/devices/devicetree/dt_util.c diff --git a/CMakeLists.txt b/CMakeLists.txt index aa146e8c..765c8c89 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,7 @@ set(HARDINFO_VERSION "0.6-alpha") option(HARDINFO_NOSYNC "Disable database sync via libsoup" 1) set(OVRARCH "" CACHE STRING "Override HARDINFO_ARCH value") set(OVRCPUINFO "" CACHE STRING "Specify a different file for /proc/cpuinfo") +set(OVRDTRROOT "" CACHE STRING "Specify a different path for /proc/device-tree") SET( CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ) include(GNUInstallDirs) @@ -58,6 +59,10 @@ if (OVRCPUINFO) add_definitions(-DPROC_CPUINFO=${OVRCPUINFO}) message(STATUS "/proc/cpuinfo override: ${OVRCPUINFO}") endif() +if (OVRDTRROOT) + add_definitions(-DDTR_ROOT=${OVRDTRROOT}) + message(STATUS "/proc/device-tree override: ${OVRDTRROOT}") +endif() add_definitions(-DLOCALEDIR="${CMAKE_INSTALL_PREFIX}/share/locale") message(STATUS "LOCALEDIR = ${CMAKE_INSTALL_PREFIX}/share/locale") @@ -125,6 +130,7 @@ set(MODULE_devices_SOURCES modules/devices/devmemory.c modules/devices/dmi.c modules/devices/devicetree.c + modules/devices/devicetree/dt_util.c modules/devices/inputdevices.c modules/devices/pci.c modules/devices/printers.c diff --git a/includes/dt_util.h b/includes/dt_util.h new file mode 100644 index 00000000..e044def7 --- /dev/null +++ b/includes/dt_util.h @@ -0,0 +1,61 @@ + +#ifndef _DT_UTIL_H_ +#define _DT_UTIL_H_ + +#include + +/* some not-quite-complete stuff that can be disabled */ +#define DTEX_PHREFS 0 + +#ifndef DTR_ROOT +#define DTR_ROOT "/proc/device-tree" +#endif + +enum { + DT_TYPE_ERR, + + DT_NODE, + DTP_UNK, /* arbitrary-length byte sequence */ + DTP_EMPTY, /* empty property */ + DTP_STR, /* null-delimited list of strings */ + DTP_HEX, /* list of 32-bit values displayed in hex */ + DTP_UINT, + /* DTP_INT, */ + DTP_PH, /* phandle */ + DTP_PH_REF, /* reference to phandle */ +}; + +/* simplest, no aliases. + * use dtr_get_prop_str() for complete. */ +char* dtr_get_string(const char *p); + +typedef uint32_t dt_uint; /* big-endian */ + +typedef struct _dtr dtr; +typedef struct _dtr_obj dtr_obj; + +dtr *dtr_new(char *base_path); /* NULL for DTR_ROOT */ +void dtr_free(dtr *); +const char *dtr_base_path(dtr *); + +dtr_obj *dtr_obj_read(dtr *, const char *dtp); +void dtr_obj_free(dtr_obj *); +int dtr_obj_type(dtr_obj *); +char *dtr_obj_alias(dtr_obj *); +char *dtr_obj_path(dtr_obj *); +char *dtr_obj_full_path(dtr_obj *); + +char* dtr_str(dtr *, dtr_obj *obj); +dtr_obj *dtr_get_prop_obj(dtr *, dtr_obj *node, const char *name); +char *dtr_get_prop_str(dtr *, dtr_obj *node, const char *name); +uint32_t dtr_get_prop_u32(dtr *, dtr_obj *node, const char *name); + +static int dtr_guess_type(dtr_obj *obj); +char *dtr_elem_phref(dtr *, dt_uint e); +char *dtr_elem_hex(dt_uint e); +char *dtr_elem_byte(uint8_t e); +char *dtr_elem_uint(dt_uint e); +char *dtr_list_byte(uint8_t *bytes, unsigned long count); +char *dtr_list_hex(dt_uint *list, unsigned long count); + +#endif diff --git a/modules/devices.c b/modules/devices.c index fe430621..afa645d6 100644 --- a/modules/devices.c +++ b/modules/devices.c @@ -37,6 +37,7 @@ #include #include "devices.h" +#include "dt_util.h" gchar *callback_processors(); gchar *callback_memory(); @@ -218,12 +219,12 @@ gchar *get_motherboard(void) return g_strconcat(board_name, _(" (vendor unknown)"), NULL); else if (board_vendor && *board_vendor) return g_strconcat(board_vendor, _(" (model unknown)"), NULL); -#else +#endif + /* use device tree "model" */ - if (g_file_get_contents("/proc/device-tree/model", &board_vendor, NULL, NULL)) { + board_vendor = dtr_get_string("/model"); + if (board_vendor != NULL) return board_vendor; - } -#endif return g_strdup(_("Unknown")); } 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 #include #include -#include #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 + * + * 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. + * Sources: + * http://elinux.org/Device_Tree_Usage + * http://elinux.org/Device_Tree_Mysteries + */ +#include +#include +#include +#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); +} + + -- cgit v1.2.3