/* Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved.
 * openUBMC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

#include <dlfcn.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/time.h>
#include "platform.h"
#include "sml_errcodes.h"
#include "sml.h"
#include "sml_common.h"
// include LSI storelib header files start
#include "lsi/storelib.h"
#include "mfi.h"

#include "sl.h"
#include "sl_pd_log.h"
#include "sl_pd_smart.h"

/* 解决包含mpi2.h、mpi2_cnfg.h的编译问题 */
#define MPI2_POINTER *

#include "lsi/mpi2.h"
#include "lsi/mpi2_cnfg.h"
// include LSI storelib header files end

#include <errno.h>
#include "sl_pd_smart.h"

/* Seagate specified LOG PAGE. */
#define SCSI_SEAGATE_CACHE_LPAGE   0x37
#define SCSI_SEAGATE_FACTORY_LPAGE 0x3e
#define G_GUINT64_HEX_FORMAT "lx"

static PD_VENDOR_NAME_ATTR_ID_S g_vendor_attr_id[] = {
    { PD_VENDOR_NAME_INTEL,   ATA_SMART_ATTRIBUTE_ID_MEDIA_WEAROUT_INDICATOR },         // INTEL
    { PD_VENDOR_NAME_SAMSUNG, ATA_SMART_ATTRIBUTE_ID_WEAR_LEVELING_COUNT },             // SAMSUNG
    { PD_VENDOR_NAME_MICRON,  ATA_SMART_ATTRIBUTE_ID_PERCENT_LIFETIME_REMAINING },      // MICRON
    { PD_VENDOR_NAME_SANDISK, ATA_SMART_ATTRIBUTE_ID_DRIVE_LIFETIME_REMAINNING },       // SANDISK
    { PD_VENDOR_NAME_FUJITSU, 0 },                                                      // FUJITSU
    { PD_VENDOR_NAME_SEAGATE, 0 },                                                      // SEAGATE
    { PD_VENDOR_NAME_WDC,     0 },                                                      // WDC
    { PD_VENDOR_NAME_HITACHI, 0 },                                                      // HGST
    { PD_VENDOR_NAME_SMI,     ATA_SMART_ATTRIBUTE_ID_DRIVE_TEMPERATURE },               // SMI
    {PD_VENDOR_NAME_LITE_ON_ER2,  ATA_SMART_ATTRIBUTE_ID_DRIVE_TEMPERATURE},            // LITEON_ER2
    {PD_VENDOR_NAME_LITE_ON_CVZ,  ATA_SMART_ATTRIBUTE_ID_DRIVE_TEMPERATURE},            // LITEON_CVZ
    {PD_VENDOR_NAME_LITE_ON_EGT,  ATA_SMART_ATTRIBUTE_ID_DRIVE_LIFETIME_REMAINNING},    // LITEON_EGT
    { PD_VENDOR_NAME_LITE_ON, ATA_SMART_ATTRIBUTE_ID_DRIVE_LIFETIME_REMAINNING },       // LITEON
    { PD_VENDOR_NAME_PHISON,  ATA_SMART_ATTRIBUTE_ID_DRIVE_TEMPERATURE },               // PHISON
    { PD_VENDOR_NAME_SSSTC,    ATA_SMART_ATTRIBUTE_ID_DRIVE_TEMPERATURE },              // SSTC
    { PD_VENDOR_NAME_HYNIX,    ATA_SMART_ATTRIBUTE_ID_DRIVE_TEMPERATURE },              // HYNIX
    {PD_VENDOR_NAME_HUAWEI,    ATA_SMART_ATTRIBUTE_ID_DRIVE_TEMPERATURE},               // HUAWEI
    {PD_VENDOR_NAME_YANGTZE_MEMORY,  ATA_SMART_ATTRIBUTE_ID_DRIVE_LIFETIME_REMAINNING}, // YANGTZE MEMORY
    {PD_VENDOR_NAME_RAMAXEL,       ATA_SMART_ATTRIBUTE_ID_DRIVE_TEMPERATURE},           // RAMAXEL
    {PD_VENDOR_NAME_UMIS,          ATA_SMART_ATTRIBUTE_ID_DRIVE_TEMPERATURE},           // UMIS
    {PD_VENDOR_NAME_DERA,          ATA_SMART_ATTRIBUTE_ID_PERCENT_WEAROUT_REMAINING},   // DERA
    {PD_VENDOR_NAME_DATSSD,        ATA_SMART_ATTRIBUTE_ID_MEDIA_WEAROUT_INDICATOR},     // DATSSD
    {PD_VENDOR_NAME_HUADIAN,       ATA_SMART_ATTRIBUTE_ID_AVAILABLE_RESERVED_SPACE},    // HUADIAN SSD
    {PD_VENDOR_NAME_SOLIDIGM, ATA_SMART_ATTRIBUTE_ID_MEDIA_WEAROUT_INDICATOR },         // PD_VENDOR_NAME_SOLIDIGM
    { NULL }                                                                            // 必须放在最后
};

static PD_VENDOR_NAME_ATTR_ID_S g_vendor_spare_block_attr_id[] = {
    {PD_VENDOR_NAME_INTEL,     ATA_SMART_ATTRIBUTE_ID_AVAILABLE_RESERVED_SPACE},        // INTEL
    {PD_VENDOR_NAME_SAMSUNG,   ATA_SMART_ATTRIBUTE_ID_UNUSED_RSVD_BLK_CNT_TOTAL},       // SAMSUNG
    {PD_VENDOR_NAME_HUAWEI,     ATA_SMART_ATTRIBUTE_ID_AVAILABLE_RESERVED_SPACE},       // HUAWEI
    {NULL}                                                                              // 必须放在最后
};

static PD_VENDOR_NAME_ATTR_ID_S g_vendor_nand_written_attr_id[] = {
    {PD_VENDOR_NAME_INTEL,     ATA_SMART_ATTRIBUTE_ID_NAND_WRITTEN},                    // INTEL
    {PD_VENDOR_NAME_SAMSUNG,   ATA_SMART_ATTRIBUTE_ID_FLASH_WRITTEN_OP_COUNT},          // SAMSUNG
    {PD_VENDOR_NAME_HUAWEI,    ATA_SMART_ATTRIBUTE_ID_DRIVE_LIFETIME_REMAINNING},       // HUAWEI
    {NULL}                                                                              // 必须放在最后
};

static PD_VENDOR_NAME_ATTR_ID_S g_vendor_host_written_attr_id[] = {
    {PD_VENDOR_NAME_INTEL,     ATA_SMART_ATTRIBUTE_ID_TOTAL_LBAS_WRITTEN},              // INTEL
    {PD_VENDOR_NAME_SAMSUNG,   ATA_SMART_ATTRIBUTE_ID_TOTAL_LBAS_WRITTEN},              // SAMSUNG
    {PD_VENDOR_NAME_HUAWEI,    ATA_SMART_ATTRIBUTE_ID_TOTAL_LBAS_WRITTEN},              // HUAWEI
    {NULL}                                                                              // 必须放在最后
};

// 获取SAS硬盘的条带温度
static gint32 get_sas_pd_strip_temperature(guint32 ctrl_id, guint16 device_id, guint32 *strip_temperature)
{
    gint32  retval;
    guint8 *buf              = NULL;
    guint32 buf_size         = 0;
    guint32 scsi_status_code = SCSI_STATUS_SUCCESS;
    *strip_temperature       = STORAGE_INFO_INVALID_DWORD;  // indicates invalid

    /* SPC-4: 7.3.21.3 Reference Temperature log parameter. */
    retval =
        SendSCSILogSenseCommand(ctrl_id, device_id, SCSI_LOG_PAGE_TEMPERATURE, 0, &buf, &buf_size, &scsi_status_code);
    if (retval == SML_SUCCESS && buf != NULL) {
        if (buf_size > SCSI_LOG_PAGE_HEADER_SIZE) {
            *strip_temperature = buf[15];  // byte15: Strip Temperature
        }
        g_free(buf);
    }

    return retval;
}

// 获取SAS硬盘的条带温度,传参为SML_PD_SAS_SMART_INFO
static gint32 get_sas_pd_smart_strip_temperature(guint32 ctrl_id, guint16 device_id, SML_PD_SAS_SMART_INFO *data)
{
    return get_sas_pd_strip_temperature(ctrl_id, device_id, &data->strip_temperature);
}

/*
 * Description: 发送 READ DEFECT 命令获取硬盘信息
 */
