/* 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 "sml_errcodes.h"
#include "sml_public.h"
#include "pmc/pstorc.h"
#include "pmc/sysc_struct.h"
#include "pmc/sysc_cmd.h"
#include "sc_misc.h"
#include "sc_pd.h"

/*
 * Description: 获取物理盘ID列表
 */
LOCAL gint32 get_pd_list(guint32 ctrl_id, SC_PD_LIST *pd_list)
{
    SC_LIB_CMD_PARAM_T lcp;

    (void)memset_s(&lcp, SC_LIB_CMD_PARAM_S, 0, SC_LIB_CMD_PARAM_S);

    lcp.cmdType = SC_PD_CMD_TYPE;
    lcp.cmd = SC_GET_PD_LIST;
    lcp.ctrlId = ctrl_id;
    lcp.dataSize = (guint32)sizeof(SC_PD_LIST);
    lcp.pData = pd_list;

    return ProcessSCCommandCall(&lcp);
}

/*
 * Description: 获取物理盘信息
 */
LOCAL gint32 get_pd_info(guint32 ctrl_id, guint16 device_id, SC_PD_INFO *pd_info)
{
    SC_LIB_CMD_PARAM_T lcp;

    (void)memset_s(&lcp, SC_LIB_CMD_PARAM_S, 0, SC_LIB_CMD_PARAM_S);

    lcp.cmdType = SC_PD_CMD_TYPE;
    lcp.cmd = SC_GET_PD_INFO;
    lcp.ctrlId = ctrl_id;
    lcp.pdRef.deviceId = device_id;
    lcp.dataSize = (guint32)sizeof(SC_PD_INFO);
    lcp.pData = pd_info;

    /*
    【背景】现网PMC卡出现大量state abnormal告警，分析均为误告警
    【问题】PMC卡大IO压力下，会产生错误报文，产生误告警
    【解决方案】增加防护，丢弃model和sn为空数据
    【缺点】如果出现其他错误报文则不能拦截
    */
    gint32 ret = ProcessSCCommandCall(&lcp);
    if (ret == SML_SUCCESS && strlen(pd_info->model) == 0 && strlen(pd_info->sn) == 0) {
        debug_log(DLOG_INFO, "smlib PMC: get_pd_info SML DATA is invalid");
        return SML_ERR_DATA_INVALID;
    }
    return ret;
}

/*
 * Description: 获取物理盘ID列表
 */
gint32 pmc_get_ctrl_pd_list(guint32 ctrl_id, gpointer data)
{
    if (data == NULL) {
        return SML_ERR_NULL_DATA;
    }

    SC_PD_LIST pd_list;

    (void)memset_s(&pd_list, sizeof(pd_list), 0, sizeof(pd_list));

    gint32 ret = get_pd_list(ctrl_id, &pd_list);
    if (ret != SML_SUCCESS) {
        return ret;
    }

    SML_PD_LIST_S *pPDlist = (SML_PD_LIST_S *)data;

    pPDlist->pd_count = pd_list.count;
    for (guint16 i = 0; i < pd_list.count; i++) {
        pPDlist->device_ids[i] = pd_list.pd_number[i];
        pPDlist->slot_num[i] = pd_list.slot_number[i];
        pPDlist->enclosure_ids[i] = pd_list.encl_number[i];
    }

    return SML_SUCCESS;
}

/*
 * Description: 硬盘点灯
 */
LOCAL gint32 locate_pd(guint32 ctrl_id, guint16 device_id, guint32 duration)
{
    SC_LIB_CMD_PARAM_T lcp;

    (void)memset_s(&lcp, SC_LIB_CMD_PARAM_S, 0, SC_LIB_CMD_PARAM_S);

    lcp.cmdType = SC_PD_CMD_TYPE;
    lcp.cmd = SC_START_LOCATE_PD;
    lcp.ctrlId = ctrl_id;
    lcp.pdRef.deviceId = device_id;
    lcp.dataSize = (guint32)sizeof(guint32);
    lcp.pData = &duration;

    return ProcessSCCommandCall(&lcp);
}

