All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/2] lspci: Add support of JSON output format
@ 2018-02-14 16:07 Viktor Prutyanov
  2018-02-14 16:07 ` [PATCH v2 1/2] lspci: Add printing info in JSON format Viktor Prutyanov
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: Viktor Prutyanov @ 2018-02-14 16:07 UTC (permalink / raw)
  To: linux-pci, mj; +Cc: Viktor Prutyanov

This collection of patches adds support of printing PCI info in JSON format

Viktor Prutyanov (2):
  lspci: Add printing info in JSON format
  lspci: Add PCI info output in JSON format

 Makefile    |   3 +-
 common.c    |   2 +-
 ls-info.c   | 328 ++++++++++++++++++++++++++++
 ls-kernel.c |  24 +++
 lspci.c     | 701 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 lspci.h     |  55 +++++
 lspci.man   |   3 +
 pciutils.h  |   2 +-
 8 files changed, 1114 insertions(+), 4 deletions(-)
 create mode 100644 ls-info.c

-- 
2.14.1

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH v2 1/2] lspci: Add printing info in JSON format
  2018-02-14 16:07 [PATCH v2 0/2] lspci: Add support of JSON output format Viktor Prutyanov
@ 2018-02-14 16:07 ` Viktor Prutyanov
  2018-02-14 16:07 ` [PATCH v2 2/2] lspci: Add PCI info output " Viktor Prutyanov
  2018-02-15 10:32 ` [PATCH v2 0/2] lspci: Add support of JSON output format Martin Mares
  2 siblings, 0 replies; 6+ messages in thread
From: Viktor Prutyanov @ 2018-02-14 16:07 UTC (permalink / raw)
  To: linux-pci, mj; +Cc: Viktor Prutyanov

This patch adds saving objects and lists and printing them in JSON format

Signed-off-by: Viktor Prutyanov <viktor.prutyanov@virtuozzo.com>
---
 common.c   |   2 +-
 ls-info.c  | 328 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 lspci.h    |  54 ++++++++++
 pciutils.h |   2 +-
 4 files changed, 384 insertions(+), 2 deletions(-)
 create mode 100644 ls-info.c

diff --git a/common.c b/common.c
index 8ea52fa..9654feb 100644
--- a/common.c
+++ b/common.c
@@ -45,7 +45,7 @@ xrealloc(void *ptr, unsigned int howmuch)
 }
 
 char *