static gint32 disk_scsi_read_defect_len(guint8 ctrl_id, gint32 device_id, guint8 request, guint32 *pbuf)
{
    /* 参考[SBC-3]SCSI_Block_Commands-sbc3r24.pdf
     * Table 65 — READ DEFECT DATA (10) command
     * Table 67 — READ DEFECT DATA (12) command
     * 根据文档对cdb参数进行初始化 */
    gint32              ret                = 0;
    SL_SCSI_PASSTHRU_T *scsi_passthru_cmd  = NULL;
    size_t              scsi_passthru_len  = 0;
    guint32             defect_list_length = 0;

    if (pbuf == NULL) {
        return SML_ERR_NULL_DATA;
    }

    scsi_passthru_len = sizeof(SL_SCSI_PASSTHRU_T) + SCSI_DEFECT_LIST_HEADER_SIZE;
    scsi_passthru_cmd = (SL_SCSI_PASSTHRU_T *)g_malloc0(scsi_passthru_len);
    if (NULL == scsi_passthru_cmd) {
        return SML_ERR_CANNOT_ALLOC_MEM;
    }

    scsi_passthru_cmd->targetId  = device_id;
    scsi_passthru_cmd->cmd       = SL_CMD_OP_PD_SCSI;
    scsi_passthru_cmd->lun       = 0;
    scsi_passthru_cmd->dir       = SL_DIR_READ;
    scsi_passthru_cmd->timeout   = SCSI_CMD_TIME_OUT;
    scsi_passthru_cmd->cdbLength = SCSI_CDB_LENGTH12;
    // 使用 READ DEFECT 12 获取
    scsi_passthru_cmd->cdb[0] = SCSI_CMD_READ_DEFECT_DATA;
    /* request (CDB第2个 byte) : Reserved (7-5位) REQ_PLIST (第4位) REQ_GLIST (第3位) DEFECT LIST FORMAT (第2-0位) */
    scsi_passthru_cmd->cdb[1]     = request;
    scsi_passthru_cmd->cdb[8]     = 0;                            /* CDB byte 8 for ALLOCATION LENGTH */
    scsi_passthru_cmd->cdb[9]     = SCSI_DEFECT_LIST_HEADER_SIZE; /* CDB byte 9 for ALLOCATION LENGTH */
    scsi_passthru_cmd->dataSize   = SCSI_DEFECT_LIST_HEADER_SIZE;
    scsi_passthru_cmd->scsiStatus = SCSI_STATUS_SUCCESS;

    ret = FireSCSIPassthruCmd(ctrl_id, scsi_passthru_cmd);
    if (ret != SML_SUCCESS) {
        debug_log(DLOG_ERROR,
                  "Failed to Defect List length %d, DeviceId = %d, CtrlId = %d and return 0x%x and scsiStatus: 0x%x",
                  defect_list_length, device_id, SML_CTRL_ID_VALID_BIT(ctrl_id), ret, scsi_passthru_cmd->scsiStatus);
    }

    *pbuf = ((scsi_passthru_cmd->data[4] << 24) | (scsi_passthru_cmd->data[5] << 16) |  // byte 4, 5组成长度信息, 24, 16表示偏移量
             (scsi_passthru_cmd->data[6] << 8) | scsi_passthru_cmd->data[7]) /  // byte 6, 7组成长度信息, 8表示偏移量
            8;  // 每个Address为8个字节, 所以最终个数应该除以8

    g_free(scsi_passthru_cmd);

    debug_log(DLOG_DEBUG, "Defect List length %d, DeviceId = %d, CtrlId = %d", defect_list_length, device_id,
              SML_CTRL_ID_VALID_BIT(ctrl_id));
    return RET_OK;
}

// 获取SAS硬盘的Glist和Plist列表数量
static gint32 get_sas_pd_read_defect_list_len(guint32 ctrl_id, guint16 device_id, guint32 *glist_len,
                                              guint32 *plist_len)
{
    gint32 retval;
    guint8 request = 0x0D;  // 0x0D: 获取glist信息
    *glist_len     = STORAGE_INFO_INVALID_BYTE;
    *plist_len     = STORAGE_INFO_INVALID_BYTE;

    retval = disk_scsi_read_defect_len(ctrl_id, device_id, request, glist_len);
    if (retval == RET_OK) {
        request = 0x15;  // 0x15: 获取plist信息
        retval  = disk_scsi_read_defect_len(ctrl_id, device_id, request, plist_len);
    } else {
        *glist_len = STORAGE_INFO_INVALID_BYTE;
    }
    return retval;
}

// 获取SAS硬盘的Glist列表数量
static gint32 get_sas_pd_read_defect_glist_len(guint32 ctrl_id, guint16 device_id, SML_PD_SAS_SMART_INFO *data)
{
    gint32 retval;
    guint8 request = 0x0D;  // 0x0D: 获取glist信息
    retval = disk_scsi_read_defect_len(ctrl_id, device_id, request, &data->glist_len);
    if (retval != RET_OK) {
        data->glist_len = STORAGE_INFO_INVALID_BYTE;
    }
    return retval;
}

// 获取SAS硬盘的Plist列表数量
static gint32 get_sas_pd_read_defect_plist_len(guint32 ctrl_id, guint16 device_id, SML_PD_SAS_SMART_INFO *data)
{
    gint32 retval;
    guint8 request = 0x15;  // 0x15: 获取plist信息
    retval = disk_scsi_read_defect_len(ctrl_id, device_id, request, &data->plist_len);
    if (retval != RET_OK) {
        data->plist_len = STORAGE_INFO_INVALID_BYTE;
    }
    return retval;
}

// 获取SAS硬盘的生产日期
static gint32 get_sas_pd_manufacture_data(guint32 ctrl_id, guint16 device_id, gchar *manufacture_data,
                                          guint8 manufacture_data_len)
{
    gint32  retval;
    guint8 *buf      = NULL;
    guint32 buf_size = 0;
    gint32  len, k, pc;
    gint32  extra = 0;
    guint8 *ucp   = NULL;
    guint8  weekManufactured[4];
    guint8  yearManufactured[8];

    retval =
        SendSCSILogSenseCommand(ctrl_id, device_id, SCSI_LOG_PAGE_STARTSTOP_CYCLE_COUNTER, 0, &buf, &buf_size, NULL);
    if (retval != RET_OK) {
        debug_log(DLOG_ERROR, "Failed to get Log Sense");
        return retval;
    }

    /* PAGE LENGTH */
    len = ((buf[2] << 8) | buf[3]);  // 2,3字节对应page len, 8:单个byte的长度
    ucp = buf + 4;                   // 4：offset
    for (k = len; k > 0; k -= extra, ucp += extra) {
        if (k <= 3) {  // 3：min len
            g_free(buf);
            return RET_ERR;
        }

        /* PARAMETER LENGTH and PARAMETER CODE. */
        extra = ucp[0x3] + 4;                      // 4:offset
        pc    = (gint32)((ucp[0] << 8) + ucp[1]);  // 8:单个byte的长度

        if (pc == 1 && extra == 10) {  // 10: extra
            /* 4：ucp offset, 4:len */
            retval = memcpy_s(yearManufactured, sizeof(yearManufactured), ucp + 4, 4);
            if (retval != RET_OK) {
                yearManufactured[0] = 0;
            }

            /* 8：ucp offset, 2:len */
            retval = memcpy_s(weekManufactured, sizeof(weekManufactured), ucp + 8, 2);
            if (retval != RET_OK) {
                weekManufactured[0] = 0;
            }
            break;
        }
    }

    retval = snprintf_s(manufacture_data, manufacture_data_len, SL_MAX_MANUFACTURE_DATA_LEN - 1,
                        "in week %.2s of year %.4s", weekManufactured, yearManufactured);
    if (retval <= 0) {
        debug_log(DLOG_ERROR, "snprintf_s failed");
    }

    g_free(buf);
    return RET_OK;
}

// 获取SAS硬盘的生产日期，参数为SML_PD_SAS_SMART_INFO类型
static gint32 get_sas_pd_smart_manufacture_data(guint32 ctrl_id, guint16 device_id, SML_PD_SAS_SMART_INFO *data)
{
    return get_sas_pd_manufacture_data(ctrl_id, device_id, data->manufacture_data, SL_MAX_MANUFACTURE_DATA_LEN);
}

// 解析SAS硬盘发送到启动器和从启动器接收到的块(仅针对SEAGATE物理盘)
static void get_sas_pd_block_phase(guint8 *p_buf, guint32 buff_len, guint32 *data, guint8 target_pc)
{
    gint32        k, j, num, pl, pc, len, length_data;
    const guint8 *ucp = NULL;
    const guint8 *xp  = NULL;
    guint64        ull;
    guint32        temp;

    (void)buff_len;
    temp = (guint32)(p_buf[0x2] & 0xFF);
    len  = ((temp << 8) | p_buf[0x3]) + 4;  // 4:offset, 8:offset

    num = (len - 4);      /* PAGE LENGTH. 4:offset */
    ucp = &p_buf[0] + 4;  // 4:offset

    while (num > 3) {  // 3:iteration
        pc = (ucp[0] << 8) | ucp[1]; // 8:offset
        pl = ucp[0x3] + 4;  // 4:offset

        k  = pl - 4;   // 4:offset
        xp = ucp + 4;  // 4:offset

        length_data = (gint32)sizeof(ull);
        if (k > length_data) {
            xp += (k - length_data);
            k = length_data;
        }

        ull = 0;
        for (j = 0; j < k; j++) {
            ull = (j > 0) ? (ull << 8) : ull; // 8:offset
            ull |= xp[j];
        }

        num -= pl;
        ucp += pl;

        if (pc == target_pc) {
            *data = ull;
        }
    }
    return;
}