/*
 * Description: 停止硬盘点灯
 */
LOCAL gint32 stop_locate_pd(guint32 ctrl_id, guint16 device_id, guint32 duration)
{
    SC_LIB_CMD_PARAM_T lcp;

    (void)memset_s(&lcp, SC_LIB_CMD_PARAM_S, 0, SC_LIB_CMD_PARAM_S);

    lcp.cmdType = SC_PD_CMD_TYPE;
    lcp.cmd = SC_STOP_LOCATE_PD;
    lcp.ctrlId = ctrl_id;
    lcp.pdRef.deviceId = device_id;
    lcp.dataSize = (guint32)sizeof(guint32);
    lcp.pData = &duration;

    return ProcessSCCommandCall(&lcp);
}

/*
 * Description: 添加热备盘
 */
LOCAL gint32 add_hot_spare(guint32 ctrl_id, guint16 device_id, SC_ADD_SPARE_INFO *add_spare_info, gboolean is_can)
{
    SC_LIB_CMD_PARAM_T lcp;

    (void)memset_s(&lcp, SC_LIB_CMD_PARAM_S, 0, SC_LIB_CMD_PARAM_S);

    lcp.cmdType = SC_PD_CMD_TYPE;
    if (is_can == TRUE) {
        lcp.cmd = SC_ADD_SPARE_CAN;
    } else {
        lcp.cmd = SC_ADD_SPARE;
        lcp.timeout = MAX_SET_CMD_TIMEOUT;
    }
    lcp.ctrlId = ctrl_id;
    lcp.pdRef.deviceId = device_id;
    lcp.pData = add_spare_info;
    lcp.dataSize = (guint32)sizeof(SC_ADD_SPARE_INFO);

    return ProcessSCCommandCall(&lcp);
}

/*
 * Description: 取消热备盘
 */
LOCAL gint32 remove_hot_spare(guint32 ctrl_id, guint16 device_id, gboolean is_can)
{
    SC_LIB_CMD_PARAM_T lcp;

    (void)memset_s(&lcp, SC_LIB_CMD_PARAM_S, 0, SC_LIB_CMD_PARAM_S);

    lcp.cmdType = SC_PD_CMD_TYPE;
    if (is_can == TRUE) {
        lcp.cmd = SC_REMOVE_SPARE_CAN;
    } else {
        lcp.cmd = SC_REMOVE_SPARE;
        lcp.timeout = MAX_SET_CMD_TIMEOUT;
    }
    lcp.ctrlId = ctrl_id;
    lcp.pdRef.deviceId = device_id;
    lcp.pData = NULL;
    lcp.dataSize = 0;

    return ProcessSCCommandCall(&lcp);
}

LOCAL gint32 fill_spare_info(guint8 spare_type, gpointer param, guint32 param_length, SC_ADD_SPARE_INFO *spare_info)
{
    if (param == NULL) {
        return SML_ERR_NULL_DATA;
    }
    if (spare_type == kSpareDriveTypeDedicated && param_length < sizeof(guint16)) {
        return SML_ERR_DATA_LEN_INVALID;
    }

    if (spare_type == kSpareDriveTypeAutoReplace && param_length != sizeof(guint16)) { // auto replace只能为一个逻辑盘做热备
        return SML_ERR_DATA_LEN_INVALID;
    }

    (void)memset_s(spare_info, sizeof(SC_ADD_SPARE_INFO), 0, sizeof(SC_ADD_SPARE_INFO));

    spare_info->spare_type = spare_type;

    gsize i;
    for (i = 0; i < param_length / sizeof(guint16) && i < MAX_LOGICAL_DRIVE_NUM; i++) {
        spare_info->ld_number[i] = ((guint16 *)param)[i];
    }

    spare_info->ld_count = i;

    return SML_SUCCESS;
}

