]> www.infradead.org Git - users/sagi/nvme-cli.git/commitdiff
[nvme-cli] Add support for enclosures to WDC plugin commands
authorJeff Lien <jeff.lien@wdc.com>
Thu, 1 Oct 2020 19:15:28 +0000 (14:15 -0500)
committerKeith Busch <kbusch@kernel.org>
Tue, 13 Oct 2020 18:06:09 +0000 (12:06 -0600)
plugins/wdc/wdc-nvme.c
plugins/wdc/wdc-nvme.h

index 54a81076a7d8e420510f67ec56dd888f5c6d3bb1..0e912afaf9efa0095183584b8a6a8e06f99e1068 100644 (file)
@@ -38,6 +38,7 @@
 #include "nvme-ioctl.h"
 #include "plugin.h"
 #include "json.h"
+#include "nvme-status.h"
 
 #include "argconfig.h"
 #include "suffix.h"
 #define WDC_NVME_LOG_SIZE_DATA_LEN                     0x08
 #define WDC_NVME_LOG_SIZE_HDR_LEN                      0x08
 
+/* Enclosure */
+#define WDC_OPENFLEX_MI_DEVICE_MODEL    "OpenFlex"
+#define WDC_RESULT_MORE_DATA                       0x80000000
+#define WDC_RESULT_NOT_AVAILABLE       0x7FFFFFFF
+
 /* Device Config */
 #define WDC_NVME_VID                                   0x1c58
 #define WDC_NVME_VID_2                                 0x1b96
 /* VU Opcodes */
 #define WDC_DE_VU_READ_SIZE_OPCODE                     0xC0
 #define WDC_DE_VU_READ_BUFFER_OPCODE                   0xC2
+#define WDC_NVME_ADMIN_ENC_MGMT_SND         0xC9
+#define WDC_NVME_ADMIN_ENC_MGMT_RCV         0xCA
 
 #define WDC_DE_FILE_HEADER_SIZE                                4
 #define WDC_DE_FILE_OFFSET_SIZE                                2
 #define WDC_DE_DESTN_SPI                               1
 #define WDC_DE_DUMPTRACE_DESTINATION                   6
 