// 获取SAS硬盘发送到启动器和从启动器接收到的块(仅针对SEAGATE物理盘)
static gint32 get_sas_pd_block(guint32 ctrl_id, guint16 device_id, guint32 *sent, guint32 *received)
{
    gint32  retval;
    guint8 *buf      = NULL;
    guint32 buf_size = 0;

    retval = SendSCSILogSenseCommand(ctrl_id, device_id, SCSI_SEAGATE_CACHE_LPAGE, 0, &buf, &buf_size, NULL);
    if (retval != RET_OK) {
        debug_log(DLOG_ERROR, "Get cache statistics page failed!");
        return retval;
    }

    if ((buf[0] & 0x3f) != SCSI_SEAGATE_CACHE_LPAGE) { /* 先判断返回的PAGE CODE是否正确 */
        debug_log(DLOG_ERROR, "Seagate Cache Log Sense Failed, page mismatch");
        g_free(buf);
        return retval;
    }

    get_sas_pd_block_phase(buf, buf_size, sent, 0);
    get_sas_pd_block_phase(buf, buf_size, received, 1);
    g_free(buf);
    return RET_OK;
}

// 数据获取与校验函数
static gint32 get_sas_cache_page(guint32 ctrl_id, guint16 device_id, guint8 **buf, guint32 *buf_size)
{
    gint32 retval = SendSCSILogSenseCommand(ctrl_id, device_id, SCSI_SEAGATE_CACHE_LPAGE, 0, buf, buf_size, NULL);
    if (retval != RET_OK) {
        debug_log(DLOG_ERROR, "Get cache statistics page failed!");
        return retval;
    }

    if (((*buf)[0] & 0x3f) != SCSI_SEAGATE_CACHE_LPAGE) {
        debug_log(DLOG_ERROR, "Seagate Cache Log Sense Failed, page mismatch");
        g_free(*buf);
        return RET_ERR;
    }
    return RET_OK;
}

// 发送块数获取函数
static gint32 get_sas_pd_block_sent(guint32 ctrl_id, guint16 device_id, SML_PD_SAS_SMART_INFO *data)
{
    guint8* buf = NULL;
    guint32 buf_size = 0;
    
    gint32 ret = get_sas_cache_page(ctrl_id, device_id, &buf, &buf_size);
    if (ret != RET_OK) {
        return ret;
    }
    
    get_sas_pd_block_phase(buf, buf_size, &data->blocks_sent, 0);
    g_free(buf);
    return RET_OK;
}

// 接收块数获取函数
static gint32 get_sas_pd_block_received(guint32 ctrl_id, guint16 device_id, SML_PD_SAS_SMART_INFO *data)
{
    guint8* buf = NULL;
    guint32 buf_size = 0;
    
    gint32 ret = get_sas_cache_page(ctrl_id, device_id, &buf, &buf_size);
    if (ret != RET_OK) {
        return ret;
    }
    get_sas_pd_block_phase(buf, buf_size, &data->blocks_received, 1);
    g_free(buf);
    return RET_OK;
}


// 获取SAS硬盘距下一次内部SMART测试的时长(分钟)(仅针对SEAGATE物理盘)
static gint32 get_sas_pd_until_next_inter_smart_test_minutes(guint32 ctrl_id, guint16 device_id,
                                                             guint32 *minutes_left)
{
    gint32  retval;
    guint8 *buf      = NULL;
    guint32 buf_size = 0;

    retval = SendSCSILogSenseCommand(ctrl_id, device_id, SCSI_SEAGATE_FACTORY_LPAGE, 0, &buf, &buf_size, NULL);
    if (retval != RET_OK) {
        debug_log(DLOG_ERROR, "Get factory log page failed!");
        return retval;
    }

    if ((buf[0] & 0x3f) != SCSI_SEAGATE_FACTORY_LPAGE) { /* 先判断返回的PAGE CODE是否正确 */
        debug_log(DLOG_ERROR, "Seagate Factory Log Sense Failed, page mismatch");
        g_free(buf);
        return retval;
    }

    get_sas_pd_block_phase(buf, buf_size, minutes_left, 8); // 8:flag
    g_free(buf);
    return RET_OK;
}

// 获取SAS硬盘距下一次内部SMART测试的时长(分钟)(仅针对SEAGATE物理盘),参数为SML_PD_SAS_SMART_INFO类型
static gint32 get_sas_pd_smart_until_next_inter_smart_test_minutes(
    guint32 ctrl_id, guint16 device_id, SML_PD_SAS_SMART_INFO *data)
{
    return get_sas_pd_until_next_inter_smart_test_minutes(ctrl_id, device_id, &data->minutes_left);
}

// 获取硬盘的SMART信息
gint32 lsi_get_pd_smart_info(guint32 ctrl_id, guint16 device_id, SML_PD_SAS_SMART_INFO *data)
{
    guint8 lp_support = FALSE;
    gint32 retval;

    retval = get_sas_pd_strip_temperature(ctrl_id, device_id, &(data->strip_temperature));
    if (retval != SML_SUCCESS) {
        debug_log(DLOG_ERROR, "Failed to get strip temperature, and return 0x%x", retval);
    }

    retval = get_sas_pd_read_defect_list_len(ctrl_id, device_id, &(data->glist_len), &(data->plist_len));
    if (retval != SML_SUCCESS) {
        debug_log(DLOG_ERROR, "Failed to get read defect list, and return 0x%x", retval);
    }

    retval = get_sas_pd_manufacture_data(ctrl_id, device_id, data->manufacture_data, SL_MAX_MANUFACTURE_DATA_LEN);
    if (retval != SML_SUCCESS) {
        debug_log(DLOG_ERROR, "Failed to get manufacture data, and return 0x%x", retval);
    }

    // 判断是否是希捷的硬盘设备
    retval = GetSCSIDeviceSupportLogPages(ctrl_id, device_id, SCSI_SEAGATE_CACHE_LPAGE, &lp_support);
    if (retval != SML_SUCCESS) {
        return retval;
    }

    if (lp_support == FALSE) {
        debug_log(DLOG_DEBUG, "do not support seagate pages, and return 0x%x", retval);
        data->blocks_sent     = STORAGE_INFO_INVALID_DWORD;
        data->blocks_received = STORAGE_INFO_INVALID_DWORD;
        data->minutes_left  = STORAGE_INFO_INVALID_DWORD;
        return RET_OK;
    }

    retval = get_sas_pd_block(ctrl_id, device_id, &(data->blocks_sent), &(data->blocks_received));
    if (retval != SML_SUCCESS) {
        debug_log(DLOG_ERROR, "Failed to get blocks information, and return 0x%x", retval);
    }

    retval = get_sas_pd_until_next_inter_smart_test_minutes(ctrl_id, device_id, &(data->minutes_left));
    if (retval != SML_SUCCESS) {
        debug_log(DLOG_ERROR, "Failed to get until next inter smart test minutes, and return 0x%x", retval);
    }

    return RET_OK;
}

// 初始化SML_PD_SAS_SMART_INFO内容
static void init_smart_info(SML_PD_SAS_SMART_INFO *data)
{
    data->strip_temperature = STORAGE_INFO_INVALID_DWORD;
    data->glist_len = STORAGE_INFO_INVALID_BYTE;
    data->plist_len = STORAGE_INFO_INVALID_BYTE;

    errno_t ret = strcpy_s(data->manufacture_data, sizeof(data->manufacture_data), STORAGE_INFO_INVALID_STRING);
    if (ret != EOK) {
        debug_log(DLOG_ERROR, "Failed to init manufacture data, ret = %d", ret);
    }

    data->blocks_sent = STORAGE_INFO_INVALID_DWORD;
    data->blocks_received = STORAGE_INFO_INVALID_DWORD;
    data->minutes_left = STORAGE_INFO_INVALID_DWORD;
}

// 获取硬盘指定的SMART信息
gint32 lsi_get_pd_smart_info_spec(
    guint32 ctrl_id, guint16 device_id, SML_PD_SAS_SMART_INFO *data, guint8 filter)
{
    init_smart_info(data);
    gint32 retval;
    guint8 lp_support = FALSE;

    SMART_INFO_PROCESSOR processor_list[] = {{SMART_STRIP_TEMPERATURE,
                                                 get_sas_pd_smart_strip_temperature,
                                                 "Failed to get strip temperature, and return 0x%x"},
        {SMART_READ_DEFECT_GLIST_LEN,
            get_sas_pd_read_defect_glist_len,
            "Failed to get read defect glist, and return 0x%x"},
        {SMART_READ_DEFECT_PLIST_LEN,
            get_sas_pd_read_defect_plist_len,
            "Failed to get read defect plist, and return 0x%x"},
        {SMART_MANUATURE_DATA, get_sas_pd_smart_manufacture_data, "Failed to get manufacture data, and return 0x%x"},
        {SMART_BLOCK_SENT, get_sas_pd_block_sent, "Failed to get blocks sent information, and return 0x%x"},
        {SMART_BLOCK_RECEIVED, get_sas_pd_block_received, "Failed to get blocks received information, and return 0x%x"},
        {SMART_UNTIL_NEXT_INTER_SMART_TEST_MINUTES,
            get_sas_pd_smart_until_next_inter_smart_test_minutes,
            "Failed to get until next inter smart test minutes, and return 0x%x"}};

    guint8 arr_len = sizeof(processor_list) / sizeof(processor_list[0]);
    for (guint8 i = 0; i < arr_len; ++i) {
        if (i == 4) {
            // 判断是否是希捷的硬盘设备
            retval = GetSCSIDeviceSupportLogPages(ctrl_id, device_id, SCSI_SEAGATE_CACHE_LPAGE, &lp_support);
            if (retval != SML_SUCCESS) {
                return retval;
            }
            if (lp_support == FALSE) {
                debug_log(DLOG_DEBUG, "do not support seagate pages");
                return RET_OK;
            }
        }
        if ((processor_list[i].mask & filter) != 0 && processor_list[i].process_func != NULL) {
            retval = processor_list[i].process_func(ctrl_id, device_id, data);
            if (retval != SML_SUCCESS) {
                debug_log(DLOG_ERROR, processor_list[i].log_info, retval);
            }
        }
    }
    return RET_OK;
}