LOCAL gint32 set_hot_spare(guint32 ctrl_id, guint16 device_id, guint8 operation, gpointer param, guint32 param_length)
{
    gint32 ret;
    guint8 spare_type;
    gboolean is_can;

    if (operation == PD_OPERATION_SET_DEDICATED_HOTSPARE) {
        spare_type = kSpareDriveTypeDedicated;
        is_can = FALSE;
    } else if (operation == PD_OPERATION_CAN_SET_DEDICATED_HOTSPARE) {
        spare_type = kSpareDriveTypeDedicated;
        is_can = TRUE;
    } else if (operation == PD_OPERATION_SET_AUTO_REPLACE_HOTSPARE) {
        spare_type = kSpareDriveTypeAutoReplace;
        is_can = FALSE;
    } else {
        spare_type = kSpareDriveTypeAutoReplace;
        is_can = TRUE;
    }

    SC_ADD_SPARE_INFO spare_info = {0};
    ret = fill_spare_info(spare_type, param, param_length, &spare_info);
    if (ret == SML_SUCCESS) {
        ret = add_hot_spare(ctrl_id, device_id, &spare_info, is_can);
    }

    return ret;
}

static gint32 cancel_hot_spare(guint32 ctrl_id, guint16 device_id, guint8 operation)
{
    gint32 ret;

    if (operation == PD_OPERATION_CANCEL_HOTSPARE) {
        ret = remove_hot_spare(ctrl_id, device_id, FALSE);
    } else {
        ret = remove_hot_spare(ctrl_id, device_id, TRUE);
    }

    return ret;
}

LOCAL gint32 set_pd_boot_priority_cmd(guint32 ctrl_id, guint8 device_id, guint8 boot_priority)
{
    SC_LIB_CMD_PARAM_T lcp;

    (void)memset_s(&lcp, SC_LIB_CMD_PARAM_S, 0, SC_LIB_CMD_PARAM_S);

    lcp.cmdType = SC_PD_CMD_TYPE;
    lcp.cmd = SC_SET_PD_BOOT_PRIORITY;
    lcp.ctrlId = ctrl_id;
    lcp.ldRef.targetId = device_id;
    lcp.dataSize = (guint32)sizeof(boot_priority);
    lcp.pData = &boot_priority;

    return ProcessSCCommandCall(&lcp);
}

/*
 * Description: 设置指定物理盘的启动优先级
 */
LOCAL gint32 set_pd_boot_priority(guint32 ctrl_id, guint8 device_id, guint8 boot_priority)
{
    SC_PD_INFO pdInfo;

    (void)memset_s(&pdInfo, sizeof(SC_PD_INFO), 0, sizeof(SC_PD_INFO));

    gint32 ret = get_pd_info(ctrl_id, device_id, &pdInfo);
    if (ret != SML_SUCCESS) {
        return ret;
    }

    // 检查设置的参数是否与当前的值一致
    if (pdInfo.boot_priority == boot_priority) {
        debug_log(DLOG_INFO, "smllib: PMC: %s --> Physical drive %d is already set as boot drive.",
            __FUNCTION__, device_id);
    } else {
        ret = set_pd_boot_priority_cmd(ctrl_id, device_id, boot_priority);
        if (ret != SML_SUCCESS) {
            debug_log(DLOG_ERROR, "smllib: PMC: %s  --> Set PD as boot drive failed, device id = %d, return 0x%0x ",
                __FUNCTION__, device_id, ret);
        }
    }

    return ret;
}

/*
 * Description: 物理盘操作函数入口
 */