-xstrdup(char *str)
+xstrdup(const char *str)
 {
   int len = strlen(str) + 1;
   char *copy = xmalloc(len);
diff --git a/ls-info.c b/ls-info.c
new file mode 100644
index 0000000..e1430aa
--- /dev/null
+++ b/ls-info.c
@@ -0,0 +1,328 @@
+/*
+ *	The PCI Utilities -- Save PCI info
+ *
+ *	Copyright (c) 2017 Virtuozzo International GmbH
+ *
+ *	Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#include "lspci.h"
+
+struct info_list *
+info_list_create(enum info_val_type type)
+{
+  struct info_list *list = xmalloc(sizeof(struct info_list));
+
+  list->node = NULL;
+  list->type = type;
+
+  return list;
+}
+
+struct info_list *
+info_list_create_in_obj(struct info_obj *parent_obj, char *key, enum info_val_type type)
+{
+  struct info_list *list = info_list_create(type);
+
+  info_obj_add_list(parent_obj, key, list);
+
+  return list;
+}
+
+static void
+info_list_delete(struct info_list *list)
+{
+  struct info_list_node *node = list->node, *next;
+
+  while (node)
+    {
+      switch (list->type)
+	{
+	case INFO_VAL_STRING:
+	  free(node->val.str);
+	  break;
+	case INFO_VAL_LIST:
+	  info_list_delete(node->val.list);
+	  break;
+	case INFO_VAL_OBJECT:
+	  info_obj_delete(node->val.obj);
+	  break;
+	default:
+	  break;
+	}
+      next = node->next;
+      free(node);
+      node = next;
+    }
+  free(list);
+}
+
+struct info_obj *
+info_obj_create(void)
+{
+  struct info_obj *obj = xmalloc(sizeof(struct info_obj));
+
+  obj->pair = NULL;
+
+  return obj;
+}
+
+struct info_obj *
+info_obj_create_in_obj(struct info_obj *parent_obj, char *key)
+{
+  struct info_obj *obj = info_obj_create();
+
+  info_obj_add_obj(parent_obj, key, obj);
+
+  return obj;
+}
+
+static void
+info_pair_delete(struct info_pair *pair)
+{
+  switch (pair->type)
+  {
+  case INFO_VAL_STRING:
+    free(pair->val.str);
+    break;
+  case INFO_VAL_LIST:
+    info_list_delete(pair->val.list);
+    break;
+  case INFO_VAL_OBJECT:
+    info_obj_delete(pair->val.obj);
+    break;
+  default:
+    break;
+  }
+  free(pair->key);
+  free(pair);
+}
+
+void
+info_obj_delete(struct info_obj *obj)
+{
+  struct info_pair *pair = obj->pair, *next;
+
+  while (pair)
+    {
+      next = pair->next;
+      info_pair_delete(pair);
+      pair = next;
+    }
+  free(obj);
+}
+
+void
+info_obj_delete_pair(struct info_obj *obj, char *key)
+{
+  struct info_pair *pair = obj->pair, *next, *prev = NULL;
+
+  while (pair)
+    {
+      next = pair->next;
+      if (!strcmp(pair->key, key))
+	{
+	  info_pair_delete(pair);
+	  if (prev)
+	    prev->next = next;
+	  else
+	    obj->pair = next;
+	  break;
+	}
+      prev = pair;
+      pair = next;
+   }
+}
+
+static struct info_list_node *
+info_list_add_node(struct info_list *list)
+{
+  struct info_list_node *new_node = xmalloc(sizeof(struct info_list_node));
+
+  new_node->next = NULL;
+
+  if (list->node)
+    {
+      struct info_list_node *node;
+
+      for (node = list->node; node && node->next; node = node->next);
+      node->next = new_node;
+    }
+  else
+    list->node = new_node;
+
+  return new_node;
+}
+
+void
+info_list_add_str(struct info_list *list, const char *str)
+{
+  struct info_list_node *new_node = info_list_add_node(list);
+
+  new_node->val.str = xstrdup(str);
+}
+
+void
+info_list_add_obj(struct info_list *list, struct info_obj *obj)
+{
+  struct info_list_node *new_node = info_list_add_node(list);
+
+  new_node->val.obj = obj;
+}
+
+static struct info_pair *
+info_obj_add_pair(struct info_obj *obj, const char *key, enum info_val_type type)
+{
+  struct info_pair *new_pair = xmalloc(sizeof(struct info_pair));
+  new_pair->key = xstrdup(key);
+  new_pair->next = NULL;
+  new_pair->type = type;
+
+  if (obj->pair)
+    {
+      struct info_pair *pair;
+
+      for (pair = obj->pair; pair && pair->next; pair = pair->next);
+      pair->next = new_pair;
+    }
+  else
+    obj->pair = new_pair;
+
+  return new_pair;
+}
+
+void
+info_obj_add_flag(struct info_obj *obj, const char *key, char flag)
+{
+  struct info_pair *new_pair = info_obj_add_pair(obj, key, INFO_VAL_FLAG);
+
+  new_pair->val.flag = flag;
+}
+
+void
+info_obj_add_str(struct info_obj *obj, const char *key, const char *str)
+{
+  struct info_pair *new_pair = info_obj_add_pair(obj, key, INFO_VAL_STRING);
+
+  new_pair->val.str = xstrdup(str);
+}
+
+void
+info_obj_add_fmt_buf_str(struct info_obj *obj, const char *key, char *buf, size_t size, const char *fmt, ...)
+{
+  va_list ap;
+
+  va_start(ap, fmt);
+  vsnprintf(buf, size, fmt, ap);
+  va_end(ap);
+
+  info_obj_add_str(obj, key, buf);
+}
+
+void
+info_obj_add_fmt_str(struct info_obj *obj, const char *key, size_t size, const char *fmt, ...)
+{
+  va_list ap;
+  char *buf = xmalloc(size);
+
+  va_start(ap, fmt);
+  vsnprintf(buf, size, fmt, ap);
+  va_end(ap);
+
+  info_obj_add_str(obj, key, buf);
+  free(buf);
+}
+
+void
+info_obj_add_list(struct info_obj *obj, const char *key, struct info_list *list)
+{
+  struct info_pair *new_pair = info_obj_add_pair(obj, key, INFO_VAL_LIST);
+
+  new_pair->val.list = list;
+}
+
+void
+info_obj_add_obj(struct info_obj *obj, const char *key, struct info_obj *new_obj)
+{
+  struct info_pair *new_pair = info_obj_add_pair(obj, key, INFO_VAL_OBJECT);
+
+  new_pair->val.obj = new_obj;
+}
+
+static void
+info_pair_print_json(struct info_pair *pair);
+
+void
+info_obj_print_json(struct info_obj *obj)
+{
+  struct info_pair *pair;
+
+  printf("{");
+  for (pair = obj->pair; pair; pair = pair->next)
+    {
+      info_pair_print_json(pair);
+      if (pair->next)
+	printf(", ");
+    }
+  printf("}");
+}
+
+static void
+info_list_print_json(struct info_list *list)
+{
+  struct info_list_node *node;
+
+  printf("[");
+  for (node = list->node; node; node = node->next)
+    {
+      switch (list->type)
+	{
+	case INFO_VAL_STRING:
+	  printf("\"%s\"", node->val.str);
+	  break;
+	case INFO_VAL_LIST:
+	  info_list_print_json(node->val.list);
+	  break;
+	case INFO_VAL_OBJECT:
+	  info_obj_print_json(node->val.obj);
+	  break;
+	case INFO_VAL_FLAG:
+	  printf("%s", node->val.flag ? "true" : "false");
+	  break;
+	default:
+	  break;
+	}
+      if (node->next)
+	printf(", ");
+    }
+  printf("]");
+}
+
+static void
+info_pair_print_json(struct info_pair *pair)
+{
+  printf("\"%s\": ", pair->key);
+  switch (pair->type)
+    {
+    case INFO_VAL_STRING:
+      printf("\"%s\"", pair->val.str);
+      break;
+    case INFO_VAL_LIST:
+      info_list_print_json(pair->val.list);
+      break;
+    case INFO_VAL_OBJECT:
+      info_obj_print_json(pair->val.obj);
+      break;
+    case INFO_VAL_FLAG:
+      printf("%s", (pair->val.flag == '+') ? "true" :
+		   ((pair->val.flag == '-') ? "false" : "null"));
+      break;
+    default:
+      break;
+    }
+}
diff --git a/lspci.h b/lspci.h
index bcd007e..ba5a56b 100644
--- a/lspci.h
+++ b/lspci.h
@@ -74,6 +74,60 @@ void show_ext_caps(struct device *d, int type);
 
 void show_vendor_caps(struct device *d, int where, int cap);
 
+/* ls-info.c */
+
+enum info_val_type {
+    INFO_VAL_STRING,
+    INFO_VAL_OBJECT,
+    INFO_VAL_LIST,
+    INFO_VAL_FLAG
+};
+
+struct info_obj {
+  struct info_pair *pair;
+};
+
+union info_val {
+  char *str;
+  struct info_obj *obj;
+  struct info_list *list;
+  char flag;
+};
+
+struct info_pair {
+  char *key;
+  enum info_val_type type;
+  union info_val val;
+  struct info_pair *next;
+};
+
+struct info_list {
+  enum info_val_type type;
+  struct info_list_node *node;
+};
+
+struct info_list_node {
+  union info_val val;
+  struct info_list_node *next;
+};
+
+struct info_obj *info_obj_create(void);
+struct info_obj *info_obj_create_in_obj(struct info_obj *parent_obj, char *key);
+void info_obj_add_str(struct info_obj *obj, const char *key, const char *str);
+void info_obj_add_list(struct info_obj *obj, const char *key, struct info_list *list);
+void info_obj_add_obj(struct info_obj *obj, const char *key, struct info_obj *new_obj);
+void info_obj_add_flag(struct info_obj *obj, const char *key, char flag);
+void info_obj_add_fmt_str(struct info_obj *obj, const char *key, size_t size, const char *fmt, ...);
+void info_obj_add_fmt_buf_str(struct info_obj *obj, const char *key, char *buf, size_t size, const char *fmt, ...);
+void info_obj_print_json(struct info_obj *obj);
+void info_obj_delete_pair(struct info_obj *obj, char *key);
+void info_obj_delete(struct info_obj *obj);
+
+struct info_list *info_list_create(enum info_val_type type);
+struct info_list *info_list_create_in_obj(struct info_obj *parent_obj, char *key, enum info_val_type type);
+void info_list_add_str(struct info_list *list, const char *str);
+void info_list_add_obj(struct info_list *list, struct info_obj *obj);
+
 /* ls-kernel.c */
 
 void show_kernel_machine(struct device *d UNUSED);
diff --git a/pciutils.h b/pciutils.h
index e433e6b..53a868b 100644
--- a/pciutils.h
+++ b/pciutils.h
@@ -22,7 +22,7 @@ extern const char program_name[];
 void die(char *msg, ...) NONRET PCI_PRINTF(1,2);
 void *xmalloc(unsigned int howmuch);
 void *xrealloc(void *ptr, unsigned int howmuch);
-char *xstrdup(char *str);
+char *xstrdup(const char *str);
 int parse_generic_option(int i, struct pci_access *pacc, char *optarg);
 
 #ifdef PCI_HAVE_PM_INTEL_CONF
-- 
2.14.1

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH v2 2/2] lspci: Add PCI info output in JSON format
  2018-02-14 16:07 [PATCH v2 0/2] lspci: Add support of JSON output format Viktor Prutyanov
  2018-02-14 16:07 ` [PATCH v2 1/2] lspci: Add printing info in JSON format Viktor Prutyanov
@ 2018-02-14 16:07 ` Viktor Prutyanov
  2018-02-15 10:32 ` [PATCH v2 0/2] lspci: Add support of JSON output format Martin Mares
  2 siblings, 0 replies; 6+ messages in thread
From: Viktor Prutyanov @ 2018-02-14 16:07 UTC (permalink / raw)
  To: linux-pci, mj; +Cc: Viktor Prutyanov

This patch adds '-J' option for output in JSON format.
When this option is enabled, the output contains the same data as without it,
except for capabilities.

Signed-off-by: Viktor Prutyanov <viktor.prutyanov@virtuozzo.com>
---
 Makefile    |   3 +-
 ls-kernel.c |  24 +++
 lspci.c     | 701 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 lspci.h     |   1 +
 lspci.man   |   3 +
 5 files changed, 730 insertions(+), 2 deletions(-)

diff --git a/Makefile b/Makefile
index 8c7edb7..5bdf84d 100644
--- a/Makefile
+++ b/Makefile
@@ -69,7 +69,7 @@ force:
 lib/config.h lib/config.mk:
 	cd lib && ./configure
 
-lspci: lspci.o ls-vpd.o ls-caps.o ls-caps-vendor.o ls-ecaps.o ls-kernel.o ls-tree.o ls-map.o common.o lib/$(PCILIB)
+lspci: lspci.o ls-vpd.o ls-caps.o ls-caps-vendor.o ls-ecaps.o ls-kernel.o ls-tree.o ls-map.o ls-info.o common.o lib/$(PCILIB)
 setpci: setpci.o common.o lib/$(PCILIB)
 
 LSPCIINC=lspci.h pciutils.h $(PCIINC)
@@ -80,6 +80,7 @@ ls-ecaps.o: ls-ecaps.c $(LSPCIINC)
 ls-kernel.o: ls-kernel.c $(LSPCIINC)
 ls-tree.o: ls-tree.c $(LSPCIINC)
 ls-map.o: ls-map.c $(LSPCIINC)
+ls-info.o: ls-info.c $(LSPCIINC)
 
 setpci.o: setpci.c pciutils.h $(PCIINC)
 common.o: common.c pciutils.h $(PCIINC)
diff --git a/ls-kernel.c b/ls-kernel.c
index ecacd0e..b0ded3b 100644
--- a/ls-kernel.c
+++ b/ls-kernel.c
@@ -302,6 +302,25 @@ show_kernel_machine(struct device *d)
     printf("Module:\t%s\n", module);
 }
 
+void
+fill_info_kernel(struct info_obj *dev_obj, struct device *d)
+{
+  char buf[DRIVER_BUF_SIZE];
+  const char *driver, *module;
+
+  if (driver = find_driver(d, buf))
+    info_obj_add_str(dev_obj, "Driver", driver);
+
+  if (!show_kernel_init())
+    return;
+
+  struct info_list *mod_list = info_list_create(INFO_VAL_STRING);
+  while (module = next_module_filtered(d))
+    info_list_add_str(mod_list, module);
+
+  info_obj_add_list(dev_obj, "Modules", mod_list);
+}
+
 #else
 
 void
@@ -319,5 +338,10 @@ show_kernel_cleanup(void)
 {
 }
 
+void
+fill_info_kernel(struct info_obj *dev_obj UNUSED, struct device *d UNUSED)
+{
+}
+
 #endif
 
diff --git a/lspci.c b/lspci.c
index b50c76a..ae50a85 100644
--- a/lspci.c
+++ b/lspci.c
@@ -25,11 +25,12 @@ static int opt_domains;			/* Show domain numbers (0=disabled, 1=auto-detected, 2
 static int opt_kernel;			/* Show kernel drivers */
 static int opt_query_dns;		/* Query the DNS (0=disabled, 1=enabled, 2=refresh cache) */
 static int opt_query_all;		/* Query the DNS for all entries */
+static int opt_json;
 char *opt_pcimap;			/* Override path to Linux modules.pcimap */
 
 const char program_name[] = "lspci";
 
-static char options[] = "nvbxs:d:ti:mgp:qkMDQ" GENERIC_OPTIONS ;
+static char options[] = "nvbxs:d:ti:mgp:qkJMDQ" GENERIC_OPTIONS ;
 
 static char help_msg[] =
 "Usage: lspci [<switches>]\n"
@@ -48,6 +49,7 @@ static char help_msg[] =
 "-xxxx\t\tShow hex-dump of the 4096-byte extended config space (root only)\n"
 "-b\t\tBus-centric view (addresses and IRQ's as seen by the bus)\n"
 "-D\t\tAlways show domain numbers\n"
+"-J\t\tUse JSON output format\n"
 "\n"
 "Resolving of device ID's to names:\n"
 "-n\t\tShow numeric ID's\n"
@@ -257,6 +259,16 @@ show_slot_name(struct device *d)
   printf("%02x:%02x.%d", p->bus, p->dev, p->func);
 }
 
+static void
+fill_slot_name(struct device *d, char *buf, size_t size)
+{
+  struct pci_dev *p = d->dev;
+
+  if (!opt_machine ? opt_domains : (p->domain || opt_domains >= 2))
+    snprintf(buf, size, "%04x:", p->domain);
+  snprintf(buf, size, "%02x:%02x.%d", p->bus, p->dev, p->func);
+}
+
 void
 get_subid(struct device *d, word *subvp, word *subdp)
 {
@@ -343,6 +355,24 @@ show_size(u64 x)
   printf(" [size=%u%s]", (unsigned)x, suffix[i]);
 }
 
+static void
+fill_size(char *buf, size_t size, u64 x)
+{
+  static const char suffix[][2] = { "", "K", "M", "G", "T" };
+  unsigned i;
+  if (!x)
+    {
+      snprintf(buf, size, "0");
+      return;
+    }
+  for (i = 0; i < (sizeof(suffix) / sizeof(*suffix) - 1); i++) {
+    if (x % 1024)
+      break;
+    x /= 1024;
+  }
+  snprintf(buf, size, "%u%s", (unsigned)x, suffix[i]);
+}
+
 static void
 show_range(char *prefix, u64 base, u64 limit, int is_64bit)
 {
@@ -369,6 +399,21 @@ show_range(char *prefix, u64 base, u64 limit, int is_64bit)
   putchar('\n');
 }
 
+static void
+fill_range(char *buf, size_t size, u64 base, u64 limit, int is_64bit)
+{
+  if (base > limit)
+    {
+      snprintf(buf, size, "None");
+      return;
+    }
+
+  if (is_64bit)
+    snprintf(buf, size, "%016" PCI_U64_FMT_X "-%016" PCI_U64_FMT_X, base, limit);
+  else
+    snprintf(buf, size, "%08x-%08x", (unsigned) base, (unsigned) limit);
+}
+
 static void
 show_bases(struct device *d, int cnt)
 {
@@ -840,6 +885,30 @@ show_hex_dump(struct device *d)
     }
 }
 
+static void
+fill_info_hex_dump(struct info_obj *dev_obj, struct device *d)
+{
+  unsigned int i, cnt;
+  char buf[3] = {0};
+  struct info_list *hex_dump_list = info_list_create(INFO_VAL_STRING);
+
+  cnt = d->config_cached;
+  if (opt_hex >= 3 && config_fetch(d, cnt, 256-cnt))
+    {
+      cnt = 256;
+      if (opt_hex >= 4 && config_fetch(d, 256, 4096-256))
+	cnt = 4096;
+    }
+
+  for (i=0; i<cnt; i++)
+    {
+      snprintf(buf, sizeof(buf), "%02x", get_conf_byte(d, i));
+      info_list_add_str(hex_dump_list, buf);
+    }
+
+  info_obj_add_list(dev_obj, "hexdump", hex_dump_list);
+}
+
 static void
 print_shell_escaped(char *c)
 {
@@ -945,6 +1014,631 @@ show(void)
     show_device(d);
 }
 
+static void
+fill_info_machine(struct info_obj *dev_obj, struct device *d)
+{
+  struct pci_dev *p = d->dev;
+  int c;
+  word sv_id, sd_id;
+  char buf[128];
+
+  get_subid(d, &sv_id, &sd_id);
+
+  fill_slot_name(d, buf, sizeof(buf));
+  info_obj_add_str(dev_obj, "Slot", buf);
+
+  pci_lookup_name(pacc, buf, sizeof(buf), PCI_LOOKUP_CLASS, p->device_class);
+  info_obj_add_str(dev_obj, "Class", buf);
+  pci_lookup_name(pacc, buf, sizeof(buf), PCI_LOOKUP_VENDOR, p->vendor_id, p->device_id);
+  info_obj_add_str(dev_obj, "Vendor", buf);
+  pci_lookup_name(pacc, buf, sizeof(buf), PCI_LOOKUP_DEVICE, p->vendor_id, p->device_id);
+  info_obj_add_str(dev_obj, "Device", buf);
+
+  if (sv_id && sv_id != 0xffff)
+    {
+      pci_lookup_name(pacc, buf, sizeof(buf), PCI_LOOKUP_SUBSYSTEM | PCI_LOOKUP_VENDOR, sv_id);
+      info_obj_add_str(dev_obj, "SVendor", buf);
+      pci_lookup_name(pacc, buf, sizeof(buf), PCI_LOOKUP_SUBSYSTEM | PCI_LOOKUP_DEVICE, p->vendor_id, p->device_id, sv_id, sd_id);
+      info_obj_add_str(dev_obj, "SDevice", buf);
+    }
+  else if (!verbose)
+    {
+      info_obj_add_str(dev_obj, "SVendor", "");
+      info_obj_add_str(dev_obj, "SDevice", "");
+    }
+
+  if (c = get_conf_byte(d, PCI_REVISION_ID))
+    {
+      snprintf(buf, sizeof(buf), "%02x", c);
+      info_obj_add_str(dev_obj, "Rev", buf);
+    }
+  if (c = get_conf_byte(d, PCI_CLASS_PROG))
+    {
+      snprintf(buf, sizeof(buf), "%02x", c);
+      info_obj_add_str(dev_obj, "ProgIf", buf);
+    }
+
+  if (opt_kernel)
+     fill_info_kernel(dev_obj, d);
+
+  if (verbose)
+    {
+      pci_fill_info(p, PCI_FILL_PHYS_SLOT | PCI_FILL_NUMA_NODE);
+      if (p->phy_slot)
+	info_obj_add_str(dev_obj, "PhySlot", p->phy_slot);
+      if (p->numa_node != -1)
+	{
+	  snprintf(buf, sizeof(buf), "%d", p->numa_node);
+	  info_obj_add_str(dev_obj, "NUMAnode", buf);
+	}
+    }
+}
+
+static void
+fill_info_terse(struct info_obj *dev_obj, struct device *d)
+{
+  struct pci_dev *p = d->dev;
+
+  fill_info_machine(dev_obj, d);
+
+  if (verbose || opt_kernel)
+    {
+      word subsys_v, subsys_d;
+      char ssnamebuf[256];
+
+      if (p->label)
+	info_obj_add_str(dev_obj, "DeviceName", p->label);
+      info_obj_delete_pair(dev_obj, "SDevice");
+      info_obj_delete_pair(dev_obj, "SVendor");
+      get_subid(d, &subsys_v, &subsys_d);
+      if (subsys_v && subsys_v != 0xffff)
+	info_obj_add_str(dev_obj, "Subsystem",
+		pci_lookup_name(pacc, ssnamebuf, sizeof(ssnamebuf),
+			PCI_LOOKUP_SUBSYSTEM | PCI_LOOKUP_VENDOR | PCI_LOOKUP_DEVICE,
+			p->vendor_id, p->device_id, subsys_v, subsys_d));
+    }
+}
+
+static void
+fill_info_rom(struct info_obj *dev_obj, struct device *d, int reg)
+{
+  struct pci_dev *p = d->dev;
+  pciaddr_t rom = p->rom_base_addr;
+  pciaddr_t len = (p->known_fields & PCI_FILL_SIZES) ? p->rom_size : 0;
+  pciaddr_t ioflg = (p->known_fields & PCI_FILL_IO_FLAGS) ? p->rom_flags : 0;
+  u32 flg = get_conf_long(d, reg);
+  word cmd = get_conf_word(d, PCI_COMMAND);
+  int virtual = 0;
+  char buf[64];
+  struct info_obj *rom_obj;
+  struct info_list *attrs_list;
+
+  if (!rom && !flg && !len)
+    return;
+
+  rom_obj = info_obj_create_in_obj(dev_obj, "ROM");
+  attrs_list = info_list_create(INFO_VAL_STRING);
+  info_obj_add_list(rom_obj, "attrs", attrs_list);
+
+  if (ioflg & PCI_IORESOURCE_PCI_EA_BEI)
+      info_list_add_str(attrs_list, "[enhanced]");
+  else if ((rom & PCI_ROM_ADDRESS_MASK) && !(flg & PCI_ROM_ADDRESS_MASK))
+    {
+      info_list_add_str(attrs_list, "[virtual]");
+      flg = rom;
+      virtual = 1;
+    }
+
+  if (rom & PCI_ROM_ADDRESS_MASK)
+    info_obj_add_fmt_buf_str(rom_obj, "at", buf, sizeof(buf), PCIADDR_T_FMT, rom & PCI_ROM_ADDRESS_MASK);
+  else if (flg & PCI_ROM_ADDRESS_MASK)
+    info_list_add_str(attrs_list, "<ignored>");
+  else
+    info_list_add_str(attrs_list, "<unassigned>");
+
+  if (!(flg & PCI_ROM_ADDRESS_ENABLE))
+    info_list_add_str(attrs_list, "[disabled]");
+  else if (!virtual && !(cmd & PCI_COMMAND_MEMORY))
+    info_list_add_str(attrs_list, "[disabled by cmd]");
+
+  fill_size(buf, sizeof(buf), len);
+  info_obj_add_str(rom_obj, "size", buf);
+}
+
+static void
+fill_info_bases(struct info_obj *dev_obj, struct device *d, int cnt)
+{
+  struct pci_dev *p = d->dev;
+  word cmd = get_conf_word(d, PCI_COMMAND);
+  int i;
+  int virtual = 0;
+  char buf[64];
+  struct info_obj *bases_obj = info_obj_create_in_obj(dev_obj, "bases");
+  struct info_list *regions_list = info_list_create(INFO_VAL_OBJECT);
+
+  info_obj_add_list(bases_obj, "regions", regions_list);
+
+  for (i=0; i<cnt; i++)
+    {
+      struct info_obj *region_obj;
+      struct info_list *attrs_list;
+
+      pciaddr_t pos = p->base_addr[i];
+      pciaddr_t len = (p->known_fields & PCI_FILL_SIZES) ? p->size[i] : 0;
+      pciaddr_t ioflg = (p->known_fields & PCI_FILL_IO_FLAGS) ? p->flags[i] : 0;
+      u32 flg = get_conf_long(d, PCI_BASE_ADDRESS_0 + 4*i);
+      if (flg == 0xffffffff)
+	flg = 0;
+      if (!pos && !flg && !len)
+	continue;
+
+      region_obj = info_obj_create();
+      info_list_add_obj(regions_list, region_obj);
+      attrs_list = info_list_create(INFO_VAL_STRING);
+      info_obj_add_list(region_obj, "attrs", attrs_list);
+
+      if (ioflg & PCI_IORESOURCE_PCI_EA_BEI)
+	  info_list_add_str(attrs_list, "[enhanced]");
+      else if (pos && !flg)	/* Reported by the OS, but not by the device */
+	{
+	  info_list_add_str(attrs_list, "[virtual]");
+	  flg = pos;
+	  virtual = 1;
+	}
+      if (flg & PCI_BASE_ADDRESS_SPACE_IO)
+	{
+	  pciaddr_t a = pos & PCI_BASE_ADDRESS_IO_MASK;
+	  if (a || (cmd & PCI_COMMAND_IO))
+	    info_obj_add_fmt_buf_str(region_obj, "io-ports-at", buf, sizeof(buf), PCIADDR_PORT_FMT, a);
+	  else if (flg & PCI_BASE_ADDRESS_IO_MASK)
+	    info_list_add_str(attrs_list, "<ignored>");
+	  else
+	    info_list_add_str(attrs_list, "<unassigned>");
+	  if (!virtual && !(cmd & PCI_COMMAND_IO))
+	    info_list_add_str(attrs_list, "[disabled]");
+	}
+      else
+	{
+	  int t = flg & PCI_BASE_ADDRESS_MEM_TYPE_MASK;
+	  pciaddr_t a = pos & PCI_ADDR_MEM_MASK;
+	  int done = 0;
+	  u32 z = 0;
+
+	  if (t == PCI_BASE_ADDRESS_MEM_TYPE_64)
+	    {
+	      if (i >= cnt - 1)
+		{
+		  info_list_add_str(attrs_list, "<invalid-64bit-slot>");
+		  done = 1;
+		}
+	      else
+		{
+		  i++;
+		  z = get_conf_long(d, PCI_BASE_ADDRESS_0 + 4*i);
+		}
+	    }
+	  if (!done)
+	    {
+	      if (a)
+		info_obj_add_fmt_buf_str(region_obj, "memory-at", buf, sizeof(buf), PCIADDR_T_FMT, a);
+	      else
+		{
+		  if ((flg & PCI_BASE_ADDRESS_MEM_MASK) || z)
+		    info_list_add_str(attrs_list, "<ignored>");
+		  else
+		    info_list_add_str(attrs_list, "<unassigned>");
+		}
+	    }
+	  info_list_add_str(attrs_list,
+		 (t == PCI_BASE_ADDRESS_MEM_TYPE_32) ? "32-bit" :
+		 (t == PCI_BASE_ADDRESS_MEM_TYPE_64) ? "64-bit" :
+		 (t == PCI_BASE_ADDRESS_MEM_TYPE_1M) ? "low-1M" : "type 3");
+	  info_list_add_str(attrs_list,
+		 (flg & PCI_BASE_ADDRESS_MEM_PREFETCH) ? "prefetchable" : "non-prefetchable");
+	  if (!virtual && !(cmd & PCI_COMMAND_MEMORY))
+	    info_list_add_str(attrs_list, "[disabled]");
+	}
+      fill_size(buf, sizeof(buf), len);
+      info_obj_add_str(region_obj, "size", buf);
+    }
+}
+
+static void
+fill_info_htype0(struct info_obj *dev_obj, struct device *d)
+{
+  struct info_obj *htype0_obj = info_obj_create_in_obj(dev_obj, "htype0");
+
+  fill_info_bases(htype0_obj, d, 6);
+  fill_info_rom(htype0_obj, d, PCI_ROM_ADDRESS);
+}
+
+static void
+fill_info_htype1(struct info_obj *dev_obj, struct device *d)
+{
+  u32 io_base = get_conf_byte(d, PCI_IO_BASE);
+  u32 io_limit = get_conf_byte(d, PCI_IO_LIMIT);
+  u32 io_type = io_base & PCI_IO_RANGE_TYPE_MASK;
+  u32 mem_base = get_conf_word(d, PCI_MEMORY_BASE);
+  u32 mem_limit = get_conf_word(d, PCI_MEMORY_LIMIT);
+  u32 mem_type = mem_base & PCI_MEMORY_RANGE_TYPE_MASK;
+  u32 pref_base = get_conf_word(d, PCI_PREF_MEMORY_BASE);
+  u32 pref_limit = get_conf_word(d, PCI_PREF_MEMORY_LIMIT);
+  u32 pref_type = pref_base & PCI_PREF_RANGE_TYPE_MASK;
+  word sec_stat = get_conf_word(d, PCI_SEC_STATUS);
+  word brc = get_conf_word(d, PCI_BRIDGE_CONTROL);
+  struct info_obj *htype1_obj = info_obj_create_in_obj(dev_obj, "htype1");
+  struct info_obj *bus_obj;
+  char buf[64];
+
+  fill_info_bases(htype1_obj, d, 2);
+
+  bus_obj = info_obj_create_in_obj(htype1_obj, "Bus");
+  info_obj_add_fmt_buf_str(bus_obj, "primary", buf, sizeof(buf), "%02x", get_conf_byte(d, PCI_PRIMARY_BUS));
+  info_obj_add_fmt_buf_str(bus_obj, "secondary", buf, sizeof(buf), "%02x", get_conf_byte(d, PCI_SECONDARY_BUS));
+  info_obj_add_fmt_buf_str(bus_obj, "subordinate", buf, sizeof(buf), "%02x", get_conf_byte(d, PCI_SUBORDINATE_BUS));
+  info_obj_add_fmt_buf_str(bus_obj, "sec-latency", buf, sizeof(buf), "%d", get_conf_byte(d, PCI_SEC_LATENCY_TIMER));
+
+  if (io_type != (io_limit & PCI_IO_RANGE_TYPE_MASK) ||
+      (io_type != PCI_IO_RANGE_TYPE_16 && io_type != PCI_IO_RANGE_TYPE_32))
+    fprintf(stderr, "\t!!! Unknown I/O range types %x/%x\n", io_base, io_limit);
+  else
+    {
+      io_base = (io_base & PCI_IO_RANGE_MASK) << 8;
+      io_limit = (io_limit & PCI_IO_RANGE_MASK) << 8;
+      if (io_type == PCI_IO_RANGE_TYPE_32)
+	{
+	  io_base |= (get_conf_word(d, PCI_IO_BASE_UPPER16) << 16);
+	  io_limit |= (get_conf_word(d, PCI_IO_LIMIT_UPPER16) << 16);
+	}
+      fill_range(buf, sizeof(buf), io_base, io_limit + 0xfff, 0);
+      info_obj_add_str(htype1_obj, "io-behind-bridge", buf);
+    }
+
+  if (mem_type != (mem_limit & PCI_MEMORY_RANGE_TYPE_MASK) ||
+      mem_type)
+    fprintf(stderr, "\t!!! Unknown memory range types %x/%x\n", mem_base, mem_limit);
+  else
+    {
+      mem_base = (mem_base & PCI_MEMORY_RANGE_MASK) << 16;
+      mem_limit = (mem_limit & PCI_MEMORY_RANGE_MASK) << 16;
+      fill_range(buf, sizeof(buf), mem_base, mem_limit + 0xfffff, 0);
+      info_obj_add_str(htype1_obj, "memory-behind-bridge", buf);
+    }
+
+  if (pref_type != (pref_limit & PCI_PREF_RANGE_TYPE_MASK) ||
+      (pref_type != PCI_PREF_RANGE_TYPE_32 && pref_type != PCI_PREF_RANGE_TYPE_64))
+    fprintf(stderr, "\t!!! Unknown prefetchable memory range types %x/%x\n", pref_base, pref_limit);
+  else
+    {
+      u64 pref_base_64 = (pref_base & PCI_PREF_RANGE_MASK) << 16;
+      u64 pref_limit_64 = (pref_limit & PCI_PREF_RANGE_MASK) << 16;
+      if (pref_type == PCI_PREF_RANGE_TYPE_64)
+	{
+	  pref_base_64 |= (u64) get_conf_long(d, PCI_PREF_BASE_UPPER32) << 32;
+	  pref_limit_64 |= (u64) get_conf_long(d, PCI_PREF_LIMIT_UPPER32) << 32;
+	}
+      fill_range(buf, sizeof(buf), pref_base_64, pref_limit_64 + 0xfffff, (pref_type == PCI_PREF_RANGE_TYPE_64));
+      info_obj_add_str(htype1_obj, "prefetchable-memory-behind-bridge", buf);
+    }
+
+  if (verbose > 1)
+    {
+      struct info_obj *sec_status_obj = info_obj_create();
+
+      sec_status_obj = info_obj_create_in_obj(htype1_obj, "SecondaryStatus");
+      info_obj_add_flag(sec_status_obj, "66MHz", FLAG(sec_stat, PCI_STATUS_66MHZ));
+      info_obj_add_flag(sec_status_obj, "FastB2B", FLAG(sec_stat, PCI_STATUS_FAST_BACK));
+      info_obj_add_flag(sec_status_obj, "ParErr", FLAG(sec_stat, PCI_STATUS_PARITY));
+      info_obj_add_str(sec_status_obj, "DEVSEL=", ((sec_stat & PCI_STATUS_DEVSEL_MASK) == PCI_STATUS_DEVSEL_SLOW) ? "slow" :
+	      ((sec_stat & PCI_STATUS_DEVSEL_MASK) == PCI_STATUS_DEVSEL_MEDIUM) ? "medium" :
+	      ((sec_stat & PCI_STATUS_DEVSEL_MASK) == PCI_STATUS_DEVSEL_FAST) ? "fast" : "??");
+      info_obj_add_flag(sec_status_obj, ">TAbort", FLAG(sec_stat, PCI_STATUS_SIG_TARGET_ABORT));
+      info_obj_add_flag(sec_status_obj, "<TAbort", FLAG(sec_stat, PCI_STATUS_REC_TARGET_ABORT));
+      info_obj_add_flag(sec_status_obj, "<MAbort", FLAG(sec_stat, PCI_STATUS_REC_MASTER_ABORT));
+      info_obj_add_flag(sec_status_obj, "<SERR", FLAG(sec_stat, PCI_STATUS_SIG_SYSTEM_ERROR));
+      info_obj_add_flag(sec_status_obj, "<PERR", FLAG(sec_stat, PCI_STATUS_DETECTED_PARITY));
+    }
+
+  fill_info_rom(htype1_obj, d, PCI_ROM_ADDRESS1);
+
+  if (verbose > 1)
+    {
+      struct info_obj *bridgectl_obj;
+
+      bridgectl_obj = info_obj_create_in_obj(htype1_obj, "BridgeCtl");
+      info_obj_add_flag(bridgectl_obj, "Parity", FLAG(brc, PCI_BRIDGE_CTL_PARITY));
+      info_obj_add_flag(bridgectl_obj, "SERR", FLAG(brc, PCI_BRIDGE_CTL_SERR));
+      info_obj_add_flag(bridgectl_obj, "NoISA", FLAG(brc, PCI_BRIDGE_CTL_NO_ISA));
+      info_obj_add_flag(bridgectl_obj, "VGA", FLAG(brc, PCI_BRIDGE_CTL_VGA));
+      info_obj_add_flag(bridgectl_obj, "VGA16", FLAG(brc, PCI_BRIDGE_CTL_VGA_16BIT));
+      info_obj_add_flag(bridgectl_obj, "MAbort", FLAG(brc, PCI_BRIDGE_CTL_MASTER_ABORT));
+      info_obj_add_flag(bridgectl_obj, ">Reset", FLAG(brc, PCI_BRIDGE_CTL_BUS_RESET));
+      info_obj_add_flag(bridgectl_obj, "FastB2B", FLAG(brc, PCI_BRIDGE_CTL_FAST_BACK));
+      info_obj_add_flag(bridgectl_obj, "PriDiscTmr", FLAG(brc, PCI_BRIDGE_CTL_PRI_DISCARD_TIMER));
+      info_obj_add_flag(bridgectl_obj, "SecDiscTmr", FLAG(brc, PCI_BRIDGE_CTL_SEC_DISCARD_TIMER));
+      info_obj_add_flag(bridgectl_obj, "DiscTmrStat", FLAG(brc, PCI_BRIDGE_CTL_DISCARD_TIMER_STATUS));
+      info_obj_add_flag(bridgectl_obj, "DiscTmrSERREn", FLAG(brc, PCI_BRIDGE_CTL_DISCARD_TIMER_SERR_EN));
+    }
+}
+
+static void
+fill_info_htype2(struct info_obj *dev_obj, struct device *d)
+{
+  int i;
+  word cmd = get_conf_word(d, PCI_COMMAND);
+  word brc = get_conf_word(d, PCI_CB_BRIDGE_CONTROL);
+  word exca;
+  int verb = verbose > 2;
+  struct info_obj *htype2_obj = info_obj_create_in_obj(dev_obj, "htype2");
+  struct info_obj *bus_obj;
+  char buf[64];
+
+  fill_info_bases(htype2_obj, d, 1);
+
+  bus_obj = info_obj_create_in_obj(htype2_obj, "Bus");
+  info_obj_add_fmt_buf_str(bus_obj, "primary", buf, sizeof(buf), "%02x", get_conf_byte(d, PCI_CB_PRIMARY_BUS));
+  info_obj_add_fmt_buf_str(bus_obj, "secondary", buf, sizeof(buf), "%02x", get_conf_byte(d, PCI_CB_CARD_BUS));
+  info_obj_add_fmt_buf_str(bus_obj, "subordinate", buf, sizeof(buf), "%02x", get_conf_byte(d, PCI_CB_SUBORDINATE_BUS));
+  info_obj_add_fmt_buf_str(bus_obj, "sec-latency", buf, sizeof(buf), "%d", get_conf_byte(d, PCI_CB_LATENCY_TIMER));
+
+  for (i=0; i<2; i++)
+    {
+      int p = 8*i;
+      struct info_obj *mem_win_obj;
+
+      u32 base = get_conf_long(d, PCI_CB_MEMORY_BASE_0 + p);
+      u32 limit = get_conf_long(d, PCI_CB_MEMORY_LIMIT_0 + p);
+      limit = limit + 0xfff;
+      if (base <= limit || verb)
+	{
+	  snprintf(buf, sizeof(buf), "memory-window-%d", i);
+	  mem_win_obj = info_obj_create_in_obj(htype2_obj, buf);
+	  info_obj_add_fmt_buf_str(mem_win_obj, "base", buf, sizeof(buf), "%08x", base);
+	  info_obj_add_fmt_buf_str(mem_win_obj, "limit", buf, sizeof(buf), "%08x", limit);
+	  info_obj_add_flag(mem_win_obj, "disabled", (cmd & PCI_COMMAND_MEMORY) ? '-' : '+');
+	  info_obj_add_flag(mem_win_obj, "prefetchable", (brc & (PCI_CB_BRIDGE_CTL_PREFETCH_MEM0 << i)) ? '-' : '+');
+	}
+    }
+  for (i=0; i<2; i++)
+    {
+      int p = 8*i;
+      struct info_obj *io_win_obj;
+
+      u32 base = get_conf_long(d, PCI_CB_IO_BASE_0 + p);
+      u32 limit = get_conf_long(d, PCI_CB_IO_LIMIT_0 + p);
+      if (!(base & PCI_IO_RANGE_TYPE_32))
+	{
+	  base &= 0xffff;
+	  limit &= 0xffff;
+	}
+      base &= PCI_CB_IO_RANGE_MASK;
+      limit = (limit & PCI_CB_IO_RANGE_MASK) + 3;
+      if (base <= limit || verb)
+	{
+	  snprintf(buf, sizeof(buf), "io-window-%d", i);
+	  io_win_obj = info_obj_create_in_obj(htype2_obj, buf);
+	  info_obj_add_fmt_buf_str(io_win_obj, "base", buf, sizeof(buf), "%08x", base);
+	  info_obj_add_fmt_buf_str(io_win_obj, "limit", buf, sizeof(buf), "%08x", limit);
+	  info_obj_add_flag(io_win_obj, "disabled", (cmd & PCI_COMMAND_IO) ? '-' : '+');
+	}
+    }
+
+  if (get_conf_word(d, PCI_CB_SEC_STATUS) & PCI_STATUS_SIG_SYSTEM_ERROR)
+    info_obj_add_str(htype2_obj, "SecondaryStatus", "SERR");
+  if (verbose > 1)
+    {
+      struct info_obj *bridgectl_obj;
+
+      bridgectl_obj = info_obj_create_in_obj(htype2_obj, "BridgeCtl");
+      info_obj_add_flag(bridgectl_obj, "Parity", FLAG(brc, PCI_CB_BRIDGE_CTL_PARITY));
+      info_obj_add_flag(bridgectl_obj, "SERR", FLAG(brc, PCI_CB_BRIDGE_CTL_SERR));
+      info_obj_add_flag(bridgectl_obj, "ISA", FLAG(brc, PCI_CB_BRIDGE_CTL_ISA));
+      info_obj_add_flag(bridgectl_obj, "VGA", FLAG(brc, PCI_CB_BRIDGE_CTL_VGA));
+      info_obj_add_flag(bridgectl_obj, "MAbort", FLAG(brc, PCI_CB_BRIDGE_CTL_MASTER_ABORT));
+      info_obj_add_flag(bridgectl_obj, ">Reset", FLAG(brc, PCI_CB_BRIDGE_CTL_CB_RESET));
+      info_obj_add_flag(bridgectl_obj, "16bInt", FLAG(brc, PCI_CB_BRIDGE_CTL_16BIT_INT));
+      info_obj_add_flag(bridgectl_obj, "PostWrite", FLAG(brc, PCI_CB_BRIDGE_CTL_POST_WRITES));
+    }
+
+  if (d->config_cached < 128)
+    return;
+
+  exca = get_conf_word(d, PCI_CB_LEGACY_MODE_BASE);
+  if (exca)
+    info_obj_add_fmt_buf_str(htype2_obj, "exca", buf, sizeof(buf), "%04x", exca);
+}
+
+static void
+fill_info_verbose(struct info_obj *dev_obj, struct device *d)
+{
+  struct pci_dev *p = d->dev;
+  word status = get_conf_word(d, PCI_STATUS);
+  word cmd = get_conf_word(d, PCI_COMMAND);
+  word class = p->device_class;
+  byte bist = get_conf_byte(d, PCI_BIST);
+  byte htype = get_conf_byte(d, PCI_HEADER_TYPE) & 0x7f;
+  byte latency = get_conf_byte(d, PCI_LATENCY_TIMER);
+  byte cache_line = get_conf_byte(d, PCI_CACHE_LINE_SIZE);
+  byte max_lat, min_gnt;
+  byte int_pin = get_conf_byte(d, PCI_INTERRUPT_PIN);
+  unsigned int irq;
+
+  fill_info_terse(dev_obj, d);
+  pci_fill_info(p, PCI_FILL_IRQ | PCI_FILL_BASES | PCI_FILL_ROM_BASE | PCI_FILL_SIZES |
+    PCI_FILL_PHYS_SLOT | PCI_FILL_LABEL | PCI_FILL_NUMA_NODE);
+  irq = p->irq;
+
+  switch (htype)
+    {
+    case PCI_HEADER_TYPE_NORMAL:
+      if (class == PCI_CLASS_BRIDGE_PCI)
+	fprintf(stderr, "\t!!! Invalid class %04x for header type %02x\n", class, htype);
+      max_lat = get_conf_byte(d, PCI_MAX_LAT);
+      min_gnt = get_conf_byte(d, PCI_MIN_GNT);
+      break;
+    case PCI_HEADER_TYPE_BRIDGE:
+      if ((class >> 8) != PCI_BASE_CLASS_BRIDGE)
+	fprintf(stderr, "\t!!! Invalid class %04x for header type %02x\n", class, htype);
+      min_gnt = max_lat = 0;
+      break;
+    case PCI_HEADER_TYPE_CARDBUS:
+      if ((class >> 8) != PCI_BASE_CLASS_BRIDGE)
+	fprintf(stderr, "\t!!! Invalid class %04x for header type %02x\n", class, htype);
+      min_gnt = max_lat = 0;
+      break;
+    default:
+      fprintf(stderr, "\t!!! Unknown header type %02x\n", htype);
+      return;
+    }
+
+  if (p->phy_slot)
+    info_obj_add_str(dev_obj, "PhySlot", p->phy_slot);
+
+  if (verbose > 1)
+    {
+      struct info_obj *control_obj;
+      struct info_obj *status_obj;
+
+      control_obj = info_obj_create_in_obj(dev_obj, "Control");
+      info_obj_add_flag(control_obj, "I/O", FLAG(cmd, PCI_COMMAND_IO));
+      info_obj_add_flag(control_obj, "Mem", FLAG(cmd, PCI_COMMAND_MEMORY));
+      info_obj_add_flag(control_obj, "BusMaster", FLAG(cmd, PCI_COMMAND_MASTER));
+      info_obj_add_flag(control_obj, "SpecCycle", FLAG(cmd, PCI_COMMAND_SPECIAL));
+      info_obj_add_flag(control_obj, "MemWINV", FLAG(cmd, PCI_COMMAND_INVALIDATE));
+      info_obj_add_flag(control_obj, "VGASnoop", FLAG(cmd, PCI_COMMAND_VGA_PALETTE));
+      info_obj_add_flag(control_obj, "ParErr", FLAG(cmd, PCI_COMMAND_PARITY));
+      info_obj_add_flag(control_obj, "Stepping", FLAG(cmd, PCI_COMMAND_WAIT));
+      info_obj_add_flag(control_obj, "SERR", FLAG(cmd, PCI_COMMAND_SERR));
+      info_obj_add_flag(control_obj, "FastB2B", FLAG(cmd, PCI_COMMAND_FAST_BACK));
+      info_obj_add_flag(control_obj, "DisINTx", FLAG(cmd, PCI_COMMAND_DISABLE_INTx));
+
+      status_obj = info_obj_create_in_obj(dev_obj, "Status");
+      info_obj_add_flag(status_obj, "Cap", FLAG(status, PCI_STATUS_CAP_LIST));
+      info_obj_add_flag(status_obj, "66MHz", FLAG(status, PCI_STATUS_66MHZ));
+      info_obj_add_flag(status_obj, "UDF", FLAG(status, PCI_STATUS_UDF));
+      info_obj_add_flag(status_obj, "FastB2B", FLAG(status, PCI_STATUS_FAST_BACK));
+      info_obj_add_flag(status_obj, "ParErr", FLAG(status, PCI_STATUS_PARITY));
+      info_obj_add_str(status_obj, "DEVSEL=",
+	      ((status & PCI_STATUS_DEVSEL_MASK) == PCI_STATUS_DEVSEL_SLOW) ? "slow" :
+	      ((status & PCI_STATUS_DEVSEL_MASK) == PCI_STATUS_DEVSEL_MEDIUM) ? "medium" :
+	      ((status & PCI_STATUS_DEVSEL_MASK) == PCI_STATUS_DEVSEL_FAST) ? "fast" : "??");
+      info_obj_add_flag(status_obj, ">TAbort", FLAG(status, PCI_STATUS_SIG_TARGET_ABORT));
+      info_obj_add_flag(status_obj, "<TAbort", FLAG(status, PCI_STATUS_REC_TARGET_ABORT));
+      info_obj_add_flag(status_obj, "<MAbort", FLAG(status, PCI_STATUS_REC_MASTER_ABORT));
+      info_obj_add_flag(status_obj, ">SERR", FLAG(status, PCI_STATUS_SIG_SYSTEM_ERROR));
+      info_obj_add_flag(status_obj, "<PERR", FLAG(status, PCI_STATUS_DETECTED_PARITY));
+      info_obj_add_flag(status_obj, "INTx", FLAG(status, PCI_STATUS_INTx));
+
+      if (cmd & PCI_COMMAND_MASTER)
+	{
+	  info_obj_add_fmt_str(dev_obj, "Latency", 16, "%d", latency);
+	  if (min_gnt)
+	    info_obj_add_fmt_str(dev_obj, "min-gnt", 16, "%d", min_gnt*250);
+	  if (max_lat)
+	    info_obj_add_fmt_str(dev_obj, "max-lat", 16, "%d", max_lat*250);
+	  if (cache_line)
+	    info_obj_add_fmt_str(dev_obj, "CacheLineSize", 16, "%d", cache_line * 4);
+	}
+      if (int_pin || irq)
+	{
+	  info_obj_add_fmt_str(dev_obj, "int-pin", 2, "%c", (int_pin ? 'A' + int_pin - 1 : '?'));
+	  info_obj_add_fmt_str(dev_obj, "IRQ", 16, PCIIRQ_FMT, irq);
+	}
+    }
+  else
+    {
+      struct info_list *flags_list =  info_list_create(INFO_VAL_STRING);
+
+      info_obj_add_list(dev_obj, "Flags", flags_list);
+      if (cmd & PCI_COMMAND_MASTER)
+	info_list_add_str(flags_list, "bus master");
+      if (cmd & PCI_COMMAND_VGA_PALETTE)
+	info_list_add_str(flags_list, "VGA palette snoop");
+      if (cmd & PCI_COMMAND_WAIT)
+	info_list_add_str(flags_list, "stepping");
+      if (cmd & PCI_COMMAND_FAST_BACK)
+	info_list_add_str(flags_list, "fast Back2Back");
+      if (status & PCI_STATUS_66MHZ)
+	info_list_add_str(flags_list, "66MHz");
+      if (status & PCI_STATUS_UDF)
+	info_list_add_str(flags_list, "user-definable features");
+
+      info_obj_add_str(dev_obj, "devsel",
+	      ((status & PCI_STATUS_DEVSEL_MASK) == PCI_STATUS_DEVSEL_SLOW) ? "slow" :
+	      ((status & PCI_STATUS_DEVSEL_MASK) == PCI_STATUS_DEVSEL_MEDIUM) ? "medium" :
+	      ((status & PCI_STATUS_DEVSEL_MASK) == PCI_STATUS_DEVSEL_FAST) ? "fast" : "??");
+      if (cmd & PCI_COMMAND_MASTER)
+	info_obj_add_fmt_str(dev_obj, "Latency", 16, "%d", latency);
+      if (irq)
+	info_obj_add_fmt_str(dev_obj, "IRQ", 16, PCIIRQ_FMT, irq);
+    }
+
+  if (bist & PCI_BIST_CAPABLE)
+    {
+      if (bist & PCI_BIST_START)
+	info_obj_add_str(dev_obj, "BIST", "running");
+      else
+	info_obj_add_fmt_str(dev_obj, "BIST", 3, "%02x", bist & PCI_BIST_CODE_MASK);
+    }
+
+  switch (htype)
+    {
+    case PCI_HEADER_TYPE_NORMAL:
+      fill_info_htype0(dev_obj, d);
+      break;
+    case PCI_HEADER_TYPE_BRIDGE:
+      fill_info_htype1(dev_obj, d);
+      break;
+    case PCI_HEADER_TYPE_CARDBUS:
+      fill_info_htype2(dev_obj, d);
+      break;
+    }
+}
+
+static void
+fill_info_device(struct info_obj *dev_obj, struct device *d)
+{
+  if (opt_machine)
+    fill_info_machine(dev_obj, d);
+  else
+    {
+      if (verbose)
+	fill_info_verbose(dev_obj, d);
+      else
+	fill_info_terse(dev_obj, d);
+      if (!opt_kernel && verbose)
+	fill_info_kernel(dev_obj, d);
+    }
+  if (opt_hex)
+    fill_info_hex_dump(dev_obj, d);
+}
+
+static void
+fill_info(struct info_obj *root)
+{
+  struct device *d;
+  struct info_list *dev_list = info_list_create(INFO_VAL_OBJECT);
+
+  for (d=first_dev; d; d=d->next)
+    {
+      struct info_obj *dev_obj = info_obj_create();
+      fill_info_device(dev_obj, d);
+      info_list_add_obj(dev_list, dev_obj);
+    }
+
+  info_obj_add_list(root, "pcidevices", dev_list);
+}
+
+static void
+show_json(void)
+{
+  struct info_obj *root = info_obj_create();
+
+  fill_info(root);
+  info_obj_print_json(root);
+  info_obj_delete(root);
+}
+
 /* Main */
 
 int
@@ -1009,6 +1703,9 @@ main(int argc, char **argv)
       case 'D':
 	opt_domains = 2;
 	break;
+      case 'J':
+	opt_json = 1;
+	break;
 #ifdef PCI_USE_DNS
       case 'q':
 	opt_query_dns++;
@@ -1049,6 +1746,8 @@ main(int argc, char **argv)
       sort_them();
       if (opt_tree)
 	show_forest();
+      else if (opt_json)
+	show_json();
       else
 	show();
     }
diff --git a/lspci.h b/lspci.h
index ba5a56b..fc23d32 100644
--- a/lspci.h
+++ b/lspci.h
@@ -133,6 +133,7 @@ void info_list_add_obj(struct info_list *list, struct info_obj *obj);
 void show_kernel_machine(struct device *d UNUSED);
 void show_kernel(struct device *d UNUSED);
 void show_kernel_cleanup(void);
+void fill_info_kernel(struct info_obj *dev_obj UNUSED, struct device *d UNUSED);
 
 /* ls-tree.c */
 
diff --git a/lspci.man b/lspci.man
index 9348cfc..3a126a4 100644
--- a/lspci.man
+++ b/lspci.man
@@ -51,6 +51,9 @@ See below for details.
 .B -t
 Show a tree-like diagram containing all buses, bridges, devices and connections
 between them.
+.TP
+.B -J
+Use JSON output format.
 
 .SS Display options
 .TP
-- 
2.14.1

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH v2 0/2] lspci: Add support of JSON output format
  2018-02-14 16:07 [PATCH v2 0/2] lspci: Add support of JSON output format Viktor Prutyanov
  2018-02-14 16:07 ` [PATCH v2 1/2] lspci: Add printing info in JSON format Viktor Prutyanov
  2018-02-14 16:07 ` [PATCH v2 2/2] lspci: Add PCI info output " Viktor Prutyanov
@ 2018-02-15 10:32 ` Martin Mares
  2018-02-18 21:45   ` viktor.prutyanov
  2 siblings, 1 reply; 6+ messages in thread
From: Martin Mares @ 2018-02-15 10:32 UTC (permalink / raw)
  To: Viktor Prutyanov; +Cc: linux-pci

Hello!

> This collection of patches adds support of printing PCI info in JSON format

First of all, I would really like to hear the reasons behind that --
especially why the current machine-readable format is not sufficient.

Generally, adding 1000 lines of code which duplicate a lot of existing
logic should have a strong reason.

				Have a nice fortnight
-- 
Martin `MJ' Mares                          <mj@ucw.cz>   http://mj.ucw.cz/
Faculty of Math and Physics, Charles University, Prague, Czech Rep., Earth
return(ENOTOBACCO); /* Read on an empty pipe */

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v2 0/2] lspci: Add support of JSON output format
  2018-02-15 10:32 ` [PATCH v2 0/2] lspci: Add support of JSON output format Martin Mares
@ 2018-02-18 21:45   ` viktor.prutyanov
  2018-03-16 16:57     ` Martin Mares
  0 siblings, 1 reply; 6+ messages in thread
From: viktor.prutyanov @ 2018-02-18 21:45 UTC (permalink / raw)
  To: Martin Mares; +Cc: linux-pci, dmonakhov

=D0=92 Thu, 15 Feb 2018 11:32:55 +0100
Martin Mares <mj@ucw.cz> =D0=BF=D0=B8=D1=88=D0=B5=D1=82:

> Hello!
>=20
> > This collection of patches adds support of printing PCI info in
> > JSON format =20
>=20
> First of all, I would really like to hear the reasons behind that --
> especially why the current machine-readable format is not sufficient.
>=20
> Generally, adding 1000 lines of code which duplicate a lot of existing
> logic should have a strong reason.
>=20
> 				Have a nice fortnight

Hello!

Because current machine-readable format is very limited and it is
difficult to extend it to support verbosity options.

JSON is defacto standard structured format. The key JSON's advantage is
good portability and extensibility and rich toolchain support. Almost
every modern utility has JSON format support for output, for example
lsblk and lscpu.

For example here is how structured -vv output looks like:
$ ./lspci -Jvv | python -m json.tool

JSON has nice tool called 'jq' which is basically Swiss-knife parser.
For example you can output some fields you need as tsv:
$ ./lspci -J -vv | jq -r '.[][] | [.Slot, .Device, .IRQ] | @tsv'

...
00:14.0	8 Series USB xHCI HC	41
03:00.0	RTL8411B PCI Express Card Reader	43
04:00.0	GK107M [GeForce GT 750M] 255
...

Or let's reformat part of input structure to another structure you need:
$ ./lspci -Jvv | jq '.[][3] | {dev:.Device, class:.Class, irq:.IRQ}'

{
  "dev": "Haswell-ULT HD Audio Controller",
  "class": "Audio device",
  "irq": "48"
}

The patch is large mostly because current lspci use add-hoc output
approach. So patch add skeleton which construct structured object
separately from printing. Objects can later be easily extended to
support new fields and output formats (not only JSON).

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v2 0/2] lspci: Add support of JSON output format
  2018-02-18 21:45   ` viktor.prutyanov