/*
 * Description: 从厂商自定义Smart信息中获取指定项的current值
*/
static guint8 GetSmartAttrCurrentByAttrId(ATA_SMART_DATA_S *smart_data, guint8 attr_id)
{
    guint8 attr_current_val = STORAGE_INFO_INVALID_BYTE;
    if (smart_data == NULL) {
        return attr_current_val;
    }
    ATA_SMART_ATTRIBUTE_S *smart_attr = NULL;
    guint8 attr_len = G_N_ELEMENTS(smart_data->smartAttribute);
    for (guint8 idx = 0; idx < attr_len; idx++) {
        smart_attr = &(smart_data->smartAttribute[idx]);
        if (smart_attr->id == attr_id) {
            attr_current_val = smart_attr->current;
            break;
        }
    }
    return attr_current_val;
}

static guint8 GetAttrIdByVendorName(SML_PD_INFO_S *pPDInfo, PD_VENDOR_NAME_ATTR_ID_S *vendor_attr_id)
{
    for (guint8 idx = 0; vendor_attr_id[idx].vendor_name != NULL; idx++) {
        if (strncasecmp(pPDInfo->pdinfo.manufacturer, vendor_attr_id[idx].vendor_name,
            strlen(vendor_attr_id[idx].vendor_name)) == 0 ||
            strstr(pPDInfo->pdinfo.manufacturer, vendor_attr_id[idx].vendor_name) != NULL ||
            strncasecmp(pPDInfo->pdinfo.model, vendor_attr_id[idx].vendor_name,
            strlen(vendor_attr_id[idx].vendor_name)) == 0  ||
            strstr(pPDInfo->pdinfo.model, vendor_attr_id[idx].vendor_name) != NULL) {
            return vendor_attr_id[idx].attr_id;
        }
    }
    return STORAGE_INFO_INVALID_BYTE;
}

static guint32 GetAttrIdWearoutByVendorName(SML_PD_INFO_S *pPDInfo, DRIVE_VENDOR_INFO_EXTEND_S *vendor_attr_id)
{
    if (drive_info_list_lsi_len != 0 && vendor_attr_id != NULL) {
        for (guint8 idx = 0; idx < drive_info_list_lsi_len; idx++) {
            if (strncasecmp(pPDInfo->pdinfo.manufacturer, vendor_attr_id[idx].vendor_name,
                strlen(vendor_attr_id[idx].vendor_name)) == 0 ||
                strstr(pPDInfo->pdinfo.manufacturer, vendor_attr_id[idx].vendor_name) != NULL ||
                strncasecmp(pPDInfo->pdinfo.model,  vendor_attr_id[idx].vendor_name,
                strlen(vendor_attr_id[idx].vendor_name)) == 0 ||
                strstr(pPDInfo->pdinfo.model, vendor_attr_id[idx].vendor_name) != NULL) {
                return vendor_attr_id[idx].attr_id_wear;
            }
        }
    }
    return STORAGE_INFO_INVALID_BYTE;
}

/*
 * Description: 从厂商自定义Smart信息中获取SATA接口SSD硬盘的磨损情况, 即被擦写次数的剩余百分比，
 *              百分比从100到1的线性下降就相当于平均擦写次数从0到最大值
 * History: 2017年12月25日  AR.SR.SFEA02109385.002.001
*/
static guint8 GetSATARemnantMediaWearOutByVendor(SML_PD_INFO_S *pPDInfo)
{
    guint8 media_wearout = STORAGE_INFO_INVALID_BYTE; // 0xFF indicates not support
    ATA_SMART_DATA_S *smart_data = (ATA_SMART_DATA_S *)pPDInfo->smartinfo.SATADevice.smart_data;
    guint8 wearout_attr_id = GetAttrIdByVendorName(pPDInfo, g_vendor_attr_id);
    if (wearout_attr_id == STORAGE_INFO_INVALID_BYTE) {
        wearout_attr_id = GetAttrIdWearoutByVendorName(pPDInfo, drive_info_list_lsi);
    }
    if (wearout_attr_id != STORAGE_INFO_INVALID_BYTE) {
        media_wearout = GetSmartAttrCurrentByAttrId(smart_data, wearout_attr_id);
    }

    return media_wearout;
}
/*
 * Description: 从厂商自定义Smart信息中获取指定项的Raw值
*/
static guint64 GetVendorSmartAttrRawByAttrId(SML_PD_INFO_S *pPDInfo, guint8 attr_id)
{
    guint64 attr_raw_val = STORAGE_INFO_INVALID_DWORD; // indicates not support
    ATA_SMART_DATA_S *smart_data = (ATA_SMART_DATA_S *)pPDInfo->smartinfo.SATADevice.smart_data;

    guint8 attr_len = G_N_ELEMENTS(smart_data->smartAttribute);
    ATA_SMART_ATTRIBUTE_S *smart_attr;

    for (guint8 idx = 0; idx < attr_len; idx++) {
        smart_attr = &(smart_data->smartAttribute[idx]);
        if (smart_attr->id == attr_id) {
            // Smart信息中的RAW值为6个bytes, 一次全部捞取, 高两个bytes需要填充0。在使用的时候进行截断操作。
            guint32 raw_h = MAKE_DWORD(0, 0, smart_attr->raw[5], smart_attr->raw[4]);   // 取raw值的第4、5个byte
            // 取raw值的第0、1、2、3 bytes进行拼接
            guint32 raw_l = MAKE_DWORD(smart_attr->raw[3], smart_attr->raw[2], smart_attr->raw[1], smart_attr->raw[0]);
            attr_raw_val = (((guint64)raw_h << 32) | ((guint64)raw_l)); // 右移32位
            break;
        }
    }

    return attr_raw_val;
}

/*
 * Description: 从华为自定义Smart信息中获取指定项的Raw值
*/
static guint64 GetHWDefinedSmartAttrRawById(SML_PD_INFO_S *pPDInfo, guint8 attr_id)
{
    guint64 attr_raw_val = STORAGE_INFO_INVALID_QWORD; // indicates not support
    ATA_SMART_DATA_HW_DEFINED_S *smart_data =
        (ATA_SMART_DATA_HW_DEFINED_S *)pPDInfo->hw_defined_smartinfo.smart_data;

    guint16 attr_len = G_N_ELEMENTS(smart_data->smartAttribute);
    for (guint16 idx = 0; idx < attr_len; idx++) {
        ATA_SMART_ATTRIBUTE_S *smart_attr = &(smart_data->smartAttribute[idx]);
        if (smart_attr->id == attr_id) {
            // Smart信息中的RAW值为6个bytes, 一次全部捞取, 高两个bytes需要填充0。在使用的时候进行截断操作。
            guint32 raw_h = MAKE_DWORD(0, 0, smart_attr->raw[5], smart_attr->raw[4]);
            // 取raw值的第0、1、2、3 bytes进行拼接
            guint32 raw_l = MAKE_DWORD(smart_attr->raw[3], smart_attr->raw[2], smart_attr->raw[1], smart_attr->raw[0]);
            attr_raw_val = (((guint64)raw_h << 32) | ((guint64)raw_l)); // 右移32位
            break;
        }
    }

    return attr_raw_val;
}

/*
 * Description: 从厂商自定义Smart信息中获取SATA接口SSD硬盘的磨损情况
*/
static guint64 GetSATANandFlashWrittenByVendor(SML_PD_INFO_S *pPDInfo)
{
    guint64 nand_written = STORAGE_INFO_INVALID_QWORD; // indicates not support
    guint8 attr_id = GetAttrIdByVendorName(pPDInfo, g_vendor_nand_written_attr_id);
    if (attr_id != STORAGE_INFO_INVALID_BYTE) {
        nand_written = GetVendorSmartAttrRawByAttrId(pPDInfo, attr_id);
    }

    return nand_written;
}

/*
 * Description: 从厂商自定义Smart信息中获取SATA接口SSD硬盘的磨损情况
*/
static guint64 GetSATAHostWrittenByVendor(SML_PD_INFO_S *pPDInfo)
{
    guint64 host_written = STORAGE_INFO_INVALID_QWORD; // indicates not support
    guint8 attr_id = GetAttrIdByVendorName(pPDInfo, g_vendor_host_written_attr_id);
    if (attr_id != STORAGE_INFO_INVALID_BYTE) {
        host_written = GetVendorSmartAttrRawByAttrId(pPDInfo, attr_id);
    }

    return host_written;
}