gint32 pmc_pd_operations(guint32 ctrl_id, guint16 device_id, guint8 operation, gpointer param, guint32 param_length)
{
    gint32 ret = SML_ERR_PD_OPERATION_NOT_SUPPORT;
    guint32 duration = 86400;  // 默认持续时间

    switch (operation) {
        case PD_OPERATION_LOCATE:
            if (param != NULL && param_length != 0 && *(guint8 *)param != 0) {
                duration = *(guint8 *)param;
            }
            ret = locate_pd(ctrl_id, device_id, duration);
            break;
        case PD_OPERATION_STOP_LOCATE:
            ret = stop_locate_pd(ctrl_id, device_id, duration);
            break;

        case PD_OPERATION_SET_DEDICATED_HOTSPARE:
        case PD_OPERATION_CAN_SET_DEDICATED_HOTSPARE:
        case PD_OPERATION_SET_AUTO_REPLACE_HOTSPARE:
        case PD_OPERATION_CAN_SET_AUTO_REPLACE_HOTSPARE:
            ret = set_hot_spare(ctrl_id, device_id, operation, param, param_length);
            break;

        case PD_OPERATION_CANCEL_HOTSPARE:
        case PD_OPERATION_CAN_CANCEL_HOTSPARE:
            ret = cancel_hot_spare(ctrl_id, device_id, operation);
            break;

        case PD_OPERATION_SET_BOOTABLE:
            if (param == NULL) {
                return SML_ERR_NULL_DATA;
            }
            ret = set_pd_boot_priority(ctrl_id, (guint8)device_id, *(guint8 *)param);
            break;
        default:
            debug_log(DLOG_ERROR, "%s: unknown operation = %d", __FUNCTION__, operation);
    }

    return ret;
}

/*
 * Description: 取出物理盘的型号信息
 */
LOCAL void extract_model(const gchar* model, gchar* dest, gsize dest_len)
{
    gchar **array = NULL;
    array = g_strsplit(model, " ", 2);  // 分成2段
    if (array == NULL) {
        return;
    }

    if (array[1] == NULL) {
        g_strfreev(array);
        return;
    }

    gchar *result = g_strchug(array[1]);
    if (result == NULL) {
        g_strfreev(array);
        return;
    }
    errno_t securec_rv = strncpy_s(dest, dest_len, result, strlen(result));
    if (securec_rv != EOK) {
        debug_log(DLOG_ERROR, "%s: strncpy_s failed, ret = %d", __FUNCTION__, securec_rv);
    }

    g_strfreev(array);
    return;
}

/*
 * Description: 解析物理盘 Sas link rate
 */
LOCAL guint8 parse_sas_link_rate(guint8 link_info)
{
    guint8 link_rate;

    switch (link_info) {
        case 0x08:
            link_rate = PD_SPEED_1P5G; // 1.5Gb/s
            break;
        case 0x09:
            link_rate = PD_SPEED_3G; // 3.0Gb/s
            break;
        case 0x0A:
            link_rate = PD_SPEED_6G; // 6.0Gb/s
            break;
        case 0x0B:
            link_rate = PD_SPEED_12G; // 12.0Gb/s
            break;
        case 0x0C:
            link_rate = PD_SPEED_22P5G; // 22.5Gb/s
            break;
        default:
            link_rate = PD_SPEED_UNKNOWN;
    }
    return link_rate;
}

/*
 * Description: 解析物理盘的link speed
 */
LOCAL guint8 parse_link_speed(guint32 link_rate)
{
    guint8 physical_link_rate;
	
    if (PHY_INFO_FORMAT(link_rate) == kPhyInfoFormatSAS) {
        physical_link_rate =  parse_sas_link_rate(PHY_NEGOTIATED_PHYSICAL_LINK_RATE(link_rate));
        debug_log(DLOG_INFO, "%s: physical_link_rate %d", __FUNCTION__, physical_link_rate);
        return physical_link_rate;
    }
    return PD_SPEED_UNKNOWN;
}

/*
 * Description: 解析物理盘的device speed
 */
