// SPDX-License-Identifier: GPL-2.0-or-later
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include "nvme.h"
#include "libnvme.h"
#include "plugin.h"
#include "linux/types.h"
#include "nvme-print.h"

#define CREATE_CMD
#include "ymtc-nvme.h"
#include "ymtc-utils.h"

static void get_ymtc_smart_info(struct nvme_ymtc_smart_log *smart, int index, u8 *nm_val, u8 *raw_val)
{
    memcpy(nm_val, smart->itemArr[index].nmVal, NM_SIZE);
    memcpy(raw_val, smart->itemArr[index].rawVal, RAW_SIZE);
}

static int show_ymtc_smart_log(struct nvme_dev *dev, __u32 nsid,
			       struct nvme_ymtc_smart_log *smart)
{
    struct nvme_id_ctrl ctrl;
    char fw_ver[10];
    int err = 0;

    u8 *nm = malloc(NM_SIZE * sizeof(u8));
    u8 *raw = malloc(RAW_SIZE * sizeof(u8));

    if (!nm) {
        if (raw)
            free(raw);
        return -1;
    }
    if (!raw) {
        free(nm);
        return -1;
    }
    err = nvme_identify_ctrl(dev_fd(dev), &ctrl);
    if (err) {
        free(nm);
        free(raw);
        return err;
    }

    snprintf(fw_ver, sizeof(fw_ver), "%c.%c%c.%c%c%c%c",
        ctrl.fr[0], ctrl.fr[1], ctrl.fr[2], ctrl.fr[3],
        ctrl.fr[4], ctrl.fr[5], ctrl.fr[6]);