/*
 * Description: 从厂商自定义Smart信息中获取SATA接口SSD硬盘的有效冗余块情况
 *              即剩余有效冗余块占出厂时冗余块的百分比
 * Note：厂商自定义Smart信息中只能获取到TLC(用户区)的有效冗余块信息，故设置SLC(非用户)为无效值
*/
static guint8 GetSATASpareBlockByVendor(SML_PD_INFO_S *pPDInfo)
{
    guint8 tlc_spare_block = STORAGE_INFO_INVALID_BYTE; // 0xFF indicates not support
    ATA_SMART_DATA_S *smart_data = (ATA_SMART_DATA_S *)pPDInfo->smartinfo.SATADevice.smart_data;

    guint8 spare_block_attr_id = GetAttrIdByVendorName(pPDInfo, g_vendor_spare_block_attr_id);
    if (spare_block_attr_id != STORAGE_INFO_INVALID_BYTE) {
        tlc_spare_block = GetSmartAttrCurrentByAttrId(smart_data, spare_block_attr_id);
    }

    return tlc_spare_block;
}

/*
 * Description: 从华为自定义Smart信息中获取SATA接口SSD硬盘的有效冗余块情况
*/
static guint8 GetSATASpareBlockByHWDefined(guint8 valid_id, guint8 total_id, SML_PD_INFO_S *pPDInfo)
{
    guint8 spare_block_percent = STORAGE_INFO_INVALID_BYTE;
    guint32 valid_spare_block = STORAGE_INFO_INVALID_DWORD;
    guint32 total_spare_block = STORAGE_INFO_INVALID_DWORD;
    ATA_SMART_DATA_HW_DEFINED_S *smart_data =
        (ATA_SMART_DATA_HW_DEFINED_S *)pPDInfo->hw_defined_smartinfo.smart_data;
    guint16 attr_len = G_N_ELEMENTS(smart_data->smartAttribute);
    for (guint16 idx = 0; idx < attr_len; idx++) {
        ATA_SMART_ATTRIBUTE_S *smart_attr = &(smart_data->smartAttribute[idx]);
        if (smart_attr->id == valid_id) {
            valid_spare_block = MAKE_DWORD(0, 0, smart_attr->raw[1], smart_attr->raw[0]);
        }
        if (smart_attr->id == total_id) {
            total_spare_block = MAKE_DWORD(0, 0, smart_attr->raw[1], smart_attr->raw[0]);
        }
        if (valid_spare_block != STORAGE_INFO_INVALID_DWORD && total_spare_block != STORAGE_INFO_INVALID_DWORD) {
            break;  // 当都已经匹配到的时候直接退出, 不在遍历后边的smart项
        }
    }

    if (valid_spare_block != STORAGE_INFO_INVALID_DWORD && total_spare_block != STORAGE_INFO_INVALID_DWORD &&
        total_spare_block != 0) {
        spare_block_percent = (valid_spare_block * ONE_HUNDRED_PERCENT) / total_spare_block; // 转换为百分比
    }
    return spare_block_percent;
}

/*
 * Description: 获取SATA接口SSD硬盘的有效冗余块情况
 * Note: 当硬盘支持华为自定义Smart时，优先使用自定义Smart信息获取用户区与用户区的有效冗余块情况
         当不支持华为自定义时，从厂商自定义Smart信息中各wearout的smart_id获取
*/
static void GetSATASpareBlockPercent(SML_PD_INFO_S *pPDInfo)
{
    guint8 slc_spare_block_percent = STORAGE_INFO_INVALID_BYTE;
    guint8 tlc_spare_block_percent = STORAGE_INFO_INVALID_BYTE;

    if (pPDInfo->hw_defined_smartinfo.valid_flag == TRUE) {
        slc_spare_block_percent = GetSATASpareBlockByHWDefined(HW_DEFINED_SMART_ATTRIBUTE_ID_SLC_VALID_SPARE_BLOCK,
            HW_DEFINED_SMART_ATTRIBUTE_ID_SLC_TOTAL_SPARE_BLOCK, pPDInfo);
        tlc_spare_block_percent = GetSATASpareBlockByHWDefined(HW_DEFINED_SMART_ATTRIBUTE_ID_TLC_VALID_SPARE_BLOCK,
            HW_DEFINED_SMART_ATTRIBUTE_ID_TLC_TOTAL_SPARE_BLOCK, pPDInfo);
        debug_log(DLOG_INFO, "Smart from HW defined, SLC spare block percent = %u, TLC spare block percent = %u",
            slc_spare_block_percent, tlc_spare_block_percent);
    } else if (pPDInfo->smartinfo.valid_flag == TRUE) {
        // 由于厂商自定义的Smart信息只有用户区冗余块剩余比例，故非用户区的直接赋值无效值
        tlc_spare_block_percent = GetSATASpareBlockByVendor(pPDInfo);
        debug_log(DLOG_INFO, "Smart from vendor, TLC spare block percent = %u", tlc_spare_block_percent);
    }

    pPDInfo->pdinfo.spare_block.slc_value = slc_spare_block_percent;
    pPDInfo->pdinfo.spare_block.tlc_value = tlc_spare_block_percent;
}

/*
 * Description: 获取SSD硬盘的有效冗余块情况
*/
static void GetSSDSpareBlockPercent(SML_PD_INFO_S *pPDInfo)
{
    pPDInfo->pdinfo.spare_block.slc_value = STORAGE_INFO_INVALID_BYTE;
    pPDInfo->pdinfo.spare_block.tlc_value = STORAGE_INFO_INVALID_BYTE;

    if (pPDInfo->pdinfo.media_type == MR_PD_MEDIA_TYPE_SSD) {
        if (pPDInfo->pdinfo.interface_type == PD_INTERFACE_TYPE_SATA) {
            // SATA SSD盘
            GetSATASpareBlockPercent(pPDInfo);
        }
    }
}

/*
 * Description: 更新有效冗余块百分比
*/
gint32 GetSpareBlockPercent(SML_PD_INFO_S *pPDinfo)
{
    guint32 cur_timestamp = (guint32)vos_tick_get(); // 获取时间为毫秒级
    guint32 interval;

    if (cur_timestamp >= pPDinfo->pdinfo.spare_block.last_update_timestamp) {
        interval = cur_timestamp - pPDinfo->pdinfo.spare_block.last_update_timestamp;
    } else {
        interval = cur_timestamp + (G_MAXUINT32 - pPDinfo->pdinfo.spare_block.last_update_timestamp);
    }
    // 24小时更新一次
    if (interval > UPDATE_INTERVAL_24_HOURS || pPDinfo->pdinfo.spare_block.last_update_timestamp == 0 ||
        pPDinfo->pdinfo.spare_block.slc_value == 0 || pPDinfo->pdinfo.spare_block.tlc_value == 0) {
        GetSSDSpareBlockPercent(pPDinfo);
        pPDinfo->pdinfo.spare_block.last_update_timestamp = cur_timestamp;
    }
    return SML_SUCCESS;
}

/*
 * Description: 从华为自定义Smart信息中获取SATA接口SSD硬盘的磨损情况, 即被擦写次数的剩余百分比，
 *              百分比从100到1的线性下降就相当于平均擦写次数从0到最大值
*/
static guint8 GetSATARemnantMediaWearOutByHWDefined(SML_PD_INFO_S *pPDInfo)
{
    guint8 media_wearout = STORAGE_INFO_INVALID_BYTE;
    guint8 slc_wearout_val = STORAGE_INFO_INVALID_BYTE;
    guint8 tlc_wearout_val = STORAGE_INFO_INVALID_BYTE;
    guint8 slc_wearout_spare_val = STORAGE_INFO_INVALID_BYTE;
    guint8 tlc_wearout_spare_val = STORAGE_INFO_INVALID_BYTE;
    ATA_SMART_DATA_HW_DEFINED_S *smart_data =
        (ATA_SMART_DATA_HW_DEFINED_S *)pPDInfo->hw_defined_smartinfo.smart_data;
    guint16 attr_len = G_N_ELEMENTS(smart_data->smartAttribute);

    for (guint16 idx = 0; idx < attr_len; idx++) {
        ATA_SMART_ATTRIBUTE_S *smart_attr = &(smart_data->smartAttribute[idx]);
        if (smart_attr->id == HW_DEFINED_SMART_ATTRIBUTE_ID_SLC_WEAROUT) {
            slc_wearout_val = smart_attr->raw[0];
            debug_log(DLOG_INFO, "SATA SLC Remnant Media WearOut raw = %u", smart_attr->raw[0]);
        }
        if (smart_attr->id == HW_DEFINED_SMART_ATTRIBUTE_ID_TLC_WEAROUT) {
            tlc_wearout_val = smart_attr->raw[0];
            debug_log(DLOG_INFO, "SATA TLC Remnant Media WearOut raw = %u", smart_attr->raw[0]);
        }
        // 支持从0x4E,0x4F获取静态寿命，由于不能保证id的排序，获取到0x4E,0x4F后不停止遍历
        if (smart_attr->id == HW_DEFINED_SMART_ATTRIBUTE_ID_SLC_LIFESPAN) {
            slc_wearout_spare_val = smart_attr->current;
            debug_log(DLOG_INFO, "SATA SLC Remnant Media WearOut current = %u", smart_attr->current);
        }
        if (smart_attr->id == HW_DEFINED_SMART_ATTRIBUTE_ID_TLC_LIFESPAN) {
            tlc_wearout_spare_val = smart_attr->current;
            debug_log(DLOG_INFO, "SATA TLC Remnant Media WearOut current = %u", smart_attr->current);
        }
        if (slc_wearout_val != STORAGE_INFO_INVALID_BYTE && tlc_wearout_val != STORAGE_INFO_INVALID_BYTE) {
            break;  // 当都已经匹配到的时候直接退出, 不在遍历后边的smart项
        }
    }
 
    if (slc_wearout_spare_val != STORAGE_INFO_INVALID_BYTE || tlc_wearout_spare_val != STORAGE_INFO_INVALID_BYTE) {
        media_wearout = slc_wearout_spare_val > tlc_wearout_spare_val ? tlc_wearout_spare_val : slc_wearout_spare_val;
    }
    // 优先采用0x5C和0x69的数据
    if (slc_wearout_val != STORAGE_INFO_INVALID_BYTE || tlc_wearout_val != STORAGE_INFO_INVALID_BYTE) {
        media_wearout = slc_wearout_val > tlc_wearout_val ? tlc_wearout_val : slc_wearout_val;
    }
 
    return media_wearout;
}