LOCAL guint8 parse_device_speed(guint32 link_rate)
{
    guint8 physical_device_speed;
    if (PHY_INFO_FORMAT(link_rate) == kPhyInfoFormatSAS) {
        physical_device_speed = parse_sas_link_rate(PHY_MAX_LINK_RATE(link_rate));
        debug_log(DLOG_INFO, "%s: physical_device_speed %d", __FUNCTION__, physical_device_speed);
        return physical_device_speed;
    }
    return PD_SPEED_UNKNOWN;
}

/*
 * Description: 解析介质类型
 */
LOCAL guint8 parse_media_type(guint32 if_type)
{
    if (IS_SSD_PHYSICAL_DRIVE(if_type) == kTrue) {
        return PD_MEDIA_TYPE_SSD;
    }
    if (IS_HDD_PHYSICAL_DRIVE(if_type) == kTrue) {
        return PD_MEDIA_TYPE_ROTATIONAL;
    }
    return PD_MEDIA_TYPE_UNKNOWN;
}

/*
 * Description: 解析接口类型
 */
LOCAL guint8 parse_interface_type(guint32 if_type)
{
    if (IS_SAS_PHYSICAL_DRIVE(if_type) == kTrue) {
        return PD_INTERFACE_TYPE_SAS;
    }
    if (IS_SATA_PHYSICAL_DRIVE(if_type) == kTrue) {
        return PD_INTERFACE_TYPE_SATA;
    }
    if (IS_NVME_PHYSICAL_DRIVE(if_type) == kTrue) {
        return PD_INTERFACE_TYPE_PCIE;
    }

    return PD_INTERFACE_TYPE_UNKNOWN;
}

LOCAL guint8 convert_usage_to_fw_state(guint8 usage_info)
{
    guint8 fw_state;
    switch (usage_info) {
        case kPhysicalDriveUsageUnassigned:
            fw_state = PD_STATE_READY;
            break;
        case kPhysicalDriveUsageData:
            fw_state = PD_STATE_ONLINE;
            break;
        case kPhysicalDriveUsageAutoReplaceStandbySpare:
            fw_state = PD_STATE_HOT_SPARE;
            break;
        case kPhysicalDriveUsageShareableStandbySpare:
            fw_state = PD_STATE_HOT_SPARE;
            break;
        case kPhysicalDriveUsageShareableActiveSpare:
            fw_state = PD_STATE_ONLINE;
            break;
        case kPhysicalDriveUsageLUCacheData:
            fw_state = PD_STATE_ONLINE;
            break;
        case kPhysicalDriveUsageHBA:
            fw_state = PD_STATE_RAW;
            break;
        case kPhysicalDriveUsageUnconfigurable:
            fw_state = PD_STATE_NOT_SUPPORTED;
            break;
        default:
            fw_state = PD_STATE_UNKNOWN;
            break;
    }
    return fw_state;
}

/*
 * Description: 解析物理盘 firmware state
 */
LOCAL guint8 parse_fw_state(guint32 status_info, guint8 usage_info)
{
    if ((status_info & kPhysicalDriveStatusFailed) || (status_info & kPhysicalDriveStatusFailedDueToPSA)) {
        return PD_STATE_FAILED;
    }

    if ((status_info & kPhysicalDriveStatusPredictiveFailure) ||
        (status_info & kPhysicalDriveStatusPredictiveFailureWornOut)) {
        return PD_STATE_PREDICTIVE_FAILURE;
    }

    if (status_info & kPhysicalDriveStatusRebuilding) {
        return PD_STATE_REBUILD;
    }

    if (status_info & kPhysicalDriveStatusErasingOffline) {
        return PD_STATE_ERASING;
    }
    return convert_usage_to_fw_state(usage_info);
}

/*
 * Description: 解析物理盘 hot spare type
 */
