diff options
Diffstat (limited to 'modules/devices')
38 files changed, 11683 insertions, 0 deletions
diff --git a/modules/devices/alpha/processor.c b/modules/devices/alpha/processor.c new file mode 100644 index 00000000..0e625316 --- /dev/null +++ b/modules/devices/alpha/processor.c @@ -0,0 +1,94 @@ +/* + * HardInfo - Displays System Information + * Copyright (C) 2003-2006 L. A. F. Pereira <l@tia.mat.br> + * + * 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 or later. + * + * 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 "devices.h" +#include "cpu_util.h" + +GSList * +processor_scan(void) +{ + Processor *processor; + FILE *cpuinfo; + gchar buffer[128]; + long long hz = 0; + + cpuinfo = fopen(PROC_CPUINFO, "r"); + if (!cpuinfo) + return NULL; + + processor = g_new0(Processor, 1); + while (fgets(buffer, 128, cpuinfo)) { + gchar **tmp = g_strsplit(buffer, ":", 2); + + if (tmp[0] && tmp[1]) { + tmp[0] = g_strstrip(tmp[0]); + tmp[1] = g_strstrip(tmp[1]); + + get_str("cpu model", processor->model_name); + get_float("BogoMIPS", processor->bogomips); + get_str("platform string", processor->strmodel); + get_str("cycle frequency [Hz]", processor->cycle_frequency_hz_str); + + } + g_strfreev(tmp); + } + + fclose(cpuinfo); + + gchar *tmp = g_strconcat("Alpha ", processor->model_name, NULL); + g_free(processor->model_name); + processor->model_name = tmp; + + if (processor->cycle_frequency_hz_str) { + hz = atoll(processor->cycle_frequency_hz_str); + processor->cpu_mhz = hz; + processor->cpu_mhz /= 1000000; + } else + processor->cpu_mhz = 0.0f; + + return g_slist_append(NULL, processor); +} + +gchar *processor_name(GSList * processors) { + return processor_name_default(processors); +} + +gchar *processor_describe(GSList * processors) { + return processor_describe_default(processors); +} + +gchar * +processor_get_info(GSList *processors) +{ + Processor *processor = (Processor *)processors->data; + + return g_strdup_printf("[%s]\n" + "%s=%s\n" + "%s=%s\n" + "%s=%.2f %s\n" /* frequency */ + "%s=%.2f\n" /* bogomips */ + "%s=%s\n", /* byte order */ + _("Processor"), + _("Model"), processor->model_name, + _("Platform String"), processor->strmodel, + _("Frequency"), processor->cpu_mhz, _("MHz"), + _("BogoMips"), processor->bogomips, + _("Byte Order"), byte_order_str() + ); +} diff --git a/modules/devices/arm/arm_data.c b/modules/devices/arm/arm_data.c new file mode 100644 index 00000000..aece272f --- /dev/null +++ b/modules/devices/arm/arm_data.c @@ -0,0 +1,235 @@ +/* + * 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 <stdlib.h> +#include <stdio.h> +#include <string.h> +#include "arm_data.h" + +#ifndef C_ +#define C_(Ctx, String) String +#endif +#ifndef NC_ +#define NC_(Ctx, String) String +#endif + +/* sources: + * https://unix.stackexchange.com/a/43563 + * git:linux/arch/arm/kernel/setup.c + * git:linux/arch/arm64/kernel/cpuinfo.c + */ +static struct { + char *name, *meaning; +} tab_flag_meaning[] = { + /* arm/hw_cap */ + { "swp", NC_("arm-flag", /*/flag:swp*/ "SWP instruction (atomic read-modify-write)") }, + { "half", NC_("arm-flag", /*/flag:half*/ "Half-word loads and stores") }, + { "thumb", NC_("arm-flag", /*/flag:thumb*/ "Thumb (16-bit instruction set)") }, + { "26bit", NC_("arm-flag", /*/flag:26bit*/ "26-Bit Model (Processor status register folded into program counter)") }, + { "fastmult", NC_("arm-flag", /*/flag:fastmult*/ "32x32->64-bit multiplication") }, + { "fpa", NC_("arm-flag", /*/flag:fpa*/ "Floating point accelerator") }, + { "vfp", NC_("arm-flag", /*/flag:vfp*/ "VFP (early SIMD vector floating point instructions)") }, + { "edsp", NC_("arm-flag", /*/flag:edsp*/ "DSP extensions (the 'e' variant of the ARM9 CPUs, and all others above)") }, + { "java", NC_("arm-flag", /*/flag:java*/ "Jazelle (Java bytecode accelerator)") }, + { "iwmmxt", NC_("arm-flag", /*/flag:iwmmxt*/ "SIMD instructions similar to Intel MMX") }, + { "crunch", NC_("arm-flag", /*/flag:crunch*/ "MaverickCrunch coprocessor (if kernel support enabled)") }, + { "thumbee", NC_("arm-flag", /*/flag:thumbee*/ "ThumbEE") }, + { "neon", NC_("arm-flag", /*/flag:neon*/ "Advanced SIMD/NEON on AArch32") }, + { "evtstrm", NC_("arm-flag", /*/flag:evtstrm*/ "Kernel event stream using generic architected timer") }, + { "vfpv3", NC_("arm-flag", /*/flag:vfpv3*/ "VFP version 3") }, + { "vfpv3d16", NC_("arm-flag", /*/flag:vfpv3d16*/ "VFP version 3 with 16 D-registers") }, + { "vfpv4", NC_("arm-flag", /*/flag:vfpv4*/ "VFP version 4 with fast context switching") }, + { "vfpd32", NC_("arm-flag", /*/flag:vfpd32*/ "VFP with 32 D-registers") }, + { "tls", NC_("arm-flag", /*/flag:tls*/ "TLS register") }, + { "idiva", NC_("arm-flag", /*/flag:idiva*/ "SDIV and UDIV hardware division in ARM mode") }, + { "idivt", NC_("arm-flag", /*/flag:idivt*/ "SDIV and UDIV hardware division in Thumb mode") }, + { "lpae", NC_("arm-flag", /*/flag:lpae*/ "40-bit Large Physical Address Extension") }, + /* arm/hw_cap2 */ + { "pmull", NC_("arm-flag", /*/flag:pmull*/ "64x64->128-bit F2m multiplication (arch>8)") }, + { "aes", NC_("arm-flag", /*/flag:aes*/ "Crypto:AES (arch>8)") }, + { "sha1", NC_("arm-flag", /*/flag:sha1*/ "Crypto:SHA1 (arch>8)") }, + { "sha2", NC_("arm-flag", /*/flag:sha2*/ "Crypto:SHA2 (arch>8)") }, + { "crc32", NC_("arm-flag", /*/flag:crc32*/ "CRC32 checksum instructions (arch>8)") }, + /* arm64/hw_cap */ + { "fp", NULL }, + { "asimd", NC_("arm-flag", /*/flag:asimd*/ "Advanced SIMD/NEON on AArch64 (arch>8)") }, + { "atomics", NULL }, + { "fphp", NULL }, + { "asimdhp", NULL }, + { "cpuid", NULL }, + { "asimdrdm", NULL }, + { "jscvt", NULL }, + { "fcma", NULL }, + { "lrcpc", NULL }, + { NULL, NULL } +}; + +static struct { + char *code; char *name; char *more; +} tab_arm_arch[] = { + { "7", "AArch32", "AArch32 (ARMv7)" }, + { "8", "AArch64", "AArch64 (ARMv8)" }, + { "AArch32", "AArch32", "AArch32 (ARMv7)" }, + { "AArch64", "AArch64", "AArch64 (ARMv8)" }, + { NULL, NULL, NULL }, +}; + +static char all_flags[1024] = ""; + +#define APPEND_FLAG(f) strcat(all_flags, f); strcat(all_flags, " "); +const char *arm_flag_list() { + int i = 0, built = 0; + built = strlen(all_flags); + if (!built) { + while(tab_flag_meaning[i].name != NULL) { + APPEND_FLAG(tab_flag_meaning[i].name); + i++; + } + } + return all_flags; +} + +const char *arm_flag_meaning(const char *flag) { + int i = 0; + if (flag) + while(tab_flag_meaning[i].name != NULL) { + if (strcmp(tab_flag_meaning[i].name, flag) == 0) { + if (tab_flag_meaning[i].meaning != NULL) + return C_("arm-flag", tab_flag_meaning[i].meaning); + else return NULL; + } + i++; + } + return NULL; +} + +#include "util_ids.h" + +gchar *arm_ids_file = NULL; + +void find_arm_ids_file() { + if (arm_ids_file) return; + char *file_search_order[] = { + g_build_filename(g_get_user_config_dir(), "hardinfo2", "arm.ids", NULL), + g_build_filename(params.path_data, "arm.ids", NULL), + NULL + }; + int n; + for(n = 0; file_search_order[n]; n++) { + if (!arm_ids_file && !access(file_search_order[n], R_OK)) + arm_ids_file = (gchar*) auto_free_on_exit( file_search_order[n] ); + else + g_free(file_search_order[n]); + } +} + +void arm_part(const char *imp_code, const char *part_code, char **imp, char **part) { + gchar *qpath = NULL; + ids_query_result result = {}; + unsigned int i,p; + + if (!arm_ids_file) + find_arm_ids_file(); + + i = strtol(imp_code, NULL, 0); + p = strtol(part_code, NULL, 0); + qpath = g_strdup_printf("%02x/%03x", i, p); + scan_ids_file(arm_ids_file, qpath, &result, -1); + g_free(qpath); + if (imp) + *imp = result.results[0] + ? g_strdup(result.results[0]) + : NULL; + if (part) + *part = result.results[1] + ? g_strdup(result.results[1]) + : NULL; +} + +const char *arm_arch(const char *cpuinfo_arch_str) { + int i = 0; + if (cpuinfo_arch_str) + while(tab_arm_arch[i].code) { + if (strcmp(tab_arm_arch[i].code, cpuinfo_arch_str) == 0) + return tab_arm_arch[i].name; + i++; + } + return cpuinfo_arch_str; +} + +const char *arm_arch_more(const char *cpuinfo_arch_str) { + int i = 0; + if (cpuinfo_arch_str) + while(tab_arm_arch[i].code) { + if (strcmp(tab_arm_arch[i].code, cpuinfo_arch_str) == 0) + return tab_arm_arch[i].more; + i++; + } + return cpuinfo_arch_str; +} + +char *arm_decoded_name(const char *imp, const char *part, const char *var, const char *rev, const char *arch, const char *model_name) { + char *dnbuff; + char *imp_name = NULL, *part_desc = NULL, *arch_name = NULL; + int r = 0, p = 0; + dnbuff = malloc(256); + if (dnbuff) { + memset(dnbuff, 0, 256); + + if (imp && arch && part && rev) { + /* http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0395b/CIHCAGHH.html + * variant and revision can be rendered r{variant}p{revision} */ + r = strtol(var, NULL, 0); + p = strtol(rev, NULL, 0); + arm_part(imp, part, &imp_name, &part_desc); + arch_name = (char*) arm_arch(arch); + if (imp_name || part_desc) { + if (arch_name != arch) + sprintf(dnbuff, "%s %s r%dp%d (%s)", + (imp_name) ? imp_name : imp, + (part_desc) ? part_desc : part, + r, p, arch_name); + else + sprintf(dnbuff, "%s %s r%dp%d (arch:%s)", + (imp_name) ? imp_name : imp, + (part_desc) ? part_desc : part, + r, p, arch); + } else { + /* fallback for now */ + sprintf(dnbuff, "%s [imp:%s part:%s r%dp%d arch:%s]", + model_name, + (imp_name) ? imp_name : imp, + (part_desc) ? part_desc : part, + r, p, arch); + } + g_free(imp_name); + g_free(part_desc); + } else { + /* prolly not ARM arch at all */ + if (model_name) + sprintf(dnbuff, "%s", model_name); + else { + free(dnbuff); + return NULL; + } + } + } + return dnbuff; +} diff --git a/modules/devices/arm/arm_data.h b/modules/devices/arm/arm_data.h new file mode 100644 index 00000000..0e93d323 --- /dev/null +++ b/modules/devices/arm/arm_data.h @@ -0,0 +1,39 @@ +/* + * 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. + * + */ + +#ifndef _ARMDATA_H_ +#define _ARMDATA_H_ + +/* table lookups */ +void arm_part(const char *imp_code, const char *part_code, char **imp, char **part); +const char *arm_arch(const char *cpuinfo_arch_str); +const char *arm_arch_more(const char *cpuinfo_arch_str); + +/* cpu_implementer, cpu_part, cpu_variant, cpu_revision, cpu_architecture from /proc/cpuinfo + * strdup(model_name) is returned as a fallback if not enough data is known */ +char *arm_decoded_name( + const char *imp, const char *part, const char *var, const char *rev, + const char *arch, const char *model_name); + +/* cpu flags from /proc/cpuinfo */ +const char *arm_flag_list(void); /* list of all known flags */ +const char *arm_flag_meaning(const char *flag); /* lookup flag meaning */ + +#endif diff --git a/modules/devices/arm/processor.c b/modules/devices/arm/processor.c new file mode 100644 index 00000000..9446108d --- /dev/null +++ b/modules/devices/arm/processor.c @@ -0,0 +1,515 @@ +/* + * HardInfo - Displays System Information + * Copyright (C) 2003-2006 L. A. F. Pereira <l@tia.mat.br> + * + * 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 or later. + * + * 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 "devices.h" +#include "cpu_util.h" +#include "dt_util.h" + +#include "arm_data.h" +#include "arm_data.c" + +enum { + ARM_A32 = 0, + ARM_A64 = 1, + ARM_A32_ON_A64 = 2, +}; + +static const gchar *arm_mode_str[] = { + "A32", + "A64", + "A32 on A64", +}; + +GSList * +processor_scan(void) +{ + GSList *procs = NULL; + Processor *processor = NULL; + FILE *cpuinfo; + gchar buffer[128]; + gchar *rep_pname = NULL; + GSList *pi = NULL; + dtr *dt = dtr_new(NULL); + + cpuinfo = fopen(PROC_CPUINFO, "r"); + if (!cpuinfo) + return NULL; + +#define CHECK_FOR(k) (g_str_has_prefix(tmp[0], k)) + while (fgets(buffer, 128, cpuinfo)) { + gchar **tmp = g_strsplit(buffer, ":", 2); + if (tmp[0] && tmp[1]) { + tmp[0] = g_strstrip(tmp[0]); + tmp[1] = g_strstrip(tmp[1]); + } else { + g_strfreev(tmp); + continue; + } + + get_str("Processor", rep_pname); + + if ( CHECK_FOR("processor") ) { + /* finish previous */ + if (processor) { + procs = g_slist_append(procs, processor); + } + + /* start next */ + processor = g_new0(Processor, 1); + processor->id = atol(tmp[1]); + + if (rep_pname) + processor->linux_name = g_strdup(rep_pname); + + g_strfreev(tmp); + continue; + } + + if (!processor && + ( CHECK_FOR("model name") + || CHECK_FOR("Features") + || CHECK_FOR("BogoMIPS") ) ) { + + /* single proc/core may not have "processor : n" */ + processor = g_new0(Processor, 1); + processor->id = 0; + + if (rep_pname) + processor->linux_name = g_strdup(rep_pname); + } + + if (processor) { + get_str("model name", processor->linux_name); + get_str("Features", processor->flags); + get_float("BogoMIPS", processor->bogomips); + + get_str("CPU implementer", processor->cpu_implementer); + get_str("CPU architecture", processor->cpu_architecture); + get_str("CPU variant", processor->cpu_variant); + get_str("CPU part", processor->cpu_part); + get_str("CPU revision", processor->cpu_revision); + } + g_strfreev(tmp); + } + + if (processor) + procs = g_slist_append(procs, processor); + + g_free(rep_pname); + fclose(cpuinfo); + + /* re-duplicate missing data for /proc/cpuinfo variant that de-duplicated it */ +#define REDUP(f) if (dproc->f && !processor->f) processor->f = g_strdup(dproc->f); + Processor *dproc; + GSList *l; + l = procs = g_slist_reverse(procs); + while (l) { + processor = l->data; + if (processor->flags) { + dproc = processor; + } else if (dproc) { + REDUP(flags); + REDUP(cpu_implementer); + REDUP(cpu_architecture); + REDUP(cpu_variant); + REDUP(cpu_part); + REDUP(cpu_revision); + } + l = g_slist_next(l); + } + procs = g_slist_reverse(procs); + + /* data not from /proc/cpuinfo */ + for (pi = procs; pi; pi = pi->next) { + processor = (Processor *) pi->data; + + /* strings can't be null or segfault later */ + STRIFNULL(processor->linux_name, _("ARM Processor") ); + EMPIFNULL(processor->flags); + UNKIFNULL(processor->cpu_implementer); + UNKIFNULL(processor->cpu_architecture); + UNKIFNULL(processor->cpu_variant); + UNKIFNULL(processor->cpu_part); + UNKIFNULL(processor->cpu_revision); + + processor->model_name = arm_decoded_name( + processor->cpu_implementer, processor->cpu_part, + processor->cpu_variant, processor->cpu_revision, + processor->cpu_architecture, processor->linux_name); + UNKIFNULL(processor->model_name); + + /* topo & freq */ + processor->cpufreq = cpufreq_new(processor->id); + processor->cputopo = cputopo_new(processor->id); + + if (processor->cpufreq->cpukhz_max) + processor->cpu_mhz = processor->cpufreq->cpukhz_max / 1000; + else + processor->cpu_mhz = 0.0f; + + /* Try OPP, although if it exists, it should have been available + * via cpufreq. */ + if (dt && processor->cpu_mhz == 0.0f) { + gchar *dt_cpu_path = g_strdup_printf("/cpus/cpu@%d", processor->id); + dt_opp_range *opp = dtr_get_opp_range(dt, dt_cpu_path); + if (opp) { + processor->cpu_mhz = (double)opp->khz_max / 1000; + g_free(opp); + } + g_free(dt_cpu_path); + } + + /* mode */ + processor->mode = ARM_A32; + if ( processor_has_flag(processor->flags, "pmull") + || processor_has_flag(processor->flags, "crc32") ) { +#ifdef __aarch64__ + processor->mode = ARM_A64; +#else + processor->mode = ARM_A32_ON_A64; +#endif + } + } + dtr_free(dt); + + return procs; +} + +gchar *processor_get_capabilities_from_flags(gchar * strflags) +{ + gchar **flags, **old; + gchar *tmp = NULL; + gint j = 0; + + flags = g_strsplit(strflags, " ", 0); + old = flags; + + while (flags[j]) { + const gchar *meaning = arm_flag_meaning( flags[j] ); + + if (meaning) { + tmp = h_strdup_cprintf("%s=%s\n", tmp, flags[j], meaning); + } else { + tmp = h_strdup_cprintf("%s=\n", tmp, flags[j]); + } + j++; + } + if (tmp == NULL || g_strcmp0(tmp, "") == 0) + tmp = g_strdup_printf("%s=%s\n", "empty", _("Empty List")); + + g_strfreev(old); + return tmp; +} + +#define khzint_to_mhzdouble(k) (((double)k)/1000) +#define cmp_clocks_test(f) if (a->f < b->f) return -1; if (a->f > b->f) return 1; + +static gint cmp_cpufreq_data(cpufreq_data *a, cpufreq_data *b) { + gint i = 0; + i = g_strcmp0(a->shared_list, b->shared_list); if (i!=0) return i; + cmp_clocks_test(cpukhz_max); + cmp_clocks_test(cpukhz_min); + return 0; +} + +static gint cmp_cpufreq_data_ignore_affected(cpufreq_data *a, cpufreq_data *b) { + gint i = 0; + cmp_clocks_test(cpukhz_max); + cmp_clocks_test(cpukhz_min); + return 0; +} + +gchar *clocks_summary(GSList * processors) +{ + gchar *ret = g_strdup_printf("[%s]\n", _("Clocks")); + GSList *all_clocks = NULL, *uniq_clocks = NULL; + GSList *tmp, *l; + Processor *p; + cpufreq_data *c, *cur = NULL; + gint cur_count = 0, i = 0; + + /* create list of all clock references */ + for (l = processors; l; l = l->next) { + p = (Processor*)l->data; + if (p->cpufreq && p->cpufreq->cpukhz_max > 0) { + all_clocks = g_slist_prepend(all_clocks, p->cpufreq); + } + } + + if (g_slist_length(all_clocks) == 0) { + ret = h_strdup_cprintf("%s=\n", ret, _("(Not Available)") ); + g_slist_free(all_clocks); + return ret; + } + + /* ignore duplicate references */ + all_clocks = g_slist_sort(all_clocks, (GCompareFunc)cmp_cpufreq_data); + for (l = all_clocks; l; l = l->next) { + c = (cpufreq_data*)l->data; + if (!cur) { + cur = c; + } else { + if (cmp_cpufreq_data(cur, c) != 0) { + uniq_clocks = g_slist_prepend(uniq_clocks, cur); + cur = c; + } + } + } + uniq_clocks = g_slist_prepend(uniq_clocks, cur); + uniq_clocks = g_slist_reverse(uniq_clocks); + cur = 0, cur_count = 0; + + /* count and list clocks */ + for (l = uniq_clocks; l; l = l->next) { + c = (cpufreq_data*)l->data; + if (!cur) { + cur = c; + cur_count = 1; + } else { + if (cmp_cpufreq_data_ignore_affected(cur, c) != 0) { + ret = h_strdup_cprintf(_("%.2f-%.2f %s=%dx\n"), + ret, + khzint_to_mhzdouble(cur->cpukhz_min), + khzint_to_mhzdouble(cur->cpukhz_max), + _("MHz"), + cur_count); + cur = c; + cur_count = 1; + } else { + cur_count++; + } + } + } + ret = h_strdup_cprintf(_("%.2f-%.2f %s=%dx\n"), + ret, + khzint_to_mhzdouble(cur->cpukhz_min), + khzint_to_mhzdouble(cur->cpukhz_max), + _("MHz"), + cur_count); + + g_slist_free(all_clocks); + g_slist_free(uniq_clocks); + return ret; +} + +gchar * +processor_get_detailed_info(Processor *processor) +{ + gchar *tmp_flags, *tmp_imp = NULL, *tmp_part = NULL, + *tmp_arch, *tmp_cpufreq, *tmp_topology, *ret; + tmp_flags = processor_get_capabilities_from_flags(processor->flags); + arm_part(processor->cpu_implementer, processor->cpu_part, &tmp_imp, &tmp_part); + tmp_arch = (char*)arm_arch_more(processor->cpu_architecture); + + tmp_topology = cputopo_section_str(processor->cputopo); + tmp_cpufreq = cpufreq_section_str(processor->cpufreq); + + ret = g_strdup_printf("[%s]\n" + "%s=%s\n" /* linux name */ + "%s=%s\n" /* decoded name */ + "%s=%s\n" /* mode */ + "%s=%.2f %s\n" /* frequency */ + "%s=%.2f\n" /* bogomips */ + "%s=%s\n" /* byte order */ + "%s" /* topology */ + "%s" /* frequency scaling */ + "[%s]\n" /* ARM */ + "%s=[%s] %s\n" /* implementer */ + "%s=[%s] %s\n" /* part */ + "%s=[%s] %s\n" /* architecture */ + "%s=%s\n" /* variant */ + "%s=%s\n" /* revision */ + "[%s]\n" /* flags */ + "%s" + "%s", /* empty */ + _("Processor"), + _("Linux Name"), processor->linux_name, + _("Decoded Name"), processor->model_name, + _("Mode"), arm_mode_str[processor->mode], + _("Frequency"), processor->cpu_mhz, _("MHz"), + _("BogoMips"), processor->bogomips, + _("Byte Order"), byte_order_str(), + tmp_topology, + tmp_cpufreq, + _("ARM"), + _("Implementer"), processor->cpu_implementer, (tmp_imp) ? tmp_imp : "", + _("Part"), processor->cpu_part, (tmp_part) ? tmp_part : "", + _("Architecture"), processor->cpu_architecture, (tmp_arch) ? tmp_arch : "", + _("Variant"), processor->cpu_variant, + _("Revision"), processor->cpu_revision, + _("Capabilities"), tmp_flags, + ""); + g_free(tmp_flags); + g_free(tmp_cpufreq); + g_free(tmp_topology); + return ret; +} + +gchar *processor_name(GSList *processors) { + /* compatible contains a list of compatible hardware, so be careful + * with matching order. + * ex: "ti,omap3-beagleboard-xm", "ti,omap3450", "ti,omap3"; + * matches "omap3 family" first. + * ex: "brcm,bcm2837", "brcm,bcm2836"; + * would match 2836 when it is a 2837. + */ +#define UNKSOC "(Unknown)" /* don't translate this */ + const struct { + char *search_str; + char *vendor; + char *soc; + } dt_compat_searches[] = { + { "brcm,bcm2838", "Broadcom", "BCM2838" }, // RPi 4 + { "brcm,bcm2837", "Broadcom", "BCM2837" }, // RPi 3 + { "brcm,bcm2836", "Broadcom", "BCM2836" }, // RPi 2 + { "brcm,bcm2835", "Broadcom", "BCM2835" }, // RPi 1 + { "rockchip,rk3288", "Rockchip", "RK3288" }, // Asus Tinkerboard + { "rockchip,rk3328", "Rockchip", "RK3328" }, // Firefly Renegade + { "rockchip,rk3399", "Rockchip", "RK3399" }, // Firefly Renegade Elite + { "rockchip,rk32", "Rockchip", "RK32xx-family" }, + { "rockchip,rk33", "Rockchip", "RK33xx-family" }, + { "ti,omap5432", "Texas Instruments", "OMAP5432" }, + { "ti,omap5430", "Texas Instruments", "OMAP5430" }, + { "ti,omap4470", "Texas Instruments", "OMAP4470" }, + { "ti,omap4460", "Texas Instruments", "OMAP4460" }, + { "ti,omap4430", "Texas Instruments", "OMAP4430" }, + { "ti,omap3620", "Texas Instruments", "OMAP3620" }, + { "ti,omap3450", "Texas Instruments", "OMAP3450" }, + { "ti,omap5", "Texas Instruments", "OMAP5-family" }, + { "ti,omap4", "Texas Instruments", "OMAP4-family" }, + { "ti,omap3", "Texas Instruments", "OMAP3-family" }, + { "ti,omap2", "Texas Instruments", "OMAP2-family" }, + { "ti,omap1", "Texas Instruments", "OMAP1-family" }, + { "mediatek,mt6799", "MediaTek", "MT6799 Helio X30" }, + { "mediatek,mt6799", "MediaTek", "MT6799 Helio X30" }, + { "mediatek,mt6797x", "MediaTek", "MT6797X Helio X27" }, + { "mediatek,mt6797t", "MediaTek", "MT6797T Helio X25" }, + { "mediatek,mt6797", "MediaTek", "MT6797 Helio X20" }, + { "mediatek,mt6757T", "MediaTek", "MT6757T Helio P25" }, + { "mediatek,mt6757", "MediaTek", "MT6757 Helio P20" }, + { "mediatek,mt6795", "MediaTek", "MT6795 Helio X10" }, + { "mediatek,mt6755", "MediaTek", "MT6755 Helio P10" }, + { "mediatek,mt6750t", "MediaTek", "MT6750T" }, + { "mediatek,mt6750", "MediaTek", "MT6750" }, + { "mediatek,mt6753", "MediaTek", "MT6753" }, + { "mediatek,mt6752", "MediaTek", "MT6752" }, + { "mediatek,mt6738", "MediaTek", "MT6738" }, + { "mediatek,mt6737t", "MediaTek", "MT6737T" }, + { "mediatek,mt6735", "MediaTek", "MT6735" }, + { "mediatek,mt6732", "MediaTek", "MT6732" }, + { "qcom,msm8939", "Qualcomm", "Snapdragon 615"}, + { "qcom,msm", "Qualcomm", "Snapdragon-family"}, + { "nvidia,tegra", "nVidia", "Tegra-family" }, + { "brcm,", "Broadcom", UNKSOC }, + { "nvidia,", "nVidia", UNKSOC }, + { "rockchip,", "Rockchip", UNKSOC }, + { "ti,", "Texas Instruments", UNKSOC }, + { "qcom,", "Qualcom", UNKSOC }, + { "mediatek,", "MediaTek", UNKSOC }, + { "amlogic,", "Amlogic", UNKSOC }, + { "allwinner,", "Allwinner", UNKSOC }, + { NULL, NULL } + }; + gchar *ret = NULL; + gchar *compat = NULL; + int i; + + compat = dtr_get_string("/compatible", 1); + + if (compat != NULL) { + i = 0; + while(dt_compat_searches[i].search_str != NULL) { + if (strstr(compat, dt_compat_searches[i].search_str) != NULL) { + ret = g_strdup_printf("%s %s", dt_compat_searches[i].vendor, dt_compat_searches[i].soc); + break; + } + i++; + } + } + g_free(compat); + UNKIFNULL(ret); + return ret; +} + +gchar *processor_describe(GSList * processors) { + return processor_describe_by_counting_names(processors); +} + +gchar *processor_meta(GSList * processors) { + gchar *meta_soc = processor_name(processors); + gchar *meta_cpu_desc = processor_describe(processors); + gchar *meta_cpu_topo = processor_describe_default(processors); + gchar *meta_freq_desc = processor_frequency_desc(processors); + gchar *meta_clocks = clocks_summary(processors); + gchar *ret = NULL; + UNKIFNULL(meta_cpu_desc); + ret = g_strdup_printf("[%s]\n" + "%s=%s\n" + "%s=%s\n" + "%s=%s\n" + "%s=%s\n" + "%s", + _("SOC/Package"), + _("Name"), meta_soc, + _("Description"), meta_cpu_desc, + _("Topology"), meta_cpu_topo, + _("Logical CPU Config"), meta_freq_desc, + meta_clocks ); + g_free(meta_soc); + g_free(meta_cpu_desc); + g_free(meta_cpu_topo); + g_free(meta_freq_desc); + g_free(meta_clocks); + return ret; +} + +gchar *processor_get_info(GSList * processors) +{ + Processor *processor; + gchar *ret, *tmp, *hashkey; + gchar *meta; /* becomes owned by more_info? no need to free? */ + GSList *l; + + tmp = g_strdup_printf("$!CPU_META$%s=\n", _("SOC/Package Information") ); + + meta = processor_meta(processors); + moreinfo_add_with_prefix("DEV", "CPU_META", meta); + + for (l = processors; l; l = l->next) { + processor = (Processor *) l->data; + + tmp = g_strdup_printf("%s$CPU%d$%s=%.2f %s\n", + tmp, processor->id, + processor->model_name, + processor->cpu_mhz, _("MHz")); + + hashkey = g_strdup_printf("CPU%d", processor->id); + moreinfo_add_with_prefix("DEV", hashkey, + processor_get_detailed_info(processor)); + g_free(hashkey); + } + + ret = g_strdup_printf("[$ShellParam$]\n" + "ViewType=1\n" + "[Processors]\n" + "%s", tmp); + g_free(tmp); + + return ret; +} diff --git a/modules/devices/battery.c b/modules/devices/battery.c new file mode 100644 index 00000000..e356c14a --- /dev/null +++ b/modules/devices/battery.c @@ -0,0 +1,385 @@ +/* + * HardInfo - Displays System Information + * Copyright (C) 2003-2006 L. A. F. Pereira <l@tia.mat.br> + * + * 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 or later. + * + * 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 <time.h> + +#include "hardinfo.h" +#include "devices.h" + +const struct { + gchar *key, *name; +} ups_fields[] = { + { "UPS Status", NULL }, + { "STATUS", "Status" }, + { "TIMELEFT", "Time Left" }, + { "LINEV", "Line Voltage" }, + { "LOADPCT", "Load Percent" }, + + { "UPS Battery Information", NULL }, + { "BATTV", "Battery Voltage" }, + { "BCHARGE", "Battery Charge" }, + { "BATTDATE", "Battery Date" }, + + { "UPS Information", NULL }, + { "APCMODEL", "Model" }, + { "FIRMWARE", "Firmware Version" }, + { "SERIALNO", "Serial Number" }, + { "UPSMODE", "UPS Mode" }, + { "CABLE", "Cable" }, + { "UPSNAME", "UPS Name" }, + + { "UPS Nominal Values", NULL }, + { "NOMINV", "Voltage" }, + { "NOMBATTV", "Battery Voltage" }, + { "NOMPOWER", "Power" } +}; + + +static void +__scan_battery_apcupsd(void) +{ + GHashTable *ups_data; + FILE *apcaccess; + char buffer[512], *apcaccess_path; + guint i; + + apcaccess_path = find_program("apcaccess"); + if (apcaccess_path && (apcaccess = popen(apcaccess_path, "r"))) { + /* first line isn't important */ + if (fgets(buffer, 512, apcaccess)) { + /* allocate the key, value hash table */ + ups_data = g_hash_table_new(g_str_hash, g_str_equal); + + /* read up all the apcaccess' output, saving it in the key, value hash table */ + while (fgets(buffer, 512, apcaccess)) { + buffer[9] = '\0'; + + g_hash_table_insert(ups_data, + g_strdup(g_strstrip(buffer)), + g_strdup(g_strstrip(buffer + 10))); + } + + /* builds the ups info string, respecting the field order as found in ups_fields */ + for (i = 0; i < G_N_ELEMENTS(ups_fields); i++) { + if (!ups_fields[i].name) { + /* there's no name: make a group with the key as its name */ + battery_list = h_strdup_cprintf("[%s]\n", battery_list, ups_fields[i].key); + } else { + /* there's a name: adds a line */ + const gchar *name = g_hash_table_lookup(ups_data, ups_fields[i].key); + battery_list = h_strdup_cprintf("%s=%s\n", battery_list, + ups_fields[i].name, name); + } + } + + g_hash_table_destroy(ups_data); + } + + pclose(apcaccess); + } + + g_free(apcaccess_path); +} + +static void +__scan_battery_acpi(void) +{ + gchar *acpi_path; + + gchar *present = NULL; + gchar *capacity = NULL; + gchar *technology = NULL; + gchar *voltage = NULL; + gchar *model = NULL, *serial = NULL, *type = NULL; + gchar *state = NULL, *rate = NULL; + gchar *remaining = NULL; + gchar *manufacturer = NULL; + + acpi_path = g_strdup("/proc/acpi/battery"); + if (g_file_test(acpi_path, G_FILE_TEST_EXISTS)) { + GDir *acpi; + + if ((acpi = g_dir_open(acpi_path, 0, NULL))) { + const gchar *entry; + + while ((entry = g_dir_read_name(acpi))) { + gchar *path = g_strdup_printf("%s/%s/info", acpi_path, entry); + FILE *f; + gchar buffer[256]; + gdouble charge_rate = 1.0; + + f = fopen(path, "r"); + g_free(path); + + if (!f) + goto cleanup; + + while (fgets(buffer, 256, f)) { + gchar **tmp = g_strsplit(buffer, ":", 2); + + GET_STR("present", present); + GET_STR("design capacity", capacity); + GET_STR("battery technology", technology); + GET_STR("design voltage", voltage); + GET_STR("model number", model); + GET_STR("serial number", serial); + GET_STR("battery type", type); + GET_STR("OEM info", manufacturer); + + g_strfreev(tmp); + } + fclose(f); + + path = g_strdup_printf("%s/%s/state", acpi_path, entry); + f = fopen(path, "r"); + g_free(path); + + if (!f) + goto cleanup; + + while (fgets(buffer, 256, f)) { + gchar **tmp = g_strsplit(buffer, ":", 2); + + GET_STR("charging state", state); + GET_STR("present rate", rate); + GET_STR("remaining capacity", remaining); + + g_strfreev(tmp); + } + + fclose(f); + + gchar *tmp = vendor_get_link(manufacturer); + g_free(manufacturer); + manufacturer = tmp; + + if (g_str_equal(present, "yes")) { + if (remaining && capacity) + charge_rate = atof(remaining) / atof(capacity); + else + charge_rate = 0; + + battery_list = h_strdup_cprintf(_("\n[Battery: %s]\n" + "State=%s (load: %s)\n" + "Capacity=%s / %s (%.2f%%)\n" + "Battery Technology=%s (%s)\n" + "Manufacturer=%s\n" + "Model Number=%s\n" + "Serial Number=%s\n"), + battery_list, + entry, + state, rate, + remaining, capacity, charge_rate * 100.0, + technology, type, + manufacturer, + model, + serial); + } + + cleanup: + g_free(present); + g_free(capacity); + g_free(technology); + g_free(type); + g_free(model); + g_free(serial); + g_free(state); + g_free(remaining); + g_free(rate); + g_free(manufacturer); + + present = capacity = technology = type = \ + model = serial = state = remaining = rate = manufacturer = NULL; + } + + g_dir_close(acpi); + } + } + + g_free(acpi_path); +} + +static gchar * +read_contents(const gchar *base, const gchar *key) +{ + gchar *value; + gchar *path; + + path = g_strdup_printf("%s/%s", base, key); + if (!path) + return NULL; + + if (!g_file_get_contents(path, &value, NULL, NULL)) { + g_free(path); + return NULL; + } + + g_free(path); + return g_strchomp(value); +} + +static void +__scan_battery_sysfs_add_battery(const gchar *name) +{ + gchar *path = g_strdup_printf("/sys/class/power_supply/%s", name); + gchar *status, *capacity, *capacity_level, *technology, *manufacturer, + *model_name, *serial_number; + + if (!path) + return; + + status = read_contents(path, "status"); + capacity = read_contents(path, "capacity"); + capacity_level = read_contents(path, "capacity_level"); + technology = read_contents(path, "technology"); + manufacturer = read_contents(path, "manufacturer"); + model_name = read_contents(path, "model_name"); + serial_number = read_contents(path, "serial_number"); + + battery_list = h_strdup_cprintf(_("\n[Battery: %s]\n" + "State=%s\n" + "Capacity=%s / %s\n" + "Battery Technology=%s\n" + "Manufacturer=%s\n" + "Model Number=%s\n" + "Serial Number=%s\n"), + battery_list, + name, + status, + capacity, capacity_level, + technology, + manufacturer, + model_name, + serial_number); + + free(status); + free(capacity); + free(capacity_level); + free(technology); + free(manufacturer); + free(model_name); + free(serial_number); +} + +static void +__scan_battery_sysfs(void) +{ + GDir *dir; + const gchar *entry; + + dir = g_dir_open("/sys/class/power_supply", 0, NULL); + if (!dir) + return; + + while ((entry = g_dir_read_name(dir))) { + if (g_str_has_prefix(entry, "BAT")) + __scan_battery_sysfs_add_battery(entry); + } + + g_dir_close(dir); +} + +static void +__scan_battery_apm(void) +{ + FILE *procapm; + static char *sremaining = NULL, *stotal = NULL; + static unsigned int last_time = 0; + static int percentage = 0; + const char *ac_status[] = { "Battery", + "AC Power", + "Charging" }; + int ac_bat; + char apm_bios_ver[16], apm_drv_ver[16]; + char trash[10]; + + if ((procapm = fopen("/proc/apm", "r"))) { + int old_percentage = percentage; + + (void)fscanf(procapm, "%s %s %s 0x%x %s %s %d%%", + apm_drv_ver, apm_bios_ver, trash, + &ac_bat, trash, trash, &percentage); + fclose(procapm); + + if (last_time == 0) { + last_time = time(NULL); + sremaining = stotal = NULL; + } + + if (old_percentage - percentage > 0) { + if (sremaining && stotal) { + g_free(sremaining); + g_free(stotal); + } + + int secs_remaining = (time(NULL) - last_time) * percentage / + (old_percentage - percentage); + sremaining = seconds_to_string(secs_remaining); + stotal = seconds_to_string((secs_remaining * 100) / percentage); + + last_time = time(NULL); + } + } else { + return; + } + + if (stotal && sremaining) { + battery_list = h_strdup_cprintf(_("\n[Battery (APM)]\n" + "Charge=%d%%\n" + "Remaining Charge=%s of %s\n" + "Using=%s\n" + "APM driver version=%s\n" + "APM BIOS version=%s\n"), + battery_list, + percentage, + sremaining, stotal, + ac_status[ac_bat], + apm_drv_ver, apm_bios_ver); + } else { + battery_list = h_strdup_cprintf(_("\n[Battery (APM)]\n" + "Charge=%d%%\n" + "Using=%s\n" + "APM driver version=%s\n" + "APM BIOS version=%s\n"), + battery_list, + percentage, + ac_status[ac_bat], + apm_drv_ver, apm_bios_ver); + + } +} + +void +scan_battery_do(void) +{ + g_free(battery_list); + battery_list = g_strdup(""); + + __scan_battery_sysfs(); + __scan_battery_acpi(); + __scan_battery_apm(); + __scan_battery_apcupsd(); + + if (*battery_list == '\0') { + g_free(battery_list); + + battery_list = g_strdup(_("[No batteries]\n" + "No batteries found on this system=\n")); + } +} diff --git a/modules/devices/devicetree.c b/modules/devices/devicetree.c new file mode 100644 index 00000000..7c798670 --- /dev/null +++ b/modules/devices/devicetree.c @@ -0,0 +1,307 @@ +/* + * HardInfo - Displays System Information + * Copyright (C) 2003-2007 L. A. F. Pereira <l@tia.mat.br> + * + * 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 or later. + * + * 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 <stdint.h> +#include "hardinfo.h" +#include "devices.h" +#include "cpu_util.h" +#include "dt_util.h" +#include "appf.h" + +gchar *dtree_info = NULL; +const char *dtree_mem_str = NULL; /* used by memory devices when nothing else is available */ + +/* These should really go into CMakeLists.txt */ +#if defined(__arm__) +#include "devicetree/rpi_data.c" +#elif defined(__powerpc__) +#include "devicetree/pmac_data.c" +#endif + +static gchar *get_node(dtr *dt, char *np) { + gchar *nodes = NULL, *props = NULL, *ret = NULL; + gchar *tmp = NULL, *pstr = NULL, *lstr = NULL; + gchar *dir_path; + gchar *node_path; + const gchar *fn; + GDir *dir; + dtr_obj *node, *child; + + props = g_strdup_printf("[%s]\n", _("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) { + child = dtr_get_prop_obj(dt, node, fn); + pstr = hardinfo_clean_value(dtr_str(child), 1); + lstr = hardinfo_clean_label(fn, 0); + if (dtr_obj_type(child) == DT_NODE) { + tmp = g_strdup_printf("%s%s=%s\n", + nodes, lstr, pstr); + g_free(nodes); + nodes = tmp; + } else { + tmp = g_strdup_printf("%s%s=%s\n", + props, lstr, pstr); + g_free(props); + props = tmp; + } + dtr_obj_free(child); + g_free(pstr); + g_free(lstr); + } + } + g_dir_close(dir); + g_free(dir_path); + + lstr = dtr_obj_alias(node); + pstr = dtr_obj_symbol(node); + ret = g_strdup_printf("[%s]\n" + "%s=%s\n" + "%s=%s\n" + "%s=%s\n" + "%s%s", + _("Node"), + _("Node Path"), dtr_obj_path(node), + _("Alias"), (lstr != NULL) ? lstr : _("(None)"), + _("Symbol"), (pstr != NULL) ? pstr : _("(None)"), + props, nodes); + + dtr_obj_free(node); + g_free(props); + g_free(nodes); + + return ret; +} + +/* different from dtr_get_string() in that it re-uses the existing dt */ +static char *get_dt_string(dtr *dt, char *path, gboolean decode) { + char *ret; + + if (decode) { + dtr_obj *obj = dtr_get_prop_obj(dt, NULL, path); + + ret = dtr_str(obj); + + dtr_obj_free(obj); + } else { + ret = dtr_get_prop_str(dt, NULL, path); + } + + return ret; +} + +static gchar *get_summary(dtr *dt) { + char *model = NULL, *compat = NULL; + char *ret = NULL; + + model = get_dt_string(dt, "/model", 0); + compat = get_dt_string(dt, "/compatible", 1); + UNKIFNULL(model); + EMPIFNULL(compat); + +#if defined(__arm__) + /* Expand on the DT information from known machines, like RPi. + * RPi stores a revision value in /proc/cpuinfo that can be used + * to look up details. This is just a nice place to pull it all + * together for DT machines, with a nice fallback. + * PPC Macs could be handled this way too. They store + * machine identifiers in /proc/cpuinfo. */ + if (strstr(model, "Raspberry Pi") + || strstr(compat, "raspberrypi")) { + gchar *gpu_compat = get_dt_string(dt, "/soc/gpu/compatible", 1); + gchar *rpi_details = rpi_board_details(); + gchar *basic_info; + + basic_info = g_strdup_printf( + "[%s]\n" + "%s=%s\n" + "%s=%s\n", + _("Platform"), + _("Compatible"), compat, + _("GPU-compatible"), gpu_compat); + + if (rpi_details) { + ret = g_strconcat(rpi_details, basic_info, NULL); + + g_free(rpi_details); + } else { + gchar *serial_number = get_dt_string(dt, "/serial-number", 1); + + ret = g_strdup_printf( + "[%s]\n" + "%s=%s\n" + "%s=%s\n" + "%s=%s\n" + "%s", + _("Raspberry Pi or Compatible"), + _("Model"), model, + _("Serial Number"), serial_number, + _("RCode"), _("No revision code available; unable to lookup model details."), + basic_info); + + g_free(serial_number); + } + + g_free(gpu_compat); + g_free(basic_info); + } +#endif + +#if defined(__powerpc__) + /* Power Macintosh */ + if (strstr(compat, "PowerBook") != NULL + || strstr(compat, "MacRISC") != NULL + || strstr(compat, "Power Macintosh") != NULL) { + gchar *mac_details = ppc_mac_details(); + + if (mac_details) { + gchar *serial_number = get_dt_string(dt, "/serial-number", 1); + + ret = g_strdup_printf( + "%s[%s]\n" + "%s=%s\n", + mac_details, + _("More"), + _("Serial Number"), serial_number); + + free(mac_details); + free(serial_number); + } + } +#endif + + /* fallback */ + if (!ret) { + gchar *serial_number = get_dt_string(dt, "/serial-number", 1); + EMPIFNULL(serial_number); + ret = g_strdup_printf( + "[%s]\n" + "%s=%s\n" + "%s=%s\n" + "%s=%s\n", + _("Board"), + _("Model"), model, + _("Serial Number"), serial_number, + _("Compatible"), compat); + free(serial_number); + } + + free(model); + free(compat); + + return ret; +} + +static void mi_add(const char *key, const char *value, int report_details) { + gchar *ckey, *rkey; + + ckey = hardinfo_clean_label(key, 0); + rkey = g_strdup_printf("%s:%s", "DTREE", ckey); + + dtree_info = h_strdup_cprintf("$%s%s$%s=\n", dtree_info, + (report_details) ? "!" : "", rkey, ckey); + moreinfo_add_with_prefix("DEV", rkey, g_strdup(value)); + + g_free(ckey); + g_free(rkey); +} + +static void add_keys(dtr *dt, char *np) { + gchar *dir_path, *dt_path; + gchar *ftmp, *ntmp; + gchar *n_info; + const gchar *fn; + GDir *dir; + dtr_obj *obj; + + dir_path = g_strdup_printf("%s/%s", dtr_base_path(dt), np); + dir = g_dir_open(dir_path, 0 , NULL); + if(!dir){ /* add self */ + obj = dtr_obj_read(dt, np); + dt_path = dtr_obj_path(obj); + n_info = get_node(dt, dt_path); + mi_add(dt_path, n_info, 0); + }else { //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) ) { + if (strcmp(np, "/") == 0) + ntmp = g_strdup_printf("/%s", fn); + else + ntmp = g_strdup_printf("%s/%s", np, fn); + if(strlen(ntmp)>0) add_keys(dt, ntmp); + g_free(ntmp); + } + g_free(ftmp); + } + g_dir_close(dir); + } + g_free(dir_path); +} + +static char *msg_section(dtr *dt, int dump) { + gchar *aslbl = NULL; + gchar *messages = dtr_messages(dt); + gchar *ret = g_strdup_printf("[%s]", _("Messages")); + gchar **lines = g_strsplit(messages, "\n", 0); + int i = 0; + while(lines[i] != NULL) { + aslbl = hardinfo_clean_label(lines[i], 0); + ret = appfnl(ret, "%s=", aslbl); + g_free(aslbl); + i++; + } + g_strfreev(lines); + if (dump) + printf("%s", messages); + g_free(messages); + return ret; +} + +void __scan_dtree() +{ + dtr *dt = dtr_new(NULL); + gchar *summary = get_summary(dt); + gchar *maps = dtr_maps_info(dt); + gchar *messages = NULL; + + dtree_info = g_strdup("[Device Tree]\n"); + mi_add("Summary", summary, 1); + mi_add("Maps", maps, 0); + + if(dtr_was_found(dt)) + add_keys(dt, "/"); + messages = msg_section(dt, 0); + mi_add("Messages", messages, 0); + + g_free(summary); + g_free(maps); + g_free(messages); + dtr_free(dt); +} diff --git a/modules/devices/devicetree/pmac_data.c b/modules/devices/devicetree/pmac_data.c new file mode 100644 index 00000000..8c5471f0 --- /dev/null +++ b/modules/devices/devicetree/pmac_data.c @@ -0,0 +1,98 @@ +/* + * HardInfo - Displays System Information + * Copyright (C) 2003-2007 L. A. F. Pereira <l@tia.mat.br> + * + * 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 or later. + * + * 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 "cpu_util.h" /* for PROC_CPUINFO */ + +static gchar *ppc_mac_details(void) { + int i = 0; + gchar *ret = NULL; + gchar *platform = NULL; + gchar *model = NULL; + gchar *machine = NULL; + gchar *motherboard = NULL; + gchar *detected_as = NULL; + gchar *pmac_flags = NULL; + gchar *l2_cache = NULL; + gchar *pmac_gen = NULL; + + FILE *cpuinfo; + gchar buffer[128]; + + cpuinfo = fopen(PROC_CPUINFO, "r"); + if (!cpuinfo) + return NULL; + while (fgets(buffer, 128, cpuinfo)) { + gchar **tmp = g_strsplit(buffer, ":", 2); + if (tmp[1] == NULL) { + g_strfreev(tmp); + continue; + } + tmp[0] = g_strstrip(tmp[0]); + tmp[1] = g_strstrip(tmp[1]); + get_str("platform", platform); + get_str("model", model); + get_str("machine", machine); + get_str("motherboard", motherboard); + get_str("detected as", detected_as); + get_str("pmac flags", pmac_flags); + get_str("L2 cache", l2_cache); + get_str("pmac-generation", pmac_gen); + } + fclose(cpuinfo); + + if (machine == NULL) + goto pmd_exit; + + UNKIFNULL(platform); + UNKIFNULL(model); + UNKIFNULL(motherboard); + UNKIFNULL(detected_as); + UNKIFNULL(pmac_flags); + UNKIFNULL(l2_cache); + UNKIFNULL(pmac_gen); + + ret = g_strdup_printf("[%s]\n" + "%s=%s\n" + "%s=%s\n" + "%s=%s\n" + "%s=%s\n" + "%s=%s\n" + "%s=%s\n" + "%s=%s\n" + "%s=%s\n", + _("Apple Power Macintosh"), + _("Platform"), platform, + _("Model"), model, + _("Machine"), machine, + _("Motherboard"), motherboard, + _("Detected as"), detected_as, + _("PMAC Flags"), pmac_flags, + _("L2 Cache"), l2_cache, + _("PMAC Generation"), pmac_gen ); + +pmd_exit: + g_free(platform); + g_free(model); + g_free(machine); + g_free(motherboard); + g_free(detected_as); + g_free(pmac_flags); + g_free(l2_cache); + g_free(pmac_gen); + return ret; +} diff --git a/modules/devices/devicetree/rpi_data.c b/modules/devices/devicetree/rpi_data.c new file mode 100644 index 00000000..63daa7f3 --- /dev/null +++ b/modules/devices/devicetree/rpi_data.c @@ -0,0 +1,179 @@ +/* + * HardInfo - Displays System Information + * Copyright (C) 2003-2007 L. A. F. Pereira <l@tia.mat.br> + * This file from: 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, version 2 or later. + * + * 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 + */ + +static char unk[] = "(Unknown)"; + +/* information table from: http://elinux.org/RPi_HardwareHistory */ +typedef struct { + char *value, *intro, *model, *pcb, *mem, *mfg, *soc; +} RaspberryPiBoard; + +static const RaspberryPiBoard rpi_boardinfo[] = { +/* Value Introduction Model Name PCB rev. Memory(spec) Manufacturer SOC(spec) * + * Raspberry Pi %s */ + { unk, unk, unk, unk, unk, unk, NULL }, + { "Beta", "Q1 2012", "B (Beta)", unk, "256 MB", "(Beta board)", NULL }, + { "0002", "Q1 2012", "B", "1.0", "256 MB", unk, "BCM2835" }, + { "0003", "Q3 2012", "B (ECN0001)", "1.0", "256 MB", "(Fuses mod and D14 removed)", NULL }, + { "0004", "Q3 2012", "B", "2.0", "256 MB", "Sony", NULL }, + { "0005", "Q4 2012", "B", "2.0", "256 MB", "Qisda", NULL }, + { "0006", "Q4 2012", "B", "2.0", "256 MB", "Egoman", NULL }, + { "0007", "Q1 2013", "A", "2.0", "256 MB", "Egoman", NULL }, + { "0008", "Q1 2013", "A", "2.0", "256 MB", "Sony", NULL }, + { "0009", "Q1 2013", "A", "2.0", "256 MB", "Qisda", NULL }, + { "000d", "Q4 2012", "B", "2.0", "512 MB", "Egoman", NULL }, + { "000e", "Q4 2012", "B", "2.0", "512 MB", "Sony", NULL }, + { "000f", "Q4 2012", "B", "2.0", "512 MB", "Qisda", NULL }, + { "0010", "Q3 2014", "B+", "1.0", "512 MB", "Sony", NULL }, + { "0011", "Q2 2014", "Compute Module 1", "1.0", "512 MB", "Sony", NULL }, + { "0012", "Q4 2014", "A+", "1.1", "256 MB", "Sony", NULL }, + { "0013", "Q1 2015", "B+", "1.2", "512 MB", unk, NULL }, + { "0014", "Q2 2014", "Compute Module 1", "1.0", "512 MB", "Embest", NULL }, + { "0015", unk, "A+", "1.1", "256 MB/512 MB", "Embest", NULL }, + { "a01040", unk, "2 Model B", "1.0", "1024 MB DDR2", "Sony", "BCM2836" }, + { "a01041", "Q1 2015", "2 Model B", "1.1", "1024 MB DDR2", "Sony", "BCM2836" }, + { "a21041", "Q1 2015", "2 Model B", "1.1", "1024 MB DDR2", "Embest", "BCM2836" }, + { "a22042", "Q3 2016", "2 Model B", "1.2", "1024 MB DDR2", "Embest", "BCM2837" }, /* (with BCM2837) */ + { "900021", "Q3 2016", "A+", "1.1", "512 MB", "Sony", NULL }, + { "900032", "Q2 2016?", "B+", "1.2", "512 MB", "Sony", NULL }, + { "900092", "Q4 2015", "Zero", "1.2", "512 MB", "Sony", NULL }, + { "900093", "Q2 2016", "Zero", "1.3", "512 MB", "Sony", NULL }, + { "920093", "Q4 2016?", "Zero", "1.3", "512 MB", "Embest", NULL }, + { "9000c1", "Q1 2017", "Zero W", "1.1", "512 MB", "Sony", NULL }, + { "a02082", "Q1 2016", "3 Model B", "1.2", "1024 MB DDR2", "Sony", "BCM2837" }, + { "a020a0", "Q1 2017", "Compute Module 3 or CM3 Lite", "1.0", "1024 MB DDR2", "Sony", NULL }, + { "a22082", "Q1 2016", "3 Model B", "1.2", "1024 MB DDR2", "Embest", "BCM2837" }, + { "a32082", "Q4 2016", "3 Model B", "1.2", "1024 MB DDR2", "Sony Japan", NULL }, + { "a020d3", "Q1 2018", "3 Model B+", "1.3", "1024 MB DDR2", "Sony", "BCM2837" }, + { "9020e0", "Q4 2018", "3 Model A+", "1.0", "512 MB DDR2", "Sony", "BCM2837" }, + + { "a03111", "Q2 2019", "4 Model B", "1.0", "1024 MB DDR4", "Sony", "BCM2838" }, + { "b03111", "Q2 2019", "4 Model B", "1.0", "2048 MB DDR4", "Sony", "BCM2838" }, + { "c03111", "Q2 2019", "4 Model B", "1.1", "4096 MB DDR4", "Sony", "BCM2838" }, + { "c03114", "Q2 2020", "4 Model B", "1.4", "8192 MB DDR4", "Sony", "BCM2838" }, + + { NULL, NULL, NULL, NULL, NULL, NULL, NULL } +}; + +/* return number of chars to skip */ +static int rpi_ov_check(const char *r_code) { + /* sources differ. prefix is either 1000... or just 1... */ + //if (strncmp(r, "1000", 4) == 0) + // return 4; + if (strncmp(r_code, "1", 1) == 0) + return 1; + return 0; +} + +static int rpi_code_match(const char* code0, const char* code1) { + int c0, c1; + if (code0 == NULL || code1 == NULL) return 0; + c0 = strtol(code0, NULL, 16); + c1 = strtol(code1, NULL, 16); + if (c0 && c1) + return (c0 == c1) ? 1 : 0; + else + return (strcmp(code0, code1) == 0) ? 1 : 0; +} + +static int rpi_find_board(const char *r_code) { + int i = 0; + char *r = (char*)r_code; + if (r_code == NULL) + return 0; + /* ignore the overvolt prefix */ + r += rpi_ov_check(r_code); + while (rpi_boardinfo[i].value != NULL) { + if (rpi_code_match(r, rpi_boardinfo[i].value)) + return i; + + i++; + } + return 0; +} + +/* ------------------------- */ + +#include "cpu_util.h" /* for PROC_CPUINFO */ + +static gchar *rpi_board_details(void) { + int i = 0; + gchar *ret = NULL; + gchar *soc = NULL; + gchar *serial = NULL; + gchar *revision = NULL; + int ov = 0; + FILE *cpuinfo; + gchar buffer[128]; + + cpuinfo = fopen(PROC_CPUINFO, "r"); + if (!cpuinfo) + return NULL; + while (fgets(buffer, 128, cpuinfo)) { + gchar **tmp = g_strsplit(buffer, ":", 2); + if (tmp[1] == NULL) { + g_strfreev(tmp); + continue; + } + tmp[0] = g_strstrip(tmp[0]); + tmp[1] = g_strstrip(tmp[1]); + get_str("Revision", revision); + get_str("Hardware", soc); + get_str("Serial", serial); + } + fclose(cpuinfo); + + if (revision == NULL || soc == NULL) { + g_free(soc); + g_free(revision); + return NULL; + } + + ov = rpi_ov_check(revision); + i = rpi_find_board(revision); + ret = g_strdup_printf("[%s]\n" + "%s=%s %s\n" + "%s=%s\n" + "%s=%s\n" + "%s=%s\n" + "%s=%s\n" + "%s=%s\n" + "%s=%s\n" + "%s=%s\n" + "%s=%s\n", + _("Raspberry Pi"), + _("Board Name"), _("Raspberry Pi"), rpi_boardinfo[i].model, + _("PCB Revision"), rpi_boardinfo[i].pcb, + _("Introduction"), rpi_boardinfo[i].intro, + _("Manufacturer"), rpi_boardinfo[i].mfg, + _("RCode"), (rpi_boardinfo[i].value != unk) ? rpi_boardinfo[i].value : revision, + _("SOC (spec)"), rpi_boardinfo[i].soc, + _("Memory (spec)"), rpi_boardinfo[i].mem, + _("Serial Number"), serial, + _("Permanent overvolt bit"), (ov) ? C_("rpi-ov-bit", "Set") : C_("rpi-ov-bit", "Not set") ); + + g_free(soc); + g_free(revision); + + if (rpi_boardinfo[i].mem) + dtree_mem_str = rpi_boardinfo[i].mem; + + return ret; +} diff --git a/modules/devices/dmi.c b/modules/devices/dmi.c new file mode 100644 index 00000000..34374fbe --- /dev/null +++ b/modules/devices/dmi.c @@ -0,0 +1,168 @@ +/* + * HardInfo - Displays System Information + * Copyright (C) 2003-2007 L. A. F. Pereira <l@tia.mat.br> + * + * 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 or later. + * + * 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 + */ +/* + * DMI support based on patch by Stewart Adam <s.adam@diffingo.com> + */ +#include <unistd.h> +#include <sys/types.h> + +#include "devices.h" +#include "dmi_util.h" + +typedef struct _DMIInfo DMIInfo; + +struct _DMIInfo { + const gchar *name; + const gchar *id_str; + int group; + gboolean maybe_vendor; +}; + +DMIInfo dmi_info_table[] = { + { N_("Product"), NULL, 1 }, + { N_("Name"), "system-product-name", 0 }, + { N_("Family"), "system-product-family", 0 }, + { N_("Vendor"), "system-manufacturer", 0, TRUE }, + { N_("Version"), "system-version", 0 }, + { N_("Serial Number"), "system-serial-number", 0 }, + { N_("SKU"), "system-sku", 0 }, + { N_("BIOS"), NULL, 1 }, + { N_("Date"), "bios-release-date", 0 }, + { N_("Vendor"), "bios-vendor", 0, TRUE }, + { N_("Version"), "bios-version", 0 }, + { N_("Board"), NULL, 1 }, + { N_("Name"), "baseboard-product-name", 0 }, + { N_("Vendor"), "baseboard-manufacturer", 0, TRUE }, + { N_("Version"), "baseboard-version", 0 }, + { N_("Serial Number"), "baseboard-serial-number", 0 }, + { N_("Asset Tag"), "baseboard-asset-tag", 0 }, + { N_("Chassis"), NULL, 1 }, + { N_("Vendor"), "chassis-manufacturer", 0, TRUE }, + { N_("Type"), "chassis-type", 0 }, + { N_("Version"), "chassis-version", 0 }, + { N_("Serial Number"), "chassis-serial-number", 0 }, + { N_("Asset Tag"), "chassis-asset-tag", 0 }, +}; + +gchar *dmi_info = NULL; + +static void add_to_moreinfo(const char *group, const char *key, char *value) +{ + char *new_key = g_strconcat("DMI:", group, ":", key, NULL); + moreinfo_add_with_prefix("DEV", new_key, g_strdup(g_strstrip(value))); +} + +gboolean dmi_get_info(void) +{ + const gchar *group = NULL; + DMIInfo *info; + gboolean dmi_succeeded = FALSE; + guint i; + gchar *value; + const gchar *vendor; + + if (dmi_info) { + g_free(dmi_info); + dmi_info = NULL; + } + + for (i = 0; i < G_N_ELEMENTS(dmi_info_table); i++) { + info = &dmi_info_table[i]; + + if (info->group) { + group = info->name; + dmi_info = h_strdup_cprintf("[%s]\n", dmi_info, _(info->name)); + } else if (group && info->id_str) { + int state = 3; + + if (strcmp(info->id_str, "chassis-type") == 0) { + value = dmi_chassis_type_str(-1, 1); + if (value == NULL) + state = (getuid() == 0) ? 0 : 1; + } + else { + switch (dmi_str_status(info->id_str)) { + case 0: + value = NULL; + state = (getuid() == 0) ? 0 : 1; + break; + case -1: + state = 2; + value = dmi_get_str_abs(info->id_str); + break; + case 1: + value = dmi_get_str_abs(info->id_str); + break; + } + } + + switch (state) { + case 0: /* no value, root */ + dmi_info = h_strdup_cprintf("%s=%s\n", dmi_info, _(info->name), + _("(Not available)")); + break; + case 1: /* no value, no root */ + dmi_info = h_strdup_cprintf("%s=%s\n", dmi_info, _(info->name), + _("(Not available; Perhaps try " + "running HardInfo as root.)")); + break; + case 2: /* ignored value */ + if (params.markup_ok) + dmi_info = h_strdup_cprintf("%s=<s>%s</s>\n", dmi_info, + _(info->name), value); + else + dmi_info = h_strdup_cprintf("%s=[X]\"%s\"\n", dmi_info, + _(info->name), value); + break; + case 3: /* good value */ + { + dmi_info = + h_strdup_cprintf("%s%s=%s\n", dmi_info, + info->maybe_vendor ? "$^$" : "", + _(info->name), value); + add_to_moreinfo(group, info->name, value); + dmi_succeeded = TRUE; + break; + } + } + } + } + + if (!dmi_succeeded) { + g_free(dmi_info); + dmi_info = NULL; + } + + return dmi_succeeded; +} + +void __scan_dmi(void) +{ + gboolean dmi_ok; + + dmi_ok = dmi_get_info(); + + if (!dmi_ok) { + dmi_info = g_strdup_printf("[%s]\n%s=\n", + _("DMI Unavailable"), + (getuid() == 0) + ? _("DMI is not avaliable. Perhaps this platform does not provide DMI.") + : _("DMI is not available; Perhaps try running HardInfo as root.") ); + + } +} diff --git a/modules/devices/dmi_memory.c b/modules/devices/dmi_memory.c new file mode 100644 index 00000000..eba26c8b --- /dev/null +++ b/modules/devices/dmi_memory.c @@ -0,0 +1,1050 @@ +/* + * HardInfo - Displays System Information + * Copyright (C) 2003-2019 L. A. F. Pereira <l@tia.mat.br> + * Copyright (C) 2019 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 or later. + * + * 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 + */ + +#define _GNU_SOURCE + +#include "hardinfo.h" +#include "devices.h" +#include "vendor.h" +#include <inttypes.h> + +extern const char *dtree_mem_str; /* in devicetree.c */ + +/* in monitors.c */ +gchar **get_output_lines(const char *cmd_line); + +#include "util_sysobj.h" /* for appfsp() */ +#define dmi_spd_msg(...) /* fprintf (stderr, __VA_ARGS__) */ + +typedef uint64_t dmi_mem_size; + +#include "spd-decode.c" + +gboolean no_handles = FALSE; +gboolean sketchy_info = FALSE; + +int dmi_ram_types = 0; /* bits using enum RamType */ + +/* strings from dmidecode */ +static const char empty_mem_str[] = "No Module Installed"; +static const char unknown_mfgr_str[] = "<BAD INDEX>"; +static const char mobo_location[] = "System Board Or Motherboard"; +static const char mobo_shorter[] = "Mainboard"; +static const dmi_type dta = 16; /* array */ +static const dmi_type dtm = 17; /* socket */ +static const char mem_icon[] = "memory.png"; +static const char array_icon[] = "devices.png"; +static const char empty_icon[] = "module.png"; + +#define UNKNOWN_MEM_TYPE_STRING _("RAM") +#define UNKIFNULL2(f) ((f) ? f : _("(Unknown)")) +#define UNKIFEMPTY2(f) ((*f) ? f : _("(Unknown)")) +#define STR_IGNORE(str, ignore) if (SEQ(str, ignore)) { *str = 0; null_if_empty(&str); } + +dmi_mem_size dmi_read_memory_str_to_MiB(const char *memstr) { + dmi_mem_size ret = 0, v = 0; + char l[7] = ""; + /* dmidecode units: "bytes", "kB", "MB", "GB", "TB", "PB", "EB", "ZB" */ + int mc = sscanf(memstr, "%"PRId64" %6s", &v, l); + if (mc == 2) { + if (SEQ(l, "ZB")) ret = v * 1024 * 1024 * 1024 * 1024 * 1024; + else if (SEQ(l, "EB")) ret = v * 1024 * 1024 * 1024 * 1024; + else if (SEQ(l, "PB")) ret = v * 1024 * 1024 * 1024; + else if (SEQ(l, "TB")) ret = v * 1024 * 1024; + else if (SEQ(l, "GB")) ret = v * 1024; + else if (SEQ(l, "MB")) ret = v; + else if (SEQ(l, "kB")) { + /* should never appear */ + if (v % 1024) { dmi_spd_msg("OMG kB!"); } + ret = v / 1024; + } + else if (SEQ(l, "bytes")) { + /* should never appear */ + if (v % 1024) { dmi_spd_msg("OMG bytes!"); } + ret = v / (1024 * 1024); + } + } + return ret; +} + +typedef struct { + dmi_handle array_handle; + gboolean is_main_memory; + gchar *locator; + gchar *use; + gchar *ecc; + int devs; + int devs_populated; + dmi_mem_size size_MiB_max; + dmi_mem_size size_MiB_present; + dmi_mem_size size_MiB_rom; + int ram_types; /* bits using enum RamType */ +} dmi_mem_array; + +dmi_mem_array *dmi_mem_array_new(dmi_handle h) { + dmi_mem_array *s = g_new0(dmi_mem_array, 1); + s->array_handle = h; + s->use = dmidecode_match("Use", &dta, &h); + if (SEQ(s->use, "System Memory")) + s->is_main_memory = TRUE; + s->ecc = dmidecode_match("Error Correction Type", &dta, &h); + s->locator = dmidecode_match("Location", &dta, &h); + if (SEQ(s->locator, mobo_location)) { + g_free(s->locator); + s->locator = g_strdup(mobo_shorter); + s->is_main_memory = TRUE; + } + + gchar *array_max_size = dmidecode_match("Maximum Capacity", &dta, &h); + if (array_max_size) { + s->size_MiB_max = dmi_read_memory_str_to_MiB(array_max_size); + g_free(array_max_size); + } + gchar *array_devs = dmidecode_match("Number Of Devices", &dta, &h); + if (array_devs) { + s->devs = strtol(array_devs, NULL, 10); + g_free(array_devs); + } + return s; +} + +void dmi_mem_array_free(dmi_mem_array* s) { + if (s) { + g_free(s->locator); + g_free(s->use); + g_free(s->ecc); + g_free(s); + } +} + +typedef struct dmi_mem_socket { + dmi_handle handle; + dmi_handle array_handle; + gboolean populated; + gchar *locator; + gchar *full_locator; + gchar *short_locator; + gchar *size_str; + dmi_mem_size size_MiB; + + gboolean is_not_ram; /* maybe is_rom, maybe elsewise, but don't include in RAM total */ + gboolean is_rom; + + gchar *type; + gchar *type_detail; + int ram_type; /* using enum RamType */ + gchar *array_locator; + gchar *bank_locator; + gchar *rank; + gchar *form_factor; + gchar *speed_str; + gchar *configured_clock_str; + gchar *voltage_min_str; + gchar *voltage_max_str; + gchar *voltage_conf_str; + gchar *partno; + gchar *data_width; + gchar *total_width; + gchar *mfgr; + gboolean has_jedec_mfg_id; + int mfgr_bank, mfgr_index; + + const Vendor *vendor; + spd_data *spd; +} dmi_mem_socket; + +typedef struct { + gboolean empty; + GSList *arrays; + GSList *sockets; + GSList *spd; + dmi_mem_size spd_size_MiB; + int spd_ram_types; /* bits using enum RamType */ + + dmi_mem_size system_memory_MiB; + int system_memory_ram_types; /* bits using enum RamType */ + + /* ->short_locator is unique among *sockets */ + gboolean unique_short_locators; +} dmi_mem; + +gboolean null_if_empty(gchar **str) { + if (str && *str) { + gchar *p = *str; + while(p && *p) { + if (isalnum(*p)) + return FALSE; + p++; + } + *str = NULL; + } + return TRUE; +} + +dmi_mem_socket *dmi_mem_socket_new(dmi_handle h) { + dmi_mem_socket *s = g_new0(dmi_mem_socket, 1); + s->handle = h; + s->locator = dmidecode_match("Locator", &dtm, &h); + s->size_str = dmidecode_match("Size", &dtm, &h); + if (s->size_str) + s->size_MiB = dmi_read_memory_str_to_MiB(s->size_str); + + s->bank_locator = dmidecode_match("Bank Locator", &dtm, &h); + STR_IGNORE(s->bank_locator, "Unknown"); + STR_IGNORE(s->bank_locator, "Not Specified"); + null_if_empty(&s->bank_locator); + + gchar *ah = dmidecode_match("Array Handle", &dtm, &h); + STR_IGNORE(ah, "Unknown"); + if (ah) { + s->array_handle = strtol(ah, NULL, 16); + g_free(ah); + s->array_locator = dmidecode_match("Location", &dta, &s->array_handle); + if (SEQ(s->array_locator, mobo_location)) { + g_free(s->array_locator); + s->array_locator = g_strdup(mobo_shorter); + } + } + + gchar *ah_str = g_strdup_printf("0x%"PRIx32, s->array_handle); + gchar *h_str = g_strdup_printf("0x%"PRIx32, s->handle); + s->short_locator = g_strdup_printf("%s \u27A4 %s", + s->array_locator ? s->array_locator : ah_str, + s->locator ? s->locator : h_str); + + if (s->bank_locator) + s->full_locator = g_strdup_printf("%s \u27A4 %s \u27A4 %s", + s->array_locator ? s->array_locator : ah_str, + s->bank_locator, + s->locator ? s->locator : h_str); + else + s->full_locator = g_strdup(s->short_locator); + + g_free(ah_str); + g_free(h_str); + + if (!g_str_has_prefix(s->size_str, empty_mem_str)) { + s->populated = 1; + + s->form_factor = dmidecode_match("Form Factor", &dtm, &h); + s->type = dmidecode_match("Type", &dtm, &h); + STR_IGNORE(s->type, "Unknown"); + if (SEQ(s->type, "Flash") || SEQ(s->type, "ROM")) { + s->is_rom = TRUE; + s->is_not_ram = TRUE; + } else { + if (SEQ(s->type, "DDR")) s->ram_type = DDR_SDRAM; + if (SEQ(s->type, "DDR2")) s->ram_type = DDR2_SDRAM; + if (SEQ(s->type, "DDR3")) s->ram_type = DDR3_SDRAM; + if (SEQ(s->type, "DDR4")) s->ram_type = DDR4_SDRAM; + if (SEQ(s->type, "DRDRAM")) s->ram_type = DIRECT_RAMBUS; + if (SEQ(s->type, "RDRAM")) s->ram_type = RAMBUS; + if (s->ram_type) + dmi_ram_types |= (1 << (s->ram_type-1)); + } + s->type_detail = dmidecode_match("Type Detail", &dtm, &h); + STR_IGNORE(s->type_detail, "None"); + + s->speed_str = dmidecode_match("Speed", &dtm, &h); + s->configured_clock_str = dmidecode_match("Configured Clock Speed", &dtm, &h); + if (!s->configured_clock_str) + s->configured_clock_str = dmidecode_match("Configured Memory Speed", &dtm, &h); + + s->voltage_min_str = dmidecode_match("Minimum Voltage", &dtm, &h); + s->voltage_max_str = dmidecode_match("Maximum Voltage", &dtm, &h); + s->voltage_conf_str = dmidecode_match("Configured Voltage", &dtm, &h); + STR_IGNORE(s->voltage_min_str, "Unknown"); + STR_IGNORE(s->voltage_max_str, "Unknown"); + STR_IGNORE(s->voltage_conf_str, "Unknown"); + + s->partno = dmidecode_match("Part Number", &dtm, &h); + STR_IGNORE(s->partno, "PartNum0"); + STR_IGNORE(s->partno, "PartNum1"); + STR_IGNORE(s->partno, "PartNum2"); + STR_IGNORE(s->partno, "PartNum3"); + null_if_empty(&s->partno); + + s->data_width = dmidecode_match("Data Width", &dtm, &h); + s->total_width = dmidecode_match("Total Width", &dtm, &h); + + s->rank = dmidecode_match("Rank", &dtm, &h); + + s->mfgr = dmidecode_match("Manufacturer", &dtm, &h); + STR_IGNORE(s->mfgr, unknown_mfgr_str); + STR_IGNORE(s->mfgr, "Manufacturer0"); + STR_IGNORE(s->mfgr, "Manufacturer1"); + STR_IGNORE(s->mfgr, "Manufacturer2"); + STR_IGNORE(s->mfgr, "Manufacturer3"); + STR_IGNORE(s->mfgr, "Unknown"); + null_if_empty(&s->mfgr); + + gchar *mfgr_id_str = dmidecode_match("Module Manufacturer ID", &dtm, &h); + STR_IGNORE(mfgr_id_str, "Unknown"); + if (mfgr_id_str) { + static const char dmi_mfg_id_fmt[] = "Bank %d, Hex 0x%02X"; /* from dmidecode.c */ + int mc = sscanf(strstr(mfgr_id_str, "Bank"), dmi_mfg_id_fmt, &s->mfgr_bank, &s->mfgr_index); + if (mc > 0 && !s->mfgr) { + s->has_jedec_mfg_id = TRUE; + s->mfgr = g_strdup(JEDEC_MFG_STR(s->mfgr_bank, s->mfgr_index)); + } + } + + if (s->mfgr && !s->has_jedec_mfg_id && strlen(s->mfgr) == 4) { + /* Some BIOS put the code bytes into the mfgr string + * if they don't know the manufacturer. + * It's not always reliable, but what is lost + * by trying it? */ + if (isxdigit(s->mfgr[0]) + && isxdigit(s->mfgr[1]) + && isxdigit(s->mfgr[2]) + && isxdigit(s->mfgr[3]) ) { + int codes = strtol(s->mfgr, NULL, 16); + char *mstr = NULL; + decode_ddr34_manufacturer(codes >> 8, codes & 0xff, + &mstr, &s->mfgr_bank, &s->mfgr_index); + s->has_jedec_mfg_id = TRUE; + g_free(s->mfgr); + s->mfgr = NULL; + if (mstr) + s->mfgr = g_strdup(mstr); + } + } + + s->vendor = vendor_match(s->mfgr, NULL); + } + return s; +} + +void dmi_mem_socket_free(dmi_mem_socket* s) { + if (s) { + g_free(s->locator); + g_free(s->full_locator); + g_free(s->short_locator); + g_free(s->size_str); + g_free(s->type); + g_free(s->type_detail); + g_free(s->bank_locator); + g_free(s->rank); + g_free(s->array_locator); + g_free(s->form_factor); + g_free(s->speed_str); + g_free(s->configured_clock_str); + g_free(s->voltage_min_str); + g_free(s->voltage_max_str); + g_free(s->voltage_conf_str); + g_free(s->partno); + g_free(s->data_width); + g_free(s->total_width); + g_free(s->mfgr); + + g_free(s); + } +} + +dmi_mem_array *dmi_mem_find_array(dmi_mem *s, unsigned int handle) { + GSList *l = NULL; + for(l = s->arrays; l; l = l->next) { + dmi_mem_array *a = (dmi_mem_array*)l->data; + if (a->array_handle == handle) + return a; + } + return NULL; +} + +static int dmi_spd_match_score(dmi_mem_socket *s, spd_data *e) { + int score = 0; + if (SEQ(s->partno, e->partno)) + score += 20; + if (s->size_MiB == e->size_MiB) + score += 10; + if (s->vendor == e->vendor) + score += 5; + return score; +} + +/* fill in missing from SPD */ +static void dmi_fill_from_spd(dmi_mem_socket *s) { + if (!s->spd) + return; + + if (!s->mfgr && s->spd->vendor_str) { + s->mfgr = g_strdup(s->spd->vendor_str); + s->vendor = s->spd->vendor; + } + if (!s->has_jedec_mfg_id) { + s->mfgr_bank = s->spd->vendor_bank; + s->mfgr_index = s->spd->vendor_index; + s->has_jedec_mfg_id = TRUE; + } + + //Always true - FIXME + //if (!s->partno && s->spd->partno) + s->partno = g_strdup(s->spd->partno); + + if (!s->form_factor && s->spd->form_factor) + s->form_factor = g_strdup(s->spd->form_factor); + + //Always true - FIXME + //if (!s->type_detail && s->spd->type_detail) + s->type_detail = g_strdup(s->spd->type_detail); +} + +static dmi_mem_size size_of_online_memory_blocks() { + gchar *block_size_bytes_str = NULL; + dmi_mem_size block_size_bytes = 0; + dmi_mem_size ret = 0; + + if (g_file_get_contents("/sys/devices/system/memory/block_size_bytes", &block_size_bytes_str, NULL, NULL) ) { + block_size_bytes = strtoll(block_size_bytes_str, NULL, 16); + } + if (!block_size_bytes) + return 0; + + const gchar *f = NULL; + GDir *d = g_dir_open("/sys/devices/system/memory", 0, NULL); + if (!d) return 0; + + while((f = g_dir_read_name(d))) { + gchar *p = g_strdup_printf("/sys/devices/system/memory/%s/online", f); + gchar *ol = NULL; + if (g_file_get_contents(p, &ol, NULL, NULL) ) { + if (1 == strtol(ol, NULL, 0)) { + ret += block_size_bytes; + } + } + g_free(ol); + g_free(p); + } + g_dir_close(d); + return ret; +} + +dmi_mem *dmi_mem_new() { + dmi_mem *m = g_new0(dmi_mem, 1); + + dmi_handle_list *hla = dmidecode_handles(&dta); + if (hla) { + unsigned int i = 0; + for(i = 0; i < hla->count; i++) { + dmi_handle h = hla->handles[i]; + m->arrays = g_slist_append(m->arrays, dmi_mem_array_new(h)); + } + dmi_handle_list_free(hla); + } + + dmi_handle_list *hlm = dmidecode_handles(&dtm); + if (hlm) { + unsigned int i = 0; + for(i = 0; i < hlm->count; i++) { + dmi_handle h = hlm->handles[i]; + m->sockets = g_slist_append(m->sockets, dmi_mem_socket_new(h)); + } + dmi_handle_list_free(hlm); + } + + m->spd = spd_scan(); + + if (!m->sockets && !m->arrays && !m->spd) { + m->empty = 1; + goto dmi_mem_new_last_chance; + } + + GSList *l = NULL, *l2 = NULL; + + /* totals for SPD */ + for(l2 = m->spd; l2; l2 = l2->next) { + spd_data *e = (spd_data*)l2->data; + m->spd_size_MiB += e->size_MiB; + if (e->type) + m->spd_ram_types |= (1 << (e->type-1)); + } + + m->unique_short_locators = TRUE; + for(l = m->sockets; l; l = l->next) { + dmi_mem_socket *s = (dmi_mem_socket*)l->data; + + /* check for duplicate short_locator */ + if (m->unique_short_locators) { + for(l2 = l->next; l2; l2 = l2->next) { + dmi_mem_socket *d = (dmi_mem_socket*)l2->data; + if (SEQ(s->short_locator, d->short_locator)) { + m->unique_short_locators = FALSE; + break; + } + } + } + + /* update array present devices/size */ + dmi_mem_array *a = dmi_mem_find_array(m, s->array_handle); + if (a) { + if (s->is_not_ram) { + if (s->is_rom) + a->size_MiB_rom += s->size_MiB; + } else { + a->size_MiB_present += s->size_MiB; + if (s->populated) + a->devs_populated++; + if (s->ram_type) + a->ram_types |= (1 << (s->ram_type-1)); + } + } + } + + if (m->sockets && m->spd) { + /* attempt to match DMI and SPD data */ + GSList *sock_queue = g_slist_copy(m->sockets); + int loops = g_slist_length(sock_queue) * 4; + while(sock_queue) { + if (loops-- <= 0) break; /* something is wrong, give up */ + spd_data *best = NULL; + int best_score = 0; + dmi_mem_socket *s = (dmi_mem_socket*)sock_queue->data; + /* pop that one off */ + sock_queue = g_slist_delete_link(sock_queue, sock_queue); + if (!s->populated) + continue; + for(l2 = m->spd; l2; l2 = l2->next) { + spd_data *e = (spd_data*)l2->data; + int score = dmi_spd_match_score(s, e); + if (score > best_score) { + if (score > e->match_score) { + best = e; + best_score = score; + } + } + } + if (best) { + if (best->dmi_socket) { + /* displace */ + dmi_mem_socket *old_sock = best->dmi_socket; + old_sock->spd = NULL; + sock_queue = g_slist_append(sock_queue, old_sock); + + best->dmi_socket = s; + best->match_score = best_score; + s->spd = best; + } else { + best->dmi_socket = s; + best->match_score = best_score; + s->spd = best; + } + } + } + + /* fill any missing data in DMI that is + * provided by the matched SPD */ + for(l = m->sockets; l; l = l->next) { + dmi_mem_socket *s = (dmi_mem_socket*)l->data; + dmi_fill_from_spd(s); + } + + } /* end if (m->sockets && m->spd) */ + + /* Look for arrays with "System Memory" use, + * or Mainboard as locator */ + for(l = m->arrays; l; l = l->next) { + dmi_mem_array *a = (dmi_mem_array*)l->data; + if (a->is_main_memory) { + m->system_memory_MiB += a->size_MiB_present; + m->system_memory_ram_types |= a->ram_types; + } + } + + /* If no arrays, then try the SPD total */ + if (!m->system_memory_MiB) { + m->system_memory_MiB = m->spd_size_MiB; + m->system_memory_ram_types |= m->spd_ram_types; + } + +dmi_mem_new_last_chance: + if (m->empty) { + /* reach */ + if (dtree_mem_str) { + int rt = 0; + m->system_memory_MiB = dmi_read_memory_str_to_MiB(dtree_mem_str); + if (strstr(dtree_mem_str, "DDR4")) rt = DDR4_SDRAM; + else if (strstr(dtree_mem_str, "DDR3")) rt = DDR3_SDRAM; + else if (strstr(dtree_mem_str, "DDR2")) rt = DDR2_SDRAM; + else if (strstr(dtree_mem_str, "DDR")) rt = DDR_SDRAM; + else if (strstr(dtree_mem_str, "DRDRAM")) rt = DIRECT_RAMBUS; + else if (strstr(dtree_mem_str, "RDRAM")) rt = RAMBUS; + if (rt) + m->system_memory_ram_types |= (1 << (rt-1)); + } + } + + /* Try to sum the online blocks for a physical memory total */ + if (!m->system_memory_MiB) + m->system_memory_MiB = size_of_online_memory_blocks() / 1024 / 1024; + + return m; +} + +void dmi_mem_free(dmi_mem* s) { + if (s) { + g_slist_free_full(s->arrays, (GDestroyNotify)dmi_mem_array_free); + g_slist_free_full(s->sockets, (GDestroyNotify)dmi_mem_socket_free); + g_slist_free_full(s->spd, (GDestroyNotify)spd_data_free); + g_free(s); + } +} + +gchar *make_spd_section(spd_data *spd) { + gchar *ret = NULL; + if (spd) { + gchar *full_spd = NULL; + switch(spd->type) { + case SDR_SDRAM: + full_spd = decode_sdr_sdram_extra(spd->bytes); + break; + case DDR_SDRAM: + full_spd = decode_ddr_sdram_extra(spd->bytes); + break; + case DDR2_SDRAM: + full_spd = decode_ddr2_sdram_extra(spd->bytes); + break; + case DDR3_SDRAM: + full_spd = decode_ddr3_sdram_extra(spd->bytes); + break; + case DDR4_SDRAM: + full_spd = decode_ddr4_sdram_extra(spd->bytes, spd->spd_size); + break; + default: + DEBUG("blug for type: %d %s\n", spd->type, ram_types[spd->type]); + } + gchar *size_str = NULL; + if (!spd->size_MiB) + size_str = g_strdup(_("(Unknown)")); + else + size_str = g_strdup_printf("%"PRId64" %s", spd->size_MiB, _("MiB") ); + + gchar *mfg_date_str = NULL; + if (spd->year) + mfg_date_str = g_strdup_printf("%d / %d", spd->week, spd->year); + + ret = g_strdup_printf("[%s]\n" + "%s=%s (%s)%s\n" + "%s=%d.%d\n" + "%s=%s\n" + "%s=%s\n" + "$^$%s=[%02x%02x] %s\n" /* module vendor */ + "$^$%s=[%02x%02x] %s\n" /* dram vendor */ + "%s=%s\n" /* part */ + "%s=%s\n" /* size */ + "%s=%s\n" /* mfg date */ + "%s", + _("Serial Presence Detect (SPD)"), + _("Source"), spd->dev, spd->spd_driver, + (spd->type == DDR4_SDRAM && strcmp(spd->spd_driver, "ee1004") != 0) ? problem_marker() : "", + _("SPD Revision"), spd->spd_rev_major, spd->spd_rev_minor, + _("Form Factor"), UNKIFNULL2(spd->form_factor), + _("Type"), UNKIFEMPTY2(spd->type_detail), + _("Module Vendor"), spd->vendor_bank, spd->vendor_index, + UNKIFNULL2(spd->vendor_str), + _("DRAM Vendor"), spd->dram_vendor_bank, spd->dram_vendor_index, + UNKIFNULL2(spd->dram_vendor_str), + _("Part Number"), UNKIFEMPTY2(spd->partno), + _("Size"), size_str, + _("Manufacturing Date (Week / Year)"), UNKIFNULL2(mfg_date_str), + full_spd ? full_spd : "" + ); + g_free(full_spd); + g_free(size_str); + g_free(mfg_date_str); + } + return ret; +} + +static gchar *tag_make_safe_inplace(gchar *tag) { + if (!tag) + return tag; + if (!g_utf8_validate(tag, -1, NULL)) + return tag; //TODO: reconsider + gchar *p = tag, *pd = tag; + while(*p) { + gchar *np = g_utf8_next_char(p); + gunichar c = g_utf8_get_char_validated(p, -1); + int l = g_unichar_to_utf8(c, NULL); + if (l == 1 && g_unichar_isalnum(c)) { + g_unichar_to_utf8(c, pd); + } else { + *pd = '_'; + } + p = np; + pd++; + } + return tag; +} + +gchar *memory_devices_get_info() { + gchar *icons = g_strdup(""); + gchar *ret = g_strdup_printf("[%s]\n", _("Memory Device List")); + GSList *l = NULL; + sketchy_info = FALSE; + gchar tag_prefix[] = "DEV"; + + dmi_mem *mem = dmi_mem_new(); + + /* Arrays */ + for(l = mem->arrays; l; l = l->next) { + dmi_mem_array *a = (dmi_mem_array*)l->data; + gchar *tag = g_strdup_printf("%s", a->locator); + gchar *size_str = NULL, *rom_size_str = NULL; + + tag_make_safe_inplace(tag); + + if (a->size_MiB_max > 1024 && (a->size_MiB_max % 1024 == 0) + && a->size_MiB_present > 1024 && (a->size_MiB_present % 1024 == 0) ) + size_str = g_strdup_printf("%"PRId64" / %"PRId64" %s", a->size_MiB_present / 1024, a->size_MiB_max / 1024, _("GiB")); + else + size_str = g_strdup_printf("%"PRId64" / %"PRId64" %s", a->size_MiB_present, a->size_MiB_max, _("MiB")); + + if (a->size_MiB_max < a->size_MiB_present) { + sketchy_info = TRUE; + size_str = h_strdup_cprintf(" %s", size_str, problem_marker()); + } + + if (a->size_MiB_rom > 1024 && (a->size_MiB_rom % 1024 == 0)) + rom_size_str = g_strdup_printf("%"PRId64" %s", a->size_MiB_rom / 1024, _("GiB")); + else + rom_size_str = g_strdup_printf("%"PRId64" %s", a->size_MiB_rom, _("MiB")); + + gchar *types_str = NULL; + int i; + for(i = 1; i < N_RAM_TYPES; i++) { + int bit = 1 << (i-1); + if (a->ram_types & bit) + types_str = appfsp(types_str, "%s", GET_RAM_TYPE_STR(i)); + } + + gchar *details = g_strdup_printf("[%s]\n" + "%s=0x%"PRIx32"\n" + "%s=%s\n" + "%s=%s\n" + "%s=%s\n" + "%s=%s\n" + "%s=%d / %d\n" + "%s=[0x%x] %s\n" + "%s=%s\n", + _("Memory Array"), + _("DMI Handle"), a->array_handle, + _("Locator"), a->locator ? a->locator : ".", + _("Use"), UNKIFNULL2(a->use), + _("Error Correction Type"), UNKIFNULL2(a->ecc), + _("Size (Present / Max)"), size_str, + _("Devices (Populated / Sockets)"), a->devs_populated, a->devs, + _("Types Present"), a->ram_types, UNKIFNULL2(types_str), + _("ROM Size"), rom_size_str + ); + moreinfo_add_with_prefix(tag_prefix, tag, details); /* moreinfo now owns *details */ + ret = h_strdup_cprintf("$!%s$%s=%s|%s\n", + ret, + tag, a->locator, UNKIFNULL2(types_str), size_str + ); + icons = h_strdup_cprintf("Icon$%s$=%s\n", icons, tag, array_icon); + g_free(tag); + g_free(size_str); + g_free(rom_size_str); + g_free(types_str); + } + + /* Sockets */ + for(l = mem->sockets; l; l = l->next) { + dmi_mem_socket *s = (dmi_mem_socket*)l->data; + gchar *tag = g_strdup_printf("%s", s->full_locator); + tag_make_safe_inplace(tag); + + if (s->populated) { + gchar *size_str = NULL; + if (!s->size_str) + size_str = g_strdup(_("(Unknown)")); + else if (!s->size_MiB) + size_str = g_strdup(s->size_str); + else + size_str = g_strdup_printf("%"PRId64" %s", s->size_MiB, _("MiB") ); + + gchar *spd = s->spd ? make_spd_section(s->spd) : NULL; + + gchar *details = g_strdup_printf("[%s]\n" + "%s=0x%"PRIx32", 0x%"PRIx32"\n" + "%s=%s\n" + "%s=%s\n" + "%s=%s\n" + "%s=%s / %s\n" + "$^$%s=[%02x%02x] %s\n" + "%s=%s\n" + "%s=%s\n" + "%s=%s\n" + "%s=%s\n" + "%s=%s / %s\n" + "%s=%s\n" + "%s=%s\n" + "%s=%s\n" + "%s=%s\n" + "%s", /* spd */ + _("Memory Socket"), + _("DMI Handles (Array, Socket)"), s->array_handle, s->handle, + _("Locator"), s->full_locator, + _("Bank Locator"), UNKIFNULL2(s->bank_locator), + _("Form Factor"), UNKIFNULL2(s->form_factor), + _("Type"), UNKIFNULL2(s->type), UNKIFNULL2(s->type_detail), + _("Vendor"), + s->mfgr_bank, s->mfgr_index, UNKIFNULL2(s->mfgr), + _("Part Number"), UNKIFNULL2(s->partno), + _("Size"), size_str, + _("Rated Speed"), UNKIFNULL2(s->speed_str), + _("Configured Speed"), UNKIFNULL2(s->configured_clock_str), + _("Data Width/Total Width"), UNKIFNULL2(s->data_width), UNKIFNULL2(s->total_width), + _("Rank"), UNKIFNULL2(s->rank), + _("Minimum Voltage"), UNKIFNULL2(s->voltage_min_str), + _("Maximum Voltage"), UNKIFNULL2(s->voltage_max_str), + _("Configured Voltage"), UNKIFNULL2(s->voltage_conf_str), + spd ? spd : "" + ); + g_free(spd); + moreinfo_add_with_prefix(tag_prefix, tag, details); /* moreinfo now owns *details */ + gchar *mfgr = s->mfgr ? vendor_match_tag(s->mfgr, params.fmt_opts) : NULL; + ret = h_strdup_cprintf("$!%s$%s=%s|%s|%s\n", + ret, + tag, + mem->unique_short_locators ? s->short_locator : s->full_locator, + UNKIFNULL2(s->partno), size_str, UNKIFNULL2(mfgr) + ); + icons = h_strdup_cprintf("Icon$%s$=%s\n", icons, tag, mem_icon); + g_free(size_str); + g_free(mfgr); + } else { + gchar *details = g_strdup_printf("[%s]\n" + "%s=0x%"PRIx32", 0x%"PRIx32"\n" + "%s=%s\n" + "%s=%s\n" + "%s=%s\n", + _("Memory Socket"), + _("DMI Handles (Array, Socket)"), s->array_handle, s->handle, + _("Locator"), s->full_locator, + _("Bank Locator"), UNKIFNULL2(s->bank_locator), + _("Size"), _("(Empty)") + ); + moreinfo_add_with_prefix(tag_prefix, tag, details); /* moreinfo now owns *details */ + ret = h_strdup_cprintf("$%s$%s=|%s\n", + ret, + tag, + mem->unique_short_locators ? s->short_locator : s->full_locator, + _("(Empty)") + ); + icons = h_strdup_cprintf("Icon$%s$=%s\n", icons, tag, empty_icon); + } + g_free(tag); + } + + /* No DMI Array, show SPD totals */ + if (!mem->arrays && mem->spd) { + gchar *key = g_strdup("SPD:*"); + gchar *tag = g_strdup(key); + tag_make_safe_inplace(tag); + gchar *types_str = NULL; + gchar *size_str = NULL; + + if (mem->spd_size_MiB > 1024 && (mem->spd_size_MiB % 1024 == 0) ) + size_str = g_strdup_printf("%"PRId64" %s", mem->spd_size_MiB / 1024, _("GiB")); + else + size_str = g_strdup_printf("%"PRId64" %s", mem->spd_size_MiB, _("MiB")); + + int i; + for(i = 1; i < N_RAM_TYPES; i++) { + int bit = 1 << (i-1); + if (mem->spd_ram_types & bit) + types_str = appfsp(types_str, "%s", GET_RAM_TYPE_STR(i)); + } + + gchar *details = g_strdup_printf("[%s]\n" + "%s=%s\n" + "%s=%d\n" + "%s=[0x%x] %s\n", + _("Serial Presence Detect (SPD) Summary"), + _("Size"), size_str, + _("Devices"), g_slist_length(mem->spd), + _("Types Present"), mem->spd_ram_types, UNKIFNULL2(types_str) + ); + moreinfo_add_with_prefix(tag_prefix, tag, details); /* moreinfo now owns *details */ + ret = h_strdup_cprintf("$!%s$%s=%s|%s\n", + ret, + tag, key, UNKIFNULL2(types_str), size_str + ); + icons = h_strdup_cprintf("Icon$%s$=%s\n", icons, tag, array_icon); + g_free(key); + g_free(tag); + g_free(size_str); + g_free(types_str); + } + + /* Unmatched SPD */ + for(l = mem->spd; l; l = l->next) { + spd_data *s = (spd_data*)l->data; + if (s->dmi_socket) continue; /* claimed by DMI */ + gchar *key = g_strdup_printf("SPD:%s", s->dev); + gchar *tag = g_strdup(key); + tag_make_safe_inplace(tag); + + gchar *vendor_str = NULL; + if (s->vendor) { + vendor_str = vendor_get_link_from_vendor(s->vendor); + } + gchar *size_str = NULL; + if (!s->size_MiB) + size_str = g_strdup(_("(Unknown)")); + else + size_str = g_strdup_printf("%"PRId64" %s", s->size_MiB, _("MiB") ); + + gchar *details = make_spd_section(s); + + moreinfo_add_with_prefix(tag_prefix, tag, details); /* moreinfo now owns *details */ + const gchar *mfgr = s->vendor_str ? vendor_get_shortest_name(s->vendor_str) : NULL; + ret = h_strdup_cprintf("$!%s$%s%s=%s|%s|%s\n", + ret, + tag, key, problem_marker(), UNKIFEMPTY2(s->partno), size_str, UNKIFNULL2(mfgr) + ); + icons = h_strdup_cprintf("Icon$%s$=%s\n", icons, tag, mem_icon); + g_free(vendor_str); + g_free(size_str); + g_free(key); + g_free(tag); + } + + no_handles = FALSE; + if(mem->empty) { + no_handles = TRUE; + g_free(ret); + ret = g_strdup_printf("[%s]\n%s=%s\n" "[$ShellParam$]\nViewType=0\n", + _("Memory Device List"), _("Result"), + (getuid() == 0) + ? _("(Not available)") + : _("(Not available; Perhaps try running HardInfo as root.)") ); + } else { + ret = h_strdup_cprintf( + "[$ShellParam$]\nViewType=1\n" + "ColumnTitle$TextValue=%s\n" /* Locator */ + "ColumnTitle$Extra1=%s\n" /* Size */ + "ColumnTitle$Extra2=%s\n" /* Vendor */ + "ColumnTitle$Value=%s\n" /* Part */ + "ShowColumnHeaders=true\n" + "%s", + ret, + _("Locator"), + _("Size"), + _("Vendor"), + _("Part"), + icons + ); + } + + g_free(icons); + dmi_mem_free(mem); + return ret; +} + +gchar *memory_devices_get_system_memory_types_str() { + gchar *ret = NULL, *types_str = NULL; + int i, rtypes; + + dmi_mem *lmem = dmi_mem_new(); + rtypes = lmem->system_memory_ram_types; + dmi_mem_free(lmem); + + for(i = 1; i < N_RAM_TYPES; i++) { + int bit = 1 << (i-1); + if (rtypes & bit) + types_str = appfsp(types_str, "%s", GET_RAM_TYPE_STR(i)); + } + + if (types_str) + ret = g_strdup(types_str); + else + ret = g_strdup(UNKNOWN_MEM_TYPE_STRING); + g_free(types_str); + return ret; +} + +uint64_t memory_devices_get_system_memory_MiB() { + dmi_mem *mem = dmi_mem_new(); + int ret = (int)mem->system_memory_MiB; + dmi_mem_free(mem); + return ret; +} + +gchar *memory_devices_get_system_memory_str() { + gchar *ret = NULL; + dmi_mem_size m = memory_devices_get_system_memory_MiB(); + if (m) { + if (m > 1024 && (m % 1024 == 0) ) + ret = g_strdup_printf("%"PRId64" %s", m/1024, _("GiB")); + else + ret = g_strdup_printf("%"PRId64" %s", m, _("MiB")); + } + return ret; +} + +static gchar note_state[note_max_len] = ""; + +gboolean memory_devices_hinote(const char **msg) { + gchar *want_dmi = _(" <b><i>dmidecode</i></b> utility available"); + gchar *want_root = _(" ... <i>and</i> HardInfo running with superuser privileges"); + gchar *want_at24 = _(" <b><i>at24</i></b> (or eeprom) module loaded (for SDR, DDR, DDR2, DDR3)"); + gchar *want_ee1004 = _(" ... <i>or</i> <b><i>ee1004</i></b> module loaded <b>and configured!</b> (for DDR4)"); + + gboolean has_root = (getuid() == 0); + gboolean has_dmi = !no_handles; + gboolean has_at24eep = g_file_test("/sys/bus/i2c/drivers/at24", G_FILE_TEST_IS_DIR) || + g_file_test("/sys/bus/i2c/drivers/eeprom", G_FILE_TEST_IS_DIR); + gboolean has_ee1004 = g_file_test("/sys/bus/i2c/drivers/ee1004", G_FILE_TEST_IS_DIR); + + *note_state = 0; /* clear */ + note_printf(note_state, "%s\n", _("Memory information requires <b>one or both</b> of the following:")); + note_print(note_state, "<tt>1. </tt>"); + note_cond_bullet(has_dmi, note_state, want_dmi); + note_print(note_state, "<tt> </tt>"); + note_cond_bullet(has_root, note_state, want_root); + note_print(note_state, "<tt>2. </tt>"); + note_cond_bullet(has_at24eep, note_state, want_at24); + note_print(note_state, "<tt> </tt>"); + note_cond_bullet(has_ee1004, note_state, want_ee1004); + g_strstrip(note_state); /* remove last \n */ + + gboolean ddr3_ee1004 = ((dmi_ram_types & (1<<(DDR3_SDRAM-1))) && has_ee1004); + + gboolean best_state = FALSE; + if (has_dmi && has_root && + ((has_at24eep && !spd_ddr4_partial_data) + || (has_ee1004 && !ddr3_ee1004) ) ) + best_state = TRUE; + + if (!best_state) { + *msg = note_state; + return TRUE; + } + + if (sketchy_info) { + *msg = g_strdup( + _("\"More often than not, information contained in the DMI tables is inaccurate,\n" + "incomplete or simply wrong.\" -<i><b>dmidecode</b></i> manual page")); + return TRUE; + } + + return FALSE; +} diff --git a/modules/devices/e2k/processor.c b/modules/devices/e2k/processor.c new file mode 100644 index 00000000..74476853 --- /dev/null +++ b/modules/devices/e2k/processor.c @@ -0,0 +1,420 @@ +/* + * HardInfo - Displays System Information + * Copyright (C) 2020 EntityFX <artem.solopiy@gmail.com> and MCST Elbrus Team + * modified by Boris Afonot <boris.afonot@gmail.com> (2022) + * 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 "devices.h" +#include "cpu_util.h" + +static gchar *__cache_get_info_as_string(Processor *processor) +{ + gchar *result = g_strdup(""); + GSList *cache_list; + ProcessorCache *cache; + + if (!processor->cache) { + return g_strdup(_("Cache information not available=\n")); + } + + for (cache_list = processor->cache; cache_list; cache_list = cache_list->next) { + cache = (ProcessorCache *)cache_list->data; + + result = h_strdup_cprintf(_("Level %d (%s)=%d-way set-associative, %d sets, %dKB size\n"), + result, + cache->level, + C_("cache-type", cache->type), + cache->ways_of_associativity, + cache->number_of_sets, + cache->size); + } + + return result; +} + +/* This is not used directly, but creates translatable strings for + * the type string returned from /sys/.../cache */ +static const char* cache_types[] = { + NC_("cache-type", /*/cache type, as appears in: Level 1 (Data)*/ "Data"), + NC_("cache-type", /*/cache type, as appears in: Level 1 (Instruction)*/ "Instruction"), + NC_("cache-type", /*/cache type, as appears in: Level 2 (Unified)*/ "Unified") +}; + +static void __cache_obtain_info(Processor *processor) +{ + ProcessorCache *cache; + gchar *endpoint, *entry, *index; + gchar *uref = NULL; + gint i; + gint processor_number = processor->id; + + endpoint = g_strdup_printf("/sys/devices/system/cpu/cpu%d/cache", processor_number); + + for (i = 0; ; i++) { + cache = g_new0(ProcessorCache, 1); + + index = g_strdup_printf("index%d/", i); + + entry = g_strconcat(index, "type", NULL); + cache->type = h_sysfs_read_string(endpoint, entry); + g_free(entry); + + if (!cache->type) { + g_free(cache); + g_free(index); + goto fail; + } + + entry = g_strconcat(index, "level", NULL); + cache->level = h_sysfs_read_int(endpoint, entry); + g_free(entry); + + entry = g_strconcat(index, "number_of_sets", NULL); + cache->number_of_sets = h_sysfs_read_int(endpoint, entry); + g_free(entry); + + entry = g_strconcat(index, "physical_line_partition", NULL); + cache->physical_line_partition = h_sysfs_read_int(endpoint, entry); + g_free(entry); + + entry = g_strconcat(index, "size", NULL); + cache->size = h_sysfs_read_int(endpoint, entry); + g_free(entry); + + entry = g_strconcat(index, "ways_of_associativity", NULL); + cache->ways_of_associativity = h_sysfs_read_int(endpoint, entry); + g_free(entry); + + /* unique cache references: id is nice, but share_cpu_list can be + * used if it is not available. */ + entry = g_strconcat(index, "id", NULL); + uref = h_sysfs_read_string(endpoint, entry); + g_free(entry); + if (uref != NULL && *uref != 0 ) + cache->uid = atoi(uref); + else + cache->uid = -1; + g_free(uref); + entry = g_strconcat(index, "shared_cpu_list", NULL); + cache->shared_cpu_list = h_sysfs_read_string(endpoint, entry); + g_free(entry); + + /* reacharound */ + entry = g_strconcat(index, "../../topology/physical_package_id", NULL); + cache->phy_sock = h_sysfs_read_int(endpoint, entry); + g_free(entry); + + g_free(index); + + processor->cache = g_slist_append(processor->cache, cache); + } + +fail: + g_free(endpoint); +} + +#define cmp_cache_test(f) if (a->f < b->f) return -1; if (a->f > b->f) return 1; + +static gint cmp_cache(ProcessorCache *a, ProcessorCache *b) { + gint i = 0; + cmp_cache_test(phy_sock); + i = g_strcmp0(a->type, b->type); if (i!=0) return i; + cmp_cache_test(level); + cmp_cache_test(size); + cmp_cache_test(uid); /* uid is unique among caches with the same (type, level) */ + if (a->uid == -1) { + /* if id wasn't available, use shared_cpu_list as a unique ref */ + i = g_strcmp0(a->shared_cpu_list, b->shared_cpu_list); if (i!=0) + return i; + } + return 0; +} + +static gint cmp_cache_ignore_id(ProcessorCache *a, ProcessorCache *b) { + gint i = 0; + cmp_cache_test(phy_sock); + i = g_strcmp0(a->type, b->type); if (i!=0) return i; + cmp_cache_test(level); + cmp_cache_test(size); + return 0; +} + +gchar *caches_summary(GSList * processors) +{ + gchar *ret = g_strdup_printf("[%s]\n", _("Caches")); + GSList *all_cache = NULL, *uniq_cache = NULL; + GSList *tmp, *l; + Processor *p; + ProcessorCache *c, *cur = NULL; + gint cur_count = 0, i = 0; + + /* create list of all cache references */ + for (l = processors; l; l = l->next) { + p = (Processor*)l->data; + if (p->cache) { + tmp = g_slist_copy(p->cache); + if (all_cache) { + all_cache = g_slist_concat(all_cache, tmp); + } else { + all_cache = tmp; + } + } + } + + if (g_slist_length(all_cache) == 0) { + ret = h_strdup_cprintf("%s=\n", ret, _("(Not Available)") ); + g_slist_free(all_cache); + return ret; + } + + /* ignore duplicate references */ + all_cache = g_slist_sort(all_cache, (GCompareFunc)cmp_cache); + for (l = all_cache; l; l = l->next) { + c = (ProcessorCache*)l->data; + if (!cur) { + cur = c; + } else { + if (cmp_cache(cur, c) != 0) { + uniq_cache = g_slist_prepend(uniq_cache, cur); + cur = c; + } + } + } + uniq_cache = g_slist_prepend(uniq_cache, cur); + uniq_cache = g_slist_reverse(uniq_cache); + cur = 0, cur_count = 0; + + /* count and list caches */ + for (l = uniq_cache; l; l = l->next) { + c = (ProcessorCache*)l->data; + if (!cur) { + cur = c; + cur_count = 1; + } else { + if (cmp_cache_ignore_id(cur, c) != 0) { + ret = h_strdup_cprintf(_("Level %d (%s)#%d=%dx %dKB (%dKB), %d-way set-associative, %d sets\n"), + ret, + cur->level, + C_("cache-type", cur->type), + cur->phy_sock, + cur_count, + cur->size, + cur->size * cur_count, + cur->ways_of_associativity, + cur->number_of_sets); + cur = c; + cur_count = 1; + } else { + cur_count++; + } + } + } + ret = h_strdup_cprintf(_("Level %d (%s)#%d=%dx %dKB (%dKB), %d-way set-associative, %d sets\n"), + ret, + cur->level, + C_("cache-type", cur->type), + cur->phy_sock, + cur_count, + cur->size, + cur->size * cur_count, + cur->ways_of_associativity, + cur->number_of_sets); + + g_slist_free(all_cache); + g_slist_free(uniq_cache); + return ret; +} + +static gchar *processor_get_full_name(const gchar *model_name) +{ + if(g_strcmp0(model_name ,"E2S") == 0) + return "Elbrus-4C"; + else if(g_strcmp0(model_name ,"E1C+") == 0) + return "Elbrus-1C+"; + else if(g_strcmp0(model_name ,"E2C+DSP") == 0) + return "Elbrus-2C+"; + else if(g_strcmp0(model_name ,"E8C") == 0) + return "Elbrus-8C"; + else if(g_strcmp0(model_name ,"E8C2") == 0) + return "Elbrus-8CB"; + else if(g_strcmp0(model_name ,"E12C") == 0) + return "Elbrus-12C"; + else if(g_strcmp0(model_name ,"E16C") == 0) + return "Elbrus-16C"; + else if(g_strcmp0(model_name ,"E2C3") == 0) + return "Elbrus-2C3"; + else + return (gchar *)model_name; +} + +GSList *processor_scan(void) +{ + GSList *procs = NULL, *l = NULL; + Processor *processor = NULL; + FILE *cpuinfo; + gchar buffer[1024]; + + cpuinfo = fopen(PROC_CPUINFO, "r"); + if (!cpuinfo) + return NULL; + + while (fgets(buffer, 1024, cpuinfo)) { + gchar **tmp = g_strsplit(buffer, ":", 2); + if (!tmp[1] || !tmp[0]) { + g_strfreev(tmp); + continue; + } + + tmp[0] = g_strstrip(tmp[0]); + tmp[1] = g_strstrip(tmp[1]); + + if (g_str_has_prefix(tmp[0], "processor")) { + /* finish previous */ + if (processor) + procs = g_slist_append(procs, processor); + + /* start next */ + processor = g_new0(Processor, 1); + processor->id = atol(tmp[1]); + g_strfreev(tmp); + continue; + } + + if (processor) { + if (g_str_has_prefix(tmp[0], "model name")) { + const gchar *model_name = processor_get_full_name(tmp[1]); + processor->model_name = g_strdup(model_name); + g_strfreev(tmp); + continue; + } + + get_str("vendor_id", processor->vendor_id); + get_int("cpu family", processor->family); + get_int("model", processor->model); + get_int("revision", processor->revision); + + get_float("cpu MHz", processor->cpu_mhz); + get_float("bogomips", processor->bogomips); + } + + //populate processor structure + g_strfreev(tmp); + } + + //appent to the list + if (processor) + procs = g_slist_append(procs, processor); + + for (l = procs; l; l = l->next) { + processor = (Processor *) l->data; + __cache_obtain_info(processor); + } + + fclose(cpuinfo); + + return procs; +} + +gchar *processor_name(GSList * processors) { + return processor_name_default(processors); +} + +gchar *processor_describe(GSList * processors) { + return processor_describe_default(processors); +} + +gchar * +processor_get_detailed_info(Processor *processor) +{ + gchar *ret; + gchar *cache_info; + cache_info = __cache_get_info_as_string(processor); + + ret = g_strdup_printf("[%s]\n" + "$^$%s=%s\n" /* name */ + "$^$%s=%s\n" /* vendor */ + "%s=%d\n" /* family */ + "%s=%d\n" /* model */ + "%s=%d\n" /* revision */ + "%s=%.2f %s\n" /* frequency */ + "%s=%.2f\n" /* bogomips */ + "%s=%s\n" /* byte order */ + "[%s]\n" /* cache */ + "%s\n", + _("Processor"), + _("Name"), processor->model_name, + _("Vendor"), processor->vendor_id, + _("Family"), processor->family, + _("Model"), processor->model, + _("Revision"), processor->revision, + _("Frequency"), processor->cpu_mhz, _("MHz"), + _("BogoMips"), processor->bogomips, + _("Byte Order"), byte_order_str(), + _("Cache"), cache_info + ); + g_free(cache_info); + return ret; +} + +//prepare processor info for all cpus +gchar *processor_get_info(GSList * processors) +{ + Processor *processor; + + gchar *ret, *tmp, *hashkey; + GSList *l; + + tmp = g_strdup(""); + + for (l = processors; l; l = l->next) { + processor = (Processor *) l->data; + gchar *model_name = g_strdup_printf("MCST %s", processor->model_name); + + /* change vendor id of 8CB processor for correct parse from vendor.ids */ + if (!g_strcmp0(processor->vendor_id, "E8C")) { + gchar *orig_vendor_id = processor->vendor_id; + processor->vendor_id = g_strdup_printf("%s-SWTX", orig_vendor_id); + free(orig_vendor_id); + } + + const Vendor *v = vendor_match(processor->vendor_id, NULL); + if (v) + tag_vendor(&model_name, 0, v->name_short ? v->name_short : v->name, v->ansi_color, params.fmt_opts); + + tmp = g_strdup_printf("%s$CPU%d$cpu%d=%.2f %s|%s\n", + tmp, processor->id, + processor->id, + processor->cpu_mhz, _("MHz"), + model_name); + + hashkey = g_strdup_printf("CPU%d", processor->id); + moreinfo_add_with_prefix("DEV", hashkey, + processor_get_detailed_info(processor)); + g_free(hashkey); + } + + ret = g_strdup_printf("[$ShellParam$]\n" + "ViewType=1\n" + "ColumnTitle$TextValue=%s\n" + "ColumnTitle$Value=%s\n" + "ColumnTitle$Extra1=%s\n" + "ShowColumnHeaders=true\n" + "[Processors]\n" + "%s", _("Device"), _("Frequency"), _("Model"), tmp); + g_free(tmp); + + return ret; +} diff --git a/modules/devices/firmware.c b/modules/devices/firmware.c new file mode 100644 index 00000000..ea6ce532 --- /dev/null +++ b/modules/devices/firmware.c @@ -0,0 +1,261 @@ +/* + * HardInfo - Displays System Information + * Copyright (C) 2003-2019 L. A. F. Pereira <l@tia.mat.br> + * Copyright (C) 2019 Burt P. <pburt0@gmail.com> + * Copyright (C) 2020 Ondrej Čerman + * + * 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 or later. + * + * 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 "devices.h" +#include <inttypes.h> +#include <gio/gio.h> +#include "util_sysobj.h" /* for SEQ() and appf() */ + +#define fw_msg(msg, ...) fprintf (stderr, "[%s] " msg "\n", __FUNCTION__, ##__VA_ARGS__) /**/ + +#define FWUPT_INTERFACE "org.freedesktop.fwupd" + +gboolean fail_no_fwupd = TRUE; + +char *decode_flags(guint64 flags) { + /* https://github.com/hughsie/fwupd/blob/master/libfwupd/fwupd-enums.{h,c} */ + static const struct { guint64 b; char *flag, *def; } flag_defs[] = { + { (1u << 0), "internal", N_("Device cannot be removed easily") }, + { (1u << 1), "updatable", N_("Device is updatable in this or any other mode") }, + { (1u << 2), "only-offline", N_("Update can only be done from offline mode") }, + { (1u << 3), "require-ac", N_("Requires AC power") }, + { (1u << 4), "locked", N_("Is locked and can be unlocked") }, + { (1u << 5), "supported", N_("Is found in current metadata") }, + { (1u << 6), "needs-bootloader", N_("Requires a bootloader mode to be manually enabled by the user") }, + { (1u << 7), "registered", N_("Has been registered with other plugins") }, + { (1u << 8), "needs-reboot", N_("Requires a reboot to apply firmware or to reload hardware") }, + { (1u << 17), "needs-shutdown", N_("Requires system shutdown to apply firmware") }, + { (1u << 9), "reported", N_("Has been reported to a metadata server") }, + { (1u << 10), "notified", N_("User has been notified") }, + { (1u << 11), "use-runtime-version", N_("Always use the runtime version rather than the bootloader") }, + { (1u << 12), "install-parent-first", N_("Install composite firmware on the parent before the child") }, + { (1u << 13), "is-bootloader", N_("Is currently in bootloader mode") }, + { (1u << 14), "wait-for-replug", N_("The hardware is waiting to be replugged") }, + { (1u << 15), "ignore-validation", N_("Ignore validation safety checks when flashing this device") }, + { (1u << 18), "another-write-required", N_("Requires the update to be retried with a new plugin") }, + { (1u << 19), "no-auto-instance-ids", N_("Do not add instance IDs from the device baseclass") }, + { (1u << 20), "needs-activation", N_("Device update needs to be separately activated") }, + { (1u << 21), "ensure-semver", N_("Ensure the version is a valid semantic version, e.g. numbers separated with dots") }, + { (1u << 16), "trusted", N_("Extra metadata can be exposed about this device") }, + { 0, NULL } + }; + + gchar *flag_list = g_strdup(""); + + int i; + for (i = 0; flag_defs[i].flag; i++) { + if (flags & flag_defs[i].b) + flag_list = appfnl(flag_list, "[%s] %s", flag_defs[i].flag, flag_defs[i].def); + } + return flag_list; +} + +const char *find_translation(const char *str) { + /* TODO: https://github.com/hughsie/fwupd/blob/master/src/README.md */ + static const char *translatable[] = { + N_("DeviceId"), N_("Guid"), N_("Summary"), N_("Plugin"), N_("Flags"), + N_("Vendor"), N_("VendorId"), N_("Version"), N_("VersionBootloader"), + N_("Icon"), N_("InstallDuration"), N_("Created"), + NULL + }; + int i; + if (!str) return NULL; + for (i = 0; translatable[i]; i++) { + if (SEQ(str, translatable[i])) + return _(translatable[i]); + } + return str; +}; + +/* map lvfs icon names to hardinfo icon names */ +const char *find_icon(const char *lvfs_name) { + /* icon names found by looking for fu_device_add_icon () + * in the fwupd source. */ + static const + struct { char *lvfs, *hi; } imap[] = { + { "applications-internet", "dns.png" }, + { "audio-card", "audio.png" }, + { "computer", "computer.png" }, + { "drive-harddisk", "hdd.png" }, + { "input-gaming", "joystick.png" }, + { "input-tablet", NULL }, + { "network-modem", "wireless.png" }, + { "preferences-desktop-keyboard", "keyboard.png" }, + { "thunderbolt", NULL }, + { "touchpad-disabled", NULL }, + /* default */ + { NULL, "memory.png" } /* a device with firmware maybe */ + }; + unsigned int i = 0; + for(; imap[i].lvfs; i++) { + if (SEQ(imap[i].lvfs, lvfs_name) && imap[i].hi) + return imap[i].hi; + } + return imap[i].hi; +} + +gchar *fwupdmgr_get_devices_info() { + struct Info *info = info_new(); + struct InfoGroup *this_group = NULL; + gboolean has_vendor_field = FALSE; + gboolean updatable = FALSE; + const Vendor *gv = NULL; + int gc = 0; + + GDBusConnection *conn; + GDBusProxy *proxy; + GVariant *devices, *value; + GVariantIter *deviter, *dictiter, *iter; + const gchar *key, *tmpstr; + + conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL); + if (!conn) + return g_strdup(""); + + proxy = g_dbus_proxy_new_sync(conn, G_DBUS_PROXY_FLAGS_NONE, NULL, + FWUPT_INTERFACE, "/", FWUPT_INTERFACE, + NULL, NULL); + if (!proxy) { + g_object_unref(conn); + return g_strdup(""); + } + + fail_no_fwupd = FALSE; + devices = g_dbus_proxy_call_sync(proxy, "GetDevices", NULL, + G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL); + + if (devices) { + g_variant_get(devices, "(aa{sv})", &deviter); + while(g_variant_iter_loop(deviter, "a{sv}", &dictiter)){ + + this_group = info_add_group(info, _("Unknown"), info_field_last()); + this_group->sort = INFO_GROUP_SORT_NAME_DESCENDING; + has_vendor_field = FALSE; + updatable = FALSE; + gv = NULL; + + while (g_variant_iter_loop(dictiter, "{&sv}", &key, &value)) { + if (SEQ(key, "Name")) { + tmpstr = g_variant_get_string(value, NULL); + this_group->name = hardinfo_clean_grpname(tmpstr, 0); + gv = vendor_match(tmpstr, NULL); + } else if (SEQ(key, "Vendor")) { + has_vendor_field = TRUE; + tmpstr = g_variant_get_string(value, NULL); + + const Vendor* v = vendor_match(tmpstr, NULL); + if (v) { + info_group_add_field(this_group, + info_field(_("Vendor"), v->name, + .value_has_vendor = TRUE, + .free_value_on_flatten = FALSE) ); + } else { + info_group_add_field(this_group, + info_field(_("Vendor"), g_strdup(tmpstr), + .free_value_on_flatten = TRUE) ); + } + } else if (SEQ(key, "Icon")) { + g_variant_get(value, "as", &iter); + while (g_variant_iter_loop(iter, "s", &tmpstr)) { + info_group_add_field(this_group, + info_field(_("Icon"), g_strdup(tmpstr), + .free_value_on_flatten = TRUE, + .icon = g_strdup(find_icon(tmpstr)) ) ); + } + } else if (SEQ(key, "Guid")) { + g_variant_get(value, "as", &iter); + while (g_variant_iter_loop(iter, "s", &tmpstr)) { + info_group_add_field(this_group, + info_field(_("Guid"), g_strdup(tmpstr), + .tag = g_strdup_printf("guid%d", gc++), + .free_value_on_flatten = TRUE) ); + } + g_variant_iter_free(iter); + } else if (SEQ(key, "Created")) { + guint64 created = g_variant_get_uint64(value); + GDateTime *dt = g_date_time_new_from_unix_local(created); + if (dt) { + info_group_add_field(this_group, + info_field(_("Created"), g_date_time_format(dt, "%x"), + .free_value_on_flatten = TRUE) ); + g_date_time_unref(dt); + } + } else if (SEQ(key, "Flags")) { + guint64 flags = g_variant_get_uint64(value); + updatable = (gboolean)(flags & (1u << 1)); + info_group_add_field(this_group, + info_field(_("Flags"), decode_flags(flags), + .free_value_on_flatten = TRUE) ); + } else { + if (g_variant_is_of_type(value, G_VARIANT_TYPE_STRING)) { + info_group_add_field(this_group, + info_field(find_translation(key), + g_variant_dup_string(value, NULL), + .free_value_on_flatten = TRUE) ); + } + } + } + + if (gv && !has_vendor_field) { + info_group_add_field(this_group, + info_field(_("Vendor"), gv->name, + .value_has_vendor = TRUE, + .free_value_on_flatten = FALSE) ); + } + + // hide devices that are not updatable + if (!updatable) { + info_remove_group(info, info->groups->len - 1); + } + } + g_variant_iter_free(deviter); + g_variant_unref(devices); + } + + g_object_unref(proxy); + g_object_unref(conn); + + gchar *ret = NULL; + if (info->groups->len) { + info_set_view_type(info, SHELL_VIEW_DETAIL); + //fw_msg("flatten..."); + ret = info_flatten(info); + //fw_msg("ret: %s", ret); + } else { + ret = g_strdup_printf("[%s]\n%s=%s\n" "[$ShellParam$]\nViewType=0\n", + _("Firmware List"), + _("Result"), _("(Not available)") ); + } + return ret; +} + +gchar *firmware_get_info() { + return fwupdmgr_get_devices_info(); +} + +gboolean firmware_hinote(const char **msg) { + if (fail_no_fwupd) { + *msg = g_strdup( + _("Requires the <i><b>fwupd</b></i> daemon.")); + return TRUE; + } + return FALSE; +} diff --git a/modules/devices/gpu.c b/modules/devices/gpu.c new file mode 100644 index 00000000..96520161 --- /dev/null +++ b/modules/devices/gpu.c @@ -0,0 +1,291 @@ +/* + * HardInfo - Displays System Information + * Copyright (C) 2003-2017 L. A. F. Pereira <l@tia.mat.br> + * This file + * Copyright (C) 2018 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 or later. + * + * 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 "devices.h" +#include "gpu_util.h" + +gchar *gpu_list = NULL; +gchar *gpu_summary = NULL; + +void gpu_summary_add(const char *gpu_name) { + if (strlen(gpu_summary) == 0) { + /* first one */ + gpu_summary = h_strdup_cprintf("%s", gpu_summary, gpu_name); + } else { + /* additional */ + gpu_summary = h_strdup_cprintf(" + %s", gpu_summary, gpu_name); + } +} + +#define UNKIFNULL_AC(f) (f != NULL) ? f : _("(Unknown)"); + +static void _gpu_pci_dev(gpud* gpu) { + pcid *p = gpu->pci_dev; + gchar *str; + gchar *vendor, *svendor, *product, *sproduct; + gchar *name, *key; + gchar *drm_path = NULL; + + gboolean vendor_is_svendor = (p->vendor_id == p->sub_vendor_id && p->device_id == p->sub_device_id); + + vendor = UNKIFNULL_AC(p->vendor_id_str); + svendor = UNKIFNULL_AC(p->sub_vendor_id_str); + product = UNKIFNULL_AC(p->device_id_str); + sproduct = UNKIFNULL_AC(p->sub_device_id_str); + if (gpu->drm_dev) + drm_path = g_strdup_printf("/dev/dri/%s", gpu->drm_dev); + else + drm_path = g_strdup(_("(Unknown)")); + + gchar *ven_tag = vendor_match_tag(p->vendor_id_str, params.fmt_opts); + gchar *sven_tag = vendor_match_tag(p->sub_vendor_id_str, params.fmt_opts); + if (ven_tag) { + if (sven_tag && !vendor_is_svendor) { + name = g_strdup_printf("%s %s %s", sven_tag, ven_tag, product); + } else { + name = g_strdup_printf("%s %s", ven_tag, product); + } + } else { + name = g_strdup_printf("%s %s", vendor, product); + } + g_free(ven_tag); + g_free(sven_tag); + + key = g_strdup_printf("GPU%s", gpu->id); + + gpu_summary_add((gpu->nice_name) ? gpu->nice_name : name); + gpu_list = h_strdup_cprintf("$!%s$%s=%s\n", gpu_list, key, gpu->id, name); + + gchar *vendor_device_str; + if (p->vendor_id == p->sub_vendor_id && p->device_id == p->sub_device_id) { + vendor_device_str = g_strdup_printf( + /* Vendor */ "$^$%s=[%04x] %s\n" + /* Device */ "%s=[%04x] %s\n", + _("Vendor"), p->vendor_id, vendor, + _("Device"), p->device_id, product); + } else { + vendor_device_str = g_strdup_printf( + /* Vendor */ "$^$%s=[%04x] %s\n" + /* Device */ "%s=[%04x] %s\n" + /* Sub-device vendor */ "$^$%s=[%04x] %s\n" + /* Sub-device */ "%s=[%04x] %s\n", + _("Vendor"), p->vendor_id, vendor, + _("Device"), p->device_id, product, + _("SVendor"), p->sub_vendor_id, svendor, + _("SDevice"), p->sub_device_id, sproduct); + } + + gchar *pcie_str; + if (p->pcie_width_curr) { + pcie_str = g_strdup_printf("[%s]\n" + /* Width (max) */ "%s=x%u\n" + /* Speed (max) */ "%s=%0.1f %s\n", + _("PCI Express"), + _("Maximum Link Width"), p->pcie_width_max, + _("Maximum Link Speed"), p->pcie_speed_max, _("GT/s") ); + } else + pcie_str = strdup(""); + + gchar *nv_str; + if (gpu->nv_info) { + nv_str = g_strdup_printf("[%s]\n" + /* model */ "%s=%s\n" + /* bios */ "%s=%s\n" + /* uuid */ "%s=%s\n", + _("NVIDIA"), + _("Model"), gpu->nv_info->model, + _("BIOS Version"), gpu->nv_info->bios_version, + _("UUID"), gpu->nv_info->uuid ); + } else + nv_str = strdup(""); + + gchar *freq = g_strdup(_("(Unknown)")); + if (gpu->khz_max > 0) { + if (gpu->khz_min > 0 && gpu->khz_min != gpu->khz_max) + freq = g_strdup_printf("%0.2f-%0.2f %s", (double) gpu->khz_min / 1000, (double) gpu->khz_max / 1000, _("MHz")); + else + freq = g_strdup_printf("%0.2f %s", (double) gpu->khz_max / 1000, _("MHz")); + } + + gchar *mem_freq = g_strdup(_("(Unknown)")); + if (gpu->mem_khz_max > 0) { + if (gpu->mem_khz_min > 0 && gpu->mem_khz_min != gpu->mem_khz_max) + mem_freq = g_strdup_printf("%0.2f-%0.2f %s", (double) gpu->mem_khz_min / 1000, (double) gpu->mem_khz_max / 1000, _("MHz")); + else + mem_freq = g_strdup_printf("%0.2f %s", (double) gpu->mem_khz_max / 1000, _("MHz")); + } + + str = g_strdup_printf("[%s]\n" + /* Location */ "%s=%s\n" + /* DRM Dev */ "%s=%s\n" + /* Class */ "%s=[%04x] %s\n" + "%s" + /* Revision */ "%s=%02x\n" + "[%s]\n" + /* Core freq */ "%s=%s\n" + /* Mem freq */ "%s=%s\n" + /* NV */ "%s" + /* PCIe */ "%s" + "[%s]\n" + /* Driver */ "%s=%s\n" + /* Modules */ "%s=%s\n", + _("Device Information"), + _("Location"), gpu->location, + _("DRM Device"), drm_path, + _("Class"), p->class, p->class_str, + vendor_device_str, + _("Revision"), p->revision, + _("Clocks"), + _("Core"), freq, + _("Memory"), mem_freq, + nv_str, + pcie_str, + _("Driver"), + _("In Use"), (p->driver) ? p->driver : _("(Unknown)"), + _("Kernel Modules"), (p->driver_list) ? p->driver_list : _("(Unknown)") + ); + + moreinfo_add_with_prefix("DEV", key, str); /* str now owned by morinfo */ + + g_free(drm_path); + g_free(pcie_str); + g_free(nv_str); + g_free(vendor_device_str); + g_free(name); + g_free(key); +} + +int _dt_soc_gpu(gpud *gpu) { + static char UNKSOC[] = "(Unknown)"; /* don't translate this */ + gchar *vendor = gpu->vendor_str; + gchar *device = gpu->device_str; + if (vendor == NULL) vendor = UNKSOC; + if (device == NULL) device = UNKSOC; + gchar *freq = g_strdup(_("(Unknown)")); + if (gpu->khz_max > 0) { + if (gpu->khz_min > 0) + freq = g_strdup_printf("%0.2f-%0.2f %s", (double) gpu->khz_min / 1000, (double) gpu->khz_max / 1000, _("MHz")); + else + freq = g_strdup_printf("%0.2f %s", (double) gpu->khz_max / 1000, _("MHz")); + } + gchar *key = g_strdup(gpu->id); + + gchar *name = NULL; + gchar *vtag = vendor_match_tag(gpu->vendor_str, params.fmt_opts); + if (vtag) { + name = g_strdup_printf("%s %s", vtag, device); + } else { + name = (vendor == UNKSOC && device == UNKSOC) + ? g_strdup(_("Unknown integrated GPU")) + : g_strdup_printf("%s %s", vendor, device); + } + g_free(vtag); + + gchar *opp_str; + if (gpu->dt_opp) { + static const char *freq_src[] = { + N_("clock-frequency property"), + N_("Operating Points (OPPv1)"), + N_("Operating Points (OPPv2)"), + }; + opp_str = g_strdup_printf("[%s]\n" + /* MinFreq */ "%s=%d %s\n" + /* MaxFreq */ "%s=%d %s\n" + /* Latency */ "%s=%d %s\n" + /* Source */ "%s=%s\n", + _("Frequency Scaling"), + _("Minimum"), gpu->dt_opp->khz_min, _("kHz"), + _("Maximum"), gpu->dt_opp->khz_max, _("kHz"), + _("Transition Latency"), gpu->dt_opp->clock_latency_ns, _("ns"), + _("Source"), _(freq_src[gpu->dt_opp->version]) ); + } else + opp_str = strdup(""); + + gpu_summary_add((gpu->nice_name) ? gpu->nice_name : name); + gpu_list = h_strdup_cprintf("$!%s$%s=%s\n", gpu_list, key, key, name); + gchar *str = g_strdup_printf("[%s]\n" + /* Location */ "%s=%s\n" + /* Vendor */ "$^$%s=%s\n" + /* Device */ "%s=%s\n" + "[%s]\n" + /* Freq */ "%s=%s\n" + /* opp-v2 */ "%s" + "[%s]\n" + /* Path */ "%s=%s\n" + /* Compat */ "%s=%s\n" + /* Status */ "%s=%s\n" + /* Name */ "%s=%s\n", + _("Device Information"), + _("Location"), gpu->location, + _("Vendor"), vendor, + _("Device"), device, + _("Clocks"), + _("Core"), freq, + opp_str, + _("Device Tree Node"), + _("Path"), gpu->dt_path, + _("Compatible"), gpu->dt_compat, + _("Status"), gpu->dt_status, + _("Name"), gpu->dt_name + ); + moreinfo_add_with_prefix("DEV", key, str); /* str now owned by morinfo */ + g_free(freq); + g_free(opp_str); + return 1; +} + +void scan_gpu_do(void) { + if (gpu_summary) + g_free(gpu_summary); + if (gpu_list) { + moreinfo_del_with_prefix("DEV:GPU"); + g_free(gpu_list); + } + gpu_summary = strdup(""); + gpu_list = g_strdup_printf("[%s]\n", _("GPUs")); + + gpud *gpus = gpu_get_device_list(); + gpud *curr = gpus; + + int c = gpud_list_count(gpus); + + if (c > 0) { + while(curr) { + if (curr->pci_dev) { + _gpu_pci_dev(curr); + } + if (curr->dt_compat) { + _dt_soc_gpu(curr); + } + curr=curr->next; + } + } + gpud_list_free(gpus); + + if (c) + gpu_list = g_strconcat(gpu_list, "[$ShellParam$]\n", "ViewType=1\n", NULL); + else { + /* NO GPU? */ + gpu_list = g_strconcat(gpu_list, _("No GPU devices found"), "=\n", NULL); + } +} diff --git a/modules/devices/ia64/processor.c b/modules/devices/ia64/processor.c new file mode 100644 index 00000000..f31813bc --- /dev/null +++ b/modules/devices/ia64/processor.c @@ -0,0 +1,215 @@ +/* + * HardInfo - Displays System Information + * Copyright (C) 2003-2006 L. A. F. Pereira <l@tia.mat.br> + * + * 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 or later. + * + * 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 "devices.h" +#include "cpu_util.h" + +GSList * +processor_scan(void) +{ + GSList *procs = NULL; + Processor *processor = NULL; + FILE *cpuinfo; + gchar buffer[128]; + gchar *rep_pname = NULL; + GSList *pi = NULL; + + cpuinfo = fopen(PROC_CPUINFO, "r"); + if (!cpuinfo) + return NULL; + +#define CHECK_FOR(k) (g_str_has_prefix(tmp[0], k)) + while (fgets(buffer, 128, cpuinfo)) { + gchar **tmp = g_strsplit(buffer, ":", 2); + if (tmp[0] && tmp[1]) { + tmp[0] = g_strstrip(tmp[0]); + tmp[1] = g_strstrip(tmp[1]); + } else { + g_strfreev(tmp); + continue; + } + + get_str("Processor", rep_pname); + + if ( CHECK_FOR("processor") ) { + /* finish previous */ + if (processor) { + procs = g_slist_append(procs, processor); + } + + /* start next */ + processor = g_new0(Processor, 1); + processor->id = atol(tmp[1]); + + if (rep_pname) + processor->model_name = g_strdup(rep_pname); + + g_strfreev(tmp); + continue; + } + + if (!processor && + ( CHECK_FOR("vendor") + || CHECK_FOR("arch") + || CHECK_FOR("family") ) ) { + + /* single proc/core may not have "processor : n" */ + processor = g_new0(Processor, 1); + processor->id = 0; + + if (rep_pname) + processor->model_name = g_strdup(rep_pname); + } + + if (processor) { + get_str("vendor", processor->vendor_id); + get_str("archrev", processor->archrev); + get_str("arch", processor->arch); + get_str("family", processor->family); + get_str("features", processor->features); + get_int("model", processor->model); + get_int("revision", processor->revision); + get_float("BogoMIPS", processor->bogomips); + get_float("cpu MHz", processor->cpu_mhz); + get_int("cpu regs", processor->cpu_regs); + } + g_strfreev(tmp); + } + + if (processor) + procs = g_slist_append(procs, processor); + + g_free(rep_pname); + fclose(cpuinfo); + + /* TODO: redup */ + + /* data not from /proc/cpuinfo */ + for (pi = procs; pi; pi = pi->next) { + processor = (Processor *) pi->data; + + /* strings can't be null or segfault later */ + STRIFNULL(processor->model_name, _("IA64 Processor") ); + UNKIFNULL(processor->vendor_id); + STRIFNULL(processor->arch, "IA-64"); + STRIFNULL(processor->archrev, "0"); + UNKIFNULL(processor->family); + UNKIFNULL(processor->features); + + /* topo & freq */ + processor->cpufreq = cpufreq_new(processor->id); + processor->cputopo = cputopo_new(processor->id); + + if (processor->cpufreq->cpukhz_max) + processor->cpu_mhz = processor->cpufreq->cpukhz_max / 1000; + + } + + return procs; +} + +gchar *processor_name(GSList * processors) { + return processor_name_default(processors); +} + +gchar *processor_describe(GSList * processors) { + return processor_describe_default(processors); +} + +gchar * +processor_get_detailed_info(Processor *processor) +{ + gchar *tmp_cpufreq, *tmp_topology, *ret; + + tmp_topology = cputopo_section_str(processor->cputopo); + tmp_cpufreq = cpufreq_section_str(processor->cpufreq); + + ret = g_strdup_printf("[%s]\n" + "%s=%s\n" /* name */ + "%s=%s\n" /* vendor */ + "%s=%s\n" /* arch */ + "%s=%s\n" /* archrev */ + "%s=%s\n" /* family */ + "%s=%d\n" /* model no. */ + "%s=%d\n" /* revision */ + "%s=%.2f %s\n" /* frequency */ + "%s=%.2f\n" /* bogomips */ + "%s=%s\n" /* byte order */ + "%s=%d\n" /* regs */ + "%s=%s\n" /* features */ + "%s" /* topology */ + "%s" /* frequency scaling */ + "%s",/* empty */ + _("Processor"), + _("Name"), processor->model_name, + _("Vendor"), processor->vendor_id, + _("Architecture"), processor->arch, + _("Architecture Revision"), processor->archrev, + _("Family"), processor->family, + _("Model"), processor->model, + _("Revision"), processor->revision, + _("Frequency"), processor->cpu_mhz, _("MHz"), + _("BogoMips"), processor->bogomips, + _("Byte Order"), byte_order_str(), + _("CPU regs"), processor->cpu_regs, + _("Features"), processor->features, + tmp_topology, + tmp_cpufreq, + ""); + g_free(tmp_cpufreq); + g_free(tmp_topology); + return ret; +} + +gchar *processor_get_info(GSList * processors) +{ + Processor *processor; + + if (g_slist_length(processors) > 1) { + gchar *ret, *tmp, *hashkey; + GSList *l; + + tmp = g_strdup(""); + + for (l = processors; l; l = l->next) { + processor = (Processor *) l->data; + + tmp = g_strdup_printf("%s$CPU%d$%s=%.2f %s\n", + tmp, processor->id, + processor->model_name, + processor->cpu_mhz, _("MHz")); + + hashkey = g_strdup_printf("CPU%d", processor->id); + moreinfo_add_with_prefix("DEV", hashkey, + processor_get_detailed_info(processor)); + g_free(hashkey); + } + + ret = g_strdup_printf("[$ShellParam$]\n" + "ViewType=1\n" + "[Processors]\n" + "%s", tmp); + g_free(tmp); + + return ret; + } + + processor = (Processor *) processors->data; + return processor_get_detailed_info(processor); +} diff --git a/modules/devices/inputdevices.c b/modules/devices/inputdevices.c new file mode 100644 index 00000000..301641d4 --- /dev/null +++ b/modules/devices/inputdevices.c @@ -0,0 +1,171 @@ +/* + * HardInfo - Displays System Information + * Copyright (C) 2003-2006 L. A. F. Pereira <l@tia.mat.br> + * + * 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 or later. + * + * 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 "devices.h" +#include "usb_util.h" + +gchar *input_icons = NULL; + +static struct { + char *name; + char *icon; +} input_devices[] = { + { NULL, "module.png" }, // UNKNOWN + { "Keyboard", "keyboard.png" }, + { "Joystick", "joystick.png" }, + { "Mouse", "mouse.png" }, + { "Speaker", "audio.png" }, + { "Audio", "audio.png" } +}; + +// source: https://elixir.bootlin.com/linux/v5.9/source/include/uapi/linux/input.h#L251 +static const gchar *bus_types[] = { + NULL, "PCI", "ISA PnP", "USB", // 0x0 - 0x3 + "HIL", "Bluetooth", "Virtual", NULL, // 0x4 - 0x7 + NULL, NULL, NULL, NULL, // 0x8 - 0xB + NULL, NULL, NULL, NULL, // 0xC - 0xF + "ISA", "i8042", "XT Keyboard bus", "RS232", // 0x10 - 0x13 + "Game port", "Parallel port", "Amiga bus", "ADB", // 0x14 - 0x17 + "I²C", "HOST", "GSC", "Atari bus", // 0x18 - 0x1B + "SPI", "RMI", "CEC", "Intel ISHTP" // 0x1C - 0x1F +}; + +#define UNKWNIFNULL(f) ((f) ? f : _("(Unknown)")) +#define EMPTYIFNULL(f) ((f) ? f : "") + +void +__scan_input_devices(void) +{ + FILE *dev; + gchar buffer[1024]; + vendor_list vl = NULL; + gchar *tmp, *name = NULL, *phys = NULL; + gchar *vendor_str = NULL, *product_str = NULL, *vendor_tags = NULL; + gint bus = 0, vendor = 0, product = 0, version = 0; + const gchar *bus_str = NULL; + int d = 0, n = 0; + + dev = fopen("/proc/bus/input/devices", "r"); + if (!dev) + return; + + if (input_list) { + moreinfo_del_with_prefix("DEV:INP"); + g_free(input_list); + g_free(input_icons); + } + input_list = g_strdup(""); + input_icons = g_strdup(""); + + while (fgets(buffer, sizeof(buffer), dev)) { + tmp = buffer; + + switch (*tmp) { + case 'N': + tmp = strreplacechr(tmp + strlen("N: Name="), "=", ':'); + name = g_strdup(tmp); + remove_quotes(name); + break; + case 'P': + phys = g_strdup(tmp + strlen("P: Phys=")); + break; + case 'I': + sscanf(tmp, "I: Bus=%x Vendor=%x Product=%x Version=%x", + &bus, &vendor, &product, &version); + break; + case 'H': + if (strstr(tmp, "kbd")) + d = 1; //INPUT_KEYBOARD; + else if (strstr(tmp, "js")) + d = 2; //INPUT_JOYSTICK; + else if (strstr(tmp, "mouse")) + d = 3; //INPUT_MOUSE; + else + d = 0; //INPUT_UNKNOWN; + break; + case '\n': + if (name && strstr(name, "PC Speaker")) { + d = 4; // INPUT_PCSPKR + } + if (d == 0 && g_strcmp0(phys, "ALSA")) { + d = 5; // INPUT_AUDIO + } + + if (vendor > 0 && product > 0 && g_str_has_prefix(phys, "usb-")) { + usb_lookup_ids_vendor_product_str(vendor, product, &vendor_str, &product_str); + } + + if (bus >= 0 && (guint)bus < sizeof(bus_types) / sizeof(gchar*)) { + bus_str = bus_types[bus]; + } + + vl = vendor_list_remove_duplicates_deep(vendors_match(name, vendor_str, NULL)); + vendor_tags = vendor_list_ribbon(vl, params.fmt_opts); + + tmp = g_strdup_printf("INP%d", ++n); + input_list = h_strdup_cprintf("$%s$%s=%s|%s\n", + input_list, + tmp, name, EMPTYIFNULL(vendor_tags), + EMPTYIFNULL(input_devices[d].name)); + input_icons = h_strdup_cprintf("Icon$%s$%s=%s\n", + input_icons, + tmp, name, + input_devices[d].icon); + + gchar *strhash = g_strdup_printf("[%s]\n" + /* Name */ "$^$%s=%s\n" + /* Type */ "%s=%s\n" + /* Bus */ "%s=[0x%x] %s\n" + /* Vendor */ "$^$%s=[0x%x] %s\n" + /* Product */"%s=[0x%x] %s\n" + /* Version */"%s=0x%x\n", + _("Device Information"), + _("Name"), name, + _("Type"), UNKWNIFNULL(input_devices[d].name), + _("Bus"), bus, UNKWNIFNULL(bus_str), + _("Vendor"), vendor, UNKWNIFNULL(vendor_str), + _("Product"), product, UNKWNIFNULL(product_str), + _("Version"), version ); + + if (phys && phys[1] != 0) { + strhash = h_strdup_cprintf("%s=%s\n", strhash, _("Connected to"), phys); + } + + if (phys && strstr(phys, "ir")) { + strhash = h_strdup_cprintf("%s=%s\n", strhash, _("InfraRed port"), _("Yes") ); + } + + moreinfo_add_with_prefix("DEV", tmp, strhash); + g_free(tmp); + g_free(phys); + g_free(name); + g_free(vendor_str); + g_free(vendor_tags); + g_free(product_str); + bus_str = NULL; + vendor_str = NULL; + product_str = NULL; + vendor_tags = NULL; + } + } + + fclose(dev); +} diff --git a/modules/devices/loongarch64/processor.c b/modules/devices/loongarch64/processor.c new file mode 100644 index 00000000..08f3645e --- /dev/null +++ b/modules/devices/loongarch64/processor.c @@ -0,0 +1,90 @@ +/* + * HardInfo - Displays System Information + * Copyright (C) 2003-2006 L. A. F. Pereira <l@tia.mat.br> + * + * 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 or later. + * + * 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 "devices.h" +#include "cpu_util.h" + +GSList * +processor_scan(void) +{ + Processor *processor; + FILE *cpuinfo; + gchar buffer[128]; + + cpuinfo = fopen(PROC_CPUINFO, "r"); + if (!cpuinfo) + return NULL; + + processor = g_new0(Processor, 1); + while (fgets(buffer, 128, cpuinfo)) { + gchar **tmp = g_strsplit(buffer, ":", 2); + + if (tmp[0] && tmp[1]) { + tmp[0] = g_strstrip(tmp[0]); + tmp[1] = g_strstrip(tmp[1]); + + get_str("system type", processor->vendor_id); + get_str("CPU Family", processor->family); + get_str("Model Name", processor->model_name); + get_int("CPU Revision", processor->revision); + get_float("CPU MHz", processor->cpu_mhz); + get_float("BogoMIPS", processor->bogomips); + get_str("Features", processor->features); + } + g_strfreev(tmp); + } + + fclose(cpuinfo); + + return g_slist_append(NULL, processor); +} + +gchar *processor_name(GSList * processors) { + return processor_name_default(processors); +} + +gchar *processor_describe(GSList * processors) { + return processor_describe_default(processors); +} + +gchar * +processor_get_info(GSList *processors) +{ + Processor *processor = (Processor *)processors->data; + + return g_strdup_printf("[%s]\n" + "%s=%s\n" /* vendor */ + "%s=%s\n" /* family */ + "%s=%s\n" /* name */ + "%s=%d\n" /* revision */ + "%s=%.2f %s\n" /* frequency */ + "%s=%.2f\n" /* bogoMIPS */ + "%s=%s\n" /* features */ + "%s=%s\n", /* byte order */ + _("Processor"), + _("System Type"), processor->vendor_id, + _("Family"), processor->family, + _("Model"), processor->model_name, + _("Revision"), processor->revision, + _("Frequency"), processor->cpu_mhz, _("MHz"), + _("BogoMIPS"), processor->bogomips, + _("Features"), processor->features, + _("Byte Order"), byte_order_str() + ); +} diff --git a/modules/devices/m68k/processor.c b/modules/devices/m68k/processor.c new file mode 100644 index 00000000..a9d71835 --- /dev/null +++ b/modules/devices/m68k/processor.c @@ -0,0 +1,92 @@ +/* + * HardInfo - Displays System Information + * Copyright (C) 2003-2006 L. A. F. Pereira <l@tia.mat.br> + * + * 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 or later. + * + * 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 "devices.h" +#include "cpu_util.h" + +GSList * +processor_scan(void) +{ + Processor *processor; + FILE *cpuinfo; + gchar buffer[128]; + + cpuinfo = fopen(PROC_CPUINFO, "r"); + if (!cpuinfo) + return NULL; + + processor = g_new0(Processor, 1); + while (fgets(buffer, 128, cpuinfo)) { + gchar **tmp = g_strsplit(buffer, ":", 2); + + if (tmp[0] && tmp[1]) { + tmp[0] = g_strstrip(tmp[0]); + tmp[1] = g_strstrip(tmp[1]); + + get_str("CPU", processor->model_name); + get_str("MMU", processor->mmu_name); + get_str("FPU", processor->fpu_name); + get_float("Clocking", processor->cpu_mhz); + get_float("BogoMips", processor->bogomips); + get_str("Calibration", processor->calibration); + } + g_strfreev(tmp); + } + + gchar *tmp; + tmp = g_strconcat("Motorola ", processor->model_name, NULL); + g_free(processor->model_name); + processor->model_name = tmp; + + fclose(cpuinfo); + + return g_slist_append(NULL, processor); +} + +gchar *processor_name(GSList * processors) { + return processor_name_default(processors); +} + +gchar *processor_describe(GSList * processors) { + return processor_describe_default(processors); +} + +gchar * +processor_get_info(GSList *processors) +{ + Processor *processor = (Processor *)processors->data; + + return g_strdup_printf("[%s]\n" + "%s=%s\n" /* cpu */ + "%s=%s\n" /* mmu */ + "%s=%s\n" /* fpu */ + "%s=%.2f %s\n" /* frequency */ + "%s=%.2f\n" /* bogomips */ + "%s=%s\n" /* byte order */ + "%s=%s\n", /* calibration */ + _("Processor"), + _("Model"), processor->model_name, + _("MMU"), processor->mmu_name, + _("FPU"), processor->fpu_name, + _("Frequency"), processor->cpu_mhz, _("MHz"), + _("BogoMips"), processor->bogomips, + _("Byte Order"), byte_order_str(), + _("Calibration"), processor->calibration + ); +} diff --git a/modules/devices/mips/processor.c b/modules/devices/mips/processor.c new file mode 100644 index 00000000..112a3c3b --- /dev/null +++ b/modules/devices/mips/processor.c @@ -0,0 +1,81 @@ +/* + * HardInfo - Displays System Information + * Copyright (C) 2003-2006 L. A. F. Pereira <l@tia.mat.br> + * + * 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 or later. + * + * 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 "devices.h" +#include "cpu_util.h" + +GSList * +processor_scan(void) +{ + Processor *processor; + FILE *cpuinfo; + gchar buffer[128]; + + cpuinfo = fopen(PROC_CPUINFO, "r"); + if (!cpuinfo) + return NULL; + + processor = g_new0(Processor, 1); + while (fgets(buffer, 128, cpuinfo)) { + gchar **tmp = g_strsplit(buffer, ":", 2); + + if (tmp[0] && tmp[1]) { + tmp[0] = g_strstrip(tmp[0]); + tmp[1] = g_strstrip(tmp[1]); + + get_str("system type", processor->vendor_id); + get_str("cpu model", processor->model_name); + get_float("cpu MHz", processor->cpu_mhz); + get_float("BogoMIPS", processor->bogomips); + } + g_strfreev(tmp); + } + + fclose(cpuinfo); + + return g_slist_append(NULL, processor); +} + +gchar *processor_name(GSList * processors) { + return processor_name_default(processors); +} + +gchar *processor_describe(GSList * processors) { + return processor_describe_default(processors); +} + +gchar * +processor_get_info(GSList *processors) +{ + Processor *processor = (Processor *)processors->data; + + return g_strdup_printf("[%s]\n" + "%s=%s\n" + "%s=%s\n" + "%s=%.2f %s\n" /* frequency */ + "%s=%.2f\n" /* bogomips */ + "%s=%s\n", /* byte order */ + _("Processor"), + _("Model"), processor->model_name, + _("System Type"), processor->vendor_id, + _("Frequency"), processor->cpu_mhz, _("MHz"), + _("BogoMips"), processor->bogomips, + _("Byte Order"), byte_order_str() + ); +} diff --git a/modules/devices/monitors.c b/modules/devices/monitors.c new file mode 100644 index 00000000..02fb1d67 --- /dev/null +++ b/modules/devices/monitors.c @@ -0,0 +1,515 @@ +/* + * HardInfo - Displays System Information + * Copyright (C) 2003-2019 L. A. F. Pereira <l@tia.mat.br> + * Copyright (C) 2019 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 or later. + * + * 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 "devices.h" +#include "util_sysobj.h" +#include "util_edid.h" +#include "util_ids.h" + +static const char monitor_icon[] = "monitor.png"; + +#define UNKIFNULL2(f) ((f) ? f : _("(Unknown)")) +#define UNKIFEMPTY2(f) ((*f) ? f : _("(Unknown)")) +#define UNSPECIFNULL2(f) ((f) ? f : _("(Unspecified)")) + +gboolean no_monitors = FALSE; + +gchar *edid_ids_file = NULL; +gchar *ieee_oui_ids_file = NULL; + +void find_edid_ids_file() { + if (edid_ids_file) return; + char *file_search_order[] = { + g_build_filename(g_get_user_config_dir(), "hardinfo2", "edid.ids", NULL), + g_build_filename(params.path_data, "edid.ids", NULL), + NULL + }; + int n; + for(n = 0; file_search_order[n]; n++) { + if (!edid_ids_file && !access(file_search_order[n], R_OK)) + edid_ids_file = file_search_order[n]; + else + g_free(file_search_order[n]); + } + auto_free(edid_ids_file); +} + +void find_ieee_oui_ids_file() { + if (ieee_oui_ids_file) return; + char *file_search_order[] = { + g_build_filename(g_get_user_config_dir(), "hardinfo2", "ieee_oui.ids", NULL), + g_build_filename(params.path_data, "ieee_oui.ids", NULL), + NULL + }; + int n; + for(n = 0; file_search_order[n]; n++) { + if (!ieee_oui_ids_file && !access(file_search_order[n], R_OK)) + ieee_oui_ids_file = file_search_order[n]; + else + g_free(file_search_order[n]); + } + auto_free(ieee_oui_ids_file); +} + +typedef struct { + gchar *drm_path; + gchar *drm_connection; + gchar *drm_status; + gchar *drm_enabled; + edid *e; + gchar *_vstr; /* use monitor_vendor_str() */ +} monitor; +#define monitor_new() g_new0(monitor, 1) + +monitor *monitor_new_from_sysfs(const gchar *sysfs_edid_file) { + gchar *edid_bin = NULL; + gsize edid_len = 0; + if (!sysfs_edid_file || !*sysfs_edid_file) return NULL; + monitor *m = monitor_new(); + m->drm_path = g_path_get_dirname(sysfs_edid_file); + m->drm_connection = g_path_get_basename(m->drm_path); + gchar *drm_enabled_file = g_strdup_printf("%s/%s", m->drm_path, "enabled"); + gchar *drm_status_file = g_strdup_printf("%s/%s", m->drm_path, "status"); + g_file_get_contents(drm_enabled_file, &m->drm_enabled, NULL, NULL); + if (m->drm_enabled) g_strstrip(m->drm_enabled); + g_file_get_contents(drm_status_file, &m->drm_status, NULL, NULL); + if (m->drm_status) g_strstrip(m->drm_status); + g_file_get_contents(sysfs_edid_file, &edid_bin, &edid_len, NULL); + if (edid_len) + m->e = edid_new(edid_bin, edid_len); + g_free(drm_enabled_file); + g_free(drm_status_file); + return m; +} + +void monitor_free(monitor *m) { + if (m) { + g_free(m->_vstr); + g_free(m->drm_connection); + edid_free(m->e); + g_free(m); + } +} + +gchar *monitor_vendor_str(monitor *m, gboolean include_value, gboolean link_name) { + if (!m || !m->e) return NULL; + edid_ven ven = m->e->ven; + gchar v[20] = "", t[4] = ""; + ids_query_result result;// = {}; + + memset(&result,0,sizeof(ids_query_result)); + if (ven.type == VEN_TYPE_PNP) { + strcpy(v, ven.pnp); + strcpy(t, "PNP"); + } else if (ven.type == VEN_TYPE_OUI) { + strcpy(v, ven.oui_str); + strcpy(t, "OUI"); + } + + if (!m->_vstr) { + if (ven.type == VEN_TYPE_PNP) { + if (!edid_ids_file) + find_edid_ids_file(); + scan_ids_file(edid_ids_file, v, &result, -1); + if (result.results[0]) + m->_vstr = g_strdup(result.results[0]); + } else if (ven.type == VEN_TYPE_OUI) { + if (!ieee_oui_ids_file) + find_ieee_oui_ids_file(); + scan_ids_file(ieee_oui_ids_file, v, &result, -1); + if (result.results[0]) + m->_vstr = g_strdup(result.results[0]); + } + } + + gchar *ret = NULL; + if (include_value) + ret = g_strdup_printf("[%s:%s]", t, v); + if (m->_vstr) { + if (link_name) { + gchar *lv = vendor_get_link(m->_vstr); + ret = appfsp(ret, "%s", lv); + g_free(lv); + } else + ret = appfsp(ret, "%s", m->_vstr); + } else if (!include_value && ven.type == VEN_TYPE_PNP) { + ret = appfsp(ret, "%s", ven.pnp); + } else + ret = appfsp(ret, "%s", _("(Unknown)")); + return ret; +} + +gchar *monitor_name(monitor *m, gboolean include_vendor) { + if (!m) return NULL; + gchar *desc = NULL; + edid *e = m->e; + if (!e) + return g_strdup(_("(Unknown)")); + + if (include_vendor) { + if (e->ven.type != VEN_TYPE_INVALID) { + gchar *vstr = monitor_vendor_str(m, FALSE, FALSE); + gchar *vtag = vendor_match_tag(vstr, params.fmt_opts); + desc = appfsp(desc, "%s", vtag ? vtag : vstr); + g_free(vstr); + g_free(vtag); + } else + desc = appfsp(desc, "%s", "Unknown"); + } + + if (e->img_max.diag_in) + desc = appfsp(desc, "%s", e->img_max.class_inch); + + if (e->name) + desc = appfsp(desc, "%s", e->name); + else + desc = appfsp(desc, "%s %s", e->a_or_d ? "Digital" : "Analog", "Display"); + + return desc; +} + +gchar **get_output_lines(const char *cmd_line) { + gboolean spawned; + gchar *out, *err; + gchar **ret = NULL; + + spawned = g_spawn_command_line_sync(cmd_line, + &out, &err, NULL, NULL); + if (spawned) { + ret = g_strsplit(out, "\n", -1); + g_free(out); + g_free(err); + } + return ret; +} + +static gchar *tag_make_safe_inplace(gchar *tag) { + if (!tag) + return tag; + if (!g_utf8_validate(tag, -1, NULL)) + return tag; //TODO: reconsider + gchar *p = tag, *pd = tag; + while(*p) { + gchar *np = g_utf8_next_char(p); + gunichar c = g_utf8_get_char_validated(p, -1); + int l = g_unichar_to_utf8(c, NULL); + if (l == 1 && g_unichar_isalnum(c)) { + g_unichar_to_utf8(c, pd); + } else { + *pd = '_'; + } + p = np; + pd++; + } + return tag; +} + +static gchar *make_edid_section(monitor *m) { + int i; + edid *e = m->e; + if (e->len) { + gchar *vstr = monitor_vendor_str(m, TRUE, FALSE); + + gchar *dom = NULL; + if (!e->dom.is_model_year && e->dom.week && e->dom.year) + dom = g_strdup_printf(_("Week %d of %d"), e->dom.week, e->dom.year); + else if (e->dom.year) + dom = g_strdup_printf("%d", e->dom.year); + + gchar *bpcc = NULL; + if (e->bpc) + bpcc = g_strdup_printf("%d", e->bpc); + + int aok = e->checksum_ok; + if (e->ext_blocks_fail) aok = 0; + gchar *csum = aok ? _("Ok") : _("Fail"); + + gchar *iface; + if (e->interface && e->di.exists) { + gchar *tmp = g_strdup_printf("[%x] %s\n[DI-EXT:%x] %s", + e->interface, _(edid_interface(e->interface)), + e->di.interface, _(edid_di_interface(e->di.interface)) ); + iface = gg_key_file_parse_string_as_value(tmp, '|'); + g_free(tmp); + } else if (e->di.exists) { + iface = g_strdup_printf("[DI-EXT:%x] %s", + e->di.interface, _(edid_di_interface(e->di.interface)) ); + } else { + iface = g_strdup_printf("[%x] %s", + e->interface, + e->interface ? _(edid_interface(e->interface)) : _("(Unspecified)") ); + } + + gchar *d_list, *ext_list, *dtd_list, *cea_list, + *etb_list, *std_list, *svd_list, *sad_list, + *didt_list, *did_string_list; + + etb_list = NULL; + for(i = 0; i < e->etb_count; i++) { + char *desc = edid_output_describe(&e->etbs[i]); + etb_list = appfnl(etb_list, "etb%d=%s", i, desc); + g_free(desc); + } + if (!etb_list) etb_list = g_strdup_printf("%s=\n", _("(Empty List)")); + + std_list = NULL; + for(i = 0; i < e->std_count; i++) { + char *desc = edid_output_describe(&e->stds[i].out); + std_list = appfnl(std_list, "std%d=%s", i, desc); + g_free(desc); + } + if (!std_list) std_list = g_strdup_printf("%s=\n", _("(Empty List)")); + + d_list = NULL; + for(i = 0; i < 4; i++) { + char *desc = edid_base_descriptor_describe(&e->d[i]); + d_list = appfnl(d_list, "descriptor%d=%s", i, desc); + g_free(desc); + } + if (!d_list) d_list = g_strdup_printf("%s=\n", _("(Empty List)")); + + ext_list = NULL; + for(i = 0; i < e->ext_blocks; i++) { + int type = e->u8[(i+1)*128]; + int version = e->u8[(i+1)*128 + 1]; + ext_list = appfnl(ext_list, "ext%d = ([%02x:v%02x] %s) %s", i, + type, version, _(edid_ext_block_type(type)), + e->ext_ok[i] ? "ok" : "fail" + ); + } + if (!ext_list) ext_list = g_strdup_printf("%s=\n", _("(Empty List)")); + + dtd_list = NULL; + for(i = 0; i < e->dtd_count; i++) { + char *desc = edid_dtd_describe(&e->dtds[i], 0); + dtd_list = appfnl(dtd_list, "dtd%d = %s", i, desc); + free(desc); + } + if (!dtd_list) dtd_list = g_strdup_printf("%s=\n", _("(Empty List)")); + + cea_list = NULL; + for(i = 0; i < e->cea_block_count; i++) { + char *desc = edid_cea_block_describe(&e->cea_blocks[i]); + cea_list = appfnl(cea_list, "cea_block%d = %s", i, desc); + } + if (!cea_list) cea_list = g_strdup_printf("%s=\n", _("(Empty List)")); + + svd_list = NULL; + for(i = 0; i < e->svd_count; i++) { + char *desc = edid_output_describe(&e->svds[i].out); + svd_list = appfnl(svd_list, "svd%d=%s", i, desc); + g_free(desc); + } + if (!svd_list) svd_list = g_strdup_printf("%s=\n", _("(Empty List)")); + + sad_list = NULL; + for(i = 0; i < e->sad_count; i++) { + char *desc = edid_cea_audio_describe(&e->sads[i]); + sad_list = appfnl(sad_list, "sad%d=%s", i, desc); + g_free(desc); + } + if (!sad_list) sad_list = g_strdup_printf("%s=\n", _("(Empty List)")); + + didt_list = NULL; + for(i = 0; i < e->didt_count; i++) { + char *desc = edid_output_describe(&e->didts[i]); + didt_list = appfnl(didt_list, "didt%d=%s", i, desc); + g_free(desc); + } + if (!didt_list) didt_list = g_strdup_printf("%s=\n", _("(Empty List)")); + + did_string_list = NULL; + for(i = 0; i < e->did_string_count; i++) { + did_string_list = appfnl(did_string_list, "did_string%d=%s", i, e->did_strings[i].str); + } + if (!did_string_list) did_string_list = g_strdup_printf("%s=\n", _("(Empty List)")); + + gchar *speakers = NULL; + if (e->speaker_alloc_bits) { + gchar *spk_tmp = edid_cea_speaker_allocation_describe(e->speaker_alloc_bits, 0); + speakers = gg_key_file_parse_string_as_value(spk_tmp, '|'); + g_free(spk_tmp); + } else + speakers = g_strdup(_("(Unspecified)")); + + gchar *hex = edid_dump_hex(e, 0, 1); + gchar *hex_esc = gg_key_file_parse_string_as_value(hex, '|'); + g_free(hex); + if (params.markup_ok) + hex = g_strdup_printf("<tt>%s</tt>", hex_esc); + else + hex = g_strdup(hex_esc); + g_free(hex_esc); + + gchar *ret = g_strdup_printf( + /* extending "Connection" section */ + "%s=%s\n" /* sig type */ + "%s=%s\n" /* interface */ + "%s=%s\n" /* bpcc */ + "%s=%s\n" /* speakers */ + "[%s]\n" + "%s=%s\n" /* base out */ + "%s=%s\n" /* ext out */ + "[%s]\n" + "$^$%s=%s\n" /* vendor */ + "%s=%s\n" /* name */ + "%s=[%04x-%08x] %u-%u\n" /* model, n_serial */ + "%s=%s\n" /* serial */ + "%s=%s\n" /* dom */ + "[%s]\n" + "%s=%d %s\n" /* size */ + "%s=%d.%d\n" /* version */ + "%s=%d\n" /* ext block */ + "%s=%s\n" /* ext to */ + "%s=%s %s\n" /* checksum */ + "[%s]\n%s\n" + "[%s]\n%s\n" + "[%s]\n%s\n" + "[%s]\n%s\n" + "[%s]\n%s\n" + "[%s]\n%s\n" + "[%s]\n%s\n" + "[%s]\n%s\n" + "[%s]\n%s\n" + "[%s]\n%s\n" + "[%s]\n%s=%s\n" + , + _("Signal Type"), e->a_or_d ? _("Digital") : _("Analog"), + _("Interface"), iface, + _("Bits per Color Channel"), UNSPECIFNULL2(bpcc), + _("Speaker Allocation"), speakers, + _("Output (Max)"), + edid_output_src(e->img.src), edid_output_describe(&e->img), + edid_output_src(e->img_max.src), edid_output_describe(&e->img_max), + _("EDID Device"), + _("Vendor"), vstr, + _("Name"), e->name, + _("Model"), e->product, e->n_serial, e->product, e->n_serial, + _("Serial"), UNKIFNULL2(e->serial), + _("Manufacture Date"), UNKIFNULL2(dom), + _("EDID Meta"), + _("Data Size"), e->len, _("bytes"), + _("Version"), (int)e->ver_major, (int)e->ver_minor, + _("Extension Blocks"), e->ext_blocks, + _("Extended to"), e->std ? _(edid_standard(e->std)) : _("(None)"), + _("Checksum"), csum, aok ? "" : problem_marker(), + _("EDID Descriptors"), d_list, + _("Detailed Timing Descriptors (DTD)"), dtd_list, + _("Established Timings Bitmap (ETB)"), etb_list, + _("Standard Timings (STD)"), std_list, + _("E-EDID Extension Blocks"), ext_list, + _("EIA/CEA-861 Data Blocks"), cea_list, + _("EIA/CEA-861 Short Audio Descriptors"), sad_list, + _("EIA/CEA-861 Short Video Descriptors"), svd_list, + _("DisplayID Timings"), didt_list, + _("DisplayID Strings"), did_string_list, + _("Hex Dump"), _("Data"), hex + ); + g_free(bpcc); + g_free(dom); + + g_free(d_list); + g_free(ext_list); + g_free(etb_list); + g_free(std_list); + g_free(dtd_list); + g_free(cea_list); + g_free(sad_list); + g_free(svd_list); + g_free(didt_list); + g_free(did_string_list); + g_free(iface); + g_free(vstr); + g_free(hex); + //printf("ret: %s\n", ret); + return ret; + } else + return g_strdup(""); +} + +gchar *monitors_get_info() { + gchar *icons = g_strdup(""); + gchar *ret = g_strdup_printf("[%s]\n", _("Monitors")); + gchar tag_prefix[] = "DEV"; + + gchar **edid_files = get_output_lines("find /sys/devices -name edid"); + //gchar **edid_files = get_output_lines("find /home/pburt/github/verbose-spork/junk/testing/.testing/edid2/ -name edid.*"); + int i, found = 0; + for(i = 0; edid_files[i]; i++) { + monitor *m = monitor_new_from_sysfs(edid_files[i]); + //if (m && m->e->std < STD_DISPLAYID) continue; + //if (m && !m->e->interface) continue; + //if (m && m->e->interface != 1) continue; + if (m && !SEQ(m->drm_status, "disconnected")) { + gchar *tag = g_strdup_printf("%d-%s", found, m->drm_connection); + tag_make_safe_inplace(tag); + gchar *desc = monitor_name(m, TRUE); + gchar *edid_section = NULL; + edid *e = m->e; + if (e && e->checksum_ok) + edid_section = make_edid_section(m); + + gchar *details = g_strdup_printf("[%s]\n" + "%s=%s\n" + "%s=%s %s\n" + "%s\n", + _("Connection"), + _("DRM"), m->drm_connection, + _("Status"), m->drm_status, m->drm_enabled, + edid_section ? edid_section : "" + ); + moreinfo_add_with_prefix(tag_prefix, tag, details); /* moreinfo now owns *details */ + ret = h_strdup_cprintf("$!%s$%s=%s\n", + ret, tag, m->drm_connection, desc); + icons = h_strdup_cprintf("Icon$%s$=%s\n", icons, tag, monitor_icon); + g_free(desc); + g_free(edid_section); + found++; + } + monitor_free(m); + } + g_strfreev(edid_files); + + no_monitors = FALSE; + if(!found) { + no_monitors = TRUE; + g_free(ret); + ret = g_strdup_printf("[%s]\n%s=%s\n" "[$ShellParam$]\nViewType=0\n", + _("Monitors"), _("Result"), _("(Empty)") ); + } else { + ret = h_strdup_cprintf( + "[$ShellParam$]\nViewType=1\n" + "ColumnTitle$TextValue=%s\n" /* DRM connection */ + "ColumnTitle$Value=%s\n" /* Name */ + "ShowColumnHeaders=true\n" + "%s", + ret, + _("Connection"), + _("Name"), + icons + ); + } + + return ret; +} + +gboolean monitors_hinote(const char **msg) { + PARAM_NOT_UNUSED(msg); + return FALSE; +} diff --git a/modules/devices/parisc/processor.c b/modules/devices/parisc/processor.c new file mode 100644 index 00000000..c749bc5a --- /dev/null +++ b/modules/devices/parisc/processor.c @@ -0,0 +1,210 @@ +/* + * HardInfo - Displays System Information + * Copyright (C) 2003-2006 L. A. F. Pereira <l@tia.mat.br> + * + * 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 or later. + * + * 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 "devices.h" +#include "cpu_util.h" + +GSList * +processor_scan(void) +{ + GSList *procs = NULL; + Processor *processor = NULL; + FILE *cpuinfo; + gchar buffer[128]; + gchar *rep_pname = NULL; + GSList *pi = NULL; + + cpuinfo = fopen(PROC_CPUINFO, "r"); + if (!cpuinfo) + return NULL; + +#define CHECK_FOR(k) (g_str_has_prefix(tmp[0], k)) + while (fgets(buffer, 128, cpuinfo)) { + gchar **tmp = g_strsplit(buffer, ":", 2); + if (tmp[0] && tmp[1]) { + tmp[0] = g_strstrip(tmp[0]); + tmp[1] = g_strstrip(tmp[1]); + } else { + g_strfreev(tmp); + continue; + } + + get_str("Processor", rep_pname); + + if ( CHECK_FOR("processor") ) { + /* finish previous */ + if (processor) { + procs = g_slist_append(procs, processor); + } + + /* start next */ + processor = g_new0(Processor, 1); + processor->id = atol(tmp[1]); + + if (rep_pname) + processor->model_name = g_strdup(rep_pname); + + g_strfreev(tmp); + continue; + } + + if (!processor && + ( CHECK_FOR("cpu family") + || CHECK_FOR("cpu MHz") + || CHECK_FOR("cpu") ) ) { + + /* single proc/core may not have "processor : n" */ + processor = g_new0(Processor, 1); + processor->id = 0; + + if (rep_pname) + processor->model_name = g_strdup(rep_pname); + } + + if (processor) { + get_str("cpu family", processor->cpu_family); + get_float("cpu MHz", processor->cpu_mhz); + get_str("cpu", processor->model_name); + get_float("bogomips", processor->bogomips); + get_str("model name", processor->strmodel); + get_str("I-cache", processor->icache_str); + get_str("D-cache", processor->dcache_str); + get_str("hversion", processor->hversion); + get_str("sversion", processor->sversion); + } + g_strfreev(tmp); + } + + if (processor) + procs = g_slist_append(procs, processor); + + g_free(rep_pname); + fclose(cpuinfo); + + /* TODO: redup */ + + /* data not from /proc/cpuinfo */ + for (pi = procs; pi; pi = pi->next) { + processor = (Processor *) pi->data; + + /* strings can't be null or segfault later */ + STRIFNULL(processor->model_name, _("PA-RISC Processor") ); + STRIFNULL(processor->cpu_family, "PA-RISC"); + UNKIFNULL(processor->strmodel); + + /* topo & freq */ + processor->cpufreq = cpufreq_new(processor->id); + processor->cputopo = cputopo_new(processor->id); + + if (processor->cpufreq->cpukhz_max) + processor->cpu_mhz = processor->cpufreq->cpukhz_max / 1000; + + } + + return procs; +} + +gchar *processor_name(GSList * processors) { + return processor_name_default(processors); +} + +gchar *processor_describe(GSList * processors) { + return processor_describe_default(processors); +} + +gchar * +processor_get_detailed_info(Processor *processor) +{ + gchar *tmp_cpufreq, *tmp_topology, *ret; + + tmp_topology = cputopo_section_str(processor->cputopo); + tmp_cpufreq = cpufreq_section_str(processor->cpufreq); + + ret = g_strdup_printf("[%s]\n" + "%s=%s\n" + "%s=%s\n" + "%s=%s\n" /* model name */ + "%s=%.2f %s\n" /* frequency */ + "%s=%.2f\n" /* bogomips */ + "%s=%s\n" /* byte order */ + "%s=%s\n" /* hversion */ + "%s=%s\n" /* sversion */ + "[%s]\n" + "I-Cache=%s\n" + "D-Cache=%s\n" + "%s" /* topology */ + "%s" /* frequency scaling */ + "%s",/* empty */ + _("Processor"), + _("Model"), processor->model_name, + _("Architecture"), processor->cpu_family, + _("System"), processor->strmodel, + _("Frequency"), processor->cpu_mhz, _("MHz"), + _("BogoMips"), processor->bogomips, + _("Byte Order"), byte_order_str(), + _("HVersion"), processor->hversion, + _("SVersion"), processor->sversion, + _("Cache"), + processor->icache_str, + processor->dcache_str, + tmp_topology, + tmp_cpufreq, + ""); + + g_free(tmp_cpufreq); + g_free(tmp_topology); + return ret; +} + +gchar *processor_get_info(GSList * processors) +{ + Processor *processor; + + if (g_slist_length(processors) > 1) { + gchar *ret, *tmp, *hashkey; + GSList *l; + + tmp = g_strdup(""); + + for (l = processors; l; l = l->next) { + processor = (Processor *) l->data; + + tmp = g_strdup_printf("%s$CPU%d$%s=%.2f %s\n", + tmp, processor->id, + processor->model_name, + processor->cpu_mhz, _("MHz")); + + hashkey = g_strdup_printf("CPU%d", processor->id); + moreinfo_add_with_prefix("DEV", hashkey, + processor_get_detailed_info(processor)); + g_free(hashkey); + } + + ret = g_strdup_printf("[$ShellParam$]\n" + "ViewType=1\n" + "[Processors]\n" + "%s", tmp); + g_free(tmp); + + return ret; + } + + processor = (Processor *) processors->data; + return processor_get_detailed_info(processor); +} diff --git a/modules/devices/pci.c b/modules/devices/pci.c new file mode 100644 index 00000000..859fa339 --- /dev/null +++ b/modules/devices/pci.c @@ -0,0 +1,200 @@ +/* + * HardInfo - Displays System Information + * Copyright (C) 2003-2006 L. A. F. Pereira <l@tia.mat.br> + * + * 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 or later. + * + * 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 "devices.h" +#include "pci_util.h" + +#define UNKIFNULL_AC(f) (f != NULL) ? f : _("(Unknown)"); + +static const struct { + const gchar *icon; + uint32_t class; +} class2icon[] = { + { .class = 0x0200, .icon = "network-interface.png" }, + { .class = 0x0c03, .icon = "usb.png" }, + { .class = 0x0403, .icon = "audio.png" }, + { .class = 0x0805, .icon = "usbfldisk.png" }, + { .class = 0x0d11, .icon = "bluetooth.png" }, + { .class = 0x0703, .icon = "modem.png" }, + { .class = 0x01, .icon = "hdd.png" }, + { .class = 0x02, .icon = "network.png" }, + { .class = 0x03, .icon = "monitor.png" }, + { .class = 0x05, .icon = "memory.png" }, + { .class = 0x07, .icon = "network-connections.png" }, + { .class = 0x09, .icon = "inputdevices.png" }, + { .class = 0x10, .icon = "cryptohash.png" }, +}; + +static const gchar *find_icon_for_class(uint32_t class) +{ + guint i; + + for (i = 0; i < G_N_ELEMENTS(class2icon); i++) { + if (class2icon[i].class <= 0xff) { + if ((class & 0xff00) == class2icon[i].class << 8) + return class2icon[i].icon; + } else if (class == class2icon[i].class) { + return class2icon[i].icon; + } + } + + return "devices.png"; +} + +static gchar *_pci_dev(const pcid *p, gchar *icons) { + gchar *str; + const gchar *class, *vendor, *svendor, *product, *sproduct, *lproduct; + gchar *name, *key; + + gboolean device_is_sdevice = (p->vendor_id == p->sub_vendor_id && p->device_id == p->sub_device_id); + + class = UNKIFNULL_AC(p->class_str); + vendor = UNKIFNULL_AC(p->vendor_id_str); + svendor = UNKIFNULL_AC(p->sub_vendor_id_str); + product = UNKIFNULL_AC(p->device_id_str); + sproduct = UNKIFNULL_AC(p->sub_device_id_str); + lproduct = p->device_id_str ? p->device_id_str : p->class_str; + lproduct = UNKIFNULL_AC(lproduct); + + gchar *ven_tag = vendor_match_tag(p->vendor_id_str, params.fmt_opts); + gchar *sven_tag = vendor_match_tag(p->sub_vendor_id_str, params.fmt_opts); + if (ven_tag) { + if (sven_tag && p->vendor_id != p->sub_vendor_id) { + name = g_strdup_printf("%s %s %s", sven_tag, ven_tag, lproduct); + } else { + name = g_strdup_printf("%s %s", ven_tag, lproduct); + } + } else { + name = g_strdup_printf("%s %s", vendor, lproduct); + } + g_free(ven_tag); + g_free(sven_tag); + + key = g_strdup_printf("PCI%04x:%02x:%02x.%01x", p->domain, p->bus, p->device, p->function); + + pci_list = h_strdup_cprintf("$%s$%04x:%02x:%02x.%01x=%s\n", pci_list, key, p->domain, p->bus, p->device, p->function, name); + icons = h_strdup_cprintf("Icon$%s$%04x:%02x:%02x.%01x=%s\n", icons, key, p->domain, p->bus, p->device, p->function, find_icon_for_class(p->class)); + + gchar *vendor_device_str; + if (device_is_sdevice) { + vendor_device_str = g_strdup_printf( + /* Vendor */ "$^$%s=[%04x] %s\n" + /* Device */ "%s=[%04x] %s\n", + _("Vendor"), p->vendor_id, vendor, + _("Device"), p->device_id, product); + } else { + vendor_device_str = g_strdup_printf( + /* Vendor */ "$^$%s=[%04x] %s\n" + /* Device */ "%s=[%04x] %s\n" + /* Sub-device vendor */ "$^$%s=[%04x] %s\n" + /* Sub-device */ "%s=[%04x] %s\n", + _("Vendor"), p->vendor_id, vendor, + _("Device"), p->device_id, product, + _("SVendor"), p->sub_vendor_id, svendor, + _("SDevice"), p->sub_device_id, sproduct); + } + + gchar *pcie_str; + if (p->pcie_width_curr) { + pcie_str = g_strdup_printf("[%s]\n" + /* Width */ "%s=x%u\n" + /* Width (max) */ "%s=x%u\n" + /* Speed */ "%s=%0.1f %s\n" + /* Speed (max) */ "%s=%0.1f %s\n", + _("PCI Express"), + _("Link Width"), p->pcie_width_curr, + _("Maximum Link Width"), p->pcie_width_max, + _("Link Speed"), p->pcie_speed_curr, _("GT/s"), + _("Maximum Link Speed"), p->pcie_speed_max, _("GT/s") ); + } else + pcie_str = strdup(""); + + str = g_strdup_printf("[%s]\n" + /* Class */ "%s=[%04x] %s\n" + "%s" + /* Revision */ "%s=%02x\n" + /* PCIE? */ "%s" + "[%s]\n" + /* Driver */ "%s=%s\n" + /* Modules */ "%s=%s\n" + "[%s]\n" + /* Domain */ "%s=%04x\n" + /* Bus */ "%s=%02x\n" + /* Device */ "%s=%02x\n" + /* Function */ "%s=%01x\n", + _("Device Information"), + _("Class"), p->class, class, + vendor_device_str, + _("Revision"), p->revision, + pcie_str, + _("Driver"), + _("In Use"), (p->driver) ? p->driver : _("(Unknown)"), + _("Kernel Modules"), (p->driver_list) ? p->driver_list : _("(Unknown)"), + _("Connection"), + _("Domain"), p->domain, + _("Bus"), p->bus, + _("Device"), p->device, + _("Function"), p->function + ); + + g_free(pcie_str); + + moreinfo_add_with_prefix("DEV", key, str); /* str now owned by morinfo */ + + g_free(vendor_device_str); + g_free(name); + g_free(key); + + return icons; +} + +void scan_pci_do(void) { + + if (pci_list) { + moreinfo_del_with_prefix("DEV:PCI"); + g_free(pci_list); + } + pci_list = g_strdup_printf("[%s]\n", _("PCI Devices")); + + gchar *pci_icons = g_strdup(""); + + pcid_list list = pci_get_device_list(0,0); + list = g_slist_sort(list, (GCompareFunc)pcid_cmp_by_addy); + GSList *l = list; + + int c = 0; + while(l) { + pcid *curr = (pcid*)l->data; + pci_icons = _pci_dev(curr, pci_icons); + c++; + l=l->next; + } + pcid_list_free(list); + + if (c) { + pci_list = g_strconcat(pci_list, "[$ShellParam$]\n", "ViewType=1\n", pci_icons, NULL); + } else { + /* NO PCI? */ + pci_list = g_strconcat(pci_list, _("No PCI devices found"), "=\n", NULL); + } + + g_free(pci_icons); +} diff --git a/modules/devices/ppc/processor.c b/modules/devices/ppc/processor.c new file mode 100644 index 00000000..12c575a6 --- /dev/null +++ b/modules/devices/ppc/processor.c @@ -0,0 +1,206 @@ +/* + * HardInfo - Displays System Information + * Copyright (C) 2003-2006 L. A. F. Pereira <l@tia.mat.br> + * + * 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 or later. + * + * 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 "devices.h" +#include "cpu_util.h" + +GSList * +processor_scan(void) +{ + GSList *procs = NULL; + Processor *processor = NULL; + FILE *cpuinfo; + gchar buffer[128]; + gchar *rep_pname = NULL; + GSList *pi = NULL; + + cpuinfo = fopen(PROC_CPUINFO, "r"); + if (!cpuinfo) + return NULL; + +#define CHECK_FOR(k) (g_str_has_prefix(tmp[0], k)) + while (fgets(buffer, 128, cpuinfo)) { + gchar **tmp = g_strsplit(buffer, ":", 2); + if (tmp[0] && tmp[1]) { + tmp[0] = g_strstrip(tmp[0]); + tmp[1] = g_strstrip(tmp[1]); + } else { + g_strfreev(tmp); + continue; + } + + get_str("Processor", rep_pname); + + if ( CHECK_FOR("processor") ) { + /* finish previous */ + if (processor) { + procs = g_slist_append(procs, processor); + } + + /* start next */ + processor = g_new0(Processor, 1); + processor->id = atol(tmp[1]); + + if (rep_pname) + processor->model_name = g_strdup(rep_pname); + + g_strfreev(tmp); + continue; + } + + if (!processor && + ( CHECK_FOR("cpu") + || CHECK_FOR("clock") + || CHECK_FOR("revision") ) ) { + + /* single proc/core may not have "processor : n" */ + processor = g_new0(Processor, 1); + processor->id = 0; + + if (rep_pname) + processor->model_name = g_strdup(rep_pname); + } + + if (processor) { + get_str("cpu", processor->model_name); + get_str("revision", processor->revision); + get_float("clock", processor->cpu_mhz); + get_float("BogoMIPS", processor->bogomips); + } + g_strfreev(tmp); + } + + if (processor) + procs = g_slist_append(procs, processor); + + g_free(rep_pname); + fclose(cpuinfo); + + /* re-duplicate missing data for /proc/cpuinfo variant that de-duplicated it */ +#define REDUP(f) if (dproc->f && !processor->f) processor->f = g_strdup(dproc->f); + Processor *dproc; + GSList *l; + l = procs = g_slist_reverse(procs); + while (l) { + processor = l->data; + if (processor->model_name) { + dproc = processor; + } else if (dproc) { + REDUP(model_name); + REDUP(revision); + } + l = g_slist_next(l); + } + procs = g_slist_reverse(procs); + + /* data not from /proc/cpuinfo */ + for (pi = procs; pi; pi = pi->next) { + processor = (Processor *) pi->data; + + /* strings can't be null or segfault later */ + STRIFNULL(processor->model_name, _("POWER Processor") ); + UNKIFNULL(processor->revision); + + /* topo & freq */ + processor->cpufreq = cpufreq_new(processor->id); + processor->cputopo = cputopo_new(processor->id); + + if (processor->cpufreq->cpukhz_max) + processor->cpu_mhz = processor->cpufreq->cpukhz_max / 1000; + + } + + return procs; +} + +gchar *processor_name(GSList * processors) { + return processor_name_default(processors); +} + +gchar *processor_describe(GSList * processors) { + return processor_describe_default(processors); +} + +gchar * +processor_get_detailed_info(Processor *processor) +{ + gchar *tmp_cpufreq, *tmp_topology, *ret; + + tmp_topology = cputopo_section_str(processor->cputopo); + tmp_cpufreq = cpufreq_section_str(processor->cpufreq); + + ret = g_strdup_printf("[%s]\n" + "%s=%s\n" /* model */ + "%s=%s\n" /* revision */ + "%s=%.2f %s\n" /* frequency */ + "%s=%.2f\n" /* bogomips */ + "%s=%s\n" /* byte order */ + "%s" /* topology */ + "%s" /* frequency scaling */ + "%s",/* empty */ + _("Processor"), + _("Model"), processor->model_name, + _("Revision"), processor->revision, + _("Frequency"), processor->cpu_mhz, _("MHz"), + _("BogoMips"), processor->bogomips, + _("Byte Order"), byte_order_str(), + tmp_topology, + tmp_cpufreq, + ""); + g_free(tmp_cpufreq); + g_free(tmp_topology); + return ret; +} + +gchar *processor_get_info(GSList * processors) +{ + Processor *processor; + + if (g_slist_length(processors) > 1) { + gchar *ret, *tmp, *hashkey; + GSList *l; + + tmp = g_strdup(""); + + for (l = processors; l; l = l->next) { + processor = (Processor *) l->data; + + tmp = g_strdup_printf("%s$CPU%d$%s=%.2f %s\n", + tmp, processor->id, + processor->model_name, + processor->cpu_mhz, _("MHz")); + + hashkey = g_strdup_printf("CPU%d", processor->id); + moreinfo_add_with_prefix("DEV", hashkey, + processor_get_detailed_info(processor)); + g_free(hashkey); + } + + ret = g_strdup_printf("[$ShellParam$]\n" + "ViewType=1\n" + "[Processors]\n" + "%s", tmp); + g_free(tmp); + + return ret; + } + + processor = (Processor *) processors->data; + return processor_get_detailed_info(processor); +} diff --git a/modules/devices/printers.c b/modules/devices/printers.c new file mode 100644 index 00000000..fb9389ac --- /dev/null +++ b/modules/devices/printers.c @@ -0,0 +1,272 @@ +/* + * HardInfo - Displays System Information + * Copyright (C) 2003-2007 L. A. F. Pereira <l@tia.mat.br> + * + * 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 or later. + * + * 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 + */ + +#ifndef _XOPEN_SOURCE + #define _XOPEN_SOURCE +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <time.h> + +#include "devices.h" + +typedef struct _CUPSDest CUPSDest; +typedef struct _CUPSOption CUPSOption; + +struct _CUPSOption { + char *name, *value; +}; + +struct _CUPSDest { + char *name, *instance; + int is_default; + int num_options; + CUPSOption *options; +}; + +static int (*cups_dests_get) (CUPSDest **dests) = NULL; +static int (*cups_dests_free) (int num_dests, CUPSDest *dests) = NULL; +static gboolean cups_init = FALSE; + +GModule *cups; + +void +init_cups(void) +{ + const char *libcups[] = { "libcups", "libcups.so", "libcups.so.1", "libcups.so.2", NULL }; + + if (!(cups_dests_get && cups_dests_free)) { + int i; + + for (i = 0; libcups[i] != NULL; i++) { + cups = g_module_open(libcups[i], G_MODULE_BIND_LAZY); + if (cups) + break; + } + + if (!cups) { + cups_init = FALSE; + return; + } + + if (!g_module_symbol(cups, "cupsGetDests", (gpointer) & cups_dests_get) + || !g_module_symbol(cups, "cupsFreeDests", (gpointer) & cups_dests_free)) { + if(cups) g_module_close(cups); + cups_init = FALSE; + } + } + + cups_init = TRUE; +} + +gchar *__cups_callback_ptype(gchar *strvalue) +{ + if (strvalue) { + unsigned value = atoi(strvalue); + gchar *output = g_strdup("\n"); + + if (value & 0x0004) + output = h_strdup_cprintf(_("\342\232\254 Can do black and white printing=\n"), output); + if (value & 0x0008) + output = h_strdup_cprintf(_("\342\232\254 Can do color printing=\n"), output); + if (value & 0x0010) + output = h_strdup_cprintf(_("\342\232\254 Can do duplexing=\n"), output); + if (value & 0x0020) + output = h_strdup_cprintf(_("\342\232\254 Can do staple output=\n"), output); + if (value & 0x0040) + output = h_strdup_cprintf(_("\342\232\254 Can do copies=\n"), output); + if (value & 0x0080) + output = h_strdup_cprintf(_("\342\232\254 Can collate copies=\n"), output); + if (value & 0x80000) + output = h_strdup_cprintf(_("\342\232\254 Printer is rejecting jobs=\n"), output); + if (value & 0x1000000) + output = h_strdup_cprintf(_("\342\232\254 Printer was automatically discovered and added=\n"), output); + + return output; + } else { + return g_strdup(_("Unknown")); + } +} + +gchar *__cups_callback_state(gchar *value) +{ + if (!value) { + return g_strdup(_("Unknown")); + } + + if (g_str_equal(value, "3")) { + return g_strdup(_("Idle")); + } else if (g_str_equal(value, "4")) { + return g_strdup(_("Printing a Job")); + } else if (g_str_equal(value, "5")) { + return g_strdup(_("Stopped")); + } else { + return g_strdup(_("Unknown")); + } +} + +gchar *__cups_callback_state_change_time(gchar *value) +{ + struct tm tm; + char buf[255]; + + if (value) { + strptime(value, "%s", &tm); + strftime(buf, sizeof(buf), "%c", &tm); + + return g_strdup(buf); + } else { + return g_strdup(_("Unknown")); + } +} + +gchar *__cups_callback_boolean(gchar *value) +{ + if (value) { + return g_strdup(g_str_equal(value, "1") ? _("Yes") : _("No")); + } else { + return g_strdup(_("Unknown")); + } +} + +const struct { + char *key, *name; + gchar *(*callback)(gchar *value); + gboolean maybe_vendor; +} cups_fields[] = { + { "Printer Information", NULL, NULL }, + { "printer-info", "Destination Name", NULL }, + { "printer-make-and-model", "Make and Model", NULL, TRUE }, + + { "Capabilities", NULL, NULL }, + { "printer-type", "#", __cups_callback_ptype }, + + { "Printer State", NULL, NULL }, + { "printer-state", "State", __cups_callback_state }, + { "printer-state-change-time", "Change Time", __cups_callback_state_change_time }, + { "printer-state-reasons", "State Reasons" }, + + { "Sharing Information", NULL, NULL }, + { "printer-is-shared", "Shared?", __cups_callback_boolean }, + { "printer-location", "Physical Location" }, + { "auth-info-required", "Authentication Required", __cups_callback_boolean }, + + { "Jobs", NULL, NULL }, + { "job-hold-until", "Hold Until", NULL }, + { "job-priority", "Priority", NULL }, + { "printer-is-accepting-jobs", "Accepting Jobs", __cups_callback_boolean }, + + { "Media", NULL, NULL }, + { "media", "Media", NULL }, + { "finishings", "Finishings", NULL }, + { "copies", "Copies", NULL }, +}; + +void +scan_printers_do(void) +{ + guint num_dests, j, i; + CUPSDest *dests; + gchar *prn_id, *prn_moreinfo; + + g_free(printer_list); + g_free(printer_icons); + + if (!cups_init) { + init_cups(); + + printer_icons = g_strdup(""); + printer_list = g_strdup(_("[Printers]\n" + "No suitable CUPS library found=")); + return; + } + + /* remove old devices from global device table */ + moreinfo_del_with_prefix("DEV:PRN"); + + num_dests = cups_dests_get(&dests); + if (num_dests > 0) { + printer_list = g_strdup_printf(_("[Printers (CUPS)]\n")); + printer_icons = g_strdup(""); + for (i = 0; i < num_dests; i++) { + GHashTable *options; + + options = g_hash_table_new(g_str_hash, g_str_equal); + + for (j = 0; (int)j < dests[i].num_options; j++) { + g_hash_table_insert(options, + g_strdup(dests[i].options[j].name), + g_strdup(dests[i].options[j].value)); + } + + prn_id = g_strdup_printf("PRN%d", i); + + printer_list = h_strdup_cprintf("\n$%s$%s=%s\n", + printer_list, + prn_id, + dests[i].name, + dests[i].is_default ? ((params.markup_ok) ? "<i>Default</i>" : "(Default)") : ""); + printer_icons = h_strdup_cprintf("\nIcon$%s$%s=printer.png", + printer_icons, + prn_id, + dests[i].name); + + prn_moreinfo = g_strdup(""); + for (j = 0; j < G_N_ELEMENTS(cups_fields); j++) { + if (!cups_fields[j].name) { + prn_moreinfo = h_strdup_cprintf("[%s]\n", + prn_moreinfo, + cups_fields[j].key); + } else { + gchar *temp; + + temp = g_hash_table_lookup(options, cups_fields[j].key); + + if (cups_fields[j].callback) { + temp = cups_fields[j].callback(temp); + } else { + if (temp) { + /* FIXME Do proper escaping */ + temp = g_strdup(strreplacechr(temp, "&=", ' ')); + } else { + temp = g_strdup(_("Unknown")); + } + } + + prn_moreinfo = h_strdup_cprintf("%s%s=%s\n", + prn_moreinfo, + cups_fields[j].maybe_vendor ? "$^$" : "", + cups_fields[j].name, + temp); + + g_free(temp); + } + } + + moreinfo_add_with_prefix("DEV", prn_id, prn_moreinfo); + g_free(prn_id); + g_hash_table_destroy(options); + } + + cups_dests_free(num_dests, dests); + } else { + printer_list = g_strdup(_("[Printers]\n" + "No printers found=\n")); + } +} diff --git a/modules/devices/resources.c b/modules/devices/resources.c new file mode 100644 index 00000000..c9d1ccb5 --- /dev/null +++ b/modules/devices/resources.c @@ -0,0 +1,129 @@ +/* + * HardInfo - Displays System Information + * Copyright (C) 2003-2008 L. A. F. Pereira <l@tia.mat.br> + * + * 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 or later. + * + * 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 "devices.h" + +static gchar *_resources = NULL; +static gboolean _require_root = FALSE; + +#if GLIB_CHECK_VERSION(2,14,0) +static GRegex *_regex_pci = NULL, + *_regex_module = NULL; + +static gchar *_resource_obtain_name(gchar *name) +{ + gchar *temp; + + if (!_regex_pci && !_regex_module) { + _regex_pci = g_regex_new("^[0-9a-fA-F]{4}:[0-9a-fA-F]{2}:" + "[0-9a-fA-F]{2}\\.[0-9a-fA-F]{1}$", + 0, 0, NULL); + _regex_module = g_regex_new("^[0-9a-zA-Z\\_\\-]+$", 0, 0, NULL); + } + + name = g_strstrip(name); + + if (g_regex_match(_regex_pci, name, 0, NULL)) { + temp = module_call_method_param("devices::getPCIDeviceDescription", name); + if (temp) { + if (params.markup_ok) + return g_strdup_printf("<b><small>PCI</small></b> %s", (gchar *)idle_free(temp)); + else + return g_strdup_printf("PCI %s", (gchar *)idle_free(temp)); + } + } else if (g_regex_match(_regex_module, name, 0, NULL)) { + temp = module_call_method_param("computer::getKernelModuleDescription", name); + if (temp) { + if (params.markup_ok) + return g_strdup_printf("<b><small>Module</small></b> %s", (gchar *)idle_free(temp)); + else + return g_strdup_printf("Module %s", (gchar *)idle_free(temp)); + } + } + + return g_strdup(name); +} +#else +static gchar *_resource_obtain_name(gchar *name) +{ + return g_strdup(name); +} +#endif + +void scan_device_resources(gboolean reload) +{ + SCAN_START(); + FILE *io; + gchar buffer[256]; + guint i; + gint zero_to_zero_addr = 0; + + struct { + gchar *file; + gchar *description; + } resources[] = { + { "/proc/ioports", "[I/O Ports]\n" }, + { "/proc/iomem", "[Memory]\n" }, + { "/proc/dma", "[DMA]\n" } + }; + + g_free(_resources); + _resources = g_strdup(""); + + for (i = 0; i < G_N_ELEMENTS(resources); i++) { + if ((io = fopen(resources[i].file, "r"))) { + _resources = h_strconcat(_resources, resources[i].description, NULL); + + while (fgets(buffer, 256, io)) { + gchar **temp = g_strsplit(buffer, ":", 2); + gchar *name = _resource_obtain_name(temp[1]); + + if (strstr(temp[0], "0000-0000")) + zero_to_zero_addr++; + + if (params.markup_ok) + _resources = h_strdup_cprintf("<tt>%s</tt>=%s\n", _resources, + temp[0], name); + else + _resources = h_strdup_cprintf(">%s=%s\n", _resources, + temp[0], name); + + g_strfreev(temp); + g_free(name); + } + + fclose(io); + } + } + + _require_root = zero_to_zero_addr > 16; + + SCAN_END(); +} + +gchar *callback_device_resources(void) +{ + return g_strdup(_resources); +} + +gboolean root_required_for_resources(void) +{ + return _require_root; +} diff --git a/modules/devices/riscv/processor.c b/modules/devices/riscv/processor.c new file mode 100644 index 00000000..f2e51c91 --- /dev/null +++ b/modules/devices/riscv/processor.c @@ -0,0 +1,233 @@ +/* + * HardInfo - Displays System Information + * Copyright (C) 2003-2006 L. A. F. Pereira <l@tia.mat.br> + * + * 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 or later. + * + * 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 + */ + +/* source: https://github.com/riscv/riscv-linux/blob/riscv-next/arch/riscv/kernel/cpu.c */ + +#include "hardinfo.h" +#include "devices.h" +#include "cpu_util.h" + +#include "riscv_data.h" +#include "riscv_data.c" + +GSList * +processor_scan(void) +{ + GSList *procs = NULL; + Processor *processor = NULL; + FILE *cpuinfo; + gchar buffer[128]; + gchar *rep_pname = NULL; + gchar *tmpfreq_str = NULL; + GSList *pi = NULL; + + cpuinfo = fopen(PROC_CPUINFO, "r"); + if (!cpuinfo) + return NULL; + +#define CHECK_FOR(k) (g_str_has_prefix(tmp[0], k)) + while (fgets(buffer, 128, cpuinfo)) { + gchar **tmp = g_strsplit(buffer, ":", 2); + if (tmp[0] && tmp[1]) { + tmp[0] = g_strstrip(tmp[0]); + tmp[1] = g_strstrip(tmp[1]); + } else { + g_strfreev(tmp); + continue; + } + + //get_str("Processor", rep_pname); + + if ( CHECK_FOR("hart") ) { + /* finish previous */ + if (processor) { + procs = g_slist_append(procs, processor); + } + + /* start next */ + processor = g_new0(Processor, 1); + processor->id = atol(tmp[1]); + + if (rep_pname) + processor->model_name = g_strdup(rep_pname); + + g_strfreev(tmp); + continue; + } + + if (!processor && + ( CHECK_FOR("mmu") + || CHECK_FOR("isa") + || CHECK_FOR("uarch") ) ) { + + /* single proc/core may not have "hart : n" */ + processor = g_new0(Processor, 1); + processor->id = 0; + + if (rep_pname) + processor->model_name = g_strdup(rep_pname); + } + + if (processor) { + get_str("mmu", processor->mmu); + get_str("isa", processor->isa); + get_str("uarch", processor->uarch); + } + g_strfreev(tmp); + } + + if (processor) + procs = g_slist_append(procs, processor); + + g_free(rep_pname); + fclose(cpuinfo); + + /* TODO: redup */ + + /* data not from /proc/cpuinfo */ + for (pi = procs; pi; pi = pi->next) { + processor = (Processor *) pi->data; + + /* strings can't be null or segfault later */ + STRIFNULL(processor->model_name, _("RISC-V Processor") ); + UNKIFNULL(processor->mmu); + UNKIFNULL(processor->isa); + UNKIFNULL(processor->uarch); + + processor->flags = riscv_isa_to_flags(processor->isa); + + /* topo & freq */ + processor->cpufreq = cpufreq_new(processor->id); + processor->cputopo = cputopo_new(processor->id); + + if (processor->cpufreq->cpukhz_max) + processor->cpu_mhz = processor->cpufreq->cpukhz_max / 1000; + else + processor->cpu_mhz = 0.0f; + } + + return procs; +} + +gchar *processor_get_capabilities_from_flags(gchar * strflags) +{ + gchar **flags, **old; + gchar *tmp = NULL; + gint j = 0; + + flags = g_strsplit(strflags, " ", 0); + old = flags; + + while (flags[j]) { + const gchar *meaning = riscv_ext_meaning( flags[j] ); + + if (meaning) { + tmp = h_strdup_cprintf("%s=%s\n", tmp, flags[j], meaning); + } else { + tmp = h_strdup_cprintf("%s=\n", tmp, flags[j]); + } + j++; + } + if (tmp == NULL || g_strcmp0(tmp, "") == 0) + tmp = g_strdup_printf("%s=%s\n", "empty", _("Empty List")); + + g_strfreev(old); + return tmp; +} + +gchar *processor_name(GSList * processors) { + return processor_name_default(processors); +} + +gchar *processor_describe(GSList * processors) { + return processor_describe_default(processors); +} + +gchar * +processor_get_detailed_info(Processor *processor) +{ + gchar *tmp_flags, *tmp_cpufreq, *tmp_topology, *ret; + tmp_flags = processor_get_capabilities_from_flags(processor->flags); + tmp_topology = cputopo_section_str(processor->cputopo); + tmp_cpufreq = cpufreq_section_str(processor->cpufreq); + + ret = g_strdup_printf("[%s]\n" + "%s=%s\n" /* model */ + "%s=%s\n" /* isa */ + "%s=%s\n" /* uarch */ + "%s=%s\n" /* mmu */ + "%s=%.2f %s\n" /* frequency */ + "%s=%s\n" /* byte order */ + "%s" /* topology */ + "%s" /* frequency scaling */ + "[%s]\n" /* extensions */ + "%s" + "%s",/* empty */ + _("Processor"), + _("Model"), processor->model_name, + _("Architecture"), processor->isa, + _("uarch"), processor->uarch, + _("MMU"), processor->mmu, + _("Frequency"), processor->cpu_mhz, _("MHz"), + _("Byte Order"), byte_order_str(), + tmp_topology, + tmp_cpufreq, + _("Capabilities"), tmp_flags, + ""); + g_free(tmp_flags); + g_free(tmp_cpufreq); + g_free(tmp_topology); + return ret; +} + +gchar *processor_get_info(GSList * processors) +{ + Processor *processor; + + if (g_slist_length(processors) > 1) { + gchar *ret, *tmp, *hashkey; + GSList *l; + + tmp = g_strdup(""); + + for (l = processors; l; l = l->next) { + processor = (Processor *) l->data; + + tmp = g_strdup_printf("%s$CPU%d$%s=%.2f %s\n", + tmp, processor->id, + processor->model_name, + processor->cpu_mhz, _("MHz")); + + hashkey = g_strdup_printf("CPU%d", processor->id); + moreinfo_add_with_prefix("DEV", hashkey, + processor_get_detailed_info(processor)); + g_free(hashkey); + } + + ret = g_strdup_printf("[$ShellParam$]\n" + "ViewType=1\n" + "[Processors]\n" + "%s", tmp); + g_free(tmp); + + return ret; + } + + processor = (Processor *) processors->data; + return processor_get_detailed_info(processor); +} diff --git a/modules/devices/riscv/riscv_data.c b/modules/devices/riscv/riscv_data.c new file mode 100644 index 00000000..917e8e06 --- /dev/null +++ b/modules/devices/riscv/riscv_data.c @@ -0,0 +1,212 @@ +/* + * 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 <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include "riscv_data.h" + +#ifndef C_ +#define C_(Ctx, String) String +#endif +#ifndef NC_ +#define NC_(Ctx, String) String +#endif + +static struct { + char *name, *meaning; +} tab_ext_meaning[] = { + { "RV32", NC_("rv-ext", /*/ext:RV32*/ "RISC-V 32-bit") }, + { "RV64", NC_("rv-ext", /*/ext:RV64*/ "RISC-V 64-bit") }, + { "RV128", NC_("rv-ext", /*/ext:RV128*/ "RISC-V 128-bit") }, + { "E", NC_("rv-ext", /*/ext:E*/ "Base embedded integer instructions (15 registers)") }, + { "I", NC_("rv-ext", /*/ext:I*/ "Base integer instructions (31 registers)") }, + { "M", NC_("rv-ext", /*/ext:M*/ "Hardware integer multiply and divide") }, + { "A", NC_("rv-ext", /*/ext:A*/ "Atomic memory operations") }, + { "C", NC_("rv-ext", /*/ext:C*/ "Compressed 16-bit instructions") }, + { "F", NC_("rv-ext", /*/ext:F*/ "Floating-point instructions, single-precision") }, + { "D", NC_("rv-ext", /*/ext:D*/ "Floating-point instructions, double-precision") }, + { "Q", NC_("rv-ext", /*/ext:Q*/ "Floating-point instructions, quad-precision") }, + { "B", NC_("rv-ext", /*/ext:B*/ "Bit manipulation instructions") }, + { "V", NC_("rv-ext", /*/ext:V*/ "Vector operations") }, + { "T", NC_("rv-ext", /*/ext:T*/ "Transactional memory") }, + { "P", NC_("rv-ext", /*/ext:P*/ "Packed SIMD instructions") }, + { "L", NC_("rv-ext", /*/ext:L*/ "Decimal floating-point instructions") }, + { "J", NC_("rv-ext", /*/ext:J*/ "Dynamically translated languages") }, + { "N", NC_("rv-ext", /*/ext:N*/ "User-level interrupts") }, + { NULL, NULL } +}; + +static char all_extensions[1024] = ""; + +#define APPEND_EXT(f) strcat(all_extensions, f); strcat(all_extensions, " "); +const char *riscv_ext_list() { + int i = 0, built = 0; + built = strlen(all_extensions); + if (!built) { + while(tab_ext_meaning[i].name != NULL) { + APPEND_EXT(tab_ext_meaning[i].name); + i++; + } + } + return all_extensions; +} + +const char *riscv_ext_meaning(const char *ext) { + int i = 0, l = 0; + char *c = NULL; + if (ext) { + c = strchr(ext, ':'); /* allow extension:version, ignore version */ + if (c != NULL) + l = c - ext; + else + l = strlen(ext); + while(tab_ext_meaning[i].name != NULL) { + if (strncasecmp(tab_ext_meaning[i].name, ext, l) == 0) { + if (tab_ext_meaning[i].meaning != NULL) + return C_("rv-ext", tab_ext_meaning[i].meaning); + else return NULL; + } + i++; + } + } + return NULL; +} + +/* see RISC-V spec 2.2: Chapter 22: ISA Subset Naming Conventions */ + +/* Spec says case-insensitive, but I prefer single-letter extensions + * capped and version string (like "2p1") with a lowercase p. */ +#define RV_FIX_CASE(fstr,vo) \ + p = fstr; while (*p != 0 && *p != ':') { if (!vo) *p = toupper(*p); p++; } \ + if (*p == ':') while (*p != 0) { if (*p == 'P') *p = 'p'; p++; } + +static int riscv_isa_next(const char *isap, char *flag) { + char *p = NULL, *start = NULL; + char *next_sep = NULL, *next_digit = NULL; + int skip_len = 0, tag_len = 0, ver_len = 0; + char ext_str[32], ver_str[32]; + + if (isap == NULL) + return 0; + + /* find start by skipping any '_' */ + start = (char*)isap; + while (*start != 0 && *start == '_') { start++; skip_len++; }; + if (*start == 0) + return 0; + + /* find next '_' or \0 */ + p = start; while (*p != 0 && *p != '_') { p++; }; next_sep = p; + + /* find next digit that may be a version, find length of version */ + p = start; while (*p != 0 && !isdigit(*p)) { p++; }; + if (isdigit(*p)) next_digit = p; + if (next_digit) { + while (*p != 0 && (isdigit(*p) || *p == 'p' || *p == 'P') ) { + if ((*p == 'p' || *p == 'P') && !isdigit(*(p+1)) ) + break; + ver_len++; + p++; + } + } + + /* is next version nearer than next separator */ + p = start; + if (next_digit && next_digit < next_sep) + tag_len = next_digit - p; + else { + tag_len = next_sep - p; + ver_len = 0; + } + + switch(*p) { + case 'S': case 's': /* supervisor extension */ + case 'X': case 'x': /* custom extension */ + /* custom supervisor extension (SX..) handled by S */ + break; + default: /* single character (standard) extension */ + tag_len = 1; + if (next_digit != p+1) ver_len = 0; + break; + } + + memset(ext_str, 0, 32); + memset(ver_str, 0, 32); + if (ver_len) { + strncpy(ext_str, p, tag_len); + strncpy(ver_str, next_digit, ver_len); + sprintf(flag, "%s:%s", ext_str, ver_str); + if (tag_len == 1) { + RV_FIX_CASE(flag, 0); + } else { + RV_FIX_CASE(flag, 1); + } + return skip_len + tag_len + ver_len; + } else { + strncpy(ext_str, p, tag_len); + sprintf(flag, "%s", ext_str); + if (tag_len == 1) { RV_FIX_CASE(flag, 0); } + return skip_len + tag_len; + } +} + +#define FSTR_SIZE 1024 +#define RV_CHECK_FOR(e) ( strncasecmp(ps, e, 2) == 0 ) +#define ADD_EXT_FLAG(ext) el = strlen(ext); strncpy(pd, ext, el); strncpy(pd + el, " ", 1); pd += el + 1; +char *riscv_isa_to_flags(const char *isa) { + char *flags = NULL, *ps = (char*)isa, *pd = NULL; + char flag_buf[64] = ""; + int isa_len = 0, tl = 0, el = 0; /* el used in macro */ + + if (isa) { + isa_len = strlen(isa); + flags = malloc(FSTR_SIZE); + if (flags) { + memset(flags, 0, FSTR_SIZE); + ps = (char*)isa; + pd = flags; + if ( RV_CHECK_FOR("RV") ) + { ps += 2; } + if ( RV_CHECK_FOR("32") ) + { ADD_EXT_FLAG("RV32"); ps += 2; } + else if ( RV_CHECK_FOR("64") ) + { ADD_EXT_FLAG("RV64"); ps += 2; } + else if ( RV_CHECK_FOR("128") ) + { ADD_EXT_FLAG("RV128"); ps += 3; } + + while( (tl = riscv_isa_next(ps, flag_buf)) ) { + if (flag_buf[0] == 'G') { /* G = IMAFD */ + flag_buf[0] = 'I'; ADD_EXT_FLAG(flag_buf); + flag_buf[0] = 'M'; ADD_EXT_FLAG(flag_buf); + flag_buf[0] = 'A'; ADD_EXT_FLAG(flag_buf); + flag_buf[0] = 'F'; ADD_EXT_FLAG(flag_buf); + flag_buf[0] = 'D'; ADD_EXT_FLAG(flag_buf); + } else { + ADD_EXT_FLAG(flag_buf); + } + ps += tl; + if (ps - isa >= isa_len) break; /* just in case */ + } + } + } + return flags; +} diff --git a/modules/devices/riscv/riscv_data.h b/modules/devices/riscv/riscv_data.h new file mode 100644 index 00000000..1d3a0a48 --- /dev/null +++ b/modules/devices/riscv/riscv_data.h @@ -0,0 +1,33 @@ +/* + * 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. + * + */ + +#ifndef _RISCVDATA_H_ +#define _RISCVDATA_H_ + +/* convert RISC-V ISA string to flags list */ +char *riscv_isa_to_flags(const char *isa); + +/* all known extensions as flags list */ +const char *riscv_ext_list(void); + +/* get meaning of flag */ +const char *riscv_ext_meaning(const char *ext); + +#endif diff --git a/modules/devices/s390/processor.c b/modules/devices/s390/processor.c new file mode 100644 index 00000000..e4f2b303 --- /dev/null +++ b/modules/devices/s390/processor.c @@ -0,0 +1,180 @@ +/* + * HardInfo - Displays System Information + * Copyright (C) 2003-2006 L. A. F. Pereira <l@tia.mat.br> + * + * 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 or later. + * + * 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 "devices.h" +#include "string.h" +#include "cpu_util.h" + +GSList * +processor_scan(void) +{ + GSList *procs = NULL; + Processor *processor = NULL; + FILE *cpuinfo; + gchar buffer[128]; + gchar *vendor_id = NULL; + gint num_procs = 0; + gfloat bogomips = 0.0f; + GSList *pi = NULL; + + cpuinfo = fopen(PROC_CPUINFO, "r"); + if (!cpuinfo) + return NULL; + +#define CHECK_FOR(k) (g_str_has_prefix(tmp[0], k)) + while (fgets(buffer, 128, cpuinfo)) { + gchar **tmp = g_strsplit(buffer, ":", 2); + if (tmp[0] && tmp[1]) { + tmp[0] = g_strstrip(tmp[0]); + tmp[1] = g_strstrip(tmp[1]); + } else { + g_strfreev(tmp); + continue; + } + + get_str("vendor_id", vendor_id); + get_int("# processors", num_procs); + get_int("bogomips per cpu", bogomips); + + if ( CHECK_FOR("processor") ) { + /* finish previous */ + if (processor) { + procs = g_slist_append(procs, processor); + } + + /* start next */ + processor = g_new0(Processor, 1); + if (strlen(tmp[0]) >= 10) + processor->id = atol(tmp[0] + 10); /* processor n: ... */ + else + processor->id = 0; /* idk */ + processor->proc_str = g_strdup(tmp[1]); + + if (vendor_id) + processor->model_name = g_strdup(vendor_id); + if (bogomips) + processor->bogomips = bogomips; + + g_strfreev(tmp); + continue; + } + + g_strfreev(tmp); + } + + if (processor) + procs = g_slist_append(procs, processor); + + g_free(vendor_id); + fclose(cpuinfo); + + /* data not from /proc/cpuinfo */ + for (pi = procs; pi; pi = pi->next) { + processor = (Processor *) pi->data; + + /* strings can't be null or segfault later */ + STRIFNULL(processor->model_name, _("S390 Processor") ); + UNKIFNULL(processor->proc_str); + + /* topo & freq */ + processor->cpufreq = cpufreq_new(processor->id); + processor->cputopo = cputopo_new(processor->id); + + if (processor->cpufreq->cpukhz_max) + processor->cpu_mhz = processor->cpufreq->cpukhz_max / 1000; + else + processor->cpu_mhz = 0.0f; + + } + + return procs; +} + +gchar *processor_name(GSList * processors) { + return processor_name_default(processors); +} + +gchar *processor_describe(GSList * processors) { + return processor_describe_default(processors); +} + +gchar * +processor_get_detailed_info(Processor *processor) +{ + gchar *tmp_cpufreq, *tmp_topology, *ret; + tmp_topology = cputopo_section_str(processor->cputopo); + tmp_cpufreq = cpufreq_section_str(processor->cpufreq); + + ret = g_strdup_printf("[%s]\n" + "%s=%s\n" /* model */ + "%s=%s\n" /* proc string */ + "%s=%.2f\n" /* bogomips */ + "%s=%s\n" /* byte order */ + "%s" /* topology */ + "%s", /* frequency scaling */ + _("Processor"), + _("Model"), processor->model_name, + _("ID String"), processor->proc_str, + _("BogoMips"), processor->bogomips, + _("Byte Order"), byte_order_str(), + tmp_topology, + tmp_cpufreq + ); + g_free(tmp_cpufreq); + g_free(tmp_topology); + return ret; +} + +gchar *processor_get_info(GSList * processors) +{ + Processor *processor; + + if (g_slist_length(processors) > 1) { + gchar *ret, *tmp, *hashkey; + GSList *l; + + tmp = g_strdup(""); + + for (l = processors; l; l = l->next) { + processor = (Processor *) l->data; + + tmp = g_strdup_printf("%s$CPU%d$%s=%.2f %s\n", + tmp, processor->id, + processor->model_name, + processor->cpu_mhz, _("MHz")); + + hashkey = g_strdup_printf("CPU%d", processor->id); + moreinfo_add_with_prefix("DEV", hashkey, + processor_get_detailed_info(processor)); + g_free(hashkey); + } + + ret = g_strdup_printf("[$ShellParam$]\n" + "ViewType=1\n" + "[Processors]\n" + "%s", tmp); + g_free(tmp); + + return ret; + } + + processor = (Processor *) processors->data; + return processor_get_detailed_info(processor); +} + diff --git a/modules/devices/sensors.c b/modules/devices/sensors.c new file mode 100644 index 00000000..095f0bc4 --- /dev/null +++ b/modules/devices/sensors.c @@ -0,0 +1,770 @@ +/* + * HardInfo - Displays System Information + * Copyright (C) 2003-2006 L. A. F. Pereira <l@tia.mat.br> + * + * 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 or later. + * + * 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 "devices.h" +#include "expr.h" +#include "hardinfo.h" +#include "socket.h" +#include "udisks2_util.h" + +#if defined(HAS_LIBSENSORS) && HAS_LIBSENSORS +#include <sensors/sensors.h> +#endif + +gchar *sensors = NULL; +gchar *sensor_icons = NULL; +GHashTable *sensor_compute = NULL; +GHashTable *sensor_labels = NULL; +gboolean hwmon_first_run = TRUE; + +static gchar *last_group = NULL; + +static void read_sensor_labels(gchar *devname) { + FILE *conf; + gchar buf[256], *line, *p; + gboolean lock = FALSE; + gint i; + + /* Try to open lm-sensors config file sensors3.conf */ + conf = fopen("/etc/sensors3.conf", "r"); + + /* If it fails, try to open sensors.conf */ + if (!conf) + conf = fopen("/etc/sensors.conf", "r"); + + if (!conf) { + /* Cannot open config file. */ + return; + } + + while (fgets(buf, 256, conf)) { + line = buf; + + remove_linefeed(line); + strend(line, '#'); + + if (*line == '\0') { + continue; + } else if (lock && strstr(line, "label")) { /* label lines */ + gchar **names = g_strsplit(strstr(line, "label") + 5, " ", 0); + gchar *key = NULL, *value = NULL; + + for (i = 0; names[i]; i++) { + if (names[i][0] == '\0') + continue; + + if (!key) + key = g_strdup_printf("%s/%s", devname, names[i]); + else if (!value) + value = g_strdup(names[i]); + else + value = g_strconcat(value, " ", names[i], NULL); + } + + remove_quotes(value); + g_hash_table_insert(sensor_labels, key, g_strstrip(value)); + + g_strfreev(names); + } else if (lock && strstr(line, "ignore")) { /* ignore lines */ + p = strstr(line, "ignore") + 6; + if (!strchr(p, ' ')) + continue; + + while (*p == ' ') + p++; + g_hash_table_insert(sensor_labels, g_strdup_printf("%s/%s", devname, p), "ignore"); + } else if (lock && strstr(line, "compute")) { /* compute lines */ + strend(line, ','); + gchar **formulas = g_strsplit(strstr(line, "compute") + 7, " ", 0); + gchar *key = NULL, *formula = NULL; + + for (i = 0; formulas[i]; i++) { + if (formulas[i][0] == '\0') + continue; + + if (!key) + key = g_strdup_printf("%s/%s", devname, formulas[i]); + else if (!formula) + formula = g_strdup(formulas[i]); + else + formula = g_strconcat(formula, formulas[i], NULL); + } + + g_strfreev(formulas); + g_hash_table_insert(sensor_compute, key, + math_string_to_postfix(formula)); + } else if (g_str_has_prefix(line, + "chip")) { /* chip lines (delimiter) */ + if (lock == FALSE) { + gchar **chips = g_strsplit(line, " ", 0); + + for (i = 1; chips[i]; i++) { + strend(chips[i], '*'); + + if (g_str_has_prefix(chips[i] + 1, devname)) { + lock = TRUE; + break; + } + } + + g_strfreev(chips); + } else { + break; + } + } + } + + fclose(conf); +} + +static void add_sensor(const char *type, + const char *sensor, + const char *parent, + double value, + const char *unit, + const char *icon) { + char key[64]; + + snprintf(key, sizeof(key), "%s/%s", parent, sensor); + + if (SENSORS_GROUP_BY_TYPE) { + // group by type + if (g_strcmp0(last_group, type) != 0) { + sensors = h_strdup_cprintf("[%s]\n", sensors, type); + g_free(last_group); + last_group = g_strdup(type); + } + sensors = h_strdup_cprintf("$%s$%s=%.2f%s|%s\n", sensors, + key, sensor, value, unit, parent); + } + else { + // group by device source / driver + if (g_strcmp0(last_group, parent) != 0) { + sensors = h_strdup_cprintf("[%s]\n", sensors, parent); + g_free(last_group); + last_group = g_strdup(parent); + } + sensors = h_strdup_cprintf("$%s$%s=%.2f%s|%s\n", sensors, + key, sensor, value, unit, type); + } + + if (icon != NULL) { + sensor_icons = h_strdup_cprintf("Icon$%s$%s=%s.png\n", sensor_icons, + key, sensor, icon); + } + + moreinfo_add_with_prefix("DEV", key, g_strdup_printf("%.2f%s", value, unit)); + + lginterval = h_strdup_cprintf("UpdateInterval$%s=1000\n", lginterval, key); +} + +static gchar *get_sensor_label_from_conf(gchar *key) { + gchar *ret; + ret = g_hash_table_lookup(sensor_labels, key); + + if (ret) + return g_strdup(ret); + + return NULL; +} + +static float adjust_sensor(gchar *key, float value) { + GSList *postfix; + + postfix = g_hash_table_lookup(sensor_compute, key); + if (!postfix) + return value; + + return math_postfix_eval(postfix, value); +} + +static char *get_sensor_path(int number, const char *prefix) { + return g_strdup_printf("/sys/class/hwmon/hwmon%d/%s", number, prefix); +} + +static char *determine_devname_for_hwmon_path(char *path) { + char *tmp, *devname = NULL; + + // device name + tmp = g_strdup_printf("%s/name", path); + g_file_get_contents(tmp, &devname, NULL, NULL); + g_free(tmp); + if (devname) + return g_strstrip(devname); + + // fallback: driver name (from driver link) + tmp = g_strdup_printf("%s/device/driver", path); + devname = g_file_read_link(tmp, NULL); + g_free(tmp); + + if (!devname) { + // fallback: device folder name (from device link) + tmp = g_strdup_printf("%s/device", path); + devname = g_file_read_link(tmp, NULL); + g_free(tmp); + } + + if (devname) { + tmp = g_path_get_basename(devname); + g_free(devname); + return tmp; + } + + return g_strdup("unknown"); +} + +struct HwmonSensor { + const char *friendly_name; + const char *value_file_regex; + const char *value_path_format; + const char *label_path_format; + const char *key_format; + const char *unit; + const float adjust_ratio; + const char *icon; +}; + +static const struct HwmonSensor hwmon_sensors[] = { + { + "Fan Speed", + "^fan([0-9]+)_input$", + "%s/fan%d_input", + "%s/fan%d_label", + "fan%d", + " RPM", + 1.0, + "fan" + }, + { + "Temperature", + "^temp([0-9]+)_input$", + "%s/temp%d_input", + "%s/temp%d_label", + "temp%d", + "\302\260C", + 1000.0, + "therm" + }, + { + "Voltage", + "^in([0-9]+)_input$", + "%s/in%d_input", + "%s/in%d_label", + "in%d", + "V", + 1000.0, + "bolt" + }, + { + "Current", + "^curr([0-9]+)_input$", + "%s/curr%d_input", + "%s/curr%d_label", + "curr%d", + " A", + 1000.0, + "bolt" + }, + { + "Power", + "^power([0-9]+)_input$", + "%s/power%d_input", + "%s/power%d_label", + "power%d", + " W", + 1000000.0, + "bolt" + }, + { + "CPU Voltage", + "^cpu([0-9]+)_vid$", + "%s/cpu%d_vid", + NULL, + "cpu%d_vid", + " V", + 1000.0, + "bolt" + }, + { } +}; + +static const char *hwmon_prefix[] = {"device", "", NULL}; + +static gboolean read_raw_hwmon_value(gchar *path_hwmon, const gchar *item_path_format, int item_id, gchar **result){ + gchar *full_path; + gboolean file_result; + + if (item_path_format == NULL) + return FALSE; + + full_path = g_strdup_printf(item_path_format, path_hwmon, item_id); + file_result = g_file_get_contents(full_path, result, NULL, NULL); + + g_free(full_path); + + return file_result; +} + +static void read_sensors_hwmon(void) { + int hwmon, count, min, max; + gchar *path_hwmon, *tmp, *devname, *name, *mon, *key; + const char **prefix, *entry; + GDir *dir; + GRegex *regex; + GMatchInfo *match_info; + GError *err = NULL; + + for (prefix = hwmon_prefix; *prefix; prefix++) { + hwmon = 0; + path_hwmon = get_sensor_path(hwmon, *prefix); + while (path_hwmon && g_file_test(path_hwmon, G_FILE_TEST_EXISTS)) { + const struct HwmonSensor *sensor; + + devname = determine_devname_for_hwmon_path(path_hwmon); + DEBUG("hwmon%d has device=%s", hwmon, devname); + if (hwmon_first_run) { + read_sensor_labels(devname); + } + + dir = g_dir_open(path_hwmon, 0, NULL); + if (!dir) + continue; + + for (sensor = hwmon_sensors; sensor->friendly_name; sensor++) { + DEBUG("current sensor type=%s", sensor->friendly_name); + regex = g_regex_new (sensor->value_file_regex, 0, 0, &err); + if (err != NULL){ + g_free(err); + err = NULL; + continue; + } + + g_dir_rewind(dir); + min = 999; + max = -1; + + while ((entry = g_dir_read_name(dir))) { + g_regex_match(regex, entry, 0, &match_info); + if (g_match_info_matches(match_info)) { + tmp = g_match_info_fetch(match_info, 1); + count = atoi(tmp); + g_free (tmp); + + if (count < min){ + min = count; + } + if (count > max){ + max = count; + } + } + g_match_info_free(match_info); + } + g_regex_unref(regex); + + for (count = min; count <= max; count++) { + if (!read_raw_hwmon_value(path_hwmon, sensor->value_path_format, count, &tmp)) { + continue; + } + + mon = g_strdup_printf(sensor->key_format, count); + key = g_strdup_printf("%s/%s", devname, mon); + name = get_sensor_label_from_conf(key); + if (name == NULL){ + if (read_raw_hwmon_value(path_hwmon, sensor->label_path_format, count, &name)){ + name = g_strchomp(name); + } + else{ + name = g_strdup(mon); + } + } + + if (!g_str_equal(name, "ignore")) { + float adjusted = adjust_sensor(key, + atof(tmp) / sensor->adjust_ratio); + + add_sensor(sensor->friendly_name, + name, + devname, + adjusted, + sensor->unit, + sensor->icon); + } + + g_free(tmp); + g_free(mon); + g_free(key); + g_free(name); + } + } + + g_dir_close(dir); + g_free(path_hwmon); + g_free(devname); + + path_hwmon = get_sensor_path(++hwmon, *prefix); + } + g_free(path_hwmon); + } + hwmon_first_run = FALSE; +} + +static void read_sensors_acpi(void) { + const gchar *path_tz = "/proc/acpi/thermal_zone"; + + if (g_file_test(path_tz, G_FILE_TEST_EXISTS)) { + GDir *tz; + + if ((tz = g_dir_open(path_tz, 0, NULL))) { + const gchar *entry; + + while ((entry = g_dir_read_name(tz))) { + gchar *path = + g_strdup_printf("%s/%s/temperature", path_tz, entry); + gchar *contents; + + if (g_file_get_contents(path, &contents, NULL, NULL)) { + int temperature; + + sscanf(contents, "temperature: %d C", &temperature); + + add_sensor("Temperature", + entry, + "ACPI Thermal Zone", + temperature, + "\302\260C", + "therm"); + } + } + + g_dir_close(tz); + } + } +} + +// Sensors for Apple PowerPC devices using Windfarm driver. +struct WindfarmSensorType { + const char *type; + const char *icon; + const char *file_regex; + const char *unit; + gboolean with_decimal_p; +}; +static const struct WindfarmSensorType windfarm_sensor_types[] = { + {"Fan", "fan", "^[a-z-]+-fan(-[0-9]+)?$", " RPM", FALSE}, + {"Temperature", "therm", "^[a-z-]+-temp(-[0-9]+)?$", "\302\260C", TRUE}, + {"Power", "bolt", "^[a-z-]+-power(-[0-9]+)?$", " W", TRUE}, + {"Current", "bolt", "^[a-z-]+-current(-[0-9]+)?$", " A", TRUE}, + {"Voltage", "bolt", "^[a-z-]+-voltage(-[0-9]+)?$", " V", TRUE}, + { } +}; +static void read_sensors_windfarm(void) +{ + const gchar *path_wf = "/sys/devices/platform/windfarm.0"; + GDir *wf; + gchar *tmp = NULL; + gint v1, v2; + double value; + + wf = g_dir_open(path_wf, 0, NULL); + if (wf) { + GRegex *regex; + GError *err = NULL; + const gchar *entry; + const struct WindfarmSensorType *sensor; + + for (sensor = windfarm_sensor_types; sensor->type; sensor++) { + DEBUG("current windfarm sensor type=%s", sensor->type); + regex = g_regex_new(sensor->file_regex, 0, 0, &err); + if (err != NULL) { + g_free(err); + err = NULL; + continue; + } + + g_dir_rewind(wf); + + while ((entry = g_dir_read_name(wf))) { + if (g_regex_match(regex, entry, 0, NULL)) { + gchar *path = g_strdup_printf("%s/%s", path_wf, entry); + if (g_file_get_contents(path, &tmp, NULL, NULL)) { + + if (sensor->with_decimal_p) { + // format source + // https://elixir.free-electrons.com/linux/v5.14/source/drivers/macintosh/windfarm_core.c#L301 + sscanf(tmp, "%d.%03d", &v1, &v2); + value = v1 + (v2 / 1000.0); + } else { + value = (double)atoi(tmp); + } + g_free(tmp); + + tmp = g_strdup(entry); + add_sensor(sensor->type, g_strdelimit(tmp, "-", ' '), + "windfarm", value, sensor->unit, + sensor->icon); + g_free(tmp); + } + g_free(path); + } + } + g_regex_unref(regex); + } + g_dir_close(wf); + } +} + +static void read_sensors_sys_thermal(void) { + const gchar *path_tz = "/sys/class/thermal"; + + if (g_file_test(path_tz, G_FILE_TEST_EXISTS)) { + GDir *tz; + + if ((tz = g_dir_open(path_tz, 0, NULL))) { + const gchar *entry; + gchar *temp = g_strdup(""); + + while ((entry = g_dir_read_name(tz))) { + gchar *path = g_strdup_printf("%s/%s/temp", path_tz, entry); + gchar *contents; + + if (g_file_get_contents(path, &contents, NULL, NULL)) { + int temperature; + + sscanf(contents, "%d", &temperature); + + add_sensor("Temperature", + entry, + "thermal", + temperature / 1000.0, + "\302\260C", + "therm"); + + g_free(contents); + } + } + + g_dir_close(tz); + } + } +} + +static void read_sensors_omnibook(void) { + const gchar *path_ob = "/proc/omnibook/temperature"; + gchar *contents; + + if (g_file_get_contents(path_ob, &contents, NULL, NULL)) { + int temperature; + + sscanf(contents, "CPU temperature: %d C", &temperature); + + add_sensor("Temperature", + "CPU", + "omnibook", + temperature, + "\302\260C", + "therm"); + + g_free(contents); + } +} + +static void read_sensors_hddtemp(void) { + Socket *s; + gchar buffer[1024]; + gint len = 0; + + if (!(s = sock_connect("127.0.0.1", 7634))) + return; + + while (!len) + len = sock_read(s, buffer, sizeof(buffer)); + sock_close(s); + + if (len > 2 && buffer[0] == '|' && buffer[1] == '/') { + gchar **disks; + int i; + + disks = g_strsplit(buffer, "||", 0); + for (i = 0; disks[i]; i++) { + gchar **fields = g_strsplit(disks[i] + 1, "|", 5); + + /* + * 0 -> /dev/hda + * 1 -> FUJITSU MHV2080AH + * 2 -> 41 + * 3 -> C + */ + const gchar *unit = strcmp(fields[3], "C") + ? "\302\260F" : "\302\260C"; + add_sensor("Drive Temperature", + fields[1], + "hddtemp", + atoi(fields[2]), + unit, + "therm"); + + g_strfreev(fields); + } + + g_strfreev(disks); + } +} + +static void read_sensors_udisks2(void) { + GSList *node; + GSList *temps; + udiskt *disk; + + temps = get_udisks2_temps(); + if (temps == NULL) + return; + + for (node = temps; node != NULL; node = node->next) { + disk = (udiskt *)node->data; + add_sensor("Drive Temperature", + disk->drive, + "udisks2", + disk->temperature, + "\302\260C", + "therm"); + udiskt_free(disk); + } + g_slist_free(temps); +} + +#if HAS_LIBSENSORS +static const struct libsensors_feature_type { + const char *type_name; + const char *icon; + const char *unit; + sensors_subfeature_type input; +} libsensors_feature_types[SENSORS_FEATURE_MAX] = { + [SENSORS_FEATURE_FAN] = {"Fan", "fan", "RPM", + SENSORS_SUBFEATURE_FAN_INPUT}, + [SENSORS_FEATURE_TEMP] = {"Temperature", "therm", "\302\260C", + SENSORS_SUBFEATURE_TEMP_INPUT}, + [SENSORS_FEATURE_POWER] = {"Power", "bolt", "W", + SENSORS_SUBFEATURE_POWER_INPUT}, + [SENSORS_FEATURE_CURR] = {"Current", "bolt", "A", + SENSORS_SUBFEATURE_CURR_INPUT}, + [SENSORS_FEATURE_IN] = {"Voltage", "bolt", "V", + SENSORS_SUBFEATURE_IN_INPUT}, + [SENSORS_FEATURE_VID] = {"CPU Voltage", "bolt", "V", + SENSORS_SUBFEATURE_VID}, +}; +static gboolean libsensors_initialized; + +static int read_sensors_libsensors(void) { + char chip_name_buf[512]; + const sensors_chip_name *name; + int chip_nr = 0; + int added_sensors = 0; + + if (!libsensors_initialized) + return 0; + + while ((name = sensors_get_detected_chips(NULL, &chip_nr))) { + const struct sensors_feature *feat; + int feat_nr = 0; + + sensors_snprintf_chip_name(chip_name_buf, 512, name); + + while ((feat = sensors_get_features(name, &feat_nr))) { + const struct libsensors_feature_type *feat_descr; + const struct sensors_subfeature *subfeat; + double value; + + feat_descr = &libsensors_feature_types[feat->type]; + if (!feat_descr->type_name) + continue; + + subfeat = sensors_get_subfeature(name, feat, feat_descr->input); + if (!subfeat) + continue; + + if (!sensors_get_value(name, subfeat->number, &value)) { + char *label = sensors_get_label(name, feat); + gchar *label_with_chip = g_strdup_printf("%s (%s)", label, chip_name_buf); + + add_sensor(feat_descr->type_name, + label_with_chip, + "libsensors", + value, + feat_descr->unit, + feat_descr->icon); + + free(label_with_chip); + free(label); + + added_sensors++; + } + } + } + + return added_sensors; +} +#else +static int read_sensors_libsensors(void) +{ + return 0; +} +#endif + +void scan_sensors_do(void) { + g_free(sensors); + g_free(sensor_icons); + g_free(last_group); + last_group = NULL; + sensors = g_strdup(""); + sensor_icons = g_strdup(""); + + g_free(lginterval); + lginterval = g_strdup(""); + + if (read_sensors_libsensors() == 0) { + read_sensors_hwmon(); + read_sensors_acpi(); + read_sensors_sys_thermal(); + read_sensors_omnibook(); + } + + read_sensors_windfarm(); + read_sensors_hddtemp(); + read_sensors_udisks2(); +} + +void sensor_init(void) { +#if HAS_LIBSENSORS + libsensors_initialized = sensors_init(NULL) == 0; +#endif + + sensor_labels = + g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + sensor_compute = g_hash_table_new(g_str_hash, g_str_equal); +} + +void sensor_shutdown(void) { +#if HAS_LIBSENSORS + sensors_cleanup(); +#endif + + g_hash_table_destroy(sensor_labels); + g_hash_table_destroy(sensor_compute); +} diff --git a/modules/devices/sh/processor.c b/modules/devices/sh/processor.c new file mode 100644 index 00000000..a5ce3e0a --- /dev/null +++ b/modules/devices/sh/processor.c @@ -0,0 +1,93 @@ +/* + * HardInfo - Displays System Information + * Copyright (C) 2003-2006 L. A. F. Pereira <l@tia.mat.br> + * + * 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 or later. + * + * 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 "devices.h" +#include "cpu_util.h" + +GSList * +processor_scan(void) +{ + Processor *processor; + FILE *cpuinfo; + gchar buffer[128]; + + cpuinfo = fopen(PROC_CPUINFO, "r"); + if (!cpuinfo) + return NULL; + + processor = g_new0(Processor, 1); + while (fgets(buffer, 128, cpuinfo)) { + gchar **tmp = g_strsplit(buffer, ":", 2); + + if (tmp[0] && tmp[1]) { + tmp[0] = g_strstrip(tmp[0]); + tmp[1] = g_strstrip(tmp[1]); + + get_str("machine", processor->vendor_id); + get_str("cpu type", processor->model_name); + get_str("cpu family", processor->family); + get_float("cpu clock", processor->cpu_mhz); + get_float("bus clock", processor->bus_mhz); + get_float("module clock", processor->mod_mhz); + get_float("bogomips", processor->bogomips); + } + g_strfreev(tmp); + } + + fclose(cpuinfo); + + STRIFNULL(processor->model_name, _("SuperH Processor")); + UNKIFNULL(processor->vendor_id); + + return g_slist_append(NULL, processor); +} + +gchar *processor_name(GSList * processors) { + return processor_name_default(processors); +} + +gchar *processor_describe(GSList * processors) { + return processor_describe_default(processors); +} + +gchar * +processor_get_info(GSList *processors) +{ + Processor *processor = (Processor *)processors->data; + + return g_strdup_printf("[%s]\n" + "%s=%s\n" /* cpu type */ + "%s=%s\n" /* machine */ + "%s=%s\n" /* family */ + "%s=%.2f %s\n" /* frequency */ + "%s=%.2f %s\n" /* bus frequency */ + "%s=%.2f %s\n" /* module frequency */ + "%s=%.2f\n" /* bogomips */ + "%s=%s\n", /* byte order */ + _("Processor"), + _("Name"), processor->model_name, + _("Machine"), processor->vendor_id, + _("Family"), processor->family, + _("Frequency"), processor->cpu_mhz, _("MHz"), + _("Bus Frequency"), processor->bus_mhz, _("MHz"), + _("Module Frequency"), processor->mod_mhz, _("MHz"), + _("BogoMips"), processor->bogomips, + _("Byte Order"), byte_order_str() + ); +} diff --git a/modules/devices/sparc/processor.c b/modules/devices/sparc/processor.c new file mode 100644 index 00000000..76f4b0e0 --- /dev/null +++ b/modules/devices/sparc/processor.c @@ -0,0 +1,80 @@ +/* + * HardInfo - Displays System Information + * Copyright (C) 2003-2006 L. A. F. Pereira <l@tia.mat.br> + * + * 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 or later. + * + * 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 "devices.h" +#include "cpu_util.h" + +GSList * +processor_scan(void) +{ + Processor *processor; + FILE *cpuinfo; + gchar buffer[128]; + + cpuinfo = fopen(PROC_CPUINFO, "r"); + if (!cpuinfo) + return NULL; + + processor = g_new0(Processor, 1); + while (fgets(buffer, 128, cpuinfo)) { + gchar **tmp = g_strsplit(buffer, ":", 2); + + if (tmp[0] && tmp[1]) { + tmp[0] = g_strstrip(tmp[0]); + tmp[1] = g_strstrip(tmp[1]); + + get_str("cpucaps", processor->cpucaps); + get_str("cpu", processor->model_name); + get_str("fpu", processor->has_fpu); + } + g_strfreev(tmp); + } + + fclose(cpuinfo); + + processor->cpu_mhz = 0.0f; + + return g_slist_append(NULL, processor); +} + +gchar *processor_name(GSList * processors) { + return processor_name_default(processors); +} + +gchar *processor_describe(GSList * processors) { + return processor_describe_default(processors); +} + +gchar * +processor_get_info(GSList *processors) +{ + Processor *processor = (Processor *)processors->data; + + return g_strdup_printf("[%s]\n" + "%s=%s\n" /* cpu */ + "%s=%s\n" /* fpu */ + "%s=%s\n" /* byte order */ + "%s=%s\n", /* caps */ + _("Processor"), + _("CPU"), processor->model_name, + _("FPU"), processor->has_fpu, + _("Byte Order"), byte_order_str(), + _("Capabilities"), processor->cpucaps + ); +} diff --git a/modules/devices/spd-decode.c b/modules/devices/spd-decode.c new file mode 100644 index 00000000..511504b2 --- /dev/null +++ b/modules/devices/spd-decode.c @@ -0,0 +1,1272 @@ +/* + * spd-decode.c, spd-vendors.c + * Copyright (c) 2010 L. A. F. Pereira + * modified by Ondrej Čerman (2019) + * modified by Burt P. (2019) + * + * Based on decode-dimms.pl + * Copyright 1998, 1999 Philip Edelbrock <phil@netroedge.com> + * modified by Christian Zuckschwerdt <zany@triq.net> + * modified by Burkart Lingner <burkart@bollchen.de> + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <ctype.h> +#include <math.h> +#include <stdio.h> +#include <sys/stat.h> + +#include "devices.h" +#include "hardinfo.h" + +gboolean spd_no_driver = FALSE; +gboolean spd_no_support = FALSE; +gboolean spd_ddr4_partial_data = FALSE; +int spd_ram_types = 0; /* bits using enum RamType */ + +typedef enum { + UNKNOWN = 0, + DIRECT_RAMBUS = 1, + RAMBUS = 2, + FPM_DRAM = 3, + EDO = 4, + PIPELINED_NIBBLE = 5, + SDR_SDRAM = 6, + MULTIPLEXED_ROM = 7, + DDR_SGRAM = 8, + DDR_SDRAM = 9, + DDR2_SDRAM = 10, + DDR3_SDRAM = 11, + DDR4_SDRAM = 12, + N_RAM_TYPES = 13 +} RamType; + +static const char *ram_types[] = {"Unknown", "Direct Rambus", "Rambus", "FPM DRAM", + "EDO", "Pipelined Nibble", "SDR SDRAM", "Multiplexed ROM", + "DDR SGRAM", "DDR SDRAM", "DDR2 SDRAM", "DDR3 SDRAM", + "DDR4 SDRAM"}; +#define GET_RAM_TYPE_STR(rt) (ram_types[(rt < N_RAM_TYPES) ? rt : 0]) + +#include "spd-vendors.c" + +struct dmi_mem_socket; +typedef struct { + unsigned char bytes[512]; + char dev[32]; /* %1d-%04d\0 */ + const char *spd_driver; + int spd_size; + + RamType type; + + int vendor_bank; + int vendor_index; + const char *vendor_str; + const Vendor *vendor; + + int dram_vendor_bank; + int dram_vendor_index; + const char *dram_vendor_str; + const Vendor *dram_vendor; + + char partno[32]; + const char *form_factor; + char type_detail[256]; + + dmi_mem_size size_MiB; + + int spd_rev_major; // bytes[1] >> 4 + int spd_rev_minor; // bytes[1] & 0xf + + int week, year; + + gboolean ddr4_no_ee1004; + + struct dmi_mem_socket *dmi_socket; + int match_score; +} spd_data; + +#define spd_data_new() g_new0(spd_data, 1) +void spd_data_free(spd_data *s) { g_free(s); } + +/* + * We consider that no data was written to this area of the SPD EEPROM if + * all bytes read 0x00 or all bytes read 0xff + */ +static int spd_written(unsigned char *bytes, int len) { + do { + if (*bytes == 0x00 || *bytes == 0xFF) return 1; + } while (--len && bytes++); + + return 0; +} + +static int parity(int value) { + value ^= value >> 16; + value ^= value >> 8; + value ^= value >> 4; + value &= 0xf; + + return (0x6996 >> value) & 1; +} + +static void decode_sdr_module_size(unsigned char *bytes, dmi_mem_size *size) { + unsigned short i, k = 0; + + i = (bytes[3] & 0x0f) + (bytes[4] & 0x0f) - 17; + if (bytes[5] <= 8 && bytes[17] <= 8) { k = bytes[5] * bytes[17]; } + + if (i > 0 && i <= 12 && k > 0) { + if (size) { *size = (dmi_mem_size)k * (unsigned short)(1 << i); } + } else { + if (size) { *size = -1; } + } +} + +static void decode_sdr_module_timings(unsigned char *bytes, float *tcl, float *trcd, float *trp, + float *tras) { + float cas[3], ctime; + int i, j; + + for (i = 0, j = 0; j < 7; j++) { + if (bytes[18] & 1 << j) { cas[i++] = j + 1; } + } + + ctime = ((bytes[9] >> 4) + (bytes[9] & 0xf)) * 0.1; + + if (trcd) { *trcd = ceil(bytes[29] / ctime); } + if (trp) { *trp = ceil(bytes[27] / ctime); } + if (tras) { *tras = ceil(bytes[30] / ctime); } + if (tcl) { *tcl = cas[i]; } +} + +static void decode_sdr_module_row_address_bits(unsigned char *bytes, char **bits) { + char *temp; + + switch (bytes[3]) { + case 0: temp = "Undefined"; break; + case 1: temp = "1/16"; break; + case 2: temp = "2/27"; break; + case 3: temp = "3/18"; break; + default: + /* printf("%d\n", bytes[3]); */ + temp = NULL; + } + + if (bits) { *bits = temp; } +} + +static void decode_sdr_module_col_address_bits(unsigned char *bytes, char **bits) { + char *temp; + + switch (bytes[4]) { + case 0: temp = "Undefined"; break; + case 1: temp = "1/16"; break; + case 2: temp = "2/17"; break; + case 3: temp = "3/18"; break; + default: + /*printf("%d\n", bytes[4]); */ + temp = NULL; + } + + if (bits) { *bits = temp; } +} + +static void decode_sdr_module_number_of_rows(unsigned char *bytes, int *rows) { + if (rows) { *rows = bytes[5]; } +} + +static void decode_sdr_module_data_with(unsigned char *bytes, int *width) { + if (width) { + if (bytes[7] > 1) { + *width = 0; + } else { + *width = (bytes[7] * 0xff) + bytes[6]; + } + } +} + +static void decode_sdr_module_interface_signal_levels(unsigned char *bytes, char **signal_levels) { + char *temp; + + switch (bytes[8]) { + case 0: temp = "5.0 Volt/TTL"; break; + case 1: temp = "LVTTL"; break; + case 2: temp = "HSTL 1.5"; break; + case 3: temp = "SSTL 3.3"; break; + case 4: temp = "SSTL 2.5"; break; + case 255: temp = "New Table"; break; + default: temp = NULL; + } + + if (signal_levels) { *signal_levels = temp; } +} + +static void decode_sdr_module_configuration_type(unsigned char *bytes, char **module_config_type) { + char *temp; + + switch (bytes[11]) { + case 0: temp = "No parity"; break; + case 1: temp = "Parity"; break; + case 2: temp = "ECC"; break; + default: temp = NULL; + } + + if (module_config_type) { *module_config_type = temp; } +} + +static void decode_sdr_module_refresh_type(unsigned char *bytes, char **refresh_type) { + char *temp; + + if (bytes[12] & 0x80) { + temp = "Self refreshing"; + } else { + temp = "Not self refreshing"; + } + + if (refresh_type) { *refresh_type = temp; } +} + +static void decode_sdr_module_refresh_rate(unsigned char *bytes, char **refresh_rate) { + char *temp; + + switch (bytes[12] & 0x7f) { + case 0: temp = "Normal (15.625us)"; break; + case 1: temp = "Reduced (3.9us)"; break; + case 2: temp = "Reduced (7.8us)"; break; + case 3: temp = "Extended (31.3us)"; break; + case 4: temp = "Extended (62.5us)"; break; + case 5: temp = "Extended (125us)"; break; + default: temp = NULL; + } + + if (refresh_rate) { *refresh_rate = temp; } +} + +static void decode_sdr_module_detail(unsigned char *bytes, char *type_detail) { + bytes = bytes; /* silence unused warning */ + if (type_detail) { + snprintf(type_detail, 255, "SDR"); + } +} + +static gchar *decode_sdr_sdram_extra(unsigned char *bytes) { + int rows, data_width; + float tcl, trcd, trp, tras; + char *row_address_bits, *col_address_bits, *signal_level; + char *module_config_type, *refresh_type, *refresh_rate; + + decode_sdr_module_timings(bytes, &tcl, &trcd, &trp, &tras); + decode_sdr_module_row_address_bits(bytes, &row_address_bits); + decode_sdr_module_col_address_bits(bytes, &col_address_bits); + decode_sdr_module_number_of_rows(bytes, &rows); + decode_sdr_module_data_with(bytes, &data_width); + decode_sdr_module_interface_signal_levels(bytes, &signal_level); + decode_sdr_module_configuration_type(bytes, &module_config_type); + decode_sdr_module_refresh_type(bytes, &refresh_type); + decode_sdr_module_refresh_rate(bytes, &refresh_rate); + + /* TODO: + - RAS to CAS delay + - Supported CAS latencies + - Supported CS latencies + - Supported WE latencies + - Cycle Time / Access time + - SDRAM module attributes + - SDRAM device attributes + - Row densities + - Other misc stuff + */ + + /* expected to continue an [SPD] section */ + return g_strdup_printf("%s=%s\n" + "%s=%s\n" + "%s=%d\n" + "%s=%d bits\n" + "%s=%s\n" + "%s=%s\n" + "%s=%s (%s)\n" + "[%s]\n" + "tCL=%.2f\n" + "tRCD=%.2f\n" + "tRP=%.2f\n" + "tRAS=%.2f\n", + _("Row address bits"), row_address_bits ? row_address_bits : _("(Unknown)"), + _("Column address bits"), col_address_bits ? col_address_bits : _("(Unknown)"), + _("Number of rows"), rows, + _("Data width"), data_width, + _("Interface signal levels"), signal_level ? signal_level : _("(Unknown)"), + _("Configuration type"), module_config_type ? module_config_type : _("(Unknown)"), + _("Refresh"), refresh_type, refresh_rate ? refresh_rate : _("Unknown"), + _("Timings"), tcl, trcd, trp, tras); +} + +static void decode_ddr_module_speed(unsigned char *bytes, float *ddrclk, int *pcclk) { + float temp, clk; + int tbits, pc; + + temp = (bytes[9] >> 4) + (bytes[9] & 0xf) * 0.1; + clk = 2 * (1000 / temp); + tbits = (bytes[7] * 256) + bytes[6]; + + if (bytes[11] == 2 || bytes[11] == 1) { tbits -= 8; } + + pc = clk * tbits / 8; + if (pc % 100 > 50) { pc += 100; } + pc -= pc % 100; + + if (ddrclk) *ddrclk = (int)clk; + + if (pcclk) *pcclk = pc; +} + +static void decode_ddr_module_size(unsigned char *bytes, dmi_mem_size *size) { + unsigned short i, k; + + i = (bytes[3] & 0x0f) + (bytes[4] & 0x0f) - 17; + k = (bytes[5] <= 8 && bytes[17] <= 8) ? bytes[5] * bytes[17] : 0; + + if (i > 0 && i <= 12 && k > 0) { + if (size) { *size = (dmi_mem_size)k * (unsigned short)(1 << i); } + } else { + if (size) { *size = -1; } + } +} + +static void decode_ddr_module_timings(unsigned char *bytes, float *tcl, float *trcd, float *trp, + float *tras) { + float ctime; + float highest_cas = 0; + int i; + + for (i = 0; i < 7; i++) { + if (bytes[18] & (1 << i)) { highest_cas = 1 + i * 0.5f; } + } + + ctime = (bytes[9] >> 4) + (bytes[9] & 0xf) * 0.1; + + if (trcd) { + *trcd = (bytes[29] >> 2) + ((bytes[29] & 3) * 0.25); + *trcd = ceil(*trcd / ctime); + } + + if (trp) { + *trp = (bytes[27] >> 2) + ((bytes[27] & 3) * 0.25); + *trp = ceil(*trp / ctime); + } + + if (tras) { + *tras = bytes[30]; + *tras = ceil(*tras / ctime); + } + + if (tcl) { *tcl = highest_cas; } +} + +static void decode_ddr_module_detail(unsigned char *bytes, char *type_detail) { + float ddr_clock; + int pc_speed; + if (type_detail) { + decode_ddr_module_speed(bytes, &ddr_clock, &pc_speed); + snprintf(type_detail, 255, "DDR-%.0f (PC-%d)", ddr_clock, pc_speed); + } +} + +static gchar *decode_ddr_sdram_extra(unsigned char *bytes) { + float tcl, trcd, trp, tras; + + decode_ddr_module_timings(bytes, &tcl, &trcd, &trp, &tras); + + return g_strdup_printf("[%s]\n" + "tCL=%.2f\n" + "tRCD=%.2f\n" + "tRP=%.2f\n" + "tRAS=%.2f\n", + _("Timings"), tcl, trcd, trp, tras); +} + +static float decode_ddr2_module_ctime(unsigned char byte) { + float ctime; + + ctime = (byte >> 4); + byte &= 0xf; + + if (byte <= 9) { + ctime += byte * 0.1; + } else if (byte == 10) { + ctime += 0.25; + } else if (byte == 11) { + ctime += 0.33; + } else if (byte == 12) { + ctime += 0.66; + } else if (byte == 13) { + ctime += 0.75; + } + + return ctime; +} + +static void decode_ddr2_module_speed(unsigned char *bytes, float *ddr_clock, int *pc2_speed) { + float ctime; + float ddrclk; + int tbits, pcclk; + ctime = decode_ddr2_module_ctime(bytes[9]); + ddrclk = 2 * (1000 / ctime); + + tbits = (bytes[7] * 256) + bytes[6]; + if (bytes[11] & 0x03) { tbits -= 8; } + + pcclk = ddrclk * tbits / 8; + pcclk -= pcclk % 100; + + if (ddr_clock) { *ddr_clock = (int)ddrclk; } + if (pc2_speed) { *pc2_speed = pcclk; } +} + +static void decode_ddr2_module_size(unsigned char *bytes, dmi_mem_size *size) { + unsigned short i, k; + + i = (bytes[3] & 0x0f) + (bytes[4] & 0x0f) - 17; + k = ((bytes[5] & 0x7) + 1) * bytes[17]; + + if (i > 0 && i <= 12 && k > 0) { + if (size) { *size = (dmi_mem_size)k * (unsigned short)(1 << i); } + } else { + if (size) { *size = 0; } + } +} + +static void decode_ddr2_module_type(unsigned char *bytes, const char **type) { + switch (bytes[20]) { + case 0x01: *type = "RDIMM (Registered DIMM)"; break; + case 0x02: *type = "UDIMM (Unbuffered DIMM)"; break; + case 0x04: *type = "SO-DIMM (Small Outline DIMM)"; break; + case 0x06: *type = "72b-SO-CDIMM (Small Outline Clocked DIMM, 72-bit data bus)"; break; + case 0x07: *type = "72b-SO-RDIMM (Small Outline Registered DIMM, 72-bit data bus)"; break; + case 0x08: *type = "Micro-DIMM"; break; + case 0x10: *type = "Mini-RDIMM (Mini Registered DIMM)"; break; + case 0x20: *type = "Mini-UDIMM (Mini Unbuffered DIMM)"; break; + default: *type = NULL; + } +} + +static void decode_ddr2_module_timings(float ctime, unsigned char *bytes, float *trcd, float *trp, float *tras) { + + if (trcd) { *trcd = ceil(((bytes[29] >> 2) + ((bytes[29] & 3) * 0.25)) / ctime); } + + if (trp) { *trp = ceil(((bytes[27] >> 2) + ((bytes[27] & 3) * 0.25)) / ctime); } + + if (tras) { *tras = ceil(bytes[30] / ctime); } +} + +static gboolean decode_ddr2_module_ctime_for_casx(int casx_minus, unsigned char *bytes, float *ctime, float *tcl){ + int highest_cas, i, bytei; + float ctimev = 0; + + switch (casx_minus){ + case 0: + bytei = 9; + break; + case 1: + bytei = 23; + break; + case 2: + bytei = 25; + break; + default: + return FALSE; + } + + for (i = 0; i < 7; i++) { + if (bytes[18] & (1 << i)) { highest_cas = i; } + } + + if ((bytes[18] & (1 << (highest_cas-casx_minus))) == 0) + return FALSE; + + ctimev = decode_ddr2_module_ctime(bytes[bytei]); + if (ctimev == 0) + return FALSE; + + if (tcl) { *tcl = highest_cas-casx_minus; } + if (ctime) { *ctime = ctimev; } + + return TRUE; +} + +static void decode_ddr2_module_detail(unsigned char *bytes, char *type_detail) { + float ddr_clock; + int pc2_speed; + if (type_detail) { + decode_ddr2_module_speed(bytes, &ddr_clock, &pc2_speed); + snprintf(type_detail, 255, "DDR2-%.0f (PC2-%d)", ddr_clock, pc2_speed); + } +} + +static gchar *decode_ddr2_sdram_extra(unsigned char *bytes) { + float trcd, trp, tras, ctime, tcl; + const char* voltage; + gchar *out; + int i; + + switch(bytes[8]){ + case 0x0: + voltage = "TTL/5 V tolerant"; + break; + case 0x1: + voltage = "LVTTL"; + break; + case 0x2: + voltage = "HSTL 1.5 V"; + break; + case 0x3: + voltage = "SSTL 3.3 V"; + break; + case 0x4: + voltage = "SSTL 2.5 V"; + break; + case 0x5: + voltage = "SSTL 1.8 V"; + break; + default: + voltage = _("(Unknown)"); + } + + /* expected to continue an [SPD] section */ + out = g_strdup_printf("%s=%s\n" + "[%s]\n", + _("Voltage"), voltage, + _("JEDEC Timings")); + + for (i = 0; i <= 2; i++) { + if (!decode_ddr2_module_ctime_for_casx(i, bytes, &ctime, &tcl)) + break; + decode_ddr2_module_timings(ctime, bytes, &trcd, &trp, &tras); + out = h_strdup_cprintf("DDR2-%d=%.0f-%.0f-%.0f-%.0f\n", + out, + (int)(2 * (1000 / ctime)), tcl, trcd, trp, tras); + } + + return out; +} + +static void decode_ddr3_module_speed(unsigned char *bytes, float *ddr_clock, int *pc3_speed) { + float ctime; + float ddrclk; + int tbits, pcclk; + float mtb = 0.125; + + if (bytes[10] == 1 && bytes[11] == 8) mtb = 0.125; + if (bytes[10] == 1 && bytes[11] == 15) mtb = 0.0625; + ctime = mtb * bytes[12]; + + ddrclk = 2 * (1000 / ctime); + + tbits = 64; + switch (bytes[8]) { + case 1: tbits = 16; break; + case 4: tbits = 32; break; + case 3: + case 0xb: tbits = 64; break; + } + + pcclk = ddrclk * tbits / 8; + pcclk -= pcclk % 100; + + if (ddr_clock) { *ddr_clock = (int)ddrclk; } + if (pc3_speed) { *pc3_speed = pcclk; } +} + +static void decode_ddr3_module_size(unsigned char *bytes, dmi_mem_size *size) { + unsigned int sdr_capacity = 256 << (bytes[4] & 0xF); + unsigned int sdr_width = 4 << (bytes[7] & 0x7); + unsigned int bus_width = 8 << (bytes[8] & 0x7); + unsigned int ranks = 1 + ((bytes[7] >> 3) & 0x7); + + *size = (dmi_mem_size)sdr_capacity / 8 * bus_width / sdr_width * ranks; +} + +static void decode_ddr3_module_timings(unsigned char *bytes, float *trcd, float *trp, float *tras, + float *tcl) { + float ctime; + float mtb = 0.125; + float taa; + + if (bytes[10] == 1 && bytes[11] == 8) mtb = 0.125; + if (bytes[10] == 1 && bytes[11] == 15) mtb = 0.0625; + ctime = mtb * bytes[12]; + taa = bytes[16] * mtb; + + if (trcd) { *trcd = bytes[18] * mtb; } + + if (trp) { *trp = bytes[20] * mtb; } + + if (tras) { *tras = (bytes[22] + (bytes[21] & 0xf)) * mtb; } + + if (tcl) { *tcl = ceil(taa/ctime); } +} + +static void decode_ddr3_module_type(unsigned char *bytes, const char **type) { + switch (bytes[3]) { + case 0x01: *type = "RDIMM (Registered Long DIMM)"; break; + case 0x02: *type = "UDIMM (Unbuffered Long DIMM)"; break; + case 0x03: *type = "SODIMM (Small Outline DIMM)"; break; + default: *type = NULL; + } +} + +static void decode_ddr3_module_detail(unsigned char *bytes, char *type_detail) { + float ddr_clock; + int pc3_speed; + if (type_detail) { + decode_ddr3_module_speed(bytes, &ddr_clock, &pc3_speed); + snprintf(type_detail, 255, "DDR3-%.0f (PC3-%d)", ddr_clock, pc3_speed); + } +} + +static gchar *decode_ddr3_sdram_extra(unsigned char *bytes) { + float trcd, trp, tras, tcl; + + decode_ddr3_module_timings(bytes, &trcd, &trp, &tras, &tcl); + + int ranks = 1 + ((bytes[7] >> 3) & 0x7); + int pins = 4 << (bytes[7] & 0x7); + int die_count = (bytes[33] >> 4) & 0x7; + int ts = !!(bytes[32] & 0x80); + + /* expected to continue an [SPD] section */ + return g_strdup_printf("%s=%d\n" + "%s=%d\n" + "%s=%d %s\n" + "%s=[%02x] %s\n" + "%s=%s%s%s\n" + "%s=" + "%s%s%s%s%s%s%s%s" + "%s%s%s%s%s%s%s\n" + "[%s]\n" + "tCL=%.0f\n" + "tRCD=%.3fns\n" + "tRP=%.3fns\n" + "tRAS=%.3fns\n", + _("Ranks"), ranks, + _("IO Pins per Chip"), pins, + _("Die count"), die_count, die_count ? "" : _("(Unspecified)"), + _("Thermal Sensor"), bytes[32], ts ? _("Present") : _("Not present"), + _("Supported Voltages"), + (bytes[6] & 4) ? "1.25V " : "", + (bytes[6] & 2) ? "1.35V " : "", + (bytes[6] & 1) ? "" : "1.5V", + _("Supported CAS Latencies"), + (bytes[15] & 0x40) ? "18 " : "", + (bytes[15] & 0x20) ? "17 " : "", + (bytes[15] & 0x10) ? "16 " : "", + (bytes[15] & 0x08) ? "15 " : "", + (bytes[15] & 0x04) ? "14 " : "", + (bytes[15] & 0x02) ? "13 " : "", + (bytes[15] & 0x01) ? "12 " : "", + (bytes[14] & 0x80) ? "11 " : "", + (bytes[14] & 0x40) ? "10 " : "", + (bytes[14] & 0x20) ? "9 " : "", + (bytes[14] & 0x10) ? "8 " : "", + (bytes[14] & 0x08) ? "7 " : "", + (bytes[14] & 0x04) ? "6 " : "", + (bytes[14] & 0x02) ? "5 " : "", + (bytes[14] & 0x01) ? "4" : "", + _("Timings"), tcl, trcd, trp, tras + ); +} + +static void decode_ddr3_part_number(unsigned char *bytes, char *part_number) { + int i; + if (part_number) { + for (i = 128; i <= 145; i++) *part_number++ = bytes[i]; + *part_number = '\0'; + } +} + +static void decode_ddr34_manufacturer(unsigned char count, unsigned char code, char **manufacturer, int *bank, int *index) { + if (!manufacturer) return; + + if (code == 0x00 || code == 0xFF) { + *manufacturer = NULL; + return; + } + + if (parity(count) != 1 || parity(code) != 1) { + *manufacturer = _("Invalid"); + return; + } + + *bank = count & 0x7f; + *index = code & 0x7f; + if (*bank >= VENDORS_BANKS) { + *manufacturer = NULL; + return; + } + + *manufacturer = (char *)JEDEC_MFG_STR(*bank, *index - 1); +} + +static void decode_ddr3_manufacturer(unsigned char *bytes, char **manufacturer, int *bank, int *index) { + decode_ddr34_manufacturer(bytes[117], bytes[118], (char **) manufacturer, bank, index); +} + +static void decode_module_manufacturer(unsigned char *bytes, char **manufacturer) { + char *out = "Unknown"; + unsigned char first; + int ai = 0; + int len = 8; + + if (!spd_written(bytes, 8)) { + out = "Undefined"; + goto end; + } + + do { ai++; } while ((--len && (*bytes++ == 0x7f))); + first = *--bytes; + + if (ai == 0) { + out = "Invalid"; + goto end; + } + + if (parity(first) != 1) { + out = "Invalid"; + goto end; + } + + out = (char*)JEDEC_MFG_STR(ai - 1, (first & 0x7f) - 1); + +end: + if (manufacturer) { *manufacturer = out; } +} + +static void decode_module_part_number(unsigned char *bytes, char *part_number) { + if (part_number) { + bytes += 8 + 64; + + while (*++bytes && *bytes >= 32 && *bytes < 127) { *part_number++ = *bytes; } + *part_number = '\0'; + } +} + +static char *print_spd_timings(int speed, float cas, float trcd, float trp, float tras, + float ctime) { + return g_strdup_printf("DDR4-%d=%.0f-%.0f-%.0f-%.0f\n", speed, cas, ceil(trcd / ctime - 0.025), + ceil(trp / ctime - 0.025), ceil(tras / ctime - 0.025)); +} + +static void decode_ddr4_module_type(unsigned char *bytes, const char **type) { + switch (bytes[3]) { + case 0x01: *type = "RDIMM (Registered DIMM)"; break; + case 0x02: *type = "UDIMM (Unbuffered DIMM)"; break; + case 0x03: *type = "SODIMM (Small Outline Unbuffered DIMM)"; break; + case 0x04: *type = "LRDIMM (Load-Reduced DIMM)"; break; + case 0x05: *type = "Mini-RDIMM (Mini Registered DIMM)"; break; + case 0x06: *type = "Mini-UDIMM (Mini Unbuffered DIMM)"; break; + case 0x08: *type = "72b-SO-RDIMM (Small Outline Registered DIMM, 72-bit data bus)"; break; + case 0x09: *type = "72b-SO-UDIMM (Small Outline Unbuffered DIMM, 72-bit data bus)"; break; + case 0x0c: *type = "16b-SO-UDIMM (Small Outline Unbuffered DIMM, 16-bit data bus)"; break; + case 0x0d: *type = "32b-SO-UDIMM (Small Outline Unbuffered DIMM, 32-bit data bus)"; break; + default: *type = NULL; + } +} + +static float ddr4_mtb_ftb_calc(unsigned char b1, signed char b2) { + float mtb = 0.125; + float ftb = 0.001; + return b1 * mtb + b2 * ftb; +} + +static void decode_ddr4_module_speed(unsigned char *bytes, float *ddr_clock, int *pc4_speed) { + float ctime; + float ddrclk; + int tbits, pcclk; + + ctime = ddr4_mtb_ftb_calc(bytes[18], bytes[125]); + ddrclk = 2 * (1000 / ctime); + tbits = 8 << (bytes[13] & 7); + + pcclk = ddrclk * tbits / 8; + pcclk -= pcclk % 100; + + if (ddr_clock) { *ddr_clock = (int)ddrclk; } + if (pc4_speed) { *pc4_speed = pcclk; } +} + +static void decode_ddr4_module_spd_timings(unsigned char *bytes, float speed, char **str) { + float ctime, ctime_max, pctime, taa, trcd, trp, tras; + int pcas, best_cas, base_cas, ci, i, j; + unsigned char cas_support[] = {bytes[20], bytes[21], bytes[22], bytes[23] & 0x1f}; + float possible_ctimes[] = {15 / 24.0, 15 / 22.0, 15 / 20.0, 15 / 18.0, + 15 / 16.0, 15 / 14.0, 15 / 12.0}; + + base_cas = bytes[23] & 0x80 ? 23 : 7; + + ctime = ddr4_mtb_ftb_calc(bytes[18], bytes[125]); + ctime_max = ddr4_mtb_ftb_calc(bytes[19], bytes[124]); + + taa = ddr4_mtb_ftb_calc(bytes[24], bytes[123]); + trcd = ddr4_mtb_ftb_calc(bytes[25], bytes[122]); + trp = ddr4_mtb_ftb_calc(bytes[26], bytes[121]); + tras = (((bytes[27] & 0x0f) << 8) + bytes[28]) * 0.125; + + *str = print_spd_timings((int)speed, ceil(taa / ctime - 0.025), trcd, trp, tras, ctime); + + for (ci = 0; ci < 7; ci++) { + best_cas = 0; + pctime = possible_ctimes[ci]; + + for (i = 3; i >= 0; i--) { + for (j = 7; j >= 0; j--) { + if ((cas_support[i] & (1 << j)) != 0) { + pcas = base_cas + 8 * i + j; + if (ceil(taa / pctime) - 0.025 <= pcas) { best_cas = pcas; } + } + } + } + + if (best_cas > 0 && pctime <= ctime_max && pctime >= ctime) { + *str = h_strdup_cprintf( + "%s\n", *str, + print_spd_timings((int)(2000.0 / pctime), best_cas, trcd, trp, tras, pctime)); + } + } +} + +static void decode_ddr4_module_size(unsigned char *bytes, dmi_mem_size *size) { + int sdrcap = 256 << (bytes[4] & 15); + int buswidth = 8 << (bytes[13] & 7); + int sdrwidth = 4 << (bytes[12] & 7); + int signal_loading = bytes[6] & 3; + int lranks_per_dimm = ((bytes[12] >> 3) & 7) + 1; + + if (signal_loading == 2) lranks_per_dimm *= ((bytes[6] >> 4) & 7) + 1; + + *size = (dmi_mem_size)sdrcap / 8 * buswidth / sdrwidth * lranks_per_dimm; +} + + +static void decode_ddr234_module_date(unsigned char weekb, unsigned char yearb, int *week, int *year) { + if (yearb == 0x0 || yearb == 0xff || + weekb == 0x0 || weekb == 0xff) { + return; + } + *week = (weekb>>4) & 0xf; + *week *= 10; + *week += weekb & 0xf; + *year = (yearb>>4) & 0xf; + *year *= 10; + *year += yearb & 0xf; + *year += 2000; +} + +static void decode_ddr2_module_date(unsigned char *bytes, int *week, int *year) { + decode_ddr234_module_date(bytes[94], bytes[93], week, year); +} + +static void decode_ddr3_module_date(unsigned char *bytes, int *week, int *year) { + decode_ddr234_module_date(bytes[121], bytes[120], week, year); +} + +static void decode_ddr4_module_date(unsigned char *bytes, int spd_size, int *week, int *year) { + if (spd_size < 324) + return; + decode_ddr234_module_date(bytes[324], bytes[323], week, year); +} + +static void decode_ddr3_dram_manufacturer(unsigned char *bytes, + char **manufacturer, int *bank, int *index) { + decode_ddr34_manufacturer(bytes[94], bytes[95], (char **) manufacturer, bank, index); +} + +static void decode_ddr4_dram_manufacturer(unsigned char *bytes, int spd_size, + char **manufacturer, int *bank, int *index) { + if (spd_size < 351) { + *manufacturer = NULL; + return; + } + + decode_ddr34_manufacturer(bytes[350], bytes[351], (char **) manufacturer, bank, index); +} + +static void detect_ddr4_xmp(unsigned char *bytes, int spd_size, int *majv, int *minv) { + if (spd_size < 387) + return; + + *majv = 0; *minv = 0; + + if (bytes[384] != 0x0c || bytes[385] != 0x4a) // XMP magic number + return; + + if (majv) + *majv = bytes[387] >> 4; + if (minv) + *minv = bytes[387] & 0xf; +} + +static void decode_ddr4_xmp(unsigned char *bytes, int spd_size, char **str) { + float ctime; + float ddrclk, taa, trcd, trp, tras; + + if (spd_size < 405) + return; + + ctime = ddr4_mtb_ftb_calc(bytes[396], bytes[431]); + ddrclk = 2 * (1000 / ctime); + taa = ddr4_mtb_ftb_calc(bytes[401], bytes[430]); + trcd = ddr4_mtb_ftb_calc(bytes[402], bytes[429]); + trp = ddr4_mtb_ftb_calc(bytes[403], bytes[428]); + tras = (((bytes[404] & 0x0f) << 8) + bytes[405]) * 0.125; + + *str = g_strdup_printf("[%s]\n" + "%s=DDR4 %d MHz\n" + "%s=%d.%d V\n" + "[%s]\n" + "%s", + _("XMP Profile"), + _("Speed"), (int) ddrclk, + _("Voltage"), bytes[393] >> 7, bytes[393] & 0x7f, + _("XMP Timings"), + print_spd_timings((int) ddrclk, ceil(taa/ctime - 0.025), trcd, trp, tras, ctime)); +} + +static void decode_ddr4_module_detail(unsigned char *bytes, char *type_detail) { + float ddr_clock; + int pc4_speed; + if (type_detail) { + decode_ddr4_module_speed(bytes, &ddr_clock, &pc4_speed); + snprintf(type_detail, 255, "DDR4-%.0f (PC4-%d)", ddr_clock, pc4_speed); + } +} + +static gchar *decode_ddr4_sdram_extra(unsigned char *bytes, int spd_size) { + float ddr_clock; + int pc4_speed, xmp_majv = -1, xmp_minv = -1; + char *speed_timings = NULL, *xmp_profile = NULL, *xmp = NULL, *manf_date = NULL; + static gchar *out; + + decode_ddr4_module_speed(bytes, &ddr_clock, &pc4_speed); + decode_ddr4_module_spd_timings(bytes, ddr_clock, &speed_timings); + detect_ddr4_xmp(bytes, spd_size, &xmp_majv, &xmp_minv); + + if (xmp_majv == -1 && xmp_minv == -1) { + xmp = NULL; + } + else if (xmp_majv <= 0 && xmp_minv <= 0) { + xmp = g_strdup(_("No")); + } + else { + xmp = g_strdup_printf("%s (revision %d.%d)", _("Yes"), xmp_majv, xmp_minv); + if (xmp_majv == 2 && xmp_minv == 0) + decode_ddr4_xmp(bytes, spd_size, &xmp_profile); + } + + /* expected to continue an [SPD] section */ + out = g_strdup_printf("%s=%s\n" + "%s=%s\n" + "[%s]\n" + "%s\n" + "%s", + _("Voltage"), bytes[11] & 0x01 ? "1.2 V": _("(Unknown)"), + _("XMP"), xmp ? xmp : _("(Unknown)"), + _("JEDEC Timings"), speed_timings, + xmp_profile ? xmp_profile: ""); + + g_free(speed_timings); + g_free(manf_date); + g_free(xmp); + g_free(xmp_profile); + + return out; +} + +static void decode_ddr4_part_number(unsigned char *bytes, int spd_size, char *part_number) { + int i; + if (!part_number) return; + + if (spd_size < 348) { + *part_number++ = '\0'; + return; + } + + for (i = 329; i <= 348; i++) + *part_number++ = bytes[i]; + *part_number = '\0'; +} + +static void decode_ddr4_module_manufacturer(unsigned char *bytes, int spd_size, + char **manufacturer, int *bank, int *index) { + if (spd_size < 321) { + *manufacturer = NULL; + return; + } + + decode_ddr34_manufacturer(bytes[320], bytes[321], manufacturer, bank, index); +} + +static int decode_ram_type(unsigned char *bytes) { + if (bytes[0] < 4) { + switch (bytes[2]) { + case 1: return DIRECT_RAMBUS; + case 17: return RAMBUS; + } + } else { + switch (bytes[2]) { + case 1: return FPM_DRAM; + case 2: return EDO; + case 3: return PIPELINED_NIBBLE; + case 4: return SDR_SDRAM; + case 5: return MULTIPLEXED_ROM; + case 6: return DDR_SGRAM; + case 7: return DDR_SDRAM; + case 8: return DDR2_SDRAM; + case 11: return DDR3_SDRAM; + case 12: return DDR4_SDRAM; + } + } + + return UNKNOWN; +} + +static int read_spd(char *spd_path, int offset, size_t size, int use_sysfs, + unsigned char *bytes_out) { + int data_size = 0; + if (use_sysfs) { + FILE *spd; + gchar *temp_path; + + temp_path = g_strdup_printf("%s/eeprom", spd_path); + if ((spd = fopen(temp_path, "rb"))) { + fseek(spd, offset, SEEK_SET); + data_size = fread(bytes_out, 1, size, spd); + fclose(spd); + } + + g_free(temp_path); + } else { + int i; + + for (i = 0; i <= 3; i++) { + FILE *spd; + char *temp_path; + + temp_path = g_strdup_printf("%s/%02x", spd_path, offset + i * 16); + if ((spd = fopen(temp_path, "rb"))) { + data_size += fread(bytes_out + i * 16, 1, 16, spd); + fclose(spd); + } + + g_free(temp_path); + } + } + + return data_size; +} + +static GSList *decode_dimms2(GSList *eeprom_list, const gchar *driver, gboolean use_sysfs, int max_size) { + GSList *eeprom, *dimm_list = NULL; + int count = 0; + int spd_size = 0; + unsigned char bytes[512] = {0}; + spd_data *s = NULL; + + for (eeprom = eeprom_list; eeprom; eeprom = eeprom->next, count++) { + gchar *spd_path = (gchar*)eeprom->data; + s = NULL; + + RamType ram_type; + + memset(bytes, 0, 512); /* clear */ + spd_size = read_spd(spd_path, 0, max_size, use_sysfs, bytes); + ram_type = decode_ram_type(bytes); + + switch (ram_type) { + case SDR_SDRAM: + s = spd_data_new(); + memcpy(s->bytes, bytes, 512); + decode_module_part_number(bytes, s->partno); + decode_module_manufacturer(bytes + 64, (char**)&s->vendor_str); + decode_sdr_module_size(bytes, &s->size_MiB); + decode_sdr_module_detail(bytes, s->type_detail); + break; + case DDR_SDRAM: + s = spd_data_new(); + memcpy(s->bytes, bytes, 512); + decode_module_part_number(bytes, s->partno); + decode_module_manufacturer(bytes + 64, (char**)&s->vendor_str); + decode_ddr_module_size(bytes, &s->size_MiB); + decode_ddr_module_detail(bytes, s->type_detail); + break; + case DDR2_SDRAM: + s = spd_data_new(); + memcpy(s->bytes, bytes, 512); + decode_module_part_number(bytes, s->partno); + decode_module_manufacturer(bytes + 64, (char**)&s->vendor_str); + decode_ddr2_module_size(bytes, &s->size_MiB); + decode_ddr2_module_detail(bytes, s->type_detail); + decode_ddr2_module_type(bytes, &s->form_factor); + decode_ddr2_module_date(bytes, &s->week, &s->year); + break; + case DDR3_SDRAM: + s = spd_data_new(); + memcpy(s->bytes, bytes, 512); + decode_ddr3_part_number(bytes, s->partno); + decode_ddr3_manufacturer(bytes, (char**)&s->vendor_str, + &s->vendor_bank, &s->vendor_index); + decode_ddr3_dram_manufacturer(bytes, (char**)&s->dram_vendor_str, + &s->dram_vendor_bank, &s->dram_vendor_index); + decode_ddr3_module_size(bytes, &s->size_MiB); + decode_ddr3_module_type(bytes, &s->form_factor); + decode_ddr3_module_detail(bytes, s->type_detail); + decode_ddr3_module_date(bytes, &s->week, &s->year); + break; + case DDR4_SDRAM: + s = spd_data_new(); + memcpy(s->bytes, bytes, 512); + decode_ddr4_part_number(bytes, spd_size, s->partno); + decode_ddr4_module_manufacturer(bytes, spd_size, (char**)&s->vendor_str, + &s->vendor_bank, &s->vendor_index); + decode_ddr4_dram_manufacturer(bytes, spd_size, (char**)&s->dram_vendor_str, + &s->dram_vendor_bank, &s->dram_vendor_index); + decode_ddr4_module_size(bytes, &s->size_MiB); + decode_ddr4_module_type(bytes, &s->form_factor); + decode_ddr4_module_detail(bytes, s->type_detail); + decode_ddr4_module_date(bytes, spd_size, &s->week, &s->year); + s->ddr4_no_ee1004 = s->ddr4_no_ee1004 || (spd_size < 512); + spd_ddr4_partial_data = spd_ddr4_partial_data || s->ddr4_no_ee1004; + break; + case UNKNOWN: + break; + default: + DEBUG("Unsupported EEPROM type: %s for %s\n", GET_RAM_TYPE_STR(ram_type), spd_path); continue; + if (ram_type) { + /* some kind of RAM that we can't decode, but exists */ + s = spd_data_new(); + memcpy(s->bytes, bytes, 512); + } + } + + if (s) { + strncpy(s->dev, g_basename(spd_path), 31); + s->spd_driver = driver; + s->spd_size = spd_size; + s->type = ram_type; + spd_ram_types |= (1 << (ram_type-1)); + switch (ram_type) { + case SDR_SDRAM: + case DDR_SDRAM: + case DDR2_SDRAM: + s->spd_rev_major = bytes[62] >> 4; + s->spd_rev_minor = bytes[62] & 0xf; + break; + case DDR3_SDRAM: + case DDR4_SDRAM: + s->spd_rev_major = bytes[1] >> 4; + s->spd_rev_minor = bytes[1] & 0xf; + break; + } + s->vendor = vendor_match(s->vendor_str, NULL); + s->dram_vendor = vendor_match(s->dram_vendor_str, NULL); + dimm_list = g_slist_append(dimm_list, s); + } + } + + return dimm_list; +} + +struct SpdDriver { + const char *driver; + const char *dir_path; + const gint max_size; + const gboolean use_sysfs; + const char *spd_name; +}; + +static const struct SpdDriver spd_drivers[] = { + { "ee1004", "/sys/bus/i2c/drivers/ee1004", 512, TRUE, "ee1004"}, + { "at24", "/sys/bus/i2c/drivers/at24" , 256, TRUE, "spd"}, + { "eeprom", "/sys/bus/i2c/drivers/eeprom", 256, TRUE, "eeprom"}, + { "eeprom-proc", "/proc/sys/dev/sensors" , 256, FALSE, NULL}, + { NULL } +}; + +GSList *spd_scan() { + GDir *dir = NULL; + GSList *eeprom_list = NULL, *dimm_list = NULL; + gchar *dimm_list_entry, *dir_entry, *name_file, *name; + const struct SpdDriver *driver; + gboolean driver_found = FALSE; + gboolean is_spd = FALSE; + + spd_ddr4_partial_data = FALSE; + spd_no_driver = FALSE; + spd_no_support = FALSE; + + for (driver = spd_drivers; driver->dir_path; driver++) { + if (g_file_test(driver->dir_path, G_FILE_TEST_EXISTS)) { + dir = g_dir_open(driver->dir_path, 0, NULL); + if (!dir) continue; + + driver_found = TRUE; + while ((dir_entry = (char *)g_dir_read_name(dir))) { + is_spd = FALSE; + + if (driver->use_sysfs) { + name_file = NULL; + name = NULL; + + if (isdigit(dir_entry[0])) { + name_file = g_build_filename(driver->dir_path, dir_entry, "name", NULL); + g_file_get_contents(name_file, &name, NULL, NULL); + + is_spd = g_strcmp0(name, driver->spd_name); + + g_free(name_file); + g_free(name); + } + } + else { + is_spd = g_str_has_prefix(dir_entry, "eeprom-"); + } + + if (is_spd){ + dimm_list_entry = g_strdup_printf("%s/%s", driver->dir_path, dir_entry); + eeprom_list = g_slist_prepend(eeprom_list, dimm_list_entry); + } + } + + g_dir_close(dir); + + if (eeprom_list) { + dimm_list = decode_dimms2(eeprom_list, driver->driver, driver->use_sysfs, driver->max_size); + g_slist_free(eeprom_list); + eeprom_list = NULL; + } + if (dimm_list) break; + } + } + + if (!driver_found) { + if (!g_file_test("/sys/module/eeprom", G_FILE_TEST_EXISTS) && + !g_file_test("/sys/module/at24", G_FILE_TEST_EXISTS)) { + spd_no_driver = TRUE; /* trigger hinote for no eeprom driver */ + } else { + spd_no_support = TRUE; /* trigger hinote for unsupported system */ + } + } + + return dimm_list; +} diff --git a/modules/devices/spd-vendors.c b/modules/devices/spd-vendors.c new file mode 100644 index 00000000..80b2a6a4 --- /dev/null +++ b/modules/devices/spd-vendors.c @@ -0,0 +1,292 @@ +/* + * spd-decode.c, spd-vendors.c + * Copyright (c) 2010 L. A. F. Pereira + * modified by Ondrej Čerman (2019) + * modified by Burt P. (2019) + * + * Based on decode-dimms.pl + * Copyright 1998, 1999 Philip Edelbrock <phil@netroedge.com> + * modified by Christian Zuckschwerdt <zany@triq.net> + * modified by Burkart Lingner <burkart@bollchen.de> + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* from decode-dimms, in the i2c-tools package: + * https://github.com/angeloc/i2c-tools/blob/master/eeprom/decode-dimms + * + * STANDARD MANUFACTURERS IDENTIFICATION CODEs + * as defined in JEP106 + * + * As of: 28 June 2019 + * + */ + +#define VENDORS_BANKS 8 +#define VENDORS_ITEMS 128 +#define JEDEC_MFG_STR(b,i) ( (b >= 0 && b < VENDORS_BANKS && i < VENDORS_ITEMS) ? vendors[(b)][(i)] : NULL ) +static const char* vendors[VENDORS_BANKS][VENDORS_ITEMS] = +{ +{"AMD", "AMI", "Fairchild", "Fujitsu", + "GTE", "Harris", "Hitachi", "Inmos", + "Intel", "I.T.T.", "Intersil", "Monolithic Memories", + "Mostek", "Freescale (former Motorola)", "National", "NEC", + "RCA", "Raytheon", "Conexant (Rockwell)", "Seeq", + "NXP (former Signetics, Philips Semi.)", "Synertek", "Texas Instruments", "Toshiba", + "Xicor", "Zilog", "Eurotechnique", "Mitsubishi", + "Lucent (AT&T)", "Exel", "Atmel", "SGS/Thomson", + "Lattice Semi.", "NCR", "Wafer Scale Integration", "IBM", + "Tristar", "Visic", "Intl. CMOS Technology", "SSSI", + "MicrochipTechnology", "Ricoh Ltd.", "VLSI", "Micron Technology", + "SK Hynix (former Hyundai Electronics)", "OKI Semiconductor", "ACTEL", "Sharp", + "Catalyst", "Panasonic", "IDT", "Cypress", + "DEC", "LSI Logic", "Zarlink (former Plessey)", "UTMC", + "Thinking Machine", "Thomson CSF", "Integrated CMOS (Vertex)", "Honeywell", + "Tektronix", "Oracle Corporation (former Sun Microsystems)", "Silicon Storage Technology", "ProMos/Mosel Vitelic", + "Infineon (former Siemens)", "Macronix", "Xerox", "Plus Logic", + "SunDisk", "Elan Circuit Tech.", "European Silicon Str.", "Apple Computer", + "Xilinx", "Compaq", "Protocol Engines", "SCI", + "Seiko Instruments", "Samsung", "I3 Design System", "Klic", + "Crosspoint Solutions", "Alliance Semiconductor", "Tandem", "Hewlett-Packard", + "Integrated Silicon Solutions", "Brooktree", "New Media", "MHS Electronic", + "Performance Semi.", "Winbond Electronic", "Kawasaki Steel", "Bright Micro", + "TECMAR", "Exar", "PCMCIA", "LG Semi (former Goldstar)", + "Northern Telecom", "Sanyo", "Array Microsystems", "Crystal Semiconductor", + "Analog Devices", "PMC-Sierra", "Asparix", "Convex Computer", + "Quality Semiconductor", "Nimbus Technology", "Transwitch", "Micronas (ITT Intermetall)", + "Cannon", "Altera", "NEXCOM", "QUALCOMM", + "Sony", "Cray Research", "AMS(Austria Micro)", "Vitesse", + "Aster Electronics", "Bay Networks (Synoptic)", "Zentrum or ZMD", "TRW", + "Thesys", "Solbourne Computer", "Allied-Signal", "Dialog", + "Media Vision", "Numonyx Corporation (former Level One Communication)"}, +{"Cirrus Logic", "National Instruments", "ILC Data Device", "Alcatel Mietec", + "Micro Linear", "Univ. of NC", "JTAG Technologies", "BAE Systems", + "Nchip", "Galileo Tech", "Bestlink Systems", "Graychip", + "GENNUM", "VideoLogic", "Robert Bosch", "Chip Express", + "DATARAM", "United Microelec Corp.", "TCSI", "Smart Modular", + "Hughes Aircraft", "Lanstar Semiconductor", "Qlogic", "Kingston", + "Music Semi", "Ericsson Components", "SpaSE", "Eon Silicon Devices", + "Programmable Micro Corp", "DoD", "Integ. Memories Tech.", "Corollary Inc.", + "Dallas Semiconductor", "Omnivision", "EIV(Switzerland)", "Novatel Wireless", + "Zarlink (former Mitel)", "Clearpoint", "Cabletron", "STEC (former Silicon Technology)", + "Vanguard", "Hagiwara Sys-Com", "Vantis", "Celestica", + "Century", "Hal Computers", "Rohm Company Ltd.", "Juniper Networks", + "Libit Signal Processing", "Mushkin Enhanced Memory", "Tundra Semiconductor", "Adaptec Inc.", + "LightSpeed Semi.", "ZSP Corp.", "AMIC Technology", "Adobe Systems", + "Dynachip", "PNY Electronics", "Newport Digital", "MMC Networks", + "T Square", "Seiko Epson", "Broadcom", "Viking Components", + "V3 Semiconductor", "Flextronics (former Orbit)", "Suwa Electronics", "Transmeta", + "Micron CMS", "American Computer & Digital Components Inc", "Enhance 3000 Inc", "Tower Semiconductor", + "CPU Design", "Price Point", "Maxim Integrated Product", "Tellabs", + "Centaur Technology", "Unigen Corporation", "Transcend Information", "Memory Card Technology", + "CKD Corporation Ltd.", "Capital Instruments, Inc.", "Aica Kogyo, Ltd.", "Linvex Technology", + "MSC Vertriebs GmbH", "AKM Company, Ltd.", "Dynamem, Inc.", "NERA ASA", + "GSI Technology", "Dane-Elec (C Memory)", "Acorn Computers", "Lara Technology", + "Oak Technology, Inc.", "Itec Memory", "Tanisys Technology", "Truevision", + "Wintec Industries", "Super PC Memory", "MGV Memory", "Galvantech", + "Gadzoox Nteworks", "Multi Dimensional Cons.", "GateField", "Integrated Memory System", + "Triscend", "XaQti", "Goldenram", "Clear Logic", + "Cimaron Communications", "Nippon Steel Semi. Corp.", "Advantage Memory", "AMCC", + "LeCroy", "Yamaha Corporation", "Digital Microwave", "NetLogic Microsystems", + "MIMOS Semiconductor", "Advanced Fibre", "BF Goodrich Data.", "Epigram", + "Acbel Polytech Inc.", "Apacer Technology", "Admor Memory", "FOXCONN", + "Quadratics Superconductor", "3COM"}, +{"Camintonn Corporation", "ISOA Incorporated", "Agate Semiconductor", "ADMtek Incorporated", + "HYPERTEC", "Adhoc Technologies", "MOSAID Technologies", "Ardent Technologies", + "Switchcore", "Cisco Systems, Inc.", "Allayer Technologies", "WorkX AG (Wichman)", + "Oasis Semiconductor", "Novanet Semiconductor", "E-M Solutions", "Power General", + "Advanced Hardware Arch.", "Inova Semiconductors GmbH", "Telocity", "Delkin Devices", + "Symagery Microsystems", "C-Port Corporation", "SiberCore Technologies", "Southland Microsystems", + "Malleable Technologies", "Kendin Communications", "Great Technology Microcomputer", "Sanmina Corporation", + "HADCO Corporation", "Corsair", "Actrans System Inc.", "ALPHA Technologies", + "Silicon Laboratories, Inc. (Cygnal)", "Artesyn Technologies", "Align Manufacturing", "Peregrine Semiconductor", + "Chameleon Systems", "Aplus Flash Technology", "MIPS Technologies", "Chrysalis ITS", + "ADTEC Corporation", "Kentron Technologies", "Win Technologies", "Tachyon Semiconductor (former ASIC Designs Inc.)", + "Extreme Packet Devices", "RF Micro Devices", "Siemens AG", "Sarnoff Corporation", + "Itautec SA (former Itautec Philco SA)", "Radiata Inc.", "Benchmark Elect. (AVEX)", "Legend", + "SpecTek Incorporated", "Hi/fn", "Enikia Incorporated", "SwitchOn Networks", + "AANetcom Incorporated", "Micro Memory Bank", "ESS Technology", "Virata Corporation", + "Excess Bandwidth", "West Bay Semiconductor", "DSP Group", "Newport Communications", + "Chip2Chip Incorporated", "Phobos Corporation", "Intellitech Corporation", "Nordic VLSI ASA", + "Ishoni Networks", "Silicon Spice", "Alchemy Semiconductor", "Agilent Technologies", + "Centillium Communications", "W.L. Gore", "HanBit Electronics", "GlobeSpan", + "Element 14", "Pycon", "Saifun Semiconductors", "Sibyte, Incorporated", + "MetaLink Technologies", "Feiya Technology", "I & C Technology", "Shikatronics", + "Elektrobit", "Megic", "Com-Tier", "Malaysia Micro Solutions", + "Hyperchip", "Gemstone Communications", "Anadigm (former Anadyne)", "3ParData", + "Mellanox Technologies", "Tenx Technologies", "Helix AG", "Domosys", + "Skyup Technology", "HiNT Corporation", "Chiaro", "MDT Technologies GmbH (former MCI Computer GMBH)", + "Exbit Technology A/S", "Integrated Technology Express", "AVED Memory", "Legerity", + "Jasmine Networks", "Caspian Networks", "nCUBE", "Silicon Access Networks", + "FDK Corporation", "High Bandwidth Access", "MultiLink Technology", "BRECIS", + "World Wide Packets", "APW", "Chicory Systems", "Xstream Logic", + "Fast-Chip", "Zucotto Wireless", "Realchip", "Galaxy Power", + "eSilicon", "Morphics Technology", "Accelerant Networks", "Silicon Wave", + "SandCraft", "Elpida"}, +{"Solectron", "Optosys Technologies", "Buffalo (former Melco)", "TriMedia Technologies", + "Cyan Technologies", "Global Locate", "Optillion", "Terago Communications", + "Ikanos Communications", "Princeton Technology", "Nanya Technology", "Elite Flash Storage", + "Mysticom", "LightSand Communications", "ATI Technologies", "Agere Systems", + "NeoMagic", "AuroraNetics", "Golden Empire", "Mushkin", + "Tioga Technologies", "Netlist", "TeraLogic", "Cicada Semiconductor", + "Centon Electronics", "Tyco Electronics", "Magis Works", "Zettacom", + "Cogency Semiconductor", "Chipcon AS", "Aspex Technology", "F5 Networks", + "Programmable Silicon Solutions", "ChipWrights", "Acorn Networks", "Quicklogic", + "Kingmax Semiconductor", "BOPS", "Flasys", "BitBlitz Communications", + "eMemory Technology", "Procket Networks", "Purple Ray", "Trebia Networks", + "Delta Electronics", "Onex Communications", "Ample Communications", "Memory Experts Intl", + "Astute Networks", "Azanda Network Devices", "Dibcom", "Tekmos", + "API NetWorks", "Bay Microsystems", "Firecron Ltd", "Resonext Communications", + "Tachys Technologies", "Equator Technology", "Concept Computer", "SILCOM", + "3Dlabs", "c't Magazine", "Sanera Systems", "Silicon Packets", + "Viasystems Group", "Simtek", "Semicon Devices Singapore", "Satron Handelsges", + "Improv Systems", "INDUSYS GmbH", "Corrent", "Infrant Technologies", + "Ritek Corp", "empowerTel Networks", "Hypertec", "Cavium Networks", + "PLX Technology", "Massana Design", "Intrinsity", "Valence Semiconductor", + "Terawave Communications", "IceFyre Semiconductor", "Primarion", "Picochip Designs Ltd", + "Silverback Systems", "Jade Star Technologies", "Pijnenburg Securealink", + "takeMS - Ultron AG (former Memorysolution GmbH)", "Cambridge Silicon Radio", + "Swissbit", "Nazomi Communications", "eWave System", + "Rockwell Collins", "Picocel Co., Ltd.", "Alphamosaic Ltd", "Sandburst", + "SiCon Video", "NanoAmp Solutions", "Ericsson Technology", "PrairieComm", + "Mitac International", "Layer N Networks", "MtekVision", "Allegro Networks", + "Marvell Semiconductors", "Netergy Microelectronic", "NVIDIA", "Internet Machines", + "Peak Electronics", "Litchfield Communication", "Accton Technology", "Teradiant Networks", + "Scaleo Chip (former Europe Technologies)", "Cortina Systems", "RAM Components", "Raqia Networks", + "ClearSpeed", "Matsushita Battery", "Xelerated", "SimpleTech", + "Utron Technology", "Astec International", "AVM gmbH", "Redux Communications", + "Dot Hill Systems", "TeraChip"}, +{"T-RAM Incorporated", "Innovics Wireless", "Teknovus", "KeyEye Communications", + "Runcom Technologies", "RedSwitch", "Dotcast", "Silicon Mountain Memory", + "Signia Technologies", "Pixim", "Galazar Networks", "White Electronic Designs", + "Patriot Scientific", "Neoaxiom Corporation", "3Y Power Technology", "Scaleo Chip (former Europe Technologies)", + "Potentia Power Systems", "C-guys Incorporated", "Digital Communications Technology Incorporated", "Silicon-Based Technology", + "Fulcrum Microsystems", "Positivo Informatica Ltd", "XIOtech Corporation", "PortalPlayer", + "Zhiying Software", "Parker Vision, Inc. (former Direct2Data)", "Phonex Broadband", "Skyworks Solutions", + "Entropic Communications", "Pacific Force Technology", "Zensys A/S", "Legend Silicon Corp.", + "sci-worx GmbH", "SMSC (former Oasis Silicon Systems)", "Renesas Electronics (former Renesas Technology)", "Raza Microelectronics", + "Phyworks", "MediaTek", "Non-cents Productions", "US Modular", + "Wintegra Ltd", "Mathstar", "StarCore", "Oplus Technologies", + "Mindspeed", "Just Young Computer", "Radia Communications", "OCZ", + "Emuzed", "LOGIC Devices", "Inphi Corporation", "Quake Technologies", + "Vixel", "SolusTek", "Kongsberg Maritime", "Faraday Technology", + "Altium Ltd.", "Insyte", "ARM Ltd.", "DigiVision", + "Vativ Technologies", "Endicott Interconnect Technologies", "Pericom", "Bandspeed", + "LeWiz Communications", "CPU Technology", "Ramaxel Technology", "DSP Group", + "Axis Communications", "Legacy Electronics", "Chrontel", "Powerchip Semiconductor", + "MobilEye Technologies", "Excel Semiconductor", "A-DATA Technology", "VirtualDigm", + "G.Skill Intl", "Quanta Computer", "Yield Microelectronics", "Afa Technologies", + "KINGBOX Technology Co. Ltd.", "Ceva", "iStor Networks", "Advance Modules", + "Microsoft", "Open-Silicon", "Goal Semiconductor", "ARC International", + "Simmtec", "Metanoia", "Key Stream", "Lowrance Electronics", + "Adimos", "SiGe Semiconductor", "Fodus Communications", "Credence Systems Corp.", + "Genesis Microchip Inc.", "Vihana, Inc.", "WIS Technologies", "GateChange Technologies", + "High Density Devices AS", "Synopsys", "Gigaram", "Enigma Semiconductor Inc.", + "Century Micro Inc.", "Icera Semiconductor", "Mediaworks Integrated Systems", "O'Neil Product Development", + "Supreme Top Technology Ltd.", "MicroDisplay Corporation", "Team Group Inc.", "Sinett Corporation", + "Toshiba Corporation", "Tensilica", "SiRF Technology", "Bacoc Inc.", + "SMaL Camera Technologies", "Thomson SC", "Airgo Networks", "Wisair Ltd.", + "SigmaTel", "Arkados", "Compete IT gmbH Co. KG", "Eudar Technology Inc.", + "Focus Enhancements", "Xyratex"}, +{"Specular Networks", "Patriot Memory", "U-Chip Technology Corp.", "Silicon Optix", + "Greenfield Networks", "CompuRAM GmbH", "Stargen, Inc.", "NetCell Corporation", + "Excalibrus Technologies Ltd", "SCM Microsystems", "Xsigo Systems, Inc.", "CHIPS & Systems Inc", + "Tier 1 Multichip Solutions", "CWRL Labs", "Teradici", "Gigaram, Inc.", + "g2 Microsystems", "PowerFlash Semiconductor", "P.A. Semi, Inc.", "NovaTech Solutions, S.A.", + "c2 Microsystems, Inc.", "Level5 Networks", "COS Memory AG", "Innovasic Semiconductor", + "02IC Co. Ltd", "Tabula, Inc.", "Crucial Technology", "Chelsio Communications", + "Solarflare Communications", "Xambala Inc.", "EADS Astrium", "Terra Semiconductor Inc. (former ATO Semicon Co. Ltd.)", + "Imaging Works, Inc.", "Astute Networks, Inc.", "Tzero", "Emulex", + "Power-One", "Pulse~LINK Inc.", "Hon Hai Precision Industry", "White Rock Networks Inc.", + "Telegent Systems USA, Inc.", "Atrua Technologies, Inc.", "Acbel Polytech Inc.", + "eRide Inc.","ULi Electronics Inc.", "Magnum Semiconductor Inc.", "neoOne Technology, Inc.", + "Connex Technology, Inc.", "Stream Processors, Inc.", "Focus Enhancements", "Telecis Wireless, Inc.", + "uNav Microelectronics", "Tarari, Inc.", "Ambric, Inc.", "Newport Media, Inc.", "VMTS", + "Enuclia Semiconductor, Inc.", "Virtium Technology Inc.", "Solid State System Co., Ltd.", "Kian Tech LLC", + "Artimi", "Power Quotient International", "Avago Technologies", "ADTechnology", "Sigma Designs", + "SiCortex, Inc.", "Ventura Technology Group", "eASIC", "M.H.S. SAS", "Micro Star International", + "Rapport Inc.", "Makway International", "Broad Reach Engineering Co.", + "Semiconductor Mfg Intl Corp", "SiConnect", "FCI USA Inc.", "Validity Sensors", + "Coney Technology Co. Ltd.", "Spans Logic", "Neterion Inc.", "Qimonda", + "New Japan Radio Co. Ltd.", "Velogix", "Montalvo Systems", "iVivity Inc.", "Walton Chaintech", + "AENEON", "Lorom Industrial Co. Ltd.", "Radiospire Networks", "Sensio Technologies, Inc.", + "Nethra Imaging", "Hexon Technology Pte Ltd", "CompuStocx (CSX)", "Methode Electronics, Inc.", + "Connect One Ltd.", "Opulan Technologies", "Septentrio NV", "Goldenmars Technology Inc.", + "Kreton Corporation", "Cochlear Ltd.", "Altair Semiconductor", "NetEffect, Inc.", + "Spansion, Inc.", "Taiwan Semiconductor Mfg", "Emphany Systems Inc.", + "ApaceWave Technologies", "Mobilygen Corporation", "Tego", "Cswitch Corporation", + "Haier (Beijing) IC Design Co.", "MetaRAM", "Axel Electronics Co. Ltd.", "Tilera Corporation", + "Aquantia", "Vivace Semiconductor", "Redpine Signals", "Octalica", "InterDigital Communications", + "Avant Technology", "Asrock, Inc.", "Availink", "Quartics, Inc.", "Element CXI", + "Innovaciones Microelectronicas", "VeriSilicon Microelectronics", "W5 Networks"}, +{"MOVEKING", "Mavrix Technology, Inc.", "CellGuide Ltd.", "Faraday Technology", + "Diablo Technologies, Inc.", "Jennic", "Octasic", "Molex Incorporated", "3Leaf Networks", + "Bright Micron Technology", "Netxen", "NextWave Broadband Inc.", "DisplayLink", "ZMOS Technology", + "Tec-Hill", "Multigig, Inc.", "Amimon", "Euphonic Technologies, Inc.", "BRN Phoenix", + "InSilica", "Ember Corporation", "Avexir Technologies Corporation", "Echelon Corporation", + "Edgewater Computer Systems", "XMOS Semiconductor Ltd.", "GENUSION, Inc.", "Memory Corp NV", + "SiliconBlue Technologies", "Rambus Inc.", "Andes Technology Corporation", "Coronis Systems", + "Achronix Semiconductor", "Siano Mobile Silicon Ltd.", "Semtech Corporation", "Pixelworks Inc.", + "Gaisler Research AB", "Teranetics", "Toppan Printing Co. Ltd.", "Kingxcon", + "Silicon Integrated Systems", "I-O Data Device, Inc.", "NDS Americas Inc.", "Solomon Systech Limited", + "On Demand Microelectronics", "Amicus Wireless Inc.", "SMARDTV SNC", "Comsys Communication Ltd.", + "Movidia Ltd.", "Javad GNSS, Inc.", "Montage Technology Group", "Trident Microsystems", "Super Talent", + "Optichron, Inc.", "Future Waves UK Ltd.", "SiBEAM, Inc.", "Inicore, Inc.", "Virident Systems", + "M2000, Inc.", "ZeroG Wireless, Inc.", "Gingle Technology Co. Ltd.", "Space Micro Inc.", "Wilocity", + "Novafora, Inc.", "iKoa Corporation", "ASint Technology", "Ramtron", "Plato Networks Inc.", + "IPtronics AS", "Infinite-Memories", "Parade Technologies Inc.", "Dune Networks", + "GigaDevice Semiconductor", "Modu Ltd.", "CEITEC", "Northrop Grumman", "XRONET Corporation", + "Sicon Semiconductor AB", "Atla Electronics Co. Ltd.", "TOPRAM Technology", "Silego Technology Inc.", + "Kinglife", "Ability Industries Ltd.", "Silicon Power Computer & Communications", + "Augusta Technology, Inc.", "Nantronics Semiconductors", "Hilscher Gesellschaft", "Quixant Ltd.", + "Percello Ltd.", "NextIO Inc.", "Scanimetrics Inc.", "FS-Semi Company Ltd.", "Infinera Corporation", + "SandForce Inc.", "Lexar Media", "Teradyne Inc.", "Memory Exchange Corp.", "Suzhou Smartek Electronics", + "Avantium Corporation", "ATP Electronics Inc.", "Valens Semiconductor Ltd", "Agate Logic, Inc.", + "Netronome", "Zenverge, Inc.", "N-trig Ltd", "SanMax Technologies Inc.", "Contour Semiconductor Inc.", + "TwinMOS", "Silicon Systems, Inc.", "V-Color Technology Inc.", "Certicom Corporation", "JSC ICC Milandr", + "PhotoFast Global Inc.", "InnoDisk Corporation", "Muscle Power", "Energy Micro", "Innofidei", + "CopperGate Communications", "Holtek Semiconductor Inc.", "Myson Century, Inc.", "FIDELIX", + "Red Digital Cinema", "Densbits Technology", "Zempro", "MoSys", "Provigent", "Triad Semiconductor, Inc."}, +{"Siklu Communication Ltd.", "A Force Manufacturing Ltd.", "Strontium", "Abilis Systems", "Siglead, Inc.", + "Ubicom, Inc.", "Unifosa Corporation", "Stretch, Inc.", "Lantiq Deutschland GmbH", "Visipro", + "EKMemory", "Microelectronics Institute ZTE", "Cognovo Ltd.", "Carry Technology Co. Ltd.", "Nokia", + "King Tiger Technology", "Sierra Wireless", "HT Micron", "Albatron Technology Co. Ltd.", + "Leica Geosystems AG", "BroadLight", "AEXEA", "ClariPhy Communications, Inc.", "Green Plug", + "Design Art Networks", "Mach Xtreme Technology Ltd.", "ATO Solutions Co. Ltd.", "Ramsta", + "Greenliant Systems, Ltd.", "Teikon", "Antec Hadron", "NavCom Technology, Inc.", + "Shanghai Fudan Microelectronics", "Calxeda, Inc.", "JSC EDC Electronics", "Kandit Technology Co. Ltd.", + "Ramos Technology", "Goldenmars Technology", "XeL Technology Inc.", "Newzone Corporation", + "ShenZhen MercyPower Tech", "Nanjing Yihuo Technology", "Nethra Imaging Inc.", "SiTel Semiconductor BV", + "SolidGear Corporation", "Topower Computer Ind Co Ltd.", "Wilocity", "Profichip GmbH", + "Gerad Technologies", "Ritek Corporation", "Gomos Technology Limited", "Memoright Corporation", + "D-Broad, Inc.", "HiSilicon Technologies", "Syndiant Inc.", "Enverv Inc.", "Cognex", + "Xinnova Technology Inc.", "Ultron AG", "Concord Idea Corporation", "AIM Corporation", + "Lifetime Memory Products", "Ramsway", "Recore Systems BV", "Haotian Jinshibo Science Tech", + "Being Advanced Memory", "Adesto Technologies", "Giantec Semiconductor, Inc.", "HMD Electronics AG", + "Gloway International (HK)", "Kingcore", "Anucell Technology Holding", + "Accord Software & Systems Pvt. Ltd.", "Active-Semi Inc.", "Denso Corporation", "TLSI Inc.", + "Shenzhen Daling Electronic Co. Ltd.", "Mustang", "Orca Systems", "Passif Semiconductor", + "GigaDevice Semiconductor (Beijing) Inc.", "Memphis Electronic", "Beckhoff Automation GmbH", + "Harmony Semiconductor Corp (former ProPlus Design Solutions)", "Air Computers SRL", "TMT Memory", + "Eorex Corporation", "Xingtera", "Netsol", "Bestdon Technology Co. Ltd.", "Baysand Inc.", + "Uroad Technology Co. Ltd. (former Triple Grow Industrial Ltd.)", "Wilk Elektronik S.A.", + "AAI", "Harman", "Berg Microelectronics Inc.", "ASSIA, Inc.", "Visiontek Products LLC", + "OCMEMORY", "Welink Solution Inc.", "Shark Gaming", "Avalanche Technology", + "R&D Center ELVEES OJSC", "KingboMars Technology Co. Ltd.", + "High Bridge Solutions Industria Eletronica", "Transcend Technology Co. Ltd.", + "Everspin Technologies", "Hon-Hai Precision", "Smart Storage Systems", "Toumaz Group", + "Zentel Electronics Corporation", "Panram International Corporation", + "Silicon Space Technology"} +}; diff --git a/modules/devices/storage.c b/modules/devices/storage.c new file mode 100644 index 00000000..bbf9b195 --- /dev/null +++ b/modules/devices/storage.c @@ -0,0 +1,820 @@ +/* + * HardInfo - Displays System Information + * Copyright (C) 2003-2006 L. A. F. Pereira <l@tia.mat.br> + * modified by Ondrej Čerman (2019-2021) + * + * 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 or later. + * + * 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 "devices.h" +#include "udisks2_util.h" +#include "storage_util.h" + +#define UNKIFNULL_AC(f) (f != NULL) ? f : _("(Unknown)"); + +gchar *storage_icons = NULL; + +gchar *nvme_pci_sections(pcid *p) { + const gchar *vendor, *svendor, *product, *sproduct; + + if (!p) return NULL; + + vendor = UNKIFNULL_AC(p->vendor_id_str); + svendor = UNKIFNULL_AC(p->sub_vendor_id_str); + product = UNKIFNULL_AC(p->device_id_str); + sproduct = UNKIFNULL_AC(p->sub_device_id_str); + + gchar *vendor_device_str; + if (p->vendor_id == p->sub_vendor_id && p->device_id == p->sub_device_id) { + vendor_device_str = g_strdup_printf("[%s]\n" + /* Vendor */ "$^$%s=[%04x] %s\n" + /* Device */ "%s=[%04x] %s\n", + _("NVMe Controller"), + _("Vendor"), p->vendor_id, vendor, + _("Device"), p->device_id, product); + } else { + vendor_device_str = g_strdup_printf("[%s]\n" + /* Vendor */ "$^$%s=[%04x] %s\n" + /* Device */ "%s=[%04x] %s\n" + /* Sub-device vendor */ "$^$%s=[%04x] %s\n" + /* Sub-device */ "%s=[%04x] %s\n", + _("NVMe Controller"), + _("Vendor"), p->vendor_id, vendor, + _("Device"), p->device_id, product, + _("SVendor"), p->sub_vendor_id, svendor, + _("SDevice"), p->sub_device_id, sproduct); + } + + gchar *pcie_str; + if (p->pcie_width_curr) { + pcie_str = g_strdup_printf("[%s]\n" + /* Addy */ "%s=PCI/%s\n" + /* Width (max) */ "%s=x%u\n" + /* Speed (max) */ "%s=%0.1f %s\n", + _("PCI Express"), + _("Location"), p->slot_str, + _("Maximum Link Width"), p->pcie_width_max, + _("Maximum Link Speed"), p->pcie_speed_max, _("GT/s") ); + } else + pcie_str = strdup(""); + + gchar *ret = g_strdup_printf("%s%s", vendor_device_str, pcie_str); + g_free(vendor_device_str); + g_free(pcie_str); + return ret; +} + +gboolean __scan_udisks2_devices(void) { + GSList *node, *drives; + u2driveext *ext; + udiskd *disk; + udiskp *part; + udisksa *attrib; + gchar *udisks2_storage_list = NULL, *features = NULL, *moreinfo = NULL; + gchar *devid, *size, *tmp = NULL, *media_comp = NULL, *ven_tag = NULL; + const gchar *url, *media_label, *alabel, *icon, *media_curr = NULL; + int n = 0, i, j, m; + + // http://storaged.org/doc/udisks2-api/latest/gdbus-org.freedesktop.UDisks2.Drive.html#gdbus-property-org-freedesktop-UDisks2-Drive.MediaCompatibility + static struct { + char *media; + char *label; + char *icon; + } media_info[] = { + { "thumb", "Thumb-drive", "usbfldisk" }, + { "flash", "Flash Card", "usbfldisk" }, + { "flash_cf", "CompactFlash", "usbfldisk" }, + { "flash_ms", "MemoryStick", "usbfldisk" }, + { "flash_sm", "SmartMedia", "usbfldisk" }, + { "flash_sd", "SD", "usbfldisk" }, + { "flash_sdhc", "SDHC", "usbfldisk" }, + { "flash_sdxc", "SDXC", "usbfldisk" }, + { "flash_mmc", "MMC", "usbfldisk" }, + { "floppy", "Floppy Disk", "media-floppy" }, + { "floppy_zip", "Zip Disk", "media-floppy" }, + { "floppy_jaz", "Jaz Disk", "media-floppy" }, + { "optical", "Optical Disc", "cdrom" }, + { "optical_cd", "CD-ROM", "cdrom" }, + { "optical_cd_r", "CD-R", "cdrom" }, + { "optical_cd_rw", "CD-RW", "cdrom" }, + { "optical_dvd", "DVD-ROM", "cdrom" }, + { "optical_dvd_r", "DVD-R", "cdrom" }, + { "optical_dvd_rw", "DVD-RW", "cdrom" }, + { "optical_dvd_ram", "DVD-RAM", "cdrom" }, + { "optical_dvd_plus_r", "DVD+R" , "cdrom" }, + { "optical_dvd_plus_rw", "DVD+RW" , "cdrom" }, + { "optical_dvd_plus_r_dl", "DVD+R DL", "cdrom" }, + { "optical_dvd_plus_rw_dl", "DVD+RW DL", "cdrom" }, + { "optical_bd", "BD-ROM", "cdrom" }, + { "optical_bd_r", "BD-R", "cdrom" }, + { "optical_bd_re", "BD-RE", "cdrom" }, + { "optical_hddvd", "HD DVD-ROM", "cdrom" }, + { "optical_hddvd_r", "HD DVD-R", "cdrom" }, + { "optical_hddvd_rw", "HD DVD-RW", "cdrom" }, + { "optical_mo", "MO Disc", "cdrom" }, + { "optical_mrw", "MRW Media", "cdrom" }, + { "optical_mrw_w", "MRW Media (write)", "cdrom" }, + { NULL, NULL } + }; + + struct { + char *identifier; + char *label; + } smart_attrib_info[] = { + { "raw-read-error-rate", _("Read Error Rate" ) }, + { "throughput-performance", _("Throughput Performance") }, + { "spin-up-time", _("Spin-Up Time") }, + { "start-stop-count", _("Start/Stop Count") }, + { "reallocated-sector-count", _("Reallocated Sector Count") }, + { "read-channel-margin", _("Read Channel Margin") }, + { "seek-error-rate", _("Seek Error Rate") }, + { "seek-time-performance", _("Seek Timer Performance") }, + { "power-on-hours", _("Power-On Hours") }, + { "spin-retry-count", _("Spin Retry Count") }, + { "calibration-retry-count", _("Calibration Retry Count") }, + { "power-cycle-count", _("Power Cycle Count") }, + { "read-soft-error-rate", _("Soft Read Error Rate") }, + { "runtime-bad-block-total", _("Runtime Bad Block") }, + { "end-to-end-error", _("End-to-End error") }, + { "reported-uncorrect", _("Reported Uncorrectable Errors") }, + { "command-timeout", _("Command Timeout") }, + { "high-fly-writes", _("High Fly Writes") }, + { "airflow-temperature-celsius", _("Airflow Temperature") }, + { "g-sense-error-rate", _("G-sense Error Rate") }, + { "power-off-retract-count", _("Power-off Retract Count") }, + { "load-cycle-count", _("Load Cycle Count") }, + { "temperature-celsius-2", _("Temperature") }, + { "hardware-ecc-recovered", _("Hardware ECC Recovered") }, + { "reallocated-event-count", _("Reallocation Event Count") }, + { "current-pending-sector", _("Current Pending Sector Count") }, + { "offline-uncorrectable", _("Uncorrectable Sector Count") }, + { "udma-crc-error-count", _("UltraDMA CRC Error Count") }, + { "multi-zone-error-rate", _("Multi-Zone Error Rate") }, + { "soft-read-error-rate", _("Soft Read Error Rate") }, + { "run-out-cancel", _("Run Out Cancel") }, + { "flying-height", _("Flying Height") }, + { "spin-high-current", _("Spin High Current") }, + { "spin-buzz", _("Spin Buzz") }, + { "offline-seek-performance", _("Offline Seek Performance") }, + { "disk-shift", _("Disk Shift") }, + { "g-sense-error-rate-2", _("G-Sense Error Rate") }, + { "loaded-hours", _("Loaded Hours") }, + { "load-retry-count", _("Load/Unload Retry Count") }, + { "load-friction", _("Load Friction") }, + { "load-cycle-count-2", _("Load/Unload Cycle Count") }, + { "load-in-time", _("Load-in time") }, + { "torq-amp-count", _("Torque Amplification Count") }, + { "power-off-retract-count-2", _("Power-Off Retract Count") }, + { "head-amplitude", _("GMR Head Amplitude") }, + { "temperature-celsius", _("Temperature") }, + { "endurance-remaining", _("Endurance Remaining") }, + { "power-on-seconds-2", _("Power-On Hours") }, + { "good-block-rate", _("Good Block Rate") }, + { "head-flying-hours", _("Head Flying Hours") }, + { "read-error-retry-rate", _("Read Error Retry Rate") }, + { "total-lbas-written", _("Total LBAs Written") }, + { "total-lbas-read", _("Total LBAs Read") }, + { "wear-leveling-count", _("Wear leveling Count") }, + { "used-reserved-blocks-total", _("Total Used Reserved Block Count") }, + { "program-fail-count-total", _("Total Program Fail Count") }, + { "erase-fail-count-total", _("Total Erase Fail Count") }, + { "available-reserved-space", _("Available Reserved Space") }, + { "program-fail-count", _("Program Fail Count") }, + { "erase-fail-count", _("Erase Fail Count") }, + { "ta-increase-count", _("TA Counter Increased") }, + { "unused-reserved-blocks", _("Total Unused Reserved Block Count") }, + { NULL, NULL } + }; + + moreinfo_del_with_prefix("DEV:UDISKS"); + udisks2_storage_list = g_strdup(_("\n[UDisks2]\n")); + + drives = get_udisks2_drives_ext(); + for (node = drives; node != NULL; node = node->next) { + ext = (u2driveext *)node->data; + disk = ext->d; + devid = g_strdup_printf("UDISKS%d", n++); + + icon = NULL; + + media_curr = disk->media; + if (disk->media){ + for (j = 0; media_info[j].media != NULL; j++) { + if (g_strcmp0(disk->media, media_info[j].media) == 0) { + media_curr = media_info[j].label; + break; + } + } + } + + if (disk->media_compatibility){ + for (i = 0; disk->media_compatibility[i] != NULL; i++){ + media_label = disk->media_compatibility[i]; + + for (j = 0; media_info[j].media != NULL; j++) { + if (g_strcmp0(disk->media_compatibility[i], media_info[j].media) == 0) { + media_label = media_info[j].label; + if (icon == NULL) + icon = media_info[j].icon; + break; + } + } + + if (media_comp == NULL){ + media_comp = g_strdup(media_label); + } + else{ + media_comp = h_strdup_cprintf(", %s", media_comp, media_label); + } + } + } + if (icon == NULL && disk->ejectable && g_strcmp0(disk->connection_bus, "usb") == 0) { + icon = "usbfldisk"; + } + if (icon == NULL){ + icon = "hdd"; + } + + size = size_human_readable((gfloat) disk->size); + ven_tag = vendor_list_ribbon(ext->vendors, params.fmt_opts); + + udisks2_storage_list = h_strdup_cprintf("$%s$%s=%s|%s %s\n", udisks2_storage_list, devid, disk->block_dev, size, ven_tag ? ven_tag : "", disk->model); + storage_icons = h_strdup_cprintf("Icon$%s$%s=%s.png\n", storage_icons, devid, disk->model, icon); + features = h_strdup_cprintf("%s", features, disk->removable ? _("Removable"): _("Fixed")); + + if (disk->ejectable) { + features = h_strdup_cprintf(", %s", features, _("Ejectable")); + } + if (disk->smart_supported) { + features = h_strdup_cprintf(", %s", features, _("Self-monitoring (S.M.A.R.T.)")); + } + if (disk->pm_supported) { + features = h_strdup_cprintf(", %s", features, _("Power Management")); + } + if (disk->apm_supported) { + features = h_strdup_cprintf(", %s", features, _("Advanced Power Management")); + } + if (disk->aam_supported) { + features = h_strdup_cprintf(", %s", features, _("Automatic Acoustic Management")); + } + + moreinfo = g_strdup_printf(_("[Drive Information]\n" + "Model=%s\n"), + disk->model); + + if (disk->vendor && *disk->vendor) { + moreinfo = h_strdup_cprintf("$^$%s=%s\n", + moreinfo, + _("Vendor"), disk->vendor); + } + + moreinfo = h_strdup_cprintf(_("Revision=%s\n" + "Block Device=%s\n" + "Serial=%s\n" + "Size=%s\n" + "Features=%s\n"), + moreinfo, + disk->revision, + disk->block_dev, + disk->serial, + size, + features); + g_free(size); + g_free(ven_tag); + + if (disk->rotation_rate > 0) { + moreinfo = h_strdup_cprintf(_("Rotation Rate=%d RPM\n"), moreinfo, disk->rotation_rate); + } + if (media_comp || media_curr) { + moreinfo = h_strdup_cprintf(_("Media=%s\n" + "Media compatibility=%s\n"), + moreinfo, + media_curr ? media_curr : _("(None)"), + media_comp ? media_comp : _("(Unknown)")); + } + if (disk->connection_bus && strlen(disk->connection_bus) > 0) { + moreinfo = h_strdup_cprintf(_("Connection bus=%s\n"), moreinfo, disk->connection_bus); + } + + tmp = NULL; + if (disk->wwid) { + m = strlen(disk->wwid); + if (m > 2 && m % 2 == 0){ + for (j = 4; j < m; j = j + 2) { + tmp = h_strdup_cprintf("%s%c%c", tmp, j > 4 ? "-": "", disk->wwid[j], disk->wwid[j+1]); + } + } + moreinfo = h_strdup_cprintf("%s=%s\n", moreinfo, + g_str_has_prefix(disk->wwid, "nna.") ? _("WWN"): + (g_str_has_prefix(disk->wwid, "eui.") ? _("EUI "): "Unknown ID"), + tmp); + g_free(tmp); + } + else{ + moreinfo = h_strdup_cprintf("%s=%s\n", moreinfo, _("WWN / EUI"), _("(None)")); + } + + if (ext->wwid_oui.oui) { + moreinfo = h_strdup_cprintf(_("$^$%s=[%s] %s\n"), + moreinfo, + _("IEEE OUI"), ext->wwid_oui.oui, + ext->wwid_oui.vendor ? + ext->wwid_oui.vendor : _("(Unknown)")); + } + + if (ext->nvme_controller) { + gchar *nvme = nvme_pci_sections(ext->nvme_controller); + if (nvme) + moreinfo = h_strdup_cprintf("%s", moreinfo, nvme); + g_free(nvme); + } + if (disk->smart_enabled) { + moreinfo = h_strdup_cprintf(_("[Self-monitoring (S.M.A.R.T.)]\n" + "Status=%s\n" + "Bad Sectors=%" G_GINT64_FORMAT "\n" + "Power on time=%" G_GUINT64_FORMAT " days %" G_GUINT64_FORMAT " hours\n" + "Temperature=%d°C\n"), + moreinfo, + disk->smart_failing ? _("Failing"): _("OK"), + disk->smart_bad_sectors, + disk->smart_poweron/(60*60*24), (disk->smart_poweron/60/60) % 24, + disk->smart_temperature); + + if (disk->smart_attributes != NULL) { + moreinfo = h_strdup_cprintf(_("[S.M.A.R.T. Attributes]\n" + "Attribute=<tt>Value / Normalized / Worst / Threshold</tt>\n"), + moreinfo); + + attrib = disk->smart_attributes; + + while (attrib != NULL){ + tmp = g_strdup(""); + + switch (attrib->interpreted_unit){ + case UDSK_INTPVAL_SKIP: + tmp = h_strdup_cprintf("-", tmp); + break; + case UDSK_INTPVAL_MILISECONDS: + tmp = h_strdup_cprintf("%" G_GINT64_FORMAT " ms", tmp, attrib->interpreted); + break; + case UDSK_INTPVAL_HOURS: + tmp = h_strdup_cprintf("%" G_GINT64_FORMAT " h", tmp, attrib->interpreted); + break; + case UDSK_INTPVAL_CELSIUS: + tmp = h_strdup_cprintf("%" G_GINT64_FORMAT "°C", tmp, attrib->interpreted); + break; + case UDSK_INTPVAL_SECTORS: + tmp = h_strdup_cprintf(ngettext("%" G_GINT64_FORMAT " sector", + "%" G_GINT64_FORMAT " sectors", attrib->interpreted), + tmp, attrib->interpreted); + break; + case UDSK_INTPVAL_DIMENSIONLESS: + default: + tmp = h_strdup_cprintf("%" G_GINT64_FORMAT, tmp, attrib->interpreted); + break; + } + + // pad spaces to next col + j = g_utf8_strlen(tmp, -1); + if (j < 13) tmp = h_strdup_cprintf("%*c", tmp, 13 - j, ' '); + + if (attrib->value != -1) + tmp = h_strdup_cprintf("%-13d", tmp, attrib->value); + else + tmp = h_strdup_cprintf("%-13s", tmp, "???"); + + if (attrib->worst != -1) + tmp = h_strdup_cprintf("%-8d", tmp, attrib->worst); + else + tmp = h_strdup_cprintf("%-8s", tmp, "???"); + + if (attrib->threshold != -1) + tmp = h_strdup_cprintf("%d", tmp, attrib->threshold); + else + tmp = h_strdup_cprintf("???", tmp); + + + alabel = attrib->identifier; + for (i = 0; smart_attrib_info[i].identifier != NULL; i++) { + if (g_strcmp0(attrib->identifier, smart_attrib_info[i].identifier) == 0) { + alabel = smart_attrib_info[i].label; + break; + } + } + + moreinfo = h_strdup_cprintf(_("(%d) %s=<tt>%s</tt>\n"), + moreinfo, + attrib->id, alabel, tmp); + g_free(tmp); + attrib = attrib->next; + } + } + } + if (disk->partition_table || disk->partitions) { + moreinfo = h_strdup_cprintf(_("[Partition table]\n" + "Type=%s\n"), + moreinfo, + disk->partition_table ? disk->partition_table : _("(Unknown)")); + + if (disk->partitions != NULL) { + part = disk->partitions; + while (part != NULL){ + + tmp = size_human_readable((gfloat) part->size); + if (part->label) { + tmp = h_strdup_cprintf(" - %s", tmp, part->label); + } + if (part->type && part->version) { + tmp = h_strdup_cprintf(" (%s %s)", tmp, part->type, part->version); + } + else if (part->type) { + tmp = h_strdup_cprintf(" (%s)", tmp, part->type); + } + moreinfo = h_strdup_cprintf(_("Partition %s=%s\n"), + moreinfo, + part->block, tmp); + g_free(tmp); + part = part->next; + } + } + } + + moreinfo_add_with_prefix("DEV", devid, moreinfo); + g_free(devid); + g_free(features); + g_free(media_comp); + media_comp = NULL; + + features = NULL; + moreinfo = NULL; + devid = NULL; + + u2driveext_free(ext); + } + g_slist_free(drives); + + if (n) { + storage_list = h_strconcat(storage_list, udisks2_storage_list, NULL); + g_free(udisks2_storage_list); + return TRUE; + } + + g_free(udisks2_storage_list); + return FALSE; +} + +/* SCSI support by Pascal F.Martin <pascalmartin@earthlink.net> */ +void __scan_scsi_devices(void) +{ + FILE *proc_scsi; + gchar buffer[256], *buf; + gint n = 0; + gint scsi_controller = 0; + gint scsi_channel = 0; + gint scsi_id = 0 ; + gint scsi_lun = 0; + gchar *vendor = NULL, *revision = NULL, *model = NULL; + gchar *scsi_storage_list; + + /* remove old devices from global device table */ + moreinfo_del_with_prefix("DEV:SCSI"); + + scsi_storage_list = g_strdup(_("\n[SCSI Disks]\n")); + + int otype = 0; + if (proc_scsi = fopen("/proc/scsi/scsi", "r")) { + otype = 1; + } else if (proc_scsi = popen("lsscsi -c", "r")) { + otype = 2; + } + + if (otype) { + while (fgets(buffer, 256, proc_scsi)) { + buf = g_strstrip(buffer); + if (!strncmp(buf, "Host: scsi", 10)) { + sscanf(buf, + "Host: scsi%d Channel: %d Id: %d Lun: %d", + &scsi_controller, &scsi_channel, &scsi_id, &scsi_lun); + + n++; + } else if (!strncmp(buf, "Vendor: ", 8)) { + buf[17] = '\0'; + buf[41] = '\0'; + buf[53] = '\0'; + + vendor = g_strdup(g_strstrip(buf + 8)); + model = g_strdup_printf("%s %s", vendor, g_strstrip(buf + 24)); + revision = g_strdup(g_strstrip(buf + 46)); + } else if (!strncmp(buf, "Type: ", 8)) { + char *p; + gchar *type = NULL, *icon = NULL; + + if (!(p = strstr(buf, "ANSI SCSI revision"))) { + p = strstr(buf, "ANSI SCSI revision"); + } + + if (p != NULL) { + while (*(--p) == ' '); + *(++p) = 0; + + static struct { + char *type; + char *label; + char *icon; + } type2icon[] = { + { "Direct-Access", "Disk", "hdd"}, + { "Sequential-Access", "Tape", "tape"}, + { "Printer", "Printer", "lpr"}, + { "WORM", "CD-ROM", "cdrom"}, + { "CD-ROM", "CD-ROM", "cdrom"}, + { "Scanner", "Scanner", "scanner"}, + { "Flash Disk", "USB Flash Disk", "usbfldisk" }, + { NULL, "Generic", "scsi"} + }; + int i; + + if (model && strstr(model, "Flash Disk")) { + type = "Flash Disk"; + icon = "usbfldisk"; + } else { + for (i = 0; type2icon[i].type != NULL; i++) + if (g_str_equal(buf + 8, type2icon[i].type)) + break; + + type = type2icon[i].label; + icon = type2icon[i].icon; + } + } + + gchar *devid = g_strdup_printf("SCSI%d", n); + scsi_storage_list = h_strdup_cprintf("$%s$scsi%d=|%s\n", scsi_storage_list, devid, scsi_controller, model); + storage_icons = h_strdup_cprintf("Icon$%s$%s=%s.png\n", storage_icons, devid, model, icon); + + gchar *strhash = g_strdup_printf(_("[Device Information]\n" + "Model=%s\n"), model); + + strhash = h_strdup_cprintf("$^$%s=%s\n", + strhash, + _("Vendor"), model); + + strhash = h_strdup_cprintf(_("Type=%s\n" + "Revision=%s\n" + "[SCSI Controller]\n" + "Controller=scsi%d\n" + "Channel=%d\n" + "ID=%d\n" "LUN=%d\n"), + strhash, + type, + revision, + scsi_controller, + scsi_channel, + scsi_id, + scsi_lun); + + moreinfo_add_with_prefix("DEV", devid, strhash); + g_free(devid); + + g_free(model); + g_free(revision); + g_free(vendor); + + scsi_controller = scsi_channel = scsi_id = scsi_lun = 0; + } + } + if (otype == 1) + fclose(proc_scsi); + else if (otype == 2) + pclose(proc_scsi); + } + + if (n) { + storage_list = h_strconcat(storage_list, scsi_storage_list, NULL); + g_free(scsi_storage_list); + } +} + +void __scan_ide_devices(void) +{ + FILE *proc_ide; + gchar *device, *model, *media, *pgeometry = NULL, *lgeometry = NULL; + gchar iface; + gint n = 0, i = 0, cache, nn = 0; + gchar *capab = NULL, *speed = NULL, *driver = NULL, *ide_storage_list; + + /* remove old devices from global device table */ + moreinfo_del_with_prefix("DEV:IDE"); + + ide_storage_list = g_strdup(_("\n[IDE Disks]\n")); + + iface = 'a'; + for (i = 0; i <= 16; i++) { + device = g_strdup_printf("/proc/ide/hd%c/model", iface); + if (g_file_test(device, G_FILE_TEST_EXISTS)) { + gchar buf[128]; + + cache = 0; + + proc_ide = fopen(device, "r"); + if (!proc_ide) + continue; + + (void) fgets(buf, 128, proc_ide); + fclose(proc_ide); + + buf[strlen(buf) - 1] = 0; + + model = g_strdup(buf); + + g_free(device); + + device = g_strdup_printf("/proc/ide/hd%c/media", iface); + proc_ide = fopen(device, "r"); + if (!proc_ide) { + free(model); + continue; + } + + (void) fgets(buf, 128, proc_ide); + fclose(proc_ide); + buf[strlen(buf) - 1] = 0; + + media = g_strdup(buf); + if (g_str_equal(media, "cdrom")) { + /* obtain cd-rom drive information from cdrecord */ + GTimer *timer; + gchar *tmp = g_strdup_printf("cdrecord dev=/dev/hd%c -prcap 2>/dev/stdout", iface); + FILE *prcap; + + if ((prcap = popen(tmp, "r"))) { + /* we need a timeout so cdrecord does not try to get information on cd drives + with inserted media, which is not possible currently. half second should be + enough. */ + timer = g_timer_new(); + g_timer_start(timer); + + while (fgets(buf, 128, prcap) + && g_timer_elapsed(timer, NULL) < 0.5) { + if (g_str_has_prefix(buf, " Does")) { + if (g_str_has_suffix(buf, "media\n") + && !strstr(buf, "speed")) { + gchar *media_type = g_strstrip(strstr(buf, "Does ")); + gchar **ttmp = g_strsplit(media_type, " ", 0); + + capab = h_strdup_cprintf("\nCan %s#%d=%s\n", capab, ttmp[1], ++nn, ttmp[2]); + + g_strfreev(ttmp); + } else if (strstr(buf, "Buffer-Underrun-Free")) { + capab = + h_strdup_cprintf + ("\nSupports BurnProof=%s\n", capab, strstr(buf, "Does not") ? "No" : "Yes"); + } else if (strstr(buf, "multi-session")) { + capab = + h_strdup_cprintf + ("\nCan read multi-session CDs=%s\n", + capab, strstr(buf, "Does not") ? "No" : "Yes"); + } else if (strstr(buf, "audio CDs")) { + capab = + h_strdup_cprintf + ("\nCan play audio CDs=%s\n", capab, strstr(buf, "Does not") ? "No" : "Yes"); + } else if (strstr(buf, "PREVENT/ALLOW")) { + capab = + h_strdup_cprintf + ("\nCan lock media=%s\n", capab, strstr(buf, "Does not") ? "No" : "Yes"); + } + } else if ((strstr(buf, "read") + || strstr(buf, "write")) + && strstr(buf, "kB/s")) { + speed = + g_strconcat(speed ? speed : "", strreplacechr(g_strstrip(buf), ":", '='), "\n", NULL); + } else if (strstr(buf, "Device seems to be")) { + driver = g_strdup_printf(_("Driver=%s\n"), strchr(buf, ':') + 1); + } + } + + pclose(prcap); + g_timer_destroy(timer); + } + + g_free(tmp); + } + g_free(device); + + device = g_strdup_printf("/proc/ide/hd%c/cache", iface); + if (g_file_test(device, G_FILE_TEST_EXISTS)) { + proc_ide = fopen(device, "r"); + if (proc_ide) { + (void) fscanf(proc_ide, "%d", &cache); + fclose(proc_ide); + } else { + cache = 0; + } + } + g_free(device); + + device = g_strdup_printf("/proc/ide/hd%c/geometry", iface); + if (g_file_test(device, G_FILE_TEST_EXISTS)) { + gchar *tmp; + + proc_ide = fopen(device, "r"); + if (proc_ide) { + (void) fgets(buf, 64, proc_ide); + for (tmp = buf; *tmp; tmp++) { + if (*tmp >= '0' && *tmp <= '9') + break; + } + + pgeometry = g_strdup(g_strstrip(tmp)); + + (void) fgets(buf, 64, proc_ide); + for (tmp = buf; *tmp; tmp++) { + if (*tmp >= '0' && *tmp <= '9') + break; + } + lgeometry = g_strdup(g_strstrip(tmp)); + + fclose(proc_ide); + } else { + pgeometry = g_strdup("Unknown"); + lgeometry = g_strdup("Unknown"); + } + + } + g_free(device); + + n++; + + gchar *devid = g_strdup_printf("IDE%d", n); + + ide_storage_list = h_strdup_cprintf("$%s$hd%c=|%s\n", ide_storage_list, devid, iface, model); + storage_icons = + h_strdup_cprintf("Icon$%s$%s=%s.png\n", storage_icons, + devid, model, g_str_equal(media, "cdrom") ? "cdrom" : "hdd"); + + gchar *strhash = g_strdup_printf(_("[Device Information]\n" "Model=%s\n"), + model); + + strhash = h_strdup_cprintf("$^$%s=%s\n", + strhash, _("Vendor"), model); + + strhash = h_strdup_cprintf(_("Device Name=hd%c\n" + "Media=%s\n" "Cache=%dkb\n"), strhash, iface, media, cache); + if (driver) { + strhash = h_strdup_cprintf("%s\n", strhash, driver); + + g_free(driver); + driver = NULL; + } + + if (pgeometry && lgeometry) { + strhash = h_strdup_cprintf(_("[Geometry]\n" + "Physical=%s\n" "Logical=%s\n"), strhash, pgeometry, lgeometry); + + g_free(pgeometry); + pgeometry = NULL; + g_free(lgeometry); + lgeometry = NULL; + } + + if (capab) { + strhash = h_strdup_cprintf(_("[Capabilities]\n%s"), strhash, capab); + + g_free(capab); + capab = NULL; + } + + if (speed) { + strhash = h_strdup_cprintf(_("[Speeds]\n%s"), strhash, speed); + + g_free(speed); + speed = NULL; + } + + moreinfo_add_with_prefix("DEV", devid, strhash); + g_free(devid); + g_free(model); + } else { + g_free(device); + } + + iface++; + } + + if (n) { + storage_list = h_strconcat(storage_list, ide_storage_list, NULL); + g_free(ide_storage_list); + } +} diff --git a/modules/devices/usb.c b/modules/devices/usb.c new file mode 100644 index 00000000..c6a5ab39 --- /dev/null +++ b/modules/devices/usb.c @@ -0,0 +1,224 @@ +/* + * HardInfo - Displays System Information + * Copyright (C) 2003-2008 L. A. F. Pereira <l@tia.mat.br> + * + * 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 or later. + * + * 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 "cpu_util.h" +#include "hardinfo.h" +#include "devices.h" +#include "usb_util.h" + +gchar *usb_list = NULL; +gchar *usb_icons = NULL; + +#define UNKIFNULL_AC(f) (f != NULL) ? f : _("(Unknown)") +#define IARR_END -2 +#define IARR_ANY -1 + +static struct { + int class; + char *icon; +} usb_class_icons[] = { + { 0x1, "audio"}, /* Audio */ + { 0x2, "modem"}, /* Communications and CDC Control */ + { 0x3, "inputdevices"}, /* HID (Human Interface Device) */ + { 0x6, "camera-photo"}, /* Still Imaging */ + { 0x7, "printer"}, /* Printer */ + { 0x8, "media-removable"}, /* Mass storage */ + { 0x9, "usb"}, /* Hub */ + { 0xe, "camera-web"}, /* Video */ + {IARR_END, NULL} +}; + +static struct { + int class, subclass, protocol; + char *icon; +} usb_type_icons[] = { + { 0x2, 0x6, IARR_ANY, "network-interface"}, /* Ethernet Networking Control Model */ + { 0x3, 0x1, 0x1, "keyboard"}, /* Keyboard */ + { 0x3, 0x1, 0x2, "mouse"}, /* Mouse */ + {0xe0, 0x1, 0x1, "bluetooth"}, /* Bluetooth Programming Interface */ + {IARR_END, IARR_END, IARR_END, NULL}, +}; + +static const char* get_class_icon(int class){ + int i = 0; + while (usb_class_icons[i].class != IARR_END) { + if (usb_class_icons[i].class == class) { + return usb_class_icons[i].icon; + } + i++; + } + return NULL; +} + +static const char* get_usbif_icon(const usbi *usbif) { + int i = 0; + while (usb_type_icons[i].class != IARR_END) { + if (usb_type_icons[i].class == usbif->if_class && usb_type_icons[i].subclass == usbif->if_subclass && + (usb_type_icons[i].protocol == IARR_ANY || usb_type_icons[i].protocol == usbif->if_protocol)) { + + return usb_type_icons[i].icon; + } + i++; + } + + return get_class_icon(usbif->if_class); +} + +static const char* get_usbdev_icon(const usbd *u) { + const char * icon = NULL; + usbi *curr_if; + + curr_if = u->if_list; + while (icon == NULL && curr_if != NULL){ + icon = get_usbif_icon(curr_if); + curr_if = curr_if->next; + } + + if (icon == NULL){ + icon = get_class_icon(u->dev_class); + } + + return icon; +} + +static void _usb_dev(const usbd *u) { + gchar *name, *key, *label, *str, *speed; + gchar *product, *vendor, *manufacturer, *device; /* don't free */ + gchar *interfaces = strdup(""); + usbi *i; + const char* icon; + + vendor = UNKIFNULL_AC(u->vendor); + product = UNKIFNULL_AC(u->product); + manufacturer = UNKIFNULL_AC(u->manufacturer); + device = UNKIFNULL_AC(u->device); + + if (u->vendors) { + gchar *ribbon = vendor_list_ribbon(u->vendors, params.fmt_opts); + name = g_strdup_printf("%s %s", ribbon, u->product? product: device); + } else { + name = g_strdup_printf("%s %s", u->vendor? vendor: manufacturer, u->product? product: device); + } + key = g_strdup_printf("USB%03d:%03d:%03d", u->bus, u->dev, 0); + label = g_strdup_printf("%03d:%03d", u->bus, u->dev); + icon = get_usbdev_icon(u); + + usb_list = h_strdup_cprintf("$%s$%s=%s\n", usb_list, key, label, name); + usb_icons = h_strdup_cprintf("Icon$%s$%s=%s.png\n", usb_icons, key, label, icon ? icon: "usb"); + + if (u->if_list != NULL) { + i = u->if_list; + while (i != NULL){ + interfaces = h_strdup_cprintf("[%s %d %s]\n" + /* Class */ "%s=[%d] %s\n" + /* Sub-class */ "%s=[%d] %s\n" + /* Protocol */ "%s=[%d] %s\n" + /* Driver */ "%s=%s\n", + interfaces, + _("Interface"), i->if_number, i->if_label? i->if_label: "", + _("Class"), i->if_class, UNKIFNULL_AC(i->if_class_str), + _("Sub-class"), i->if_subclass, UNKIFNULL_AC(i->if_subclass_str), + _("Protocol"), i->if_protocol, UNKIFNULL_AC(i->if_protocol_str), + _("Driver"), UNKIFNULL_AC(i->driver) + ); + i = i->next; + } + } + + if (u->speed_mbs > 0){ + speed = g_strdup_printf("%d %s", u->speed_mbs, _("Mb/s")); + } + else{ + speed = g_strdup(_("Unknown")); + } + + str = g_strdup_printf("[%s]\n" + /* Product */ "%s=[0x%04x] %s\n" + /* Vendor */ "$^$%s=[0x%04x] %s\n" + /* Device */ "%s=%s\n" + /* Manufacturer */ "$^$%s=%s\n" + /* Max Current */ "%s=%d %s\n" + /* USB Version */ "%s=%s\n" + /* Speed */ "%s=%s\n" + /* Class */ "%s=[%d] %s\n" + /* Sub-class */ "%s=[%d] %s\n" + /* Protocol */ "%s=[%d] %s\n" + /* Dev Version */ "%s=%s\n" + /* Serial */ "%s=%s\n" + "[%s]\n" + /* Bus */ "%s=%03d\n" + /* Device */ "%s=%03d\n" + /* Interfaces */ "%s", + _("Device Information"), + _("Product"), u->product_id, product, + _("Vendor"), u->vendor_id, vendor, + _("Device"), device, + _("Manufacturer"), manufacturer, + _("Max Current"), u->max_curr_ma, _("mA"), + _("USB Version"), u->usb_version, + _("Speed"), speed, + _("Class"), u->dev_class, UNKIFNULL_AC(u->dev_class_str), + _("Sub-class"), u->dev_subclass, UNKIFNULL_AC(u->dev_subclass_str), + _("Protocol"), u->dev_protocol, UNKIFNULL_AC(u->dev_protocol_str), + _("Device Version"), UNKIFNULL_AC(u->device_version), + _("Serial Number"), UNKIFNULL_AC(u->serial), + _("Connection"), + _("Bus"), u->bus, + _("Device"), u->dev, + interfaces + ); + + moreinfo_add_with_prefix("DEV", key, str); /* str now owned by morinfo */ + + g_free(speed); + g_free(name); + g_free(key); + g_free(label); + g_free(interfaces); +} + +void __scan_usb(void) { + usbd *list = usb_get_device_list(); + usbd *curr = list; + + int c = usbd_list_count(list); + + if (usb_list) { + moreinfo_del_with_prefix("DEV:USB"); + g_free(usb_list); + } + if (usb_icons){ + g_free(usb_icons); + usb_icons = NULL; + } + usb_list = g_strdup_printf("[%s]\n", _("USB Devices")); + + if (c > 0) { + while(curr) { + _usb_dev(curr); + curr=curr->next; + } + + usbd_list_free(list); + } else { + /* No USB? */ + usb_list = g_strconcat(usb_list, _("No USB devices found."), "=\n", NULL); + } +} diff --git a/modules/devices/x86/processor.c b/modules/devices/x86/processor.c new file mode 100644 index 00000000..4141f051 --- /dev/null +++ b/modules/devices/x86/processor.c @@ -0,0 +1,823 @@ +/* + * HardInfo - Displays System Information + * Copyright (C) 2003-2006 L. A. F. Pereira <l@tia.mat.br> + * + * 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 or later. + * + * 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 "devices.h" +#include "cpu_util.h" +#include "nice_name.h" +#include "x86_data.h" +#include "x86_data.c" + +/* + * This function is partly based on x86cpucaps + * by Osamu Kayasono <jacobi@jcom.home.ne.jp> + */ +void get_processor_strfamily(Processor * processor) +{ + gint family = processor->family; + gint model = processor->model; + + if (g_str_equal(processor->vendor_id, "GenuineIntel")) { + if (family == 4) { + processor->strmodel = g_strdup("i486 series"); + } else if (family == 5) { + if (model < 4) { + processor->strmodel = g_strdup("Pentium Classic"); + } else { + processor->strmodel = g_strdup("Pentium MMX"); + } + } else if (family == 6) { + if (model <= 1) { + processor->strmodel = g_strdup("Pentium Pro"); + } else if (model < 7) { + processor->strmodel = g_strdup("Pentium II/Pentium II Xeon/Celeron"); + } else if (model == 9) { + processor->strmodel = g_strdup("Pentium M"); + } else { + processor->strmodel = g_strdup("Pentium III/Pentium III Xeon/Celeron/Core Duo/Core Duo 2"); + } + } else if (family > 6) { + processor->strmodel = g_strdup("Pentium 4"); + } else { + processor->strmodel = g_strdup("i386 class"); + } + } else if (g_str_equal(processor->vendor_id, "AuthenticAMD")) { + if (family == 4) { + if (model <= 9) { + processor->strmodel = g_strdup("AMD i80486 series"); + } else { + processor->strmodel = g_strdup("AMD 5x86"); + } + } else if (family == 5) { + if (model <= 3) { + processor->strmodel = g_strdup("AMD K5"); + } else if (model <= 7) { + processor->strmodel = g_strdup("AMD K6"); + } else if (model == 8) { + processor->strmodel = g_strdup("AMD K6-2"); + } else if (model == 9) { + processor->strmodel = g_strdup("AMD K6-III"); + } else { + processor->strmodel = g_strdup("AMD K6-2+/III+"); + } + } else if (family == 6) { + if (model == 1) { + processor->strmodel = g_strdup("AMD Athlon (K7)"); + } else if (model == 2) { + processor->strmodel = g_strdup("AMD Athlon (K75)"); + } else if (model == 3) { + processor->strmodel = g_strdup("AMD Duron (Spitfire)"); + } else if (model == 4) { + processor->strmodel = g_strdup("AMD Athlon (Thunderbird)"); + } else if (model == 6) { + processor->strmodel = g_strdup("AMD Athlon XP/MP/4 (Palomino)"); + } else if (model == 7) { + processor->strmodel = g_strdup("AMD Duron (Morgan)"); + } else if (model == 8) { + processor->strmodel = g_strdup("AMD Athlon XP/MP (Thoroughbred)"); + } else if (model == 10) { + processor->strmodel = g_strdup("AMD Athlon XP/MP (Barton)"); + } else { + processor->strmodel = g_strdup("AMD Athlon (unknown)"); + } + } else if (family > 6) { + processor->strmodel = g_strdup("AMD Opteron/Athlon64/FX"); + } else { + processor->strmodel = g_strdup("AMD i386 class"); + } + } else if (g_str_equal(processor->vendor_id, "CyrixInstead")) { + if (family == 4) { + processor->strmodel = g_strdup("Cyrix 5x86"); + } else if (family == 5) { + processor->strmodel = g_strdup("Cyrix M1 (6x86)"); + } else if (family == 6) { + if (model == 0) { + processor->strmodel = g_strdup("Cyrix M2 (6x86MX)"); + } else if (model <= 5) { + processor->strmodel = g_strdup("VIA Cyrix III (M2 core)"); + } else if (model == 6) { + processor->strmodel = g_strdup("VIA Cyrix III (WinChip C5A)"); + } else if (model == 7) { + processor->strmodel = g_strdup("VIA Cyrix III (WinChip C5B/C)"); + } else { + processor->strmodel = g_strdup("VIA Cyrix III (WinChip C5C-T)"); + } + } else { + processor->strmodel = g_strdup("Cyrix i386 class"); + } + } else if (g_str_equal(processor->vendor_id, "CentaurHauls")) { + if (family == 5) { + if (model <= 4) { + processor->strmodel = g_strdup("Centaur WinChip C6"); + } else if (model <= 8) { + processor->strmodel = g_strdup("Centaur WinChip 2"); + } else { + processor->strmodel = g_strdup("Centaur WinChip 2A"); + } + } else { + processor->strmodel = g_strdup("Centaur i386 class"); + } + } else if (g_str_equal(processor->vendor_id, "GenuineTMx86")) { + processor->strmodel = g_strdup("Transmeta Crusoe TM3x00/5x00"); + } else { + processor->strmodel = g_strdup("Unknown"); + } +} + +static gchar *__cache_get_info_as_string(Processor *processor) +{ + gchar *result = g_strdup(""); + GSList *cache_list; + ProcessorCache *cache; + + if (!processor->cache) { + return g_strdup(_("Cache information not available=\n")); + } + + for (cache_list = processor->cache; cache_list; cache_list = cache_list->next) { + cache = (ProcessorCache *)cache_list->data; + + result = h_strdup_cprintf(_("Level %d (%s)=%d-way set-associative, %d sets, %dKB size\n"), + result, + cache->level, + C_("cache-type", cache->type), + cache->ways_of_associativity, + cache->number_of_sets, + cache->size); + } + + return result; +} + +/* This is not used directly, but creates translatable strings for + * the type string returned from /sys/.../cache */ +static const char* cache_types[] = { + NC_("cache-type", /*/cache type, as appears in: Level 1 (Data)*/ "Data"), + NC_("cache-type", /*/cache type, as appears in: Level 1 (Instruction)*/ "Instruction"), + NC_("cache-type", /*/cache type, as appears in: Level 2 (Unified)*/ "Unified") +}; + +static void __cache_obtain_info(Processor *processor) +{ + ProcessorCache *cache; + gchar *endpoint, *entry, *index; + gchar *uref = NULL; + gint i; + gint processor_number = processor->id; + + endpoint = g_strdup_printf("/sys/devices/system/cpu/cpu%d/cache", processor_number); + + for (i = 0; ; i++) { + cache = g_new0(ProcessorCache, 1); + + index = g_strdup_printf("index%d/", i); + + entry = g_strconcat(index, "type", NULL); + cache->type = h_sysfs_read_string(endpoint, entry); + g_free(entry); + + if (!cache->type) { + g_free(cache); + g_free(index); + goto fail; + } + + entry = g_strconcat(index, "level", NULL); + cache->level = h_sysfs_read_int(endpoint, entry); + g_free(entry); + + entry = g_strconcat(index, "number_of_sets", NULL); + cache->number_of_sets = h_sysfs_read_int(endpoint, entry); + g_free(entry); + + entry = g_strconcat(index, "physical_line_partition", NULL); + cache->physical_line_partition = h_sysfs_read_int(endpoint, entry); + g_free(entry); + + entry = g_strconcat(index, "size", NULL); + cache->size = h_sysfs_read_int(endpoint, entry); + g_free(entry); + + entry = g_strconcat(index, "ways_of_associativity", NULL); + cache->ways_of_associativity = h_sysfs_read_int(endpoint, entry); + g_free(entry); + + /* unique cache references: id is nice, but share_cpu_list can be + * used if it is not available. */ + entry = g_strconcat(index, "id", NULL); + uref = h_sysfs_read_string(endpoint, entry); + g_free(entry); + if (uref != NULL && *uref != 0 ) + cache->uid = atoi(uref); + else + cache->uid = -1; + g_free(uref); + entry = g_strconcat(index, "shared_cpu_list", NULL); + cache->shared_cpu_list = h_sysfs_read_string(endpoint, entry); + g_free(entry); + + /* reacharound */ + entry = g_strconcat(index, "../../topology/physical_package_id", NULL); + cache->phy_sock = h_sysfs_read_int(endpoint, entry); + g_free(entry); + + g_free(index); + + processor->cache = g_slist_append(processor->cache, cache); + } + +fail: + g_free(endpoint); +} + +#define khzint_to_mhzdouble(k) (((double)k)/1000) +#define cmp_clocks_test(f) if (a->f < b->f) return -1; if (a->f > b->f) return 1; + +static gint cmp_cpufreq_data(cpufreq_data *a, cpufreq_data *b) { + gint i = 0; + i = g_strcmp0(a->shared_list, b->shared_list); if (i!=0) return i; + cmp_clocks_test(cpukhz_max); + cmp_clocks_test(cpukhz_min); + return 0; +} + +static gint cmp_cpufreq_data_ignore_affected(cpufreq_data *a, cpufreq_data *b) { + gint i = 0; + cmp_clocks_test(cpukhz_max); + cmp_clocks_test(cpukhz_min); + return 0; +} + +gchar *clocks_summary(GSList * processors) +{ + gchar *ret = g_strdup_printf("[%s]\n", _("Clocks")); + GSList *all_clocks = NULL, *uniq_clocks = NULL; + GSList *tmp, *l; + Processor *p; + cpufreq_data *c, *cur = NULL; + gint cur_count = 0, i = 0; + + /* create list of all clock references */ + for (l = processors; l; l = l->next) { + p = (Processor*)l->data; + if (p->cpufreq && p->cpufreq->cpukhz_max > 0) { + all_clocks = g_slist_prepend(all_clocks, p->cpufreq); + } + } + + if (g_slist_length(all_clocks) == 0) { + ret = h_strdup_cprintf("%s=\n", ret, _("(Not Available)") ); + g_slist_free(all_clocks); + return ret; + } + + /* ignore duplicate references */ + all_clocks = g_slist_sort(all_clocks, (GCompareFunc)cmp_cpufreq_data); + for (l = all_clocks; l; l = l->next) { + c = (cpufreq_data*)l->data; + if (!cur) { + cur = c; + } else { + if (cmp_cpufreq_data(cur, c) != 0) { + uniq_clocks = g_slist_prepend(uniq_clocks, cur); + cur = c; + } + } + } + uniq_clocks = g_slist_prepend(uniq_clocks, cur); + uniq_clocks = g_slist_reverse(uniq_clocks); + cur = 0, cur_count = 0; + + /* count and list clocks */ + for (l = uniq_clocks; l; l = l->next) { + c = (cpufreq_data*)l->data; + if (!cur) { + cur = c; + cur_count = 1; + } else { + if (cmp_cpufreq_data_ignore_affected(cur, c) != 0) { + ret = h_strdup_cprintf(_("%.2f-%.2f %s=%dx\n"), + ret, + khzint_to_mhzdouble(cur->cpukhz_min), + khzint_to_mhzdouble(cur->cpukhz_max), + _("MHz"), + cur_count); + cur = c; + cur_count = 1; + } else { + cur_count++; + } + } + } + ret = h_strdup_cprintf(_("%.2f-%.2f %s=%dx\n"), + ret, + khzint_to_mhzdouble(cur->cpukhz_min), + khzint_to_mhzdouble(cur->cpukhz_max), + _("MHz"), + cur_count); + + g_slist_free(all_clocks); + g_slist_free(uniq_clocks); + return ret; +} + +#define cmp_cache_test(f) if (a->f < b->f) return -1; if (a->f > b->f) return 1; + +static gint cmp_cache(ProcessorCache *a, ProcessorCache *b) { + gint i = 0; + cmp_cache_test(phy_sock); + i = g_strcmp0(a->type, b->type); if (i!=0) return i; + cmp_cache_test(level); + cmp_cache_test(size); + cmp_cache_test(uid); /* uid is unique among caches with the same (type, level) */ + if (a->uid == -1) { + /* if id wasn't available, use shared_cpu_list as a unique ref */ + i = g_strcmp0(a->shared_cpu_list, b->shared_cpu_list); if (i!=0) + return i; + } + return 0; +} + +static gint cmp_cache_ignore_id(ProcessorCache *a, ProcessorCache *b) { + gint i = 0; + cmp_cache_test(phy_sock); + i = g_strcmp0(a->type, b->type); if (i!=0) return i; + cmp_cache_test(level); + cmp_cache_test(size); + return 0; +} + +gchar *caches_summary(GSList * processors) +{ + gchar *ret = g_strdup_printf("[%s]\n", _("Caches")); + GSList *all_cache = NULL, *uniq_cache = NULL; + GSList *tmp, *l; + Processor *p; + ProcessorCache *c, *cur = NULL; + gint cur_count = 0, i = 0; + + /* create list of all cache references */ + for (l = processors; l; l = l->next) { + p = (Processor*)l->data; + if (p->cache) { + tmp = g_slist_copy(p->cache); + if (all_cache) { + all_cache = g_slist_concat(all_cache, tmp); + } else { + all_cache = tmp; + } + } + } + + if (g_slist_length(all_cache) == 0) { + ret = h_strdup_cprintf("%s=\n", ret, _("(Not Available)") ); + g_slist_free(all_cache); + return ret; + } + + /* ignore duplicate references */ + all_cache = g_slist_sort(all_cache, (GCompareFunc)cmp_cache); + for (l = all_cache; l; l = l->next) { + c = (ProcessorCache*)l->data; + if (!cur) { + cur = c; + } else { + if (cmp_cache(cur, c) != 0) { + uniq_cache = g_slist_prepend(uniq_cache, cur); + cur = c; + } + } + } + uniq_cache = g_slist_prepend(uniq_cache, cur); + uniq_cache = g_slist_reverse(uniq_cache); + cur = 0, cur_count = 0; + + /* count and list caches */ + for (l = uniq_cache; l; l = l->next) { + c = (ProcessorCache*)l->data; + if (!cur) { + cur = c; + cur_count = 1; + } else { + if (cmp_cache_ignore_id(cur, c) != 0) { + ret = h_strdup_cprintf(_("Level %d (%s)#%d=%dx %dKB (%dKB), %d-way set-associative, %d sets\n"), + ret, + cur->level, + C_("cache-type", cur->type), + cur->phy_sock, + cur_count, + cur->size, + cur->size * cur_count, + cur->ways_of_associativity, + cur->number_of_sets); + cur = c; + cur_count = 1; + } else { + cur_count++; + } + } + } + ret = h_strdup_cprintf(_("Level %d (%s)#%d=%dx %dKB (%dKB), %d-way set-associative, %d sets\n"), + ret, + cur->level, + C_("cache-type", cur->type), + cur->phy_sock, + cur_count, + cur->size, + cur->size * cur_count, + cur->ways_of_associativity, + cur->number_of_sets); + + g_slist_free(all_cache); + g_slist_free(uniq_cache); + return ret; +} + +#define PROC_SCAN_READ_BUFFER_SIZE 1024 +GSList *processor_scan(void) +{ + GSList *procs = NULL, *l = NULL; + Processor *processor = NULL; + FILE *cpuinfo; + gchar *buffer; + + buffer = g_malloc(PROC_SCAN_READ_BUFFER_SIZE); + cpuinfo = fopen(PROC_CPUINFO, "r"); + if (!cpuinfo) + return NULL; + + while (fgets(buffer, PROC_SCAN_READ_BUFFER_SIZE, cpuinfo)) { + int rlen = strlen(buffer); + if (rlen >= PROC_SCAN_READ_BUFFER_SIZE - 1) { + fprintf(stderr, "Warning: truncated a line (probably flags list) longer than %d bytes while reading %s.\n", PROC_SCAN_READ_BUFFER_SIZE, PROC_CPUINFO); + } + gchar **tmp = g_strsplit(buffer, ":", 2); + if (!tmp[1] || !tmp[0]) { + g_strfreev(tmp); + continue; + } + + tmp[0] = g_strstrip(tmp[0]); + tmp[1] = g_strstrip(tmp[1]); + + if (g_str_has_prefix(tmp[0], "processor")) { + /* finish previous */ + if (processor) + procs = g_slist_append(procs, processor); + + /* start next */ + processor = g_new0(Processor, 1); + processor->id = atol(tmp[1]); + g_strfreev(tmp); + continue; + } + + if (processor) { + get_str("model name", processor->model_name); + get_str("vendor_id", processor->vendor_id); + get_str("flags", processor->flags); + get_str("bugs", processor->bugs); + get_str("power management", processor->pm); + get_str("microcode", processor->microcode); + get_int("cache size", processor->cache_size); + get_float("cpu MHz", processor->cpu_mhz); + get_float("bogomips", processor->bogomips); + + get_str("fpu", processor->has_fpu); + + get_str("fdiv_bug", processor->bug_fdiv); + get_str("hlt_bug", processor->bug_hlt); + get_str("f00f_bug", processor->bug_f00f); + get_str("coma_bug", processor->bug_coma); + /* sep_bug? */ + + get_int("model", processor->model); + get_int("cpu family", processor->family); + get_int("stepping", processor->stepping); + } + g_strfreev(tmp); + } + + fclose(cpuinfo); + g_free(buffer); + + /* finish last */ + if (processor) + procs = g_slist_append(procs, processor); + + for (l = procs; l; l = l->next) { + processor = (Processor *) l->data; + + STRIFNULL(processor->microcode, _("(Not Available)") ); + + get_processor_strfamily(processor); + __cache_obtain_info(processor); + +#define NULLIFNOTYES(f) if (processor->f) if (strcmp(processor->f, "yes") != 0) { g_free(processor->f); processor->f = NULL; } + NULLIFNOTYES(bug_fdiv); + NULLIFNOTYES(bug_hlt); + NULLIFNOTYES(bug_f00f); + NULLIFNOTYES(bug_coma); + + if (processor->bugs == NULL || g_strcmp0(processor->bugs, "") == 0) { + g_free(processor->bugs); + /* make bugs list on old kernels that don't offer one */ + processor->bugs = g_strdup_printf("%s%s%s%s%s%s%s%s%s%s", + /* the oldest bug workarounds indicated in /proc/cpuinfo */ + processor->bug_fdiv ? " fdiv" : "", + processor->bug_hlt ? " _hlt" : "", + processor->bug_f00f ? " f00f" : "", + processor->bug_coma ? " coma" : "", + /* these bug workarounds were reported as "features" in older kernels */ + processor_has_flag(processor->flags, "fxsave_leak") ? " fxsave_leak" : "", + processor_has_flag(processor->flags, "clflush_monitor") ? " clflush_monitor" : "", + processor_has_flag(processor->flags, "11ap") ? " 11ap" : "", + processor_has_flag(processor->flags, "tlb_mmatch") ? " tlb_mmatch" : "", + processor_has_flag(processor->flags, "apic_c1e") ? " apic_c1e" : "", + ""); /* just to make adding lines easier */ + g_strchug(processor->bugs); + } + + if (processor->pm == NULL || g_strcmp0(processor->pm, "") == 0) { + g_free(processor->pm); + /* make power management list on old kernels that don't offer one */ + processor->pm = g_strdup_printf("%s%s", + /* "hw_pstate" -> "hwpstate" */ + processor_has_flag(processor->flags, "hw_pstate") ? " hwpstate" : "", + ""); /* just to make adding lines easier */ + g_strchug(processor->pm); + } + + /* topo & freq */ + processor->cpufreq = cpufreq_new(processor->id); + processor->cputopo = cputopo_new(processor->id); + + if (processor->cpufreq->cpukhz_max) + processor->cpu_mhz = processor->cpufreq->cpukhz_max / 1000; + + nice_name_x86_cpuid_model_string(processor->model_name); + } + + return procs; +} + +gchar *processor_get_capabilities_from_flags(gchar *strflags, gchar *lookup_prefix) +{ + gchar **flags, **old; + gchar tmp_flag[64] = ""; + const gchar *meaning; + gchar *tmp = NULL; + gint j = 0, i = 0; + + flags = g_strsplit(strflags, " ", 0); + old = flags; + + while (flags[j]) { + if ( sscanf(flags[j], "[%d]", &i)==1 ) { + /* Some flags are indexes, like [13], and that looks like + * a new section to hardinfo shell */ + tmp = h_strdup_cprintf("(%s%d)=\n", tmp, + (lookup_prefix) ? lookup_prefix : "", + i ); + } else { + sprintf(tmp_flag, "%s%s", lookup_prefix, flags[j]); + meaning = x86_flag_meaning(tmp_flag); + + if (meaning) { + tmp = h_strdup_cprintf("%s=%s\n", tmp, flags[j], meaning); + } else { + tmp = h_strdup_cprintf("%s=\n", tmp, flags[j]); + } + } + j++; + } + if (tmp == NULL || g_strcmp0(tmp, "") == 0) + tmp = g_strdup_printf("%s=%s\n", "empty", _("Empty List")); + + g_strfreev(old); + return tmp; +} + +gchar *processor_get_detailed_info(Processor * processor) +{ + gchar *tmp_flags, *tmp_bugs, *tmp_pm, *tmp_cpufreq, *tmp_topology, *ret, *cache_info; + + tmp_flags = processor_get_capabilities_from_flags(processor->flags, ""); + tmp_bugs = processor_get_capabilities_from_flags(processor->bugs, "bug:"); + tmp_pm = processor_get_capabilities_from_flags(processor->pm, "pm:"); + cache_info = __cache_get_info_as_string(processor); + + tmp_topology = cputopo_section_str(processor->cputopo); + tmp_cpufreq = cpufreq_section_str(processor->cpufreq); + + ret = g_strdup_printf("[%s]\n" + "%s=%s\n" + "%s=%d, %d, %d (%s)\n" /* family, model, stepping (decoded name) */ + "$^$%s=%s\n" /* vendor */ + "%s=%s\n" /* microcode */ + "[%s]\n" /* configuration */ + "%s=%d %s\n" /* cache size (from cpuinfo) */ + "%s=%.2f %s\n" /* frequency */ + "%s=%.2f\n" /* bogomips */ + "%s=%s\n" /* byte order */ + "%s" /* topology */ + "%s" /* frequency scaling */ + "[%s]\n" /* cache */ + "%s\n" + "[%s]\n" /* pm */ + "%s" + "[%s]\n" /* bugs */ + "%s" + "[%s]\n" /* flags */ + "%s", + _("Processor"), + _("Model Name"), processor->model_name, + _("Family, model, stepping"), + processor->family, + processor->model, + processor->stepping, + processor->strmodel, + _("Vendor"), processor->vendor_id, + _("Microcode Version"), processor->microcode, + _("Configuration"), + _("Cache Size"), processor->cache_size, _("kb"), + _("Frequency"), processor->cpu_mhz, _("MHz"), + _("BogoMips"), processor->bogomips, + _("Byte Order"), byte_order_str(), + tmp_topology, + tmp_cpufreq, + _("Cache"), cache_info, + _("Power Management"), tmp_pm, + _("Bug Workarounds"), tmp_bugs, + _("Capabilities"), tmp_flags ); + g_free(tmp_flags); + g_free(tmp_bugs); + g_free(tmp_pm); + g_free(cache_info); + g_free(tmp_cpufreq); + g_free(tmp_topology); + return ret; +} + +gchar *processor_name(GSList * processors) { + return processor_name_default(processors); +} + +gchar *processor_describe(GSList * processors) { + return processor_describe_default(processors); +} + +gchar *dmi_socket_info() { + gchar *ret; + dmi_type dt = 4; + guint i; + dmi_handle_list *hl = dmidecode_handles(&dt); + + if (!hl) { + ret = g_strdup_printf("[%s]\n%s=%s\n", + _("Socket Information"), _("Result"), + (getuid() == 0) + ? _("(Not available)") + : _("(Not available; Perhaps try running HardInfo as root.)") ); + } else { + ret = g_strdup(""); + for(i = 0; i < hl->count; i++) { + dmi_handle h = hl->handles[i]; + gchar *upgrade = dmidecode_match("Upgrade", &dt, &h); + gchar *socket = dmidecode_match("Socket Designation", &dt, &h); + gchar *bus_clock_str = dmidecode_match("External Clock", &dt, &h); + gchar *voltage_str = dmidecode_match("Voltage", &dt, &h); + gchar *max_speed_str = dmidecode_match("Max Speed", &dt, &h); + + ret = h_strdup_cprintf("[%s (%d) %s]\n" + "%s=0x%x\n" + "%s=%s\n" + "%s=%s\n" + "%s=%s\n" + "%s=%s\n", + ret, + _("CPU Socket"), i, socket, + _("DMI Handle"), h, + _("Type"), upgrade, + _("Voltage"), voltage_str, + _("External Clock"), bus_clock_str, + _("Max Frequency"), max_speed_str + ); + g_free(upgrade); + g_free(socket); + g_free(bus_clock_str); + g_free(voltage_str); + g_free(max_speed_str); + } + + dmi_handle_list_free(hl); + } + + return ret; +} + +gchar *processor_meta(GSList * processors) { + gchar *meta_cpu_name = processor_name(processors); + gchar *meta_cpu_desc = processor_describe(processors); + gchar *meta_freq_desc = processor_frequency_desc(processors); + gchar *meta_clocks = clocks_summary(processors); + gchar *meta_caches = caches_summary(processors); + gchar *meta_dmi = dmi_socket_info(); + gchar *ret = NULL; + UNKIFNULL(meta_cpu_desc); + ret = g_strdup_printf("[%s]\n" + "%s=%s\n" + "%s=%s\n" + "%s=%s\n" + "%s" + "%s" + "%s", + _("Package Information"), + _("Name"), meta_cpu_name, + _("Topology"), meta_cpu_desc, + _("Logical CPU Config"), meta_freq_desc, + meta_clocks, + meta_caches, + meta_dmi); + g_free(meta_cpu_desc); + g_free(meta_freq_desc); + g_free(meta_clocks); + g_free(meta_caches); + return ret; +} + +gchar *processor_get_info(GSList * processors) +{ + Processor *processor; + gchar *ret, *tmp, *hashkey; + gchar *meta; /* becomes owned by more_info? no need to free? */ + GSList *l; + gchar *icons=g_strdup(""); + + tmp = g_strdup_printf("$!CPU_META$%s=|Summary\n", "all"); + + meta = processor_meta(processors); + moreinfo_add_with_prefix("DEV", "CPU_META", meta); + + for (l = processors; l; l = l->next) { + processor = (Processor *) l->data; + + gchar *model_name = g_strdup(processor->model_name); + const Vendor *v = vendor_match(processor->vendor_id, NULL); + if (v) + tag_vendor(&model_name, 0, v->name_short ? v->name_short : v->name, v->ansi_color, params.fmt_opts); + + // bp: not convinced it looks good, but here's how it would be done... + //icons = h_strdup_cprintf("Icon$CPU%d$cpu%d=processor.png\n", icons, processor->id, processor->id); + + tmp = g_strdup_printf("%s$CPU%d$cpu%d=%.2f %s|%s|%d:%d\n", + tmp, processor->id, + processor->id, + processor->cpu_mhz, _("MHz"), + model_name, + processor->cputopo->socket_id, + processor->cputopo->core_id); + + hashkey = g_strdup_printf("CPU%d", processor->id); + moreinfo_add_with_prefix("DEV", hashkey, + processor_get_detailed_info(processor)); + g_free(hashkey); + g_free(model_name); + } + + ret = g_strdup_printf("[$ShellParam$]\n" + "ViewType=1\n" + "ColumnTitle$TextValue=%s\n" + "ColumnTitle$Value=%s\n" + "ColumnTitle$Extra1=%s\n" + "ColumnTitle$Extra2=%s\n" + "ShowColumnHeaders=true\n" + "%s" + "[Processors]\n" + "%s", _("Device"), _("Frequency"), _("Model"), _("Socket:Core"), icons, tmp); + g_free(tmp); + g_free(icons); + + // now here's something fun... + struct Info *i = info_unflatten(ret); + g_free(ret); + ret = info_flatten(i); + + return ret; +} + diff --git a/modules/devices/x86/x86_data.c b/modules/devices/x86/x86_data.c new file mode 100644 index 00000000..f56e8668 --- /dev/null +++ b/modules/devices/x86/x86_data.c @@ -0,0 +1,400 @@ +/* + * 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 <json-glib/json-glib.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include "x86_data.h" + +#ifndef C_ +#define C_(Ctx, String) String +#endif +#ifndef NC_ +#define NC_(Ctx, String) String +#endif + +/* sources: + * https://unix.stackexchange.com/a/43540 + * https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git/tree/arch/x86/include/asm/cpufeatures.h?id=refs/tags/v4.9 + * hardinfo: modules/devices/x86/processor.c + */ + +struct flag_to_meaning { + char *name; + char *meaning; +}; + +static const struct flag_to_meaning builtin_tab_flag_meaning[] = { +/* Intel-defined CPU features, CPUID level 0x00000001 (edx) + * See also Wikipedia and table 2-27 in Intel Advanced Vector Extensions Programming Reference */ + { "fpu", NC_("x86-flag", /*/flag:fpu*/ "Onboard FPU (floating point support)") }, + { "vme", NC_("x86-flag", /*/flag:vme*/ "Virtual 8086 mode enhancements") }, + { "de", NC_("x86-flag", /*/flag:de*/ "Debugging Extensions (CR4.DE)") }, + { "pse", NC_("x86-flag", /*/flag:pse*/ "Page Size Extensions (4MB memory pages)") }, + { "tsc", NC_("x86-flag", /*/flag:tsc*/ "Time Stamp Counter (RDTSC)") }, + { "msr", NC_("x86-flag", /*/flag:msr*/ "Model-Specific Registers (RDMSR, WRMSR)") }, + { "pae", NC_("x86-flag", /*/flag:pae*/ "Physical Address Extensions (support for more than 4GB of RAM)") }, + { "mce", NC_("x86-flag", /*/flag:mce*/ "Machine Check Exception") }, + { "cx8", NC_("x86-flag", /*/flag:cx8*/ "CMPXCHG8 instruction (64-bit compare-and-swap)") }, + { "apic", NC_("x86-flag", /*/flag:apic*/ "Onboard APIC") }, + { "sep", NC_("x86-flag", /*/flag:sep*/ "SYSENTER/SYSEXIT") }, + { "mtrr", NC_("x86-flag", /*/flag:mtrr*/ "Memory Type Range Registers") }, + { "pge", NC_("x86-flag", /*/flag:pge*/ "Page Global Enable (global bit in PDEs and PTEs)") }, + { "mca", NC_("x86-flag", /*/flag:mca*/ "Machine Check Architecture") }, + { "cmov", NC_("x86-flag", /*/flag:cmov*/ "CMOV instructions (conditional move) (also FCMOV)") }, + { "pat", NC_("x86-flag", /*/flag:pat*/ "Page Attribute Table") }, + { "pse36", NC_("x86-flag", /*/flag:pse36*/ "36-bit PSEs (huge pages)") }, + { "pn", NC_("x86-flag", /*/flag:pn*/ "Processor serial number") }, + { "clflush", NC_("x86-flag", /*/flag:clflush*/ "Cache Line Flush instruction") }, + { "dts", NC_("x86-flag", /*/flag:dts*/ "Debug Store (buffer for debugging and profiling instructions), or alternately: digital thermal sensor") }, + { "acpi", NC_("x86-flag", /*/flag:acpi*/ "ACPI via MSR (temperature monitoring and clock speed modulation)") }, + { "mmx", NC_("x86-flag", /*/flag:mmx*/ "Multimedia Extensions") }, + { "fxsr", NC_("x86-flag", /*/flag:fxsr*/ "FXSAVE/FXRSTOR, CR4.OSFXSR") }, + { "sse", NC_("x86-flag", /*/flag:sse*/ "Intel SSE vector instructions") }, + { "sse2", NC_("x86-flag", /*/flag:sse2*/ "SSE2") }, + { "ss", NC_("x86-flag", /*/flag:ss*/ "CPU self snoop") }, + { "ht", NC_("x86-flag", /*/flag:ht*/ "Hyper-Threading") }, + { "tm", NC_("x86-flag", /*/flag:tm*/ "Automatic clock control (Thermal Monitor)") }, + { "ia64", NC_("x86-flag", /*/flag:ia64*/ "Intel Itanium Architecture 64-bit (not to be confused with Intel's 64-bit x86 architecture with flag x86-64 or \"AMD64\" bit indicated by flag lm)") }, + { "pbe", NC_("x86-flag", /*/flag:pbe*/ "Pending Break Enable (PBE# pin) wakeup support") }, +/* AMD-defined CPU features, CPUID level 0x80000001 + * See also Wikipedia and table 2-23 in Intel Advanced Vector Extensions Programming Reference */ + { "syscall", NC_("x86-flag", /*/flag:syscall*/ "SYSCALL (Fast System Call) and SYSRET (Return From Fast System Call)") }, + { "mp", NC_("x86-flag", /*/flag:mp*/ "Multiprocessing Capable.") }, + { "nx", NC_("x86-flag", /*/flag:nx*/ "Execute Disable") }, + { "mmxext", NC_("x86-flag", /*/flag:mmxext*/ "AMD MMX extensions") }, + { "fxsr_opt", NC_("x86-flag", /*/flag:fxsr_opt*/ "FXSAVE/FXRSTOR optimizations") }, + { "pdpe1gb", NC_("x86-flag", /*/flag:pdpe1gb*/ "One GB pages (allows hugepagesz=1G)") }, + { "rdtscp", NC_("x86-flag", /*/flag:rdtscp*/ "Read Time-Stamp Counter and Processor ID") }, + { "lm", NC_("x86-flag", /*/flag:lm*/ "Long Mode (x86-64: amd64, also known as Intel 64, i.e. 64-bit capable)") }, + { "3dnow", NC_("x86-flag", /*/flag:3dnow*/ "3DNow! (AMD vector instructions, competing with Intel's SSE1)") }, + { "3dnowext", NC_("x86-flag", /*/flag:3dnowext*/ "AMD 3DNow! extensions") }, +/* Transmeta-defined CPU features, CPUID level 0x80860001 */ + { "recovery", NC_("x86-flag", /*/flag:recovery*/ "CPU in recovery mode") }, + { "longrun", NC_("x86-flag", /*/flag:longrun*/ "Longrun power control") }, + { "lrti", NC_("x86-flag", /*/flag:lrti*/ "LongRun table interface") }, +/* Other features, Linux-defined mapping */ + { "cxmmx", NC_("x86-flag", /*/flag:cxmmx*/ "Cyrix MMX extensions") }, + { "k6_mtrr", NC_("x86-flag", /*/flag:k6_mtrr*/ "AMD K6 nonstandard MTRRs") }, + { "cyrix_arr", NC_("x86-flag", /*/flag:cyrix_arr*/ "Cyrix ARRs (= MTRRs)") }, + { "centaur_mcr", NC_("x86-flag", /*/flag:centaur_mcr*/ "Centaur MCRs (= MTRRs)") }, + { "constant_tsc", NC_("x86-flag", /*/flag:constant_tsc*/ "TSC ticks at a constant rate") }, + { "up", NC_("x86-flag", /*/flag:up*/ "SMP kernel running on UP") }, + { "art", NC_("x86-flag", /*/flag:art*/ "Always-Running Timer") }, + { "arch_perfmon", NC_("x86-flag", /*/flag:arch_perfmon*/ "Intel Architectural PerfMon") }, + { "pebs", NC_("x86-flag", /*/flag:pebs*/ "Precise-Event Based Sampling") }, + { "bts", NC_("x86-flag", /*/flag:bts*/ "Branch Trace Store") }, + { "rep_good", NC_("x86-flag", /*/flag:rep_good*/ "rep microcode works well") }, + { "acc_power", NC_("x86-flag", /*/flag:acc_power*/ "AMD accumulated power mechanism") }, + { "nopl", NC_("x86-flag", /*/flag:nopl*/ "The NOPL (0F 1F) instructions") }, + { "xtopology", NC_("x86-flag", /*/flag:xtopology*/ "cpu topology enum extensions") }, + { "tsc_reliable", NC_("x86-flag", /*/flag:tsc_reliable*/ "TSC is known to be reliable") }, + { "nonstop_tsc", NC_("x86-flag", /*/flag:nonstop_tsc*/ "TSC does not stop in C states") }, + { "extd_apicid", NC_("x86-flag", /*/flag:extd_apicid*/ "has extended APICID (8 bits)") }, + { "amd_dcm", NC_("x86-flag", /*/flag:amd_dcm*/ "multi-node processor") }, + { "aperfmperf", NC_("x86-flag", /*/flag:aperfmperf*/ "APERFMPERF") }, + { "eagerfpu", NC_("x86-flag", /*/flag:eagerfpu*/ "Non lazy FPU restore") }, + { "nonstop_tsc_s3", NC_("x86-flag", /*/flag:nonstop_tsc_s3*/ "TSC doesn't stop in S3 state") }, + { "mce_recovery", NC_("x86-flag", /*/flag:mce_recovery*/ "CPU has recoverable machine checks") }, +/* Intel-defined CPU features, CPUID level 0x00000001 (ecx) + * See also Wikipedia and table 2-26 in Intel Advanced Vector Extensions Programming Reference */ + { "pni", NC_("x86-flag", /*/flag:pni*/ "SSE-3 (\"Prescott New Instructions\")") }, + { "pclmulqdq", NC_("x86-flag", /*/flag:pclmulqdq*/ "Perform a Carry-Less Multiplication of Quadword instruction - accelerator for GCM)") }, + { "dtes64", NC_("x86-flag", /*/flag:dtes64*/ "64-bit Debug Store") }, + { "monitor", NC_("x86-flag", /*/flag:monitor*/ "Monitor/Mwait support (Intel SSE3 supplements)") }, + { "ds_cpl", NC_("x86-flag", /*/flag:ds_cpl*/ "CPL Qual. Debug Store") }, + { "vmx", NC_("x86-flag", /*/flag:vmx*/ "Hardware virtualization, Intel VMX") }, + { "smx", NC_("x86-flag", /*/flag:smx*/ "Safer mode TXT (TPM support)") }, + { "est", NC_("x86-flag", /*/flag:est*/ "Enhanced SpeedStep") }, + { "tm2", NC_("x86-flag", /*/flag:tm2*/ "Thermal Monitor 2") }, + { "ssse3", NC_("x86-flag", /*/flag:ssse3*/ "Supplemental SSE-3") }, + { "cid", NC_("x86-flag", /*/flag:cid*/ "Context ID") }, + { "sdbg", NC_("x86-flag", /*/flag:sdbg*/ "silicon debug") }, + { "fma", NC_("x86-flag", /*/flag:fma*/ "Fused multiply-add") }, + { "cx16", NC_("x86-flag", /*/flag:cx16*/ "CMPXCHG16B") }, + { "xtpr", NC_("x86-flag", /*/flag:xtpr*/ "Send Task Priority Messages") }, + { "pdcm", NC_("x86-flag", /*/flag:pdcm*/ "Performance Capabilities") }, + { "pcid", NC_("x86-flag", /*/flag:pcid*/ "Process Context Identifiers") }, + { "dca", NC_("x86-flag", /*/flag:dca*/ "Direct Cache Access") }, + { "sse4_1", NC_("x86-flag", /*/flag:sse4_1*/ "SSE-4.1") }, + { "sse4_2", NC_("x86-flag", /*/flag:sse4_2*/ "SSE-4.2") }, + { "x2apic", NC_("x86-flag", /*/flag:x2apic*/ "x2APIC") }, + { "movbe", NC_("x86-flag", /*/flag:movbe*/ "Move Data After Swapping Bytes instruction") }, + { "popcnt", NC_("x86-flag", /*/flag:popcnt*/ "Return the Count of Number of Bits Set to 1 instruction (Hamming weight, i.e. bit count)") }, + { "tsc_deadline_timer", NC_("x86-flag", /*/flag:tsc_deadline_timer*/ "Tsc deadline timer") }, + { "aes/aes-ni", NC_("x86-flag", /*/flag:aes/aes-ni*/ "Advanced Encryption Standard (New Instructions)") }, + { "xsave", NC_("x86-flag", /*/flag:xsave*/ "Save Processor Extended States: also provides XGETBY,XRSTOR,XSETBY") }, + { "avx", NC_("x86-flag", /*/flag:avx*/ "Advanced Vector Extensions") }, + { "f16c", NC_("x86-flag", /*/flag:f16c*/ "16-bit fp conversions (CVT16)") }, + { "rdrand", NC_("x86-flag", /*/flag:rdrand*/ "Read Random Number from hardware random number generator instruction") }, + { "hypervisor", NC_("x86-flag", /*/flag:hypervisor*/ "Running on a hypervisor") }, +/* VIA/Cyrix/Centaur-defined CPU features, CPUID level 0xC0000001 */ + { "rng", NC_("x86-flag", /*/flag:rng*/ "Random Number Generator present (xstore)") }, + { "rng_en", NC_("x86-flag", /*/flag:rng_en*/ "Random Number Generator enabled") }, + { "ace", NC_("x86-flag", /*/flag:ace*/ "on-CPU crypto (xcrypt)") }, + { "ace_en", NC_("x86-flag", /*/flag:ace_en*/ "on-CPU crypto enabled") }, + { "ace2", NC_("x86-flag", /*/flag:ace2*/ "Advanced Cryptography Engine v2") }, + { "ace2_en", NC_("x86-flag", /*/flag:ace2_en*/ "ACE v2 enabled") }, + { "phe", NC_("x86-flag", /*/flag:phe*/ "PadLock Hash Engine") }, + { "phe_en", NC_("x86-flag", /*/flag:phe_en*/ "PHE enabled") }, + { "pmm", NC_("x86-flag", /*/flag:pmm*/ "PadLock Montgomery Multiplier") }, + { "pmm_en", NC_("x86-flag", /*/flag:pmm_en*/ "PMM enabled") }, +/* More extended AMD flags: CPUID level 0x80000001, ecx */ + { "lahf_lm", NC_("x86-flag", /*/flag:lahf_lm*/ "Load AH from Flags (LAHF) and Store AH into Flags (SAHF) in long mode") }, + { "cmp_legacy", NC_("x86-flag", /*/flag:cmp_legacy*/ "If yes HyperThreading not valid") }, + { "svm", NC_("x86-flag", /*/flag:svm*/ "\"Secure virtual machine\": AMD-V") }, + { "extapic", NC_("x86-flag", /*/flag:extapic*/ "Extended APIC space") }, + { "cr8_legacy", NC_("x86-flag", /*/flag:cr8_legacy*/ "CR8 in 32-bit mode") }, + { "abm", NC_("x86-flag", /*/flag:abm*/ "Advanced Bit Manipulation") }, + { "sse4a", NC_("x86-flag", /*/flag:sse4a*/ "SSE-4A") }, + { "misalignsse", NC_("x86-flag", /*/flag:misalignsse*/ "indicates if a general-protection exception (#GP) is generated when some legacy SSE instructions operate on unaligned data. Also depends on CR0 and Alignment Checking bit") }, + { "3dnowprefetch", NC_("x86-flag", /*/flag:3dnowprefetch*/ "3DNow prefetch instructions") }, + { "osvw", NC_("x86-flag", /*/flag:osvw*/ "indicates OS Visible Workaround, which allows the OS to work around processor errata.") }, + { "ibs", NC_("x86-flag", /*/flag:ibs*/ "Instruction Based Sampling") }, + { "xop", NC_("x86-flag", /*/flag:xop*/ "extended AVX instructions") }, + { "skinit", NC_("x86-flag", /*/flag:skinit*/ "SKINIT/STGI instructions") }, + { "wdt", NC_("x86-flag", /*/flag:wdt*/ "Watchdog timer") }, + { "lwp", NC_("x86-flag", /*/flag:lwp*/ "Light Weight Profiling") }, + { "fma4", NC_("x86-flag", /*/flag:fma4*/ "4 operands MAC instructions") }, + { "tce", NC_("x86-flag", /*/flag:tce*/ "translation cache extension") }, + { "nodeid_msr", NC_("x86-flag", /*/flag:nodeid_msr*/ "NodeId MSR") }, + { "tbm", NC_("x86-flag", /*/flag:tbm*/ "Trailing Bit Manipulation") }, + { "topoext", NC_("x86-flag", /*/flag:topoext*/ "Topology Extensions CPUID leafs") }, + { "perfctr_core", NC_("x86-flag", /*/flag:perfctr_core*/ "Core Performance Counter Extensions") }, + { "perfctr_nb", NC_("x86-flag", /*/flag:perfctr_nb*/ "NB Performance Counter Extensions") }, + { "bpext", NC_("x86-flag", /*/flag:bpext*/ "data breakpoint extension") }, + { "ptsc", NC_("x86-flag", /*/flag:ptsc*/ "performance time-stamp counter") }, + { "perfctr_l2", NC_("x86-flag", /*/flag:perfctr_l2*/ "L2 Performance Counter Extensions") }, + { "mwaitx", NC_("x86-flag", /*/flag:mwaitx*/ "MWAIT extension (MONITORX/MWAITX)") }, +/* Auxiliary flags: Linux defined - For features scattered in various CPUID levels */ + { "cpb", NC_("x86-flag", /*/flag:cpb*/ "AMD Core Performance Boost") }, + { "epb", NC_("x86-flag", /*/flag:epb*/ "IA32_ENERGY_PERF_BIAS support") }, + { "hw_pstate", NC_("x86-flag", /*/flag:hw_pstate*/ "AMD HW-PState") }, + { "proc_feedback", NC_("x86-flag", /*/flag:proc_feedback*/ "AMD ProcFeedbackInterface") }, + { "intel_pt", NC_("x86-flag", /*/flag:intel_pt*/ "Intel Processor Tracing") }, +/* Virtualization flags: Linux defined */ + { "tpr_shadow", NC_("x86-flag", /*/flag:tpr_shadow*/ "Intel TPR Shadow") }, + { "vnmi", NC_("x86-flag", /*/flag:vnmi*/ "Intel Virtual NMI") }, + { "flexpriority", NC_("x86-flag", /*/flag:flexpriority*/ "Intel FlexPriority") }, + { "ept", NC_("x86-flag", /*/flag:ept*/ "Intel Extended Page Table") }, + { "vpid", NC_("x86-flag", /*/flag:vpid*/ "Intel Virtual Processor ID") }, + { "vmmcall", NC_("x86-flag", /*/flag:vmmcall*/ "prefer VMMCALL to VMCALL") }, +/* Intel-defined CPU features, CPUID level 0x00000007:0 (ebx) */ + { "fsgsbase", NC_("x86-flag", /*/flag:fsgsbase*/ "{RD/WR}{FS/GS}BASE instructions") }, + { "tsc_adjust", NC_("x86-flag", /*/flag:tsc_adjust*/ "TSC adjustment MSR") }, + { "bmi1", NC_("x86-flag", /*/flag:bmi1*/ "1st group bit manipulation extensions") }, + { "hle", NC_("x86-flag", /*/flag:hle*/ "Hardware Lock Elision") }, + { "avx2", NC_("x86-flag", /*/flag:avx2*/ "AVX2 instructions") }, + { "smep", NC_("x86-flag", /*/flag:smep*/ "Supervisor Mode Execution Protection") }, + { "bmi2", NC_("x86-flag", /*/flag:bmi2*/ "2nd group bit manipulation extensions") }, + { "erms", NC_("x86-flag", /*/flag:erms*/ "Enhanced REP MOVSB/STOSB") }, + { "invpcid", NC_("x86-flag", /*/flag:invpcid*/ "Invalidate Processor Context ID") }, + { "rtm", NC_("x86-flag", /*/flag:rtm*/ "Restricted Transactional Memory") }, + { "cqm", NC_("x86-flag", /*/flag:cqm*/ "Cache QoS Monitoring") }, + { "mpx", NC_("x86-flag", /*/flag:mpx*/ "Memory Protection Extension") }, + { "avx512f", NC_("x86-flag", /*/flag:avx512f*/ "AVX-512 foundation") }, + { "avx512dq", NC_("x86-flag", /*/flag:avx512dq*/ "AVX-512 Double/Quad instructions") }, + { "rdseed", NC_("x86-flag", /*/flag:rdseed*/ "The RDSEED instruction") }, + { "adx", NC_("x86-flag", /*/flag:adx*/ "The ADCX and ADOX instructions") }, + { "smap", NC_("x86-flag", /*/flag:smap*/ "Supervisor Mode Access Prevention") }, + { "clflushopt", NC_("x86-flag", /*/flag:clflushopt*/ "CLFLUSHOPT instruction") }, + { "clwb", NC_("x86-flag", /*/flag:clwb*/ "CLWB instruction") }, + { "avx512pf", NC_("x86-flag", /*/flag:avx512pf*/ "AVX-512 Prefetch") }, + { "avx512er", NC_("x86-flag", /*/flag:avx512er*/ "AVX-512 Exponential and Reciprocal") }, + { "avx512cd", NC_("x86-flag", /*/flag:avx512cd*/ "AVX-512 Conflict Detection") }, + { "sha_ni", NC_("x86-flag", /*/flag:sha_ni*/ "SHA1/SHA256 Instruction Extensions") }, + { "avx512bw", NC_("x86-flag", /*/flag:avx512bw*/ "AVX-512 Byte/Word instructions") }, + { "avx512vl", NC_("x86-flag", /*/flag:avx512vl*/ "AVX-512 128/256 Vector Length extensions") }, +/* Extended state features, CPUID level 0x0000000d:1 (eax) */ + { "xsaveopt", NC_("x86-flag", /*/flag:xsaveopt*/ "Optimized XSAVE") }, + { "xsavec", NC_("x86-flag", /*/flag:xsavec*/ "XSAVEC") }, + { "xgetbv1", NC_("x86-flag", /*/flag:xgetbv1*/ "XGETBV with ECX = 1") }, + { "xsaves", NC_("x86-flag", /*/flag:xsaves*/ "XSAVES/XRSTORS") }, +/* Intel-defined CPU QoS sub-leaf, CPUID level 0x0000000F:0 (edx) */ + { "cqm_llc", NC_("x86-flag", /*/flag:cqm_llc*/ "LLC QoS") }, +/* Intel-defined CPU QoS sub-leaf, CPUID level 0x0000000F:1 (edx) */ + { "cqm_occup_llc", NC_("x86-flag", /*/flag:cqm_occup_llc*/ "LLC occupancy monitoring") }, + { "cqm_mbm_total", NC_("x86-flag", /*/flag:cqm_mbm_total*/ "LLC total MBM monitoring") }, + { "cqm_mbm_local", NC_("x86-flag", /*/flag:cqm_mbm_local*/ "LLC local MBM monitoring") }, +/* AMD-defined CPU features, CPUID level 0x80000008 (ebx) */ + { "clzero", NC_("x86-flag", /*/flag:clzero*/ "CLZERO instruction") }, + { "irperf", NC_("x86-flag", /*/flag:irperf*/ "instructions retired performance counter") }, +/* Thermal and Power Management leaf, CPUID level 0x00000006 (eax) */ + { "dtherm", NC_("x86-flag", /*/flag:dtherm*/ "digital thermal sensor") }, /* formerly dts */ + { "ida", NC_("x86-flag", /*/flag:ida*/ "Intel Dynamic Acceleration") }, + { "arat", NC_("x86-flag", /*/flag:arat*/ "Always Running APIC Timer") }, + { "pln", NC_("x86-flag", /*/flag:pln*/ "Intel Power Limit Notification") }, + { "pts", NC_("x86-flag", /*/flag:pts*/ "Intel Package Thermal Status") }, + { "hwp", NC_("x86-flag", /*/flag:hwp*/ "Intel Hardware P-states") }, + { "hwp_notify", NC_("x86-flag", /*/flag:hwp_notify*/ "HWP notification") }, + { "hwp_act_window", NC_("x86-flag", /*/flag:hwp_act_window*/ "HWP Activity Window") }, + { "hwp_epp", NC_("x86-flag", /*/flag:hwp_epp*/ "HWP Energy Performance Preference") }, + { "hwp_pkg_req", NC_("x86-flag", /*/flag:hwp_pkg_req*/ "HWP package-level request") }, +/* AMD SVM Feature Identification, CPUID level 0x8000000a (edx) */ + { "npt", NC_("x86-flag", /*/flag:npt*/ "AMD Nested Page Table support") }, + { "lbrv", NC_("x86-flag", /*/flag:lbrv*/ "AMD LBR Virtualization support") }, + { "svm_lock", NC_("x86-flag", /*/flag:svm_lock*/ "AMD SVM locking MSR") }, + { "nrip_save", NC_("x86-flag", /*/flag:nrip_save*/ "AMD SVM next_rip save") }, + { "tsc_scale", NC_("x86-flag", /*/flag:tsc_scale*/ "AMD TSC scaling support") }, + { "vmcb_clean", NC_("x86-flag", /*/flag:vmcb_clean*/ "AMD VMCB clean bits support") }, + { "flushbyasid", NC_("x86-flag", /*/flag:flushbyasid*/ "AMD flush-by-ASID support") }, + { "decodeassists", NC_("x86-flag", /*/flag:decodeassists*/ "AMD Decode Assists support") }, + { "pausefilter", NC_("x86-flag", /*/flag:pausefilter*/ "AMD filtered pause intercept") }, + { "pfthreshold", NC_("x86-flag", /*/flag:pfthreshold*/ "AMD pause filter threshold") }, + { "avic", NC_("x86-flag", /*/flag:avic*/ "Virtual Interrupt Controller") }, +/* Intel-defined CPU features, CPUID level 0x00000007:0 (ecx) */ + { "pku", NC_("x86-flag", /*/flag:pku*/ "Protection Keys for Userspace") }, + { "ospke", NC_("x86-flag", /*/flag:ospke*/ "OS Protection Keys Enable") }, +/* AMD-defined CPU features, CPUID level 0x80000007 (ebx) */ + { "overflow_recov", NC_("x86-flag", /*/flag:overflow_recov*/ "MCA overflow recovery support") }, + { "succor", NC_("x86-flag", /*/flag:succor*/ "uncorrectable error containment and recovery") }, + { "smca", NC_("x86-flag", /*/flag:smca*/ "Scalable MCA") }, + +/* bug workarounds */ + { "bug:f00f", NC_("x86-flag", /*/bug:f00f*/ "Intel F00F bug") }, + { "bug:fdiv", NC_("x86-flag", /*/bug:fdiv*/ "FPU FDIV") }, + { "bug:coma", NC_("x86-flag", /*/bug:coma*/ "Cyrix 6x86 coma") }, + { "bug:tlb_mmatch", NC_("x86-flag", /*/bug:tlb_mmatch*/ "AMD Erratum 383") }, + { "bug:apic_c1e", NC_("x86-flag", /*/bug:apic_c1e*/ "AMD Erratum 400") }, + { "bug:11ap", NC_("x86-flag", /*/bug:11ap*/ "Bad local APIC aka 11AP") }, + { "bug:fxsave_leak", NC_("x86-flag", /*/bug:fxsave_leak*/ "FXSAVE leaks FOP/FIP/FOP") }, + { "bug:clflush_monitor", NC_("x86-flag", /*/bug:clflush_monitor*/ "AAI65, CLFLUSH required before MONITOR") }, + { "bug:sysret_ss_attrs", NC_("x86-flag", /*/bug:sysret_ss_attrs*/ "SYSRET doesn't fix up SS attrs") }, + { "bug:espfix", NC_("x86-flag", /*/bug:espfix*/ "IRET to 16-bit SS corrupts ESP/RSP high bits") }, + { "bug:null_seg", NC_("x86-flag", /*/bug:null_seg*/ "Nulling a selector preserves the base") }, /* see: detect_null_seg_behavior() */ + { "bug:swapgs_fence", NC_("x86-flag", /*/bug:swapgs_fence*/ "SWAPGS without input dep on GS") }, + { "bug:monitor", NC_("x86-flag", /*/bug:monitor*/ "IPI required to wake up remote CPU") }, + { "bug:amd_e400", NC_("x86-flag", /*/bug:amd_e400*/ "AMD Erratum 400") }, + { "bug:cpu_insecure", NC_("x86-flag", /*/bug:cpu_insecure & bug:cpu_meltdown*/ "CPU is affected by meltdown attack and needs kernel page table isolation") }, + { "bug:cpu_meltdown", NC_("x86-flag", /*/bug:cpu_insecure & bug:cpu_meltdown*/ "CPU is affected by meltdown attack and needs kernel page table isolation") }, + { "bug:spectre_v1", NC_("x86-flag", /*/bug:spectre_v1*/ "CPU is affected by Spectre variant 1 attack with conditional branches") }, + { "bug:spectre_v2", NC_("x86-flag", /*/bug:spectre_v2*/ "CPU is affected by Spectre variant 2 attack with indirect branches") }, + { "bug:spec_store_bypass", NC_("x86-flag", /*/bug:spec_store_bypass*/ "CPU is affected by speculative store bypass attack") }, + { "bug:l1tf", NC_("x86-flag", /*/bug:l1tf*/ "CPU is affected by L1 Terminal Fault") }, +/* power management + * ... from arch/x86/kernel/cpu/powerflags.h */ + { "pm:ts", NC_("x86-flag", /*/flag:pm:ts*/ "temperature sensor") }, + { "pm:fid", NC_("x86-flag", /*/flag:pm:fid*/ "frequency id control") }, + { "pm:vid", NC_("x86-flag", /*/flag:pm:vid*/ "voltage id control") }, + { "pm:ttp", NC_("x86-flag", /*/flag:pm:ttp*/ "thermal trip") }, + { "pm:tm", NC_("x86-flag", /*/flag:pm:tm*/ "hardware thermal control") }, + { "pm:stc", NC_("x86-flag", /*/flag:pm:stc*/ "software thermal control") }, + { "pm:100mhzsteps", NC_("x86-flag", /*/flag:pm:100mhzsteps*/ "100 MHz multiplier control") }, + { "pm:hwpstate", NC_("x86-flag", /*/flag:pm:hwpstate*/ "hardware P-state control") }, + { "pm:cpb", NC_("x86-flag", /*/flag:pm:cpb*/ "core performance boost") }, + { "pm:eff_freq_ro", NC_("x86-flag", /*/flag:pm:eff_freq_ro*/ "Readonly aperf/mperf") }, + { "pm:proc_feedback", NC_("x86-flag", /*/flag:pm:proc_feedback*/ "processor feedback interface") }, + { "pm:acc_power", NC_("x86-flag", /*/flag:pm:acc_power*/ "accumulated power mechanism") }, + { NULL, NULL}, +}; + +static struct flag_to_meaning *tab_flag_meaning; + +static char all_flags[4096] = ""; + +static void build_meaning_table_iter(JsonObject *object, + const gchar *member_name, + JsonNode *member_node, + gpointer user_data) +{ + int *i = user_data; + + tab_flag_meaning[*i] = (struct flag_to_meaning) { + .name = g_strdup(member_name), + .meaning = g_strdup(json_node_get_string(member_node)), + }; + + (*i)++; +} + +void cpuflags_x86_init(void) +{ + gchar *flag_json = g_build_filename(g_get_user_config_dir(), "hardinfo2", + "cpuflags.json", NULL); + gboolean use_builtin_table = TRUE; + + if (!g_file_test(flag_json, G_FILE_TEST_EXISTS)) + goto use_builtin_table; + + JsonParser *parser = json_parser_new(); + if (!json_parser_load_from_file(parser, flag_json, NULL)) + goto use_builtin_table_with_json; + + JsonNode *root = json_parser_get_root(parser); + if (json_node_get_node_type(root) != JSON_NODE_OBJECT) + goto use_builtin_table_with_json; + + JsonObject *x86_flags = + json_object_get_object_member(json_node_get_object(root), "x86"); + if (!x86_flags) + goto use_builtin_table_with_json; + + tab_flag_meaning = + g_new(struct flag_to_meaning, json_object_get_size(x86_flags) + 1); + int i = 0; + json_object_foreach_member(x86_flags, build_meaning_table_iter, &i); + tab_flag_meaning[i] = (struct flag_to_meaning){NULL, NULL}; + use_builtin_table = FALSE; + +use_builtin_table_with_json: + g_object_unref(parser); +use_builtin_table: + g_free(flag_json); + + if (use_builtin_table) + tab_flag_meaning = (struct flag_to_meaning *)builtin_tab_flag_meaning; +} + +const char *x86_flag_meaning(const char *flag) { + int i; + + if (!flag) + return NULL; + + for (i = 0; tab_flag_meaning[i].name; i++) { + if (strcmp(tab_flag_meaning[i].name, flag) == 0) { + if (tab_flag_meaning[i].meaning != NULL) + return C_("x86-flag", tab_flag_meaning[i].meaning); + else return NULL; + } + } + + return NULL; +} + +static void x86_flag_find_dups(void) { + int t, i; + + t = 0; + while(tab_flag_meaning[t].name != NULL) { + i = t+1; + while(tab_flag_meaning[i].name != NULL) { + if (strcmp(tab_flag_meaning[t].name, tab_flag_meaning[i].name) == 0) { + printf("x86-flag duplicate definition: %s\n ... %d: %s\n ... %d: %s\n", + tab_flag_meaning[i].name, + t, tab_flag_meaning[t].meaning, + i, tab_flag_meaning[i].meaning); + } + i++; + } + t++; + } +} diff --git a/modules/devices/x86/x86_data.h b/modules/devices/x86/x86_data.h new file mode 100644 index 00000000..66a4c80f --- /dev/null +++ b/modules/devices/x86/x86_data.h @@ -0,0 +1,28 @@ +/* + * 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. + * + */ + +#ifndef _X86DATA_H_ +#define _X86DATA_H_ + +/* cpu flags from /proc/cpuinfo */ +const char *x86_flag_list(void); /* list of all known flags */ +const char *x86_flag_meaning(const char *flag); /* lookup flag meaning */ + +#endif |