/*
 * Description: 获取SATA接口SSD硬盘的磨损情况
 * Note: 当硬盘支持华为自定义Smart时，优先使用自定义Smart信息获取剩余磨损率
         当不支持华为自定义时，从厂商自定义Smart信息中各wearout的smart_id获取
*/
static guint8 GetSATARemnantMediaWearOut(SML_PD_INFO_S *pPDInfo)
{
    guint8 media_wearout = STORAGE_INFO_INVALID_BYTE;

    if (pPDInfo->hw_defined_smartinfo.valid_flag == TRUE) {
        media_wearout = GetSATARemnantMediaWearOutByHWDefined(pPDInfo);
    } else if (pPDInfo->smartinfo.valid_flag == TRUE) {
        media_wearout = GetSATARemnantMediaWearOutByVendor(pPDInfo);
    }
 
    return media_wearout;
}

/*
 * Description: 获取SAS接口SSD硬盘的磨损情况, 即被擦写次数的剩余百分比，百分比从100到1
 *              的线性下降就相当于平均擦写次数从0到最大值
 * History: 2017年12月25日  AR.SR.SFEA02109385.002.001
*/
static guint8 GetSASRemnantMediaWearOut(guint32 ctrl_id, guint16 device_id, SML_PD_INFO_S *pPDInfo)
{
    gint32 retval = SML_SUCCESS;
    guint8 media_wearout = 0xFF; // 0xFF indicates not support
    guint8 *buf = NULL;
    guint32 buf_size = 0;
    guint16 page_length = 0;
    guint16 page_param_code = 0;
    guint16 offset = 0;
    guint16 percent_used_endurance = 0;
#define PERCENTAGE_USED_ENDURANCE_INDICATOR_PARAM_LENGTH 8
#define PERCENTAGE_USED_ENDURANCE_INDICATOR_PARAM_CODE 0x0001

    // 如果当前值不等于0，即之前有获取到过，先采用之前获取的值
    if (pPDInfo->pdinfo.remnant_media_wearout) {
        media_wearout = pPDInfo->pdinfo.remnant_media_wearout;
    }
 
    if (pPDInfo->pdinfo.power_state != PD_POWER_STATE_ACTIVE) {
        return media_wearout;
    }
    retval = SendSCSILogSenseCommand(ctrl_id, device_id, SCSI_LOG_PAGE_SOLID_STATE_MEDIA, 0, &buf, &buf_size, NULL);
    if (retval != SML_SUCCESS || buf == NULL) {
        return media_wearout;
    }
    if (buf_size > SCSI_LOG_PAGE_HEADER_SIZE) {
        // refers to SBC-3 Table 177/178/179 右移byte2至高8位, 且拼接byte3为16位
        page_length = (buf[2] << 8) + buf[3];
        offset = SCSI_LOG_PAGE_HEADER_SIZE;

        while (offset < MIN(page_length + SCSI_LOG_PAGE_HEADER_SIZE, buf_size) && percent_used_endurance < buf_size) {
            // byte0-1: PARAMETER CODE (0001h), 右移byte0至高8位, 且拼接byte1为16位
            page_param_code = (buf[offset] << 8) + buf[offset + 1];
 
            if (page_param_code != PERCENTAGE_USED_ENDURANCE_INDICATOR_PARAM_CODE) {
                offset += PERCENTAGE_USED_ENDURANCE_INDICATOR_PARAM_LENGTH;
                continue;
            }
            percent_used_endurance = offset + 7;    // byte7: PERCENTAGE USED ENDURANCE INDICATOR
            if (buf[percent_used_endurance] <= ONE_HUNDRED_PERCENT) {
                media_wearout = ONE_HUNDRED_PERCENT - buf[percent_used_endurance];
            }
            break;
        }
    }
    g_free(buf);
    return media_wearout;
}

/*
 * Description: 获取SSD硬盘的磨损情况, 即被擦写次数的剩余百分比，百分比从100到1
 *              的线性下降就相当于平均擦写次数从0到最大值
 * History: 2016年4月26日  新生成函数
 *          2017年12月25日  AR.SR.SFEA02109385.002.001
*/
static guint8 GetSSDRemnantMediaWearOut(guint32 ctrl_id, guint16 device_id, SML_PD_INFO_S *pPDInfo)
{
    guint8 media_wearout = 0xFF; // 0xFF indicates not support

    if (pPDInfo == NULL) {
        return media_wearout;
    }
    if (pPDInfo->pdinfo.media_type == MR_PD_MEDIA_TYPE_SSD) {
        if (pPDInfo->pdinfo.interface_type == PD_INTERFACE_TYPE_SATA) {
            // SATA SSD盘
            media_wearout = GetSATARemnantMediaWearOut(pPDInfo);
        } else if (pPDInfo->pdinfo.interface_type == PD_INTERFACE_TYPE_SAS) {
            // SAS SSD盘
            media_wearout = GetSASRemnantMediaWearOut(ctrl_id, device_id, pPDInfo);
        }
    }
    
    return media_wearout;
}

/*
 * Description: 获取SATA硬盘的累计上电时间
*/
static guint32 GetSATAPowerOnHours(SML_PD_INFO_S *pPDInfo)
{
    guint32 power_on_hours = STORAGE_INFO_INVALID_DWORD; // indicates not support

    if (pPDInfo->hw_defined_smartinfo.valid_flag == TRUE) {
        power_on_hours = (guint32)(0xFFFFFFFF &
            GetHWDefinedSmartAttrRawById(pPDInfo, HW_DEFINED_SMART_ATTRIBUTE_ID_POWER_ON_HOURS));
        debug_log(DLOG_INFO, "SATA HW Defined Smart, power on hours: %d", power_on_hours);
    } else if (pPDInfo->smartinfo.valid_flag == TRUE) {
        power_on_hours = (guint32)(0xFFFFFFFF &
            GetVendorSmartAttrRawByAttrId(pPDInfo, ATA_SMART_ATTRIBUTE_ID_POWER_ON_HOURS));
        debug_log(DLOG_INFO, "SATA Vendor Smart, power on hours: %d", power_on_hours);
    }
 
    return power_on_hours;
}

/*
 * Description: 获取硬盘的累计上电时间
 * History: 2017年12月18日  AR.SR.SFEA02109385.002.001
*/
static guint32 GetSASPowerOnHours(guint32 ctrl_id, guint16 device_id, SML_PD_INFO_S *pPDInfo)
{
    gint32 retval = 0;
    guint32 power_on_hours = 0xFFFF; // indicates not support
    guint32 power_on_minutes = 0xFFFF; // indicates not support
    guint8 *buf = NULL;
    guint32 buf_size = 0;
    guint16 page_length = 0;
    guint16 offset = 0;

    // 如果当前值不等于0，即之前有获取到过，先采用之前获取的值
    if (pPDInfo->pdinfo.power_on_hours) {
        power_on_hours = pPDInfo->pdinfo.power_on_hours;
    }

    // 上电去获取
    if (pPDInfo->pdinfo.power_state != PD_POWER_STATE_ACTIVE) {
        return power_on_hours;
    }
    buf_size = 20; // 只需要获取前20个字节数据内容就可以解析
    retval = SendSCSILogSenseCommand(ctrl_id, device_id, SCSI_LOG_PAGE_BACKGROUND_RESULTS, 0, &buf, &buf_size, NULL);
    if (retval == SML_SUCCESS && buf != NULL) {
        if (buf_size > SCSI_LOG_PAGE_HEADER_SIZE) {
            // refers to SBC-3 Table 177/178/179 右移byte2至高8位, 且拼接byte3为16位
            page_length = (buf[2] << 8) + buf[3];
            offset = SCSI_LOG_PAGE_HEADER_SIZE;
            // byte7: PERCENTAGE USED ENDURANCE INDICATOR
            if ((offset + 7) < MIN(page_length + SCSI_LOG_PAGE_HEADER_SIZE, buf_size)) {
                // 取raw值的第4、5、6、7 bytes进行拼接
                power_on_minutes = MAKE_DWORD(buf[offset + 4], buf[offset + 5], buf[offset + 6], buf[offset + 7]);
                debug_log(DLOG_DEBUG, "SAS power_on_minutes =  0x%X \n", power_on_minutes);
                power_on_hours = (power_on_minutes / 60); // 对拼接的值除以60，将分钟转换为小时
            }
        }
        g_free(buf);
    }
    return power_on_hours;
}

