]> www.infradead.org Git - users/hch/nvme-cli.git/commitdiff
Merge branch 'master' into integration-libnvme
authorKeith Busch <kbusch@kernel.org>
Thu, 28 May 2020 20:41:19 +0000 (13:41 -0700)
committerKeith Busch <kbusch@kernel.org>
Thu, 28 May 2020 20:41:19 +0000 (13:41 -0700)
12 files changed:
1  2 
Makefile
libnvme
nvme.c
plugins/huawei/huawei-nvme.c
plugins/intel/intel-nvme.c
plugins/memblaze/memblaze-nvme.c
plugins/netapp/netapp-nvme.c
plugins/shannon/shannon-nvme.c
plugins/transcend/transcend-nvme.c
plugins/virtium/virtium-nvme.c
plugins/wdc/wdc-nvme.c
util/user-types.c

diff --cc Makefile
index ef8935ca06b7444ecd50cd68a58a9517a2fd1931,373146a02e0956165af4e96e81a30b71329aa9ba..a822776e2f98376a4dfc2110402f682ff5a9af72
+++ b/Makefile
@@@ -1,9 -1,9 +1,9 @@@
  CFLAGS ?= -O2 -g -Wall -Werror
 -override CFLAGS += -std=gnu99 -I.
 -override CPPFLAGS += -D_GNU_SOURCE -D__CHECK_ENDIAN__
 +override CFLAGS += -std=gnu99
 +override CPPFLAGS += -D_GNU_SOURCE -D__CHECK_ENDIAN__ -ljson-c
  LIBUUID = $(shell $(LD) -o /dev/null -luuid >/dev/null 2>&1; echo $$?)
  LIBHUGETLBFS = $(shell $(LD) -o /dev/null -lhugetlbfs >/dev/null 2>&1; echo $$?)
- HAVE_SYSTEMD = $(shell pkg-config --exists systemd  --atleast-version=232; echo $$?)
+ HAVE_SYSTEMD = $(shell pkg-config --exists libsystemd  --atleast-version=242; echo $$?)
  NVME = nvme
  INSTALL ?= install
  DESTDIR =
@@@ -76,12 -77,10 +77,13 @@@ PLUGIN_OBJS :=                                     
        plugins/seagate/seagate-nvme.o          \
        plugins/virtium/virtium-nvme.o          \
        plugins/shannon/shannon-nvme.o          \
-       plugins/dera/dera-nvme.o
+       plugins/dera/dera-nvme.o            \
+     plugins/transcend/transcend-nvme.o
  
 -nvme: nvme.c nvme.h $(OBJS) $(PLUGIN_OBJS) $(UTIL_OBJS) NVME-VERSION-FILE
 +libnvme:
 +      $(MAKE) -C $(LIBNVMEDIR)
 +
 +nvme: nvme.c nvme.h libnvme $(OBJS) $(PLUGIN_OBJS) $(UTIL_OBJS) NVME-VERSION-FILE
        $(QUIET_CC)$(CC) $(CPPFLAGS) $(CFLAGS) $(INC) $< -o $(NVME) $(OBJS) $(PLUGIN_OBJS) $(UTIL_OBJS) $(LDFLAGS)
  
  verify-no-dep: nvme.c nvme.h $(OBJS) NVME-VERSION-FILE
@@@ -117,10 -115,8 +119,11 @@@ clobber: clea
  install-man:
        $(MAKE) -C Documentation install-no-build
  
 +install-lib:
 +      $(MAKE) -C libnvme install
 +
  install-bin: default
+       $(RM) $(DESTDIROLD)/nvme
        $(INSTALL) -d $(DESTDIR)$(SBINDIR)
        $(INSTALL) -m 755 nvme $(DESTDIR)$(SBINDIR)
  
diff --cc libnvme
index 91fd58d5ad37fae8e0b56630a425d3d78f0922d7,0000000000000000000000000000000000000000..380d801f46c58fd0a6883e441001edf2352ace07
mode 160000,000000..160000
--- /dev/null
+++ b/libnvme
@@@ -1,1 -1,0 +1,1 @@@
- Subproject commit 91fd58d5ad37fae8e0b56630a425d3d78f0922d7
++Subproject commit 380d801f46c58fd0a6883e441001edf2352ace07
diff --cc nvme.c
index 44771beca10885411b0d139fbb532b0dad78119d,107f01267569ce9cdc978d13a0a5989a74924b45..8cbef0cbf865a0a93f86915cde68761c047bde6c
--- 1/nvme.c
--- 2/nvme.c
+++ b/nvme.c
  #include <sys/stat.h>
  #include <sys/time.h>
  
- #include <uuid.h>
 -#include "common.h"
 -#include "nvme-print.h"
 -#include "nvme-ioctl.h"
 -#include "nvme-status.h"
 -#include "nvme-lightnvm.h"
 -#include "plugin.h"
++#include <uuid/uuid.h>
  
 +#include "nvme.h"
 +#include "plugin.h"
  #include "argconfig.h"
 -#include "fabrics.h"
 +
 +#define PATH_NVMF_DISC                "/etc/nvme/discovery.conf"
 +#define MAX_DISC_ARGS         32
 +#define MAX_DISC_RETRIES      10
  
  #define CREATE_CMD
 -#include "nvme-builtin.h"
 +#include "builtin.h"
 +
 +#define ARRAY_SIZE(x) sizeof(x) / sizeof(*x)
 +
 +#define min(x, y) (x < y) ? x : y
 +#define max(x, y) (x < y) ? y : x
  
  static struct stat nvme_stat;
  const char *devicename;