LOCAL guint8 convert_usage_to_spare_type(guint8 usage_info)
{
    switch (usage_info) {
        case kPhysicalDriveUsageAutoReplaceStandbySpare:
            return PD_HOT_SPARE_AUTO_REPLACE;

        case kPhysicalDriveUsageShareableStandbySpare:
        case kPhysicalDriveUsageShareableActiveSpare:
            return PD_HOT_SPARE_DEDICATED;

        default:
            return PD_HOT_SPARE_NONE;
    }
}

/*
 * Description: 获取ssd盘相关信息
 */
LOCAL void get_ssd_related_info(SC_PD_INFO *pd_info, SML_PD_INFO_S *pPDinfo)
{
#define REMNANT_ONE_HUNDRED 100
    // 仅支持SSD硬盘
    if (IS_SSD_PHYSICAL_DRIVE(pd_info->if_type) == kTrue) {
        pPDinfo->pdinfo.power_on_hours =
            (pd_info->poweron_hours == 0) ? STORAGE_INFO_INVALID_DWORD : pd_info->poweron_hours;
        if (pd_info->utilization != 0xFFFF && pd_info->utilization <= REMNANT_ONE_HUNDRED) {
            pPDinfo->pdinfo.remnant_media_wearout = REMNANT_ONE_HUNDRED - pd_info->utilization; // 剩余磨损率，
        } else {
            pPDinfo->pdinfo.remnant_media_wearout = STORAGE_INFO_INVALID_BYTE; // SSD 默认获取的无效值
        }
    } else {
        pPDinfo->pdinfo.power_on_hours = STORAGE_INFO_INVALID_DWORD;
        pPDinfo->pdinfo.remnant_media_wearout = STORAGE_INFO_INVALID_BYTE;
    }

    return;
}

/*
 * Description: 不支持的字段，赋无效值
 */
LOCAL void set_pd_properties_unsupported(SML_PD_INFO_S *pPDinfo)
{
    pPDinfo->pdinfo.power_state = STORAGE_INFO_INVALID_BYTE;
    pPDinfo->pdinfo.proginfo.rebuild_progress = STORAGE_INFO_INVALID_BYTE;
    pPDinfo->pdinfo.proginfo.patrol_state = STORAGE_INFO_INVALID_BYTE;
    pPDinfo->pdinfo.proginfo.patrol_progress = STORAGE_INFO_INVALID_BYTE;
    return;
}

gint32 get_pd_info_compl(SML_PD_INFO_S *pPDinfo, SC_PD_INFO *pd_info)
{
    errno_t ret = EOK;
    ret = strncpy_s(pPDinfo->pdinfo.serial_num, sizeof(pPDinfo->pdinfo.serial_num), pd_info->sn,
        sizeof(pd_info->sn) - 1);
    if (ret != EOK) {
        debug_log(DLOG_ERROR, "%s: strncpy_s pdinfo.serial_num failed, ret = %d", __FUNCTION__, ret);
    }
    ret = strncpy_s(pPDinfo->pdinfo.manufacturer, sizeof(pPDinfo->pdinfo.manufacturer), pd_info->vendor,
        sizeof(pd_info->vendor) - 1); // Manufacturer
    if (ret != EOK) {
        debug_log(DLOG_ERROR, "%s: strncpy_s pdinfo.model failed, ret = %d", __FUNCTION__, ret);
    }
    ret = strncpy_s(pPDinfo->pdinfo.firmware_version, sizeof(pPDinfo->pdinfo.firmware_version), pd_info->fm_version,
        sizeof(pd_info->fm_version) - 1);
    if (ret != EOK) {
        debug_log(DLOG_ERROR, "%s: strncpy_s pdinfo.firmware_version failed, ret = %d", __FUNCTION__, ret);
    }
    ret = strncpy_s(pPDinfo->pdinfo.sas_addr1, sizeof(pPDinfo->pdinfo.sas_addr1), pd_info->wwid,
        sizeof(pd_info->wwid) - 1); // 只能获取到一个SAS地址
    if (ret != EOK) {
        debug_log(DLOG_ERROR, "%s: strncpy_s pdinfo.sas_addr1 failed, ret = %d", __FUNCTION__, ret);
    }

    return ret;
}