/*
 * Description: 获取硬盘的累计上电时间
 * History: 2017年12月18日  AR.SR.SFEA02109385.002.001
*/
static guint32 GetPDPowrOnHours(guint32 ctrl_id, guint16 device_id, SML_PD_INFO_S *pPDInfo)
{
    guint32 power_on_hours = 0xFFFF; // indicates not support
 
    if (pPDInfo == NULL) {
        return power_on_hours;
    }
 
    if (pPDInfo->pdinfo.interface_type == PD_INTERFACE_TYPE_SATA) {
        power_on_hours = GetSATAPowerOnHours(pPDInfo);
    } else if (pPDInfo->pdinfo.interface_type == PD_INTERFACE_TYPE_SAS ||
        pPDInfo->pdinfo.interface_type == PD_INTERFACE_TYPE_PCIE) {
        power_on_hours = GetSASPowerOnHours(ctrl_id, device_id, pPDInfo);
    }
 
    return power_on_hours;
}

/*
 * Description: 更新剩余磨损率和累计上电时间
 * History: 2017年12月25日  AR.SR.SFEA02109385.002.001
*/
gint32 GetRemMediaWearOutAndPowerOnHours(guint32 ctrl_id, guint16 device_id, SML_PD_INFO_S *pPDinfo)
{
    if (pPDinfo == NULL) {
        return SML_SUCCESS;
    }

    guint32 interval = 0;
    guint32 cur_timestamp = (guint32)vos_tick_get();
    if (cur_timestamp >= pPDinfo->pdinfo.last_update_timestamp) {
        interval = cur_timestamp - pPDinfo->pdinfo.last_update_timestamp;
    } else {
        interval = cur_timestamp + (G_MAXUINT32 - pPDinfo->pdinfo.last_update_timestamp);
    }
    // 10分钟更新一次
    if (interval > UPDATE_INTERVAL_TEN_MINS || pPDinfo->pdinfo.last_update_timestamp == 0 ||
        pPDinfo->pdinfo.remnant_media_wearout == 0 || pPDinfo->pdinfo.power_on_hours == 0) {
        pPDinfo->pdinfo.remnant_media_wearout = GetSSDRemnantMediaWearOut(ctrl_id, device_id, pPDinfo);
        pPDinfo->pdinfo.power_on_hours = GetPDPowrOnHours(ctrl_id, device_id, pPDinfo);
 
        pPDinfo->pdinfo.last_update_timestamp = cur_timestamp;
    }

    return SML_SUCCESS;
}

/*
 * Description: 30s使用smart温度属性更新3004raid下的硬盘温度
 * History: 2018年5月3日  新生成函数
*/
gint32 GetSataDeviceTemperature(guint32 ctrl_id, guint16 device_id, SML_PD_INFO_S *pPDinfo)
{
    ATA_SMART_DATA_S *smart_data = NULL;
    ATA_SMART_ATTRIBUTE_S *smart_attr = NULL;
    guint8 attr_len;
    if (pPDinfo == NULL) {
        return SML_ERR_NULL_DATA;
    }

    if (((LSI_3004_WITH_IMR & 0x7F) != ((ctrl_id >> 16) & 0x7F)) &&
        (LSI_STORELIB_TYPE_VALID_BIT(ctrl_id) != SL_LIB_TYPE_STORELIBIT)) {
        return SML_ERR_INVALID_PARAMETER;
    }

    if (pPDinfo->pdinfo.interface_type == PD_INTERFACE_TYPE_SATA) {
        smart_data = (ATA_SMART_DATA_S *)pPDinfo->smartinfo.SATADevice.smart_data;
        attr_len = G_N_ELEMENTS(smart_data->smartAttribute);
        for (guint8 idx = 0; idx < attr_len; idx++) {
            smart_attr = &(smart_data->smartAttribute[idx]);

            if (smart_attr->id == ATA_SMART_ATTRIBUTE_ID_TEMPERATURE_CELSIUS) {
                pPDinfo->pdinfo.temperature = smart_attr->raw[0];
                debug_log(DLOG_DEBUG, "SATA temperature current = %u,raw = %u",
                    smart_attr->current, smart_attr->raw[0]);
            }
        }
    }
    return SML_SUCCESS;
}

static gboolean CheckManufactureStatisfiedTop3(gchar *manufacturer)
{
    // 仅支持更新支持厂商自定义的TOP3厂商
    if (strncasecmp(manufacturer, PD_VENDOR_NAME_INTEL, strlen(PD_VENDOR_NAME_INTEL)) != 0 &&
        strncasecmp(manufacturer, PD_VENDOR_NAME_SAMSUNG, strlen(PD_VENDOR_NAME_SAMSUNG)) != 0 &&
        strncasecmp(manufacturer, PD_VENDOR_NAME_HUAWEI, strlen(PD_VENDOR_NAME_HUAWEI)) != 0) {
        return FALSE;
    }
    return TRUE;
}
 
/*
 * Description: 获取SATA接口SSD硬盘的预估寿命需要的信息情况
*/
static void GetSATAEstimatedLifespanInfo(SML_PD_INFO_S *pPDInfo)
{
    if (pPDInfo->hw_defined_smartinfo.valid_flag == TRUE) {
        // 记录当前TLC/SLC的磨损均值和规格值数据
        pPDInfo->pdinfo.estimated_lifespan.hw_defined.valid_flag = TRUE;
        pPDInfo->pdinfo.estimated_lifespan.hw_defined.slc_pe_cycle = (guint32)(0xFFFFFFFF &
            GetHWDefinedSmartAttrRawById(pPDInfo, HW_DEFINED_SMART_ATTRIBUTE_ID_SLC_PE_CYCLE));
        pPDInfo->pdinfo.estimated_lifespan.hw_defined.slc_avg_ec = (guint32)(0xFFFFFFFF & (
            GetHWDefinedSmartAttrRawById(pPDInfo, HW_DEFINED_SMART_ATTRIBUTE_ID_SLC_ERASE_COUNT) >> 32));  // 右移32位
        pPDInfo->pdinfo.estimated_lifespan.hw_defined.tlc_pe_cycle = (guint32)(0xFFFFFFFF &
            GetHWDefinedSmartAttrRawById(pPDInfo, HW_DEFINED_SMART_ATTRIBUTE_ID_TLC_PE_CYCLE));
        pPDInfo->pdinfo.estimated_lifespan.hw_defined.tlc_avg_ec = (guint32)(0xFFFFFFFF & (
            GetHWDefinedSmartAttrRawById(pPDInfo, HW_DEFINED_SMART_ATTRIBUTE_ID_TLC_ERASE_COUNT) >> 32));  // 右移32位
        pPDInfo->pdinfo.estimated_lifespan.hw_defined.slc_poh = (guint32)(0xFFFFFFFF &
            GetHWDefinedSmartAttrRawById(pPDInfo, HW_DEFINED_SMART_ATTRIBUTE_ID_POWER_ON_HOURS));
        pPDInfo->pdinfo.estimated_lifespan.hw_defined.tlc_poh = (guint32)(0xFFFFFFFF &
            GetHWDefinedSmartAttrRawById(pPDInfo, HW_DEFINED_SMART_ATTRIBUTE_ID_POWER_ON_HOURS));
        pPDInfo->pdinfo.estimated_lifespan.hw_defined.tlc_used_lifespan = (guint32)(0xFFFFFFFF &
            GetHWDefinedSmartAttrRawById(pPDInfo, HW_DEFINED_SMART_ATTRIBUTE_ID_TLC_LIFESPAN));
        pPDInfo->pdinfo.estimated_lifespan.hw_defined.slc_used_lifespan = (guint32)(0xFFFFFFFF &
            GetHWDefinedSmartAttrRawById(pPDInfo, HW_DEFINED_SMART_ATTRIBUTE_ID_SLC_LIFESPAN));
        // 大于等于30天, update_support_flag为True表明支持计算利用当前值计算剩余寿命
        if (pPDInfo->pdinfo.power_on_hours >= POWER_ON_HOURS_LIMIT_30_DAYS) {
            pPDInfo->pdinfo.estimated_lifespan.update_support_flag = TRUE;
        }
    } else if (pPDInfo->smartinfo.valid_flag == TRUE) {
        if (CheckManufactureStatisfiedTop3(pPDInfo->pdinfo.manufacturer) == FALSE) {
            return;
        }
        // 记录当前的累计上电时长和剩余磨损率
        pPDInfo->pdinfo.estimated_lifespan.vendor.valid_flag = TRUE;
        pPDInfo->pdinfo.estimated_lifespan.vendor.remn_wearout = GetSATARemnantMediaWearOutByVendor(pPDInfo);
        pPDInfo->pdinfo.estimated_lifespan.vendor.power_on_hours = (guint32)(0xFFFFFFFF &
            GetVendorSmartAttrRawByAttrId(pPDInfo, ATA_SMART_ATTRIBUTE_ID_POWER_ON_HOURS));
        if (pPDInfo->pdinfo.power_on_hours >= POWER_ON_HOURS_LIMIT_30_DAYS) {
            pPDInfo->pdinfo.estimated_lifespan.update_support_flag = TRUE;
        }
    }
}