@@@ -454,12 -277,12 +457,13 @@@ static int get_ana_log(int argc, char *
  {
        const char *desc = "Retrieve ANA log for the given device in " \
                            "decoded format (default), json or binary.";
 -      void *ana_log;
 -      int err, fd;
 -      int groups = 0; /* Right now get all the per ANA group NSIDS */
 -      size_t ana_log_len;
 -      struct nvme_id_ctrl ctrl;
 +
 +      enum nvme_log_ana_lsp lsp = NVME_LOG_ANA_LSP_RGO_NAMESPACES;
        enum nvme_print_flags flags;
 +      int err, fd;
 +      size_t len;
 +      void *log;
++      bool huge = false;
  
        struct config {
                char *output_format;
        if (flags < 0)
                goto close_fd;
  
 -      err = nvme_identify_ctrl(fd, &ctrl);
 -      if (err) {
 -              fprintf(stderr, "ERROR : nvme_identify_ctrl() failed 0x%x\n",
 -                              err);
 +      err = nvme_get_ana_log_len(fd, &len);
 +      if (err)
                goto close_fd;
 -      }
  
-       log = malloc(len);
 -      ana_log_len = sizeof(struct nvme_ana_rsp_hdr) +
 -              le32_to_cpu(ctrl.nanagrpid) * sizeof(struct nvme_ana_group_desc);
 -      if (!(ctrl.anacap & (1 << 6)))
 -              ana_log_len += le32_to_cpu(ctrl.mnan) * sizeof(__le32);
 -
 -      ana_log = malloc(ana_log_len);
 -      if (!ana_log) {
++      log = nvme_alloc(len, &huge);
 +      if (!log) {
                perror("malloc");
 -              err = -ENOMEM;
 +              err = -1;
                goto close_fd;
        }
  
 -      err = nvme_ana_log(fd, ana_log, ana_log_len, groups ? NVME_ANA_LOG_RGO : 0);
 -      if (!err) {
 -              nvme_show_ana_log(ana_log, devicename, flags, ana_log_len);
 -      } else if (err > 0)
 -              nvme_show_status(err);
 +      if (cfg.groups)
 +              lsp = NVME_LOG_ANA_LSP_RGO_GROUPS_ONLY;
 +
 +      err = nvme_get_log_ana(fd, lsp, true, 0, len, log);
 +      if (!err)
 +              nvme_show_ana_log(log, devicename, flags, len);
        else
 -              perror("ana-log");
 -      free(ana_log);
 +              nvme_show_status("ana-log", err);
-       free(log);
++
++      nvme_free(log, huge);
  close_fd:
        close(fd);
  ret:
@@@ -563,23 -398,69 +568,23 @@@ static int get_telemetry_log(int argc, 
                fprintf(stderr, "Failed to open output file %s: %s!\n",
                                cfg.file_name, strerror(errno));
                err = output;
 -              goto free_mem;
 -      }
 -
 -      err = nvme_get_telemetry_log(fd, hdr, cfg.host_gen, cfg.ctrl_init, bs, 0);
 -      if (err < 0)
 -              perror("get-telemetry-log");
 -      else if (err > 0) {
 -              nvme_show_status(err);
 -              fprintf(stderr, "Failed to acquire telemetry header %d!\n", err);
 -              goto close_output;
 -      }
 -
 -      err = write(output, (void *) hdr, bs);
 -      if (err != bs) {
 -              fprintf(stderr, "Failed to flush all data to file!\n");
 -              goto close_output;
 -      }
 -
 -      switch (cfg.data_area) {
 -      case 1:
 -              full_size = (le16_to_cpu(hdr->dalb1) * bs) + offset;
 -              break;
 -      case 2:
 -              full_size = (le16_to_cpu(hdr->dalb2) * bs) + offset;
 -              break;
 -      case 3:
 -              full_size = (le16_to_cpu(hdr->dalb3) * bs) + offset;
 -              break;
 -      default:
 -              fprintf(stderr, "Invalid data area requested\n");
 -              err = -EINVAL;
 -              goto close_output;
 +              goto close_fd;
        }
  
 -      /*
 -       * Continuously pull data until the offset hits the end of the last
 -       * block.
 -       */
 -      while (offset != full_size) {
 -              err = nvme_get_telemetry_log(fd, page_log, 0, cfg.ctrl_init, bs, offset);
 -              if (err < 0) {
 -                      perror("get-telemetry-log");
 -                      break;
 -              } else if (err > 0) {
 -                      fprintf(stderr, "Failed to acquire full telemetry log!\n");
 -                      nvme_show_status(err);
 -                      break;
 -              }
 +      if (cfg.host_gen)
-               nvme_get_new_host_telemetry(fd, &log);
++              err = nvme_get_new_host_telemetry(fd, &log);
 +      else if (cfg.ctrl_init)
-               nvme_get_ctrl_telemetry(fd, true, &log);
++              err = nvme_get_ctrl_telemetry(fd, true, &log);
 +      else
-               nvme_get_host_telemetry(fd, &log);
++              err = nvme_get_host_telemetry(fd, &log);
  
-       if (!err)
-               ;
-       else
 -              err = write(output, (void *) page_log, bs);
 -              if (err != bs) {
 -                      fprintf(stderr, "Failed to flush all data to file!\n");
 -                      break;
 -              }
 -              err = 0;
 -              offset += bs;
 -      }
++      if (err)
 +              nvme_show_status("get-telemetry-log", err);
++      else
++              ;
  
 -close_output:
 +      free(log);
        close(output);
 -free_mem:
 -      free(hdr);
 -      free(page_log);
  close_fd:
        close(fd);
  ret:
@@@ -910,31 -807,34 +915,30 @@@ static int get_log(int argc, char **arg
        if (!cfg.log_len) {
                fprintf(stderr, "non-zero log-len is required param\n");
                err = -EINVAL;
 -      } else {
 -              unsigned char *log;
 -
 -              log = malloc(cfg.log_len);
 -              if (!log) {
 -                      fprintf(stderr, "could not alloc buffer for log: %s\n",
 -                                      strerror(errno));
 -                      err = -EINVAL;
 -                      goto close_fd;
 -              }
 +              goto close_fd;
 +      }
  
 -              err = nvme_get_log14(fd, cfg.namespace_id, cfg.log_id,
 -                                   cfg.lsp, cfg.lpo, 0, cfg.rae,
 -                                   cfg.uuid_index, cfg.log_len, log);
 -              if (!err) {
 -                      if (!cfg.raw_binary) {
 -                              printf("Device:%s log-id:%d namespace-id:%#x\n",
 -                                     devicename, cfg.log_id,
 -                                     cfg.namespace_id);
 -                              d(log, cfg.log_len, 16, 1);
 -                      } else
 -                              d_raw((unsigned char *)log, cfg.log_len);
 -              } else if (err > 0)
 -                      nvme_show_status(err);
 -              else
 -                      perror("log page");
 -              free(log);
 +      log = nvme_alloc(cfg.log_len, &huge);
 +      if (!log) {
 +              fprintf(stderr, "could not alloc buffer for log: %s\n",
 +                              strerror(errno));
 +              err = -errno;
 +              goto close_fd;
        }
 +
 +      err = nvme_get_log(fd, cfg.log_id, cfg.namespace_id, cfg.lpo, cfg.lsp, 
 +                           0, cfg.rae, cfg.uuid_index, cfg.log_len, log);
 +      if (!err) {
 +              if (!cfg.raw_binary) {
 +                      printf("Device:%s log-id:%d namespace-id:%#x\n",
 +                             devicename, cfg.log_id, cfg.namespace_id);
 +                      d(log, cfg.log_len, 16, 1);
 +              } else
 +                      d_raw((unsigned char *)log, cfg.log_len);
 +      } else
 +              nvme_show_status("log-page", err);
 +
 +      nvme_free(log, huge);
  close_fd:
        close(fd);
  ret:
@@@ -1133,18 -1037,16 +1141,13 @@@ static int delete_ns(int argc, char **a
                        err = cfg.namespace_id;
                        goto close_fd;
                }
-       } else if (!cfg.namespace_id) {
-               fprintf(stderr, "%s: namespace-id parameter required\n",
-                                               cmd->name);
-               err = -EINVAL;
-               goto close_fd;
        }
  
 -      err = nvme_ns_delete(fd, cfg.namespace_id, cfg.timeout);
 +      err = nvme_ns_mgmt_delete(fd, cfg.namespace_id);
        if (!err)
 -              printf("%s: Success, deleted nsid:%d\n", cmd->name,
 -                                                              cfg.namespace_id);
 -      else if (err > 0)
 -              nvme_show_status(err);
 +              printf("%s: Success, deleted nsid:%d\n", cmd->name, cfg.namespace_id);
        else
 -              perror("delete namespace");
 +              nvme_show_status("delete-namespace", err);
  
  close_fd:
        close(fd);
@@@ -1185,12 -1090,16 +1188,8 @@@ static int nvme_attach_ns(int argc, cha
        }
  
        num = argconfig_parse_comma_sep_array(cfg.cntlist, list, 2047);
-       if (num <= 0) {
-               fprintf(stderr, "%s: controller id list is required\n",
-                                               cmd->name);
-               err = -EINVAL;
-               goto close_fd;
-       }
 -      if (!num) {
++      if (num <= 0)
+               fprintf(stderr, "warning: empty controller-id list will result in no actual change in namespace attachment\n");
 -      }
 -
 -      if (num == -1) {
 -              fprintf(stderr, "%s: controller id list is malformed\n",
 -                                              cmd->name);
 -              err = -EINVAL;
 -              goto close_fd;
 -      }
  
        for (i = 0; i < num; i++)
                ctrlist[i] = (uint16_t)list[i];
        return nvme_status_to_errno(err, false);
  }
  
 +static bool nvme_match_device_filter(nvme_subsystem_t s)
 +{
 +      nvme_ctrl_t c;
 +      nvme_ns_t n;
 +
 +      if (!devicename || !strlen(devicename))
 +              return true;
 +
 +      nvme_subsystem_for_each_ctrl(s, c)
 +              if (!strcmp(devicename, nvme_ctrl_get_name(c)))
 +                      return true;
 +
 +      nvme_subsystem_for_each_ns(s, n)
 +              if (!strcmp(devicename, nvme_ns_get_name(n)))
 +                      return true;
 +
 +      return false;
 +}
 +
 +static void nvme_show_subsystem_list(nvme_root_t r, unsigned long flags)
 +{
 +      nvme_subsystem_t s, _s;
 +      nvme_ctrl_t c, _c;
 +      nvme_path_t p, _p;
 +      nvme_ns_t n, _n;
 +
 +      printf(".\n");
 +      nvme_for_each_subsystem_safe(r, s, _s) {
 +              printf("%c-- %s - NQN=%s\n",
 +                      _s ? '|' : '`',
 +                      nvme_subsystem_get_name(s),
 +                      nvme_subsystem_get_nqn(s));
 +
 +              nvme_subsystem_for_each_ns_safe(s, n, _n) {
 +                      printf("%c   |-- %s lba size:%d lba max:%lu\n",
 +                              _s ? '|' : ' ',
 +                              nvme_ns_get_name(n), nvme_ns_get_lba_size(n),
 +                              nvme_ns_get_lba_count(n));
 +              }
 +
 +              nvme_subsystem_for_each_ctrl_safe(s, c, _c) {
 +                      printf("%c   %c-- %s %s %s %s\n",
 +                              _s ? '|' : ' ',
 +                              _c ? '|' : '`',
 +                              nvme_ctrl_get_name(c),
 +                              nvme_ctrl_get_transport(c),
 +                              nvme_ctrl_get_address(c),
 +                              nvme_ctrl_get_state(c));
 +
 +                      nvme_ctrl_for_each_ns_safe(c, n, _n) 
 +                              printf("%c   %c   %c-- %s lba size:%d lba max:%lu\n",
 +                                      _s ? '|' : ' ', 
 +                                      _c ? '|' : ' ', 
 +                                      _n ? '|' : '`',
 +                                      nvme_ns_get_name(n),
 +                                      nvme_ns_get_lba_size(n),
 +                                      nvme_ns_get_lba_count(n));
 +
 +                      nvme_ctrl_for_each_path_safe(c, p, _p) 
 +                              printf("%c   %c   %c-- %s %s\n",
 +                                      _s ? '|' : ' ',
 +                                      _c ? '|' : ' ', 
 +                                      _p ? '|' : '`',
 +                                      nvme_path_get_name(p),
 +                                      nvme_path_get_ana_state(p));
 +              }
 +      }
 +      printf("\n");
 +}
 +
 +static const char dash[101] = {[0 ... 99] = '-'};
 +
 +static void nvme_show_list(nvme_root_t r, unsigned long flags)
 +{
 +      nvme_subsystem_t s;
 +      nvme_ctrl_t c;
 +      nvme_path_t p;
 +      nvme_ns_t n;
 +
 +      printf("%-16s %-96s %-.16s\n", "Subsystem", "Subsystem-NQN", "Controllers");
 +      printf("%-.16s %-.96s %-.16s\n", dash, dash, dash);
 +
 +      nvme_for_each_subsystem(r, s) {
 +              bool first = true;
 +              printf("%-16s %-96s ", nvme_subsystem_get_name(s), nvme_subsystem_get_nqn(s));
 +
 +              nvme_subsystem_for_each_ctrl(s, c) {
 +                      printf("%s%s", first ? "": ", ", nvme_ctrl_get_name(c));
 +                      first = false;
 +              }
 +              printf("\n");
 +      }
 +      printf("\n");
 +
 +      printf("%-8s %-20s %-40s %-8s %-6s %-14s %-12s %-16s\n", "Device",
 +              "SN", "MN", "FR", "TxPort", "Address", "Subsystem", "Namespaces");
 +      printf("%-.8s %-.20s %-.40s %-.8s %-.6s %-.14s %-.12s %-.16s\n", dash, dash,
 +              dash, dash, dash, dash, dash, dash);
 +
 +      nvme_for_each_subsystem(r, s) {
 +              nvme_subsystem_for_each_ctrl(s, c) {
 +                      bool first = true;
 +
 +                      printf("%-8s %-20s %-40s %-8s %-6s %-14s %-12s ",
 +                              nvme_ctrl_get_name(c), nvme_ctrl_get_serial(c),
 +                              nvme_ctrl_get_model(c), nvme_ctrl_get_firmware(c),
 +                              nvme_ctrl_get_transport(c), nvme_ctrl_get_address(c),
 +                              nvme_subsystem_get_name(s));
 +
 +                      nvme_ctrl_for_each_ns(c, n) {
 +                              printf("%s%s", first ? "": ", ",
 +                                      nvme_ns_get_name(n));
 +                              first = false;
 +                      }
 +
 +                      nvme_ctrl_for_each_path(c, p) {
 +                              printf("%s%s", first ? "": ", ",
 +                                      nvme_ns_get_name(nvme_path_get_ns(p)));
 +                              first = false;
 +                      }
 +                      printf("\n");
 +              }
 +      }
 +      printf("\n");
 +
 +      printf("%-12s %-8s %-26s %-16s %-16s\n", "Device", "NSID", "Usage", "Format", "Controllers");
 +      printf("%-.12s %-.8s %-.26s %-.16s %-.16s\n", dash, dash, dash, dash, dash);
 +
 +      nvme_for_each_subsystem(r, s) {
 +              nvme_subsystem_for_each_ctrl(s, c)
 +                      nvme_ctrl_for_each_ns(c, n)
 +                              printf("%-12s %8d %lu/%lu %16d %s\n",
 +                                      nvme_ns_get_name(n),
 +                                      nvme_ns_get_nsid(n),
 +                                      nvme_ns_get_lba_count(n),
 +                                      nvme_ns_get_lba_util(n),
 +                                      nvme_ns_get_lba_size(n),
 +                                      nvme_ctrl_get_name(c));
 +
 +              nvme_subsystem_for_each_ns(s, n) {
 +                      bool first = true;
 +
 +                      printf("%-12s %8d %lu/%lu %16d ",
 +                              nvme_ns_get_name(n),
 +                              nvme_ns_get_nsid(n),
 +                              nvme_ns_get_lba_count(n),
 +                              nvme_ns_get_lba_util(n),
 +                              nvme_ns_get_lba_size(n));
 +
 +                      nvme_subsystem_for_each_ctrl(s, c) {
 +                              printf("%s%s", first ? "" : ", ",
 +                                      nvme_ctrl_get_name(c));
 +                              first = false;
 +                      }
 +                      printf("\n");
 +              }
 +      }
 +}
 +
  static int list_subsys(int argc, char **argv, struct command *cmd,
                struct plugin *plugin)
  {
@@@ -1590,19 -1363,21 +1588,18 @@@ static int list(int argc, char **argv, 
        err = flags = validate_output_format(cfg.output_format);
        if (flags < 0)
                return err;
 -      if (flags != JSON && flags != NORMAL) {
 -              fprintf(stderr, "Invalid output format\n");
 -              return -EINVAL;
 -      }
        if (cfg.verbose)
 -              flags |= VERBOSE;
 +              flags |= 0;
  
 -      err = scan_subsystems(&t, NULL, 0);
 -      if (err) {
 -              fprintf(stderr, "Failed to scan namespaces\n");
 -              return err;
 +      r = nvme_scan();
 +      if (r) {
 +              nvme_show_list(r, flags);
 +              nvme_free_tree(r);
 +      } else {
 +              fprintf(stderr, "Failed to scan nvme subssytems\n");
 +              err = -errno;
        }
  
 -      nvme_show_list_items(&t, flags);
 -      free_topology(&t);
        return 0;
  }
  
@@@ -1795,22 -1574,21 +1792,23 @@@ static int id_ns(int argc, char **argv
                        err = cfg.namespace_id;
                        goto close_fd;
                }
-       } else if (!cfg.namespace_id) {
-               fprintf(stderr,
-                       "Error: requesting namespace-id from non-block device\n");
-               err = -ENOTBLK;
-               goto close_fd;
+               else if (!cfg.namespace_id) {
+                       fprintf(stderr,
+                               "Error: requesting namespace-id from non-block device\n");
+                       err = -ENOTBLK;
+                       goto close_fd;
+               }
        }
  
 -      err = nvme_identify_ns(fd, cfg.namespace_id, cfg.force, &ns);
 +      if (cfg.force)
 +              err = nvme_identify_allocated_ns(fd, cfg.namespace_id, &ns);
 +      else
 +              err = nvme_identify_ns(fd, cfg.namespace_id, &ns);
 +
        if (!err)
                nvme_show_id_ns(&ns, cfg.namespace_id, flags);
 -      else if (err > 0)
 -              nvme_show_status(err);
        else
 -              perror("identify namespace");
 +              nvme_show_status("identify-namespace", err);
  close_fd:
        close(fd);
  ret:
@@@ -4562,9 -4644,9 +4565,10 @@@ static int passthru(int argc, char **ar
        if (cfg.metadata_len) {
                metadata = malloc(cfg.metadata_len);
                if (!metadata) {
--                      fprintf(stderr, "can not allocate metadata "
--                                      "payload: %s\n", strerror(errno));
 -                      err = -ENOMEM;
++                      fprintf(stderr,
++                              "can not allocate metadata payload: %s\n",
++                              strerror(errno));
 +                      err = -1;
                        goto close_wfd;
                }
                memset(metadata, cfg.prefill, cfg.metadata_len);
@@@ -4672,12 -4747,12 +4676,12 @@@ static int admin_passthru(int argc, cha
  {
        const char *desc = "Send a user-defined Admin command to the specified "\
                "device via IOCTL passthrough, return results.";
 -      return passthru(argc, argv, NVME_IOCTL_ADMIN_CMD, desc, cmd);
 +      return passthru(argc, argv, true, desc, cmd);
  }
  
 -#ifdef LIBUUID
  static int gen_hostnqn_cmd(int argc, char **argv, struct command *command, struct plugin *plugin)
  {
- #ifdef LIBUUIDX
++#ifdef LIBUUID
        uuid_t uuid;
        char uuid_str[37]; /* e.g. 1b4e28ba-2fa1-11d2-883f-0016d3cca427 + \0 */
  
@@@ -4696,309 -4774,15 +4700,309 @@@ static int show_hostnqn_cmd(int argc, c
  {
        char *hostnqn;
  
 -      hostnqn = hostnqn_read();
 -      if (hostnqn) {
 -              fputs(hostnqn, stdout);
 -              free(hostnqn);
 -              return 0;
 -      } else {
 +      hostnqn = nvmf_hostnqn_from_file();
 +      if (!hostnqn)
 +              hostnqn =  nvmf_hostnqn_generate();
 +
 +      if (!hostnqn) {
                fprintf(stderr, "hostnqn is not available -- use nvme gen-hostnqn\n");
 -              return -ENOENT;
 +              return ENOENT;
 +      }
 +
 +      fprintf(stdout, hostnqn);
 +      free(hostnqn);
 +
 +      return 0;
 +}
 +
 +static bool nvme_ctrl_match_cfg(nvme_ctrl_t c, struct nvme_fabrics_config *cfg)
 +{
 +      const int size = 0x100;
 +      char addr[size];
 +      const char *a, *n, *t;
 +      int len = 0;
 +
 +      if (!c)
 +              return false;
 +
 +      a = nvme_ctrl_get_address(c);
 +      n = nvme_ctrl_get_subsysnqn(c);
 +      t = nvme_ctrl_get_transport(c);
 +
 +      memset(addr, 0, size);
 +      if (cfg->traddr)
 +                len += snprintf(addr, size, "traddr=%s", cfg->traddr);
 +      if (cfg->trsvcid)
 +                len += snprintf(addr + len, size - len, "%strsvcid=%s",
 +                                len ? "," : "", cfg->trsvcid);
 +      if (cfg->host_traddr)
 +                len += snprintf(addr + len, size - len, "%shost_traddr=%s",
 +                                len ? "," : "", cfg->host_traddr);
 +
 +      if (t && cfg->transport)
 +              if (strcmp(t, cfg->transport))
 +                      return false;
 +
 +      if (n && cfg->nqn)
 +              if (strcmp(n, cfg->nqn))
 +                      return false;
 +
 +      if (a && strlen(addr))
 +              if (strcmp(a, addr))
 +                      return false;
 +
 +      return true;
 +}
 +
 +static void space_strip_len(int max, char *str)
 +{
 +      int i;
 +
 +      for (i = max - 1; i >= 0; i--) {
 +              if (str[i] != '\0' && str[i] != ' ')
 +                      return;
 +              else
 +                      str[i] = '\0';
 +      }
 +}
 +
 +static void save_discovery_log(struct nvmf_discovery_log *log)
 +{
 +      uint64_t numrec = le64_to_cpu(log->numrec);
 +      int fd, len, ret;
 +
 +      fd = open(raw, O_CREAT|O_RDWR|O_TRUNC, S_IRUSR|S_IWUSR);
 +      if (fd < 0) {
 +              fprintf(stderr, "failed to open %s: %s\n",
 +                      raw, strerror(errno));
 +              return;
 +      }
 +
 +      len = sizeof(struct nvmf_discovery_log) +
 +              numrec * sizeof(struct nvmf_disc_log_entry);
 +      ret = write(fd, log, len);
 +      if (ret < 0)
 +              fprintf(stderr, "failed to write to %s: %s\n",
 +                      raw, strerror(errno));
 +      else
 +              printf("Discovery log is saved to %s\n", raw);
 +
 +      close(fd);
 +}
 +
 +static int nvmf_discover(nvme_ctrl_t c, const struct nvme_fabrics_config *defcfg,
 +      bool connect)
 +{
 +      struct nvmf_discovery_log *log = NULL;
 +      int ret;
 +
 +      ret = nvmf_get_discovery_log(c, &log, MAX_DISC_RETRIES);
 +      if (ret) {
 +              nvme_show_status("nvmf-discover-log", ret);
 +              return nvme_status_to_errno(ret, false);
 +      }
 +
 +      if (raw)
 +              save_discovery_log(log);
 +      else if (!connect)
 +              nvme_print_object(nvme_discovery_log_to_json(log, 0));
 +      else if (connect) {
 +              uint64_t numrec;
 +              int i;
 +
 +              numrec = le64_to_cpu(log->numrec);
 +              for (i = 0; i < numrec; i++) {
 +                      struct nvmf_disc_log_entry *e = &log->entries[i];
 +                      bool discover = false;
 +                      nvme_ctrl_t child;
 +
 +                      errno = 0;
 +                      child = nvmf_connect_disc_entry(e, defcfg, &discover);
 +                      if (child) {
 +                              if (discover)
 +                                      nvmf_discover(child, defcfg, true);
 +                              if (!persistent)
 +                                      nvme_ctrl_disconnect(c);
 +                              nvme_free_ctrl(child);
 +                      } else if (errno == EALREADY && !quiet) {
 +                              char *traddr = log->entries[i].traddr;
 +
 +                              space_strip_len(NVMF_TRADDR_SIZE, traddr);
 +                              fprintf(stderr, "traddr=%s is already connected\n",
 +                                      traddr);
 +                      }
 +              }
 +      }
 +
 +      free(log);
 +      return 0;
 +}
 +
 +static int discover_from_conf_file(const char *desc, bool connect,
 +      const struct nvme_fabrics_config *defcfg)
 +{
 +      char *ptr, **argv, *p, line[4096];
 +      int argc, ret = 0;
 +      FILE *f;
 +
 +      struct nvme_fabrics_config cfg = { 0 };
 +
 +      OPT_ARGS(opts) = {
 +              NVMF_OPTS(cfg),
 +      };
 +
 +      f = fopen(PATH_NVMF_DISC, "r");
 +      if (f == NULL) {
 +              errno = ENOENT;
 +              return -1;
 +      }
 +
 +      argv = calloc(MAX_DISC_ARGS, sizeof(char *));
 +      if (!argv) {
 +              ret = -1;
 +              goto out;
 +      }
 +
 +      argv[0] = "discover";
 +      memset(line, 0, sizeof(line));
 +      while (fgets(line, sizeof(line), f) != NULL) {
 +              nvme_ctrl_t c;
 +
 +              if (line[0] == '#' || line[0] == '\n')
 +                      continue;
 +
 +              argc = 1;
 +              p = line;
 +              while ((ptr = strsep(&p, " =\n")) != NULL)
 +                      argv[argc++] = ptr;
 +              argv[argc] = NULL;
 +
 +              memcpy(&cfg, defcfg, sizeof(cfg));
 +              ret = argconfig_parse(argc, argv, desc, opts);
 +              if (ret)
 +                      goto next;
 +
 +              if (!cfg.transport && !cfg.traddr)
 +                      goto next;
 +
 +              errno = 0;
 +              c = nvmf_add_ctrl(&cfg);
 +              if (c) {
 +                      nvmf_discover(c, defcfg, connect);
 +                              return 0;
 +                      if (!persistent)
 +                              ret = nvme_ctrl_disconnect(c);
 +                      nvme_free_ctrl(c);
 +              }
 +next:
 +              memset(&cfg, 0, sizeof(cfg));
 +      }
 +      free(argv);
 +out:
 +      fclose(f);
 +      return ret;
 +}
 +
 +const static char *nqn_match;
 +static bool nvme_match_subsysnqn_filter(nvme_subsystem_t s)
 +{
 +      if (nqn_match && strlen(nqn_match))
 +              return strcmp(nvme_subsystem_get_nqn(s), nqn_match) == 0;
 +      return true;
 +}
 +
 +
 +static nvme_ctrl_t nvme_find_matching_ctrl(struct nvme_fabrics_config *cfg)
 +{
 +      nvme_subsystem_t s;
 +      nvme_root_t r;
 +      nvme_ctrl_t c;
 +
 +      nqn_match = cfg->nqn;
 +      r = nvme_scan_filter(nvme_match_subsysnqn_filter);
 +      nvme_for_each_subsystem(r, s) {
 +              nvme_subsystem_for_each_ctrl(s, c) {
 +                      if (nvme_ctrl_match_cfg(c, cfg)) {
 +                              nvme_unlink_ctrl(c);
 +                              goto found;
 +                      }
 +              }
 +      }
 +found:
 +      nvme_free_tree(r);
 +      return c;
 +}
 +
 +int discover(const char *desc, int argc, char **argv, bool connect)
 +{
 +      char *hnqn = NULL, *hid = NULL;
 +      int ret;
 +
 +      struct nvme_fabrics_config cfg = {
 +              .nqn = NVME_DISC_SUBSYS_NAME,
 +              .tos = -1,
 +      };
 +
 +      char *device = NULL;
 +
 +      OPT_ARGS(opts) = {
-               OPT_LIST("device",     'd', &device,     "use existing discovery controller device"),
++              OPT_STRING("device",   'd', "DEV", &device, "use existing discovery controller device"),
 +              NVMF_OPTS(cfg),
-               OPT_FILE("raw",        'r', &raw,        "save raw output to file"),
-               OPT_FLAG("persistent", 'p', &persistent, "persistent discovery connection"),
-               OPT_FLAG("quiet",      'S', &quiet,      "suppress already connected errors"),
++              OPT_FILE("raw",        'r', &raw,           "save raw output to file"),
++              OPT_FLAG("persistent", 'p', &persistent,    "persistent discovery connection"),
++              OPT_FLAG("quiet",      'S', &quiet,         "suppress already connected errors"),
 +              OPT_END()
 +      };
 +
 +      ret = argconfig_parse(argc, argv, desc, opts);
 +      if (ret)
 +              return ret;
 +
 +      if (persistent && !cfg.keep_alive_tmo)
 +              cfg.keep_alive_tmo = 30;
 +      if (!cfg.hostnqn)
 +              cfg.hostnqn = hnqn = nvmf_hostnqn_from_file();
 +      if (device && !strcmp(device, "none"))
 +              device = NULL;
 +
 +      if (!device && !cfg.transport && !cfg.traddr)
 +              ret = discover_from_conf_file(desc, connect, &cfg);
 +      else {
 +              nvme_ctrl_t c = NULL;
 +
 +              if (device) {
 +                      c = nvme_scan_ctrl(device);
 +                      if (c && !nvme_ctrl_match_cfg(c, &cfg)) {
 +                              nvme_free_ctrl(c);
 +                              device = NULL;
 +                              c = NULL;
 +                      }
 +                      if (!c)
 +                              c = nvme_find_matching_ctrl(&cfg);
 +              }
 +
 +              if (!c) {
 +                      errno = 0;
 +                      c = nvmf_add_ctrl(&cfg);
 +              }
 +
 +              if (c) {
 +                      ret = nvmf_discover(c, &cfg, connect);
 +                      if (!device && !persistent)
 +                              nvme_ctrl_disconnect(c);
 +                      nvme_free_ctrl(c);
 +              } else {
 +                      fprintf(stderr, "no controller found\n");
 +                      ret = errno;
 +              }
        }
 +
 +      if (hnqn)
 +              free(hnqn);
 +      if (hid)
 +              free(hid);
 +
 +      return ret;
  }
  
  static int discover_cmd(int argc, char **argv, struct command *command, struct plugin *plugin)
index caf02029fb86b62777bf20eb4085e0feb7162720,482ea0d3f545028d05f83e08e827f4e22103dde8..8fe9fd3bf48e71bafe48af36324a52d8fa77996d
@@@ -80,11 -86,10 +82,10 @@@ static int huawei_get_nvme_info(int fd
                item->huawei_device = false;
                return 0;
        }
-       else
-               item->huawei_device = true;
  
+       item->huawei_device = true;
        item->nsid = nvme_get_nsid(fd);
 -      err = nvme_identify_ns(fd, item->nsid, 0, &item->ns);
 +      err = nvme_identify_ns(fd, item->nsid, &item->ns);
        if (err)
                return err;
  
index e8503930ecdbbfc59b92c947ec581d86f8b8a90f,8217c466ebaf6b063acd3f925a4bfeaae358c172..6e2440e4909d905c581005103f6285dd77e77ef5
@@@ -1211,3 -1228,89 +1224,89 @@@ static int get_internal_log(int argc, c
        free(intel);
        return err;
  }
 -              err = nvme_get_feature(fd, nsid, fid, sel, cdw11, data_len, buf,
 -                                      &result);
+ static int enable_lat_stats_tracking(int argc, char **argv,
+               struct command *command, struct plugin *plugin)
+ {
+       int err, fd;
+       const char *desc = (
+                       "Enable/Disable Intel Latency Statistics Tracking.\n"
+                       "No argument prints current status.");
+       const char *enable_desc = "Enable LST";
+       const char *disable_desc = "Disable LST";
+       const __u32 nsid = 0;
+       const __u8 fid = 0xe2;
+       const __u8 sel = 0;
+       const __u32 cdw11 = 0x0;
+       const __u32 cdw12 = 0x0;
+       const __u32 data_len = 32;
+       const __u32 save = 0;
+       __u32 result;
+       void *buf = NULL;
+       struct config {
+               bool enable, disable;
+       };
+       struct config cfg = {
+               .enable = false,
+               .disable = false,
+       };
+       const struct argconfig_commandline_options command_line_options[] = {
+               {"enable", 'e', "", CFG_NONE, &cfg.enable, no_argument, enable_desc},
+               {"disable", 'd', "", CFG_NONE, &cfg.disable, no_argument, disable_desc},
+               {NULL}
+       };
+       fd = parse_and_open(argc, argv, desc, command_line_options);
+       enum Option {
+               None = -1,
+               True = 1,
+               False = 0,
+       };
+       enum Option option = None;
+       if (cfg.enable && cfg.disable)
+               printf("Cannot enable and disable simultaneously.");
+       else if (cfg.enable || cfg.disable)
+               option = cfg.enable;
+       if (fd < 0)
+               return fd;
+       switch (option) {
+       case None:
 -              err = nvme_set_feature(fd, nsid, fid, option, cdw12, save,
 -                              data_len, buf, &result);
++              err = nvme_get_features(fd, fid, nsid, sel, cdw11, 0, data_len,
++                                      buf, &result);
+               if (!err) {
+                       printf(
+                               "Latency Statistics Tracking (FID 0x%X) is currently (%i).\n",
+                               fid, result);
+               } else {
+                       printf("Could not read feature id 0xE2.\n");
+                       return err;
+               }
+               break;
+       case True:
+       case False:
 -                                      nvme_status_to_string(err), err);
++              err = nvme_set_features(fd, fid, nsid, option, cdw12, save,
++                              0, 0, data_len, buf, &result);
+               if (err > 0) {
+                       fprintf(stderr, "NVMe Status:%s(%x)\n",
++                                      nvme_status_to_string(err, false), err);
+               } else if (err < 0) {
+                       perror("Enable latency tracking");
+                       fprintf(stderr, "Command failed while parsing.\n");
+               } else {
+                       printf("Successfully set enable bit for FID (0x%X) to %i.\n",
+                               fid, option);
+               }
+               break;
+       default:
+               printf("%d not supported.\n", option);
+               return EINVAL;
+       }
+       return fd;
+ }
index 2871e609e0cc1539a368f93c759679ec01c1eec8,c75f49cd4dacd2a39df8492d455881aec0ad245d..ff0bcb259cf796004058b8059321ca4341c00bab
  
  #define CREATE_CMD
  #include "memblaze-nvme.h"
 -#include "memblaze-utils.h"
  
++#define SMART_INFO_OLD_SIZE     512
++#define SMART_INFO_NEW_SIZE     4096
++
++#define ID_SIZE                 3
++#define NM_SIZE                 2
++#define RAW_SIZE                7
++
++typedef unsigned char           u8;
++
++// Intel Format & new format
++/* Raisin Additional smart external ID */
++#define RAISIN_SI_VD_PROGRAM_FAIL_ID                        0xAB
++#define RAISIN_SI_VD_ERASE_FAIL_ID                          0xAC
++#define RAISIN_SI_VD_WEARLEVELING_COUNT_ID                  0xAD
++#define RAISIN_SI_VD_E2E_DECTECTION_COUNT_ID                0xB8
++#define RAISIN_SI_VD_PCIE_CRC_ERR_COUNT_ID                  0xC7
++#define RAISIN_SI_VD_TIMED_WORKLOAD_MEDIA_WEAR_ID           0xE2
++#define RAISIN_SI_VD_TIMED_WORKLOAD_HOST_READ_ID            0xE3
++#define RAISIN_SI_VD_TIMED_WORKLOAD_TIMER_ID                0xE4
++#define RAISIN_SI_VD_THERMAL_THROTTLE_STATUS_ID             0xEA
++#define RAISIN_SI_VD_RETRY_BUFF_OVERFLOW_COUNT_ID           0xF0
++#define RAISIN_SI_VD_PLL_LOCK_LOSS_COUNT_ID                 0xF3
++#define RAISIN_SI_VD_TOTAL_WRITE_ID                         0xF4
++#define RAISIN_SI_VD_HOST_WRITE_ID                          0xF5
++#define RAISIN_SI_VD_SYSTEM_AREA_LIFE_LEFT_ID               0xF6
++#define RAISIN_SI_VD_TOTAL_READ_ID                          0xFA
++#define RAISIN_SI_VD_TEMPT_SINCE_BORN_ID                    0xE7
++#define RAISIN_SI_VD_POWER_CONSUMPTION_ID                   0xE8
++#define RAISIN_SI_VD_TEMPT_SINCE_BOOTUP_ID                  0xAF
++#define RAISIN_SI_VD_POWER_LOSS_PROTECTION_ID               0xEC
++#define RAISIN_SI_VD_READ_FAIL_ID                           0xF2
++#define RAISIN_SI_VD_THERMAL_THROTTLE_TIME_ID               0xEB
++#define RAISIN_SI_VD_FLASH_MEDIA_ERROR_ID                   0xED
++
++/* Raisin Addtional smart internal ID */
++typedef enum
++{
++    /* smart attr following intel */
++    RAISIN_SI_VD_PROGRAM_FAIL = 0, /* 0xAB */
++    RAISIN_SI_VD_ERASE_FAIL = 1,   /* 0xAC */
++    RAISIN_SI_VD_WEARLEVELING_COUNT = 2,/* 0xAD */
++    RAISIN_SI_VD_E2E_DECTECTION_COUNT = 3, /* 0xB8 */
++    RAISIN_SI_VD_PCIE_CRC_ERR_COUNT = 4, /* 0xC7, 2 port data in one attribute */
++    RAISIN_SI_VD_TIMED_WORKLOAD_MEDIA_WEAR = 5, /* 0xE2 , unknown definition*/
++    RAISIN_SI_VD_TIMED_WORKLOAD_HOST_READ = 6, /* 0xE3 , unknown definition */
++    RAISIN_SI_VD_TIMED_WORKLOAD_TIMER = 7, /* 0xE4 , unknown definition */
++    RAISIN_SI_VD_THERMAL_THROTTLE_STATUS = 8, /* 0xEA */
++    RAISIN_SI_VD_RETRY_BUFF_OVERFLOW_COUNT = 9, /* 0xF0, unknown definition*/
++    RAISIN_SI_VD_PLL_LOCK_LOSS_COUNT = 10, /* 0xF3, unknown definition*/
++    RAISIN_SI_VD_TOTAL_WRITE = 11, /* 0xF4, unit is 32MiB */
++    RAISIN_SI_VD_HOST_WRITE = 12, /* 0xF5, unit is 32MiB */
++    RAISIN_SI_VD_SYSTEM_AREA_LIFE_LEFT = 13, /* 0xF6, unknown definition*/
++    RAISIN_SI_VD_TOTAL_READ = 14, /* 0xFA, unit is 32MiB */
++
++    /* smart attr self defined */
++    RAISIN_SI_VD_TEMPT_SINCE_BORN = 15, /* 0xE7 */
++    RAISIN_SI_VD_POWER_CONSUMPTION = 16, /* 0xE8 */
++    RAISIN_SI_VD_TEMPT_SINCE_BOOTUP = 17, /* 0xAF */
++    RAISIN_SI_VD_POWER_LOSS_PROTECTION = 18, /* 0xEC */
++    RAISIN_SI_VD_READ_FAIL = 19, /* 0xF2 */
++    RAISIN_SI_VD_THERMAL_THROTTLE_TIME = 20, /* 0xEB */
++    RAISIN_SI_VD_FLASH_MEDIA_ERROR = 21, /* 0xED */
++    RAISIN_SI_VD_SMART_INFO_ITEMS_MAX,
++} RAISIN_si_vendor_smart_item_e;
++
++// Memblaze Format & old format
  enum {
-       TOTAL_WRITE,
-       TOTAL_READ,
-       THERMAL_THROTTLE,
-       TEMPT_SINCE_RESET,
-       POWER_CONSUMPTION,
-       TEMPT_SINCE_BOOTUP,
-       POWER_LOSS_PROTECTION,
-       WEARLEVELING_COUNT,
-       HOST_WRITE,
-       THERMAL_THROTTLE_CNT,
-       CORRECT_PCIE_PORT0,
-       CORRECT_PCIE_PORT1,
-       REBUILD_FAIL,
-       ERASE_FAIL,
-       PROGRAM_FAIL,
-       READ_FAIL,
-       NR_SMART_ITEMS,
 -    MB_FEAT_POWER_MGMT = 0xc6,
++    /*0*/TOTAL_WRITE = 0,
++    /*1*/TOTAL_READ,
++    /*2*/THERMAL_THROTTLE,
++    /*3*/TEMPT_SINCE_RESET,
++    /*4*/POWER_CONSUMPTION,
++    /*5*/TEMPT_SINCE_BOOTUP,
++    /*6*/POWER_LOSS_PROTECTION,
++    /*7*/WEARLEVELING_COUNT,
++    /*8*/HOST_WRITE,
++    /*9*/THERMAL_THROTTLE_CNT,
++    /*10*/CORRECT_PCIE_PORT0,
++    /*11*/CORRECT_PCIE_PORT1,
++    /*12*/REBUILD_FAIL,
++    /*13*/ERASE_FAIL,
++    /*14*/PROGRAM_FAIL,
++    /*15*/READ_FAIL,
++    /*16*/NR_SMART_ITEMS = RAISIN_SI_VD_SMART_INFO_ITEMS_MAX,
 +};
 +
- enum {
-       MB_FEAT_POWER_MGMT = 0xc6,
++// Memblaze Format & old format
++struct __attribute__((__packed__)) nvme_memblaze_smart_log_item {
++        __u8 id[3];
++        union {
++            __u8    __nmval[2];
++            __le16  nmval;
++        };
++        union {
++        __u8 rawval[6];
++        struct temperature {
++        __le16 max;
++        __le16 min;
++        __le16 curr;
++        } temperature;
++        struct power {
++            __le16 max;
++            __le16 min;
++            __le16 curr;
++        } power;
++        struct thermal_throttle_mb {
++            __u8 on;
++            __u32 count;
++        } thermal_throttle;
++        struct temperature_p {
++            __le16 max;
++            __le16 min;
++        } temperature_p;
++        struct power_loss_protection {
++            __u8 curr;
++        } power_loss_protection;
++        struct wearleveling_count {
++            __le16 min;
++            __le16 max;
++            __le16 avg;
++        } wearleveling_count;
++        struct thermal_throttle_cnt {
++            __u8 active;
++            __le32 cnt;
++        } thermal_throttle_cnt;
++    };
++    __u8 resv;
 +};
 +
- #pragma pack(push, 1)
- struct nvme_memblaze_smart_log_item {
-       __u8 id[3];
-       union {
-               __u8    __nmval[2];
-               __le16  nmval;
-       };
-       union {
-               __u8 rawval[6];
-               struct temperature {
-                       __le16 max;
-                       __le16 min;
-                       __le16 curr;
-               } temperature;
-               struct power {
-                       __le16 max;
-                       __le16 min;
-                       __le16 curr;
-               } power;
-               struct thermal_throttle_mb {
-                       __u8 on;
-                       __u32 count;
-               } thermal_throttle;
-               struct temperature_p {
-                       __le16 max;
-                       __le16 min;
-               } temperature_p;
-               struct power_loss_protection {
-                       __u8 curr;
-               } power_loss_protection;
-               struct wearleveling_count {
-                       __le16 min;
-                       __le16 max;
-                       __le16 avg;
-               } wearleveling_count;
-               struct thermal_throttle_cnt {
-                       __u8 active;
-                       __le32 cnt;
-               } thermal_throttle_cnt;
-       };
-       __u8 resv;
++struct nvme_memblaze_smart_log {
++    struct nvme_memblaze_smart_log_item items[NR_SMART_ITEMS];
++    u8 resv[SMART_INFO_OLD_SIZE - sizeof(struct nvme_memblaze_smart_log_item) * NR_SMART_ITEMS];
 +};
- #pragma pack(pop)
 +
- struct nvme_memblaze_smart_log {
-       struct nvme_memblaze_smart_log_item items[NR_SMART_ITEMS];
-       __u8 resv[512 - sizeof(struct nvme_memblaze_smart_log_item) * NR_SMART_ITEMS];
++// Intel Format & new format
++struct nvme_p4_smart_log_item
++{
++    /* Item identifier */
++    u8 id[ID_SIZE];
++    /* Normalized value or percentage. In the range from 0 to 100. */
++    u8 nmVal[NM_SIZE];
++    /* raw value */
++    u8 rawVal[RAW_SIZE];
++};
++
++struct nvme_p4_smart_log
++{
++    struct nvme_p4_smart_log_item itemArr[NR_SMART_ITEMS];
++
++    /**
++     * change 512 to 4096.
++     * because micron's getlogpage request,the size of many commands have changed to 4k.
++     * request size > user malloc size,casuing parameters that are closed in momery are dirty.
++     */
++    u8 resv[SMART_INFO_NEW_SIZE - sizeof(struct nvme_p4_smart_log_item) * NR_SMART_ITEMS];
++};
++
++enum {
++      MB_FEAT_POWER_MGMT = 0xc6,
  };
  
  /*
@@@ -272,9 -424,336 +576,324 @@@ static int get_additional_smart_log(in
  static char *mb_feature_to_string(int feature)
  {
        switch (feature) {
--      case MB_FEAT_POWER_MGMT: return "Memblaze power management";
--      default:        return "Unknown";
++      case MB_FEAT_POWER_MGMT:return "Memblaze power management";
++      default:                return "Unknown";
+       }
+ }
+ static int get_additional_feature(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+ {
+       const char *desc = "Read operating parameters of the "\
+               "specified controller. Operating parameters are grouped "\
+               "and identified by Feature Identifiers; each Feature "\
+               "Identifier contains one or more attributes that may affect "\
+               "behaviour of the feature. Each Feature has three possible "\
+               "settings: default, saveable, and current. If a Feature is "\
+               "saveable, it may be modified by set-feature. Default values "\
+               "are vendor-specific and not changeable. Use set-feature to "\
+               "change saveable Features.\n\n"\
+               "Available additional feature id:\n"\
+               "0xc6:  Memblaze power management\n"\
+               "       (value 0 - 25w, 1 - 20w, 2 - 15w)";
+       const char *raw = "show feature in binary format";
+       const char *namespace_id = "identifier of desired namespace";
+       const char *feature_id = "hexadecimal feature name";
+       const char *sel = "[0-3]: curr./default/saved/supp.";
+       const char *data_len = "buffer len (if) data is returned";
+       const char *cdw11 = "dword 11 for interrupt vector config";
+       const char *human_readable = "show infos in readable format";
+       int err, fd;
+       __u32 result;
+       void *buf = NULL;
+       struct config {
+               __u32 namespace_id;
+               __u32 feature_id;
+               __u8  sel;
+               __u32 cdw11;
+               __u32 data_len;
+               int  raw_binary;
+               int  human_readable;
+       };
+       struct config cfg = {
+               .namespace_id = 1,
+               .feature_id   = 0,
+               .sel          = 0,
+               .cdw11        = 0,
+               .data_len     = 0,
+       };
+       OPT_ARGS(opts) = {
+               OPT_UINT("namespace-id",   'n', &cfg.namespace_id,   namespace_id),
+               OPT_UINT("feature-id",     'f', &cfg.feature_id,     feature_id),
+               OPT_BYTE("sel",            's', &cfg.sel,            sel),
+               OPT_UINT("data-len",       'l', &cfg.data_len,       data_len),
+               OPT_UINT("cdw11",          'c', &cfg.cdw11,          cdw11),
+               OPT_FLAG("human-readable", 'H', &cfg.human_readable, human_readable),
+               OPT_FLAG("raw-binary",     'b', &cfg.raw_binary,     raw),
+               OPT_END()
+       };
+       fd = parse_and_open(argc, argv, desc, opts);
+       if (fd < 0)
+               return fd;
+       if (cfg.sel > 7) {
+               fprintf(stderr, "invalid 'select' param:%d\n", cfg.sel);
+               return EINVAL;
+       }
+       if (!cfg.feature_id) {
+               fprintf(stderr, "feature-id required param\n");
+               return EINVAL;
+       }
+       if (cfg.data_len) {
+               if (posix_memalign(&buf, getpagesize(), cfg.data_len))
+                       exit(ENOMEM);
+               memset(buf, 0, cfg.data_len);
+       }
 -      err = nvme_get_feature(fd, cfg.namespace_id, cfg.feature_id, cfg.sel, cfg.cdw11,
 -                      cfg.data_len, buf, &result);
++      err = nvme_get_features(fd, cfg.feature_id, cfg.namespace_id, cfg.sel,
++                      cfg.cdw11, 0, cfg.data_len, buf, &result);
+       if (!err) {
+               printf("get-feature:0x%02x (%s), %s value: %#08x\n", cfg.feature_id,
+                               mb_feature_to_string(cfg.feature_id),
 -                              nvme_select_to_string(cfg.sel), result);
 -              if (cfg.human_readable)
 -                      nvme_feature_show_fields(cfg.feature_id, result, buf);
 -              else {
 -                      if (buf) {
 -                              if (!cfg.raw_binary)
 -                                      d(buf, cfg.data_len, 16, 1);
 -                              else
 -                                      d_raw(buf, cfg.data_len);
 -                      }
 -              }
++                              nvme_get_feature_select_to_string(cfg.sel), result);
+       } else if (err > 0)
+               fprintf(stderr, "NVMe Status:%s(%x)\n",
 -                              nvme_status_to_string(err), err);
++                              nvme_status_to_string(err, false), err);
+       if (buf)
+               free(buf);
+       return err;
+ }
+ static int set_additional_feature(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+ {
+       const char *desc = "Modify the saveable or changeable "\
+               "current operating parameters of the controller. Operating "\
+               "parameters are grouped and identified by Feature "\
+               "Identifiers. Feature settings can be applied to the entire "\
+               "controller and all associated namespaces, or to only a few "\
+               "namespace(s) associated with the controller. Default values "\
+               "for each Feature are vendor-specific and may not be modified."\
+               "Use get-feature to determine which Features are supported by "\
+               "the controller and are saveable/changeable.\n\n"\
+               "Available additional feature id:\n"\
+               "0xc6:  Memblaze power management\n"\
+               "       (value 0 - 25w, 1 - 20w, 2 - 15w)";
+       const char *namespace_id = "desired namespace";
+       const char *feature_id = "hex feature name (required)";
+       const char *data_len = "buffer length if data required";
+       const char *data = "optional file for feature data (default stdin)";
+       const char *value = "new value of feature (required)";
+       const char *save = "specifies that the controller shall save the attribute";
+       int err, fd;
+       __u32 result;
+       void *buf = NULL;
+       int ffd = STDIN_FILENO;
+       struct config {
+               char *file;
+               __u32 namespace_id;
+               __u32 feature_id;
+               __u32 value;
+               __u32 data_len;
+               int   save;
+       };
+       struct config cfg = {
+               .file         = "",
+               .namespace_id = 0,
+               .feature_id   = 0,
+               .value        = 0,
+               .data_len     = 0,
+               .save         = 0,
+       };
+       OPT_ARGS(opts) = {
+               OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id),
+               OPT_UINT("feature-id",   'f', &cfg.feature_id,   feature_id),
+               OPT_UINT("value",        'v', &cfg.value,        value),
+               OPT_UINT("data-len",     'l', &cfg.data_len,     data_len),
+               OPT_FILE("data",         'd', &cfg.file,         data),
+               OPT_FLAG("save",         's', &cfg.save,         save),
+               OPT_END()
+       };
+       fd = parse_and_open(argc, argv, desc, opts);
+       if (fd < 0)
+               return fd;
+       if (!cfg.feature_id) {
+               fprintf(stderr, "feature-id required param\n");
+               return EINVAL;
+       }
+       if (cfg.data_len) {
+               if (posix_memalign(&buf, getpagesize(), cfg.data_len))
+                       exit(ENOMEM);
+               memset(buf, 0, cfg.data_len);
+       }
+       if (buf) {
+               if (strlen(cfg.file)) {
+                       ffd = open(cfg.file, O_RDONLY);
+                       if (ffd <= 0) {
+                               fprintf(stderr, "no firmware file provided\n");
+                               err = EINVAL;
+                               goto free;
+                       }
+               }
+               if (read(ffd, (void *)buf, cfg.data_len) < 0) {
+                       fprintf(stderr, "failed to read data buffer from input file\n");
+                       err = EINVAL;
+                       goto free;
+               }
+       }
 -      err = nvme_set_feature(fd, cfg.namespace_id, cfg.feature_id, cfg.value,
 -                              0, cfg.save, cfg.data_len, buf, &result);
++      err = nvme_set_features(fd, cfg.feature_id, cfg.namespace_id, cfg.value,
++                              0, cfg.save, 0, 0, cfg.data_len, buf, &result);
+       if (err < 0) {
+               perror("set-feature");
+               goto free;
+       }
+       if (!err) {
+               printf("set-feature:%02x (%s), value:%#08x\n", cfg.feature_id,
+                       mb_feature_to_string(cfg.feature_id), cfg.value);
+               if (buf)
+                       d(buf, cfg.data_len, 16, 1);
+       } else if (err > 0)
+               fprintf(stderr, "NVMe Status:%s(%x)\n",
 -                              nvme_status_to_string(err), err);
++                              nvme_status_to_string(err, false), err);
+ free:
+       if (buf)
+               free(buf);
+       return err;
+ }
+ static int memblaze_fw_commit(int fd, int select)
+ {
 -      struct nvme_admin_cmd cmd = {
 -              .opcode         = nvme_admin_activate_fw,
 -              .cdw10          = 8,
 -              .cdw12      = select,
++      struct nvme_passthru_cmd cmd = {
++              .opcode = nvme_admin_fw_commit,
++              .cdw10  = 8,
++              .cdw12  = select,
+       };
 -      return nvme_submit_admin_passthru(fd, &cmd);
++      return nvme_submit_admin_passthru(fd, &cmd, NULL);
+ }
+ static int memblaze_selective_download(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+ {
+       const char *desc =
+               "This performs a selective firmware download, which allows the user to "
+               "select which firmware binary to update for 9200 devices. This requires a power cycle once the "
+               "update completes. The options available are: \n\n"
+               "OOB - This updates the OOB and main firmware\n"
+               "EEP - This updates the eeprom and main firmware\n"
+               "ALL - This updates the eeprom, OOB, and main firmware";
+       const char *fw = "firmware file (required)";
+       const char *select = "FW Select (e.g., --select=OOB, EEP, ALL)";
+       int xfer = 4096;
+       void *fw_buf;
+       int fd, selectNo,fw_fd,fw_size,err,offset = 0;
+       struct stat sb;
+       int i;
+       struct config {
+               char  *fw;
+               char  *select;
+       };
+       struct config cfg = {
+               .fw     = "",
+               .select = "\0",
+       };
+       OPT_ARGS(opts) = {
+               OPT_STRING("fw", 'f', "FILE", &cfg.fw, fw),
+               OPT_STRING("select", 's', "flag", &cfg.select, select),
+               OPT_END()
+       };
+       fd = parse_and_open(argc, argv, desc, opts);
+       if (fd < 0)
+               return fd;
+       if (strlen(cfg.select) != 3) {
+               fprintf(stderr, "Invalid select flag\n");
+               err = EINVAL;
+               goto out;
+       }
+       for (i = 0; i < 3; i++) {
+               cfg.select[i] = toupper(cfg.select[i]);
        }
 -                                      nvme_status_to_string(err), err);
+       if (strncmp(cfg.select,"OOB", 3) == 0) {
+               selectNo = 18;
+       } else if (strncmp(cfg.select,"EEP", 3) == 0) {
+               selectNo = 10;
+       } else if (strncmp(cfg.select,"ALL", 3) == 0) {
+               selectNo = 26;
+       } else {
+               fprintf(stderr, "Invalid select flag\n");
+               err = EINVAL;
+               goto out;
+       }
+       fw_fd = open(cfg.fw, O_RDONLY);
+       if (fw_fd < 0) {
+               fprintf(stderr, "no firmware file provided\n");
+               err = EINVAL;
+               goto out;
+       }
+       err = fstat(fw_fd, &sb);
+       if (err < 0) {
+               perror("fstat");
+               err = errno;
+       }
+       fw_size = sb.st_size;
+       if (fw_size & 0x3) {
+               fprintf(stderr, "Invalid size:%d for f/w image\n", fw_size);
+               err = EINVAL;
+               goto out;
+       }
+       if (posix_memalign(&fw_buf, getpagesize(), fw_size)) {
+               fprintf(stderr, "No memory for f/w size:%d\n", fw_size);
+               err = ENOMEM;
+               goto out;
+       }
+       if (read(fw_fd, fw_buf, fw_size) != ((ssize_t)(fw_size)))
+               return EIO;
+       while (fw_size > 0) {
+               xfer = min(xfer, fw_size);
+               err = nvme_fw_download(fd, offset, xfer, fw_buf);
+               if (err < 0) {
+                       perror("fw-download");
+                       goto out;
+               } else if (err != 0) {
+                       fprintf(stderr, "NVME Admin command error:%s(%x)\n",
 -      err = memblaze_fw_commit(fd,selectNo);
 -
 -      if(err == 0x10B || err == 0x20B) {
 -              err = 0;
++                                      nvme_status_to_string(err, false), err);
+                       goto out;
+               }
+               fw_buf     += xfer;
+               fw_size    -= xfer;
+               offset += xfer;
+       }
++      err = memblaze_fw_commit(fd, selectNo);
++      if (err == 0x10B || err == 0x20B) {
+               fprintf(stderr, "Update successful! Please power cycle for changes to take effect\n");
++              err = 0;
+       }
+ out:
+       return err;
  }
- #endif
 -
Simple merge
Simple merge
index 0000000000000000000000000000000000000000,dbb56be3ba349083907d40def8db79d78dcabe91..d6bcf3aac5c2dc5ec373838abefec80a2429d6b3
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,92 +1,81 @@@
 -#include "linux/nvme_ioctl.h"
+ #include <fcntl.h>
+ #include <errno.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <unistd.h>
+ #include <inttypes.h>
 -#include "nvme-print.h"
 -#include "nvme-ioctl.h"
++#include <libnvme.h>
+ #include "nvme.h"
 -      int result=0, fd;
 -      int  percent_used = 0, healthvalue=0;
+ #include "plugin.h"
+ #include "argconfig.h"
+ #include "suffix.h"
+ #define CREATE_CMD
+ #include "transcend-nvme.h"
+ static const __u32 OP_BAD_BLOCK = 0xc2;
+ static const __u32 DW10_BAD_BLOCK = 0x400;
+ static const __u32 DW12_BAD_BLOCK = 0x5a;
+ static int getHealthValue(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+ {
+       struct nvme_smart_log smart_log;
+       char *desc = "Get nvme health percentage.";
 -       
++      int result, fd;
+        
+       OPT_ARGS(opts) = {
+               OPT_END()
+       };
+       fd = parse_and_open(argc, argv, desc, opts);
 -      result = nvme_smart_log(fd, 0xffffffff, &smart_log);
+       if (fd < 0) {
+               printf("\nDevice not found \n");;
+               return -1;
+       }
 -              percent_used =smart_log.percent_used;
 -              
 -              if(percent_used>100 || percent_used<0)
 -              {
++
++      result = nvme_get_log_smart(fd, 0xffffffff, false, &smart_log);
+       if (!result) {
++              int percent_used = smart_log.percent_used;
++
+               printf("Transcend NVME heath value: ");
 -              }
++              if (percent_used > 100 || percent_used < 0)
+                       printf("0%%\n");
 -              {
 -                      healthvalue = 100 - percent_used;
 -                      printf("%d%%\n",healthvalue);
 -              }
 -                       
+               else
 -
++                      printf("%d%%\n", 100 - percent_used);
+       }
+       return result;
+ }
 -
+  
+ static int getBadblock(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+ {
 -      int result=0, fd;
 - 
+       char *desc = "Get nvme bad block number.";
 -               
++      int result, fd;
++
++      unsigned char data[1]={ };
++
++      struct nvme_passthru_cmd nvmecmd = {
++              .opcode = OP_BAD_BLOCK,
++              .cdw10 = DW10_BAD_BLOCK,
++              .cdw12 = DW12_BAD_BLOCK,
++              .addr = (__u64)(uintptr_t)data,
++              .data_len = 0x1,
++      };
++
+       OPT_ARGS(opts) = {
 -      unsigned char data[1]={0};
 -      struct nvme_passthru_cmd nvmecmd;
 -      memset(&nvmecmd,0,sizeof(nvmecmd));
 -      nvmecmd.opcode=OP_BAD_BLOCK;
 -      nvmecmd.cdw10=DW10_BAD_BLOCK;
 -      nvmecmd.cdw12=DW12_BAD_BLOCK;
 -      nvmecmd.addr = (__u64)(uintptr_t)data;
 -      nvmecmd.data_len = 0x1;
 -      result = nvme_submit_admin_passthru(fd,&nvmecmd);
 -      if(!result) {
 -              int badblock  = data[0];
 -              printf("Transcend NVME badblock count: %d\n",badblock);
 -      }
+               OPT_END()
+       };
+       fd = parse_and_open(argc, argv, desc, opts);
+       if (fd < 0) {
+               printf("\nDevice not found \n");;
+               return -1;
+       }
++
++      result = nvme_submit_admin_passthru(fd, &nvmecmd, NULL);
++      if(!result)
++              printf("Transcend NVME badblock count: %d\n", data[0]);
+       return result;
+ }
index 50fd0c0731792f64d22df09bb7d542238d55353a,47b0fdc9ad1ddf9210fc3dc08f27da0d98b28cb9..4b717c739b28dd90fb627ef707325f103cb41f00
@@@ -361,215 -381,308 +378,308 @@@ static void vt_build_identify_lv2(unsig
                printf("    },\n");
  }
  
- static void vt_parse_detail_identify(const struct nvme_id_ctrl *ctrl)
+ static void vt_build_power_state_descriptor(const struct nvme_id_ctrl *ctrl)
  {
+       unsigned int i;
        unsigned char *buf;
-       unsigned int temp, pos;
-       char s[1024] = "";
-       const char *CMICtable[6] =  {
-                               "0 = the NVM subsystem contains only a single NVM subsystem port",
-                               "1 = the NVM subsystem may contain more than one subsystem ports",
-                               "0 = the NVM subsystem contains only a single controller",
-                               "1 = the NVM subsystem may contain two or more controllers (see section 1.4.1)",
-                               "0 = the controller is associated with a PCI Function or a Fabrics connection",
-                               "1 = the controller is associated with an SR-IOV Virtual Function"
-       };
-       const char *OAEStable[18] = {
-                               "Reversed",
-                               "Reserved",
-                               "Reserved",
-                               "Reserved",
-                               "Reserved",
-                               "Reserved",
-                               "Reserved",
-                               "Reserved",
-                               "Reserved",
-                               "Reserved",
-                               "Reserved",
-                               "Reserved",
-                               "Reserved",
-                               "Reserved",
-                               "0 = does not support sending the Namespace Attribute Notices event nor the associated Changed Namespace List log page",
-                               "1 = supports sending the Namespace Attribute Notices  & the associated Changed Namespace List log page",
-                               "0 = does not support sending sending Firmware Activation Notices event",
-                               "1 = supports sending Firmware Activation Notices"
-       };
-       const char *CTRATTtable[4] =  {
-                               "0 = does not support a 128-bit Host Identifier",
-                               "1 = supports a 128-bit Host Identifier",
-                               "0 = does not support Non-Operational Power State Permissive Mode",
-                               "1 = supports Non-Operational Power State Permissive Mode"
-       };
-       const char *OACStable[18] =  {
-                               "0 = does not support the Security Send and Security Receive commands",
-                               "1 = supports the Security Send and Security Receive commands",
-                               "0 = does not support the Format NVM command",
-                               "1 = supports the Format NVM command",
-                               "0 = does not support the Firmware Commit and Firmware Image Download commands",
-                               "1 = supports the Firmware Commit and Firmware Image Download commands",
-                               "0 = does not support the Namespace Management capability",
-                               "1 = supports the Namespace Management capability",
-                               "0 = does not support the Device Self-test command",
-                               "1 = supports the Device Self-test command",
-                               "0 = does not support Directives",
-                               "1 = supports Directive Send & Directive Receive commands",
-                               "0 = does not support the NVMe-MI Send and NVMe-MI Receive commands",
-                               "1 = supports the NVMe-MI Send and NVMe-MI Receive commands",
-                               "0 = does not support the Virtualization Management command",
-                               "1 = supports the Virtualization Management command",
-                               "0 = does not support the Doorbell Buffer Config command",
-                               "1 = supports the Doorbell Buffer Config command"
-       };
-       const char *FRMWtable[10] = {
-                               "0 = the 1st firmware slot (slot 1) is read/write",
-                               "1 = the 1st firmware slot (slot 1) is read only",
-                               "Reserved",
-                               "Reserved",
-                               "Reserved",
-                               "Reserved",
-                               "Reserved",
-                               "Reserved",
-                               "0 = requires a reset for firmware to be activated",
-                               "1 = supports firmware activation without a reset"
-       };
-       const char *LPAtable[8] =  {
-                               "0 = does not support the SMART / Health information log page on a per namespace basis",
-                               "1 = supports the SMART / Health information log page on a per namespace basis",
-                               "0 = does not support the Commands Supported & Effects log page",
-                               "1 = supports the Commands Supported Effects log page",
-                               "0 = does not support extended data for Get Log Page",
-                               "1 = supports extended data for Get Log Page (including extended Number of Dwords and Log Page Offset fields)",
-                               "0 = does not support the Telemetry Host-Initiated and Telemetry Controller-Initiated log pages and Telemetry Log Notices events",
-                               "1 = supports the Telemetry Host-Initiated and Telemetry Controller-Initiated log pages and sending Telemetry Log Notices"
-       };
-       const char *AVSCCtable[2] = {
-                               "0 = the format of all Admin Vendor Specific Commands are vendor specific",
-                               "1 = all Admin Vendor Specific Commands use the format defined in NVM Express specification"
-       };
-       const char *APSTAtable[2] = {
-                               "0 = does not support autonomous power state transitions",
-                               "1 = supports autonomous power state transitions"
-       };
-       const char *DSTOtable[2] =  {
-                               "0 = the NVM subsystem supports one device self-test operation per controller at a time",
-                               "1 = the NVM subsystem supports only one device self-test operation in progress at a time"
-       };
-       const char *HCTMAtable[2] = {
-                               "0 = does not support host controlled thermal management",
-                               "1 = supports host controlled thermal management. Supports Set Features & Get Features commands with the Feature Identifier field set to 10h"
-       };
  
-       const char *SANICAPtable[6] =  {
-                               "0 = does not support the Crypto Erase sanitize operation",
-                               "1 = supports the Crypto Erase sanitize operation",
-                               "0 = does not support the Block Erase sanitize operation",
-                               "1 = supports the Block Erase sanitize operation",
-                               "0 = does not support the Overwrite sanitize operation",
-                               "1 = supports the Overwrite sanitize operation"
-       };
-       const char *ONCStable[14] =  {
-                               "0 = does not support the Compare command",
-                               "1 = supports the Compare command",
-                               "0 = does not support the Write Uncorrectable command",
-                               "1 = supports the Write Uncorrectable command",
-                               "0 = does not support the Dataset Management command",
-                               "1 = supports the Dataset Management command",
-                               "0 = does not support the Write Zeroes command",
-                               "1 = supports the Write Zeroes command",
-                               "0 = does not support the Save field set to a non-zero value in the Set Features and the Get Features commands",
-                               "1 = supports the Save field set to a non-zero value in the Set Features and the Get Features commands",
-                               "0 = does not support reservations",
-                               "1 = supports reservations",
-                               "0 = does not support the Timestamp feature (refer to section 5.21.1.14)",
-                               "1 = supports the Timestamp feature"
-       };
-       const char *FUSEStable[2] =  {
-                               "0 =  does not support the Compare and Write fused operation",
-                               "1 =  supports the Compare and Write fused operation"
-       };
+       printf("{\n");
+       printf("\"Power State Descriptors\":{\n");
+       printf("    \"NOPS\":\"Non-Operational State,\"\n");
+       printf("    \"MPS\":\"Max Power Scale (0: in 0.01 Watts; 1: in 0.0001 Watts),\"\n");
+       printf("    \"ENLAT\":\"Entry Latency in microseconds,\"\n");
+       printf("    \"RWL\":\"Relative Write Latency,\"\n");
+       printf("    \"RRL\":\"Relative Read Latency,\"\n");
+       printf("    \"IPS\":\"Idle Power Scale (00b: Not reported; 01b: 0.0001 W; 10b: 0.01 W; 11b: Reserved),\"\n");
+       printf("    \"APS\":\"Active Power Scale (00b: Not reported; 01b: 0.0001 W; 10b: 0.01 W; 11b: Reserved),\"\n");
+       printf("    \"ACTP\":\"Active Power,\"\n");
+       printf("    \"MP\":\"Maximum Power,\"\n");
+       printf("    \"EXLAT\":\"Exit Latency in microsecond,\"\n");
+       printf("    \"RWT\":\"Relative Write Throughput,\"\n");
+       printf("    \"RRT\":\"Relative Read Throughput,\"\n");
+       printf("    \"IDLP\":\"Idle Power,\"\n");
+       printf("    \"APW\":\"Active Power Workload,\"\n");
+       printf("    \"Ofs\":\"BYTE Offset,\"\n");
+       printf("    \"Power State Descriptors\":\"\n");
+       printf("%6s%10s%5s%4s%6s%10s%10s%10s%4s%4s%4s%4s%10s%4s%6s%10s%4s%5s%6s\n", "Entry", "0fs 00-03", "NOPS", "MPS", "MP", "ENLAT", "EXLAT", "0fs 12-15",
+                       "RWL", "RWT", "RRL", "RRT", "0fs 16-19", "IPS", "IDLP", "0fs 20-23", "APS", "APW", "ACTP");
+       printf("%6s%10s%5s%4s%6s%10s%10s%10s%4s%4s%4s%4s%10s%4s%6s%10s%4s%5s%6s\n", "=====", "=========", "====", "===", "=====", "=========", "=========",
+                       "=========", "===", "===", "===", "===", "=========", "===", "=====", "=========", "===", "====", "=====");
+       for (i = 0; i < 32; i++) {
+               char s[100];
+               unsigned int temp;
+               printf("%6d", i);
+               buf = (unsigned char*) (&ctrl->psd[i]);
+               vt_convert_data_buffer_to_hex_string(&buf[0], 4, true, s);
+               printf("%9sh", s);
+               temp = ctrl->psd[i].flags;
+               printf("%4ub", ((unsigned char)temp & 0x02));
+               printf("%3ub", ((unsigned char)temp & 0x01));
+               vt_convert_data_buffer_to_hex_string(&buf[0], 2, true, s);
+               printf("%5sh", s);
+               vt_convert_data_buffer_to_hex_string(&buf[4], 4, true, s);
+               printf("%9sh", s);
+               vt_convert_data_buffer_to_hex_string(&buf[8], 4, true, s);
+               printf("%9sh", s);
+               vt_convert_data_buffer_to_hex_string(&buf[12], 4, true, s);
+               printf("%9sh", s);
+               vt_convert_data_buffer_to_hex_string(&buf[15], 1, true, s);
+               printf("%3sh", s);
+               vt_convert_data_buffer_to_hex_string(&buf[14], 1, true, s);
+               printf("%3sh", s);
+               vt_convert_data_buffer_to_hex_string(&buf[13], 1, true, s);
+               printf("%3sh", s);
+               vt_convert_data_buffer_to_hex_string(&buf[12], 1, true, s);
+               printf("%3sh", s);
+               vt_convert_data_buffer_to_hex_string(&buf[16], 4, true, s);
+               printf("%9sh", s);
 -              temp = ctrl->psd[i].idle_scale;
++              temp = ctrl->psd[i].ips;
+               snprintf(s, sizeof(s), "%u%u", (((unsigned char)temp >> 6) & 0x01), (((unsigned char)temp >> 7) & 0x01));
+               printf("%3sb", s);
+               vt_convert_data_buffer_to_hex_string(&buf[16], 2, true, s);
+               printf("%5sh", s);
+               vt_convert_data_buffer_to_hex_string(&buf[20], 4, true, s);
+               printf("%9sh", s);
 -              temp = ctrl->psd[i].active_work_scale;
++              temp = ctrl->psd[i].aps;
+               snprintf(s, sizeof(s), "%u%u", (((unsigned char)temp >> 6) & 0x01), (((unsigned char)temp >> 7) & 0x01));
+               printf("%3sb", s);
+               snprintf(s, sizeof(s), "%u%u%u", (((unsigned char)temp) & 0x01), (((unsigned char)temp >> 1) & 0x01), (((unsigned char)temp >> 2) & 0x01));
+               printf("%4sb", s);
+               vt_convert_data_buffer_to_hex_string(&buf[20], 2, true, s);
+               printf("%5sh", s);
+               printf("\n");
+       }
  
-       const char *FNAtable[6] =  {
-                               "0 = supports format on a per namespace basis",
-                               "1 = all namespaces shall be configured with the same attributes and a format (excluding secure erase) of any namespace results in a format of all namespaces in an NVM subsystem",
-                               "0 = any secure erase performed as part of a format results in a secure erase of a particular namespace specified",
-                               "1 = any secure erase performed as part of a format operation results in a secure erase of all namespaces in the NVM subsystem",
-                               "0 = cryptographic erase is not supported",
-                               "1 = cryptographic erase is supported as part of the secure erase functionality"
-       };
+       printf("    \"}\n}\n");
  
-       const char *VWCtable[2] =  {
-                               "0 = a volatile write cache is not present",
-                               "1 = a volatile write cache is present"
-       };
+ }
  
-       const char *NVSCCtable[2] =  {
-                               "0 = the format of all NVM Vendor Specific Commands are vendor specific",
-                               "1 = all NVM Vendor Specific Commands use the format defined in NVM Express specification"
-       };
+ static void vt_dump_hex_data(const unsigned char *pbuff, size_t pbuffsize) {
+       char textbuf[33];
+       unsigned long int i, j;
+       textbuf[32] = '\0';
+       printf("[%08X] ", 0);
+       for (i = 0; i < pbuffsize; i++) {
+               printf("%02X ", pbuff[i]);
+               if (pbuff[i] >= ' ' && pbuff[i] <= '~') 
+                       textbuf[i % 32] = pbuff[i];
+               else 
+                       textbuf[i % 32] = '.';
+               if ((((i + 1) % 8) == 0) || ((i + 1) == pbuffsize)) {
+                       printf(" ");
+                       if ((i + 1) % 32 == 0) {
+                               printf(" %s\n", textbuf);
+                               if((i + 1) != pbuffsize)
+                                   printf("[%08lX] ", (i + 1));
+                       } 
+                       else if (i + 1 == pbuffsize) {
+                               textbuf[(i + 1) % 32] = '\0';
+                               if(((i + 1) % 8) == 0)
+                                       printf(" ");
+                               for (j = ((i + 1) % 32); j < 32; j++) {
+                                       printf("   ");
+                                       if(((j + 1) % 8) == 0)
+                                               printf(" ");
+                               }
+                               printf("%s\n", textbuf);
+                       }
+               }
+       }
+ }
  
-       const char *SGLSSubtable[4] =  {
-                               "00b = SGLs are not supported"
-                               "01b = SGLs are supported. There is no alignment nor granularity requirement for Data Blocks",
-                               "10b = SGLs are supported. There is a Dword alignment and granularity requirement for Data Blocks",
-                               "11b = Reserved"
-       };
+ static void vt_parse_detail_identify(const struct nvme_id_ctrl *ctrl)
+ {
+       unsigned char *buf;
+       unsigned int temp, pos;
+       char s[1024] = "";
  
-       const char *SGLStable[42] =  {
-                               "Used"
-                               "Used",
-                               "Used",
-                               "Used",
-                               "0 = does not support the Keyed SGL Data Block descriptor",
-                               "1 = supports the Keyed SGL Data Block descriptor",
-                               "Reserved",
-                               "Reserved",
-                               "Reserved",
-                               "Reserved",
-                               "Reserved",
-                               "Reserved",
-                               "Reserved",
-                               "Reserved",
-                               "Reserved",
-                               "Reserved",
-                               "Reserved",
-                               "Reserved",
-                               "Reserved",
-                               "Reserved",
-                               "Reserved",
-                               "Reserved",
-                               "Reserved",
-                               "Reserved",
-                               "Reserved",
-                               "Reserved",
-                               "Reserved",
-                               "Reserved",
-                               "Reserved",
-                               "Reserved",
-                               "Reserved",
-                               "Reserved",
-                               "0 = the SGL Bit Bucket descriptor is not supported",
-                               "1 = the SGL Bit Bucket descriptor is supported",
-                               "0 = use of a byte aligned contiguous physical buffer of metadata is not supported",
-                               "1 = use of a byte aligned contiguous physical buffer of metadata is supported",
-                               "0 = the SGL length shall be equal to the amount of data to be transferred",
-                               "1 = supports commands that contain a data or metadata SGL of a length larger than the amount of data to be transferred",
-                               "0 = use of Metadata Pointer (MPTR) that contains an address of an SGL segment containing exactly one SGL Descriptor that is Qword aligned is not supported",
-                               "1 = use of Metadata Pointer (MPTR) that contains an address of an SGL segment containing exactly one SGL Descriptor that is Qword aligned is supported",
-                               "0 = the Address field specifying an offset is not supported",
-                               "1 = supports the Address field in SGL Data Block, SGL Segment, and SGL Last Segment descriptor types specifying an offset"
-       };
+       const char *CMICtable[6] = {"0 = the NVM subsystem contains only a single NVM subsystem port",
+                                                               "1 = the NVM subsystem may contain more than one subsystem ports",
+                                                               "0 = the NVM subsystem contains only a single controller",
+                                                               "1 = the NVM subsystem may contain two or more controllers (see section 1.4.1)",
+                                                               "0 = the controller is associated with a PCI Function or a Fabrics connection",
+                                                               "1 = the controller is associated with an SR-IOV Virtual Function"};
+       const char *OAEStable[20] = {"Reserved",
+                                                                "Reserved",
+                                                                "Reserved",
+                                                                "Reserved",
+                                                                "Reserved",
+                                                                "Reserved",
+                                                                "Reserved",
+                                                                "Reserved",
+                                                                "Reserved",
+                                                                "Reserved",
+                                                                "Reserved",
+                                                                "Reserved",
+                                                                "Reserved",
+                                                                "Reserved",
+                                                                "Reserved",
+                                                                "Reserved",
+                                                                "0 = does not support sending the Namespace Attribute Notices event nor the associated Changed Namespace List log page",
+                                                                "1 = supports sending the Namespace Attribute Notices  & the associated Changed Namespace List log page",
+                                                                "0 = does not support sending Firmware Activation Notices event",
+                                                                "1 = supports sending Firmware Activation Notices"};
+       const char *CTRATTtable[4] = {"0 = does not support a 128-bit Host Identifier",
+                                                                 "1 = supports a 128-bit Host Identifier",
+                                                                 "0 = does not support Non-Operational Power State Permissive Mode",
+                                                                 "1 = supports Non-Operational Power State Permissive Mode"};
+       const char *OACStable[18] = {"0 = does not support the Security Send and Security Receive commands",
+                                                                "1 = supports the Security Send and Security Receive commands",
+                                                                "0 = does not support the Format NVM command",
+                                                                "1 = supports the Format NVM command",
+                                                                "0 = does not support the Firmware Commit and Firmware Image Download commands",
+                                                                "1 = supports the Firmware Commit and Firmware Image Download commands",
+                                                                "0 = does not support the Namespace Management capability",
+                                                                "1 = supports the Namespace Management capability",
+                                                                "0 = does not support the Device Self-test command",
+                                                                "1 = supports the Device Self-test command",
+                                                                "0 = does not support Directives",
+                                                                "1 = supports Directive Send & Directive Receive commands",
+                                                                "0 = does not support the NVMe-MI Send and NVMe-MI Receive commands",
+                                                                "1 = supports the NVMe-MI Send and NVMe-MI Receive commands",
+                                                                "0 = does not support the Virtualization Management command",
+                                                                "1 = supports the Virtualization Management command",
+                                                                "0 = does not support the Doorbell Buffer Config command",
+                                                                "1 = supports the Doorbell Buffer Config command"};
+       const char *FRMWtable[10] = {"0 = the 1st firmware slot (slot 1) is read/write",
+                                                                "1 = the 1st firmware slot (slot 1) is read only",
+                                                                "Reserved",
+                                                                "Reserved",
+                                                                "Reserved",
+                                                                "Reserved",
+                                                                "Reserved",
+                                                                "Reserved",
+                                                                "0 = requires a reset for firmware to be activated",
+                                                                "1 = supports firmware activation without a reset"};
+       const char *LPAtable[8] = {"0 = does not support the SMART / Health information log page on a per namespace basis",
+                                                          "1 = supports the SMART / Health information log page on a per namespace basis",
+                                                          "0 = does not support the Commands Supported & Effects log page",
+                                                          "1 = supports the Commands Supported Effects log page",
+                                                          "0 = does not support extended data for Get Log Page",
+                                                          "1 = supports extended data for Get Log Page (including extended Number of Dwords and Log Page Offset fields)",
+                                                          "0 = does not support the Telemetry Host-Initiated and Telemetry Controller-Initiated log pages and Telemetry Log Notices events",
+                                                          "1 = supports the Telemetry Host-Initiated and Telemetry Controller-Initiated log pages and sending Telemetry Log Notices"                                                      };
+       const char *AVSCCtable[2] = {"0 = the format of all Admin Vendor Specific Commands are vendor specific",
+                                                                "1 = all Admin Vendor Specific Commands use the format defined in NVM Express specification"};
+       const char *APSTAtable[2] = {"0 = does not support autonomous power state transitions",
+                                                                "1 = supports autonomous power state transitions"};
+       const char *DSTOtable[2] =  {"0 = the NVM subsystem supports one device self-test operation per controller at a time",
+                                                                "1 = the NVM subsystem supports only one device self-test operation in progress at a time"};
+       const char *HCTMAtable[2] = {"0 = does not support host controlled thermal management",
+                                                                "1 = supports host controlled thermal management. Supports Set Features & Get Features commands with the Feature Identifier field set to 10h"};
+       const char *SANICAPtable[6] =  {"0 = does not support the Crypto Erase sanitize operation",
+                                                                       "1 = supports the Crypto Erase sanitize operation",
+                                                                       "0 = does not support the Block Erase sanitize operation",
+                                                                       "1 = supports the Block Erase sanitize operation",
+                                                                       "0 = does not support the Overwrite sanitize operation",
+                                                                       "1 = supports the Overwrite sanitize operation"};
+       const char *ONCStable[14] =  {"0 = does not support the Compare command",
+                                                                 "1 = supports the Compare command",
+                                                                 "0 = does not support the Write Uncorrectable command",
+                                                                 "1 = supports the Write Uncorrectable command",
+                                                                 "0 = does not support the Dataset Management command",
+                                                                 "1 = supports the Dataset Management command",
+                                                                 "0 = does not support the Write Zeroes command",
+                                                                 "1 = supports the Write Zeroes command",
+                                                                 "0 = does not support the Save field set to a non-zero value in the Set Features and the Get Features commands",
+                                                                 "1 = supports the Save field set to a non-zero value in the Set Features and the Get Features commands", \
+                                                                 "0 = does not support reservations",
+                                                                 "1 = supports reservations",
+                                                                 "0 = does not support the Timestamp feature (refer to section 5.21.1.14)",
+                                                                 "1 = supports the Timestamp feature"};
+       const char *FUSEStable[2] = {"0 =  does not support the Compare and Write fused operation",
+                                                                "1 =  supports the Compare and Write fused operation"};
+       const char *FNAtable[6] = {"0 = supports format on a per namespace basis",
+                                                          "1 = all namespaces shall be configured with the same attributes and a format (excluding secure erase) of any namespace results in a format of all namespaces in an NVM subsystem",
+                                                          "0 = any secure erase performed as part of a format results in a secure erase of a particular namespace specified",
+                                                          "1 = any secure erase performed as part of a format operation results in a secure erase of all namespaces in the NVM subsystem",
+                                                          "0 = cryptographic erase is not supported",
+                                                          "1 = cryptographic erase is supported as part of the secure erase functionality"};
+       const char *VWCtable[2] = {"0 = a volatile write cache is not present",
+                                                          "1 = a volatile write cache is present"};
+       const char *NVSCCtable[2] = {"0 = the format of all NVM Vendor Specific Commands are vendor specific",
+                                                                "1 = all NVM Vendor Specific Commands use the format defined in NVM Express specification"};
+       const char *SGLSSubtable[4] =  {"00b = SGLs are not supported",
+                                                                       "01b = SGLs are supported. There is no alignment nor granularity requirement for Data Blocks",
+                                                                       "10b = SGLs are supported. There is a Dword alignment and granularity requirement for Data Blocks",
+                                                                       "11b = Reserved"};
+       const char *SGLStable[42] =  {"Used",
+                                                                 "Used",
+                                                                 "Used",
+                                                                 "Used",
+                                                                 "0 = does not support the Keyed SGL Data Block descriptor",
+                                                                 "1 = supports the Keyed SGL Data Block descriptor",
+                                                                 "Reserved",
+                                                                 "Reserved",
+                                                                 "Reserved",
+                                                                 "Reserved",
+                                                                 "Reserved",
+                                                                 "Reserved",
+                                                                 "Reserved",
+                                                                 "Reserved",
+                                                                 "Reserved",
+                                                                 "Reserved",
+                                                                 "Reserved",
+                                                                 "Reserved",
+                                                                 "Reserved",
+                                                                 "Reserved",
+                                                                 "Reserved",
+                                                                 "Reserved",
+                                                                 "Reserved",
+                                                                 "Reserved",
+                                                                 "Reserved",
+                                                                 "Reserved",
+                                                                 "Reserved",
+                                                                 "Reserved",
+                                                                 "Reserved",
+                                                                 "Reserved",
+                                                                 "Reserved",
+                                                                 "Reserved",
+                                                                 "0 = the SGL Bit Bucket descriptor is not supported",
+                                                                 "1 = the SGL Bit Bucket descriptor is supported",
+                                                                 "0 = use of a byte aligned contiguous physical buffer of metadata is not supported",
+                                                                 "1 = use of a byte aligned contiguous physical buffer of metadata is supported",
+                                                                 "0 = the SGL length shall be equal to the amount of data to be transferred",
+                                                                 "1 = supports commands that contain a data or metadata SGL of a length larger than the amount of data to be transferred",
+                                                                 "0 = use of Metadata Pointer (MPTR) that contains an address of an SGL segment containing exactly one SGL Descriptor that is Qword aligned is not supported",
+                                                                 "1 = use of Metadata Pointer (MPTR) that contains an address of an SGL segment containing exactly one SGL Descriptor that is Qword aligned is supported",
+                                                                 "0 = the Address field specifying an offset is not supported",
+                                     "1 = supports the Address field in SGL Data Block, SGL Segment, and SGL Last Segment descriptor types specifying an offset"};
  
        buf = (unsigned char *)(ctrl);
  
index cb08f8bac206c454f55624ad400894ad5a8a4432,740b6be23549a9708d661e84991f1390b352c0c2..a805b9c35a77490241406ba8717154e349c9333c
@@@ -491,9 -509,12 +505,12 @@@ static int wdc_reason_identifier(int ar
  static int wdc_do_get_reason_id(int fd, char *file, int log_id);
  static int wdc_save_reason_id(int fd, __u8 *rsn_ident,  int size);
  static int wdc_clear_reason_id(int fd);
 -static int wdc_dump_telemetry_hdr(int fd, int log_id, struct nvme_telemetry_log_page_hdr *log_hdr);
 +static int wdc_dump_telemetry_hdr(int fd, int log_id, struct nvme_telemetry_log *log_hdr);
  static int wdc_log_page_directory(int argc, char **argv, struct command *command,
                struct plugin *plugin);
+ static int wdc_do_drive_info(int fd, __u32 *result);
+ static int wdc_vs_drive_info(int argc, char **argv, struct command *command,
+               struct plugin *plugin);
  
  /* Drive log data size */
  struct wdc_log_size {
@@@ -1040,11 -1069,11 +1065,11 @@@ static bool get_dev_mgment_cbs_data(in
                fprintf(stderr, "ERROR : WDC : malloc : %s\n", strerror(errno));
                return false;
        }
 -      memset(data, 0, sizeof (__u8) * WDC_C2_LOG_BUF_LEN);
 +      memset(data, 0, WDC_C2_LOG_BUF_LEN);
  
        /* get the log page length */
-       ret = nvme_get_log(fd, WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_OPCODE, 0xFFFFFFFF, 0, 0, 0,
-                          false, 0, WDC_C2_LOG_BUF_LEN, data);
 -      ret = nvme_get_log(fd, 0xFFFFFFFF, WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_OPCODE,
++      ret = nvme_get_log_page(fd, 0xFFFFFFFF, WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_OPCODE,
+                          false, WDC_C2_LOG_BUF_LEN, data);
        if (ret) {
                fprintf(stderr, "ERROR : WDC : Unable to get C2 Log Page length, ret = 0x%x\n", ret);
                goto end;
                }
        }
  
-       ret = nvme_get_log(fd, WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_OPCODE, 0xFFFFFFFF, 0, 0, 0,
 -      ret = nvme_get_log(fd, 0xFFFFFFFF, WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_OPCODE,
 -                         false, le32_to_cpu(hdr_ptr->length), data);
++      ret = nvme_get_log(fd, 0xFFFFFFFF, WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_OPCODE, 0, 0, 0,
 +                         false, 0, le32_to_cpu(hdr_ptr->length), data);
        /* parse the data until the List of log page ID's is found */
        if (ret) {
                fprintf(stderr, "ERROR : WDC : Unable to read C2 Log Page data, ret = 0x%x\n", ret);
@@@ -1394,10 -1424,11 +1419,11 @@@ static int wdc_do_cap_telemetry_log(in
                ctrl_init = 0;
        } else if (type == WDC_TELEMETRY_TYPE_CONTROLLER) {
                /* Verify the Controller Initiated Option is enabled */
 -              err = nvme_get_feature(fd, 0, WDC_VU_DISABLE_CNTLR_TELEMETRY_OPTION_FEATURE_ID, 0, 0,
 +              err = nvme_get_features(fd, WDC_VU_DISABLE_CNTLR_TELEMETRY_OPTION_FEATURE_ID, 0, 0, 0, 0,
                                4, buf, &result);
                if (err == 0) {
-                       if (result) {
+                       if (result == 0) {
+                               /* enabled */
                                host_gen = 0;
                                ctrl_init = 1;
                        }
@@@ -3166,24 -3536,110 +3489,109 @@@ static int wdc_get_ca_log_page(int fd, 
                return -1;
        }
  
-       if ((data = (__u8*) malloc(sizeof (__u8) * WDC_CA_LOG_BUF_LEN)) == NULL) {
-               fprintf(stderr, "ERROR : WDC : malloc : %s\n", strerror(errno));
+       if (!get_dev_mgment_cbs_data(fd, WDC_C2_CUSTOMER_ID_ID, (void*)&data)) {
+               fprintf(stderr, "%s: ERROR : WDC : 0xC2 Log Page entry ID 0x%x not found\n", __func__, WDC_C2_CUSTOMER_ID_ID);
                return -1;
        }
-       memset(data, 0, sizeof (__u8) * WDC_CA_LOG_BUF_LEN);
  
-       ret = nvme_get_log(fd, WDC_NVME_GET_DEVICE_INFO_LOG_OPCODE, 0xFFFFFFFF, 0, 0, 0,
-                          false, 0, WDC_CA_LOG_BUF_LEN, data);
-       if (strcmp(format, "json"))
-               fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret, false), ret);
+       ret = wdc_get_pci_ids(&read_device_id, &read_vendor_id);
 -
+       cust_id = (__u32*)data;
  
-       if (ret == 0) {
-               /* parse the data */
-               perf = (struct wdc_ssd_ca_perf_stats *)(data);
-               ret = wdc_print_ca_log(perf, fmt);
-       } else {
-               fprintf(stderr, "ERROR : WDC : Unable to read CA Log Page data\n");
-               ret = -1;
+       switch (read_device_id) {
+       case WDC_NVME_SN200_DEV_ID:
+               if (*cust_id == WDC_CUSTOMER_ID_0x1005) {
+                       if ((data = (__u8*) malloc(sizeof (__u8) * WDC_FB_CA_LOG_BUF_LEN)) == NULL) {
+                               fprintf(stderr, "ERROR : WDC : malloc : %s\n", strerror(errno));
+                               return -1;
+                       }
+                       memset(data, 0, sizeof (__u8) * WDC_FB_CA_LOG_BUF_LEN);
 -                      ret = nvme_get_log(fd, 0xFFFFFFFF, WDC_NVME_GET_DEVICE_INFO_LOG_OPCODE,
++                      ret = nvme_get_log_page(fd, 0xFFFFFFFF, WDC_NVME_GET_DEVICE_INFO_LOG_OPCODE,
+                                          false, WDC_FB_CA_LOG_BUF_LEN, data);
+                       if (strcmp(format, "json"))
 -                              fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret);
++                              fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret, false), ret);
+                       if (ret == 0) {
+                               /* parse the data */
+                               perf = (struct wdc_ssd_ca_perf_stats *)(data);
+                               ret = wdc_print_fb_ca_log(perf, fmt);
+                       } else {
+                               fprintf(stderr, "ERROR : WDC : Unable to read CA Log Page data\n");
+                               ret = -1;
+                       }
+               } else {
+                       fprintf(stderr, "ERROR : WDC : Unsupported Customer id, id = %d\n", *cust_id);
+                       return -1;
+               }
+               break;
+       case WDC_NVME_SN640_DEV_ID:
+       case WDC_NVME_SN640_DEV_ID_1:
+       case WDC_NVME_SN640_DEV_ID_2:
+       case WDC_NVME_SN840_DEV_ID:
+       case WDC_NVME_SN840_DEV_ID_1:
+               if (*cust_id == WDC_CUSTOMER_ID_0x1005) {
+                       if ((data = (__u8*) malloc(sizeof (__u8) * WDC_FB_CA_LOG_BUF_LEN)) == NULL) {
+                               fprintf(stderr, "ERROR : WDC : malloc : %s\n", strerror(errno));
+                               return -1;
+                       }
+                       memset(data, 0, sizeof (__u8) * WDC_FB_CA_LOG_BUF_LEN);
 -                      ret = nvme_get_log(fd, 0xFFFFFFFF, WDC_NVME_GET_DEVICE_INFO_LOG_OPCODE,
++                      ret = nvme_get_log_page(fd, 0xFFFFFFFF, WDC_NVME_GET_DEVICE_INFO_LOG_OPCODE,
+                                          false, WDC_FB_CA_LOG_BUF_LEN, data);
+                       if (strcmp(format, "json"))
 -                              fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret);
++                              fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret, false), ret);
+                       if (ret == 0) {
+                               /* parse the data */
+                               perf = (struct wdc_ssd_ca_perf_stats *)(data);
+                               ret = wdc_print_fb_ca_log(perf, fmt);
+                       } else {
+                               fprintf(stderr, "ERROR : WDC : Unable to read CA Log Page data\n");
+                               ret = -1;
+                       }
+               } else if (*cust_id == WDC_CUSTOMER_ID_GENERIC) {
+                       if ((data = (__u8*) malloc(sizeof (__u8) * WDC_BD_CA_LOG_BUF_LEN)) == NULL) {
+                               fprintf(stderr, "ERROR : WDC : malloc : %s\n", strerror(errno));
+                               return -1;
+                       }
+                       memset(data, 0, sizeof (__u8) * WDC_BD_CA_LOG_BUF_LEN);
 -                      ret = nvme_get_log(fd, 0xFFFFFFFF, WDC_NVME_GET_DEVICE_INFO_LOG_OPCODE,
++                      ret = nvme_get_log_page(fd, 0xFFFFFFFF, WDC_NVME_GET_DEVICE_INFO_LOG_OPCODE,
+                                          false, WDC_BD_CA_LOG_BUF_LEN, data);
+                       if (strcmp(format, "json"))
 -                              fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret);
++                              fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret, false), ret);
+                       if (ret == 0) {
+                               /* parse the data */
+                               ret = wdc_print_bd_ca_log(data, fmt);
+                       } else {
+                               fprintf(stderr, "ERROR : WDC : Unable to read CA Log Page data\n");
+                               ret = -1;
+                       }
+                       break;
+               } else {
+                       fprintf(stderr, "ERROR : WDC : Unsupported Customer id, id = %d\n", *cust_id);
+                       return -1;
+               }
+               break;
+       default:
+               fprintf(stderr, "ERROR : WDC : Log page 0xCA not supported for this device\n");
+               return -1;
+               break;
        }
  
        free(data);
@@@ -3222,10 -3678,10 +3630,10 @@@ static int wdc_get_c1_log_page(int fd, 
        }
        memset(data, 0, sizeof (__u8) * WDC_ADD_LOG_BUF_LEN);
  
-       ret = nvme_get_log(fd, WDC_NVME_ADD_LOG_OPCODE, 0x01, 0, 0, 0, false, 0,
 -      ret = nvme_get_log(fd, 0x01, WDC_NVME_ADD_LOG_OPCODE, false,
++      ret = nvme_get_log_page(fd, 0x01, WDC_NVME_ADD_LOG_OPCODE, false,
                           WDC_ADD_LOG_BUF_LEN, data);
        if (strcmp(format, "json"))
 -              fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret);
 +              fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret, false), ret);
        if (ret == 0) {
                l = (struct wdc_log_page_header*)data;
                total_subpages = l->num_subpages + WDC_NVME_GET_STAT_PERF_INTERVAL_LIFETIME - 1;
@@@ -3275,10 -3731,10 +3683,10 @@@ static int wdc_get_d0_log_page(int fd, 
        }
        memset(data, 0, sizeof (__u8) * WDC_NVME_VU_SMART_LOG_LEN);
  
-       ret = nvme_get_log(fd, WDC_NVME_GET_VU_SMART_LOG_OPCODE, 0xFFFFFFFF, 0,0,0,
-                          false, 0, WDC_NVME_VU_SMART_LOG_LEN, data);
 -      ret = nvme_get_log(fd, 0xFFFFFFFF, WDC_NVME_GET_VU_SMART_LOG_OPCODE,
++      ret = nvme_get_log_page(fd, 0xFFFFFFFF, WDC_NVME_GET_VU_SMART_LOG_OPCODE,
+                          false, WDC_NVME_VU_SMART_LOG_LEN, data);
        if (strcmp(format, "json"))
 -              fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret);
 +              fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret, false), ret);
  
        if (ret == 0) {
                /* parse the data */
@@@ -3726,9 -4183,17 +4134,17 @@@ static int wdc_vs_telemetry_controller_
                goto out;
        }
  
+       /* allow only one option at a time */
+       if ((cfg.disable + cfg.enable + cfg.status) > 1) {
+               fprintf(stderr, "ERROR : WDC : Invalid option\n");
+               ret = -1;
+               goto out;
+       }
        if (cfg.disable) {
 -              ret = nvme_set_feature(fd, 0, WDC_VU_DISABLE_CNTLR_TELEMETRY_OPTION_FEATURE_ID, 1,
 -                                     0, 0, 0, buf, &result);
 +              ret = nvme_set_features(fd, WDC_VU_DISABLE_CNTLR_TELEMETRY_OPTION_FEATURE_ID, 0, 1,
 +                                     0, false, 0, 0, 0, NULL, &result);
  
                wdc_clear_reason_id(fd);
        }
                                else
                                        fprintf(stderr, "Controller Option Telemetry Log Page State: Enabled\n");
                        } else {
 -                              fprintf(stderr, "ERROR : WDC: NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret);
 +                              fprintf(stderr, "ERROR : WDC: NVMe Status:%s(%x)\n", nvme_status_to_string(ret, false), ret);
                        }
 -         }
 -         else {
 +         } else {
                        fprintf(stderr, "ERROR : WDC: unsupported option for this command\n");
+                       fprintf(stderr, "Please provide an option, -d, -e or -s\n");
                        ret = -1;
                        goto out;
           }
@@@ -4532,6 -4999,25 +4949,18 @@@ static int wdc_do_namespace_resize(int 
        return ret;
  }
  
 -      int ret;
 -      struct nvme_admin_cmd admin_cmd;
 -
 -      memset(&admin_cmd, 0, sizeof (struct nvme_admin_cmd));
 -      admin_cmd.opcode = WDC_NVME_DRIVE_INFO_OPCODE;
 -      admin_cmd.cdw12 = ((WDC_NVME_DRIVE_INFO_SUBCMD << WDC_NVME_SUBCMD_SHIFT) |
 -                          WDC_NVME_DRIVE_INFO_CMD);
 -
 -      ret = nvme_submit_admin_passthru(fd, &admin_cmd);
 -
 -      if (!ret && result)
 -              *result = admin_cmd.result;
+ static int wdc_do_drive_info(int fd, __u32 *result)
+ {
 -      return ret;
++      struct nvme_passthru_cmd admin_cmd = {
++              .opcode = WDC_NVME_DRIVE_INFO_OPCODE,
++              .cdw12  = ((WDC_NVME_DRIVE_INFO_SUBCMD << WDC_NVME_SUBCMD_SHIFT) |
++                              WDC_NVME_DRIVE_INFO_CMD),
++      };
++      return nvme_submit_admin_passthru(fd, &admin_cmd, result);
+ }
  static int wdc_drive_resize(int argc, char **argv,
                struct command *command, struct plugin *plugin)
  {
@@@ -5127,3 -5612,44 +5556,44 @@@ static int wdc_vs_nand_stats(int argc, 
  
        return ret;
  }
 -      fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret);
+ static int wdc_vs_drive_info(int argc, char **argv,
+               struct command *command, struct plugin *plugin)
+ {
+       const char *desc = "Send a vs-drive-info command.";
+       uint64_t capabilities = 0;
+       int fd, ret;
+       __le32 result;
+       __u16 size;
+       double rev;
+       OPT_ARGS(opts) = {
+               OPT_END()
+       };
+       fd = parse_and_open(argc, argv, desc, opts);
+       if (fd < 0)
+               return fd;
+       wdc_check_device(fd);
+       capabilities = wdc_get_drive_capabilities(fd);
+       if ((capabilities & WDC_DRIVE_CAP_INFO) == WDC_DRIVE_CAP_INFO) {
+               ret = wdc_do_drive_info(fd, &result);
+       } else {
+               fprintf(stderr, "ERROR : WDC: unsupported device for this command\n");
+               ret = -1;
+       }
+       if (!ret) {
+               size = (__u16)((cpu_to_le32(result) & 0xffff0000) >> 16);
+               rev = (double)(cpu_to_le32(result) & 0x0000ffff);
+               printf("Drive HW Revison: %4.1f\n", (.1 * rev));
+               printf("FTL Unit Size:     0x%x KB\n", size);
+       }
++      fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret, false), ret);
+       return ret;
+ }
index 7764a5811c7e1e0344e052703b412afa0876ffee,0000000000000000000000000000000000000000..49150ee1b9282e265480dcfbd1f20d9fdb9eafc8
mode 100644,000000..100644
--- /dev/null
@@@ -1,4221 -1,0 +1,4221 @@@
-                else if (fabrics)
 +#include <errno.h>
 +#include <stdbool.h>
 +#include <stdio.h>
 +#include <stdlib.h>
 +#include <string.h>
 +
 +#include <sys/param.h>
 +#include <json-c/json.h>
 +#include <uuid/uuid.h>
 +
 +#include "user-types.h"
 +
 +static const char dash[101] = {[0 ... 99] = '-'};
 +
 +void d(unsigned char *buf, int len, int width, int group)
 +{
 +      int i, offset = 0;
 +      char ascii[32 + 1];
 +      bool line_done = false;
 +
 +      printf("     ");
 +      for (i = 0; i <= 15; i++)
 +              printf("%3x", i);
 +
 +      for (i = 0; i < len; i++) {
 +              line_done = false;
 +              if (i % width == 0)
 +                      printf( "\n%04x:", offset);
 +              if (i % group == 0)
 +                      printf( " %02x", buf[i]);
 +              else
 +                      printf( "%02x", buf[i]);
 +              ascii[i % width] = (buf[i] >= '!' && buf[i] <= '~') ? buf[i] : '.';
 +              if (((i + 1) % width) == 0) {
 +                      ascii[i % width + 1] = '\0';
 +                      printf( " \"%.*s\"", width, ascii);
 +                      offset += width;
 +                      line_done = true;
 +              }
 +      }
 +
 +      if (!line_done) {
 +              unsigned b = width - (i % width);
 +
 +              ascii[i % width + 1] = '\0';
 +              printf( " %*s \"%.*s\"",
 +                              2 * b + b / group + (b % group ? 1 : 0), "",
 +                              width, ascii);
 +      }
 +      printf( "\n");
 +}
 +
 +void d_raw(unsigned char *buf, unsigned len)
 +{
 +      unsigned i;
 +      for (i = 0; i < len; i++)
 +              putchar(*(buf+i));
 +}
 +
 +#define ARRAY_SIZE(x) sizeof(x) / sizeof(*x)
 +#define ARGSTR(s, i) arg_str(s, ARRAY_SIZE(s), i)
 +
 +static const char *arg_str(const char * const *strings,
 +              size_t array_size, size_t idx)
 +{
 +      if (idx < array_size && strings[idx])
 +              return strings[idx];
 +      return "unrecognized";
 +}
 +
 +static const char * const features_sels[] = {
 +      [NVME_GET_FEATURES_SEL_CURRENT] = "current",
 +      [NVME_GET_FEATURES_SEL_DEFAULT] = "default",
 +      [NVME_GET_FEATURES_SEL_SAVED]   = "saved",
 +};
 +
 +const char *nvme_get_feature_select_to_string(__u8 sel)
 +{
 +      return ARGSTR(features_sels, sel);
 +}
 +
 +static const char * const cntrltypes[] = {
 +      [NVME_CTRL_CNTRLTYPE_IO]        = "io",
 +      [NVME_CTRL_CNTRLTYPE_DISCOVERY] = "discovery",
 +      [NVME_CTRL_CNTRLTYPE_ADMIN]     = "administrative",
 +};
 +
 +const char *nvme_id_ctrl_cntrltype_str(__u8 cntrltype)
 +{
 +      return ARGSTR(cntrltypes, cntrltype);
 +}
 +
 +static const char * const rbtypes[] = {
 +      [NVME_NS_DLFEAT_RB_NR]          = "not-reported",
 +      [NVME_NS_DLFEAT_RB_ALL_0S]      = "all-0s",
 +      [NVME_NS_DLFEAT_RB_ALL_FS]      = "all-fs",
 +};
 +
 +const char *id_ns_dlfeat_rb_str(__u8 rb)
 +{
 +      return ARGSTR(rbtypes, rb);
 +}
 +
 +static const char * const dpstypes[] = {
 +      [NVME_NS_DPS_PI_NONE]   = "none",
 +      [NVME_NS_DPS_PI_TYPE1]  = "type1",
 +      [NVME_NS_DPS_PI_TYPE2]  = "type2",
 +      [NVME_NS_DPS_PI_TYPE3]  = "type3",
 +};
 +
 +const char *nvme_id_ns_dps_str(__u8 dps)
 +{
 +      return ARGSTR(dpstypes, dps);
 +}
 +
 +static const char * const rptypes[] = {
 +      [NVME_LBAF_RP_BEST]     = "best",
 +      [NVME_LBAF_RP_BETTER]   = "better",
 +      [NVME_LBAF_RP_GOOD]     = "good",
 +      [NVME_LBAF_RP_DEGRADED] = "degraded",
 +};
 +
 +const char *nvme_id_ns_lbaf_rp_str(__u8 rp)
 +{
 +      return ARGSTR(rptypes, rp);
 +}
 +
 +static const char * const featuretypes[] = {
 +      [NVME_FEAT_FID_ARBITRATION]             = "Arbitration",
 +      [NVME_FEAT_FID_POWER_MGMT]              = "Power Management",
 +      [NVME_FEAT_FID_LBA_RANGE]               =  "LBA Range Type",
 +      [NVME_FEAT_FID_TEMP_THRESH]             = "Temperature Threshold",
 +      [NVME_FEAT_FID_ERR_RECOVERY]            = "Error Recovery",
 +      [NVME_FEAT_FID_VOLATILE_WC]             = "Volatile Write Cache",
 +      [NVME_FEAT_FID_NUM_QUEUES]              = "Number of Queues",
 +      [NVME_FEAT_FID_IRQ_COALESCE]            = "Interrupt Coalescing",
 +      [NVME_FEAT_FID_IRQ_CONFIG]              = "Interrupt Vector Configuration",
 +      [NVME_FEAT_FID_WRITE_ATOMIC]            = "Write Atomicity Normal",
 +      [NVME_FEAT_FID_ASYNC_EVENT]             = "Async Event Configuration",
 +      [NVME_FEAT_FID_AUTO_PST]                = "Autonomous Power State Transition",
 +      [NVME_FEAT_FID_HOST_MEM_BUF]            = "Host Memory Buffer",
 +      [NVME_FEAT_FID_TIMESTAMP]               = "Timestamp",
 +      [NVME_FEAT_FID_KATO]                    = "Keep Alive Timer",
 +      [NVME_FEAT_FID_HCTM]                    = "Host Controlled Thermal Management",
 +      [NVME_FEAT_FID_NOPSC]                   = "Non-Operational Power State Config",
 +      [NVME_FEAT_FID_RRL]                     = "Read Recovery Level Config",
 +      [NVME_FEAT_FID_PLM_CONFIG]              = "Predicatable Latency Mode Config",
 +      [NVME_FEAT_FID_PLM_WINDOW]              = "Predicatable Latency Mode Window",
 +      [NVME_FEAT_FID_LBA_STS_INTERVAL]        = "LBA Status Infomration Report Interval",
 +      [NVME_FEAT_FID_HOST_BEHAVIOR]           = "Host Behavior Support",
 +      [NVME_FEAT_FID_SANITIZE]                = "Sanitize Config",
 +      [NVME_FEAT_FID_ENDURANCE_EVT_CFG]       = "Endurance Group Event Configuration",
 +      [NVME_FEAT_FID_SW_PROGRESS]             = "Software Progress",
 +      [NVME_FEAT_FID_HOST_ID]                 = "Host Identifier",
 +      [NVME_FEAT_FID_RESV_MASK]               = "Reservation Notification Mask",
 +      [NVME_FEAT_FID_RESV_PERSIST]            = "Reservation Persistence",
 +      [NVME_FEAT_FID_WRITE_PROTECT]           = "Namespce Write Protection Config",
 +};
 +
 +const char *nvme_feature_str(__u8 fid)
 +{
 +      return ARGSTR(featuretypes, fid);
 +}
 +
 +static const char * const nidttypes[] = {
 +      [NVME_NIDT_EUI64]       = "eui64",
 +      [NVME_NIDT_NGUID]       = "nguid",
 +      [NVME_NIDT_UUID]        = "uuid",
 +};
 +
 +const char *nvme_id_nsdesc_nidt_str(__u8 nidt)
 +{
 +      return ARGSTR(nidttypes, nidt);
 +}
 +
 +static const char * const associationtypes[] = {
 +      [NVME_ID_UUID_ASSOCIATION_NONE]                 = "none",
 +      [NVME_ID_UUID_ASSOCIATION_VENDOR]               = "vendor",
 +      [NVME_ID_UUID_ASSOCIATION_SUBSYSTEM_VENDOR]     = "subsystem-vendor",
 +};
 +
 +const char *nvme_id_uuid_assoc_str(__u8 assoc)
 +{
 +      return ARGSTR(associationtypes, assoc);
 +}
 +
 +static const char * const trtypes[] = {
 +      [NVMF_TRTYPE_UNSPECIFIED]       = "unspecified",
 +      [NVMF_TRTYPE_RDMA]              = "rdma",
 +      [NVMF_TRTYPE_FC]                = "fc",
 +      [NVMF_TRTYPE_TCP]               = "tcp",
 +      [NVMF_TRTYPE_LOOP]              = "loop",
 +};
 +
 +const char *nvmf_trtype_str(__u8 trtype)
 +{
 +      return ARGSTR(trtypes, trtype);
 +}
 +
 +static const char * const adrfams[] = {
 +      [NVMF_ADDR_FAMILY_PCI]  = "pci",
 +      [NVMF_ADDR_FAMILY_IP4]  = "ipv4",
 +      [NVMF_ADDR_FAMILY_IP6]  = "ipv6",
 +      [NVMF_ADDR_FAMILY_IB]   = "infiniband",
 +      [NVMF_ADDR_FAMILY_FC]   = "fibre-channel",
 +};
 +
 +const char *nvmf_adrfam_str(__u8 adrfam)
 +{
 +      return ARGSTR(adrfams, adrfam);
 +}
 +
 +static const char * const subtypes[] = {
 +      [NVME_NQN_DISC]         = "discovery subsystem",
 +      [NVME_NQN_NVME]         = "nvme subsystem",
 +};
 +
 +const char *nvmf_subtype_str(__u8 subtype)
 +{
 +      return ARGSTR(subtypes, subtype);
 +}
 +
 +static const char * const treqs[] = {
 +      [NVMF_TREQ_NOT_SPECIFIED]       = "not specified",
 +      [NVMF_TREQ_REQUIRED]            = "required",
 +      [NVMF_TREQ_NOT_REQUIRED]        = "not required",
 +      [NVMF_TREQ_DISABLE_SQFLOW]      = "not specified, sq flow control disable supported",
 +};
 +
 +const char *nvmf_treq_str(__u8 treq)
 +{
 +      return ARGSTR(treqs, treq);
 +}
 +
 +static const char * const sectypes[] = {
 +      [NVMF_TCP_SECTYPE_NONE]         = "none",
 +      [NVMF_TCP_SECTYPE_TLS]          = "tls",
 +};
 +
 +const char *nvmf_sectype_str(__u8 sectype)
 +{
 +      return ARGSTR(sectypes, sectype);
 +}
 +
 +static const char * const prtypes[] = {
 +      [NVMF_RDMA_PRTYPE_NOT_SPECIFIED]        = "not specified",
 +      [NVMF_RDMA_PRTYPE_IB]                   = "infiniband",
 +      [NVMF_RDMA_PRTYPE_ROCE]                 = "roce",
 +      [NVMF_RDMA_PRTYPE_ROCEV2]               = "roce-v2",
 +      [NVMF_RDMA_PRTYPE_IWARP]                = "iwarp",
 +};
 +
 +const char *nvmf_prtype_str(__u8 prtype)
 +{
 +      return ARGSTR(prtypes, prtype);
 +}
 +
 +static const char * const qptypes[] = {
 +      [NVMF_RDMA_QPTYPE_CONNECTED]    = "connected",
 +      [NVMF_RDMA_QPTYPE_DATAGRAM]     = "datagram",
 +};
 +
 +const char *nvmf_qptype_str(__u8 qptype)
 +{
 +      return ARGSTR(qptypes, qptype);
 +}
 +
 +static const char * const cms[] = {
 +      [NVMF_RDMA_CMS_RDMA_CM] = "rdma-cm",
 +};
 +
 +const char *nvmf_cms_str(__u8 cm)
 +{
 +      return ARGSTR(cms, cm);
 +}
 +
 +static const char * const generic_status[] = {
 +      [NVME_SC_SUCCESS]                 = "Successful Completion: The command completed without error",
 +      [NVME_SC_INVALID_OPCODE]          = "Invalid Command Opcode: A reserved coded value or an unsupported value in the command opcode field",
 +      [NVME_SC_INVALID_FIELD]           = "Invalid Field in Command: A reserved coded value or an unsupported value in a defined field",
 +      [NVME_SC_CMDID_CONFLICT]          = "Command ID Conflict: The command identifier is already in use",
 +      [NVME_SC_DATA_XFER_ERROR]         = "Data Transfer Error: Transferring the data or metadata associated with a command experienced an error",
 +      [NVME_SC_POWER_LOSS]              = "Commands Aborted due to Power Loss Notification: Indicates that the command was aborted due to a power loss notification",
 +      [NVME_SC_INTERNAL]                = "Internal Error: The command was not completed successfully due to an internal error",
 +      [NVME_SC_ABORT_REQ]               = "Command Abort Requested: The command was aborted due to an Abort command",
 +      [NVME_SC_ABORT_QUEUE]             = "Command Aborted due to SQ Deletion: The command was aborted due to a Delete I/O Submission Queue",
 +      [NVME_SC_FUSED_FAIL]              = "Command Aborted due to Failed Fused Command: The command was aborted due to the other command in a fused operation failing",
 +      [NVME_SC_FUSED_MISSING]           = "Command Aborted due to Missing Fused Command: The fused command was aborted due to the adjacent submission queue entry not containing a fused command",
 +      [NVME_SC_INVALID_NS]              = "Invalid Namespace or Format: The namespace or the format of that namespace is invalid",
 +      [NVME_SC_CMD_SEQ_ERROR]           = "Command Sequence Error: The command was aborted due to a protocol violation in a multi- command sequence",
 +      [NVME_SC_SGL_INVALID_LAST]        = "Invalid SGL Segment Descriptor: The command includes an invalid SGL Last Segment or SGL Segment descriptor",
 +      [NVME_SC_SGL_INVALID_COUNT]       = "Invalid Number of SGL Descriptors: There is an SGL Last Segment descriptor or an SGL Segment descriptor in a location other than the last descriptor of a segment based on the length indicated",
 +      [NVME_SC_SGL_INVALID_DATA]        = "Data SGL Length Invalid: The length of a Data SGL is too short or too long and the controller does not support SGL transfers longer than the amount of data to be transferred",
 +      [NVME_SC_SGL_INVALID_METADATA]    = "Metadata SGL Length Invalid: The length of a Metadata SGL is too short or too long and the controller does not support SGL transfers longer than the amount of data to be transferred",
 +      [NVME_SC_SGL_INVALID_TYPE]        = "SGL Descriptor Type Invalid: The type of an SGL Descriptor is a type that is not supported by the controller",
 +      [NVME_SC_CMB_INVALID_USE]         = "Invalid Use of Controller Memory Buffer: The attempted use of the Controller Memory Buffer is not supported by the controller",
 +      [NVME_SC_PRP_INVALID_OFFSET]      = "PRP Offset Invalid: The Offset field for a PRP entry is invalid",
 +      [NVME_SC_AWU_EXCEEDED]            = "Atomic Write Unit Exceeded: The length specified exceeds the atomic write unit size",
 +      [NVME_SC_OP_DENIED]               = "Operation Denied: The command was denied due to lack of access rights",
 +      [NVME_SC_SGL_INVALID_OFFSET]      = "SGL Offset Invalid: The offset specified in a descriptor is invalid",
 +      [NVME_SC_HOSTID_FORMAT]           = "Host Identifier Inconsistent Format: The NVM subsystem detected the simultaneous use of 64- bit and 128-bit Host Identifier values on different controllers",
 +      [NVME_SC_KAT_EXPIRED]             = "Keep Alive Timer Expired: The Keep Alive Timer expired",
 +      [NVME_SC_KAT_INVALID]             = "Keep Alive Timeout Invalid: The Keep Alive Timeout value specified is invalid",
 +      [NVME_SC_CMD_ABORTED_PREMEPT]     = "Command Aborted due to Preempt and Abort: The command was aborted due to a Reservation Acquire command",
 +      [NVME_SC_SANITIZE_FAILED]         = "Sanitize Failed: The most recent sanitize operation failed and no recovery action has been successfully completed",
 +      [NVME_SC_SANITIZE_IN_PROGRESS]    = "Sanitize In Progress: The requested function is prohibited while a sanitize operation is in progress",
 +      [NVME_SC_SGL_INVALID_GRANULARITY] = "SGL Data Block Granularity Invalid: The Address alignment or Length granularity for an SGL Data Block descriptor is invalid",
 +      [NVME_SC_CMD_IN_CMBQ_NOT_SUPP]    = "Command Not Supported for Queue in CMB: The controller does not support Submission Queue in the Controller Memory Buffer or Completion Queue in the Controller Memory Buffer",
 +      [NVME_SC_NS_WRITE_PROTECTED]      = "Namespace is Write Protected: The command is prohibited while the namespace is write protected",
 +      [NVME_SC_CMD_INTERRUPTED]         = "Command Interrupted: Command processing was interrupted and the controller is unable to successfully complete the command",
 +      [NVME_SC_TRAN_TPORT_ERROR]        = "Transient Transport Error: A transient transport error was detected",
 +      [NVME_SC_LBA_RANGE]               = "LBA Out of Range: The command references an LBA that exceeds the size of the namespace",
 +      [NVME_SC_CAP_EXCEEDED]            = "Capacity Exceeded: Execution of the command has caused the capacity of the namespace to be exceeded",
 +      [NVME_SC_NS_NOT_READY]            = "Namespace Not Ready: The namespace is not ready to be accessed",
 +      [NVME_SC_RESERVATION_CONFLICT]    = "Reservation Conflict: The command was aborted due to a conflict with a reservation held on the accessed namespace",
 +      [NVME_SC_FORMAT_IN_PROGRESS]      = "Format In Progress: A Format NVM command is in progress on the namespace",
 +};
 +
 +static const char * const cmd_spec_status[] = {
 +      [NVME_SC_CQ_INVALID]             = "Completion Queue Invalid: The Completion Queue identifier specified in the command does not exist",
 +      [NVME_SC_QID_INVALID]            = "Invalid Queue Identifier: The creation of the I/O Completion Queue failed due to an invalid queue identifier specified as part of the command",
 +      [NVME_SC_QUEUE_SIZE]             = "Invalid Queue Size: The host attempted to create an I/O Completion Queue with an invalid number of entries",
 +      [NVME_SC_ABORT_LIMIT]            = "Abort Command Limit Exceeded: The number of concurrently outstanding Abort commands has exceeded the limit indicated in the Identify Controller data structure",
 +      [NVME_SC_ASYNC_LIMIT]            = "Asynchronous Event Request Limit Exceeded: The number of concurrently outstanding Asynchronous Event Request commands has been exceeded",
 +      [NVME_SC_FIRMWARE_SLOT]          = "Invalid Firmware Slot: The firmware slot indicated is invalid or read only",
 +      [NVME_SC_FIRMWARE_IMAGE]         = "Invalid Firmware Image: The firmware image specified for activation is invalid and not loaded by the controller",
 +      [NVME_SC_INVALID_VECTOR]         = "Invalid Interrupt Vector: The creation of the I/O Completion Queue failed due to an invalid interrupt vector specified as part of the command",
 +      [NVME_SC_INVALID_LOG_PAGE]       = "Invalid Log Page: The log page indicated is invalid",
 +      [NVME_SC_INVALID_FORMAT]         = "Invalid Format: The LBA Format specified is not supported",
 +      [NVME_SC_FW_NEEDS_CONV_RESET]    = "Firmware Activation Requires Conventional Reset: The firmware commit was successful, however, activation of the firmware image requires a conventional reset",
 +      [NVME_SC_INVALID_QUEUE]          = "Invalid Queue Deletion: Invalid I/O Completion Queue specified to delete",
 +      [NVME_SC_FEATURE_NOT_SAVEABLE]   = "Feature Identifier Not Saveable: The Feature Identifier specified does not support a saveable value",
 +      [NVME_SC_FEATURE_NOT_CHANGEABLE] = "Feature Not Changeable: The Feature Identifier is not able to be changed",
 +      [NVME_SC_FEATURE_NOT_PER_NS]     = "Feature Not Namespace Specific: The Feature Identifier specified is not namespace specific",
 +      [NVME_SC_FW_NEEDS_SUBSYS_RESET]  = "Firmware Activation Requires NVM Subsystem Reset: The firmware commit was successful, however, activation of the firmware image requires an NVM Subsystem",
 +      [NVME_SC_FW_NEEDS_RESET]         = "Firmware Activation Requires Controller Level Reset: The firmware commit was successful; however, the image specified does not support being activated without a reset",
 +      [NVME_SC_FW_NEEDS_MAX_TIME]      = "Firmware Activation Requires Maximum Time Violation: The image specified if activated immediately would exceed the Maximum Time for Firmware Activation (MTFA) value reported in Identify Controller",
 +      [NVME_SC_FW_ACTIVATE_PROHIBITED] = "Firmware Activation Prohibited: The image specified is being prohibited from activation by the controller for vendor specific reasons",
 +      [NVME_SC_OVERLAPPING_RANGE]      = "Overlapping Range: The downloaded firmware image has overlapping ranges",
 +      [NVME_SC_NS_INSUFFICIENT_CAP]    = "Namespace Insufficient Capacity: Creating the namespace requires more free space than is currently available",
 +      [NVME_SC_NS_ID_UNAVAILABLE]      = "Namespace Identifier Unavailable: The number of namespaces supported has been exceeded",
 +      [NVME_SC_NS_ALREADY_ATTACHED]    = "Namespace Already Attached: The controller is already attached to the namespace specified",
 +      [NVME_SC_NS_IS_PRIVATE]          = "Namespace Is Private: The namespace is private and is already attached to one controller",
 +      [NVME_SC_NS_NOT_ATTACHED]        = "Namespace Not Attached: The request to detach the controller could not be completed because the controller is not attached to the namespace",
 +      [NVME_SC_THIN_PROV_NOT_SUPP]     = "Thin Provisioning Not Supported: Thin provisioning is not supported by the controller",
 +      [NVME_SC_CTRL_LIST_INVALID]      = "Controller List Invalid: The controller list provided contains invalid controller ids",
 +      [NVME_SC_SELF_TEST_IN_PROGRESS]  = "Device Self-test In Progress",
 +      [NVME_SC_BP_WRITE_PROHIBITED]    = "Boot Partition Write Prohibited: The command tried to modify a locked Boot Partition",
 +      [NVME_SC_INVALID_CTRL_ID]        = "Invalid Controller Identifier: An invalid controller id was specified",
 +      [NVME_SC_INVALID_SEC_CTRL_STATE] = "Invalid Secondary Controller State: The requested secondary controller action is invalid based on the secondary and primary controllers current states",
 +      [NVME_SC_INVALID_CTRL_RESOURCES] = "Invalid Number of Controller Resources: The specified number of Flexible Resources is invalid",
 +      [NVME_SC_INVALID_RESOURCE_ID]    = "Invalid Resource Identifier: At least one of the specified resource identifiers was invalid",
 +      [NVME_SC_PMR_SAN_PROHIBITED]     = "Sanitize Prohibited While Persistent Memory Region is Enabled",
 +      [NVME_SC_ANA_GROUP_ID_INVALID]   = "ANA Group Identifier Invalid",
 +      [NVME_SC_ANA_ATTACH_FAILED]      = "ANA Attach Failed: The command's specified ANA Group Identifier is not supported",
 +};
 +
 +static const char * const nvm_status[] = {
 +      [NVME_SC_BAD_ATTRIBUTES] = "Conflicting Attributes: The attributes specified in the command are conflicting",
 +      [NVME_SC_INVALID_PI]     = "Invalid Protection Information: The command's Protection Information Field settings are invalid for the namespace's Protection Information format",
 +      [NVME_SC_READ_ONLY]      = "Attempted Write to Read Only Range: The LBA range specified contains read-only blocks",
 +};
 +
 +static const char * const nvmf_status[] = {
 +      [NVME_SC_CONNECT_FORMAT]           = "Incompatible Format: The NVM subsystem does not support the record format specified by the host",
 +      [NVME_SC_CONNECT_CTRL_BUSY]        = "Controller Busy: The controller is already associated with a host",
 +      [NVME_SC_CONNECT_INVALID_PARAM]    = "Connect Invalid Parameters: One or more of the command parameters",
 +      [NVME_SC_CONNECT_RESTART_DISC]     = "Connect Restart Discovery: The NVM subsystem requested is not available",
 +      [NVME_SC_CONNECT_INVALID_HOST]     = "Connect Invalid Host: The host is not allowed to establish an association to either any controller in the NVM subsystem or the specified controller",
 +      [NVME_SC_DISCONNECT_INVALID_QTYPE] = "Invalid Queue Type: The command was sent on the wrong queue type",
 +      [NVME_SC_DISCOVERY_RESTART]        = "Discover Restart: The snapshot of the records is now invalid or out of date",
 +      [NVME_SC_AUTH_REQUIRED]            = "Authentication Required: NVMe in-band authentication is required and the queue has not yet been authenticated",
 +};
 +
 +static const char * const media_status[] = {
 +      [NVME_SC_WRITE_FAULT]     = "Write Fault: The write data could not be committed to the media",
 +      [NVME_SC_READ_ERROR]      = "Unrecovered Read Error: The read data could not be recovered from the media",
 +      [NVME_SC_GUARD_CHECK]     = "End-to-end Guard Check Error: The command was aborted due to an end-to-end guard check failure",
 +      [NVME_SC_APPTAG_CHECK]    = "End-to-end Application Tag Check Error: The command was aborted due to an end-to-end application tag check failure",
 +      [NVME_SC_REFTAG_CHECK]    = "End-to-end Reference Tag Check Error: The command was aborted due to an end-to-end reference tag check failure",
 +      [NVME_SC_COMPARE_FAILED]  = "Compare Failure: The command failed due to a miscompare during a Compare command",
 +      [NVME_SC_ACCESS_DENIED]   = "Access Denied: Access to the namespace and/or LBA range is denied due to lack of access rights",
 +      [NVME_SC_UNWRITTEN_BLOCK] = "Deallocated or Unwritten Logical Block: The command failed due to an attempt to read from or verify an LBA range containing a deallocated or unwritten logical block",
 +};
 +
 +static const char * const path_status[] = {
 +      [NVME_SC_ANA_INTERNAL_PATH_ERROR] = "Internal Path Error: An internal error specific to the controller processing the commmand prevented completion",
 +      [NVME_SC_ANA_PERSISTENT_LOSS]     = "Asymmetric Access Persistent Loss: The controller is in a persistent loss state with the requested namespace",
 +      [NVME_SC_ANA_INACCESSIBLE]        = "Asymmetric Access Inaccessible: The controller is in an inaccessible state with the requested namespace",
 +      [NVME_SC_ANA_TRANSITION]          = "Asymmetric Access Transition: The controller is currently transitioning states with the requested namespace",
 +      [NVME_SC_CTRL_PATH_ERROR]         = "Controller Pathing Error: A pathing error was detected by the controller",
 +      [NVME_SC_HOST_PATH_ERROR]         = "Host Pathing Error: A pathing error was detected by the host",
 +      [NVME_SC_CMD_ABORTED_BY_HOST]     = "Command Aborted By Host: The command was aborted as a result of host action",
 +};
 +
 +const char *nvme_status_to_string(int status, bool fabrics)
 +{
 +      const char *s = NULL;
 +      __u16 sc, sct;
 +
 +      if (status < 0)
 +              return strerror(errno);
 +
 +      sc = status & NVME_SC_MASK;
 +      sct = status & NVME_SCT_MASK;
 +
 +      switch (sct) {
 +      case NVME_SCT_GENERIC:
 +              s = ARGSTR(generic_status, sc);
 +              break;
 +      case NVME_SCT_CMD_SPECIFIC:
 +              if (sc < ARRAY_SIZE(cmd_spec_status))
 +                      s = ARGSTR(cmd_spec_status, sc);
++              else if (fabrics)
 +                      s = ARGSTR(nvmf_status, sc);
 +              else
 +                      s = ARGSTR(nvm_status, sc);
 +              break;
 +      case NVME_SCT_MEDIA:
 +              s = ARGSTR(media_status, sc);
 +              break;
 +      case NVME_SCT_PATH:
 +              s = ARGSTR(path_status, sc);
 +              break;
 +      case NVME_SCT_VS:
 +              s = "Vendor Specific Status";
 +              break;
 +      default:
 +              s = "Unknown status";
 +              break;
 +      }
 +
 +      return s;
 +}
 +
 +static const char *iec[] = {
 +      "B",
 +      "KiB",
 +      "MiB",
 +      "GiB",
 +      "TiB",
 +      "PiB",
 +      "EiB",
 +      "ZiB",
 +      "YiB",
 +};
 +
 +static const char *jedec[] = {
 +      "B",
 +      "KB",
 +      "MB",
 +      "GB",
 +      "TB",
 +      "PB",
 +      "EB",
 +      "ZB",
 +      "YB",
 +};
 +
 +static void util_split(uint64_t v, uint16_t *idx,  uint16_t *major,
 +      uint16_t *minor, uint16_t *jmajor, uint16_t *jminor)
 +{
 +      uint64_t lower = 0, upper = v, mag = 1;
 +      int i, j;
 +
 +      for (i = 0; i < ARRAY_SIZE(iec) - 1 && upper > 1024; i++)
 +              upper >>= 10;
 +      if (i)
 +              lower = (((v - (upper << (10 * i))) >> ((i - 1) * 10)) * 100) >> 10;;
 +
 +      *major = upper;
 +      *minor = lower;
 +      *idx = i;
 +
 +      if (!jmajor)
 +              return;
 +
 +      for (j = 0; j < i; j++)
 +              mag *= 1000;
 +      upper = v / mag;
 +
 +      if (j)
 +              lower = (v % mag) / (mag / 1000);
 +      *jmajor = upper;
 +      *jminor = lower;
 +}
 +
 +static int __display_human_size128(struct json_object *o, struct printbuf *p,
 +      int l, int f)
 +{
 +      uint16_t i = 0,  major = 0, minor = 0, jmajor = 0, jminor = 0, bias = 0;
 +      uint8_t *s = (uint8_t *)json_object_get_string(o);
 +      uint64_t upper = 0, lower = 0, v;
 +      int j;
 +
 +        for (j = 7; j >= 0; j--) {
 +                lower = lower * 256 + s[j];
 +              upper = upper * 256 + s[j + 8];
 +      }
 +
 +      if (upper) {
 +              if (upper >= (1 << 16)) {
 +                      v = upper >> 6;
 +                      bias = 7;
 +              } else if (upper >= (1 << 6)) {
 +                      v = (upper << 4) | (lower >> 60);
 +                      bias = 6;
 +              } else {
 +                      v = (upper << 14) | (lower >> 50);
 +                      bias = 5;
 +              }
 +      } else
 +              v = lower;
 +
 +      util_split(v, &i, &major, &minor, &jmajor, &jminor);
 +      return sprintbuf(p, "%u.%02u %s (%lu.%03lu %s)", major, minor,
 +              iec[i + bias], jmajor, jminor, jedec[i + bias]);
 +}
 +
 +static int __display_human_size128_str(struct json_object *o, struct printbuf *p,
 +      int l, int f)
 +{
 +      printbuf_memappend(p, "\"", 1);
 +      __display_human_size128(o, p, l, f);
 +      printbuf_memappend(p, "\"", 1);
 +
 +      return 0;
 +}
 +
 +static int __display_human_size(struct json_object *o, struct printbuf *p,
 +      int l, int f)
 +{
 +      uint16_t i = 0, major = 0, minor = 0, jmajor = 0, jminor = 0;
 +      uint64_t v = json_object_get_int64(o);
 +      
 +      util_split(v, &i, &major, &minor, &jmajor, &jminor);
 +      return sprintbuf(p, "%u.%02u %s (%lu.%03lu %s)", major, minor, iec[i],
 +              jmajor, jminor, jedec[i]);
 +}
 +
 +static int __display_human_size_str(struct json_object *o, struct printbuf *p,
 +      int l, int f)
 +{
 +      printbuf_memappend(p, "\"", 1);
 +      __display_human_size(o, p, l, f);
 +      printbuf_memappend(p, "\"", 1);
 +
 +      return 0;
 +}
 +
 +static int _display_human_size(struct json_object *o, struct printbuf *p,
 +      int l, int f)
 +{
 +      uint64_t v = json_object_get_int64(o);
 +      uint16_t i = 0, major = 0, minor = 0;
 +      
 +      util_split(v, &i, &major, &minor, NULL, NULL);
 +      if (minor)
 +              return sprintbuf(p, "%u.%02u %s", major, minor, iec[i]);
 +      return sprintbuf(p, "%u %s", major, iec[i]);
 +}
 +
 +static int _display_human_size_str(struct json_object *o, struct printbuf *p,
 +      int l, int f)
 +{
 +      printbuf_memappend(p, "\"", 1);
 +      _display_human_size(o, p, l, f);
 +      printbuf_memappend(p, "\"", 1);
 +
 +      return 0;
 +}
 +
 +static int display_human_size(struct json_object *o, struct printbuf *p,
 +      int l, int f)
 +{
 +      uint16_t i = 0, major = 0, minor = 0;
 +      uint64_t v = json_object_get_int64(o);
 +
 +      util_split(v, &i, &major, &minor, NULL, NULL);
 +      return sprintbuf(p, "%u %s", major, iec[i]);
 +}
 +
 +static int display_human_size_str(struct json_object *o, struct printbuf *p,
 +      int l, int f)
 +{
 +      printbuf_memappend(p, "\"", 1);
 +      display_human_size(o, p, l, f);
 +      printbuf_memappend(p, "\"", 1);
 +
 +      return 0;
 +}
 +
 +static int display_binary(struct json_object *o, struct printbuf *p,
 +      int l, int f)
 +{
 +      void *s = (void *)json_object_get_string(o);
 +      int len = json_object_get_string_len(o);
 +
 +      d_raw(s, len);
 +      return 0;
 +}
 +
 +static int display_hex_array(struct json_object *o, struct printbuf *p,
 +      int l, int f)
 +{
 +      uint8_t *s = (uint8_t *)json_object_get_string(o);
 +      int i, len = json_object_get_string_len(o);
 +
 +      for (i = 0; i < len; i++)
 +              sprintbuf(p, "%02x", s[i]);
 +      return 0;
 +}
 +
 +static int display_hex_array_str(struct json_object *o, struct printbuf *p,
 +      int l, int f)
 +{
 +      printbuf_memappend(p, "\"", 1);
 +      display_hex_array(o, p, l, f);
 +      printbuf_memappend(p, "\"", 1);
 +
 +      return 0;
 +}
 +
 +static void util_wrap_string(struct printbuf *pb, const char *s, int indent)
 +{
 +      const int width = 76;
 +      const char *c, *t;
 +      char *p, *wrap = malloc(strlen(s) * 4);
 +      int next_space = -1;
 +      int last_line = indent;
 +
 +      p = wrap;
 +      for (c = s; *c != 0; c++) {
 +              if (*c == '\n')
 +                      goto new_line;
 +
 +              if (*c == ' ' || next_space < 0) {
 +                      next_space = 0;
 +                      for (t = c + 1; *t != 0 && *t != ' '; t++)
 +                              next_space++;
 +
 +                      if (((int)(c - s) + indent + next_space) >
 +                          (last_line - indent + width)) {
 +new_line:
 +                              if (*(c + 1) == 0)
 +                                      continue;
 +                              last_line = (int) (c-s) + indent;
 +                              p += sprintf(p, "\n%-*s",  indent, "");
 +                              continue;
 +                      }
 +              }
 +              p += sprintf(p, "%c", *c);
 +      }
 +      p += sprintf(p, "\n");
 +      printbuf_memappend(pb, wrap, strlen(wrap));
 +      free(wrap);
 +}
 +
 +static int util_count_child_primitive_objects(struct json_object *j)
 +{
 +      int i = 0;
 +
 +      json_object_object_foreach(j, key, val) {
 +              (void)key;
 +
 +              switch (json_object_get_type(val)) {
 +              case json_type_boolean:
 +              case json_type_double:
 +              case json_type_int:
 +              case json_type_string:
 +                      i++;
 +                      break;
 +              default:
 +                      break;
 +              }
 +      }
 +
 +      return i;
 +}
 +
 +static void util_set_column_widths(struct json_object *j, int *widths)
 +{
 +      int i = 0;
 +
 +      json_object_object_foreach(j, key, val) {
 +              (void)key;
 +
 +              switch (json_object_get_type(val)) {
 +              case json_type_boolean:
 +              case json_type_double:
 +              case json_type_int:
 +              case json_type_string:
 +                      if (strlen(json_object_to_json_string(val)) + 2 > widths[i]) {
 +                              widths[i] =
 +                                      strlen(json_object_to_json_string(val)) + 2;
 +                      }
 +                      i++;
 +                      break;
 +              default:
 +                      break;
 +              }
 +      }
 +}
 +
 +static void util_init_column_widths(struct json_object *j, int *widths)
 +{
 +      int i = 0;
 +
 +      json_object_object_foreach(j, key, val) {
 +              switch (json_object_get_type(val)) {
 +              case json_type_boolean:
 +              case json_type_double:
 +              case json_type_int:
 +              case json_type_string:
 +                      break;
 +              default:
 +                      continue;;
 +              }
 +
 +              widths[i++] = strlen(key);
 +      }
 +}
 +
 +static void util_print_column_widths(struct json_object *j, int *widths,
 +      int indent, struct printbuf *p)
 +{
 +      int i = 0;
 +
 +      json_object_object_foreach(j, key, val) {
 +              switch (json_object_get_type(val)) {
 +              case json_type_boolean:
 +              case json_type_double:
 +              case json_type_int:
 +              case json_type_string:
 +                      break;
 +              default:
 +                      continue;
 +              }
 +
 +              sprintbuf(p, "%-*s%*s", i ? 1 : 0, "", widths[i], key);
 +              i++;
 +      }
 +      printbuf_memappend(p, "\n", 1);
 +
 +      i = 0;
 +      sprintbuf(p, "%-.*s : ", indent, dash);
 +      json_object_object_foreach(j, _key, _val) {
 +              (void)_key;
 +
 +              switch (json_object_get_type(_val)) {
 +              case json_type_boolean:
 +              case json_type_double:
 +              case json_type_int:
 +              case json_type_string:
 +                      break;
 +              default:
 +                      continue;
 +              }
 +
 +              sprintbuf(p, "%-*s%-.*s", i ? 1 : 0, "", widths[i], dash);
 +              i++;
 +      }
 +      printbuf_memappend(p, "\n", 1);
 +}
 +
 +static void util_print_values(struct json_object *j, int *widths,
 +      struct printbuf *p)
 +{
 +      int i = 0;
 +
 +      json_object_object_foreach(j, key, val) {
 +              (void)key;
 +
 +              switch (json_object_get_type(val)) {
 +              case json_type_boolean:
 +              case json_type_double:
 +              case json_type_int:
 +              case json_type_string:
 +                      break;
 +              default:
 +                      continue;
 +              }
 +
 +              sprintbuf(p, "%-*s%*s", i ? 1 : 0, "", widths[i], json_object_to_json_string(val));
 +              i++;
 +      }
 +      printbuf_memappend(p, "\n", 1);
 +}
 +
 +static void nvme_print_compact_json_array(struct printbuf *p, struct json_object *o, char *key, int space)
 +{
 +      size_t i, len = json_object_array_length(o);
 +      int *widths, indent = strlen(key);
 +      struct json_object *jso;
 +
 +      if (!len)
 +              return;
 +
 +      jso = json_object_array_get_idx(o, 0);
 +      if (json_object_get_type(jso) != json_type_object)
 +              return;
 +
 +      i = util_count_child_primitive_objects(jso);
 +      if (!i)
 +              return;
 +
 +      widths = calloc(i, sizeof(*widths));
 +      if (!widths)
 +              return;
 +
 +      widths = calloc(i, sizeof(*widths));
 +      util_init_column_widths(jso, widths);
 +      for (i = 0; i < len; i++) {
 +              jso = json_object_array_get_idx(o, i);
 +              util_set_column_widths(jso, widths);
 +      }
 +
 +      sprintbuf(p, "%-*s : ", space, key);
 +      jso = json_object_array_get_idx(o, 0);
 +      util_print_column_widths(jso, widths, indent, p);
 +      for (i = 0; i < len; i++) {
 +              jso = json_object_array_get_idx(o, i);
 +              sprintbuf(p, "%*d : ", indent, i);
 +              util_print_values(jso, widths, p);
 +      }
 +}
 +
 +int display_compact_object(struct json_object *jso, struct printbuf *p,
 +                                int level, int flags)
 +{
 +      int ret, l = 0, i = 0;
 +      char *buf;
 +
 +      json_object_object_foreach(jso, key, val) {
 +              switch (json_object_get_type(val)) {
 +              case json_type_boolean:
 +              case json_type_double:
 +              case json_type_int:
 +              case json_type_string:
 +                      sprintbuf(p, "%s%s:%s", i ? " " : "", key,
 +                              json_object_to_json_string(val));
 +                      i++;
 +                      break;
 +              case json_type_object:
 +                      sprintbuf(p, "\n%-*s:",  l, key);
 +                      l++;
 +                      ret = asprintf(&buf, "%*s%s", l * 2, "",
 +                              json_object_to_json_string(val));
 +                      l--;
 +                      if (ret < 0  || !buf)
 +                              break;
 +                      util_wrap_string(p, buf, (l + 1) * 2);
 +                      free(buf);
 +                      break;
 +              case json_type_array:
 +                      nvme_print_compact_json_array(p, val, key, l + 2);
 +                      break;
 +              default:
 +                      break;
 +              }
 +      }
 +      printbuf_memappend(p, "\n", 1);
 +
 +      return 0;
 +}
 +
 +int display_compact_object_str(struct json_object *jso, struct printbuf *p,
 +                                int level, int flags)
 +{
 +      int i = 0;
 +
 +      json_object_object_foreach(jso, key, val) {
 +              switch (json_object_get_type(val)) {
 +              case json_type_boolean:
 +              case json_type_double:
 +              case json_type_int:
 +              case json_type_string:
 +                      sprintbuf(p, "%s\"%s\":%s", i ? ", " : "", key,
 +                              json_object_to_json_string(val));
 +                      i++;
 +                      break;
 +              default:
 +                      break;
 +              }
 +      }
 +      return 0;
 +}
 +
 +static void nvme_print_json_array(struct printbuf *p, struct json_object *o, char *key, int space)
 +{
 +      size_t i, len = json_object_array_length(o);
 +      char *buf;
 +
 +      for (i = 0; i < len; i++) {
 +              int ret;
 +
 +              sprintbuf(p, "%-*s%3lu : ", space - 6, key, i);
 +              ret = asprintf(&buf, "%s", json_object_to_json_string(
 +                      json_object_array_get_idx(o, i)));
 +              if (ret < 0  || !buf)
 +                      break;
 +              util_wrap_string(p, buf, space);
 +              free(buf);
 +      }
 +}
 +
 +static int l = 0;
 +
 +int display_tabular(struct json_object *jso, struct printbuf *p,
 +                                int level, int flags)
 +{
 +      int ret, len = 0;
 +      char *buf;
 +
 +      json_object_object_foreach(jso, tkey, tval) {
 +              (void)tkey;
 +
 +              if (strlen(tkey) + 1 > len)
 +                      len = strlen(tkey) + 1;
 +      }
 +
 +      json_object_object_foreach(jso, key, val) {
 +              switch (json_object_get_type(val)) {
 +              case json_type_boolean:
 +              case json_type_double:
 +              case json_type_int:
 +              case json_type_string:
 +                      sprintbuf(p, "%-*s: %s\n", len, key,
 +                              json_object_to_json_string(val));
 +                      break;
 +              case json_type_object:
 +                      sprintbuf(p, "%-*s:\n",  len, key);
 +                      l++;
 +                      ret = asprintf(&buf, "%*s%s", l * 2, "",
 +                              json_object_to_json_string(val));
 +                      l--;
 +                      if (ret < 0  || !buf)
 +                              break;
 +                      util_wrap_string(p, buf, (l + 1) * 2);
 +                      free(buf);
 +                      break;
 +              case json_type_array:
 +                      nvme_print_json_array(p, val, key, len + 2);
 +                      break;
 +              default:
 +                      break;
 +              }
 +      }
 +      return 0;
 +}
 +
 +int display_tree(struct json_object *jso, struct printbuf *p,
 +               int level, int flags)
 +{
 +      static char char_stack[16];
 +      int j, i = 0;
 +
 +      if (l == 0)
 +              printbuf_memappend(p, ".\n", 2);
 +      if (l >= sizeof(char_stack))
 +              return 0;
 +
 +      json_object_object_foreach(jso, tkey, tval) {
 +              (void)tkey;
 +
 +              switch (json_object_get_type(tval)) {
 +              case json_type_object:
 +                      i++;
 +                      break;
 +              default:
 +                      break;
 +              }
 +      }
 +
 +      if (i > 1)
 +              char_stack[l] = '|';
 +      else    
 +              char_stack[l] = ' ';
 +
 +      printf("%s %d child objects\n", __func__, i);
 +      json_object_object_foreach(jso, key, val) {
 +              switch (json_object_get_type(val)) {
 +              case json_type_boolean:
 +              case json_type_double:
 +              case json_type_int:
 +              case json_type_string:
 +                      sprintbuf(p, " %s:%s%s", key,
 +                              json_object_to_json_string(val),
 +                              (i > 1)  ? " " : "");
 +                      break;
 +              case json_type_object:
 +                      for (j = 0; j < l; j++)
 +                              sprintbuf(p, "%c   ", char_stack[j]);
 +
 +                      l++;
 +                      sprintbuf(p, "%c-- %s - %s\n",  (i > 1) ? '|' : '`',
 +                              key, json_object_to_json_string(val));
 +                      l--;
 +                      break;
 +              case json_type_array:
 +                      l++;
 +                      sprintbuf(p, "%c-- %s - %s\n",  (i > 1) ? '|' : '`',
 +                              key, json_object_to_json_string(val));
 +                      l--;
 +                      break;
 +              default:
 +                      break;
 +              }
 +      }
 +      return 0;
 +}
 +
 +static int display_str(struct json_object *o, struct printbuf *p,
 +      int l, int f)
 +{
 +      const char *s = json_object_get_string(o);
 +      return printbuf_memappend(p, s, strlen(s));
 +}
 +
 +static int display_int128(struct json_object *o, struct printbuf *p,
 +      int l, int f)
 +{
 +      uint8_t *s = (uint8_t *)json_object_get_string(o);
 +      long double result = 0;
 +      char buf[40];
 +      int i;
 +
 +      for (i = 15; i >= 0; i--)
 +              result = result * 256 + s[i];
 +
 +      snprintf(buf, sizeof(buf), "%.0Lf", result);
 +      return printbuf_memappend(p, buf, strlen(buf));
 +}
 +
 +static int display_0x(struct json_object *o, struct printbuf *p,
 +      int l, int f)
 +{
 +      return sprintbuf(p, "%#llx", json_object_get_int64(o));
 +}
 +
 +static int display_0x_str(struct json_object *o, struct printbuf *p,
 +      int l, int f)
 +{
 +      printbuf_memappend(p, "\"", 1);
 +      display_0x(o, p, l, f);
 +      printbuf_memappend(p, "\"", 1);
 +
 +      return 0;
 +}
 +
 +static int display_hex(struct json_object *o, struct printbuf *p,
 +      int l, int f)
 +{
 +      return sprintbuf(p, "%llx", json_object_get_int64(o));
 +}
 +
 +static int display_hex_str(struct json_object *o, struct printbuf *p,
 +      int l, int f)
 +{
 +      printbuf_memappend(p, "\"", 1);
 +      display_hex(o, p, l, f);
 +      printbuf_memappend(p, "\"", 1);
 +
 +      return 0;
 +}
 +
 +static int display_percent(struct json_object *o, struct printbuf *p,
 +      int l, int f)
 +{
 +      return sprintbuf(p, "%u%%", json_object_get_int(o));
 +}
 +
 +static int display_percent_str(struct json_object *o, struct printbuf *p,
 +      int l, int f)
 +{
 +      printbuf_memappend(p, "\"", 1);
 +      display_percent(o, p, l, f);
 +      printbuf_memappend(p, "\"", 1);
 +
 +      return 0;
 +}
 +
 +static int display_temp_k(struct json_object *o, struct printbuf *p,
 +      int l, int f)
 +{
 +      uint16_t k = json_object_get_int(o);
 +      return sprintbuf(p, "%uC (%.2fF %uK)", k - 273,
 +              ((k - 273.15) * 9.0 / 5.0) + 32, k);
 +}
 +
 +static int display_temp_k_str(struct json_object *o, struct printbuf *p,
 +      int l, int f)
 +{
 +      printbuf_memappend(p, "\"", 1);
 +      display_temp_k(o, p, l, f);
 +      printbuf_memappend(p, "\"", 1);
 +
 +      return 0;
 +}
 +
 +static const char *tsuffix[] = {
 +      "µsec",
 +      "msec",
 +      "sec",
 +};
 +
 +static int display_time_us(struct json_object *o, struct printbuf *p,
 +      int l, int f)
 +{
 +      uint64_t d = 1, t, v = json_object_get_int64(o);
 +      int i = 0;
 +
 +      t = v;
 +      for (i = 0; i < 2 && (t / d) >= 1000; i++)
 +              d *= 1000;
 +
 +      t /= d;
 +      if (v % d)
 +              return sprintbuf(p, "%u.%u %s", t, v % d, tsuffix[i]);
 +      else
 +              return sprintbuf(p, "%u %s", t, tsuffix[i]);
 +}
 +
 +static int display_time_us_str(struct json_object *o, struct printbuf *p,
 +      int l, int f)
 +{
 +      printbuf_memappend(p, "\"", 1);
 +      display_time_us(o, p, l, f);
 +      printbuf_memappend(p, "\"", 1);
 +
 +      return 0;
 +}
 +
 +static int display_time_s(struct json_object *o, struct printbuf *p,
 +      int l, int f)
 +{
 +      uint64_t m, h, d, v = json_object_get_int64(o);
 +
 +      if (!v) {
 +              printbuf_memappend(p, "0", 1);
 +              return 0;
 +      }
 +
 +      d = v / (24 * 60 * 60);
 +      v %= (24 * 60 * 60);
 +
 +      h = v / (60 * 60);
 +      v %= (60 * 60);
 +
 +      m = v / 60;
 +      v %= 60;
 +
 +      if (d)
 +              sprintbuf(p, "%d day%s ", d, d > 1 ? "s" : "");
 +      if (h)
 +              sprintbuf(p, "%d hour%s ", h, h > 1 ? "s" : "");
 +      if (m)
 +              sprintbuf(p, "%d minute%s ", m, m > 1 ? "s" : "");
 +      if (v)
 +              sprintbuf(p, "%d second%s", v, v > 1 ? "s" : "");
 +
 +      return 0;
 +}
 +
 +static int display_time_s_str(struct json_object *o, struct printbuf *p,
 +      int l, int f)
 +{
 +      printbuf_memappend(p, "\"", 1);
 +      display_time_s(o, p, l, f);
 +      printbuf_memappend(p, "\"", 1);
 +
 +      return 0;
 +}
 +
 +static int display_hu_watts(struct json_object *o, struct printbuf *p,
 +      int l, int f)
 +{
 +      uint64_t v = json_object_get_int64(o);
 +
 +      if (v % 1000)
 +              return sprintbuf(p, "%u.%04uW", v / 10000, v % 10000);
 +      return sprintbuf(p, "%u.%02uW", v / 10000, (v % 10000) / 100);
 +}
 +
 +static int display_hu_watts_str(struct json_object *o, struct printbuf *p,
 +      int l, int f)
 +{
 +      printbuf_memappend(p, "\"", 1);
 +      display_hu_watts(o, p, l, f);
 +      printbuf_memappend(p, "\"", 1);
 +
 +      return 0;
 +}
 +
 +static int display_bool_terse(struct json_object *o, struct printbuf *p,
 +      int l, int f)
 +{
 +      return sprintbuf(p, "%c", json_object_get_boolean(o) ? '+' : '-');
 +}
 +
 +static int display_uuid(struct json_object *o, struct printbuf *p,
 +      int l, int f)
 +{
 +      char buf[40];
 +        uuid_t uuid;
 +
 +      memcpy((void *)uuid, json_object_get_string(o),
 +              sizeof(uuid_t));
 +        uuid_unparse(uuid, buf);
 +      return printbuf_memappend(p, buf, strlen(buf));
 +}
 +
 +static int display_uuid_str(struct json_object *o, struct printbuf *p,
 +      int l, int f)
 +{
 +      printbuf_memappend(p, "\"", 1);
 +      display_uuid(o, p, l, f);
 +      printbuf_memappend(p, "\"", 1);
 +
 +      return 1;
 +}
 +
 +static int display_oui(struct json_object *o, struct printbuf *p,
 +      int l, int f)
 +{
 +      uint8_t *value = (uint8_t *)json_object_get_string(o);
 +      int i, len = json_object_get_string_len(o);
 +
 +      for (i = 0; i < len; i++)
 +              sprintbuf(p, "%s%02x", i ? "-" : "", value[i]);
 +      return 1;
 +}
 +
 +static int display_oui_str(struct json_object *o, struct printbuf *p,
 +      int l, int f)
 +{
 +      printbuf_memappend(p, "\"", 1);
 +      display_oui(o, p, l, f);
 +      printbuf_memappend(p, "\"", 1);
 +
 +      return 1;
 +}
 +
 +static inline void fail_and_notify(void *o)
 +{
 +      if (o)
 +              return;
 +      fprintf(stderr,
 +              "Allocation of memory for json object failed, aborting\n");
 +      abort();
 +}
 +
 +struct json_object *nvme_json_new_str_len(const char *v, int l)
 +{
 +      struct json_object *o = json_object_new_string_len(v, l);
 +      fail_and_notify(o);
 +      return o;
 +}
 +
 +struct json_object *nvme_json_new_str_len_flags(const void *v, int l, unsigned long flags)
 +{
 +      struct json_object *o = nvme_json_new_str_len(v, l);
 +      if (flags & NVME_JSON_BINARY)
 +              json_object_set_serializer(o, display_binary, NULL, NULL);
 +      else if (flags & NVME_JSON_TABULAR)
 +              json_object_set_serializer(o, display_str, NULL, NULL);
 +      return o;
 +}
 +
 +struct json_object *nvme_json_new_str(const char *v, unsigned long flags)
 +{
 +      struct json_object *o = json_object_new_string(v);
 +      fail_and_notify(o);
 +      if (flags & NVME_JSON_TABULAR)
 +              json_object_set_serializer(o, display_str, NULL, NULL);
 +      return o;
 +}
 +
 +struct json_object *nvme_json_new_int128(uint8_t *v)
 +{
 +      struct json_object *o = nvme_json_new_str_len((const char *)v, 16);
 +      json_object_set_serializer(o, display_int128, NULL, NULL);
 +      return o;
 +}
 +
 +struct json_object *nvme_json_new_int64(uint64_t v)
 +{
 +      struct json_object *o = json_object_new_int64(v);
 +      fail_and_notify(o);
 +      return o;
 +}
 +
 +struct json_object *nvme_json_new_int(uint32_t v)
 +{
 +      struct json_object *o = json_object_new_int(v);
 +      fail_and_notify(o);
 +      return o;
 +}
 +
 +struct json_object *nvme_json_new_bool(bool v)
 +{
 +      struct json_object *o = json_object_new_boolean(v);
 +      fail_and_notify(o);
 +      return o;
 +}
 +
 +struct json_object *nvme_json_new_object(unsigned long flags)
 +{
 +      struct json_object *o = json_object_new_object();
 +      fail_and_notify(o);
 +
 +      if (flags & NVME_JSON_COMPACT) {
 +              if (flags & NVME_JSON_TABULAR)
 +                      json_object_set_serializer(o, display_compact_object, NULL, NULL);
 +              else
 +                      json_object_set_serializer(o, display_compact_object_str, NULL, NULL);
 +      } else if (flags & NVME_JSON_TABULAR)
 +              json_object_set_serializer(o, display_tabular, NULL, NULL);
 +
 +      return o;
 +}
 +
 +struct json_object *nvme_json_new_array()
 +{
 +      struct json_object *o = json_object_new_array();
 +      fail_and_notify(o);
 +      return o;
 +}
 +
 +struct json_object *nvme_json_new_storage_128(uint8_t *v, unsigned long flags)
 +{
 +      struct json_object *o = nvme_json_new_int128(v);
 +
 +      if (flags & NVME_JSON_TABULAR)
 +              json_object_set_serializer(o, __display_human_size128, NULL, NULL);
 +      else
 +              json_object_set_serializer(o, __display_human_size128_str, NULL, NULL);
 +      return o;
 +}
 +
 +struct json_object *nvme_json_new_storage(uint64_t v, unsigned long flags)
 +{
 +      struct json_object *o = nvme_json_new_int64(v);
 +      if (flags & NVME_JSON_TABULAR)
 +              json_object_set_serializer(o, __display_human_size, NULL, NULL);
 +      else
 +              json_object_set_serializer(o, __display_human_size_str, NULL, NULL);
 +      return o;
 +}
 +
 +struct json_object *nvme_json_new_size(uint64_t v, unsigned long flags)
 +{
 +      struct json_object *o = nvme_json_new_int64(v);
 +
 +      if (flags & NVME_JSON_TABULAR)
 +              json_object_set_serializer(o, _display_human_size, NULL, NULL);
 +      else
 +              json_object_set_serializer(o, _display_human_size_str, NULL, NULL);
 +      return o;
 +}
 +
 +struct json_object *nvme_json_new_memory(uint64_t v, unsigned long flags)
 +{
 +      struct json_object *o = nvme_json_new_int64(v);
 +
 +      if (flags & NVME_JSON_TABULAR)
 +              json_object_set_serializer(o, display_human_size, NULL, NULL);
 +      else
 +              json_object_set_serializer(o, display_human_size_str, NULL, NULL);
 +      return o;
 +}
 +
 +struct json_object *nvme_json_new_hex_array(uint8_t *v, uint32_t l)
 +{
 +      struct json_object *o = nvme_json_new_str_len((const char *)v, l);
 +      json_object_set_serializer(o, display_hex_array_str, NULL, NULL);
 +      return o;
 +}
 +
 +struct json_object *nvme_json_new_hex(uint64_t v, unsigned long flags)
 +{
 +      struct json_object *o = nvme_json_new_int64(v);
 +
 +      if (flags & NVME_JSON_TABULAR)
 +              json_object_set_serializer(o, display_hex, NULL, NULL);
 +      else
 +              json_object_set_serializer(o, display_hex_str, NULL, NULL);
 +      return o;
 +}
 +
 +struct json_object *nvme_json_new_0x(uint64_t v, unsigned long flags)
 +{
 +      struct json_object *o = nvme_json_new_int64(v);
 +
 +      if (v && (flags & NVME_JSON_HUMAN)) {
 +              if (flags & NVME_JSON_TABULAR)
 +                      json_object_set_serializer(o, display_0x, NULL, NULL);
 +              else
 +                      json_object_set_serializer(o, display_0x_str, NULL, NULL);
 +      }
 +      return o;
 +}
 +
 +struct json_object *nvme_json_new_percent(uint8_t v, unsigned long flags)
 +{
 +      struct json_object *o = nvme_json_new_int(v);
 +      if (flags & NVME_JSON_HUMAN) {
 +              if (flags & NVME_JSON_TABULAR)
 +                      json_object_set_serializer(o, display_percent, NULL, NULL);
 +              else
 +                      json_object_set_serializer(o, display_percent_str, NULL, NULL);
 +      }
 +      return o;
 +}
 +
 +struct json_object *nvme_json_new_temp(uint16_t v, unsigned long flags)
 +{
 +      struct json_object *o = nvme_json_new_int(v);
 +      if (flags & NVME_JSON_HUMAN) {
 +              if (flags & NVME_JSON_TABULAR)
 +                      json_object_set_serializer(o, display_temp_k, NULL, NULL);
 +              else
 +                      json_object_set_serializer(o, display_temp_k_str, NULL, NULL);
 +      }
 +      return o;
 +}
 +
 +struct json_object *nvme_json_new_time_us(uint64_t v, unsigned long flags)
 +{
 +      struct json_object *o = nvme_json_new_int64(v);
 +
 +      if (flags & NVME_JSON_TABULAR)
 +              json_object_set_serializer(o, display_time_us, NULL, NULL);
 +      else
 +              json_object_set_serializer(o, display_time_us_str, NULL, NULL);
 +      return o;
 +}
 +
 +struct json_object *nvme_json_new_time_s(uint64_t v, unsigned long flags)
 +{
 +      struct json_object *o = nvme_json_new_int64(v);
 +
 +      if (flags & NVME_JSON_TABULAR)
 +              json_object_set_serializer(o, display_time_s, NULL, NULL);
 +      else
 +              json_object_set_serializer(o, display_time_s_str, NULL, NULL);
 +      return o;
 +}
 +
 +struct json_object *nvme_json_new_hecto_uwatts(uint64_t v, unsigned long flags)
 +{
 +      struct json_object *o = nvme_json_new_int64(v);
 +      if (flags & NVME_JSON_TABULAR)
 +              json_object_set_serializer(o, display_hu_watts, NULL, NULL);
 +      else
 +              json_object_set_serializer(o, display_hu_watts_str, NULL, NULL);
 +      return o;
 +}
 +
 +struct json_object *nvme_json_new_uuid(uint8_t *v, unsigned long flags)
 +{
 +      struct json_object *o = nvme_json_new_str_len((const char *)v, 16);
 +
 +      if (flags & NVME_JSON_HUMAN) {
 +              if (flags & NVME_JSON_TABULAR)
 +                      json_object_set_serializer(o, display_uuid, NULL, NULL);
 +              else
 +                      json_object_set_serializer(o, display_uuid_str, NULL, NULL);
 +      } else {
 +              if (flags & NVME_JSON_TABULAR)
 +                      json_object_set_serializer(o, display_hex_array, NULL, NULL);
 +              else
 +                      json_object_set_serializer(o, display_hex_array_str, NULL, NULL);
 +      }
 +      return o;
 +}
 +
 +struct json_object *nvme_json_new_oui(uint8_t *v, int len, unsigned long flags)
 +{
 +      struct json_object *o = nvme_json_new_str_len((const char *)v, len);
 +      if (flags & NVME_JSON_HUMAN) {
 +              if (flags & NVME_JSON_TABULAR)
 +                      json_object_set_serializer(o, display_oui, NULL, NULL);
 +              else
 +                      json_object_set_serializer(o, display_oui_str, NULL, NULL);
 +      } else {
 +              if (flags & NVME_JSON_TABULAR)
 +                      json_object_set_serializer(o, display_hex_array, NULL, NULL);
 +              else
 +                      json_object_set_serializer(o, display_hex_array_str, NULL, NULL);
 +      }
 +      return o;
 +}
 +
 +struct json_object *nvme_json_new_bool_terse(bool v)
 +{
 +      struct json_object *o = nvme_json_new_bool(v);
 +      json_object_set_serializer(o, display_bool_terse, NULL, NULL);
 +      return o;
 +}
 +
 +struct json_object *nvme_identify_directives_to_json(
 +      struct nvme_id_directives *idd, unsigned long flags)
 +{
 +      struct json_object *jidd, *js, *je;
 +
 +      if (flags & NVME_JSON_BINARY)
 +              return nvme_json_new_str_len_flags(idd, sizeof(*idd), flags);
 +
 +      jidd = nvme_json_new_object(flags);
 +      js = nvme_json_new_object(flags);
 +      je = nvme_json_new_object(flags);
 +
 +      nvme_json_add_bool(js, "id", bit_set(idd->supported, NVME_ID_DIR_ID_BIT));
 +      nvme_json_add_bool(js, "sd", bit_set(idd->supported, NVME_ID_DIR_SD_BIT));
 +      json_object_object_add(jidd, "supported", js);
 +
 +      nvme_json_add_bool(je, "id", bit_set(idd->enabled, NVME_ID_DIR_ID_BIT));
 +      nvme_json_add_bool(je, "sd", bit_set(idd->enabled, NVME_ID_DIR_SD_BIT));
 +      json_object_object_add(jidd, "enabled", je);
 +
 +      return jidd;
 +}
 +
 +struct json_object *nvme_streams_status_to_json(
 +      struct nvme_streams_directive_status *sds, unsigned long flags)
 +{
 +      struct json_object *jsds, *jsids;
 +      int i, psid = -1, osc = le16_to_cpu(sds->osc);
 +
 +      if (flags & NVME_JSON_BINARY)
 +              return nvme_json_new_str_len_flags(sds, sizeof(*sds), flags);
 +
 +      jsds = nvme_json_new_object(flags);
 +      if (!jsds)
 +              return NULL;
 +
 +      jsids = json_object_new_array();
 +      if (!jsids) {
 +              json_object_put(jsds);
 +              return NULL;
 +      }
 +
 +      nvme_json_add_int(jsds, "osc", osc);
 +      for (i = 0; i < osc; i++) {
 +              struct json_object *jsid;
 +              __u16 sid;
 +
 +              sid = le16_to_cpu(sds->sid[i]);
 +              if ((int)sid <= psid)
 +                      break;
 +
 +              psid = sid;
 +              jsid = json_object_new_int(sid);
 +              if (!jsid)
 +                      break;
 +
 +              json_object_array_add(jsids, jsid);
 +      }
 +      json_object_object_add(jsds, "sids", jsids);
 +
 +      return jsds;
 +}
 +
 +struct json_object *nvme_streams_params_to_json(
 +      struct nvme_streams_directive_params *sdp, unsigned long flags)
 +{
 +      struct json_object *jsdp;
 +
 +      if (flags & NVME_JSON_BINARY)
 +              return nvme_json_new_str_len_flags(sdp, sizeof(*sdp), flags);
 +
 +      jsdp = nvme_json_new_object(flags);
 +      if (!jsdp)
 +              return NULL;
 +
 +      nvme_json_add_le16(jsdp, "msl", sdp->msl);
 +      nvme_json_add_le16(jsdp, "nssa", sdp->nssa);
 +      nvme_json_add_le16(jsdp, "nsso", sdp->nsso);
 +      nvme_json_add_int(jsdp, "nssc", sdp->nssc);
 +      nvme_json_add_le32(jsdp, "sws", sdp->sws);
 +      nvme_json_add_le16(jsdp, "sgs", sdp->sgs);
 +      nvme_json_add_le16(jsdp, "nsa", sdp->nsa);
 +      nvme_json_add_le16(jsdp, "nso", sdp->nso);
 +
 +      return jsdp;
 +}
 +
 +struct json_object *nvme_streams_allocated_to_json(__u16 nsa, unsigned long flags)
 +{
 +      if (flags & NVME_JSON_BINARY)
 +              return nvme_json_new_str_len_flags(&nsa, sizeof(nsa), flags);
 +      return nvme_json_new_int(nsa);
 +}
 +
 +static json_object *nvme_feat_simple_to_json(__u8 fid, __u32 value,
 +      unsigned long flags)
 +{
 +      struct json_object *jf;
 +
 +      jf = nvme_json_new_object(flags);
 +      nvme_json_add_int(jf, "feature-id", fid);
 +      nvme_json_add_str(jf, "feature", nvme_feature_str(fid), flags);
 +      nvme_json_add_int(jf, "value", value);
 +
 +      return jf;
 +}
 +
 +#if 0
 +static json_object *nvme_feat_arbitration_to_json(__u32 value,
 +      unsigned long flags)
 +{
 +      __u8 ab, lpw, mpw;
 +      struct json_object *jf;
 +
 +      jf = nvme_feat_simple_to_json(NVME_FEAT_FID_ARBITRATION, value, flags);
 +
 +      nvme_feature_decode_arbitration(value, &ab, &lpw, &mpw, &hpw)
 +      nvme_json_add_int(jf, "ab", ab);
 +      nvme_json_add_int(jf, "lpw", lpw);
 +      nvme_json_add_int(jf, "mpw", mpw);
 +      nvme_json_add_int(jf, "hpw", hpw);
 +
 +      return jf;
 +}
 +
 +static json_object *nvme_feat_power_mgmt_to_json(__u32 value,
 +      unsigned long flags)
 +{
 +      struct json_object *jf;
 +      __u8 ps, wh;
 +
 +      jf = nvme_feat_simple_to_json(NVME_FEAT_FID_POWER_MGMT, value, flags);
 +
 +      nvme_feature_decode_power_mgmt(value, &ps, &wh);
 +      nvme_json_add_int(jf, "ps", ps);
 +      nvme_json_add_int(jf, "wh", wh);
 +
 +      return jf;
 +}
 +
 +static json_object *nvme_lba_range_to_json(struct nvme_lba_range_type_entry *lbar,
 +      unsigned long flags)
 +{
 +      struct json_object *jlbar;
 +
 +      jlbar = nvme_json_new_object(flags);
 +
 +      nvme_json_add_int(jlbar, "type", lbar->type);
 +      nvme_json_add_int(jlbar, "attrs", lbar->attributes);
 +      nvme_json_add_le64(jlbar, "slba", lbar->slba);
 +      nvme_json_add_le64(jlbar, "nlb", lbar->nlb);
 +      nvme_json_add_hex_array(jlbar, "guid", (void *)lbar->guid, sizeof(lbar->guid));
 +
 +      return jlbar;
 +}
 +
 +static json_object *nvme_feat_lba_range_to_json(__u32 value,
 +      struct nvme_lba_range_type *lbar, unsigned long flags)
 +{
 +      struct json_object *jf, *jlbars;
 +      __u8 num;
 +      int i;
 +
 +      jf = nvme_feat_simple_to_json(NVME_FEAT_FID_LBA_RANGE, value, flags);
 +
 +      nvme_feature_decode_lba_range(value, &num);
 +      nvme_json_add_int(jf, "num", num);
 +
 +      jlbars = nvme_json_new_array();
 +      for (i = 0; i <= num; i++) {
 +              struct json_object *jlbar;
 +
 +              jlbar = nvme_lba_range_to_json(&lbar->entry[i], flags);
 +              if (!jlbar)
 +                      break;
 +
 +              json_object_array_add(jlbars, jlbar);
 +      }
 +      json_object_object_add(jf, "ranges", jlbars);
 +
 +      return jf;
 +}
 +
 +static json_object *nvme_feat_temp_thresh_to_json(__u32 value,
 +      unsigned long flags)
 +{
 +      struct json_object *jf;
 +      __u8 tmpsel, *thsel;
 +      __u16 tmpth;
 +
 +      jf = nvme_feat_simple_to_json(NVME_FEAT_FID_TEMP_THRESH, value, flags);
 +
 +      nvme_feature_decode_temp_threshold(value, &tmpth, &tmpsel, &thsel);
 +      nvme_json_add_int(jf, "tmpth", tmpth);
 +      nvme_json_add_int(jf, "tmpsel", tmpsel);
 +      nvme_json_add_int(jf, "thsel", thsel);
 +
 +      return jf;
 +}
 +
 +static json_object *nvme_feat_err_recovery_to_json(__u32 value,
 +      unsigned long flags)
 +{
 +      struct json_object *jf;
 +      __u16 tler;
 +      bool dulbe;
 +
 +      jf = nvme_feat_simple_to_json(NVME_FEAT_FID_ERR_RECOVERY, value, flags);
 +
 +      nvme_feature_decode_error_recovery(value, &tler, &dulbe);
 +      nvme_json_add_int(jf, "tler", tler);
 +      nvme_json_add_bool(jf, "dulbe", dulbe);
 +
 +      return jf;
 +}
 +
 +static json_object *nvme_feat_volatile_wc_to_json(__u32 value,
 +      unsigned long flags)
 +{
 +      struct json_object *jf;
 +      bool wce;
 +
 +      jf = nvme_feat_simple_to_json(NVME_FEAT_FID_VOLATILE_WC, value, flags);
 +
 +      nvme_feature_decode_volatile_write_cache(value, &wce);
 +      nvme_json_add_bool(jf, "wce", wce);
 +
 +      return jf;
 +}
 +
 +static json_object *nvme_feat_num_queues_to_json(__u32 value,
 +      unsigned long flags)
 +{
 +      struct json_object *jf;
 +
 +      jf = nvme_feat_simple_to_json(NVME_FEAT_FID_NUM_QUEUES, value, flags);
 +
 +      nvme_feature_decode_number_of_queues(value, &nsqr, &ncqr);
 +      nvme_json_add_int(jf, "nsqr", nsqr);
 +      nvme_json_add_int(jf, "ncqr", ncqr);
 +
 +      return jf;
 +}
 +
 +static json_object *nvme_feat_irq_coalesce_to_json(__u32 value,
 +      unsigned long flags)
 +{
 +      struct json_object *jf;
 +
 +      jf = nvme_feat_simple_to_json(NVME_FEAT_FID_IRQ_COALESCE, value, flags);
 +      nvme_json_add_int(jf, "thr", thr);
 +      nvme_json_add_int(jf, "time", time);
 +
 +      return jf;
 +}
 +
 +static json_object *nvme_feat_irq_config_to_json(__u32 value,
 +      unsigned long flags)
 +{
 +      struct json_object *jf;
 +
 +      jf = nvme_feat_simple_to_json(NVME_FEAT_FID_IRQ_CONFIG, value, flags);
 +      nvme_json_add_int(jf, "iv", iv);
 +      nvme_json_add_bool(jf, "cd", cd);
 +
 +      return jf;
 +}
 +
 +static json_object *nvme_feat_write_atomic_to_json(__u32 value,
 +      unsigned long flags)
 +{
 +      struct json_object *jf;
 +
 +      jf = nvme_feat_simple_to_json(NVME_FEAT_FID_WRITE_ATOMIC, value, flags);
 +      nvme_json_add_bool(jf, "dn", dn);
 +
 +      return jf;
 +}
 +
 +static json_object *nvme_feat_async_event_to_json(__u32 value,
 +      unsigned long flags)
 +{
 +      struct json_object *jf;
 +
 +      jf = nvme_feat_simple_to_json(NVME_FEAT_FID_ASYNC_EVENT, value, flags);
 +      nvme_json_add_int(jf, "smart", smart);
 +      nvme_json_add_bool(jf, "nan", nan);
 +      nvme_json_add_bool(jf, "fw", fw);
 +      nvme_json_add_bool(jf, "telem", telem);
 +      nvme_json_add_bool(jf, "ana", ana);
 +      nvme_json_add_bool(jf, "pla", pla);
 +      nvme_json_add_bool(jf, "lbas", lbas);
 +      nvme_json_add_bool(jf, "ega", ega);
 +
 +      return jf;
 +}
 +
 +static json_object *nvme_apst_to_json(__u64 value, unsigned long flags)
 +{
 +      struct json_object *japst;
 +
 +      japst = nvme_json_new_object(flags);
 +      nvme_json_add_int(japst, "itps", (value & NVME_APST_ENTRY_ITPS_MASK) >>
 +                                        NVME_APST_ENTRY_ITPS_SHIFT);
 +      nvme_json_add_int(japst, "itpt", (value & NVME_APST_ENTRY_ITPT_MASK) >>
 +                                        NVME_APST_ENTRY_ITPT_SHIFT);
 +
 +      return japst;
 +}
 +
 +static json_object *nvme_feat_auto_pst_to_json(__u32 value,
 +      struct nvme_feat_auto_pst *apst, unsigned long flags)
 +{
 +      struct json_object *jf, *japsts;
 +      int i;
 +
 +      jf = nvme_feat_simple_to_json(NVME_FEAT_FID_AUTO_PST, value, flags);
 +      nvme_json_add_bool(jf, "apste", apste);
 +
 +      japsts = nvme_json_new_array();
 +      for (i = 0; i < 32; i++) {
 +              struct json_object *japst;
 +
 +              japst = nvme_apst_to_json(le64_to_cpu(apst->apst_entry[i]), flags);
 +              if (!japst)
 +                      break;
 +
 +              json_object_array_add(japsts, japst);
 +      }
 +      json_object_object_add(jf, "entries", japsts);
 +
 +      return jf;
 +}
 +
 +static json_object *nvme_feat_host_mem_buf_to_json(__u32 value,
 +      unsigned long flags)
 +{
 +      struct json_object *jf;
 +
 +      jf = nvme_feat_simple_to_json(NVME_FEAT_FID_HOST_MEM_BUF, value, flags);
 +      nvme_json_add_bool(jf, "ehm", ehm);
 +
 +      return jf;
 +}
 +
 +static json_object *nvme_feat_timestamp_to_json(__u32 value,
 +      struct nvme_timestamp *ts, unsigned long flags)
 +{
 +      int timestamp = unalign_int(ts->timestamp, sizeof(ts->timestamp));
 +      struct json_object *jf, *jtimestamp;
 +
 +      jf = nvme_feat_simple_to_json(NVME_FEAT_FID_TIMESTAMP, value, flags);
 +      jtimestamp = nvme_json_new_object(flags);
 +
 +      nvme_json_add_int(jtimestamp, "timestamp", timestamp);
 +      nvme_json_add_int(jtimestamp, "sync", ts->attr & 1);
 +      nvme_json_add_int(jtimestamp, "origin", (ts->attr >> 1) & 0x7);
 +
 +      return jf;
 +}
 +
 +static json_object *nvme_feat_kato_to_json(__u32 value,
 +      unsigned long flags)
 +{
 +      struct json_object *jf;
 +
 +      jf = nvme_feat_simple_to_json(NVME_FEAT_FID_KATO, value, flags);
 +
 +      return jf;
 +}
 +
 +static json_object *nvme_feat_hctm_to_json(__u32 value,
 +      unsigned long flags)
 +{
 +      struct json_object *jf;
 +
 +      jf = nvme_feat_simple_to_json(NVME_FEAT_FID_HCTM, value, flags);
 +      nvme_json_add_int(jf, "tmt2", tmt2);
 +      nvme_json_add_int(jf, "tmt1", tmt1);
 +
 +      return jf;
 +}
 +
 +static json_object *nvme_feat_nopsc_to_json(__u32 value,
 +      unsigned long flags)
 +{
 +      struct json_object *jf;
 +
 +      jf = nvme_feat_simple_to_json(NVME_FEAT_FID_NOPSC, value, flags);
 +      nvme_json_add_bool(jf, "noppme", noppme);
 +
 +      return jf;
 +}
 +
 +static json_object *nvme_feat_rrl_to_json(__u32 value,
 +      unsigned long flags)
 +{
 +      struct json_object *jf;
 +
 +      jf = nvme_feat_simple_to_json(NVME_FEAT_FID_RRL, value, flags);
 +      nvme_json_add_int(jf, "rrl", rrl);
 +
 +      return jf;
 +}
 +
 +static json_object *nvme_feat_plm_config_to_json(__u32 value,
 +      struct nvme_plm_config *plm, unsigned long flags)
 +{
 +      struct json_object *jf, *jplm;
 +
 +      jplm = nvme_json_new_object(flags);
 +      nvme_json_add_le16(jplm, "ee", plm->ee);
 +      nvme_json_add_le64(jplm, "dtwinrt", plm->dtwinrt);
 +      nvme_json_add_le64(jplm, "dwtinwt", plm->dtwinwt);
 +      nvme_json_add_le64(jplm, "dwtintt", plm->dtwintt);
 +
 +      jf = nvme_feat_simple_to_json(NVME_FEAT_FID_PLM_CONFIG, value, flags);
 +      nvme_json_add_bool(jf, "plme", plme);
 +      json_object_object_add(jf, "plmcfg", jplm);
 +
 +      return jf;
 +}
 +
 +static json_object *nvme_feat_plm_window_to_json(__u32 value,
 +      unsigned long flags)
 +{
 +      struct json_object *jf;
 +
 +      jf = nvme_feat_simple_to_json(NVME_FEAT_FID_PLM_WINDOW, value, flags);
 +      nvme_json_add_int(jf, "ws", ws);
 +
 +      return jf;
 +}
 +
 +static json_object *nvme_feat_lba_sts_to_json(__u32 value,
 +      unsigned long flags)
 +{
 +      struct json_object *jf;
 +
 +      jf = nvme_feat_simple_to_json(NVME_FEAT_FID_LBA_STS_INTERVAL, value, flags);
 +      nvme_json_add_int(jf, "lsiri", lsiri);
 +      nvme_json_add_int(jf, "lsipi", lsipi);
 +
 +      return jf;
 +}
 +
 +static json_object *nvme_feat_host_behavior_to_json(__u32 value,
 +      struct nvme_feat_host_behavior *host, unsigned long flags)
 +{
 +      struct json_object *jf, *jhost;
 +
 +      jhost = nvme_json_new_object(flags);
 +      nvme_json_add_le16(jhost, "acre", host->acre);
 +
 +      jf = nvme_feat_simple_to_json(NVME_FEAT_FID_HOST_BEHAVIOR, value, flags);
 +      json_object_object_add(jf, "hbs", jhost);
 +
 +      return jf;
 +}
 +
 +static json_object *nvme_feat_sanitize_to_json(__u32 value,
 +      unsigned long flags)
 +{
 +      struct json_object *jf;
 +
 +      jf = nvme_feat_simple_to_json(NVME_FEAT_FID_SANITIZE, value, flags);
 +      nvme_json_add_bool(jf, "nodrm", nodrm);
 +
 +      return jf;
 +}
 +
 +static json_object *nvme_feat_endurance_evt_cfg_to_json(__u32 value,
 +      unsigned long flags)
 +{
 +      struct json_object *jf;
 +
 +      jf = nvme_feat_simple_to_json(NVME_FEAT_FID_ENDURANCE_EVT_CFG, value, flags);
 +      nvme_json_add_int(jf, "endgid", endgid);
 +      nvme_json_add_int(jf, "endgcw", endgcw);
 +
 +      return jf;
 +}
 +
 +static json_object *nvme_feat_sw_progress_to_json(__u32 value,
 +      unsigned long flags)
 +{
 +      struct json_object *jf;
 +
 +      jf = nvme_feat_simple_to_json(NVME_FEAT_FID_SW_PROGRESS, value, flags);
 +      nvme_json_add_int(jf, "pbslc", pbslc);
 +
 +      return jf;
 +}
 +
 +static json_object *nvme_feat_host_id_to_json(__u32 value,
 +      uint8_t *hostid, unsigned long flags)
 +{
 +      bool exhid = exhid );
 +      struct json_object *jf;
 +
 +      jf = nvme_feat_simple_to_json(NVME_FEAT_FID_HOST_ID, value, flags);
 +      nvme_json_add_bool(jf, "exhid", exhid);
 +
 +      if (exhid)
 +              nvme_json_add_int128(jf, "hostid", (void *)hostid);
 +      else
 +              nvme_json_add_int64(jf, "hostid", read64(hostid));
 +
 +      return jf;
 +}
 +
 +static json_object *nvme_feat_resv_mask_to_json(__u32 value,
 +      unsigned long flags)
 +{
 +      struct json_object *jf;
 +
 +      jf = nvme_feat_simple_to_json(NVME_FEAT_FID_RESV_MASK, value, flags);
 +      nvme_json_add_bool(jf, "regpre", regpre);
 +      nvme_json_add_bool(jf, "resrel", resrel);
 +      nvme_json_add_bool(jf, "respre", respre);
 +
 +      return jf;
 +}
 +
 +static json_object *nvme_feat_resv_persist_to_json(__u32 value,
 +      unsigned long flags)
 +{
 +      struct json_object *jf;
 +
 +      jf = nvme_feat_simple_to_json(NVME_FEAT_FID_RESV_PERSIST, value, flags);
 +      nvme_json_add_bool(jf, "ptpl", ptpl);
 +
 +      return jf;
 +}
 +
 +static json_object *nvme_feat_write_protect_to_json(__u32 value,
 +      unsigned long flags)
 +{
 +      struct json_object *jf;
 +
 +      jf = nvme_feat_simple_to_json(NVME_FEAT_FID_WRITE_PROTECT, value, flags);
 +      nvme_json_add_int(jf, "wps", wps);
 +
 +      return jf;
 +}
 +#endif
 +
 +struct json_object *nvme_feature_to_json(__u8 fid, __u32 value, unsigned len,
 +      void *data, unsigned long flags)
 +{
 +      if (!(flags & NVME_JSON_DECODE_COMPLEX))
 +              return nvme_feat_simple_to_json(fid, value, flags);
 +      return NULL;
 +#if 0
 +      switch (fid) {
 +      case NVME_FEAT_FID_ARBITRATION:
 +              return nvme_feat_arbitration_to_json(value, flags);
 +      case NVME_FEAT_FID_POWER_MGMT:
 +              return nvme_feat_power_mgmt_to_json(value, flags);
 +      case NVME_FEAT_FID_LBA_RANGE:
 +              return nvme_feat_lba_range_to_json(value, data, flags);
 +      case NVME_FEAT_FID_TEMP_THRESH:
 +              return nvme_feat_temp_thresh_to_json(value, flags);
 +      case NVME_FEAT_FID_ERR_RECOVERY:
 +              return nvme_feat_err_recovery_to_json(value, flags);
 +      case NVME_FEAT_FID_VOLATILE_WC:
 +              return nvme_feat_volatile_wc_to_json(value, flags);
 +      case NVME_FEAT_FID_NUM_QUEUES:
 +              return nvme_feat_num_queues_to_json(value, flags);
 +      case NVME_FEAT_FID_IRQ_COALESCE:
 +              return nvme_feat_irq_coalesce_to_json(value, flags);
 +      case NVME_FEAT_FID_IRQ_CONFIG:
 +              return nvme_feat_irq_config_to_json(value, flags);
 +      case NVME_FEAT_FID_WRITE_ATOMIC:
 +              return nvme_feat_write_atomic_to_json(value, flags);
 +      case NVME_FEAT_FID_ASYNC_EVENT:
 +              return nvme_feat_async_event_to_json(value, flags);
 +      case NVME_FEAT_FID_AUTO_PST:
 +              return nvme_feat_auto_pst_to_json(value, data, flags);
 +      case NVME_FEAT_FID_HOST_MEM_BUF:
 +              return nvme_feat_host_mem_buf_to_json(value, flags);
 +      case NVME_FEAT_FID_TIMESTAMP:
 +              return nvme_feat_timestamp_to_json(value, data, flags);
 +      case NVME_FEAT_FID_KATO:
 +              return nvme_feat_kato_to_json(value, flags);
 +      case NVME_FEAT_FID_HCTM:
 +              return nvme_feat_hctm_to_json(value, flags);
 +      case NVME_FEAT_FID_NOPSC:
 +              return nvme_feat_nopsc_to_json(value, flags);
 +      case NVME_FEAT_FID_RRL:
 +              return nvme_feat_rrl_to_json(value, flags);
 +      case NVME_FEAT_FID_PLM_CONFIG:
 +              return nvme_feat_plm_config_to_json(value, data, flags);
 +      case NVME_FEAT_FID_PLM_WINDOW:
 +              return nvme_feat_plm_window_to_json(value, flags);
 +      case NVME_FEAT_FID_LBA_STS_INTERVAL:
 +              return nvme_feat_lba_sts_to_json(value, flags);
 +      case NVME_FEAT_FID_HOST_BEHAVIOR:
 +              return nvme_feat_host_behavior_to_json(value, data, flags);
 +      case NVME_FEAT_FID_SANITIZE:
 +              return nvme_feat_sanitize_to_json(value, flags);
 +      case NVME_FEAT_FID_ENDURANCE_EVT_CFG:
 +              return nvme_feat_endurance_evt_cfg_to_json(value, flags);
 +      case NVME_FEAT_FID_SW_PROGRESS:
 +              return nvme_feat_sw_progress_to_json(value, flags);
 +      case NVME_FEAT_FID_HOST_ID:
 +              return nvme_feat_host_id_to_json(value, data, flags);
 +      case NVME_FEAT_FID_RESV_MASK:
 +              return nvme_feat_resv_mask_to_json(value, flags);
 +      case NVME_FEAT_FID_RESV_PERSIST:
 +              return nvme_feat_resv_persist_to_json(value, flags);
 +      case NVME_FEAT_FID_WRITE_PROTECT:
 +              return nvme_feat_write_protect_to_json(value, flags);
 +      default:
 +              return NULL;
 +      }
 +#endif
 +}
 +
 +static void nvme_json_add_id_ctrl_psd_human(struct json_object *j,
 +      struct nvme_id_psd *psd, unsigned long flags)
 +{
 +      struct json_object *jpsd = nvme_json_new_object(flags);
 +
 +      bool mxps = is_set(psd->flags, NVME_PSD_FLAGS_MXPS);
 +      uint8_t ips = nvme_psd_power_scale(psd->ips);
 +      uint8_t aps = nvme_psd_power_scale(psd->aps);
 +      uint16_t mp = le16_to_cpu(psd->mp);
 +      uint16_t idlp = le16_to_cpu(psd->idlp);
 +      uint16_t actp = le16_to_cpu(psd->actp);
 +
 +      switch (ips) {
 +      case 1:
 +              break;
 +      case 2: 
 +              idlp *= 100;
 +              break;
 +      default:
 +              idlp = 0;
 +              break;
 +      }
 +
 +      switch (aps) {
 +      case 1:
 +              break;
 +      case 2: 
 +              actp *= 100;
 +              break;
 +      default:
 +              actp = 0;
 +              break;
 +      }
 +
 +      if (!mxps)
 +              mp *= 100;
 +
 +      nvme_json_add_power(jpsd, "mp", mp, flags);
 +      nvme_json_add_bool(jpsd, "mxps", mxps);
 +      nvme_json_add_flag(jpsd, "nops", psd->flags, NVME_PSD_FLAGS_NOPS);
 +      nvme_json_add_time_us_flags(jpsd, "enlat", le32_to_cpu(psd->enlat), flags);
 +      nvme_json_add_time_us_flags(jpsd, "exlat", le32_to_cpu(psd->exlat), flags);
 +      nvme_json_add_int(jpsd, "rrt", psd->rrt);
 +      nvme_json_add_int(jpsd, "rrl", psd->rrl);
 +      nvme_json_add_int(jpsd, "rwt", psd->rwt);
 +      nvme_json_add_int(jpsd, "rwl", psd->rwl);
 +
 +      if (ips)
 +              nvme_json_add_power(jpsd, "idlp", idlp, flags);
 +      else
 +              nvme_json_add_bool(jpsd, "idlp", false);
 +      nvme_json_add_int(jpsd, "ips", ips);
 +
 +      if (aps)
 +              nvme_json_add_power(jpsd, "actp", actp, flags);
 +      else
 +              nvme_json_add_bool(jpsd, "actp", false);
 +      nvme_json_add_int(jpsd, "apw", psd->apw);
 +      nvme_json_add_int(jpsd, "aps", aps);
 +
 +      json_object_array_add(j, jpsd);
 +}
 +
 +static void nvme_id_ctrl_psd_to_json(struct json_object *j,
 +      struct nvme_id_psd *psd, unsigned long flags)
 +{
 +      struct json_object *jpsd = nvme_json_new_object(flags);
 +
 +      nvme_json_add_le16(jpsd, "mp", psd->mp);
 +      nvme_json_add_flag_flags(jpsd, "mxps", psd->flags, NVME_PSD_FLAGS_MXPS, flags);
 +      nvme_json_add_flag_flags(jpsd, "nops", psd->flags, NVME_PSD_FLAGS_NOPS, flags);
 +      nvme_json_add_le32(jpsd, "enlat", psd->enlat);
 +      nvme_json_add_le32(jpsd, "exlat", psd->exlat);
 +      nvme_json_add_int(jpsd, "rrt", psd->rrt);
 +      nvme_json_add_int(jpsd, "rrl", psd->rrl);
 +      nvme_json_add_int(jpsd, "rwt", psd->rwt);
 +      nvme_json_add_int(jpsd, "rwl", psd->rwl);
 +      nvme_json_add_le16(jpsd, "idlp", psd->idlp);
 +      nvme_json_add_int(jpsd, "ips", nvme_psd_power_scale(psd->ips));
 +      nvme_json_add_le16(jpsd, "actp", psd->actp);
 +      nvme_json_add_int(jpsd, "apw", psd->apw);
 +      nvme_json_add_int(jpsd, "aps", nvme_psd_power_scale(psd->aps));
 +
 +      json_object_array_add(j, jpsd);
 +}
 +
 +static void nvme_json_add_id_ctrl_psd(struct json_object *j,
 +      struct nvme_id_psd *psd, unsigned long flags)
 +{
 +      if (flags & NVME_JSON_HUMAN)
 +              return nvme_json_add_id_ctrl_psd_human(j, psd, flags);
 +      return nvme_id_ctrl_psd_to_json(j, psd, flags);
 +}
 +
 +static void nvme_json_add_id_ctrl_ieee(struct json_object *j, const char *n,
 +                                           __u8 *ieee, unsigned long flags)
 +{
 +      /*
 +       * See nvme specification 1.4 section 7.10.3 for why this byte swapping
 +       * is done.
 +       */
 +      uint8_t i[3] = { ieee[2], ieee[1], ieee[0] };
 +
 +      if (flags & NVME_JSON_HUMAN)
 +              nvme_json_add_oui(j, n, i, 3, flags);
 +      else
 +              nvme_json_add_int(j, n, nvme_ieee_to_int(ieee));
 +}
 +
 +static void nvme_json_add_id_ctrl_cmic(struct json_object *j, const char *n,
 +                                            __u8 cmic, unsigned long flags)
 +{
 +      struct json_object *jcmic;
 +
 +      if (!(flags & NVME_JSON_DECODE_COMPLEX)) {
 +              nvme_json_add_0x(j, n, cmic, flags);
 +              return;
 +      }
 +
 +      jcmic = nvme_json_new_object(flags);
 +
 +      nvme_json_add_0x(jcmic, "value", cmic, flags);
 +      nvme_json_add_flag_flags(jcmic, "mport", cmic, NVME_CTRL_CMIC_MULTI_PORT, flags);
 +      nvme_json_add_flag_flags(jcmic, "mctrl", cmic, NVME_CTRL_CMIC_MULTI_CTRL, flags);
 +      nvme_json_add_flag_flags(jcmic, "virtual", cmic, NVME_CTRL_CMIC_MULTI_SRIOV, flags);
 +      nvme_json_add_flag_flags(jcmic, "anarep", cmic, NVME_CTRL_CMIC_MULTI_ANA_REPORTING, flags);
 +
 +      json_object_object_add(j, n, jcmic);
 +}
 +
 +static void nvme_json_add_id_ctrl_mdts(struct json_object *j, const char *n,
 +                              __u8 mdts, unsigned long flags)
 +{
 +      uint64_t m = mdts ? (1ULL << (12ULL + mdts)) : 0;
 +
 +      if (!(flags & NVME_JSON_HUMAN))
 +              nvme_json_add_int(j, n, mdts);
 +      else if (m)
 +              nvme_json_add_memory(j, n, m, flags);
 +      else
 +              nvme_json_add_str(j, n, "No Limit", flags);
 +}
 +
 +static void nvme_json_add_id_ctrl_ver(struct json_object *j, const char *n,
 +                              __u32 ver, unsigned long flags)
 +{
 +      char buf[16];
 +
 +      if (!(flags & NVME_JSON_HUMAN)) {
 +              nvme_json_add_int(j, n, ver);
 +              return;
 +      }
 +
 +      if (NVME_TERTIARY(ver))
 +              sprintf(buf, "%u.%u.%u", NVME_MAJOR(ver), NVME_MINOR(ver),
 +                      NVME_TERTIARY(ver));
 +      else
 +              sprintf(buf, "%u.%u", NVME_MAJOR(ver), NVME_MINOR(ver));
 +      nvme_json_add_str(j, n, buf, flags);
 +}
 +
 +static void nvme_json_add_id_ctrl_oaes(struct json_object *j, const char *n,
 +                                    __u32 oaes, unsigned long flags)
 +{
 +      struct json_object *joaes;
 +
 +      if (!(flags & NVME_JSON_DECODE_COMPLEX)) {
 +              nvme_json_add_0x(j, n, oaes, flags);
 +              return;
 +      }
 +
 +      joaes = nvme_json_new_object(flags);
 +
 +      nvme_json_add_0x(joaes, "value", oaes, flags);
 +      nvme_json_add_flag_flags(joaes, "nsattr", oaes, NVME_CTRL_OAES_NA, flags);
 +      nvme_json_add_flag_flags(joaes, "frmwa", oaes, NVME_CTRL_OAES_FA, flags);
 +      nvme_json_add_flag_flags(joaes, "anachg", oaes, NVME_CTRL_OAES_ANA, flags);
 +      nvme_json_add_flag_flags(joaes, "ple", oaes, NVME_CTRL_OAES_PLEA, flags);
 +      nvme_json_add_flag_flags(joaes, "lbasts", oaes, NVME_CTRL_OAES_LBAS, flags);
 +      nvme_json_add_flag_flags(joaes, "ege", oaes, NVME_CTRL_OAES_EGE, flags);
 +
 +      json_object_object_add(j, n, joaes);
 +}
 +
 +static void nvme_json_add_id_ctrl_ctratt(struct json_object *j, const char *n,
 +                                    __u32 ctratt, unsigned long flags)
 +{
 +      struct json_object *jctratt;
 +
 +      if (!(flags & NVME_JSON_DECODE_COMPLEX)) {
 +              nvme_json_add_0x(j, n, ctratt, flags);
 +              return;
 +      }
 +
 +      jctratt = nvme_json_new_object(flags);
 +
 +      nvme_json_add_0x(jctratt, "value", ctratt, flags);
 +      nvme_json_add_flag_flags(jctratt, "hostid-128", ctratt, NVME_CTRL_CTRATT_128_ID, flags);
 +      nvme_json_add_flag_flags(jctratt, "nopspm", ctratt, NVME_CTRL_CTRATT_NON_OP_PSP, flags);
 +      nvme_json_add_flag_flags(jctratt, "nvmsets", ctratt, NVME_CTRL_CTRATT_NVM_SETS, flags);
 +      nvme_json_add_flag_flags(jctratt, "rrl", ctratt, NVME_CTRL_CTRATT_READ_RECV_LVLS, flags);
 +      nvme_json_add_flag_flags(jctratt, "eg", ctratt, NVME_CTRL_CTRATT_ENDURANCE_GROUPS, flags);
 +      nvme_json_add_flag_flags(jctratt, "plm", ctratt, NVME_CTRL_CTRATT_PREDICTABLE_LAT, flags);
 +      nvme_json_add_flag_flags(jctratt, "tbkas", ctratt, NVME_CTRL_CTRATT_TBKAS, flags);
 +      nvme_json_add_flag_flags(jctratt, "ng", ctratt, NVME_CTRL_CTRATT_NAMESPACE_GRANULARITY, flags);
 +      nvme_json_add_flag_flags(jctratt, "sqa", ctratt, NVME_CTRL_CTRATT_SQ_ASSOCIATIONS, flags);
 +      nvme_json_add_flag_flags(jctratt, "uuid", ctratt, NVME_CTRL_CTRATT_UUID_LIST, flags);
 +
 +      json_object_object_add(j, n, jctratt);
 +}
 +
 +static void nvme_json_add_id_ctrl_cntrltype(struct json_object *j, const char *n,
 +                                          __u8 cntrltype, unsigned long flags)
 +{
 +      const char *type;
 +
 +      if (!(flags & NVME_JSON_HUMAN)) {
 +              nvme_json_add_int(j, n, cntrltype);
 +              return;
 +      }
 +
 +      type = nvme_id_ctrl_cntrltype_str(cntrltype);
 +      nvme_json_add_str(j, n, type, flags);
 +}
 +static void nvme_json_add_id_ctrl_oacs(struct json_object *j, const char *n,
 +                              __u16 oacs, unsigned long flags)
 +{
 +      struct json_object *joacs;
 +
 +      if (!(flags & NVME_JSON_DECODE_COMPLEX)) {
 +              nvme_json_add_0x(j, n, oacs, flags);
 +              return;
 +      }
 +
 +      joacs = nvme_json_new_object(flags);
 +
 +      nvme_json_add_0x(joacs, "value", oacs, flags);
 +      nvme_json_add_flag_flags(joacs, "security", oacs, NVME_CTRL_OACS_SECURITY, flags);
 +      nvme_json_add_flag_flags(joacs, "format-nvm", oacs, NVME_CTRL_OACS_FORMAT, flags);
 +      nvme_json_add_flag_flags(joacs, "firmware", oacs, NVME_CTRL_OACS_FW, flags);
 +      nvme_json_add_flag_flags(joacs, "ns-mgmt", oacs, NVME_CTRL_OACS_NS_MGMT, flags);
 +      nvme_json_add_flag_flags(joacs, "dev-self-test", oacs, NVME_CTRL_OACS_SELF_TEST, flags);
 +      nvme_json_add_flag_flags(joacs, "directives", oacs, NVME_CTRL_OACS_DIRECTIVES, flags);
 +      nvme_json_add_flag_flags(joacs, "nvme-mi", oacs, NVME_CTRL_OACS_NVME_MI, flags);
 +      nvme_json_add_flag_flags(joacs, "virt-mgmt", oacs, NVME_CTRL_OACS_VIRT_MGMT, flags);
 +      nvme_json_add_flag_flags(joacs, "doorbell-buf-cfg", oacs, NVME_CTRL_OACS_DBBUF_CFG, flags);
 +      nvme_json_add_flag_flags(joacs, "get-lba-status", oacs, NVME_CTRL_OACS_LBA_STATUS, flags);
 +
 +      json_object_object_add(j, n, joacs);
 +}
 +
 +static void nvme_json_add_id_ctrl_frmw(struct json_object *j, const char *n,
 +                              __u8 frmw, unsigned long flags)
 +{
 +      struct json_object *jfrmw;
 +
 +      if (!(flags & NVME_JSON_DECODE_COMPLEX)) {
 +              nvme_json_add_0x(j, n, frmw, flags);
 +              return;
 +      }
 +
 +      jfrmw = nvme_json_new_object(flags);
 +
 +      nvme_json_add_0x(jfrmw, "value", frmw, flags);
 +      nvme_json_add_flag_flags(jfrmw, "first-slot-ro", frmw, NVME_CTRL_FRMW_1ST_RO, flags);
 +      nvme_json_add_int(jfrmw, "num-slots", (frmw & NVME_CTRL_FRMW_NR_SLOTS) >> 1);
 +      nvme_json_add_flag_flags(jfrmw, "activate-no-reset", frmw, NVME_CTRL_FRMW_FW_ACT_NO_RESET, flags);
 +
 +      json_object_object_add(j, n, jfrmw);
 +}
 +
 +static void nvme_json_add_id_ctrl_lpa(struct json_object *j, const char *n,
 +                              __u8 lpa, unsigned long flags)
 +{
 +      struct json_object *jlpa;
 +
 +      if (!(flags & NVME_JSON_DECODE_COMPLEX)) {
 +              nvme_json_add_0x(j, n, lpa, flags);
 +              return;
 +      }
 +
 +      jlpa = nvme_json_new_object(flags);
 +
 +      nvme_json_add_0x(jlpa, "value", lpa, flags);
 +      nvme_json_add_flag_flags(jlpa, "smart-per-namespace", lpa, NVME_CTRL_LPA_SMART_PER_NS, flags);
 +      nvme_json_add_flag_flags(jlpa, "command-effects", lpa, NVME_CTRL_LPA_CMD_EFFECTS, flags);
 +      nvme_json_add_flag_flags(jlpa, "extended", lpa, NVME_CTRL_LPA_EXTENDED, flags);
 +      nvme_json_add_flag_flags(jlpa, "telemetry", lpa, NVME_CTRL_LPA_TELEMETRY, flags);
 +      nvme_json_add_flag_flags(jlpa, "persistent-event", lpa, NVME_CTRL_LPA_PERSETENT_EVENT, flags);
 +
 +      json_object_object_add(j, n, jlpa);
 +}
 +
 +static void nvme_json_add_id_ctrl_avscc(struct json_object *j, const char *n,
 +                              __u8 avscc, unsigned long flags)
 +{
 +      struct json_object *javscc;
 +
 +      if (!(flags & NVME_JSON_DECODE_COMPLEX)) {
 +              nvme_json_add_0x(j, n, avscc, flags);
 +              return;
 +      }
 +
 +      javscc = nvme_json_new_object(flags);
 +
 +      nvme_json_add_0x(javscc, "value", avscc, flags);
 +      nvme_json_add_flag_flags(javscc, "avs-format", avscc, NVME_CTRL_AVSCC_AVS, flags);
 +
 +      json_object_object_add(j, n, javscc);
 +}
 +
 +static void nvme_json_add_id_ctrl_apsta(struct json_object *j, const char *n,
 +                              __u8 apsta, unsigned long flags)
 +{
 +      struct json_object *japsta;
 +
 +      if (!(flags & NVME_JSON_DECODE_COMPLEX)) {
 +              nvme_json_add_0x(j, n, apsta, flags);
 +              return;
 +      }
 +
 +      japsta = nvme_json_new_object(flags);
 +
 +      nvme_json_add_0x(japsta, "value", apsta, flags);
 +      nvme_json_add_flag_flags(japsta, "apst", apsta, NVME_CTRL_APSTA_APST, flags);
 +
 +      json_object_object_add(j, n, japsta);
 +}
 +
 +static void nvme_json_add_id_ctrl_4k_mem(struct json_object *j, const char *n,
 +                              __u32 size, unsigned long flags)
 +{
 +      uint64_t m = size * 4096;
 +      if (flags & NVME_JSON_HUMAN)
 +              nvme_json_add_memory(j, n, m, flags);
 +      else
 +              nvme_json_add_int(j, n, size);
 +}
 +
 +static void nvme_json_add_id_ctrl_64k_mem(struct json_object *j, const char *n,
 +                              __u32 size, unsigned long flags)
 +{
 +      uint64_t m = size * 64 * 1024ULL;
 +      if (flags & NVME_JSON_HUMAN)
 +              nvme_json_add_memory(j, n, m, flags);
 +      else
 +              nvme_json_add_int(j, n, size);
 +}
 +
 +static void nvme_json_add_id_ctrl_16_mem(struct json_object *j, const char *n,
 +                              __u32 size, unsigned long flags)
 +{
 +      uint64_t m = size * 16;
 +      if (flags & NVME_JSON_HUMAN)
 +              nvme_json_add_memory(j, n, m, flags);
 +      else
 +              nvme_json_add_int(j, n, size);
 +}
 +
 +static void nvme_json_add_id_ctrl_rpmbs(struct json_object *j, const char *n,
 +                              __u32 rpmbs, unsigned long flags)
 +{
 +      struct json_object *jrpmbs;
 +
 +      if (!(flags & NVME_JSON_DECODE_COMPLEX)) {
 +              nvme_json_add_0x(j, n, rpmbs, flags);
 +              return;
 +      }
 +
 +      jrpmbs = nvme_json_new_object(flags);
 +
 +      nvme_json_add_0x(jrpmbs, "value", rpmbs, flags);
 +      nvme_json_add_int(jrpmbs, "number-of-units", rpmbs & NVME_CTRL_RPMBS_NR_UNITS);
 +      nvme_json_add_int(jrpmbs, "authentication-method", (rpmbs & NVME_CTRL_RPMBS_AUTH_METHOD) >> 3);
 +      nvme_json_add_int(jrpmbs, "total-size", (rpmbs & NVME_CTRL_RPMBS_TOTAL_SIZE) >> 16);
 +      nvme_json_add_int(jrpmbs, "access-size", (rpmbs & NVME_CTRL_RPMBS_ACCESS_SIZE) >> 24);
 +
 +      json_object_object_add(j, n, jrpmbs);
 +}
 +
 +static void nvme_json_add_id_ctrl_dsto(struct json_object *j, const char *n,
 +                              __u8 dsto, unsigned long flags)
 +{
 +      struct json_object *jdsto;
 +
 +      if (!(flags & NVME_JSON_DECODE_COMPLEX)) {
 +              nvme_json_add_0x(j, n, dsto, flags);
 +              return;
 +      }
 +
 +      jdsto = nvme_json_new_object(flags);
 +
 +      nvme_json_add_0x(jdsto, "value", dsto, flags);
 +      nvme_json_add_flag_flags(jdsto, "one-dst", dsto, NVME_CTRL_DSTO_ONE_DST, flags);
 +
 +      json_object_object_add(j, n, jdsto);
 +}
 +
 +static void nvme_json_add_id_ctrl_hctma(struct json_object *j, const char *n,
 +                              __u32 hctma, unsigned long flags)
 +{
 +      struct json_object *jhctma;
 +
 +      if (!(flags & NVME_JSON_DECODE_COMPLEX)) {
 +              nvme_json_add_0x(j, n, hctma, flags);
 +              return;
 +      }
 +
 +      jhctma = nvme_json_new_object(flags);
 +
 +      nvme_json_add_0x(jhctma, "value", hctma, flags);
 +      nvme_json_add_flag_flags(jhctma, "hctm", hctma, NVME_CTRL_HCTMA_HCTM, flags);
 +
 +      json_object_object_add(j, n, jhctma);
 +}
 +
 +static void nvme_json_add_id_ctrl_sanicap(struct json_object *j, const char *n,
 +                              __u32 sanicap, unsigned long flags)
 +{
 +      struct json_object *jsanicap;
 +
 +      if (!(flags & NVME_JSON_DECODE_COMPLEX)) {
 +              nvme_json_add_0x(j, n, sanicap, flags);
 +              return;
 +      }
 +
 +      jsanicap = nvme_json_new_object(flags);
 +
 +      nvme_json_add_0x(jsanicap, "value", sanicap, flags);
 +      nvme_json_add_flag_flags(jsanicap, "ces", sanicap, NVME_CTRL_SANICAP_CES, flags);
 +      nvme_json_add_flag_flags(jsanicap, "bes", sanicap, NVME_CTRL_SANICAP_BES, flags);
 +      nvme_json_add_flag_flags(jsanicap, "ows", sanicap, NVME_CTRL_SANICAP_OWS, flags);
 +      nvme_json_add_flag_flags(jsanicap, "ndi", sanicap, NVME_CTRL_SANICAP_NDI, flags);
 +      nvme_json_add_int(jsanicap, "nodmmas", (sanicap & NVME_CTRL_SANICAP_NODMMAS) >> 30);
 +
 +      json_object_object_add(j, n, jsanicap);
 +}
 +
 +static void nvme_json_add_id_ctrl_anacap(struct json_object *j, const char *n,
 +                              __u8 anacap, unsigned long flags)
 +{
 +      struct json_object *janacap;
 +
 +      if (!(flags & NVME_JSON_DECODE_COMPLEX)) {
 +              nvme_json_add_0x(j, n, anacap, flags);
 +              return;
 +      }
 +
 +      janacap = nvme_json_new_object(flags);
 +
 +      nvme_json_add_0x(janacap, "value", anacap, flags);
 +      nvme_json_add_flag_flags(janacap, "optimal", anacap, NVME_CTRL_ANACAP_OPT, flags);
 +      nvme_json_add_flag_flags(janacap, "non-optimal", anacap, NVME_CTRL_ANACAP_NON_OPT, flags);
 +      nvme_json_add_flag_flags(janacap, "inaccessible", anacap, NVME_CTRL_ANACAP_INACCESSIBLE, flags);
 +      nvme_json_add_flag_flags(janacap, "persistent-loss", anacap, NVME_CTRL_ANACAP_PERSISTENT_LOSS, flags);
 +      nvme_json_add_flag_flags(janacap, "change", anacap, NVME_CTRL_ANACAP_CHANGE, flags);
 +      nvme_json_add_flag_flags(janacap, "grpid-not-changable", anacap, NVME_CTRL_ANACAP_GRPID_NO_CHG, flags);
 +      nvme_json_add_flag_flags(janacap, "grpid-ns-mgmt", anacap, NVME_CTRL_ANACAP_GRPID_MGMT, flags);
 +
 +      json_object_object_add(j, n, janacap);
 +}
 +
 +static void nvme_json_add_id_ctrl_sqes(struct json_object *j, const char *n,
 +                              __u8 sqes, unsigned long flags)
 +{
 +      struct json_object *jsqes;
 +
 +      if (!(flags & NVME_JSON_DECODE_COMPLEX)) {
 +              nvme_json_add_0x(j, n, sqes, flags);
 +              return;
 +      }
 +
 +      jsqes = nvme_json_new_object(flags);
 +
 +      nvme_json_add_0x(jsqes, "value", sqes, flags);
 +      nvme_json_add_int(jsqes, "sqes-min", sqes & NVME_CTRL_SQES_MIN);
 +      nvme_json_add_int(jsqes, "sqes-max", (sqes & NVME_CTRL_SQES_MAX) >> 4);
 +
 +      json_object_object_add(j, n, jsqes);
 +}
 +
 +static void nvme_json_add_id_ctrl_cqes(struct json_object *j, const char *n,
 +                              __u8 cqes, unsigned long flags)
 +{
 +      struct json_object *jcqes;
 +
 +      if (!(flags & NVME_JSON_DECODE_COMPLEX)) {
 +              nvme_json_add_0x(j, n, cqes, flags);
 +              return;
 +      }
 +
 +      jcqes = nvme_json_new_object(flags);
 +
 +      nvme_json_add_0x(jcqes, "value", cqes, flags);
 +      nvme_json_add_int(jcqes, "cqes-min", cqes & NVME_CTRL_CQES_MIN);
 +      nvme_json_add_int(jcqes, "cqes-max", (cqes & NVME_CTRL_CQES_MAX) >> 4);
 +
 +      json_object_object_add(j, n, jcqes);
 +}
 +
 +static void nvme_json_add_id_ctrl_oncs(struct json_object *j, const char *n,
 +                              __u16 oncs, unsigned long flags)
 +{
 +      struct json_object *joncs;
 +
 +      if (!(flags & NVME_JSON_DECODE_COMPLEX)) {
 +              nvme_json_add_0x(j, n, oncs, flags);
 +              return;
 +      }
 +
 +      joncs = nvme_json_new_object(flags);
 +
 +      nvme_json_add_0x(joncs, "value", oncs, flags);
 +      nvme_json_add_flag_flags(joncs, "compare", oncs, NVME_CTRL_ONCS_COMPARE, flags);
 +      nvme_json_add_flag_flags(joncs, "write-uncorrectable", oncs, NVME_CTRL_ONCS_WRITE_UNCORRECTABLE, flags);
 +      nvme_json_add_flag_flags(joncs, "data-set-mgmt", oncs, NVME_CTRL_ONCS_DSM, flags);
 +      nvme_json_add_flag_flags(joncs, "write-zeroes", oncs, NVME_CTRL_ONCS_WRITE_ZEROES, flags);
 +      nvme_json_add_flag_flags(joncs, "save-features", oncs, NVME_CTRL_ONCS_SAVE_FEATURES, flags);
 +      nvme_json_add_flag_flags(joncs, "reservations", oncs, NVME_CTRL_ONCS_RESERVATIONS, flags);
 +      nvme_json_add_flag_flags(joncs, "timestamp", oncs, NVME_CTRL_ONCS_TIMESTAMP, flags);
 +      nvme_json_add_flag_flags(joncs, "verify", oncs, NVME_CTRL_ONCS_VERIFY, flags);
 +
 +      json_object_object_add(j, n, joncs);
 +}
 +
 +static void nvme_json_add_id_ctrl_fuses(struct json_object *j, const char *n,
 +                              __u16 fuses, unsigned long flags)
 +{
 +      struct json_object *jfuses;
 +
 +      if (!(flags & NVME_JSON_DECODE_COMPLEX)) {
 +              nvme_json_add_0x(j, n, fuses, flags);
 +              return;
 +      }
 +
 +      jfuses = nvme_json_new_object(flags);
 +
 +      nvme_json_add_0x(jfuses, "value", fuses, flags);
 +      nvme_json_add_flag_flags(jfuses, "compare", fuses, NVME_CTRL_FUSES_COMPARE_AND_WRITE, flags);
 +
 +      json_object_object_add(j, n, jfuses);
 +}
 +
 +static void nvme_json_add_id_ctrl_fna(struct json_object *j, const char *n,
 +                              __u8 fna, unsigned long flags)
 +{
 +      struct json_object *jfna;
 +
 +      if (!(flags & NVME_JSON_DECODE_COMPLEX)) {
 +              nvme_json_add_0x(j, n, fna, flags);
 +              return;
 +      }
 +
 +      jfna = nvme_json_new_object(flags);
 +
 +      nvme_json_add_0x(jfna, "value", fna, flags);
 +      nvme_json_add_flag_flags(jfna, "format-all-ns", fna, NVME_CTRL_FNA_FMT_ALL_NAMESPACES, flags);
 +      nvme_json_add_flag_flags(jfna, "secure-erase-all-ns", fna, NVME_CTRL_FNA_SEC_ALL_NAMESPACES, flags);
 +      nvme_json_add_flag_flags(jfna, "crypto-erasens", fna, NVME_CTRL_FNA_CRYPTO_ERASE, flags);
 +
 +      json_object_object_add(j, n, jfna);
 +}
 +
 +static void nvme_json_add_id_ctrl_vwc(struct json_object *j, const char *n,
 +                              __u8 vwc, unsigned long flags)
 +{
 +      struct json_object *jvwc;
 +
 +      if (!(flags & NVME_JSON_DECODE_COMPLEX)) {
 +              nvme_json_add_0x(j, n, vwc, flags);
 +              return;
 +      }
 +
 +      jvwc = nvme_json_new_object(flags);
 +
 +      nvme_json_add_0x(jvwc, "value", vwc, flags);
 +      nvme_json_add_flag_flags(jvwc, "present", vwc, NVME_CTRL_VWC_PRESENT, flags);
 +      nvme_json_add_flag_flags(jvwc, "flush-behavior", vwc, NVME_CTRL_VWC_FLUSH, flags);
 +
 +      json_object_object_add(j, n, jvwc);
 +}
 +
 +static void nvme_json_add_id_ctrl_nvscc(struct json_object *j, const char *n,
 +                              __u8 nvscc, unsigned long flags)
 +{
 +      struct json_object *jnvscc;
 +
 +      if (!(flags & NVME_JSON_DECODE_COMPLEX)) {
 +              nvme_json_add_0x(j, n, nvscc, flags);
 +              return;
 +      }
 +
 +      jnvscc = nvme_json_new_object(flags);
 +
 +      nvme_json_add_0x(jnvscc, "value", nvscc, flags);
 +      nvme_json_add_flag_flags(jnvscc, "nvmvs-format", nvscc, NVME_CTRL_NVSCC_FMT, flags);
 +
 +      json_object_object_add(j, n, jnvscc);
 +}
 +
 +static void nvme_json_add_id_ctrl_nwpc(struct json_object *j, const char *n,
 +                              __u8 nwpc, unsigned long flags)
 +{
 +      struct json_object *jnwpc;
 +
 +      if (!(flags & NVME_JSON_DECODE_COMPLEX)) {
 +              nvme_json_add_0x(j, n, nwpc, flags);
 +              return;
 +      }
 +
 +      jnwpc = nvme_json_new_object(flags);
 +
 +      nvme_json_add_0x(jnwpc, "value", nwpc, flags);
 +      nvme_json_add_flag_flags(jnwpc, "wp", nwpc, NVME_CTRL_NWPC_WRITE_PROTECT, flags);
 +      nvme_json_add_flag_flags(jnwpc, "wpupc", nwpc, NVME_CTRL_NWPC_WRITE_PROTECT_POWER_CYCLE, flags);
 +      nvme_json_add_flag_flags(jnwpc, "pwp", nwpc, NVME_CTRL_NWPC_WRITE_PROTECT_PERMANENT, flags);
 +
 +      json_object_object_add(j, n, jnwpc);
 +}
 +
 +static void nvme_json_add_id_ctrl_sgls(struct json_object *j, const char *n,
 +                              __u32 sgls, unsigned long flags)
 +{
 +      struct json_object *jsgls;
 +
 +      if (!(flags & NVME_JSON_DECODE_COMPLEX)) {
 +              nvme_json_add_0x(j, n, sgls, flags);
 +              return;
 +      }
 +
 +      jsgls = nvme_json_new_object(flags);
 +
 +      nvme_json_add_0x(jsgls, "value", sgls, flags);
 +      nvme_json_add_int(jsgls, "supports", sgls & NVME_CTRL_SGLS_SUPPORTED);
 +      nvme_json_add_flag_flags(jsgls, "keyed", sgls, NVME_CTRL_SGLS_KEYED, flags);
 +      nvme_json_add_flag_flags(jsgls, "bitbucket", sgls, NVME_CTRL_SGLS_BIT_BUCKET, flags);
 +      nvme_json_add_flag_flags(jsgls, "aligned", sgls, NVME_CTRL_SGLS_MPTR_BYTE_ALIGNED, flags);
 +      nvme_json_add_flag_flags(jsgls, "oversize", sgls, NVME_CTRL_SGLS_OVERSIZE, flags);
 +      nvme_json_add_flag_flags(jsgls, "mptrsgl", sgls, NVME_CTRL_SGLS_MPTR_SGL, flags);
 +      nvme_json_add_flag_flags(jsgls, "offets", sgls, NVME_CTRL_SGLS_OFFSET, flags);
 +      nvme_json_add_flag_flags(jsgls, "tportdesc", sgls, NVME_CTRL_SGLS_TPORT, flags);
 +
 +      json_object_object_add(j, n, jsgls);
 +}
 +
 +static void nvme_json_add_id_ctrl_fcatt(struct json_object *j, const char *n,
 +                              __u8 fcatt, unsigned long flags)
 +{
 +      struct json_object *jfcatt;
 +
 +      if (!(flags & NVME_JSON_DECODE_COMPLEX)) {
 +              nvme_json_add_0x(j, n, fcatt, flags);
 +              return;
 +      }
 +
 +      jfcatt = nvme_json_new_object(flags);
 +
 +      nvme_json_add_0x(jfcatt, "value", fcatt, flags);
 +      nvme_json_add_flag_flags(jfcatt, "dynamic-subsystem", fcatt, NVME_CTRL_FCATT_DYNAMIC, flags);
 +
 +      json_object_object_add(j, n, jfcatt);
 +}
 +
 +static void nvme_json_add_id_ctrl_ofcs(struct json_object *j, const char *n,
 +                              __u8 ofcs, unsigned long flags)
 +{
 +      struct json_object *jofcs;
 +
 +      if (!(flags & NVME_JSON_DECODE_COMPLEX)) {
 +              nvme_json_add_0x(j, n, ofcs, flags);
 +              return;
 +      }
 +
 +      jofcs = nvme_json_new_object(flags);
 +
 +      nvme_json_add_0x(jofcs, "value", ofcs, flags);
 +      nvme_json_add_flag_flags(jofcs, "disconnect-support", ofcs, NVME_CTRL_OFCS_DISCONNECT, flags);
 +
 +      json_object_object_add(j, n, jofcs);
 +}
 +
 +struct json_object *nvme_id_ctrl_to_json(
 +      struct nvme_id_ctrl *id, unsigned long flags)
 +{
 +      struct json_object *jctrl, *jpsds;
 +      int i;
 +
 +      if (flags & NVME_JSON_BINARY)
 +              return nvme_json_new_str_len_flags(id, sizeof(*id), flags);
 +
 +      jctrl = nvme_json_new_object(flags);
 +
 +      nvme_json_add_hex_le16_flags(jctrl, "vid", id->vid, flags);
 +      nvme_json_add_hex_le16_flags(jctrl, "ssvid", id->ssvid, flags);
 +      nvme_json_add_str_flags(jctrl, "sn", id->sn, sizeof(id->sn), flags);
 +      nvme_json_add_str_flags(jctrl, "mn", id->mn, sizeof(id->mn), flags);
 +      nvme_json_add_str_flags(jctrl, "fr", id->fr, sizeof(id->fr), flags);
 +      nvme_json_add_int(jctrl, "rab", id->rab);
 +      nvme_json_add_id_ctrl_ieee(jctrl, "ieee", id->ieee, flags);
 +      nvme_json_add_id_ctrl_cmic(jctrl, "cmic", id->cmic, flags);
 +      nvme_json_add_id_ctrl_mdts(jctrl, "mdts", id->mdts, flags);
 +      nvme_json_add_le16(jctrl, "cntlid", id->cntlid);
 +      nvme_json_add_id_ctrl_ver(jctrl, "ver", le32_to_cpu(id->ver), flags);
 +      nvme_json_add_time_us_flags(jctrl, "rtd3r", le32_to_cpu(id->rtd3r), flags);
 +      nvme_json_add_time_us_flags(jctrl, "rtd3e", le32_to_cpu(id->rtd3e), flags);
 +      nvme_json_add_id_ctrl_oaes(jctrl, "oaes", le32_to_cpu(id->oaes), flags);
 +      nvme_json_add_id_ctrl_ctratt(jctrl, "ctratt", le32_to_cpu(id->ctratt), flags);
 +      nvme_json_add_0x(jctrl, "rrls", le16_to_cpu(id->rrls), flags);
 +      nvme_json_add_id_ctrl_cntrltype(jctrl, "cntrltype", id->cntrltype, flags);
 +      nvme_json_add_uuid(jctrl, "fguid", id->fguid, flags);
 +      nvme_json_add_time_100us_flags(jctrl, "crdt1", le16_to_cpu(id->crdt1), flags);
 +      nvme_json_add_time_100us_flags(jctrl, "crdt2", le16_to_cpu(id->crdt2), flags);
 +      nvme_json_add_time_100us_flags(jctrl, "crdt3", le16_to_cpu(id->crdt3), flags);
 +      nvme_json_add_0x(jctrl, "nvmsr", id->nvmsr, flags);
 +      nvme_json_add_0x(jctrl, "vwci", id->vwci, flags);
 +      nvme_json_add_0x(jctrl, "mec", id->mec, flags);
 +      nvme_json_add_id_ctrl_oacs(jctrl, "oacs", le16_to_cpu(id->oacs), flags);
 +      nvme_json_add_int(jctrl, "acl", id->acl);
 +      nvme_json_add_int(jctrl, "aerl", id->aerl);
 +      nvme_json_add_id_ctrl_frmw(jctrl, "frmw", id->frmw, flags);
 +      nvme_json_add_id_ctrl_lpa(jctrl, "lpa", id->lpa, flags);
 +      nvme_json_add_int(jctrl, "elpe", id->elpe);
 +      nvme_json_add_int(jctrl, "npss", id->npss);
 +      nvme_json_add_id_ctrl_avscc(jctrl, "avscc", id->avscc, flags);
 +      nvme_json_add_id_ctrl_apsta(jctrl, "apsta", id->apsta, flags);
 +      nvme_json_add_temp(jctrl, "wctemp", id->wctemp, flags);
 +      nvme_json_add_temp(jctrl, "cctemp", id->cctemp, flags);
 +      nvme_json_add_time_100us_flags(jctrl, "mtfa", le32_to_cpu(id->mtfa), flags);
 +      nvme_json_add_id_ctrl_4k_mem(jctrl, "hmpre",  le32_to_cpu(id->hmpre), flags);
 +      nvme_json_add_id_ctrl_4k_mem(jctrl, "hmmin",  le32_to_cpu(id->hmmin), flags);
 +      nvme_json_add_storage_128_flags(jctrl, "tnvmcap", id->tnvmcap, flags);
 +      nvme_json_add_storage_128_flags(jctrl, "unvmcap", id->unvmcap, flags);
 +      nvme_json_add_id_ctrl_rpmbs(jctrl, "rpmbs",  le32_to_cpu(id->rpmbs), flags);
 +      nvme_json_add_time_m_flags(jctrl, "edstt", le16_to_cpu(id->edstt), flags);
 +      nvme_json_add_id_ctrl_dsto(jctrl, "dsto", id->dsto, flags);
 +      nvme_json_add_id_ctrl_4k_mem(jctrl, "fwug", id->fwug, flags);
 +      nvme_json_add_time_100us_flags(jctrl, "kas", le16_to_cpu(id->kas), flags);
 +      nvme_json_add_id_ctrl_hctma(jctrl, "hctma", le16_to_cpu(id->hctma), flags);
 +      nvme_json_add_temp(jctrl, "mntmt", le16_to_cpu(id->mntmt), flags);
 +      nvme_json_add_temp(jctrl, "mxtmt", le16_to_cpu(id->mxtmt), flags);
 +      nvme_json_add_id_ctrl_sanicap(jctrl, "sanicap", le32_to_cpu(id->sanicap), flags);
 +      nvme_json_add_id_ctrl_4k_mem(jctrl, "hmminds", le32_to_cpu(id->hmminds), flags);
 +      nvme_json_add_le16(jctrl, "hmmaxd", id->hmmaxd);
 +      nvme_json_add_le16(jctrl, "nsetidmax", id->nsetidmax);
 +      nvme_json_add_le16(jctrl, "endgidmax", id->endgidmax);
 +      nvme_json_add_time_s_flags(jctrl, "anatt", id->anatt, flags);
 +      nvme_json_add_id_ctrl_anacap(jctrl, "anacap", id->anacap, flags);
 +      nvme_json_add_le32(jctrl, "anagrpmax", id->anagrpmax);
 +      nvme_json_add_le32(jctrl, "nanagrpid", id->nanagrpid);
 +      nvme_json_add_id_ctrl_64k_mem(jctrl, "pels", le32_to_cpu(id->pels), flags);
 +      nvme_json_add_id_ctrl_sqes(jctrl, "sqes", id->sqes, flags);
 +      nvme_json_add_id_ctrl_cqes(jctrl, "cqes", id->cqes, flags);
 +      nvme_json_add_le16(jctrl, "maxcmd", id->maxcmd);
 +      nvme_json_add_le32(jctrl, "nn", id->nn);
 +      nvme_json_add_id_ctrl_oncs(jctrl, "oncs", le16_to_cpu(id->oncs), flags);
 +      nvme_json_add_id_ctrl_fuses(jctrl, "fuses", le16_to_cpu(id->fuses), flags);
 +      nvme_json_add_id_ctrl_fna(jctrl, "fna", id->fna, flags);
 +      nvme_json_add_id_ctrl_vwc(jctrl, "vwc", id->vwc, flags);
 +      nvme_json_add_le16(jctrl, "awun", id->awun);
 +      nvme_json_add_le16(jctrl, "awupf", id->awupf);
 +      nvme_json_add_id_ctrl_nvscc(jctrl, "nvscc", id->nvscc, flags);
 +      nvme_json_add_id_ctrl_nwpc(jctrl, "nwpc", id->nwpc, flags);
 +      nvme_json_add_le16(jctrl, "acwu", id->acwu);
 +      nvme_json_add_id_ctrl_sgls(jctrl, "sgls", le32_to_cpu(id->sgls), flags);
 +      nvme_json_add_le32(jctrl, "mnan", id->mnan);
 +      nvme_json_add_str_flags(jctrl, "subnqn", id->subnqn, strnlen(id->subnqn, sizeof(id->subnqn)), flags);
 +      nvme_json_add_id_ctrl_16_mem(jctrl, "ioccsz", le32_to_cpu(id->ioccsz), flags);
 +      nvme_json_add_id_ctrl_16_mem(jctrl, "iorcsz", le32_to_cpu(id->iorcsz), flags);
 +      nvme_json_add_id_ctrl_16_mem(jctrl, "icdoff", le16_to_cpu(id->icdoff), flags);
 +      nvme_json_add_id_ctrl_fcatt(jctrl, "fcatt", id->fcatt, flags);
 +      nvme_json_add_int(jctrl, "msdbd", id->msdbd);
 +      nvme_json_add_id_ctrl_ofcs(jctrl, "ofcs", le16_to_cpu(id->ofcs), flags);
 +
 +      jpsds = nvme_json_new_array();
 +      for (i = 0; i <= id->npss; i++)
 +              nvme_json_add_id_ctrl_psd(jpsds, &id->psd[i], flags | NVME_JSON_COMPACT);
 +      json_object_object_add(jctrl, "psd", jpsds);
 +
 +      return jctrl;
 +}
 +
 +static void nvme_json_add_id_ns_lbaf(struct json_object *j,
 +      struct nvme_lbaf *lbaf, bool in_use, unsigned long flags)
 +{
 +      struct json_object *jlbaf = nvme_json_new_object(flags);
 +
 +      nvme_json_add_size_flags(jlbaf, "ms", le16_to_cpu(lbaf->ms), flags);
 +
 +      if (flags & NVME_JSON_HUMAN) {
 +              nvme_json_add_size_flags(jlbaf, "lbads", 1 << lbaf->ds, flags);
 +              nvme_json_add_str(jlbaf, "rp",
 +                      nvme_id_ns_lbaf_rp_str(lbaf->rp & NVME_LBAF_RP_MASK),
 +                      flags);
 +      } else {
 +              nvme_json_add_int(jlbaf, "lbads", lbaf->ds);
 +              nvme_json_add_int(jlbaf, "rp", lbaf->rp);
 +      }
 +
 +      nvme_json_add_bool(jlbaf, "in-use", in_use);
 +
 +      json_object_array_add(j, jlbaf);
 +}
 +
 +static void nvme_json_add_id_ns_nsfeat(struct json_object *j, const char *n, __u8 nsfeat, unsigned long flags)
 +{
 +      struct json_object *jnsfeat;
 +
 +      if (!(flags & NVME_JSON_DECODE_COMPLEX)) {
 +              nvme_json_add_0x(j, n, nsfeat, flags);
 +              return;
 +      }
 +
 +      jnsfeat = nvme_json_new_object(flags);
 +
 +      nvme_json_add_0x(jnsfeat, "value", nsfeat, flags);
 +      nvme_json_add_flag_flags(jnsfeat, "thin-provisioning", nsfeat, NVME_NS_FEAT_THIN, flags);
 +      nvme_json_add_flag_flags(jnsfeat, "ns-atomics", nsfeat, NVME_NS_FEAT_NATOMIC, flags);
 +      nvme_json_add_flag_flags(jnsfeat, "unwritten-blk-err", nsfeat, NVME_NS_FEAT_DULBE, flags);
 +      nvme_json_add_flag_flags(jnsfeat, "id-reuse", nsfeat, NVME_NS_FEAT_ID_REUSE, flags);
 +      nvme_json_add_flag_flags(jnsfeat, "preferred-access", nsfeat, NVME_NS_FEAT_IO_OPT, flags);
 +
 +      json_object_object_add(j, n, jnsfeat);
 +}
 +
 +static void nvme_json_add_id_ns_flbas(struct json_object *j, const char *n, __u8 flbas, unsigned long flags)
 +{
 +      struct json_object *jflbas;
 +
 +      if (!(flags & NVME_JSON_DECODE_COMPLEX)) {
 +              nvme_json_add_0x(j, n, flbas, flags);
 +              return;
 +      }
 +
 +      jflbas = nvme_json_new_object(flags);
 +
 +      nvme_json_add_0x(jflbas, "value", flbas, flags);
 +      nvme_json_add_int(jflbas, "lba-index", flbas & NVME_NS_FLBAS_LBA_MASK);
 +      nvme_json_add_flag_flags(jflbas, "extended-metadata", flbas, NVME_NS_FLBAS_META_EXT, flags);
 +
 +      json_object_object_add(j, n, jflbas);
 +}
 +
 +static void nvme_json_add_id_ns_mc(struct json_object *j, const char *n, __u8 mc, unsigned long flags)
 +{
 +      struct json_object *jmc;
 +
 +      if (!(flags & NVME_JSON_DECODE_COMPLEX)) {
 +              nvme_json_add_0x(j, n, mc, flags);
 +              return;
 +      }
 +
 +      jmc = nvme_json_new_object(flags);
 +
 +      nvme_json_add_0x(jmc, "value", mc, flags);
 +      nvme_json_add_flag_flags(jmc, "extended", mc, NVME_NS_MC_EXTENDED, flags);
 +      nvme_json_add_flag_flags(jmc, "separate", mc, NVME_NS_MC_SEPARATE, flags);
 +
 +      json_object_object_add(j, n, jmc);
 +}
 +
 +static void nvme_json_add_id_ns_dpc(struct json_object *j, const char *n, __u8 dpc, unsigned long flags)
 +{
 +      struct json_object *jdpc;
 +
 +      if (!(flags & NVME_JSON_DECODE_COMPLEX)) {
 +              nvme_json_add_0x(j, n, dpc, flags);
 +              return;
 +      }
 +
 +      jdpc = nvme_json_new_object(flags);
 +
 +      nvme_json_add_0x(jdpc, "value", dpc, flags);
 +      nvme_json_add_flag_flags(jdpc, "type1", dpc, NVME_NS_DPC_PI_TYPE1, flags);
 +      nvme_json_add_flag_flags(jdpc, "type2", dpc, NVME_NS_DPC_PI_TYPE2, flags);
 +      nvme_json_add_flag_flags(jdpc, "type3", dpc, NVME_NS_DPC_PI_TYPE3, flags);
 +      nvme_json_add_flag_flags(jdpc, "first", dpc, NVME_NS_DPC_PI_FIRST, flags);
 +      nvme_json_add_flag_flags(jdpc, "last", dpc, NVME_NS_DPC_PI_LAST, flags);
 +
 +      json_object_object_add(j, n, jdpc);
 +}
 +
 +static void nvme_json_add_id_ns_dps(struct json_object *j, const char *n, __u8 dps, unsigned long flags)
 +{
 +      struct json_object *jdps;
 +
 +      if (!(flags & NVME_JSON_DECODE_COMPLEX)) {
 +              nvme_json_add_0x(j, n, dps, flags);
 +              return;
 +      }
 +
 +      jdps = nvme_json_new_object(flags);
 +
 +      nvme_json_add_0x(jdps, "value", dps, flags);
 +
 +      if (flags & NVME_JSON_HUMAN)
 +              nvme_json_add_str(jdps, "pi",
 +                      nvme_id_ns_dps_str(dps & NVME_NS_DPS_PI_MASK),
 +                      flags);
 +      else
 +              nvme_json_add_int(jdps, "pi", dps & NVME_NS_DPS_PI_MASK);
 +
 +      nvme_json_add_flag_flags(jdps, "first", dps, NVME_NS_DPS_PI_FIRST, flags);
 +
 +      json_object_object_add(j, n, jdps);
 +}
 +
 +static void nvme_json_add_id_ns_nmic(struct json_object *j, const char *n, __u8 nmic, unsigned long flags)
 +{
 +      struct json_object *jnmic;
 +
 +      if (!(flags & NVME_JSON_DECODE_COMPLEX)) {
 +              nvme_json_add_0x(j, n, nmic, flags);
 +              return;
 +      }
 +
 +      jnmic = nvme_json_new_object(flags);
 +
 +      nvme_json_add_0x(jnmic, "value", nmic, flags);
 +      nvme_json_add_flag_flags(jnmic, "shared", nmic, NVME_NS_NMIC_SHARED, flags);
 +
 +      json_object_object_add(j, n, jnmic);
 +}
 +
 +static void nvme_json_add_id_ns_rescap(struct json_object *j, const char *n, __u8 rescap, unsigned long flags)
 +{
 +      struct json_object *jrescap;
 +
 +      if (!(flags & NVME_JSON_DECODE_COMPLEX)) {
 +              nvme_json_add_0x(j, n, rescap, flags);
 +              return;
 +      }
 +
 +      jrescap = nvme_json_new_object(flags);
 +
 +      nvme_json_add_0x(jrescap, "value", rescap, flags);
 +      nvme_json_add_flag_flags(jrescap, "ptpl", rescap, NVME_NS_RESCAP_PTPL, flags);
 +      nvme_json_add_flag_flags(jrescap, "we", rescap, NVME_NS_RESCAP_WE, flags);
 +      nvme_json_add_flag_flags(jrescap, "ea", rescap, NVME_NS_RESCAP_EA, flags);
 +      nvme_json_add_flag_flags(jrescap, "wero", rescap, NVME_NS_RESCAP_WERO, flags);
 +      nvme_json_add_flag_flags(jrescap, "earo", rescap, NVME_NS_RESCAP_EARO, flags);
 +      nvme_json_add_flag_flags(jrescap, "wear", rescap, NVME_NS_RESCAP_WEAR, flags);
 +      nvme_json_add_flag_flags(jrescap, "eaar", rescap, NVME_NS_RESCAP_EAAR, flags);
 +      nvme_json_add_flag_flags(jrescap, "iek13", rescap, NVME_NS_RESCAP_IEK_13, flags);
 +
 +      json_object_object_add(j, n, jrescap);
 +}
 +
 +static void nvme_json_add_id_ns_fpi(struct json_object *j, const char *n, __u8 fpi, unsigned long flags)
 +{
 +      struct json_object *jfpi;
 +
 +      if (!(flags & NVME_JSON_DECODE_COMPLEX)) {
 +              nvme_json_add_0x(j, n, fpi, flags);
 +              return;
 +      }
 +
 +      jfpi = nvme_json_new_object(flags);
 +      nvme_json_add_0x(jfpi, "value", fpi, flags);
 +      nvme_json_add_percent(jfpi, "remaining", fpi & NVME_NS_FPI_REMAINING, flags);
 +      nvme_json_add_flag_flags(jfpi, "supported", fpi, NVME_NS_FPI_SUPPORTED, flags);
 +
 +      json_object_object_add(j, n, jfpi);
 +}
 +
 +static void nvme_json_add_id_ns_dlfeat(struct json_object *j, const char *n, __u8 dlfeat, unsigned long flags)
 +{
 +      struct json_object *jdlfeat;
 +
 +      if (!(flags & NVME_JSON_DECODE_COMPLEX)) {
 +              nvme_json_add_0x(j, n, dlfeat, flags);
 +              return;
 +      }
 +
 +      jdlfeat = nvme_json_new_object(flags);
 +
 +      nvme_json_add_0x(jdlfeat, "value", dlfeat, flags);
 +
 +      if (flags & NVME_JSON_HUMAN)
 +              nvme_json_add_str(jdlfeat, "read-behavior",
 +                      id_ns_dlfeat_rb_str(dlfeat & NVME_NS_DLFEAT_RB), flags);
 +      else
 +              nvme_json_add_int(jdlfeat, "read-behavior", dlfeat & NVME_NS_DLFEAT_RB);
 +
 +      nvme_json_add_flag_flags(jdlfeat, "write-zeroes", dlfeat, NVME_NS_DLFEAT_WRITE_ZEROES, flags);
 +      nvme_json_add_flag_flags(jdlfeat, "crc-guard", dlfeat, NVME_NS_DLFEAT_CRC_GUARD, flags);
 +
 +      json_object_object_add(j, n, jdlfeat);
 +}
 +
 +static void nvme_json_add_id_ns_nsattr(struct json_object *j, const char *n, __u8 nsattr, unsigned long flags)
 +{
 +      struct json_object *jnsattr;
 +
 +      if (!(flags & NVME_JSON_DECODE_COMPLEX)) {
 +              nvme_json_add_0x(j, n, nsattr, flags);
 +              return;
 +      }
 +
 +      jnsattr = nvme_json_new_object(flags);
 +
 +      nvme_json_add_0x(jnsattr, "value", nsattr, flags);
 +      nvme_json_add_flag_flags(jnsattr, "write-protected", nsattr, NVME_NS_NSATTR_WRITE_PROTECTED, flags);
 +
 +      json_object_object_add(j, n, jnsattr);
 +}
 +
 +static void nvme_json_add_blocks(struct json_object *j, const char *n, __u64 blocks,
 +      uint32_t bs, unsigned long flags)
 +{
 +      uint64_t size = blocks * bs;
 +
 +      if (flags & NVME_JSON_HUMAN)
 +              nvme_json_add_size(j, n, size, flags);
 +      else
 +              nvme_json_add_int(j, n, blocks);
 +}
 +
 +struct json_object *nvme_id_ns_to_json(struct nvme_id_ns *ns,
 +      unsigned long flags)
 +{
 +      uint8_t lbaf = ns->flbas & NVME_NS_FLBAS_LBA_MASK;
 +      uint32_t bs = 1 << ns->lbaf[lbaf].ds;
 +      struct json_object *jns, *jlbafs;
 +      int i;
 +
 +      if (flags & NVME_JSON_BINARY)
 +              return nvme_json_new_str_len_flags(ns, sizeof(*ns), flags);
 +
 +      jns = nvme_json_new_object(flags);
 +
 +      if (flags & NVME_JSON_HUMAN) {
 +              nvme_json_add_storage_flags(jns, "nsze", bs * le64_to_cpu(ns->nsze), flags);
 +              nvme_json_add_storage_flags(jns, "ncap", bs * le64_to_cpu(ns->ncap), flags);
 +              nvme_json_add_storage_flags(jns, "nuse", bs * le64_to_cpu(ns->nuse), flags);
 +      } else {
 +              nvme_json_add_le64(jns, "nsze", ns->nsze);
 +              nvme_json_add_le64(jns, "ncap", ns->ncap);
 +              nvme_json_add_le64(jns, "nuse", ns->nuse);
 +      }
 +
 +      nvme_json_add_id_ns_nsfeat(jns, "nsfeat", ns->nsfeat, flags);
 +      nvme_json_add_int(jns, "nlbaf", ns->nlbaf);
 +      nvme_json_add_id_ns_flbas(jns, "flbas", ns->flbas, flags);
 +      nvme_json_add_id_ns_mc(jns, "mc", ns->mc, flags);
 +      nvme_json_add_id_ns_dpc(jns, "dpc", ns->dpc, flags);
 +      nvme_json_add_id_ns_dps(jns, "dps", ns->dps, flags);
 +      nvme_json_add_id_ns_nmic(jns, "nmic", ns->nmic, flags);
 +      nvme_json_add_id_ns_rescap(jns, "rescap", ns->rescap, flags);
 +      nvme_json_add_id_ns_fpi(jns, "fpi", ns->fpi, flags);
 +      nvme_json_add_id_ns_dlfeat(jns, "dlfeat", ns->dlfeat, flags);
 +      nvme_json_add_blocks(jns, "nawun", le16_to_cpu(ns->nawun), bs, flags);
 +      nvme_json_add_blocks(jns, "nawupf", le16_to_cpu(ns->nawupf), bs, flags);
 +      nvme_json_add_blocks(jns, "nacwu", le16_to_cpu(ns->nacwu), bs, flags);
 +      nvme_json_add_blocks(jns, "nabsn", le16_to_cpu(ns->nabsn), bs, flags);
 +      nvme_json_add_blocks(jns, "nabo", le16_to_cpu(ns->nabo), bs, flags);
 +      nvme_json_add_blocks(jns, "nabspf", le16_to_cpu(ns->nabspf), bs, flags);
 +      nvme_json_add_blocks(jns, "noiob", le16_to_cpu(ns->noiob), bs, flags);
 +      nvme_json_add_storage_128_flags(jns, "nvmcap", ns->nvmcap, flags);
 +      nvme_json_add_blocks(jns, "npwg", le16_to_cpu(ns->npwg), bs, flags);
 +      nvme_json_add_blocks(jns, "npwa", le16_to_cpu(ns->npwa), bs, flags);
 +      nvme_json_add_blocks(jns, "npdg", le16_to_cpu(ns->npdg), bs, flags);
 +      nvme_json_add_blocks(jns, "npda", le16_to_cpu(ns->npda), bs, flags);
 +      nvme_json_add_blocks(jns, "nows", le16_to_cpu(ns->nows), bs, flags);
 +      nvme_json_add_le32(jns, "anagrpid", ns->anagrpid);
 +      nvme_json_add_id_ns_nsattr(jns, "nsattr", ns->nsattr, flags);
 +      nvme_json_add_le16(jns, "nvmsetid", ns->nvmsetid);
 +      nvme_json_add_le16(jns, "endgid", ns->endgid);
 +      nvme_json_add_uuid(jns, "nguid", ns->nguid, flags);
 +      nvme_json_add_oui(jns, "eui64", ns->eui64, 8, flags);
 +
 +      jlbafs = nvme_json_new_array();
 +      for (i = 0; i <= ns->nlbaf; i++)
 +              nvme_json_add_id_ns_lbaf(jlbafs, &ns->lbaf[i], i == lbaf, flags);
 +      json_object_object_add(jns, "lbaf", jlbafs);
 +
 +      return jns;
 +}
 +
 +static struct json_object *nvme_id_ns_desc_to_json(
 +      struct nvme_ns_id_desc *desc, unsigned long flags)
 +{
 +      struct json_object *jdesc = nvme_json_new_object(flags);
 +
 +      if (flags & NVME_JSON_HUMAN)
 +              nvme_json_add_str(jdesc, "nidt", nvme_id_nsdesc_nidt_str(desc->nidt), flags);
 +      else
 +              nvme_json_add_int(jdesc, "nidt", desc->nidt);
 +
 +      nvme_json_add_int(jdesc, "nidl", desc->nidl);
 +      nvme_json_add_hex_array(jdesc, "nid", desc->nid, desc->nidl);
 +
 +      return jdesc;
 +}
 +
 +struct json_object *nvme_id_ns_desc_list_to_json(void *list,
 +      unsigned long flags)
 +{
 +      struct json_object *jlist, *jdescs;
 +      struct nvme_ns_id_desc *cur;
 +      void *p = list;
 +
 +      if (flags & NVME_JSON_BINARY)
 +              return nvme_json_new_str_len_flags(list, sizeof(*list), flags);
 +
 +      jlist = nvme_json_new_object(flags);
 +      jdescs = nvme_json_new_array();
 +
 +      cur = p;
 +      while (cur->nidl && p - list < 0x1000) {
 +              json_object_array_add(jdescs,
 +                      nvme_id_ns_desc_to_json(cur, flags));
 +              p += cur->nidl + sizeof(*cur);
 +              cur = p;
 +      } 
 +      json_object_object_add(jlist, "ns-id-descriptors", jdescs);
 +
 +      return jlist;
 +}
 +
 +static struct json_object *nvme_id_ns_granularity_desc_to_json(
 +      struct nvme_id_ns_granularity_desc *gran, unsigned long flags)
 +{
 +      struct json_object *jgran = nvme_json_new_object(flags);
 +
 +      nvme_json_add_le64(jgran, "nszeg", gran->namespace_size_granularity);
 +      nvme_json_add_le64(jgran, "ncapg", gran->namespace_capacity_granularity);
 +
 +      return jgran;
 +}
 +
 +struct json_object *nvme_id_ns_granularity_list_to_json(
 +      struct nvme_id_ns_granularity_list *glist, unsigned long flags)
 +{
 +      struct json_object *jglist, *jgrans;
 +      int i;
 +
 +      if (flags & NVME_JSON_BINARY)
 +              return nvme_json_new_str_len_flags(glist, sizeof(*glist), flags);
 +
 +      jglist = nvme_json_new_object(flags);
 +
 +      nvme_json_add_le32(jglist, "attributes", glist->attributes);
 +      nvme_json_add_int(jglist, "ndesc", glist->num_descriptors);
 +
 +      jgrans = nvme_json_new_array();
 +      for (i = 0; i <= MIN(glist->num_descriptors, 15); i++)
 +              json_object_array_add(jgrans,
 +                      nvme_id_ns_granularity_desc_to_json(&glist->entry[i],
 +                                                          flags));
 +      json_object_object_add(jglist, "ng-descriptors", jgrans);
 +
 +      return jglist;
 +}
 +
 +static json_object *nvme_nvmset_attr_to_json(
 +      struct nvme_nvmset_attr *attr, unsigned long flags)
 +{
 +      struct json_object *jattr = nvme_json_new_object(flags);
 +
 +      nvme_json_add_le16(jattr, "nvmsetid", attr->id);
 +      nvme_json_add_le16(jattr, "egi", attr->endurance_group_id);
 +      nvme_json_add_le32(jattr, "r4krt", attr->random_4k_read_typical);
 +      nvme_json_add_le32(jattr, "ows", attr->opt_write_size);
 +      nvme_json_add_int128(jattr, "tnvmsetcap", attr->total_nvmset_cap);
 +      nvme_json_add_int128(jattr, "unvmsetcap", attr->unalloc_nvmset_cap);
 +
 +      return jattr;
 +}
 +
 +struct json_object *nvme_id_nvm_set_list_to_json(
 +      struct nvme_id_nvmset_list *nvmset, unsigned long flags)
 +{
 +      struct json_object *jnvmset, *jattrs;
 +      int i;
 +
 +      if (flags & NVME_JSON_BINARY)
 +              return nvme_json_new_str_len_flags(nvmset, sizeof(*nvmset), flags);
 +
 +      jnvmset = nvme_json_new_object(flags);
 +
 +      nvme_json_add_int(jnvmset, "nids", nvmset->nid);
 +
 +      jattrs = nvme_json_new_array();
 +      for (i = 0; i < nvmset->nid; i++)
 +              json_object_array_add(jnvmset,
 +                      nvme_nvmset_attr_to_json(&nvmset->ent[i], flags));
 +      json_object_object_add(jnvmset, "entry", jattrs);
 +
 +      return jnvmset;
 +}
 +
 +struct json_object *nvme_id_primary_ctrl_cap_to_json(
 +      struct nvme_primary_ctrl_cap *cap, unsigned long flags)
 +{
 +      struct json_object *jpri;
 +
 +      if (flags & NVME_JSON_BINARY)
 +              return nvme_json_new_str_len_flags(cap, sizeof(*cap), flags);
 +
 +      jpri = nvme_json_new_object(flags);
 +
 +      nvme_json_add_le16(jpri, "cntlid", cap->cntlid);
 +      nvme_json_add_le16(jpri, "portid", cap->portid);
 +      nvme_json_add_int(jpri, "crt", cap->crt);
 +      nvme_json_add_le32(jpri, "vqfrt", cap->vqfrt);
 +      nvme_json_add_le32(jpri, "vqrfa", cap->vqrfa);
 +      nvme_json_add_le16(jpri, "vqrfap", cap->vqrfap);
 +      nvme_json_add_le16(jpri, "vqprt", cap->vqprt);
 +      nvme_json_add_le16(jpri, "vqfrsm", cap->vqfrsm);
 +      nvme_json_add_le16(jpri, "vqgran", cap->vqgran);
 +      nvme_json_add_le32(jpri, "vifrt", cap->vifrt);
 +      nvme_json_add_le32(jpri, "virfa", cap->virfa);
 +      nvme_json_add_le16(jpri, "virfap", cap->virfap);
 +      nvme_json_add_le16(jpri, "viprt", cap->viprt);
 +      nvme_json_add_le16(jpri, "vifrsm", cap->vifrsm);
 +      nvme_json_add_le16(jpri, "vigran", cap->vigran);
 +
 +      return jpri;
 +}
 +
 +static struct json_object *nvme_secondary_ctrl_entry_to_json(
 +      struct nvme_secondary_ctrl *ent, unsigned int flags)
 +{
 +      struct json_object *jsec = nvme_json_new_object(flags);
 +
 +      nvme_json_add_le16(jsec, "scid", ent->scid);
 +      nvme_json_add_le16(jsec, "pcid", ent->pcid);
 +      nvme_json_add_int(jsec, "scs", ent->scs);
 +      nvme_json_add_le16(jsec, "vfn", ent->vfn);
 +      nvme_json_add_le16(jsec, "nvq", ent->nvq);
 +      nvme_json_add_le16(jsec, "nvi", ent->nvi);
 +
 +      return jsec;
 +}
 +
 +struct json_object *nvme_id_secondary_ctrl_list_to_json(
 +      struct nvme_secondary_ctrl_list *list, unsigned long flags)
 +{
 +      struct json_object *jlist, *jsecs;
 +      int i;
 +
 +      if (flags & NVME_JSON_BINARY)
 +              return nvme_json_new_str_len_flags(list, sizeof(*list), flags);
 +
 +      jlist = nvme_json_new_object(flags);
 +      jsecs = nvme_json_new_array();
 +
 +      nvme_json_add_int(jlist, "nids", list->num);
 +      for (i = 0; i < MIN(list->num, 127); i++)
 +              json_object_array_add(jsecs,
 +                      nvme_secondary_ctrl_entry_to_json(&list->sc_entry[i],
 +                                                        flags));
 +      json_object_object_add(jlist, "sc-entrys", jsecs);
 +      return jlist;
 +}
 +
 +static struct json_object *nvme_id_uuid_to_json(
 +      struct nvme_id_uuid_list_entry *desc, unsigned long flags)
 +{
 +      struct json_object *juuid = nvme_json_new_object(flags);
 +      __u8 assoc = desc->header & NVME_ID_UUID_HDR_ASSOCIATION_MASK;
 +
 +      if (flags & NVME_JSON_HUMAN)
 +              nvme_json_add_str(juuid, "nidt", nvme_id_uuid_assoc_str(assoc), flags);
 +      else
 +              nvme_json_add_int(juuid, "association", assoc);
 +      nvme_json_add_hex_array(juuid, "uuid", desc->uuid, 16);
 +
 +      return juuid;
 +}
 +
 +struct json_object *nvme_id_uuid_list_to_json(
 +      struct nvme_id_uuid_list *list, unsigned long flags)
 +{
 +      struct json_object *jlist, *juuids;
 +      int i;
 +
 +      if (flags & NVME_JSON_BINARY)
 +              return nvme_json_new_str_len_flags(list, sizeof(*list), flags);
 +
 +      jlist = nvme_json_new_object(flags);
 +      juuids = nvme_json_new_array();
 +
 +      for (i = 0; i < NVME_ID_UUID_LIST_MAX; i++)
 +              json_object_array_add(juuids,
 +                      nvme_id_uuid_to_json(&list->entry[i], flags));
 +      json_object_object_add(jlist, "uuids", juuids);
 +
 +      return jlist;
 +}
 +
 +struct json_object *nvme_ns_list_to_json(
 +      struct nvme_ns_list *list, unsigned long flags)
 +{
 +      struct json_object *jlist, *jarray;
 +      int i;
 +
 +      if (flags & NVME_JSON_BINARY)
 +              return nvme_json_new_str_len_flags(list, sizeof(*list), flags);
 +
 +      jlist = nvme_json_new_object(flags);
 +      jarray = nvme_json_new_array();
 +      for (i = 0; i < 1024; i++) {
 +              struct json_object *jnsid;
 +              __u32 nsid;
 +
 +              nsid = le32_to_cpu(list->ns[i]);
 +              if (!nsid)
 +                      break;
 +
 +              jnsid = nvme_json_new_int(nsid);
 +              json_object_array_add(jarray, jnsid);
 +      }
 +      json_object_object_add(jlist, "nsids", jarray);
 +
 +      return jlist;
 +}
 +
 +struct json_object *nvme_ctrl_list_to_json(
 +      struct nvme_ctrl_list *list, unsigned long flags)
 +{
 +      struct json_object *jlist, *jarray;
 +      __u16 num_ids;
 +      int i;
 +
 +      if (flags & NVME_JSON_BINARY)
 +              return nvme_json_new_str_len_flags(list, sizeof(*list), flags);
 +
 +      jlist = nvme_json_new_object(flags);
 +      jarray = nvme_json_new_array();
 +
 +      num_ids = le16_to_cpu(list->num);
 +      nvme_json_add_int(jlist, "nids", num_ids);
 +
 +      for (i = 0; i < MIN(num_ids, 2047); i++)
 +              json_object_array_add(jarray, nvme_json_new_int(
 +                      le16_to_cpu(list->identifier[i])));
 +
 +      json_object_object_add(jlist, "cntlids", jarray);
 +
 +      return jlist;
 +}
 +
 +static struct json_object *nvme_lbas_desc_to_json(struct nvme_lba_status_desc *lbasd)
 +{
 +      struct json_object *jlbasd;
 +
 +      jlbasd = nvme_json_new_object(0);
 +
 +      nvme_json_add_le64(jlbasd, "dslba", lbasd->dslba);
 +      nvme_json_add_le32(jlbasd, "nlb", lbasd->nlb);
 +      nvme_json_add_int(jlbasd, "status", lbasd->status);
 +
 +      return jlbasd;
 +}
 +
 +struct json_object *nvme_lba_status_desc_list_to_json(
 +      struct nvme_lba_status *lbas, unsigned long flags)
 +{
 +      struct json_object *jlbas, *jlbasds;
 +      __u64 i, nlsd = le64_to_cpu(lbas->nlsd);
 +
 +      if (flags & NVME_JSON_BINARY)
 +              return nvme_json_new_str_len_flags(lbas,
 +                      sizeof(*lbas) + sizeof(lbas->descs[0]) * nlsd,
 +                      flags);
 +
 +      jlbas = nvme_json_new_object(flags);
 +
 +      nvme_json_add_int64(jlbas, "nlds", nlsd);
 +      nvme_json_add_int(jlbas, "cmpc", lbas->cmpc);
 +
 +      jlbasds = nvme_json_new_array();
 +      for (i = 0; i < nlsd; i++) {
 +              struct json_object *jlbasd;
 +
 +              jlbasd = nvme_lbas_desc_to_json(&lbas->descs[i]);
 +              json_object_array_add(jlbasds, jlbasd);
 +      }
 +      json_object_object_add(jlbas, "entries", jlbasds);
 +
 +      return jlbas;
 +}
 +
 +static int nvme_ana_size(struct nvme_ana_log *ana)
 +{
 +      int i, offset = 0, ngroups = le16_to_cpu(ana->ngrps);
 +      int ret = sizeof(*ana) + sizeof(ana->descs[0]) * ngroups;
 +      void *base = ana;
 +
 +      for (i = 0; i < ngroups; i++) {
 +              struct nvme_ana_group_desc *desc = base + offset;
 +              int nnsids = le32_to_cpu(desc->nnsids);
 +
 +              ret += nnsids * sizeof(desc->nsids[0]);
 +      }
 +      return ret;
 +}
 +
 +static struct json_object *nvme_ana_desc_to_json(
 +      struct nvme_ana_group_desc *desc, int *offset, unsigned long flags)
 +{
 +      struct json_object *jdesc, *jnsids;
 +      int j, nnsids;
 +
 +      jdesc = nvme_json_new_object(flags);
 +      nnsids = le32_to_cpu(desc->nnsids);
 +      nvme_json_add_le32(jdesc, "anagid", desc->grpid);
 +      nvme_json_add_int(jdesc, "nnsids", nnsids);
 +      nvme_json_add_le64(jdesc, "cc", desc->chgcnt);
 +      nvme_json_add_int(jdesc, "state", desc->state);
 +
 +      jnsids = nvme_json_new_array();
 +      for (j = 0; j < nnsids; j++) {
 +              __u32 nsid = le32_to_cpu(desc->nsids[j]);
 +              json_object_array_add(jnsids, nvme_json_new_int(nsid));
 +      }
 +      json_object_object_add(jdesc, "nsids", jnsids);
 +      *offset += nnsids * sizeof(__u32);
 +
 +      return jdesc;
 +}
 +
 +struct json_object *nvme_ana_log_to_json(
 +      struct nvme_ana_log *ana, unsigned long flags)
 +{
 +      struct json_object *jana, *jdescs;
 +      int i, offset = 0, ngroups = le16_to_cpu(ana->ngrps);
 +      void *base = &ana->descs;
 +
 +      if (flags & NVME_JSON_BINARY)
 +              return nvme_json_new_str_len_flags(ana, nvme_ana_size(ana), flags);
 +
 +      jana = nvme_json_new_object(flags);
 +      nvme_json_add_le64(jana, "cc", ana->chgcnt);
 +      nvme_json_add_int(jana, "nagd", ngroups);
 +
 +      jdescs = nvme_json_new_array();
 +      for (i = 0; i < ngroups; i++) {
 +              struct nvme_ana_group_desc *desc = base + offset;
 +
 +              json_object_array_add(jdescs,
 +                      nvme_ana_desc_to_json(desc, &offset, flags));
 +      }
 +      json_object_object_add(jana, "anagds", jdescs);
 +
 +      return jana;
 +}
 +
 +static struct json_object *nvme_disc_entry_to_json(
 +      struct nvmf_disc_log_entry *e, unsigned long flags)
 +{
 +      struct json_object *jentry = nvme_json_new_object(flags);
 +
 +      if (flags & NVME_JSON_HUMAN)
 +              nvme_json_add_str(jentry, "trtype", nvmf_trtype_str(e->trtype), flags);
 +      else
 +              nvme_json_add_int(jentry, "trtype", e->trtype);
 +
 +      nvme_json_add_int(jentry, "adrfam", e->adrfam);
 +      nvme_json_add_int(jentry, "subtype", e->subtype);
 +      nvme_json_add_int(jentry, "treq", e->treq);
 +      nvme_json_add_le16(jentry, "portid", e->portid);
 +      nvme_json_add_le16(jentry, "cntlid", e->cntlid);
 +      nvme_json_add_le16(jentry, "asqsz", e->asqsz);
 +      nvme_json_add_str_flags(jentry, "trsvcid", e->trsvcid,
 +              strnlen(e->trsvcid, sizeof(e->trsvcid)), flags);
 +      nvme_json_add_str_flags(jentry, "subnqn", e->subnqn,
 +              strnlen(e->subnqn, sizeof(e->subnqn)), flags);
 +      nvme_json_add_str_flags(jentry, "traddr", e->traddr,
 +              strnlen(e->traddr, sizeof(e->traddr)), flags);
 +      nvme_json_add_hex_array(jentry, "tsas", (void *)e->tsas.common, 16);
 +
 +      return jentry;
 +}
 +
 +struct json_object *nvme_discovery_log_to_json(
 +      struct nvmf_discovery_log *log, unsigned long flags)
 +{
 +      struct json_object *jlog, *jentries;
 +      int i, numrec = le64_to_cpu(log->numrec);
 +
 +      if (flags & NVME_JSON_BINARY)
 +              return nvme_json_new_str_len_flags(log,
 +                      sizeof(*log) + sizeof(log->entries[0]) * numrec,
 +                      flags);
 +
 +      jlog = nvme_json_new_object(flags);
 +      nvme_json_add_le64(jlog, "genctr", log->genctr);
 +      nvme_json_add_int(jlog, "numrec", numrec);
 +      nvme_json_add_le16(jlog, "recfmt", log->recfmt);
 +
 +      jentries = nvme_json_new_array();
 +      for (i = 0; i < numrec; i++) {
 +              struct nvmf_disc_log_entry *e = &log->entries[i];
 +              struct json_object *jentry;
 +
 +              jentry = nvme_disc_entry_to_json(e, flags);
 +              if (!jentry)
 +                      break;
 +
 +              json_object_array_add(jentries, jentry);
 +      }
 +      json_object_object_add(jlog, "entries", jentries);
 +
 +      return jlog;
 +}
 +
 +struct json_object *nvme_cmd_effects_log_to_json(
 +      struct nvme_cmd_effects_log *effects, unsigned long flags)
 +{
 +      struct json_object *jeffects;
 +      __u32 effect;
 +      char key[8];
 +      int i;
 +
 +      if (flags & NVME_JSON_BINARY)
 +              return nvme_json_new_str_len_flags(effects, sizeof(*effects), flags);
 +
 +      jeffects = nvme_json_new_object(flags);
 +      for (i = 0; i < 255; i++) {
 +              effect = le32_to_cpu(effects->acs[i]);
 +              if (!(effect & NVME_CMD_EFFECTS_CSUPP))
 +                      continue;
 +              sprintf(key, "acs%d", i);
 +              nvme_json_add_int(jeffects, key, effect);
 +      }
 +
 +      for (i = 0; i < 255; i++) {
 +              effect = le32_to_cpu(effects->acs[i]);
 +              if (!(effect & NVME_CMD_EFFECTS_CSUPP))
 +                      continue;
 +              sprintf(key, "iocs%d", i);
 +              nvme_json_add_le32(jeffects, key, effects->iocs[i]);
 +      }
 +
 +      return jeffects;
 +}
 +
 +struct json_object *nvme_ege_aggregate_log(
 +      struct nvme_eg_event_aggregate_log *eglog, unsigned long flags)
 +{
 +      struct json_object *jeglog, *jegs;
 +      int i, nents = le64_to_cpu(eglog->nr_entries);
 +
 +      if (flags & NVME_JSON_BINARY)
 +              return nvme_json_new_str_len_flags(eglog,
 +                      sizeof(*eglog) + sizeof(eglog->egids[0]) * nents,
 +                      flags);
 +
 +      jeglog = nvme_json_new_object(flags);
 +      nvme_json_add_int(jeglog, "nents", nents);
 +
 +      jegs = nvme_json_new_array();
 +      for (i = 0; i < nents; i++) {
 +              int egid = le16_to_cpu(eglog->egids[i]);
 +
 +              if (!egid)
 +                      break;
 +              json_object_array_add(jegs, nvme_json_new_int(egid));
 +      }
 +      json_object_object_add(jeglog, "cntlids", jegs);
 +
 +      return jeglog;
 +}
 +
 +struct json_object *nvme_endurance_group_log_to_json(
 +      struct nvme_endurance_group_log *eg, unsigned long flags)
 +{
 +      struct json_object *jeg;
 +
 +      if (flags & NVME_JSON_BINARY)
 +              return nvme_json_new_str_len_flags(eg, sizeof(*eg), flags);
 +
 +      jeg = nvme_json_new_object(flags);
 +      nvme_json_add_int(jeg, "cw", eg->critical_warning);
 +      nvme_json_add_int(jeg, "ap", eg->avl_spare);
 +      nvme_json_add_int(jeg, "apt", eg->avl_spare_threshold);
 +      nvme_json_add_int(jeg, "pu", eg->percent_used);
 +      nvme_json_add_int128(jeg, "ee", eg->endurance_estimate);
 +      nvme_json_add_int128(jeg, "dur", eg->data_units_read);
 +      nvme_json_add_int128(jeg, "duw", eg->data_units_written);
 +      nvme_json_add_int128(jeg, "mwc", eg->media_units_written);
 +      nvme_json_add_int128(jeg, "hrc", eg->host_read_cmds);
 +      nvme_json_add_int128(jeg, "hwc", eg->host_write_cmds);
 +      nvme_json_add_int128(jeg, "mdie", eg->media_data_integrity_err);
 +      nvme_json_add_int128(jeg, "nele", eg->num_err_info_log_entries);
 +
 +      return jeg;
 +}
 +
 +static struct json_object *nvme_error_to_json(
 +      struct nvme_error_log_page *log, unsigned long flags)
 +{
 +      struct json_object *jerror = nvme_json_new_object(flags);
 +
 +      nvme_json_add_le64(jerror, "error-count", log->error_count);
 +      nvme_json_add_le16(jerror, "sqid", log->sqid);
 +      nvme_json_add_le16(jerror, "cmdid", log->cmdid);
 +      nvme_json_add_hex_le16_flags(jerror, "status-field", log->status_field, flags);
 +      nvme_json_add_hex_le16_flags(jerror, "err-loc", log->parm_error_location, flags);
 +      nvme_json_add_le64(jerror, "lba", log->lba);
 +      nvme_json_add_le32(jerror, "nsid", log->nsid);
 +      nvme_json_add_int(jerror, "vsia", log->vs);
 +
 +      if (flags & NVME_JSON_HUMAN)
 +              nvme_json_add_str(jerror, "trtype", nvmf_trtype_str(log->trtype), flags);
 +      else
 +              nvme_json_add_int(jerror, "trtype", log->trtype);
 +      nvme_json_add_le64(jerror, "csi", log->cs);
 +      nvme_json_add_le16(jerror, "ttsi", log->trtype_spec_info);
 +
 +      return jerror;
 +}
 +
 +struct json_object *nvme_error_log_to_json(
 +      struct nvme_error_log_page *log, int entries, unsigned long flags)
 +{
 +      struct json_object *jlog, *jerrors;
 +      int i;
 +
 +      if (flags & NVME_JSON_BINARY)
 +              return nvme_json_new_str_len_flags(log, sizeof(*log) * entries,
 +                      flags);
 +
 +      flags |= NVME_JSON_COMPACT;
 +      jlog = nvme_json_new_object(flags);
 +      jerrors = nvme_json_new_array();
 +      for (i = 0; i < entries; i++)
 +              json_object_array_add(jerrors,
 +                      nvme_error_to_json(log + i, flags));
 +      json_object_object_add(jlog, "error", jerrors);
 +
 +      return jlog;
 +}
 +
 +struct json_object *nvme_fw_slot_log_to_json(
 +      struct nvme_firmware_slot *fw, unsigned long flags)
 +{
 +      struct json_object *jfw = nvme_json_new_object(flags);
 +      int i;
 +
 +      if (flags & NVME_JSON_BINARY)
 +              return nvme_json_new_str_len_flags(fw, sizeof(*fw), flags);
 +
 +      nvme_json_add_int(jfw, "afi", fw->afi);
 +      for (i = 0; i < 7; i++) {
 +              char key[5] = { 0 }; /* frsX\0 */
 +
 +              if (!strcmp(fw->frs[i], "") ||
 +                  !fw->frs[i][0])
 +                      continue;
 +
 +              sprintf(key, "frs%d", i + 1);
 +              nvme_json_add_str_flags(jfw, key, fw->frs[i],
 +                      sizeof(fw->frs[i]), 0);
 +      }
 +      return jfw;
 +}
 +
 +static struct json_object *nvme_lba_status_lba_rd_to_json(
 +      struct nvme_lba_rd *rd, unsigned long flags)
 +{
 +      struct json_object *jrd = nvme_json_new_object(flags);
 +
 +      nvme_json_add_le64(jrd, "rslba", rd->rslba);
 +      nvme_json_add_le32(jrd, "rnlb", rd->rnlb);
 +
 +      return jrd;
 +}
 +
 +static struct json_object *nvme_lba_status_log_ns_element_to_json(
 +      struct nvme_lbas_ns_element *element, int *offset,
 +      unsigned long flags)
 +{
 +      int i, neid = le32_to_cpu(element->neid);
 +      struct json_object *jelem, *jrds;
 +
 +      jelem = nvme_json_new_object(flags);
 +      nvme_json_add_int(jelem, "neid", neid);
 +      nvme_json_add_le32(jelem, "nrld", element->nrld);
 +      nvme_json_add_int(jelem, "ratype", element->ratype);
 +
 +      jrds = nvme_json_new_array();
 +      for (i = 0; i < neid; i++)
 +              json_object_array_add(jrds,
 +                      nvme_lba_status_lba_rd_to_json(&element->lba_rd[i],
 +                                                     flags));
 +      json_object_object_add(jelem, "lbards", jrds);
 +
 +      *offset += sizeof(*element) + neid * sizeof(element->lba_rd[0]);
 +      return jelem;
 +}
 +
 +struct json_object *nvme_lba_status_log_to_json(
 +      struct nvme_lba_status_log *lbas, unsigned long flags)
 +{
 +      struct json_object *jlbas, *jelems;
 +      int offset = 0, lslplen = le32_to_cpu(lbas->lslplen);
 +      void *base = &lbas->elements;
 +
 +      if (flags & NVME_JSON_BINARY)
 +              return nvme_json_new_str_len_flags(base, lslplen, flags);
 +
 +      jlbas = nvme_json_new_object(flags);
 +      nvme_json_add_int(jlbas, "lslplen", lslplen);
 +      nvme_json_add_le32(jlbas, "nlslne", lbas->nlslne);
 +      nvme_json_add_le32(jlbas, "estulb", lbas->estulb);
 +      nvme_json_add_le16(jlbas, "lsgc", lbas->lsgc);
 +
 +      jelems = nvme_json_new_array();
 +      while (offset < lslplen - sizeof(*lbas)) {
 +              struct nvme_lbas_ns_element *element = base + offset;
 +
 +              json_object_array_add(jelems, 
 +                      nvme_lba_status_log_ns_element_to_json(element,
 +                                                             &offset, flags));
 +      }
 +      json_object_object_add(jlbas, "jelems", jelems);
 +
 +      return jlbas;
 +}
 +
 +
 +struct json_object *nvme_aggr_predictable_lat_log_to_json(
 +      struct nvme_aggregate_predictable_lat_event *pl, unsigned long flags)
 +{
 +      __u64 num_entries = le64_to_cpu(pl->num_entries);
 +      struct json_object *jpl, *jentries;
 +      int i;
 +
 +      jpl = nvme_json_new_object(flags);
 +      nvme_json_add_int64(jpl, "nents", num_entries);
 +
 +      jentries = nvme_json_new_array();
 +      for (i = 0; i < num_entries; i++)
 +              json_object_array_add(jentries,
 +                      nvme_json_new_int(le16_to_cpu(pl->entries[i])));
 +      json_object_object_add(jpl, "entries", jentries);
 +
 +      return jpl;
 +}
 +
 +struct json_object *nvme_nvmset_predictable_lat_log_to_json(
 +      struct nvme_nvmset_predictable_lat_log *pl, unsigned long flags)
 +{
 +      struct json_object *jpl;
 +
 +      if (flags & NVME_JSON_BINARY)
 +              return nvme_json_new_str_len_flags(pl, sizeof(*pl), flags);
 +
 +      jpl = nvme_json_new_object(flags);
 +      nvme_json_add_int(jpl, "status", pl->status);
 +      nvme_json_add_le16(jpl, "event_type", pl->event_type);
 +      nvme_json_add_le64(jpl, "dtwin_rt", pl->dtwin_rt);
 +      nvme_json_add_le64(jpl, "dtwin_wt", pl->dtwin_wt);
 +      nvme_json_add_le64(jpl, "dtwin_tmax", pl->dtwin_tmax);
 +      nvme_json_add_le64(jpl, "dtwin_tmin_hi", pl->dtwin_tmin_hi);
 +      nvme_json_add_le64(jpl, "dtwin_tmin_lo", pl->dtwin_tmin_lo);
 +      nvme_json_add_le64(jpl, "dtwin_re", pl->dtwin_re);
 +      nvme_json_add_le64(jpl, "dtwin_we", pl->dtwin_we);
 +      nvme_json_add_le64(jpl, "dtwin_te", pl->dtwin_te);
 +
 +      return jpl;
 +}
 +
 +
 +struct json_object *nvme_resv_notify_log_to_json(
 +      struct nvme_resv_notification_log *resv, unsigned long flags)
 +{
 +      struct json_object *jresv;
 +
 +      if (flags & NVME_JSON_BINARY)
 +              return nvme_json_new_str_len_flags(resv, sizeof(*resv), flags);
 +
 +      jresv = nvme_json_new_object(flags);
 +      nvme_json_add_le64(jresv, "lpc", resv->lpc);
 +      nvme_json_add_int(jresv, "rnlpt", resv->rnlpt);
 +      nvme_json_add_int(jresv, "nalp", resv->nalp);
 +      nvme_json_add_le32(jresv, "nsid", resv->nsid);
 +
 +      return jresv;
 +}
 +
 +struct json_object *nvme_sanitize_log_to_json(
 +      struct nvme_sanitize_log_page *san, unsigned long flags)
 +{
 +      struct json_object *jsan;
 +
 +      if (flags & NVME_JSON_BINARY)
 +              return nvme_json_new_str_len_flags(san, sizeof(*san), flags);
 +
 +      jsan = nvme_json_new_object(flags);
 +      nvme_json_add_le16(jsan, "sprog", san->sprog);
 +      nvme_json_add_le16(jsan, "sstat", san->sstat);
 +      nvme_json_add_le32(jsan, "scdw10", san->scdw10);
 +      nvme_json_add_le32(jsan, "eto", san->eto);
 +      nvme_json_add_le32(jsan, "etbe", san->etbe);
 +      nvme_json_add_le32(jsan, "etce", san->etce);
 +      nvme_json_add_le32(jsan, "etond", san->etond);
 +      nvme_json_add_le32(jsan, "etbend", san->etbend);
 +      nvme_json_add_le32(jsan, "etcend", san->etcend);
 +
 +      return jsan;
 +}
 +
 +static struct json_object *nvme_self_test_to_json(
 +      struct nvme_st_result *str, unsigned long flags)
 +{
 +      struct json_object *jstr;
 +      __u8 op = str->dsts & NVME_ST_RESULT_MASK;
 +
 +      jstr = nvme_json_new_object(flags);
 +      nvme_json_add_int(jstr, "dstop", op);
 +
 +      if (op == NVME_ST_RESULT_NOT_USED)
 +              return jstr;
 +
 +      nvme_json_add_int(jstr, "stc", str->dsts >> NVME_ST_CODE_SHIFT);
 +      nvme_json_add_int(jstr, "seg", str->seg);
 +      nvme_json_add_int(jstr, "vid", str->vdi);
 +      nvme_json_add_le64(jstr, "poh", str->poh);
 +      if (str->vdi & NVME_ST_VALID_DIAG_INFO_NSID)
 +              nvme_json_add_le32(jstr, "nsid", str->nsid);
 +      if (str->vdi & NVME_ST_VALID_DIAG_INFO_FLBA)
 +              nvme_json_add_le64(jstr, "flba", str->flba);
 +      if (str->vdi & NVME_ST_VALID_DIAG_INFO_SCT)
 +              nvme_json_add_int(jstr, "sct", str->sct);
 +      if (str->vdi & NVME_ST_VALID_DIAG_INFO_SC)
 +              nvme_json_add_int(jstr, "sc", str->sc);
 +      nvme_json_add_int(jstr, "vs", (str->vs[1] << 8) | (str->vs[0]));
 +
 +      return jstr;
 +}
 +
 +struct json_object *nvme_dev_self_test_log_to_json(
 +      struct nvme_self_test_log *st, unsigned long flags)
 +{
 +      struct json_object *jst, *jstrs;
 +      int i;
 +
 +      if (flags & NVME_JSON_BINARY)
 +              return nvme_json_new_str_len_flags(st, sizeof(*st), flags);
 +
 +      jst = nvme_json_new_object(flags);
 +      jstrs = nvme_json_new_array();
 +
 +      nvme_json_add_int(jst, "current-operation", st->current_operation);
 +      nvme_json_add_int(jst, "completion", st->completion);
 +
 +      for (i = 0; i < NVME_LOG_ST_MAX_RESULTS; i++)
 +              json_object_array_add(jstrs,
 +                      nvme_self_test_to_json(&st->result[i], flags));
 +      json_object_object_add(jst, "nsids", jstrs);
 +
 +      return jst;
 +}
 +
 +static void nvme_json_add_smart_cw(struct json_object *j, const char *n,
 +                                 __u8 cw, unsigned long flags)
 +{
 +      struct json_object *jcw;
 +
 +      if (!(flags & NVME_JSON_DECODE_COMPLEX)) {
 +              nvme_json_add_0x(j, "cw", cw, flags);
 +              return;
 +      }
 +
 +      jcw = nvme_json_new_object(flags);
 +
 +      nvme_json_add_0x(jcw, "value", cw, flags);
 +      nvme_json_add_flag_flags(jcw, "spare", cw, NVME_SMART_CRIT_SPARE, flags);
 +      nvme_json_add_flag_flags(jcw, "temperature", cw, NVME_SMART_CRIT_TEMPERATURE, flags);
 +      nvme_json_add_flag_flags(jcw, "degraded", cw, NVME_SMART_CRIT_DEGRADED, flags);
 +      nvme_json_add_flag_flags(jcw, "media", cw, NVME_SMART_CRIT_MEDIA, flags);
 +      nvme_json_add_flag_flags(jcw, "memory-backup", cw, NVME_SMART_CRIT_VOLATILE_MEMORY, flags);
 +      nvme_json_add_flag_flags(jcw, "pmr-ro", cw, NVME_SMART_CRIT_PMR_RO, flags);
 +
 +      json_object_object_add(j, n, jcw);
 +}
 +
 +static void nvme_json_add_smart_egcw(struct json_object *j, const char *n,
 +                                 __u8 egcw, unsigned long flags)
 +{
 +      struct json_object *jegcw;
 +
 +      if (!(flags & NVME_JSON_DECODE_COMPLEX)) {
 +              nvme_json_add_0x(j, "egcw", egcw, flags);
 +              return;
 +      }
 +
 +      jegcw = nvme_json_new_object(flags);
 +
 +      nvme_json_add_0x(jegcw, "value", egcw, flags);
 +      nvme_json_add_flag_flags(jegcw, "spare", egcw, NVME_SMART_EGCW_SPARE, flags);
 +      nvme_json_add_flag_flags(jegcw, "degraded", egcw, NVME_SMART_EGCW_DEGRADED, flags);
 +      nvme_json_add_flag_flags(jegcw, "read-only", egcw, NVME_SMART_EGCW_RO, flags);
 +
 +      json_object_object_add(j, n, jegcw);
 +}
 +
 +struct json_object *nvme_smart_log_to_json(
 +      struct nvme_smart_log *smart, unsigned long flags)
 +{
 +      __u32 temp = unalign_int(smart->temperature, sizeof(smart->temperature));
 +      struct json_object *jsmart = nvme_json_new_object(flags);
 +      int i;
 +
 +      if (flags & NVME_JSON_BINARY)
 +              return nvme_json_new_str_len_flags(smart, sizeof(*smart), flags);
 +
 +      nvme_json_add_smart_cw(jsmart, "critical-warning", smart->critical_warning, flags);
 +      nvme_json_add_temp(jsmart, "composite-temp", temp, flags);
 +      nvme_json_add_percent(jsmart, "available-spare", smart->avail_spare, flags);
 +      nvme_json_add_percent(jsmart, "spare-threshold", smart->spare_thresh, flags);
 +      nvme_json_add_percent(jsmart, "percent-used", smart->percent_used, flags);
 +      nvme_json_add_smart_egcw(jsmart, "endgrp-crit-warning", smart->endu_grp_crit_warn_sumry, flags);
 +      nvme_json_add_int128(jsmart, "data-units-read", smart->data_units_read);
 +      nvme_json_add_int128(jsmart, "data-units-written", smart->data_units_written);
 +      nvme_json_add_int128(jsmart, "host-reads", smart->host_reads);
 +      nvme_json_add_int128(jsmart, "host-writes", smart->host_writes);
 +      nvme_json_add_int128(jsmart, "ctrl-busy-time", smart->ctrl_busy_time);
 +      nvme_json_add_int128(jsmart, "power-cycles", smart->power_cycles);
 +      nvme_json_add_int128(jsmart, "power-on-hours", smart->power_on_hours);
 +      nvme_json_add_int128(jsmart, "unsafe-shutdowns", smart->unsafe_shutdowns);
 +      nvme_json_add_int128(jsmart, "media-errors", smart->media_errors);
 +      nvme_json_add_int128(jsmart, "error-log-entries", smart->num_err_log_entries);
 +      nvme_json_add_time_m_flags(jsmart, "warning-temp-time", smart->warning_temp_time, flags);
 +      nvme_json_add_time_m_flags(jsmart, "crit-comp-temp-time", smart->critical_comp_time, flags);
 +
 +      for (i = 0; i < 8; i++) {
 +              __u32 t = le16_to_cpu(smart->temp_sensor[i]);
 +              char key[4] = {};
 +
 +              if (t == 0)
 +                      continue;
 +              sprintf(key, "ts%d", i + 1);
 +              nvme_json_add_temp(jsmart, key, t, flags);
 +      }
 +
 +      nvme_json_add_le32(jsmart, "therm-mgmt-t1-trans-cnt", smart->thm_temp1_trans_count);
 +      nvme_json_add_le32(jsmart, "therm-mgmt-t2-trans-cnt", smart->thm_temp2_trans_count);
 +      nvme_json_add_le32(jsmart, "therm-mgmt-t1-total-time", smart->thm_temp1_total_time);
 +      nvme_json_add_le32(jsmart, "therm-mgmt-t2-total-time", smart->thm_temp2_total_time);
 +
 +      return jsmart;
 +}
 +
 +struct json_object *nvme_telemetry_log_to_json(
 +      struct nvme_telemetry_log *telem, unsigned long flags)
 +{
 +      struct json_object *jtelem;
 +      int size = sizeof(*telem) + NVME_LOG_TELEM_BLOCK_SIZE *
 +                      le16_to_cpu(telem->dalb3);
 +
 +      if (flags & NVME_JSON_BINARY)
 +              return nvme_json_new_str_len_flags(telem, size, flags);
 +
 +      jtelem = nvme_json_new_object(flags);
 +      nvme_json_add_int(jtelem, "lpi", telem->lpi);
 +      nvme_json_add_hex(jtelem, "ieee", nvme_ieee_to_int(telem->ieee), flags);
 +      nvme_json_add_le16(jtelem, "da1lb", telem->dalb1);
 +      nvme_json_add_le16(jtelem, "da2lb", telem->dalb2);
 +      nvme_json_add_le16(jtelem, "da3lb", telem->dalb3);
 +      nvme_json_add_int(jtelem, "tcida", telem->ctrlavail);
 +      nvme_json_add_int(jtelem, "tcidgn", telem->ctrldgn);
 +      nvme_json_add_hex_array(jtelem, "rid", telem->rsnident, sizeof(telem->rsnident));
 +
 +      return jtelem;
 +}
 +
 +struct json_object *nvme_persistent_event_log_to_json(
 +      struct nvme_persistent_event_log *pel, unsigned long flags)
 +{
 +      struct json_object *jpel;
 +
 +      if (flags & NVME_JSON_BINARY)
 +              return nvme_json_new_str_len_flags(pel, sizeof(*pel), flags);
 +
 +      jpel = nvme_json_new_object(flags);
 +      nvme_json_add_int(jpel, "lid", pel->lid);
 +      nvme_json_add_le32(jpel, "ttl", pel->ttl);
 +      nvme_json_add_int(jpel, "rv", pel->rv);
 +      nvme_json_add_le16(jpel, "lht", pel->lht);
 +      nvme_json_add_le64(jpel, "ts", pel->ts);
 +      nvme_json_add_int128(jpel, "poh", pel->poh);
 +      nvme_json_add_le64(jpel, "pcc", pel->pcc);
 +      nvme_json_add_hex(jpel, "vid", le16_to_cpu(pel->vid), flags);
 +      nvme_json_add_hex(jpel, "ssvid", le16_to_cpu(pel->ssvid), flags);
 +      nvme_json_add_str_flags(jpel, "sn", pel->sn, sizeof(pel->sn), 0);
 +      nvme_json_add_str_flags(jpel, "mn", pel->mn, sizeof(pel->mn), 0);
 +      nvme_json_add_str_flags(jpel, "subnqn", pel->subnqn, sizeof(pel->subnqn), 0);
 +      nvme_json_add_hex_array(jpel, "seb", pel->seb, sizeof(pel->seb));
 +
 +      return jpel;
 +}
 +
 +struct json_object *nvme_props_to_json(void *regs, unsigned long flags)
 +{
 +      struct json_object *jregs;
 +
 +      if (flags & NVME_JSON_BINARY)
 +              return nvme_json_new_str_len_flags(regs, 0x1000, flags);
 +
 +      jregs = nvme_json_new_object(flags);
 +      nvme_json_add_le64_ptr(jregs, "cap", regs + NVME_REG_CAP);
 +      nvme_json_add_le32_ptr(jregs, "vs", regs + NVME_REG_VS);
 +      nvme_json_add_le32_ptr(jregs, "intms", regs + NVME_REG_INTMS);
 +      nvme_json_add_le32_ptr(jregs, "intmc", regs + NVME_REG_INTMC);
 +      nvme_json_add_le32_ptr(jregs, "cc", regs + NVME_REG_CC);
 +      nvme_json_add_le32_ptr(jregs, "csts", regs + NVME_REG_CSTS);
 +      nvme_json_add_le32_ptr(jregs, "nssr", regs + NVME_REG_NSSR);
 +      nvme_json_add_le32_ptr(jregs, "aqa", regs + NVME_REG_AQA);
 +      nvme_json_add_le64_ptr(jregs, "asq", regs + NVME_REG_ASQ);
 +      nvme_json_add_le64_ptr(jregs, "acq", regs + NVME_REG_ACQ);
 +      nvme_json_add_le32_ptr(jregs, "cmbloc", regs + NVME_REG_CMBLOC);
 +      nvme_json_add_le32_ptr(jregs, "cmbsz", regs + NVME_REG_CMBSZ);
 +      nvme_json_add_le32_ptr(jregs, "bpinfo", regs + NVME_REG_BPINFO);
 +      nvme_json_add_le32_ptr(jregs, "bprsel", regs + NVME_REG_BPRSEL);
 +      nvme_json_add_le64_ptr(jregs, "bpmbl", regs + NVME_REG_BPMBL);
 +      nvme_json_add_le64_ptr(jregs, "cmbmsc", regs + NVME_REG_CMBMSC);
 +      nvme_json_add_le32_ptr(jregs, "cmbsts", regs + NVME_REG_CMBSTS);
 +      nvme_json_add_le32_ptr(jregs, "pmrcap", regs + NVME_REG_PMRCAP);
 +      nvme_json_add_le32_ptr(jregs, "pmrctl", regs + NVME_REG_PMRCTL);
 +      nvme_json_add_le32_ptr(jregs, "pmrsts", regs + NVME_REG_PMRSTS);
 +      nvme_json_add_le32_ptr(jregs, "pmrebs", regs + NVME_REG_PMREBS);
 +      nvme_json_add_le32_ptr(jregs, "pmrswtp", regs + NVME_REG_PMRSWTP);
 +      nvme_json_add_le64_ptr(jregs, "pmrmsc", regs + NVME_REG_PMRMSC);
 +
 +      return jregs;
 +}
 +
 +static struct json_object *nvme_resv_ctrl_to_json(
 +      struct nvme_registered_ctrl *regctl,
 +      unsigned long flags)
 +{
 +      struct json_object *jrc;
 +
 +      jrc = nvme_json_new_object(flags);
 +
 +      nvme_json_add_le16(jrc, "cntlid", regctl->cntlid);
 +      nvme_json_add_int(jrc, "rcsts", regctl->rcsts);
 +      nvme_json_add_le64(jrc, "hostid", regctl->hostid);
 +      nvme_json_add_le64(jrc, "rkey", regctl->rkey);
 +
 +      return jrc;
 +}
 +
 +static struct json_object *nvme_resv_ctrl_ext_to_json(
 +      struct nvme_registered_ctrl_ext *regctl,
 +      unsigned long flags)
 +{
 +      struct json_object *jrc;
 +
 +      jrc = nvme_json_new_object(flags);
 +
 +      nvme_json_add_le16(jrc, "cntlid", regctl->cntlid);
 +      nvme_json_add_int(jrc, "rcsts", regctl->rcsts);
 +      nvme_json_add_le64(jrc, "rkey", regctl->rkey);
 +      nvme_json_add_int128(jrc, "hostid", regctl->hostid);
 +
 +      return jrc;
 +}
 +
 +struct json_object *nvme_resv_report_to_json(
 +      struct nvme_reservation_status *status, bool ext,
 +      unsigned long flags)
 +{
 +      int i, regctl = status->regctl[0] | (status->regctl[1] << 8);
 +      struct json_object *jrs, *jrcs;
 +      int size = sizeof(*status) - sizeof(status->rsvd24) +
 +              regctl * (ext ? sizeof(status->regctl_eds[0]) :
 +                              sizeof(status->regctl_ds[0]));
 +
 +      if (flags & NVME_JSON_BINARY)
 +              return nvme_json_new_str_len_flags(status, size, flags);
 +
 +      jrs = nvme_json_new_object(flags);
 +
 +      nvme_json_add_le32(jrs, "gen", status->gen);
 +      nvme_json_add_int(jrs, "rtype", status->rtype);
 +      nvme_json_add_int(jrs, "regctl", regctl);
 +      nvme_json_add_int(jrs, "ptpls", status->ptpls);
 +
 +      jrcs = nvme_json_new_array();
 +      for (i = 0; i < regctl; i++) {
 +              struct json_object *jrc;
 +
 +              if (ext)
 +                      jrc = nvme_resv_ctrl_ext_to_json(
 +                              &status->regctl_eds[i], flags);
 +              else
 +                      jrc = nvme_resv_ctrl_to_json(
 +                              &status->regctl_ds[i], flags);
 +
 +              json_object_array_add(jrcs, jrc);
 +      }
 +      json_object_object_add(jrs, "nsids", jrcs);
 +
 +      return NULL;
 +}