/*
 * Description: 获取物理盘信息
 */
gint32 pmc_get_pd_info(guint32 ctrl_id, guint16 device_id, gpointer data)
{
    if (data == NULL) {
        return SML_ERR_NULL_DATA;
    }

    SC_PD_INFO pd_info;

    (void)memset_s(&pd_info, sizeof(pd_info), 0, sizeof(pd_info));

    gint32 ret = get_pd_info(ctrl_id, device_id, &pd_info);
    debug_log(DLOG_INFO, "smlib PMC: pmc_get_pd_info slot_number = %u status_info = %x model = %s sn = %s wwid = %s",
              pd_info.slot_number, pd_info.status_info, pd_info.model, pd_info.sn, pd_info.wwid);

    if (ret != SML_SUCCESS) {
        return ret;
    }

    SML_PD_INFO_S *pPDinfo = (SML_PD_INFO_S *)data;
    ret = get_pd_info_compl(pPDinfo, &pd_info);
    if (ret != EOK) {
        debug_log(DLOG_ERROR, "%s: get pd info by strcpy failed, ret = %d", __FUNCTION__, ret);
    }

    extract_model(pd_info.model, pPDinfo->pdinfo.model, sizeof(pPDinfo->pdinfo.model));
    pPDinfo->pdinfo.coerced_size =
        pd_info.total_size / CAPACITY_CONVERSION_SIZE / CAPACITY_CONVERSION_SIZE; // capacityMB
    pPDinfo->pdinfo.block_size = pd_info.block_size;
    pPDinfo->pdinfo.temperature = pd_info.current_temp;
    debug_log(DLOG_INFO, "smlib PMC: pmc_get_pd_info temperature = %d, device_id = %d",
        pPDinfo->pdinfo.temperature, device_id);
    pPDinfo->pdinfo.encl_device_id = STORAGE_INFO_INVALID_WORD; // 目前获取的无效值;
    pPDinfo->pdinfo.media_type = parse_media_type(pd_info.if_type);
    pPDinfo->pdinfo.interface_type = parse_interface_type(pd_info.if_type);
    pPDinfo->pdinfo.slot_num = pd_info.slot_number;
    pPDinfo->pdinfo.fw_state =
        parse_fw_state(pd_info.status_info, pd_info.usage_info); // 根据UsageInfo与StatusInfo判断硬盘固件状态
    debug_log(DLOG_INFO, "smlib PMC: pmc_get_pd_info status_info = %d, usage_info = %d, device id is %d",
        pd_info.status_info, pd_info.usage_info, device_id);
    get_ssd_related_info(&pd_info, pPDinfo);
    pPDinfo->pdinfo.link_speed = parse_link_speed(pd_info.link_rate);
    pPDinfo->pdinfo.device_speed = parse_device_speed(pd_info.link_rate); // PD的支持的最大速率
    pPDinfo->pdinfo.hot_spare = convert_usage_to_spare_type(pd_info.usage_info);
    pPDinfo->pdinfo.boot_priority = pd_info.boot_priority;
    pPDinfo->pdinfo.rotation_speed =
        (pd_info.rotation_speed == 0) ? STORAGE_INFO_INVALID_WORD : (guint16)pd_info.rotation_speed;
    pPDinfo->pdinfo.proginfo.rebuild_state =
        ((pd_info.status_info & kPhysicalDriveStatusRebuilding) == 0) ? FALSE : TRUE;
    set_pd_properties_unsupported(pPDinfo);
    pPDinfo->pdinfo.spare_block.slc_value = STORAGE_INFO_INVALID_BYTE;
    pPDinfo->pdinfo.spare_block.tlc_value = STORAGE_INFO_INVALID_BYTE;
    return SML_SUCCESS;
}