aboutsummaryrefslogtreecommitdiff
path: root/hardinfo
diff options
context:
space:
mode:
Diffstat (limited to 'hardinfo')
-rw-r--r--hardinfo/cpu_util.c225
-rw-r--r--hardinfo/cpubits.c113
-rw-r--r--hardinfo/dmi_util.c157
-rw-r--r--hardinfo/dt_util.c1040
-rw-r--r--hardinfo/info.c3
-rw-r--r--hardinfo/util.c4
6 files changed, 1539 insertions, 3 deletions
diff --git a/hardinfo/cpu_util.c b/hardinfo/cpu_util.c
new file mode 100644
index 00000000..f5bddd5c
--- /dev/null
+++ b/hardinfo/cpu_util.c
@@ -0,0 +1,225 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2006 Leandro A. F. Pereira <leandro@hardinfo.org>
+ * This file by Burt P. <pburt0@gmail.com>
+ *
+ * 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
+ */
+
+#include <string.h>
+#include "hardinfo.h"
+#include "cpu_util.h"
+
+#include "cpubits.c"
+
+#define CPU_TOPO_NULL -9877
+
+const gchar *byte_order_str() {
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+ return _("Little Endian");
+#else
+ return _("Big Endian");
+#endif
+}
+
+int processor_has_flag(gchar * strflags, gchar * strflag)
+{
+ gchar **flags;
+ gint ret = 0;
+ if (strflags == NULL || strflag == NULL)
+ return 0;
+ flags = g_strsplit(strflags, " ", 0);
+ ret = g_strv_contains((const gchar * const *)flags, strflag);
+ g_strfreev(flags);
+ return ret;
+}
+
+gchar* get_cpu_str(const gchar* file, gint cpuid) {
+ gchar *tmp0 = NULL;
+ gchar *tmp1 = NULL;
+ tmp0 = g_strdup_printf("/sys/devices/system/cpu/cpu%d/%s", cpuid, file);
+ g_file_get_contents(tmp0, &tmp1, NULL, NULL);
+ g_free(tmp0);
+ return tmp1;
+}
+
+gint get_cpu_int(const char* item, int cpuid, int null_val) {
+ gchar *fc = NULL;
+ int ret = null_val;
+ fc = get_cpu_str(item, cpuid);
+ if (fc) {
+ ret = atol(fc);
+ g_free(fc);
+ }
+ return ret;
+}
+
+/* cpubits is 32768 bits long
+ * core_ids are not unique among physical_ids
+ * hack up cpubits into 128 packs of 256 cores
+ * to make cores unique in cpubits */
+#define MAX_CORES_PER_PACK 256
+#define MAX_PACKS 128
+
+int cpu_procs_cores_threads(int *p, int *c, int *t) {
+ cpubits *threads, *cores, *packs;
+ char *tmp;
+ int i, m, pack_id, core_id;
+ g_file_get_contents("/sys/devices/system/cpu/present", &tmp, NULL, NULL);
+ if (tmp != NULL) {
+ threads = cpubits_from_str(tmp);
+ cores = cpubits_from_str("");
+ packs = cpubits_from_str("");
+ m = cpubits_max(threads);
+ for (i = 0; i <= m; i++) {
+ pack_id = get_cpu_int("topology/physical_package_id", i, CPU_TOPO_NULL);
+ core_id = get_cpu_int("topology/core_id", i, CPU_TOPO_NULL);
+ if (pack_id >= 0) { CPUBIT_SET(packs, pack_id); }
+ if (core_id >= 0) { CPUBIT_SET(cores, (pack_id * MAX_CORES_PER_PACK) + core_id ); }
+ }
+ *t = cpubits_count(threads);
+ *c = cpubits_count(cores);
+ *p = cpubits_count(packs);
+ if (!*c) *c = 1;
+ if (!*p) *p = 1;
+ free(threads);
+ free(cores);
+ free(packs);
+ free(tmp);
+ return 1;
+ } else {
+ *p = *c = *t = -1;
+ return 0;
+ }
+}
+
+cpufreq_data *cpufreq_new(gint id)
+{
+ cpufreq_data *cpufd;
+ cpufd = malloc(sizeof(cpufreq_data));
+ if (cpufd) {
+ memset(cpufd, 0, sizeof(cpufreq_data));
+ cpufd->id = id;
+ cpufreq_update(cpufd, 0);
+ }
+ return cpufd;
+}
+
+void cpufreq_update(cpufreq_data *cpufd, int cur_only)
+{
+ if (cpufd) {
+ cpufd->cpukhz_cur = get_cpu_int("cpufreq/scaling_cur_freq", cpufd->id, 0);
+ if (cur_only) return;
+ cpufd->scaling_driver = get_cpu_str("cpufreq/scaling_driver", cpufd->id);
+ cpufd->scaling_governor = get_cpu_str("cpufreq/scaling_governor", cpufd->id);
+ cpufd->transition_latency = get_cpu_int("cpufreq/cpuinfo_transition_latency", cpufd->id, 0);
+ cpufd->cpukhz_min = get_cpu_int("cpufreq/scaling_min_freq", cpufd->id, 0);
+ cpufd->cpukhz_max = get_cpu_int("cpufreq/scaling_max_freq", cpufd->id, 0);
+ if (cpufd->scaling_driver == NULL) cpufd->scaling_driver = g_strdup("(Unknown)");
+ if (cpufd->scaling_governor == NULL) cpufd->scaling_governor = g_strdup("(Unknown)");
+ }
+}
+
+void cpufreq_free(cpufreq_data *cpufd)
+{
+ if (cpufd) {
+ g_free(cpufd->scaling_driver);
+ g_free(cpufd->scaling_governor);
+ }
+ g_free(cpufd);
+}
+
+cpu_topology_data *cputopo_new(gint id)
+{
+ cpu_topology_data *cputd;
+ cputd = malloc(sizeof(cpu_topology_data));
+ if (cputd) {
+ memset(cputd, 0, sizeof(cpu_topology_data));
+ cputd->id = id;
+ cputd->socket_id = get_cpu_int("topology/physical_package_id", id, CPU_TOPO_NULL);
+ cputd->core_id = get_cpu_int("topology/core_id", id, CPU_TOPO_NULL);
+ cputd->book_id = get_cpu_int("topology/book_id", id, CPU_TOPO_NULL);
+ cputd->drawer_id = get_cpu_int("topology/drawer_id", id, CPU_TOPO_NULL);
+ }
+ return cputd;
+
+}
+
+void cputopo_free(cpu_topology_data *cputd)
+{
+ g_free(cputd);
+}
+
+gchar *cpufreq_section_str(cpufreq_data *cpufd)
+{
+ if (cpufd == NULL)
+ return g_strdup("");
+
+ if (cpufd->cpukhz_min || cpufd->cpukhz_max || cpufd->cpukhz_cur) {
+ return g_strdup_printf(
+ "[%s]\n"
+ "%s=%d %s\n"
+ "%s=%d %s\n"
+ "%s=%d %s\n"
+ "%s=%d %s\n"
+ "%s=%s\n"
+ "%s=%s\n",
+ _("Frequency Scaling"),
+ _("Minimum"), cpufd->cpukhz_min, _("kHz"),
+ _("Maximum"), cpufd->cpukhz_max, _("kHz"),
+ _("Current"), cpufd->cpukhz_cur, _("kHz"),
+ _("Transition Latency"), cpufd->transition_latency, _("ns"),
+ _("Governor"), cpufd->scaling_governor,
+ _("Driver"), cpufd->scaling_driver);
+ } else {
+ return g_strdup_printf(
+ "[%s]\n"
+ "%s=%s\n",
+ _("Frequency Scaling"),
+ _("Driver"), cpufd->scaling_driver);
+ }
+}
+
+gchar *cputopo_section_str(cpu_topology_data *cputd)
+{
+ static const char na[] = N_("(Not Available)");
+ char sock_str[64] = "", core_str[64] = "";
+ char book_str[64] = "", drawer_str[64] = "";
+
+ if (cputd == NULL)
+ return g_strdup("");
+
+ if (cputd->socket_id != CPU_TOPO_NULL && cputd->socket_id != -1)
+ sprintf(sock_str, "%s=%d\n", _("Socket"), cputd->socket_id);
+ else
+ sprintf(sock_str, "%s=%s\n", _("Socket"), na);
+
+ if (cputd->core_id != CPU_TOPO_NULL)
+ sprintf(core_str, "%s=%d\n", _("Core"), cputd->core_id);
+ else
+ sprintf(core_str, "%s=%s\n", _("Core"), na);
+
+ if (cputd->book_id != CPU_TOPO_NULL)
+ sprintf(core_str, "%s=%d\n", _("Book"), cputd->book_id);
+ if (cputd->book_id != CPU_TOPO_NULL)
+ sprintf(core_str, "%s=%d\n", _("Drawer"), cputd->drawer_id);
+
+ return g_strdup_printf(
+ "[%s]\n"
+ "%s=%d\n"
+ "%s%s%s%s",
+ _("Topology"),
+ _("ID"), cputd->id,
+ sock_str, core_str, book_str, drawer_str );
+}
diff --git a/hardinfo/cpubits.c b/hardinfo/cpubits.c
new file mode 100644
index 00000000..ba9bffc7
--- /dev/null
+++ b/hardinfo/cpubits.c
@@ -0,0 +1,113 @@
+/*
+ * rpiz - https://github.com/bp0/rpiz
+ * Copyright (C) 2017 Burt P. <pburt0@gmail.com>
+ *
+ * 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; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include <stdint.h>
+
+typedef uint32_t cpubits;
+uint32_t cpubits_count(cpubits *b);
+cpubits *cpubits_from_str(char *str);
+char *cpubits_to_str(cpubits *bits, char *str, int max_len);
+
+#define CPUBITS_SIZE 4096 /* bytes, multiple of sizeof(uint32_t) */
+#define CPUBIT_SET(BITS, BIT) (BITS[BIT/32] |= (1 << BIT%32))
+#define CPUBIT_GET(BITS, BIT) ((BITS[BIT/32] & (1 << BIT%32)) >> BIT%32)
+#define CPUBITS_CLEAR(BITS) memset(BITS, 0, CPUBITS_SIZE)
+
+uint32_t cpubits_count(cpubits *b) {
+ static const uint32_t max = CPUBITS_SIZE * 8;
+ uint32_t count = 0, i = 0;
+ while (i < max) {
+ count += CPUBIT_GET(b, i);
+ i++;
+ }
+ return count;
+}
+
+int cpubits_max(cpubits *b) {
+ int i = CPUBITS_SIZE * 8;
+ while (i >= 0) {
+ if (CPUBIT_GET(b, i))
+ break;
+ i--;
+ }
+ return i;
+}
+
+cpubits *cpubits_from_str(char *str) {
+ char *v, *nv, *hy;
+ int r0, r1;
+ cpubits *newbits = malloc(CPUBITS_SIZE);
+ if (newbits) {
+ memset(newbits, 0, CPUBITS_SIZE);
+ if (str != NULL) {
+ v = (char*)str;
+ while ( *v != 0 ) {
+ nv = strchr(v, ','); /* strchrnul() */
+ if (nv == NULL) nv = strchr(v, 0); /* equivalent */
+ hy = strchr(v, '-');
+ if (hy && hy < nv) {
+ r0 = strtol(v, NULL, 0);
+ r1 = strtol(hy + 1, NULL, 0);
+ } else {
+ r0 = r1 = strtol(v, NULL, 0);
+ }
+ for (; r0 <= r1; r0++) {
+ CPUBIT_SET(newbits, r0);
+ }
+ v = (*nv == ',') ? nv + 1 : nv;
+ }
+ }
+ }
+ return newbits;
+}
+
+char *cpubits_to_str(cpubits *bits, char *str, int max_len) {
+ static const uint32_t max = CPUBITS_SIZE * 8;
+ uint32_t i = 1, seq_start = 0, seq_last = 0, seq = 0, l = 0;
+ char buffer[65536] = "";
+ if (CPUBIT_GET(bits, 0)) {
+ seq = 1;
+ strcpy(buffer, "0");
+ }
+ while (i < max) {
+ if (CPUBIT_GET(bits, i) ) {
+ seq_last = i;
+ if (!seq) {
+ seq = 1;
+ seq_start = i;
+ l = strlen(buffer);
+ sprintf(buffer + l, "%s%d", l ? "," : "", i);
+ }
+ } else {
+ if (seq && seq_last != seq_start) {
+ l = strlen(buffer);
+ sprintf(buffer + l, "-%d", seq_last);
+ }
+ seq = 0;
+ }
+ i++;
+ }
+ if (str == NULL)
+ return strdup(buffer);
+ else {
+ strncpy(str, buffer, max_len);
+ return str;
+ }
+}
diff --git a/hardinfo/dmi_util.c b/hardinfo/dmi_util.c
new file mode 100644
index 00000000..28b2c197
--- /dev/null
+++ b/hardinfo/dmi_util.c
@@ -0,0 +1,157 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2017 Leandro A. F. Pereira <leandro@hardinfo.org>
+ * This file
+ * Copyright (C) 2017 Burt P. <pburt0@gmail.com>
+ *
+ * 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
+ */
+
+#include "hardinfo.h"
+#include "dmi_util.h"
+
+static const char *dmi_sysfs_root(void) {
+ char *candidates[] = {
+ "/sys/devices/virtual/dmi",
+ "/sys/class/dmi",
+ NULL
+ };
+ int i = 0;
+ while (candidates[i] != NULL) {
+ if(access(candidates[i], F_OK) != -1)
+ return candidates[i];
+ i++;
+ }
+ return NULL;
+}
+
+char *dmi_get_str(const char *id_str) {
+ static struct {
+ char *id;
+ char *path;
+ } tab_dmi_sysfs[] = {
+ /* dmidecode -> sysfs */
+ { "bios-release-date", "id/bios_date" },
+ { "bios-vendor", "id/bios_vendor" },
+ { "bios-version", "id/bios_version" },
+ { "baseboard-product-name", "id/board_name" },
+ { "baseboard-manufacturer", "id/board_vendor" },
+ { "baseboard-version", "id/board_version" },
+ { "baseboard-serial-number", "id/board_serial" },
+ { "baseboard-asset-tag", "id/board_asset_tag" },
+ { "system-product-name", "id/product_name" },
+ { "system-manufacturer", "id/sys_vendor" },
+ { "system-serial-number", "id/product_serial" },
+ { "system-product-family", "id/product_family" },
+ { "system-version", "id/product_version" },
+ { "system-uuid", "product_uuid" },
+ { "chassis-type", "id/chassis_type" },
+ { "chassis-serial-number", "id/chassis_serial" },
+ { "chassis-manufacturer", "id/chassis_vendor" },
+ { "chassis-version", "id/chassis_version" },
+ { "chassis-asset-tag", "id/chassis_asset_tag" },
+ { NULL, NULL }
+ };
+ const gchar *dmi_root = dmi_sysfs_root();
+ gchar *ret = NULL;
+ gchar full_path[PATH_MAX];
+ gboolean spawned;
+ gchar *out, *err;
+
+ int i = 0;
+
+ /* try sysfs first */
+ if (dmi_root) {
+ while (tab_dmi_sysfs[i].id != NULL) {
+ if (strcmp(id_str, tab_dmi_sysfs[i].id) == 0) {
+ snprintf(full_path, PATH_MAX, "%s/%s", dmi_root, tab_dmi_sysfs[i].path);
+ if (g_file_get_contents(full_path, &ret, NULL, NULL) )
+ goto dmi_str_done;
+ }
+ i++;
+ }
+ }
+
+ /* try dmidecode, but may require root */
+ snprintf(full_path, PATH_MAX, "dmidecode -s %s", id_str);
+ spawned = g_spawn_command_line_sync(full_path,
+ &out, &err, &i, NULL);
+ if (spawned) {
+ if (i == 0)
+ ret = out;
+ else
+ g_free(out);
+
+ g_free(err);
+ }
+
+dmi_str_done:
+ if (ret != NULL) {
+ ret = strend(ret, '\n');
+ ret = g_strstrip(ret);
+ /* return NULL on empty */
+ if (*ret == 0) {
+ g_free(ret);
+ ret = NULL;
+ }
+ }
+ return ret;
+}
+
+char *dmi_chassis_type_str(int chassis_type, gboolean with_val) {
+ static const char *types[] = {
+ N_("Invalid chassis type (0)"),
+ N_("Unknown chassis type"), /* 1 is "Other", but not helpful in HardInfo */
+ N_("Unknown chassis type"),
+ N_("Desktop"),
+ N_("Low-profile Desktop"),
+ N_("Pizza Box"),
+ N_("Mini Tower"),
+ N_("Tower"),
+ N_("Portable"),
+ N_("Laptop"),
+ N_("Notebook"),
+ N_("Handheld"),
+ N_("Docking Station"),
+ N_("All-in-one"),
+ N_("Subnotebook"),
+ N_("Space-saving"),
+ N_("Lunch Box"),
+ N_("Main Server Chassis"),
+ N_("Expansion Chassis"),
+ N_("Sub Chassis"),
+ N_("Bus Expansion Chassis"),
+ N_("Peripheral Chassis"),
+ N_("RAID Chassis"),
+ N_("Rack Mount Chassis"),
+ N_("Sealed-case PC"),
+ };
+
+ if (chassis_type <= 0) {
+ gchar *chassis = dmi_get_str("chassis-type");
+ if (chassis) {
+ chassis_type = atoi(chassis);
+ g_free(chassis);
+ } else
+ chassis_type = -1;
+ }
+
+ if (chassis_type >= 0 && chassis_type < G_N_ELEMENTS(types)) {
+ if (with_val)
+ return g_strdup_printf("[%d] %s", chassis_type, _(types[chassis_type]));
+
+ return g_strdup(_(types[chassis_type]));
+ }
+ return NULL;
+}
diff --git a/hardinfo/dt_util.c b/hardinfo/dt_util.c
new file mode 100644
index 00000000..9678042d
--- /dev/null
+++ b/hardinfo/dt_util.c
@@ -0,0 +1,1040 @@
+/*
+ * 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_REG },
+ { "clocks", DTP_CLOCKS },
+ { "gpios", DTP_GPIOS },
+ { "cs-gpios", DTP_GPIOS },
+ { "phandle", DTP_PH },
+ { "interrupts", DTP_INTRUPT },
+ { "interrupts-extended", DTP_INTRUPT_EX },
+ { "interrupt-parent", DTP_PH_REF },
+ { "interrupt-controller", DTP_EMPTY },
+ { "regulator-min-microvolt", DTP_UINT },
+ { "regulator-max-microvolt", DTP_UINT },
+ { "clock-frequency", DTP_UINT },
+ { "dmas", DTP_DMAS },
+ { "dma-channels", DTP_UINT },
+ { "dma-requests", DTP_UINT },
+ { NULL, 0 },
+};
+
+static struct {
+ char *name; uint32_t v;
+} default_values[] = {
+ { "#address-cells", 2 },
+ { "#size-cells", 1 },
+ { "#dma-cells", 1 },
+ { 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 *symbols;
+ dtr_map *phandles;
+ char *base_path;
+ char *log;
+};
+
+struct _dtr_obj {
+ char *path;
+ union {
+ void *data;
+ char *data_str;
+ dt_uint *data_int;
+ };
+ char *name;
+ uint32_t length;
+ int type;
+ char *prefix; /* if the name has a manufacturer's prefix or null */
+ char *np_name; /* the name without any prefix. points into .prefix or .name, do not free */
+ const char *alias; /* null until first dtr_obj_alias(). do not free */
+ const char *symbol; /* null until first dtr_obj_symbol(). 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;
+
+ 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;
+ }
+}
+
+/* simple sort for maps
+ * sv: 1 = sort by v, 0 = sort by label */
+void dtr_map_sort(dtr_map *map, int sv)
+{
+ int done = 0, cmp;
+ dtr_map *this, *next, *top, *next_top;
+ uint32_t tmp_v;
+ char *tmp_l, *tmp_p;
+ if (map == NULL) return;
+ do {
+ this = map;
+ next_top = NULL;
+ while(this != NULL) {
+ next = this->next;
+ if (this == top)
+ break;
+ if (next == NULL)
+ break;
+ if (sv)
+ cmp = (this->v > next->v) ? 1 : 0;
+ else
+ cmp = strcmp(this->label, next->label);
+ if (cmp > 0) {
+ tmp_v = this->v; this->v = next->v; next->v = tmp_v;
+ tmp_l = this->label; this->label = next->label; next->label = tmp_l;
+ tmp_p = this->path; this->path = next->path; next->path = tmp_p;
+ next_top = this;
+ }
+ this = next;
+ }
+ if (next_top == NULL)
+ done = 1;
+ else
+ top = next_top;
+ } while (!done);
+}
+
+const char *dtr_phandle_lookup(dtr *s, uint32_t v) {
+ dtr_map *phi = s->phandles;
+ /* 0 and 0xffffffff are invalid phandle values */
+ /* TODO: perhaps "INVALID" or something */
+ if (v == 0 || v == 0xffffffff)
+ return NULL;
+ 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;
+}
+
+const char *dtr_symbol_lookup_by_path(dtr *s, const char* path) {
+ dtr_map *ali = s->symbols;
+ while(ali != NULL) {
+ if (strcmp(ali->path, path) == 0)
+ return ali->label;
+ ali = ali->next;
+ }
+ return NULL;
+}
+
+void _dtr_read_aliases(dtr *);
+void _dtr_read_symbols(dtr *);
+void _dtr_map_phandles(dtr *, char *np);
+
+const char *dtr_find_device_tree_root() {
+ char *candidates[] = {
+ "/proc/device-tree",
+ "/sys/firmware/devicetree/base",
+ /* others? */
+ NULL
+ };
+ int i = 0;
+ while (candidates[i] != NULL) {
+ if(access(candidates[i], F_OK) != -1)
+ return candidates[i];
+ i++;
+ }
+ return NULL;
+}
+
+dtr *dtr_new_x(const char *base_path, int fast) {
+ dtr *dt = malloc(sizeof(dtr));
+ if (dt != NULL) {
+ memset(dt, 0, sizeof(dtr));
+ dt->log = strdup("");
+
+ if (base_path == NULL)
+ base_path = DTR_ROOT;
+
+ if (base_path != NULL)
+ dt->base_path = strdup(base_path);
+ else {
+ dtr_msg(dt, "%s", "Device Tree not found.");
+ return dt;
+ }
+
+ /* build alias and phandle lists */
+ dt->aliases = NULL;
+ dt->symbols = NULL;
+ dt->phandles = NULL;
+ if (!fast) {
+ _dtr_read_aliases(dt);
+ _dtr_read_symbols(dt);
+ _dtr_map_phandles(dt, "");
+ }
+ }
+ return dt;
+}
+
+dtr *dtr_new(const 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->symbols);
+ dtr_map_free(s->phandles);
+ free(s->base_path);
+ free(s->log);
+ free(s);
+ }
+}
+
+int dtr_was_found(dtr *s) {
+ if (s != NULL) {
+ if (s->base_path != NULL)
+ return 1;
+ }
+ return 0;
+}
+
+void dtr_msg(dtr *s, char *fmt, ...) {
+ gchar *buf, *tmp;
+ va_list args;
+
+ va_start(args, fmt);
+ buf = g_strdup_vprintf(fmt, args);
+ va_end(args);
+
+ tmp = g_strdup_printf("%s%s", s->log, buf);
+ g_free(s->log);
+ s->log = tmp;
+}
+
+char *dtr_messages(dtr *s) {
+ if (s != NULL)
+ return strdup(s->log);
+ else
+ return NULL;
+}
+
+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, *coma;
+ 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 != '/') {
+ /* doesn't start with slash, use alias */
+ obj->path = (char*)dtr_alias_lookup(s, dtp);
+ if (obj->path != NULL)
+ obj->path = strdup(obj->path);
+ else {
+ dtr_obj_free(obj);
+ return NULL;
+ }
+ } else
+ obj->path = strdup(dtp);
+
+ /* 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);
+
+ /* find manufacturer prefix */
+ obj->prefix = strdup(obj->name);
+ coma = strchr(obj->prefix, ',');
+ if (coma != NULL) {
+ /* coma -> null; .np_name starts after */
+ *coma = 0;
+ obj->np_name = coma + 1;
+ } else {
+ obj->np_name = obj->name;
+ free(obj->prefix);
+ obj->prefix = NULL;
+ }
+
+ /* read data */
+ 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 {
+ if (!g_file_get_contents(full_path, (gchar**)&obj->data, (gsize*)&obj->length, NULL)) {
+ dtr_obj_free(obj);
+ g_free(full_path);
+ return NULL;
+ }
+ obj->type = dtr_guess_type(obj);
+ }
+ g_free(full_path);
+
+ return obj;
+ }
+ return NULL;
+}
+
+void dtr_obj_free(dtr_obj *s) {
+ if (s != NULL) {
+ free(s->path);
+ free(s->name);
+ free(s->prefix);
+ 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)
+ s->alias = dtr_alias_lookup_by_path(s->dt, s->path);
+ return (char*)s->alias;
+ }
+ return NULL;
+}
+
+char *dtr_obj_symbol(dtr_obj *s) {
+ if (s) {
+ if (s->symbol == NULL)
+ s->symbol = dtr_symbol_lookup_by_path(s->dt, s->path);
+ return (char*)s->symbol;
+ }
+ 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, int decode) {
+ dtr *dt = dtr_new_x(NULL, 1);
+ dtr_obj *obj;
+ char *ret = NULL;
+ if (decode) {
+ obj = dtr_get_prop_obj(dt, NULL, p);
+ ret = dtr_str(obj);
+ dtr_obj_free(obj);
+ } else
+ 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;
+ }
+ }
+
+ /* /aliases/* and /__symbols__/* are always strings */
+ if (strncmp(obj->path, "/aliases/", strlen("/aliases/") ) == 0)
+ return DTP_STR;
+ if (strncmp(obj->path, "/__symbols__/", strlen("/__symbols__/") ) == 0)
+ return DTP_STR;
+
+ /* /__overrides__/* */
+ if (strncmp(obj->path, "/__overrides__/", strlen("/__overrides__/") ) == 0)
+ if (strcmp(obj->name, "name") != 0)
+ return DTP_OVR;
+
+ /* 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 = obj->data_str + 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, int show_path) {
+ const char *ph_path, *al_label;
+ char *ret = NULL;
+ ph_path = dtr_phandle_lookup(s, be32toh(e));
+ if (ph_path != NULL) {
+ /* TODO: alias or symbol? */
+ al_label = dtr_symbol_lookup_by_path(s, ph_path);
+ if (al_label != NULL) {
+ if (show_path)
+ ret = g_strdup_printf("&%s (%s)", al_label, ph_path);
+ else
+ ret = g_strdup_printf("&%s", al_label);
+ } else {
+ if (show_path)
+ ret = g_strdup_printf("0x%x (%s)", be32toh(e), ph_path);
+ }
+ }
+ 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_list_override(dtr_obj *obj) {
+ /* <phref, string "property:value"> ... */
+ char *ret = NULL;
+ char *ph, *str;
+ char *src;
+ int l, consumed = 0;
+ src = obj->data_str;
+ while (consumed + 5 <= obj->length) {
+ ph = dtr_elem_phref(obj->dt, *(dt_uint*)src, 1);
+ src += 4; consumed += 4;
+ l = strlen(src) + 1; /* consume the null */
+ str = dtr_list_str0(src, l);
+ ret = appf(ret, "<%s -> %s>", ph, str);
+ src += l; consumed += l;
+ free(ph);
+ free(str);
+ }
+ if (consumed < obj->length) {
+ str = dtr_list_byte(src, obj->length - consumed);
+ ret = appf(ret, "%s", str);
+ free(str);
+ }
+ return ret;
+}
+
+uint32_t dtr_get_phref_prop(dtr *s, uint32_t phandle, char *prop) {
+ const char *ph_path;
+ char *tmp;
+ uint32_t ret;
+ ph_path = dtr_phandle_lookup(s, phandle);
+ tmp = g_strdup_printf("%s/%s", ph_path, prop);
+ ret = dtr_get_prop_u32(s, NULL, tmp);
+ free(tmp);
+ return ret;
+}
+
+char *dtr_list_phref(dtr_obj *obj, char *ext_cell_prop) {
+ /* <phref, #XXX-cells> */
+ int count = obj->length / 4;
+ int i = 0, ext_cells = 0;
+ char *ph_path;
+ char *ph, *ext, *ret = NULL;
+ while (i < count) {
+ if (ext_cell_prop == NULL)
+ ext_cells = 0;
+ else
+ ext_cells = dtr_get_phref_prop(obj->dt, be32toh(obj->data_int[i]), ext_cell_prop);
+ ph = dtr_elem_phref(obj->dt, obj->data_int[i], 0); i++;
+ if (ext_cells > count - i) ext_cells = count - i;
+ ext = dtr_list_hex((obj->data_int + i), ext_cells); i+=ext_cells;
+ ret = appf(ret, "<%s%s%s>",
+ ph, (ext_cells) ? " " : "", ext);
+ g_free(ph);
+ g_free(ext);
+ }
+ return ret;
+}
+
+char *dtr_list_interrupts(dtr_obj *obj) {
+ char *ext, *ret = NULL;
+ uint32_t iparent, icells;
+ int count, i = 0;
+
+ iparent = dtr_inh_find(obj, "interrupt-parent", 0);
+ if (!iparent) {
+ dtr_msg(obj->dt, "Did not find an interrupt-parent for %s", obj->path);
+ goto intr_err;
+ }
+ icells = dtr_get_phref_prop(obj->dt, iparent, "#interrupt-cells");
+ if (!icells) {
+ dtr_msg(obj->dt, "Invalid #interrupt-cells value %d for %s", icells, obj->path);
+ goto intr_err;
+ }
+
+ count = obj->length / 4;
+ while (i < count) {
+ icells = MIN(icells, count - i);
+ ext = dtr_list_hex((obj->data_int + i), icells); i+=icells;
+ ret = appf(ret, "<%s>", ext);
+ }
+ return ret;
+
+intr_err:
+ return dtr_list_hex(obj->data_int, obj->length);
+
+}
+
+char *dtr_list_reg(dtr_obj *obj) {
+ char *tup_str, *ret = NULL;
+ uint32_t acells, scells, tup_len;
+ uint32_t tups, extra, consumed; /* extra and consumed are bytes */
+ uint32_t *next;
+
+ acells = dtr_inh_find(obj, "#address-cells", 2);
+ scells = dtr_inh_find(obj, "#size-cells", 2);
+ tup_len = acells + scells;
+ tups = obj->length / (tup_len * 4);
+ extra = obj->length % (tup_len * 4);
+ consumed = 0; /* bytes */
+
+ //printf("list reg: %s\n ... acells: %u, scells: %u, extra: %u\n", obj->path, acells, scells, extra);
+
+ if (extra) {
+ /* error: length is not a multiple of tuples */
+ dtr_msg(obj->dt, "Data length (%u) is not a multiple of (#address-cells:%u + #size-cells:%u) for %s\n",
+ obj->length, acells, scells, obj->path);
+ return dtr_list_hex(obj->data, obj->length / 4);
+ }
+
+ next = obj->data_int;
+ consumed = 0;
+ while (consumed + (tup_len * 4) <= obj->length) {
+ tup_str = dtr_list_hex(next, tup_len);
+ ret = appf(ret, "<%s>", tup_str);
+ free(tup_str);
+ consumed += (tup_len * 4);
+ next += tup_len;
+ }
+
+ //printf(" ... %s\n", ret);
+ return ret;
+}
+
+char* dtr_str(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_OVR:
+ ret = dtr_list_override(obj);
+ break;
+ case DTP_REG:
+ /* <#address-cells #size-cells> */
+ ret = dtr_list_reg(obj);
+ break;
+ case DTP_INTRUPT:
+ ret = dtr_list_interrupts(obj);
+ break;
+ case DTP_INTRUPT_EX:
+ /* <phref, #interrupt-cells"> */
+ ret = dtr_list_phref(obj, "#interrupt-cells");
+ break;
+ case DTP_CLOCKS:
+ /* <phref, #clock-cells"> */
+ ret = dtr_list_phref(obj, "#clock-cells");
+ break;
+ case DTP_GPIOS:
+ /* <phref, #gpio-cells"> */
+ ret = dtr_list_phref(obj, "#gpio-cells");
+ break;
+ case DTP_DMAS:
+ /* <phref, #dma-cells"> */
+ ret = dtr_list_phref(obj, "#dma-cells");
+ 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(obj->dt, *obj->data_int, 1);
+ 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;
+}
+
+dtr_obj *dtr_get_parent_obj(dtr_obj *obj) {
+ char *slash, *parent;
+ dtr_obj *ret = NULL;
+
+ if (obj == NULL)
+ return NULL;
+
+ parent = strdup(obj->path);
+ slash = strrchr(parent, '/');
+ if (slash != NULL) {
+ *slash = 0;
+ if (strlen(parent) > 0)
+ ret = dtr_obj_read(obj->dt, parent);
+ else
+ ret = dtr_obj_read(obj->dt, "/");
+ }
+ free(parent);
+ return ret;
+}
+
+/* find the value of a path-inherited property by climbing the path */
+int dtr_inh_find(dtr_obj *obj, char *qprop, int limit) {
+ dtr_obj *tobj, *pobj, *qobj;
+ uint32_t ret = 0;
+ int i, found = 0;
+
+ if (!limit) limit = 1000;
+
+ tobj = obj;
+ while (tobj != NULL) {
+ pobj = dtr_get_parent_obj(tobj);
+ if (tobj != obj)
+ dtr_obj_free(tobj);
+ if (!limit || pobj == NULL) break;
+ qobj = dtr_get_prop_obj(obj->dt, pobj, qprop);
+ if (qobj != NULL) {
+ ret = be32toh(*qobj->data_int);
+ found = 1;
+ dtr_obj_free(qobj);
+ break;
+ }
+ tobj = pobj;
+ limit--;
+ }
+ dtr_obj_free(pobj);
+
+ if (!found) {
+ i = 0;
+ while(default_values[i].name != NULL) {
+ if (strcmp(default_values[i].name, qprop) == 0) {
+ ret = default_values[i].v;
+ dtr_msg(obj->dt, "Using default value %d for %s in %s\n", ret, qprop, obj->path);
+ break;
+ }
+ i++;
+ }
+ }
+
+ 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) {
+ if (*prop->data_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);
+ dtr_map_sort(s->aliases, 0);
+}
+
+void _dtr_read_symbols(dtr *s) {
+ gchar *dir_path;
+ GDir *dir;
+ const gchar *fn;
+ dtr_obj *anode, *prop;
+ dtr_map *al;
+ anode = dtr_obj_read(s, "/__symbols__");
+
+ dir_path = g_strdup_printf("%s/__symbols__", 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) {
+ if (*prop->data_str == '/') {
+ al = dtr_map_add(s->symbols, 0, prop->name, prop->data_str);
+ if (s->symbols == NULL)
+ s->symbols = al;
+ }
+ }
+ dtr_obj_free(prop);
+ }
+ }
+ g_dir_close(dir);
+ g_free(dir_path);
+ dtr_obj_free(anode);
+ dtr_map_sort(s->symbols, 0);
+}
+
+/* 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);
+ dtr_map_sort(s->phandles, 1);
+}
+
+/*
+ * Maybe these should move to devicetree.c, but would have to expose
+ * struct internals.
+ */
+
+/* kvl: 0 = key is label, 1 = key is v */
+char *dtr_map_info_section(dtr *s, dtr_map *map, char *title, int kvl) {
+ gchar *tmp, *ret;
+ const gchar *sym;
+ ret = g_strdup_printf("[%s]\n", _(title));
+ dtr_map *it = map;
+ while(it != NULL) {
+ if (kvl) {
+ sym = dtr_symbol_lookup_by_path(s, it->path);
+ if (sym != NULL)
+ tmp = g_strdup_printf("%s0x%x (%s)=%s\n", ret,
+ it->v, sym, it->path);
+ else
+ tmp = g_strdup_printf("%s0x%x=%s\n", ret,
+ it->v, it->path);
+ } else
+ tmp = g_strdup_printf("%s%s=%s\n", ret,
+ it->label, it->path);
+ g_free(ret);
+ ret = tmp;
+ it = it->next;
+ }
+
+ return ret;
+}
+
+char *dtr_maps_info(dtr *s) {
+ gchar *ph_map, *al_map, *sy_map, *ret;
+
+ ph_map = dtr_map_info_section(s, s->phandles, _("phandle Map"), 1);
+ al_map = dtr_map_info_section(s, s->aliases, _("Alias Map"), 0);
+ sy_map = dtr_map_info_section(s, s->symbols, _("Symbol Map"), 0);
+ ret = g_strdup_printf("%s%s%s", ph_map, sy_map, al_map);
+ g_free(ph_map);
+ g_free(al_map);
+ g_free(sy_map);
+ return ret;
+}
+
+char *appf(char *src, char *fmt, ...) {
+ gchar *buf, *ret;
+ va_list args;
+
+ va_start(args, fmt);
+ buf = g_strdup_vprintf(fmt, args);
+ va_end(args);
+
+ if (src != NULL) {
+ ret = g_strdup_printf("%s%s%s", src, sp_sep(src), buf);
+ g_free(buf);
+ g_free(src);
+ } else
+ ret = buf;
+
+ return ret;
+}
+
diff --git a/hardinfo/info.c b/hardinfo/info.c
index ef64a420..0bbf7a07 100644
--- a/hardinfo/info.c
+++ b/hardinfo/info.c
@@ -148,7 +148,8 @@ static void flatten_group(GString *output, const struct InfoGroup *group)
{
guint i;
- g_string_append_printf(output, "[%s]\n", group->name);
+ if (group->name != NULL)
+ g_string_append_printf(output, "[%s]\n", group->name);
if (group->fields) {
for (i = 0; i < group->fields->len; i++) {
diff --git a/hardinfo/util.c b/hardinfo/util.c
index 6ddf56ad..efa19b9c 100644
--- a/hardinfo/util.c
+++ b/hardinfo/util.c
@@ -876,9 +876,9 @@ static GSList *modules_check_deps(GSList * modules)
module->name,
deps[i]);
gtk_dialog_add_buttons(GTK_DIALOG(dialog),
- GTK_STOCK_NO,
+ "_No",
GTK_RESPONSE_REJECT,
- GTK_STOCK_OPEN,
+ "_Open",
GTK_RESPONSE_ACCEPT, NULL);
if (gtk_dialog_run(GTK_DIALOG(dialog)) ==