+#define NVME_ID_CTRL_MODEL_NUMBER_SIZE         40
+#define NVME_ID_CTRL_SERIAL_NUMBER_SIZE                20
+
+/* Enclosure log */
+#define WDC_NVME_ENC_LOG_SIZE_CHUNK         0x1000
+#define WDC_NVME_ENC_NIC_LOG_SIZE           0x400000
+
+/* Enclosure nic crash dump get-log id */
+#define WDC_ENC_NIC_CRASH_DUMP_ID_SLOT_1    0xD1
+#define WDC_ENC_NIC_CRASH_DUMP_ID_SLOT_2    0xD2
+#define WDC_ENC_NIC_CRASH_DUMP_ID_SLOT_3    0xD3
+#define WDC_ENC_NIC_CRASH_DUMP_ID_SLOT_4    0xD4
+#define WDC_ENC_CRASH_DUMP_ID               0xE4
+#define WDC_ENC_LOG_DUMP_ID                 0xE2
+
 typedef enum _NVME_FEATURES_SELECT
 {
     FS_CURRENT                      = 0,
@@ -601,6 +624,9 @@ static int wdc_vs_drive_info(int argc, char **argv, struct command *command,
                struct plugin *plugin);
 static int wdc_vs_temperature_stats(int argc, char **argv, struct command *command,
                struct plugin *plugin);
+static __u64 wdc_get_enc_drive_capabilities(int fd);
+static int wdc_enc_get_nic_log(int fd, __u8 log_id, __u32 xfer_size, __u32 data_len, FILE *out);
+static int wdc_enc_submit_move_data(int fd, char *cmd, int len, int xfer_size, FILE *out, int data_id, int cdw14, int cdw15);
 
 /* Drive log data size */
 struct wdc_log_size {
@@ -992,15 +1018,71 @@ free_id:
        return ret;
 }
 
+static int wdc_get_vendor_id(int fd, uint32_t *vendor_id)
+{
+       int ret;
+       struct nvme_id_ctrl ctrl;
+
+       memset(&ctrl, 0, sizeof(struct nvme_id_ctrl));
+       ret = nvme_identify_ctrl(fd, &ctrl);
+       if (ret) {
+               fprintf(stderr, "ERROR : WDC : nvme_identify_ctrl() failed "
+                       "0x%x\n", ret);
+               return -1;
+       }
+
+       *vendor_id = (uint32_t) ctrl.vid;
+
+       return ret;
+}
+
+static bool wdc_check_power_of_2(int num)
+{
+       int first_set = 1;
+
+       if (num == 0)
+               return false;
+
+       while ((first_set & num) == 0)
+               first_set <<= 1;
+
+       return (~first_set & num) ? false : true;
+}
+
+static int wdc_get_model_number(int fd, char *model)
+{
+       int ret,i;
+       struct nvme_id_ctrl ctrl;
+
+       memset(&ctrl, 0, sizeof(struct nvme_id_ctrl));
+       ret = nvme_identify_ctrl(fd, &ctrl);
+       if (ret) {
+               fprintf(stderr, "ERROR : WDC : nvme_identify_ctrl() failed "
+                       "0x%x\n", ret);
+               return -1;
+       }
+
+       memcpy(model,ctrl.mn,NVME_ID_CTRL_MODEL_NUMBER_SIZE);
+       /* get rid of the padded spaces */
+       i = NVME_ID_CTRL_MODEL_NUMBER_SIZE-1;
+       while (model[i] == ' ') i--;
+               model[i+1]=0;
+
+       return ret;
+}
+
 static bool wdc_check_device(int fd)
 {
        int ret;
        bool supported;
-       uint32_t read_device_id, read_vendor_id;
+       uint32_t read_device_id = -1, read_vendor_id = -1;
 
        ret = wdc_get_pci_ids(&read_device_id, &read_vendor_id);
-       if (ret < 0)
-               return false;
+       if (ret < 0) {
+               /* Use the identify nvme command to get vendor id due to NVMeOF device. */
+               if (wdc_get_vendor_id(fd, &read_vendor_id) < 0)
+                       return false;
+       }
 
        supported = false;
 
@@ -1015,14 +1097,44 @@ static bool wdc_check_device(int fd)
        return supported;
 }
 
+static bool wdc_enc_check_model(int fd)
+{
+       int ret;
+       bool supported;
+       char model[NVME_ID_CTRL_MODEL_NUMBER_SIZE+1];
+
+       ret = wdc_get_model_number(fd, model);
+       if (ret < 0)
+               return false;
+
+       supported = false;
+       model[NVME_ID_CTRL_MODEL_NUMBER_SIZE] = 0; /* forced termination */
+       if (strstr(model,WDC_OPENFLEX_MI_DEVICE_MODEL) != NULL)
+               supported = true;
+       else
+               fprintf(stderr, "ERROR : WDC: unsupported WDC enclosure, Model = %s\n",model);
+
+       return supported;
+}
+
 static __u64 wdc_get_drive_capabilities(int fd) {
        int ret;
-       uint32_t read_device_id, read_vendor_id;
+       uint32_t read_device_id = -1, read_vendor_id = -1;
        __u64 capabilities = 0;
 
        ret = wdc_get_pci_ids(&read_device_id, &read_vendor_id);
        if (ret < 0)
+       {
+               if (wdc_get_vendor_id(fd, &read_vendor_id) < 0)
+                       return capabilities;
+       }
+
+       /* below check condition is added due in NVMeOF device we dont have device_id so we need to use only vendor_id*/
+       if (read_device_id == -1 && read_vendor_id != -1)
+       {
+               capabilities = wdc_get_enc_drive_capabilities(fd);
                return capabilities;
+       }
 
        switch (read_vendor_id) {
        case WDC_NVME_VID:
@@ -1081,8 +1193,8 @@ static __u64 wdc_get_drive_capabilities(int fd) {
                /* FALLTHRU */
                case WDC_NVME_ZN440_DEV_ID:
                /* FALLTHRU */
-                case WDC_NVME_SN440_DEV_ID:
-                /* FALLTHRU */
+        case WDC_NVME_SN440_DEV_ID:
+        /* FALLTHRU */
                case WDC_NVME_SN7GC_DEV_ID:
                case WDC_NVME_SN7GC_DEV_ID_1:
                case WDC_NVME_SN7GC_DEV_ID_2:
@@ -1150,6 +1262,56 @@ static __u64 wdc_get_drive_capabilities(int fd) {
        return capabilities;
 }
 
+static __u64 wdc_get_enc_drive_capabilities(int fd) {
+       int ret;
+       uint32_t read_vendor_id;
+       __u64 capabilities = 0;
+
+       ret = wdc_get_vendor_id(fd, &read_vendor_id);
+       if (ret < 0)
+               return capabilities;
+
+       switch (read_vendor_id) {
+               case WDC_NVME_VID:
+                       capabilities = (WDC_DRIVE_CAP_CAP_DIAG | WDC_DRIVE_CAP_INTERNAL_LOG | WDC_DRIVE_CAP_CLEAR_PCIE |
+                               WDC_DRIVE_CAP_DRIVE_LOG | WDC_DRIVE_CAP_CRASH_DUMP | WDC_DRIVE_CAP_PFAIL_DUMP);
+
+                       /* verify the 0xCA log page is supported */
+                       if (wdc_nvme_check_supported_log_page(fd, WDC_NVME_GET_DEVICE_INFO_LOG_OPCODE) == true)
+                               capabilities |= WDC_DRIVE_CAP_CA_LOG_PAGE;
+
+                       /* verify the 0xC1 log page is supported */
+                       if (wdc_nvme_check_supported_log_page(fd, WDC_NVME_ADD_LOG_OPCODE) == true)
+                               capabilities |= WDC_DRIVE_CAP_C1_LOG_PAGE;
+                       break;
+               case WDC_NVME_VID_2:
+                       capabilities = (WDC_DRIVE_CAP_CAP_DIAG | WDC_DRIVE_CAP_INTERNAL_LOG |
+                               WDC_DRIVE_CAP_DRIVE_STATUS | WDC_DRIVE_CAP_CLEAR_ASSERT |
+                               WDC_DRIVE_CAP_RESIZE | WDC_DRIVE_CAP_CLEAR_PCIE |
+                               WDC_DRIVE_CAP_CLEAR_FW_ACT_HISTORY);
+
+                       /* verify the 0xCB log page is supported */
+                       if (wdc_nvme_check_supported_log_page(fd, WDC_NVME_GET_FW_ACT_HISTORY_LOG_ID) == true)
+                               capabilities |= WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY;
+
+                       /* verify the 0xCA log page is supported */
+                       if (wdc_nvme_check_supported_log_page(fd, WDC_NVME_GET_DEVICE_INFO_LOG_OPCODE) == true)
+                               capabilities |= WDC_DRIVE_CAP_CA_LOG_PAGE;
+
+                       /* verify the 0xD0 log page is supported */
+                       if (wdc_nvme_check_supported_log_page(fd, WDC_NVME_GET_VU_SMART_LOG_OPCODE) == true)
+                               capabilities |= WDC_DRIVE_CAP_D0_LOG_PAGE;
+                       break;
+               case WDC_NVME_SNDK_VID:
+                       capabilities = WDC_DRIVE_CAP_DRIVE_ESSENTIALS;
+                       break;
+               default:
+                       capabilities = 0;
+       }
+
+       return capabilities;
+}
+
 static int wdc_get_serial_name(int fd, char *file, size_t len, const char *suffix)
 {
        int i;
@@ -7055,3 +7217,261 @@ static int wdc_capabilities(int argc, char **argv,
     printf("capabilities                  : Supported\n");
     return 0;
 }
+
+static int wdc_enc_get_log(int argc, char **argv, struct command *command,
+        struct plugin *plugin)
+{
+       char *desc = "Get Enclosure Log.";
+       char *file = "Output file pathname.";
+       char *size = "Data retrieval transfer size.";
+       char *log = "Enclosure Log Page ID.";
+       FILE *output_fd;
+       int xfer_size = 0;
+       int fd;
+       int len;
+       int err;
+
+       struct config {
+               char  *file;
+               __u32 xfer_size;
+               __u32 log_id;
+       };
+
+       struct config cfg = {
+               .file = NULL,
+               .xfer_size = 0,
+               .log_id = 0xffffffff,
+       };
+
+       OPT_ARGS(opts) = {
+               OPT_FILE("output-file",   'o', &cfg.file,  file),
+               OPT_UINT("transfer-size", 's', &cfg.xfer_size, size),
+               OPT_UINT("log-id",        'l', &cfg.log_id, log),
+               OPT_END()
+       };
+
+       err = fd = parse_and_open(argc, argv, desc, opts);
+       if (fd < 0) {
+               goto ret;
+       }
+
+       if (!wdc_enc_check_model(fd)) {
+               err = -EINVAL;
+               goto closed_fd;
+       }
+
+       if (cfg.log_id > 0xff) {
+               fprintf(stderr, "Invalid log identifier: %d. Valid 0xd1, 0xd2, 0xd3, 0xd4, 0xe2, 0xe4\n", cfg.log_id);
+               goto closed_fd;
+       }
+
+       if (cfg.xfer_size != 0) {
+               xfer_size = cfg.xfer_size;
+                       if (!wdc_check_power_of_2(cfg.xfer_size)) {
+                               fprintf(stderr, "%s: ERROR : xfer-size (%d) must be a power of 2\n", __func__, cfg.xfer_size);
+                               err = -EINVAL;
+                               goto closed_fd;
+                       }
+       }
+
+       // Log IDs are only for specific enclosures
+       if (cfg.log_id) {
+               xfer_size = (xfer_size) ? xfer_size : WDC_NVME_ENC_LOG_SIZE_CHUNK;
+               len = cfg.file==NULL?0:strlen(cfg.file);
+               if (len > 0) {
+                       output_fd = fopen(cfg.file,"wb");
+                       if (output_fd == 0) {
+                               fprintf(stderr, "%s: ERROR : opening:%s : %s\n", __func__,cfg.file, strerror(errno));
+                               err = -EINVAL;
+                               goto closed_fd;
+                       }
+               } else {
+                       output_fd = stdout;
+               }
+               if (cfg.log_id == WDC_ENC_NIC_CRASH_DUMP_ID_SLOT_1 || cfg.log_id == WDC_ENC_NIC_CRASH_DUMP_ID_SLOT_2
+                               || cfg.log_id == WDC_ENC_NIC_CRASH_DUMP_ID_SLOT_3 || cfg.log_id == WDC_ENC_NIC_CRASH_DUMP_ID_SLOT_4) {
+                       fprintf(stderr, "args - sz:%x logid:%x of:%s\n",xfer_size,cfg.log_id,cfg.file);
+                       err = wdc_enc_get_nic_log(fd, cfg.log_id, xfer_size, WDC_NVME_ENC_NIC_LOG_SIZE, output_fd);
+               } else {
+                       fprintf(stderr, "args - sz:%x logid:%x of:%s\n",xfer_size,cfg.log_id,cfg.file);
+                       err = wdc_enc_submit_move_data(fd, NULL, 0, xfer_size, output_fd, cfg.log_id, 0, 0);
+               }
+
+               if (err == WDC_RESULT_NOT_AVAILABLE) {
+                       fprintf(stderr, "No Log/Crashdump available\n");
+                       err = 0;
+               } else if (err) {
+                       fprintf(stderr, "ERROR:0x%x Failed to collect log-id:%x \n",err, cfg.log_id);
+               }
+       }
+closed_fd:
+       close(fd);
+ret:
+       return nvme_status_to_errno(err, false);
+}
+
+static int wdc_enc_submit_move_data(int fd, char *cmd, int len, int xfer_size, FILE *out, int log_id, int cdw14, int cdw15)
+{
+       struct timespec time;
+       uint32_t response_size, more;
+       int err;
+       int handle;
+       uint32_t offset = 0;
+       char *buf;
+
+       buf = (char *)malloc(sizeof(__u8) * xfer_size);
+       if (buf == NULL) {
+               fprintf(stderr, "%s: ERROR : malloc : %s\n", __func__, strerror(errno));
+               return -1;
+       }
+       // send something no matter what
+       cmd = (len) ? cmd : buf;
+       len = (len) ? len : 0x20;
+
+       struct nvme_admin_cmd nvme_cmd = {
+               .opcode     = WDC_NVME_ADMIN_ENC_MGMT_SND,
+               // ?
+               .nsid       = 0,
+               .addr       = (__u64)(uintptr_t) cmd,
+               .data_len   = ((len + sizeof(uint32_t) - 1)/sizeof(uint32_t)) * sizeof(uint32_t),
+               .cdw10      = len,
+               .cdw12      = log_id,
+               .cdw13      = 0,
+               .cdw14      = cdw14,
+               .cdw15      = cdw15,
+       };
+
+       clock_gettime(CLOCK_REALTIME, &time);
+       srand(time.tv_nsec);
+       handle = random();  // Handle to associate send request with receive request
+       nvme_cmd.cdw11 = handle;
+
+#ifdef WDC_NVME_CLI_DEBUG
+       unsigned char *d = (unsigned char*) nvme_cmd.addr;
+       unsigned char *md = (unsigned char*) nvme_cmd.metadata;
+       printf("NVME_ADMIN_COMMAND:\n" \
+               "opcode: 0x%02x, flags: 0x%02x, rsvd: 0x%04x, nsid: 0x%08x, cdw2: 0x%08x, cdw3: 0x%08x, " \
+               "metadata_len: 0x%08x, data_len: 0x%08x, cdw10: 0x%08x, cdw11: 0x%08x, cdw12: 0x%08x, " \
+               "cdw13: 0x%08x, cdw14: 0x%08x, cdw15: 0x%08x, timeout_ms: 0x%08x, result: 0x%08x, " \
+               "metadata: %s, " \
+               "data: %s\n", \
+               nvme_cmd.opcode, nvme_cmd.flags, nvme_cmd.rsvd1, nvme_cmd.nsid, nvme_cmd.cdw2, nvme_cmd.cdw3, \
+               nvme_cmd.metadata_len, nvme_cmd.data_len, nvme_cmd.cdw10, nvme_cmd.cdw11, nvme_cmd.cdw12, \
+               nvme_cmd.cdw13, nvme_cmd.cdw14, nvme_cmd.cdw15, nvme_cmd.timeout_ms, nvme_cmd.result,
+               md, \
+               d);
+#endif
+       nvme_cmd.result = 0;
+       err = nvme_submit_passthru(fd, NVME_IOCTL_ADMIN_CMD, &nvme_cmd);
+       if (err == NVME_SC_INTERNAL) {
+               fprintf(stderr, "%s: WARNING : WDC : No log ID:x%x available\n",
+                       __func__, log_id);
+       }
+       else if (err != 0) {
+               fprintf(stderr, "%s: ERROR : WDC : NVMe Snd Mgmt Status:%s(x%x)\n",
+                       __func__, nvme_status_to_string(err), err );
+       } else {
+               if (nvme_cmd.result == WDC_RESULT_NOT_AVAILABLE)
+               {
+                       free(buf);
+                       return WDC_RESULT_NOT_AVAILABLE;
+               }
+
+               do {
+                       /* Sent request, now go retrieve response */
+                       nvme_cmd.flags = 0;
+                       nvme_cmd.opcode = WDC_NVME_ADMIN_ENC_MGMT_RCV;
+                       nvme_cmd.addr = (__u64)(uintptr_t) buf;
+                       nvme_cmd.data_len = xfer_size;
+                       nvme_cmd.cdw10 = xfer_size / sizeof(uint32_t);
+                       nvme_cmd.cdw11 = handle;
+                       nvme_cmd.cdw12 = log_id;
+                       nvme_cmd.cdw13 = offset / sizeof(uint32_t);
+                       nvme_cmd.cdw14 = cdw14;
+                       nvme_cmd.cdw15 = cdw15;
+                       nvme_cmd.result = 0;  // returned result !=0 indicates more data available
+                       err = nvme_submit_passthru(fd, NVME_IOCTL_ADMIN_CMD, &nvme_cmd);
+                       if (err != 0) {
+                               more = 0;
+                               fprintf(stderr, "%s: ERROR : WDC : NVMe Rcv Mgmt Status:%s(x%x)\n",
+                                       __func__, nvme_status_to_string(err), err);
+                       } else {
+                               more = nvme_cmd.result & WDC_RESULT_MORE_DATA;
+                               response_size = nvme_cmd.result & ~WDC_RESULT_MORE_DATA;
+                               fwrite(buf, response_size, 1, out);
+                               offset += response_size;
+                               if (more && (response_size & (sizeof(uint32_t)-1))) {
+                                       fprintf(stderr, "%s: ERROR : WDC : NVMe Rcv Mgmt response size:x%x not LW aligned\n",
+                                               __func__, response_size);
+                               }
+                       }
+               } while (more);
+                       free(buf);
+       }
+
+       return err;
+}
+
+static int wdc_enc_get_nic_log(int fd, __u8 log_id, __u32 xfer_size, __u32 data_len, FILE *out)
+{
+       __u8 *dump_data;
+       __u32 curr_data_offset, curr_data_len;
+       int i, ret;
+       struct nvme_admin_cmd admin_cmd;
+       __u32 dump_length = data_len;
+       __u32 numd;
+       __u16 numdu, numdl;
+
+       dump_data = (__u8 *) malloc(sizeof (__u8) * dump_length);
+       if (dump_data == NULL) {
+               fprintf(stderr, "%s: ERROR : malloc : %s\n",__func__, strerror(errno));
+               return -1;
+       }
+       memset(dump_data, 0, sizeof (__u8) * dump_length);
+       memset(&admin_cmd, 0, sizeof (struct nvme_admin_cmd));
+       curr_data_offset = 0;
+       curr_data_len = xfer_size;
+       i = 0;
+
+       numd = (curr_data_len >> 2) - 1;
+       numdu = numd >> 16;
+       numdl = numd & 0xffff;
+       admin_cmd.opcode = nvme_admin_get_log_page;
+       admin_cmd.nsid = curr_data_offset;
+       admin_cmd.addr = (__u64)(uintptr_t) dump_data;
+       admin_cmd.data_len = curr_data_len;
+       admin_cmd.cdw10 = log_id | (numdl << 16);
+       admin_cmd.cdw11 = numdu;
+
+       while (curr_data_offset < data_len) {
+#ifdef WDC_NVME_CLI_DEBUG
+               fprintf(stderr, "nsid 0x%08x addr 0x%08llx, data_len 0x%08x, cdw10 0x%08x, cdw11 0x%08x, cdw12 0x%08x, cdw13 0x%08x, cdw14 0x%08x \n", admin_cmd.nsid, admin_cmd.addr, admin_cmd.data_len, admin_cmd.cdw10, admin_cmd.cdw11, admin_cmd.cdw12, admin_cmd.cdw13, admin_cmd.cdw14);
+#endif
+               ret = nvme_submit_admin_passthru(fd, &admin_cmd);
+               if (ret !=0 ) {
+                       fprintf(stderr, "%s: ERROR : WDC : NVMe Status:%s(%x)\n",__func__, nvme_status_to_string(ret), ret);
+                       fprintf(stderr, "%s: ERROR : WDC : Get chunk %d, size = 0x%x, offset = 0x%x, addr = 0x%lx\n",
+                                __func__, i, admin_cmd.data_len, curr_data_offset, (long unsigned int)admin_cmd.addr);
+                       break;
+               }
+
+               if ((curr_data_offset + xfer_size) <= data_len)
+                       curr_data_len = xfer_size;
+               else
+                       curr_data_len = data_len - curr_data_offset;   /* last transfer */
+
+               curr_data_offset += curr_data_len;
+               numd = (curr_data_len >> 2) - 1;
+               numdu = numd >> 16;
+               numdl = numd & 0xffff;
+               admin_cmd.addr = (__u64)(uintptr_t)dump_data + (__u64)curr_data_offset;
+               admin_cmd.nsid = curr_data_offset;
+               admin_cmd.data_len = curr_data_len;
+               admin_cmd.cdw10 = log_id | (numdl << 16);
+               admin_cmd.cdw11 = numdu;
+               i++;
+       }
+       fwrite(dump_data, data_len, 1, out);
+       free(dump_data);
+       return ret;
+}
index e8b4d7539a0ca3fd115f88637ece42d9813bff82..740f484de85ad7db8fdd9f571970b18d8d4901b7 100644 (file)
@@ -25,6 +25,7 @@ PLUGIN(NAME("wdc", "Western Digital vendor specific extensions"),
                ENTRY("drive-resize", "WDC Drive Resize", wdc_drive_resize)
                ENTRY("vs-fw-activate-history", "WDC Get FW Activate History", wdc_vs_fw_activate_history)
                ENTRY("clear-fw-activate-history", "WDC Clear FW Activate History", wdc_clear_fw_activate_history)
+               ENTRY("enc-get-log", "WDC Get Enclosure Log", wdc_enc_get_log)
                ENTRY("vs-telemetry-controller-option", "WDC Enable/Disable Controller Initiated Telemetry Log", wdc_vs_telemetry_controller_option)
                ENTRY("vs-error-reason-identifier", "WDC Telemetry Reason Identifier", wdc_reason_identifier)
                ENTRY("log-page-directory", "WDC Get Log Page Directory", wdc_log_page_directory)