/*
 * Description: 获取硬盘的预估寿命情况
*/
static void GetPDEstimatedLifespanInfo(SML_PD_INFO_S *pPDInfo)
{
    pPDInfo->pdinfo.estimated_lifespan.update_support_flag = FALSE;

    if (pPDInfo->pdinfo.media_type == MR_PD_MEDIA_TYPE_SSD) {
        if (pPDInfo->pdinfo.interface_type == PD_INTERFACE_TYPE_SATA) {
            // SATA SSD盘
            GetSATAEstimatedLifespanInfo(pPDInfo);
        }
    }
}

/*
 * Description: 获取SATA接口SSD硬盘的写放大量需要的信息情况
*/
static void GetSATAWriteAmpFactorInfo(SML_PD_INFO_S *pPDInfo)
{
    if (pPDInfo->hw_defined_smartinfo.valid_flag == TRUE) {
        // 获取当前华为自定义Smart信息中nand\host的高、低位数据
        pPDInfo->pdinfo.write_amp.hw_defined.valid_flag = TRUE;
        pPDInfo->pdinfo.write_amp.hw_defined.nand_write_l =
            GetHWDefinedSmartAttrRawById(pPDInfo, HW_DEFINED_SMART_ATTRIBUTE_ID_TLC_NAND_WRITTEN_L);
        pPDInfo->pdinfo.write_amp.hw_defined.nand_write_h =
            GetHWDefinedSmartAttrRawById(pPDInfo, HW_DEFINED_SMART_ATTRIBUTE_ID_TLC_NAND_WRITTEN_H);
        pPDInfo->pdinfo.write_amp.hw_defined.host_write_l =
            GetHWDefinedSmartAttrRawById(pPDInfo, HW_DEFINED_SMART_ATTRIBUTE_ID_TLC_HOST_WRITTEN_L);
        pPDInfo->pdinfo.write_amp.hw_defined.host_write_h =
            GetHWDefinedSmartAttrRawById(pPDInfo, HW_DEFINED_SMART_ATTRIBUTE_ID_TLC_HOST_WRITTEN_H);
        // 大于等于30天, update_support_flag为True表明支持计算利用当前值计算剩余寿命
        if (pPDInfo->pdinfo.power_on_hours >= POWER_ON_HOURS_LIMIT_30_DAYS) {
            pPDInfo->pdinfo.write_amp.update_support_flag = TRUE;
        }
    } else if (pPDInfo->smartinfo.valid_flag == TRUE) {
        if (CheckManufactureStatisfiedTop3(pPDInfo->pdinfo.manufacturer) == FALSE) {
            return;
        }
        // 获取厂商自定义Smart信息中nand\host数据
        pPDInfo->pdinfo.write_amp.vendor.valid_flag = TRUE;
        pPDInfo->pdinfo.write_amp.vendor.nand_write = GetSATANandFlashWrittenByVendor(pPDInfo);
        pPDInfo->pdinfo.write_amp.vendor.host_write = GetSATAHostWrittenByVendor(pPDInfo);
        if (pPDInfo->pdinfo.power_on_hours >= POWER_ON_HOURS_LIMIT_30_DAYS) {
            pPDInfo->pdinfo.write_amp.update_support_flag = TRUE;
        }
    }
}

/*
 * Description: 获取华为自研SAS接口SSD硬盘的写放大情况
 * Note: host写入量为主机写数据量, nand写数据量为(NM写数据量 + GC写数据量)
*/
static void GetSASWriteAmpFactor(guint32 ctrl_id, guint16 device_id, SML_PD_INFO_S *pPDInfo)
{
    guint8 *buf = NULL;
    guint32 buf_size = 0;
    gint32 ret = send_read_hssd_io_info_scsi_command(ctrl_id, device_id, &buf, &buf_size, NULL);
    if (ret != SML_SUCCESS) {
        return;
    }
    SML_PD_READ_WRITE_DATA_STATISTICS_S write_data = { 0 };
    ret = memcpy_s(&write_data, HUAWEI_SPECIFIC_C0H_PAGE_RESP_LENGTH, buf, buf_size);
    g_free(buf);
    if (ret != EOK) {
        debug_log(DLOG_ERROR, "memcpy_s failed, ret: %d", ret);
        return;
    }
    pPDInfo->pdinfo.write_amp.vendor.nand_write = (guint64)(write_data.nm_write_data + write_data.gc_write_data);
    pPDInfo->pdinfo.write_amp.vendor.host_write = write_data.host_write_data;
    debug_log(DLOG_INFO, "nand_write = 0x%"G_GUINT64_HEX_FORMAT", host_write = 0x%"G_GUINT64_HEX_FORMAT"",
        pPDInfo->pdinfo.write_amp.vendor.nand_write, pPDInfo->pdinfo.write_amp.vendor.host_write);
}

/*
 * Description: 获取SAS接口SSD硬盘的写放大量需要的信息情况, 仅华为自研盘支持
 * Note: 由于SAS SSD盘支持预估寿命, 需要进行写放大文件记录, 故将标志位置为TRUE。但支持获取写放大信息的厂商只有HUAWEI
*/
static void GetSASWriteAmpFactorInfo(guint32 ctrl_id, guint16 device_id, SML_PD_INFO_S *pPDInfo)
{
    if (CheckManufactureStatisfiedTop3(pPDInfo->pdinfo.manufacturer) == FALSE) {
        return;
    }
    pPDInfo->pdinfo.write_amp.vendor.valid_flag = TRUE;
    pPDInfo->pdinfo.write_amp.vendor.nand_write = STORAGE_INFO_INVALID_DWORD;
    pPDInfo->pdinfo.write_amp.vendor.host_write = STORAGE_INFO_INVALID_DWORD;

    if (pPDInfo->pdinfo.power_on_hours >= POWER_ON_HOURS_LIMIT_30_DAYS) {
        pPDInfo->pdinfo.write_amp.update_support_flag = TRUE;
    }
    if (strncasecmp(pPDInfo->pdinfo.manufacturer, PD_VENDOR_NAME_HUAWEI, strlen(PD_VENDOR_NAME_HUAWEI)) != 0) {
        return;
    }

    // 获取厂商自定义Smart信息中nand\host数据
    GetSASWriteAmpFactor(ctrl_id, device_id, pPDInfo);
}

/*
 * Description: 获取硬盘的写放大量
*/
static void GetPDWriteAmpFactorInfo(guint32 ctrl_id, guint16 device_id, SML_PD_INFO_S *pPDInfo)
{
    pPDInfo->pdinfo.write_amp.update_support_flag = FALSE;

    if (pPDInfo->pdinfo.media_type == MR_PD_MEDIA_TYPE_SSD) {
        if (pPDInfo->pdinfo.interface_type == PD_INTERFACE_TYPE_SATA) {
            // SATA SSD盘
            GetSATAWriteAmpFactorInfo(pPDInfo);
        } else if (pPDInfo->pdinfo.interface_type == PD_INTERFACE_TYPE_SAS) {
            // SAS SSD盘
            GetSASWriteAmpFactorInfo(ctrl_id, device_id, pPDInfo);
        }
    }
}

/*
 * Description: 更新预估寿命和写放大量
*/
gint32 GetEstimatedLifespanAndWriteAmpFactor(guint32 ctrl_id, guint16 device_id, SML_PD_INFO_S *pPDinfo)
{
    pPDinfo->pdinfo.estimated_lifespan.manage_support_flag = TRUE;

    guint32 interval;
    guint32 cur_timestamp = (guint32)vos_tick_get();
    if (cur_timestamp >= pPDinfo->pdinfo.estimated_lifespan.last_update_timestamp) {
        interval = cur_timestamp - pPDinfo->pdinfo.estimated_lifespan.last_update_timestamp;
    } else {
        interval = cur_timestamp + (G_MAXUINT32 - pPDinfo->pdinfo.estimated_lifespan.last_update_timestamp);
    }

    if (interval > UPDATE_INTERVAL_24_HOURS || pPDinfo->pdinfo.estimated_lifespan.last_update_timestamp == 0) {
        GetPDEstimatedLifespanInfo(pPDinfo);
        GetPDWriteAmpFactorInfo(ctrl_id, device_id, pPDinfo);
        
        pPDinfo->pdinfo.estimated_lifespan.last_update_timestamp = cur_timestamp;
    }

    return SML_SUCCESS;
}