    /* Table Title */
    printf("Additional Smart Log for NVME device:%s namespace-id:%x\n",
	   dev->name, nsid);
    /* Clumn Name*/
    printf("key                               normalized raw\n");
    /* 00 SI_VD_PROGRAM_FAIL */
    get_ymtc_smart_info(smart, SI_VD_PROGRAM_FAIL, nm, raw);
    printf("program_fail_count              : %3d%%       %"PRIu64"\n", *nm, int48_to_long(raw));
    /* 01 SI_VD_ERASE_FAIL */
    get_ymtc_smart_info(smart, SI_VD_ERASE_FAIL, nm, raw);
    printf("erase_fail_count                : %3d%%       %"PRIu64"\n", *nm, int48_to_long(raw));
    /* 02 SI_VD_WEARLEVELING_COUNT */
    get_ymtc_smart_info(smart, SI_VD_WEARLEVELING_COUNT, nm, raw);
    printf("wear_leveling                   : %3d%%       min: %u, max: %u, avg: %u\n", *nm,
        *(uint16_t *)raw, *(uint16_t *)(raw+2), *(uint16_t *)(raw+4));
    /* 03 SI_VD_E2E_DECTECTION_COUNT */
    get_ymtc_smart_info(smart, SI_VD_E2E_DECTECTION_COUNT, nm, raw);
    printf("end_to_end_error_detection_count: %3d%%       %"PRIu64"\n", *nm, int48_to_long(raw));
    /* 04 SI_VD_PCIE_CRC_ERR_COUNT */
    get_ymtc_smart_info(smart, SI_VD_PCIE_CRC_ERR_COUNT, nm, raw);
    printf("crc_error_count                 : %3d%%       %"PRIu32"\n", *nm, *(uint32_t *)raw);
    /* 08 SI_VD_THERMAL_THROTTLE_STATUS */
    get_ymtc_smart_info(smart, SI_VD_THERMAL_THROTTLE_STATUS, nm, raw);
    printf("thermal_throttle_status         : %3d%%       %d%%, cnt: %"PRIu32"\n", *nm,
        *raw, *(uint32_t *)(raw+1));
    /* 11 SI_VD_TOTAL_WRITE */
    get_ymtc_smart_info(smart, SI_VD_TOTAL_WRITE, nm, raw);
    printf("nand_bytes_written              : %3d%%       sectors: %"PRIu64"\n", *nm, int48_to_long(raw));
    /* 12 SI_VD_HOST_WRITE */
    get_ymtc_smart_info(smart, SI_VD_HOST_WRITE, nm, raw);
    printf("host_bytes_written              : %3d%%       sectors: %"PRIu64"\n", *nm, int48_to_long(raw));
    /* 14 SI_VD_TOTAL_READ */
    get_ymtc_smart_info(smart, SI_VD_TOTAL_READ, nm, raw);
    printf("nand_bytes_read                 : %3d%%       %"PRIu64"\n", *nm, int48_to_long(raw));
    /* 15 SI_VD_TEMPT_SINCE_BORN */
    get_ymtc_smart_info(smart, SI_VD_TEMPT_SINCE_BORN, nm, raw);
    printf("tempt_since_born                : %3d%%       max: %u, min: %u, curr: %u\n",  *nm,
        *(uint16_t *)raw-273, *(uint16_t *)(raw+2)-273, *(int16_t *)(raw+4)-273);
    /* 16 SI_VD_POWER_CONSUMPTION */
    get_ymtc_smart_info(smart, SI_VD_POWER_CONSUMPTION, nm, raw);
    printf("power_consumption               : %3d%%       max: %u, min: %u, curr: %u\n",  *nm,
        *(uint16_t *)raw, *(uint16_t *)(raw+2), *(uint16_t *)(raw+4));
    /* 17 SI_VD_TEMPT_SINCE_BOOTUP */
    get_ymtc_smart_info(smart, SI_VD_TEMPT_SINCE_BOOTUP, nm, raw);
    printf("tempt_since_bootup              : %3d%%       max: %u, min: %u, curr: %u\n",  *nm,
        *(uint16_t *)raw-273, *(uint16_t *)(raw+2)-273, *(uint16_t *)(raw+4)-273);
    /* 18 SI_VD_POWER_LOSS_PROTECTION */
    get_ymtc_smart_info(smart, SI_VD_POWER_LOSS_PROTECTION, nm, raw);
    printf("power_loss_protection           : %3d%%       %"PRIu64"\n", *nm, int48_to_long(raw));
    /* 19 SI_VD_READ_FAIL */
    get_ymtc_smart_info(smart, SI_VD_READ_FAIL, nm, raw);
    printf("read_fail                       : %3d%%       %"PRIu64"\n", *nm, int48_to_long(raw));
    /* 20 SI_VD_THERMAL_THROTTLE_TIME */
    get_ymtc_smart_info(smart, SI_VD_THERMAL_THROTTLE_TIME, nm, raw);
    printf("thermal_throttle_time           : %3d%%       %u, time: %"PRIu32"\n", *nm,
		*raw, *(uint32_t *)(raw+1));
    /* 21 SI_VD_FLASH_MEDIA_ERROR */
    get_ymtc_smart_info(smart, SI_VD_FLASH_MEDIA_ERROR, nm, raw);
    printf("flash_error_media_count         : %3d%%       %"PRIu64"\n", *nm, int48_to_long(raw));

    free(nm);
    free(raw);

    return err;
}

static int get_additional_smart_log(int argc, char **argv, struct command *cmd, struct plugin *plugin)
{
    struct nvme_ymtc_smart_log smart_log;
    char *desc = "Get Ymtc vendor specific additional smart log (optionally, "\
              "for the specified namespace), and show it.";
    const char *namespace = "(optional) desired namespace";
    const char *raw = "dump output in binary format";
    struct nvme_dev *dev;
    struct config {
        __u32 namespace_id;
        bool  raw_binary;
    };
    int err;

    struct config cfg = {
        .namespace_id = NVME_NSID_ALL,
    };

    OPT_ARGS(opts) = {
        OPT_UINT("namespace-id", 'n', &cfg.namespace_id,  namespace),
        OPT_FLAG("raw-binary",   'b', &cfg.raw_binary,    raw),
        OPT_END()
    };

    err = parse_and_open(&dev, argc, argv, desc, opts);
    if (err)
        return err;

    err = nvme_get_nsid_log(dev_fd(dev), false, 0xca, cfg.namespace_id,
			    sizeof(smart_log), &smart_log);
    if (!err) {
        if (!cfg.raw_binary)
            err = show_ymtc_smart_log(dev, cfg.namespace_id, &smart_log);
        else
            d_raw((unsigned char *)&smart_log, sizeof(smart_log));
    }
    if (err > 0)
        nvme_show_status(err);

    dev_close(dev);
    return err;
}
