From 040423e084012df66dfa135135d5f1274da5f4a1 Mon Sep 17 00:00:00 2001 From: Jeff Lien Date: Mon, 20 Jul 2020 10:45:58 -0500 Subject: [PATCH] [NVMe-CLI] Add support for vs-fw-activate-history and clear-fw-activate-history in wdc plugins for SN345 --- plugins/wdc/wdc-nvme.c | 385 ++++++++++++++++++++++++++++++----------- 1 file changed, 287 insertions(+), 98 deletions(-) diff --git a/plugins/wdc/wdc-nvme.c b/plugins/wdc/wdc-nvme.c index 8534cedb..8dc2752c 100644 --- a/plugins/wdc/wdc-nvme.c +++ b/plugins/wdc/wdc-nvme.c @@ -109,6 +109,8 @@ #define WDC_DRIVE_CAP_TEMP_STATS 0x0000000000200000 #define WDC_DRIVE_CAP_VUC_CLEAR_PCIE 0x0000000000400000 #define WDC_DRIVE_CAP_VU_FID_CLEAR_PCIE 0x0000000000800000 +#define WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY_C2 0x0000000001000000 +#define WDC_DRIVE_CAP_VU_FID_CLEAR_FW_ACT_HISTORY 0x0000000002000000 #define WDC_DRIVE_CAP_DRIVE_ESSENTIALS 0x0000000100000000 #define WDC_DRIVE_CAP_DUI_DATA 0x0000000200000000 @@ -234,6 +236,7 @@ #define WDC_NVME_CLEAR_FW_ACT_HIST_OPCODE 0xC6 #define WDC_NVME_CLEAR_FW_ACT_HIST_CMD 0x23 #define WDC_NVME_CLEAR_FW_ACT_HIST_SUBCMD 0x05 +#define WDC_NVME_CLEAR_FW_ACT_HIST_VU_FID 0xC1 /* Additional Smart Log */ #define WDC_ADD_LOG_BUF_LEN 0x4000 @@ -281,6 +284,10 @@ #define WDC_NVME_GET_FW_ACT_HISTORY_LOG_ID 0xCB #define WDC_FW_ACT_HISTORY_LOG_BUF_LEN 0x3d0 +/* C2 - FW Activation History Log Page */ +#define WDC_NVME_GET_FW_ACT_HISTORY_C2_LOG_ID 0xC2 +#define WDC_FW_ACT_HISTORY_C2_LOG_BUF_LEN 0x1000 + /* D0 Smart Log Page */ #define WDC_NVME_GET_VU_SMART_LOG_OPCODE 0xD0 #define WDC_NVME_VU_SMART_LOG_LEN 0x200 @@ -843,6 +850,32 @@ struct wdc_fw_act_history_log_entry { __u8 reserved[12]; }; +struct wdc_fw_act_history_log_entry_c2 { + __u8 entry_version_num; + __u8 entry_len; + __le16 reserved; + __le16 fw_act_hist_entries; + __le64 timestamp; + __u8 reserved2[8]; + __le64 power_cycle_count; + __le64 previous_fw_version; + __le64 current_fw_version; + __u8 slot_number; + __u8 commit_action_type; + __le16 result; + __u8 reserved3[14]; +}; + +struct wdc_fw_act_history_log_format_c2 { + __u8 log_identifier; + __u8 reserved[3]; + __le32 num_entries; + struct wdc_fw_act_history_log_entry_c2 entry[20]; + __u8 reserved2[2790]; + __le16 log_page_version; + __u8 log_page_guid[16]; +}; + #define WDC_REASON_INDEX_MAX 16 #define WDC_REASON_ID_ENTRY_LEN 128 #define WDC_REASON_ID_PATH_NAME "/usr/local/nvmecli" @@ -1090,7 +1123,8 @@ static __u64 wdc_get_drive_capabilities(int fd) { case WDC_NVME_ZN345_DEV_ID: /* FALLTHRU */ case WDC_NVME_ZN345_DEV_ID_1: - capabilities = WDC_DRIVE_CAP_DUI_DATA | WDC_DRIVE_CAP_VU_FID_CLEAR_PCIE | WDC_DRIVE_CAP_C0_LOG_PAGE; + capabilities = WDC_DRIVE_CAP_DUI_DATA | WDC_DRIVE_CAP_VU_FID_CLEAR_PCIE | WDC_DRIVE_CAP_C0_LOG_PAGE | + WDC_DRIVE_CAP_VU_FID_CLEAR_FW_ACT_HISTORY | WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY_C2; break; default: capabilities = 0; @@ -3661,8 +3695,7 @@ static void wdc_get_commit_action_bin(__u8 commit_action_type, char *action_bin) } -static void wdc_print_fw_act_history_log_normal(struct wdc_fw_act_history_log_entry *fw_act_history_entry, - int num_entries) +static void wdc_print_fw_act_history_log_normal(__u8 *data, int num_entries) { int i; char previous_fw[9]; @@ -3673,49 +3706,87 @@ static void wdc_print_fw_act_history_log_normal(struct wdc_fw_act_history_log_en memset((void *)commit_action_bin, 0, 8); char *null_fw = "--------"; - printf(" Firmware Activate History Log \n"); - printf(" Power on Hour Power Cycle Previous New \n"); - printf(" Entry hh:mm:ss Count Firmware Firmware Slot Action Result \n"); - printf(" ----- -------------- ------------ ---------- ---------- ----- ------ -------\n"); - - for (i = 0; i < num_entries; i++) { - memcpy(previous_fw, (char *)&(fw_act_history_entry->previous_fw_version), 8); - if (strlen((char *)&(fw_act_history_entry->new_fw_version)) > 1) - memcpy(new_fw, (char *)&(fw_act_history_entry->new_fw_version), 8); - else - memcpy(new_fw, null_fw, 8); - - printf("%5"PRIu32"", (uint32_t)le32_to_cpu(fw_act_history_entry->entry_num)); - printf(" "); - printf("%02d:%02d:%02d", (int)(le64_to_cpu(fw_act_history_entry->power_on_seconds)/3600), - (int)((le64_to_cpu(fw_act_history_entry->power_on_seconds)%3600)/60), - (int)(le64_to_cpu(fw_act_history_entry->power_on_seconds)%60)); - printf(" "); - printf("%8"PRIu32"", (uint32_t)le32_to_cpu(fw_act_history_entry->power_cycle_count)); - printf(" "); - printf("%s", (char *)previous_fw); - printf(" "); - printf("%s", (char *)new_fw); - printf(" "); - printf("%2"PRIu8"", (uint8_t)fw_act_history_entry->slot_number); - printf(" "); - wdc_get_commit_action_bin(fw_act_history_entry->commit_action_type,(char *)&commit_action_bin); - printf(" %s", (char *)commit_action_bin); - printf(" "); - if (le16_to_cpu(fw_act_history_entry->result) == 0) - printf("pass"); - else - printf("fail #%d", (uint16_t)le16_to_cpu(fw_act_history_entry->result)); - - printf("\n"); - - fw_act_history_entry++; + printf(" Power on Hour Power Cycle Previous New \n"); + printf(" Entry hh:mm:ss Count Firmware Firmware Slot Action Result \n"); + printf(" ----- -------------- -------------------- ---------- ---------- ----- ------ -------\n"); + + if(data[0] == WDC_NVME_GET_FW_ACT_HISTORY_C2_LOG_ID) { + struct wdc_fw_act_history_log_format_c2 *fw_act_history_entry = (struct wdc_fw_act_history_log_format_c2 *)(data); + + for (i = 0; i < num_entries; i++) { + memcpy(previous_fw, (char *)&(fw_act_history_entry->entry[i].previous_fw_version), 8); + if (strlen((char *)&(fw_act_history_entry->entry[i].current_fw_version)) > 1) + memcpy(new_fw, (char *)&(fw_act_history_entry[i].entry->current_fw_version), 8); + else + memcpy(new_fw, null_fw, 8); + + printf("%5"PRIu16"", (uint16_t)le16_to_cpu(fw_act_history_entry->entry[i].fw_act_hist_entries)); + printf(" "); + printf("%02d:%02d:%02d", (int)(le64_to_cpu(fw_act_history_entry->entry[i].timestamp)/(1000*3600)), + (int)(((le64_to_cpu(fw_act_history_entry->entry[i].timestamp)/1000)%3600)/60), + (int)((le64_to_cpu(fw_act_history_entry->entry[i].timestamp)/1000)%60)); + printf(" "); + printf("%16"PRIu64"", (uint64_t)le64_to_cpu(fw_act_history_entry->entry[i].power_cycle_count)); + printf(" "); + printf("%s", (char *)previous_fw); + printf(" "); + printf("%s", (char *)new_fw); + printf(" "); + printf("%2"PRIu8"", (uint8_t)fw_act_history_entry->entry[i].slot_number); + printf(" "); + wdc_get_commit_action_bin(fw_act_history_entry->entry[i].commit_action_type,(char *)&commit_action_bin); + printf(" %s", (char *)commit_action_bin); + printf(" "); + if (le16_to_cpu(fw_act_history_entry->entry[i].result) == 0) + printf("pass"); + else + printf("fail #%d", (uint16_t)le16_to_cpu(fw_act_history_entry->entry[i].result)); + + printf("\n"); + } + } + else + { + struct wdc_fw_act_history_log_entry *fw_act_history_entry = (struct wdc_fw_act_history_log_entry *)(data + sizeof(struct wdc_fw_act_history_log_hdr)); + + for (i = 0; i < num_entries; i++) { + memcpy(previous_fw, (char *)&(fw_act_history_entry->previous_fw_version), 8); + if (strlen((char *)&(fw_act_history_entry->new_fw_version)) > 1) + memcpy(new_fw, (char *)&(fw_act_history_entry->new_fw_version), 8); + else + memcpy(new_fw, null_fw, 8); + + printf("%5"PRIu32"", (uint32_t)le32_to_cpu(fw_act_history_entry->entry_num)); + printf(" "); + printf("%02d:%02d:%02d", (int)(le64_to_cpu(fw_act_history_entry->power_on_seconds)/3600), + (int)((le64_to_cpu(fw_act_history_entry->power_on_seconds)%3600)/60), + (int)(le64_to_cpu(fw_act_history_entry->power_on_seconds)%60)); + printf(" "); + printf("%16"PRIu32"", (uint32_t)le32_to_cpu(fw_act_history_entry->power_cycle_count)); + printf(" "); + printf("%s", (char *)previous_fw); + printf(" "); + printf("%s", (char *)new_fw); + printf(" "); + printf("%2"PRIu8"", (uint8_t)fw_act_history_entry->slot_number); + printf(" "); + wdc_get_commit_action_bin(fw_act_history_entry->commit_action_type,(char *)&commit_action_bin); + printf(" %s", (char *)commit_action_bin); + printf(" "); + if (le16_to_cpu(fw_act_history_entry->result) == 0) + printf("pass"); + else + printf("fail #%d", (uint16_t)le16_to_cpu(fw_act_history_entry->result)); + + printf("\n"); + + fw_act_history_entry++; + } } } -static void wdc_print_fw_act_history_log_json(struct wdc_fw_act_history_log_entry *fw_act_history_entry, - int num_entries) +static void wdc_print_fw_act_history_log_json(__u8 *data, int num_entries) { struct json_object *root; int i; @@ -3733,41 +3804,83 @@ static void wdc_print_fw_act_history_log_json(struct wdc_fw_act_history_log_entr root = json_create_object(); - for (i = 0; i < num_entries; i++) { - memcpy(previous_fw, (char *)&(fw_act_history_entry->previous_fw_version), 8); - if (strlen((char *)&(fw_act_history_entry->new_fw_version)) > 1) - memcpy(new_fw, (char *)&(fw_act_history_entry->new_fw_version), 8); - else - memcpy(new_fw, null_fw, 8); - - json_object_add_value_int(root, "Entry", - le32_to_cpu(fw_act_history_entry->entry_num)); - - sprintf((char *)time_str, "%02d:%02d:%02d", (int)(le64_to_cpu(fw_act_history_entry->power_on_seconds)/3600), - (int)((le64_to_cpu(fw_act_history_entry->power_on_seconds)%3600)/60), - (int)(le64_to_cpu(fw_act_history_entry->power_on_seconds)%60)); - json_object_add_value_string(root, "Power on Hour", time_str); - - json_object_add_value_int(root, "Power Cycle Count", - le32_to_cpu(fw_act_history_entry->power_cycle_count)); - json_object_add_value_string(root, "Previous Firmware", - previous_fw); - json_object_add_value_string(root, "New Firmware", - new_fw); - json_object_add_value_int(root, "Slot", - fw_act_history_entry->slot_number); - - wdc_get_commit_action_bin(fw_act_history_entry->commit_action_type,(char *)&commit_action_bin); - json_object_add_value_string(root, "Action", commit_action_bin); - - if (le16_to_cpu(fw_act_history_entry->result) == 0) - json_object_add_value_string(root, "Result", "pass"); - else { - sprintf((char *)fail_str, "fail #%d", (int)(le16_to_cpu(fw_act_history_entry->result))); - json_object_add_value_string(root, "Result", fail_str); + if(data[0] == WDC_NVME_GET_FW_ACT_HISTORY_C2_LOG_ID) { + struct wdc_fw_act_history_log_format_c2 *fw_act_history_entry = (struct wdc_fw_act_history_log_format_c2 *)(data); + + for (i = 0; i < num_entries; i++) { + memcpy(previous_fw, (char *)&(fw_act_history_entry->entry[i].previous_fw_version), 8); + if (strlen((char *)&(fw_act_history_entry->entry[i].current_fw_version)) > 1) + memcpy(new_fw, (char *)&(fw_act_history_entry->entry[i].current_fw_version), 8); + else + memcpy(new_fw, null_fw, 8); + + json_object_add_value_int(root, "Entry", + le16_to_cpu(fw_act_history_entry->entry[i].fw_act_hist_entries)); + + sprintf((char *)time_str, "%02d:%02d:%02d", (int)(le64_to_cpu(fw_act_history_entry->entry[i].timestamp)/(1000*3600)), + (int)(((le64_to_cpu(fw_act_history_entry->entry[i].timestamp)/1000)%3600)/60), + (int)((le64_to_cpu(fw_act_history_entry->entry[i].timestamp)/1000)%60)); + json_object_add_value_string(root, "Power on Hour", time_str); + + json_object_add_value_int(root, "Power Cycle Count", + le64_to_cpu(fw_act_history_entry->entry[i].power_cycle_count)); + json_object_add_value_string(root, "Previous Firmware", + previous_fw); + json_object_add_value_string(root, "New Firmware", + new_fw); + json_object_add_value_int(root, "Slot", + fw_act_history_entry->entry[i].slot_number); + + wdc_get_commit_action_bin(fw_act_history_entry->entry[i].commit_action_type,(char *)&commit_action_bin); + json_object_add_value_string(root, "Action", commit_action_bin); + + if (le16_to_cpu(fw_act_history_entry->entry[i].result) == 0) + json_object_add_value_string(root, "Result", "pass"); + else { + sprintf((char *)fail_str, "fail #%d", (int)(le16_to_cpu(fw_act_history_entry->entry[i].result))); + json_object_add_value_string(root, "Result", fail_str); + } } + } + else { + struct wdc_fw_act_history_log_entry *fw_act_history_entry = (struct wdc_fw_act_history_log_entry *)(data + sizeof(struct wdc_fw_act_history_log_hdr)); + + for (i = 0; i < num_entries; i++) { + memcpy(previous_fw, (char *)&(fw_act_history_entry->previous_fw_version), 8); + if (strlen((char *)&(fw_act_history_entry->new_fw_version)) > 1) + memcpy(new_fw, (char *)&(fw_act_history_entry->new_fw_version), 8); + else + memcpy(new_fw, null_fw, 8); + + json_object_add_value_int(root, "Entry", + le32_to_cpu(fw_act_history_entry->entry_num)); + + sprintf((char *)time_str, "%02d:%02d:%02d", (int)(le64_to_cpu(fw_act_history_entry->power_on_seconds)/3600), + (int)((le64_to_cpu(fw_act_history_entry->power_on_seconds)%3600)/60), + (int)(le64_to_cpu(fw_act_history_entry->power_on_seconds)%60)); + json_object_add_value_string(root, "Power on Hour", time_str); + + json_object_add_value_int(root, "Power Cycle Count", + le32_to_cpu(fw_act_history_entry->power_cycle_count)); + json_object_add_value_string(root, "Previous Firmware", + previous_fw); + json_object_add_value_string(root, "New Firmware", + new_fw); + json_object_add_value_int(root, "Slot", + fw_act_history_entry->slot_number); + + wdc_get_commit_action_bin(fw_act_history_entry->commit_action_type,(char *)&commit_action_bin); + json_object_add_value_string(root, "Action", commit_action_bin); + + if (le16_to_cpu(fw_act_history_entry->result) == 0) + json_object_add_value_string(root, "Result", "pass"); + else { + sprintf((char *)fail_str, "fail #%d", (int)(le16_to_cpu(fw_act_history_entry->result))); + json_object_add_value_string(root, "Result", fail_str); + } - fw_act_history_entry++; + fw_act_history_entry++; + } } json_print_object(root, NULL); @@ -4216,21 +4329,19 @@ static int wdc_print_d0_log(struct wdc_ssd_d0_smart_log *perf, int fmt) return 0; } -static int wdc_print_fw_act_history_log(struct wdc_fw_act_history_log_entry *fw_act_history_entries, - int num_entries, - int fmt) +static int wdc_print_fw_act_history_log(__u8 *data, int num_entries, int fmt) { - if (!fw_act_history_entries) { + if (!data) { fprintf(stderr, "ERROR : WDC : Invalid buffer to read fw activate history entries\n"); return -1; } switch (fmt) { case NORMAL: - wdc_print_fw_act_history_log_normal(fw_act_history_entries, num_entries); + wdc_print_fw_act_history_log_normal(data, num_entries); break; case JSON: - wdc_print_fw_act_history_log_json(fw_act_history_entries, num_entries); + wdc_print_fw_act_history_log_json(data, num_entries); break; } return 0; @@ -4805,7 +4916,6 @@ static int wdc_get_fw_act_history(int fd, char *format) int fmt = -1; __u8 *data; struct wdc_fw_act_history_log_hdr *fw_act_history_hdr; - struct wdc_fw_act_history_log_entry *fw_act_history_entry; if (!wdc_check_device(fd)) return -1; @@ -4838,10 +4948,57 @@ static int wdc_get_fw_act_history(int fd, char *format) if (ret == 0) { /* parse the data */ fw_act_history_hdr = (struct wdc_fw_act_history_log_hdr *)(data); - fw_act_history_entry = (struct wdc_fw_act_history_log_entry *)(data + sizeof(struct wdc_fw_act_history_log_hdr)); if (fw_act_history_hdr->num_entries > 0) - ret = wdc_print_fw_act_history_log(fw_act_history_entry, fw_act_history_hdr->num_entries, fmt); + ret = wdc_print_fw_act_history_log(data, fw_act_history_hdr->num_entries, fmt); + else + fprintf(stderr, "INFO : WDC : No entries found in FW Activate History Log Page\n"); + } else { + fprintf(stderr, "ERROR : WDC : Unable to read FW Activate History Log Page data\n"); + ret = -1; + } + + free(data); + return ret; +} + +static int wdc_get_fw_act_history_C2(int fd, char *format) +{ + int ret = 0; + int fmt = -1; + __u8 *data; + struct wdc_fw_act_history_log_format_c2 *fw_act_history_log; + __u32 num_entries = 0; + + if (!wdc_check_device(fd)) + return -1; + + fmt = validate_output_format(format); + if (fmt < 0) { + fprintf(stderr, "ERROR : WDC : invalid output format\n"); + return fmt; + } + + if ((data = (__u8*) malloc(sizeof (__u8) * WDC_FW_ACT_HISTORY_C2_LOG_BUF_LEN)) == NULL) { + fprintf(stderr, "ERROR : WDC : malloc : %s\n", strerror(errno)); + return -1; + } + + memset(data, 0, sizeof (__u8) * WDC_FW_ACT_HISTORY_C2_LOG_BUF_LEN); + + ret = nvme_get_log(fd, 0xFFFFFFFF, WDC_NVME_GET_FW_ACT_HISTORY_C2_LOG_ID, + false, WDC_FW_ACT_HISTORY_C2_LOG_BUF_LEN, data); + + if (strcmp(format, "json")) + fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret); + + if (ret == 0) { + /* parse the data */ + fw_act_history_log = (struct wdc_fw_act_history_log_format_c2*)(data); + num_entries = le32_to_cpu(fw_act_history_log->num_entries); + + if (num_entries > 0) + ret = wdc_print_fw_act_history_log(data, num_entries, fmt); else fprintf(stderr, "INFO : WDC : No entries found in FW Activate History Log Page\n"); } else { @@ -4881,16 +5038,50 @@ static int wdc_vs_fw_activate_history(int argc, char **argv, struct command *com return fd; capabilities = wdc_get_drive_capabilities(fd); + if ((capabilities & (WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY | WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY_C2)) == 0) { + fprintf(stderr, "ERROR : WDC: unsupported device for this command\n"); + ret = -1; + goto out; + } - if ((capabilities & WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY) == WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY) { + if (capabilities & WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY) { ret = wdc_get_fw_act_history(fd, cfg.output_format); - if (ret) - fprintf(stderr, "ERROR : WDC : Failure reading the FW Activate History, ret = %d\n", ret); } else { - fprintf(stderr, "ERROR : WDC: unsupported device for this command\n"); - ret = -1; + ret = wdc_get_fw_act_history_C2(fd, cfg.output_format); } + if (ret) + fprintf(stderr, "ERROR : WDC : Failure reading the FW Activate History, ret = %d\n", ret); +out: + return ret; +} + +static int wdc_do_clear_fw_activate_history_vuc(int fd) +{ + int ret = -1; + struct nvme_passthru_cmd admin_cmd; + + memset(&admin_cmd, 0, sizeof (admin_cmd)); + admin_cmd.opcode = WDC_NVME_CLEAR_FW_ACT_HIST_OPCODE; + admin_cmd.cdw12 = ((WDC_NVME_CLEAR_FW_ACT_HIST_SUBCMD << WDC_NVME_SUBCMD_SHIFT) | + WDC_NVME_CLEAR_FW_ACT_HIST_CMD); + + ret = nvme_submit_admin_passthru(fd, &admin_cmd); + fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret); + + return ret; +} + +static int wdc_do_clear_fw_activate_history_fid(int fd) +{ + int ret = -1; + __u32 result; + __u32 value = 1 << 31; /* Bit 31 - Clear Firmware Update History Log */ + + ret = nvme_set_feature(fd, 0, WDC_NVME_CLEAR_FW_ACT_HIST_VU_FID, value, + 0, 0, 0, NULL, &result); + + fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret); return ret; } @@ -4901,7 +5092,6 @@ static int wdc_clear_fw_activate_history(int argc, char **argv, struct command * int fd; int ret = -1; __u64 capabilities = 0; - struct nvme_passthru_cmd admin_cmd; OPT_ARGS(opts) = { OPT_END() @@ -4912,19 +5102,18 @@ static int wdc_clear_fw_activate_history(int argc, char **argv, struct command * return fd; capabilities = wdc_get_drive_capabilities(fd); - if ((capabilities & WDC_DRIVE_CAP_CLEAR_FW_ACT_HISTORY) != WDC_DRIVE_CAP_CLEAR_FW_ACT_HISTORY) { + if ((capabilities & (WDC_DRIVE_CAP_CLEAR_FW_ACT_HISTORY | WDC_DRIVE_CAP_VU_FID_CLEAR_FW_ACT_HISTORY)) == 0) { fprintf(stderr, "ERROR : WDC: unsupported device for this command\n"); ret = -1; goto out; } - memset(&admin_cmd, 0, sizeof (admin_cmd)); - admin_cmd.opcode = WDC_NVME_CLEAR_FW_ACT_HIST_OPCODE; - admin_cmd.cdw12 = ((WDC_NVME_CLEAR_FW_ACT_HIST_SUBCMD << WDC_NVME_SUBCMD_SHIFT) | - WDC_NVME_CLEAR_FW_ACT_HIST_CMD); - - ret = nvme_submit_admin_passthru(fd, &admin_cmd); - fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret); + if (capabilities & WDC_DRIVE_CAP_CLEAR_FW_ACT_HISTORY) { + ret = wdc_do_clear_fw_activate_history_vuc(fd); + } + else { + ret = wdc_do_clear_fw_activate_history_fid(fd); + } out: return ret; -- 2.50.1