@ 2018-03-16 16:57     ` Martin Mares
  0 siblings, 0 replies; 6+ messages in thread
From: Martin Mares @ 2018-03-16 16:57 UTC (permalink / raw)
  To: viktor.prutyanov; +Cc: linux-pci, dmonakhov

Hello!

> Because current machine-readable format is very limited and it is
> difficult to extend it to support verbosity options.
> 
> JSON is defacto standard structured format. The key JSON's advantage is
> good portability and extensibility and rich toolchain support. Almost
> every modern utility has JSON format support for output, for example
> lsblk and lscpu.

I agree that a structured output format can be useful at times,
but I do not think that it is so useful that it justifies duplicating
almost the whole source code of lspci. Essentially, you adding a second
copy of all parsing logic with a different output format.

If you find a way how to reduce the code overlap, I could be willing
to accept the change.

				Have a nice fortnight
-- 
Martin `MJ' Mares                          <mj@ucw.cz>   http://mj.ucw.cz/
Faculty of Math and Physics, Charles University, Prague, Czech Rep., Earth
Yes, XSLT is Turing-complete. But so is Intercal.

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2018-03-16 16:57 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-02-14 16:07 [PATCH v2 0/2] lspci: Add support of JSON output format Viktor Prutyanov
2018-02-14 16:07 ` [PATCH v2 1/2] lspci: Add printing info in JSON format Viktor Prutyanov
2018-02-14 16:07 ` [PATCH v2 2/2] lspci: Add PCI info output " Viktor Prutyanov
2018-02-15 10:32 ` [PATCH v2 0/2] lspci: Add support of JSON output format Martin Mares
2018-02-18 21:45   ` viktor.prutyanov
2018-03-16 16:57     ` Martin Mares

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.