aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBurt P <pburt0@gmail.com>2018-03-17 15:48:47 -0500
committerLeandro A. F. Pereira <leandro@hardinfo.org>2018-04-24 07:43:43 -0700
commitdffc0b9dd005015f5df36448b1f2952493f5fc1e (patch)
tree8d8b21ecd24d33e62f69c3f2c1db8952073b3887
parent7bec02cb303c7fc096cdb91d5ef5d9323d728a37 (diff)
[new] pci_util: functions and data structures for pci information
Based on usb_util. Only current method is via lspci, but framework exists to add other methods. Signed-off-by: Burt P <pburt0@gmail.com>
-rw-r--r--CMakeLists.txt2
-rw-r--r--hardinfo/pci_util.c306
-rw-r--r--includes/pci_util.h72
3 files changed, 380 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index bc480146..628583b5 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -215,6 +215,7 @@ endif()
if (HARDINFO_GTK3)
add_executable(hardinfo
hardinfo/usb_util.c
+ hardinfo/pci_util.c
hardinfo/binreloc.c
hardinfo/expr.c
hardinfo/hardinfo.c
@@ -244,6 +245,7 @@ target_link_libraries(hardinfo
else()
add_executable(hardinfo
hardinfo/usb_util.c
+ hardinfo/pci_util.c
hardinfo/binreloc.c
hardinfo/expr.c
hardinfo/hardinfo.c
diff --git a/hardinfo/pci_util.c b/hardinfo/pci_util.c
new file mode 100644
index 00000000..58580694
--- /dev/null
+++ b/hardinfo/pci_util.c
@@ -0,0 +1,306 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2017 Leandro A. F. Pereira <leandro@hardinfo.org>
+ * 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.
+ *
+ * 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 "pci_util.h"
+
+pcid *pcid_new() {
+ return g_new0(pcid, 1);
+}
+
+void pcid_free(pcid *s) {
+ if (s) {
+ g_free(s->slot_str);
+ g_free(s->class_str);
+ g_free(s->vendor_id_str);
+ g_free(s->device_id_str);
+ g_free(s->sub_vendor_id_str);
+ g_free(s->sub_device_id_str);
+ g_free(s->driver);
+ g_free(s->driver_list);
+ g_free(s);
+ }
+}
+
+void pcid_list_free(pcid *s) {
+ pcid *n;
+ while(s != NULL) {
+ n = s->next;
+ pcid_free(s);
+ s = n;
+ }
+}
+
+/* returns number of items after append */
+static int pcid_list_append(pcid *l, pcid *n) {
+ int c = 0;
+ while(l != NULL) {
+ c++;
+ if (l->next == NULL) {
+ if (n != NULL) {
+ l->next = n;
+ c++;
+ }
+ break;
+ }
+ l = l->next;
+ }
+ return c;
+}
+
+int pcid_list_count(pcid *s) {
+ return pcid_list_append(s, NULL);
+}
+
+static char *lspci_line_value(char *line, const char *prefix) {
+ if (g_str_has_prefix(g_strstrip(line), prefix)) {
+ line += strlen(prefix) + 1;
+ return g_strstrip(line);
+ } else
+ return NULL;
+}
+
+/* read output line of lspci -vmmnn */
+static int lspci_line_string_and_code(char *line, char *prefix, char **str, uint32_t *code) {
+ char *l = lspci_line_value(line, prefix);
+ char buff[512] = "";
+ char *e;
+ int ec;
+
+ if (l) {
+ e = strchr(l, 0);
+ while (e > l && *e != '[') e--;
+ sscanf(e, "[%x]", code);
+ *e = 0; /* terminate at start of code */
+ if (*str) free(*str); /* free if replacing */
+ *str = strdup(g_strstrip(l));
+ }
+ return 0;
+}
+
+static gboolean pci_fill_details(pcid *s) {
+ gboolean spawned;
+ gchar *out, *err, *p, *l, *t, *next_nl;
+ gchar *pci_loc = pci_address_str(s->domain, s->bus, s->device, s->function);
+ gchar *lspci_cmd = g_strdup_printf("lspci -D -s %s -vvv", pci_loc);
+
+ spawned = g_spawn_command_line_sync(lspci_cmd,
+ &out, &err, NULL, NULL);
+ g_free(lspci_cmd);
+ g_free(pci_loc);
+ if (spawned) {
+ p = out;
+ while(next_nl = strchr(p, '\n')) {
+ strend(p, '\n');
+ g_strstrip(p);
+ if (l = lspci_line_value(p, "Kernel driver in use")) {
+ s->driver = g_strdup(l);
+ goto pci_details_next;
+ }
+ if (l = lspci_line_value(p, "Kernel modules")) {
+ s->driver_list = g_strdup(l);
+ goto pci_details_next;
+ }
+ /* TODO: more details */
+
+ pci_details_next:
+ p = next_nl + 1;
+ }
+ g_free(out);
+ g_free(err);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+char *pci_address_str(uint32_t dom, uint32_t bus, uint32_t dev, uint32_t func) {
+ return g_strdup_printf("%04x:%02x:%02x.%01x", dom, bus, dev, func);
+}
+
+/* /sys/bus/pci/devices/0000:01:00.0/ */
+char *_sysfs_bus_pci(uint32_t dom, uint32_t bus, uint32_t dev, uint32_t func, const char *item) {
+ char *ret = NULL, *pci_loc, *sysfs_path;
+ pci_loc = pci_address_str(dom, bus, dev, func);
+ sysfs_path = g_strdup_printf("%s/%s/%s", SYSFS_PCI_ROOT, pci_loc, item);
+ g_file_get_contents(sysfs_path, &ret, NULL, NULL);
+ free(pci_loc);
+ free(sysfs_path);
+ return ret;
+}
+
+gboolean _sysfs_bus_pci_read_hex(uint32_t dom, uint32_t bus, uint32_t dev, uint32_t func, const char *item, uint32_t *val) {
+ char *tmp = _sysfs_bus_pci(dom, bus, dev, func, item);
+ uint32_t tval;
+ if (tmp && val) {
+ int ec = sscanf(tmp, "%x", &tval);
+ free(tmp);
+ if (ec) {
+ *val = tval;
+ return TRUE;
+ } else
+ return FALSE;
+ }
+}
+
+/* https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-bus-pci */
+static gboolean pci_get_device_sysfs(uint32_t dom, uint32_t bus, uint32_t dev, uint32_t func, pcid *s) {
+ char *tmp = NULL;
+ int ec = 0;
+ float tf;
+ s->domain = dom;
+ s->bus = bus;
+ s->device = dev;
+ s->function = func;
+ _sysfs_bus_pci_read_hex(dom, bus, dev, func, "class", &s->class);
+ s->class >>= 8; /* TODO: find out why */
+ _sysfs_bus_pci_read_hex(dom, bus, dev, func, "device", &s->device_id);
+ _sysfs_bus_pci_read_hex(dom, bus, dev, func, "vendor", &s->vendor_id);
+ _sysfs_bus_pci_read_hex(dom, bus, dev, func, "subsystem_device", &s->sub_device_id);
+ _sysfs_bus_pci_read_hex(dom, bus, dev, func, "subsystem_vendor", &s->sub_vendor_id);
+ _sysfs_bus_pci_read_hex(dom, bus, dev, func, "revision", &s->revision);
+
+ tmp = _sysfs_bus_pci(dom, bus, dev, func, "max_link_speed");
+ if (tmp) {
+ ec = sscanf(tmp, "%f GT/s", &tf);
+ if (ec) s->pcie_speed_max = tf;
+ free(tmp);
+ }
+ tmp = _sysfs_bus_pci(dom, bus, dev, func, "current_link_speed");
+ if (tmp) {
+ ec = sscanf(tmp, "%f GT/s", &tf);
+ if (ec) s->pcie_speed_curr = tf;
+ free(tmp);
+ }
+ tmp = _sysfs_bus_pci(dom, bus, dev, func, "max_link_width");
+ if (tmp) {
+ s->pcie_width_max = strtoul(tmp, NULL, 0);
+ free(tmp);
+ }
+ tmp = _sysfs_bus_pci(dom, bus, dev, func, "max_link_width");
+ if (tmp) {
+ s->pcie_width_curr = strtoul(tmp, NULL, 0);
+ free(tmp);
+ }
+
+}
+
+static gboolean pci_get_device_lspci(uint32_t dom, uint32_t bus, uint32_t dev, uint32_t func, pcid *s) {
+ gboolean spawned;
+ gchar *out, *err, *p, *l, *t, *next_nl;
+ gchar *pci_loc = pci_address_str(dom, bus, dev, func);
+ gchar *lspci_cmd = g_strdup_printf("lspci -D -s %s -vmmnn", pci_loc);
+
+ s->domain = dom;
+ s->bus = bus;
+ s->device = dev;
+ s->function = func;
+
+ spawned = g_spawn_command_line_sync(lspci_cmd,
+ &out, &err, NULL, NULL);
+ g_free(lspci_cmd);
+ if (spawned) {
+ p = out;
+ while(next_nl = strchr(p, '\n')) {
+ strend(p, '\n');
+ g_strstrip(p);
+ if (l = lspci_line_value(p, "Slot")) {
+ s->slot_str = g_strdup(l);
+ if (strcmp(s->slot_str, pci_loc) != 0) {
+ printf("PCI: %s != %s\n", s->slot_str, pci_loc);
+ }
+ }
+ if (l = lspci_line_value(p, "Rev")) {
+ s->revision = strtol(l, NULL, 0);
+ }
+ lspci_line_string_and_code(p, "Class", &s->class_str, &s->class);
+ lspci_line_string_and_code(p, "Vendor", &s->vendor_id_str, &s->vendor_id);
+ lspci_line_string_and_code(p, "Device", &s->device_id_str, &s->device_id);
+ lspci_line_string_and_code(p, "SVendor", &s->sub_vendor_id_str, &s->sub_vendor_id);
+ lspci_line_string_and_code(p, "SDevice", &s->sub_device_id_str, &s->sub_device_id);
+
+ p = next_nl + 1;
+ }
+ g_free(out);
+ g_free(err);
+ g_free(pci_loc);
+ return TRUE;
+ }
+ g_free(pci_loc);
+ return FALSE;
+}
+
+pcid *pci_get_device(uint32_t dom, uint32_t bus, uint32_t dev, uint32_t func) {
+ pcid *s = pcid_new();
+ int ok = 0;
+ if (s) {
+ ok = pci_get_device_sysfs(dom, bus, dev, func, s);
+ ok |= pci_get_device_lspci(dom, bus, dev, func, s);
+ /* TODO: other methods */
+
+ if (!ok) {
+ pcid_free(s);
+ s = NULL;
+ }
+ }
+ return s;
+}
+
+static pcid *pci_get_device_list_lspci(uint32_t class_min, uint32_t class_max) {
+ gboolean spawned;
+ gchar *out, *err, *p, *next_nl;
+ pcid *head = NULL, *nd;
+ uint32_t dom, bus, dev, func, cls;
+ int ec;
+
+ if (class_max == 0) class_max = 0xffff;
+
+ spawned = g_spawn_command_line_sync("lspci -D -mn",
+ &out, &err, NULL, NULL);
+ if (spawned) {
+ p = out;
+ while(next_nl = strchr(p, '\n')) {
+ strend(p, '\n');
+ ec = sscanf(p, "%x:%x:%x.%x \"%x\"", &dom, &bus, &dev, &func, &cls);
+ if (ec == 5) {
+ if (cls >= class_min && cls <= class_max) {
+ nd = pci_get_device(dom, bus, dev, func);
+ pci_fill_details(nd);
+
+ if (head == NULL) {
+ head = nd;
+ } else {
+ pcid_list_append(head, nd);
+ }
+ }
+ }
+ p = next_nl + 1;
+ }
+ g_free(out);
+ g_free(err);
+ }
+ return head;
+}
+
+pcid *pci_get_device_list(uint32_t class_min, uint32_t class_max) {
+ /* try lspci */
+ return pci_get_device_list_lspci(class_min, class_max);
+ /* TODO: other methods */
+}
+
diff --git a/includes/pci_util.h b/includes/pci_util.h
new file mode 100644
index 00000000..827f6197
--- /dev/null
+++ b/includes/pci_util.h
@@ -0,0 +1,72 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2017 Leandro A. F. Pereira <leandro@hardinfo.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef __PCI_UTIL_H__
+#define __PCI_UTIL_H__
+
+#include <stdint.h>
+
+#define SYSFS_PCI_ROOT "/sys/bus/pci/devices"
+
+char *pci_address_str(uint32_t dom, uint32_t bus, uint32_t dev, uint32_t func);
+
+typedef struct pcid {
+ uint32_t domain;
+ uint32_t bus;
+ uint32_t device;
+ uint32_t function;
+ uint32_t class;
+ uint32_t vendor_id;
+ uint32_t device_id;
+ uint32_t sub_vendor_id;
+ uint32_t sub_device_id;
+ uint32_t revision;
+ char *slot_str;
+ char *class_str;
+ char *vendor_id_str;
+ char *device_id_str;
+ char *sub_vendor_id_str;
+ char *sub_device_id_str;
+
+ char *driver; /* Kernel driver in use */
+ char *driver_list; /* Kernel modules */
+
+ float pcie_speed_max; /* GT/s */
+ float pcie_speed_curr; /* GT/s */
+ uint32_t pcie_width_max;
+ uint32_t pcie_width_curr;
+
+ /* ... */
+
+ struct pcid *next; /* this is a linked list */
+} pcid;
+
+/* examples:
+ * to get all pci devices:
+ * pcid *list = pci_get_device_list(0, 0);
+ * to get all display controllers:
+ * pcid *list = pci_get_device_list(0x300, 0x3ff);
+ */
+pcid *pci_get_device_list(uint32_t class_min, uint32_t class_max);
+int pcid_list_count(pcid *);
+void pcid_list_free(pcid *);
+
+pcid *pci_get_device(uint32_t dom, uint32_t bus, uint32_t dev, uint32_t func);
+void pcid_free(pcid *);
+
+#endif