summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBurt P <pburt0@gmail.com>2017-07-19 00:55:42 -0500
committerLeandro Pereira <leandro@hardinfo.org>2017-07-19 07:20:40 -0700
commit9b2982813bdb1241e2149bd48893d03e139d24bb (patch)
tree613fb1becf462956dace385c60294a8e816714f2
parentaf68d862d46e0bdc2d3d32a2fe5fba53456072cb (diff)
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 <pburt0@gmail.com>
-rw-r--r--CMakeLists.txt6
-rw-r--r--includes/dt_util.h61
-rw-r--r--modules/devices.c9
-rw-r--r--modules/devices/devicetree.c643
-rw-r--r--modules/devices/devicetree/dt_util.c569
5 files changed, 692 insertions, 596 deletions
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 <stdint.h>
+
+/* 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 <socket.h>
#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 <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);
+}
+
+