/* 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_diagnose.h"
#include "sl.h"
#include "sl_pd_log.h"
#include "sl_pd_smart.h"
#include "sl_misc.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 "json_api.h"
#include "sl_pd.h"

static void GetBlockSizeOfPDInArray(guint32 ctrl_id, guint16 *pd_ids, guint8 pd_count, guint16 *block_size);
/* END:   Added on 2016/11/8 */
static void GetSCSIInquiryDataItem(MR_PD_INFO *pPdInfo, SCSI_INQUIRY_DATA_TYPE_E data_type, gchar *buf,
    guint32 buf_size);
static guint8 GetPDFwState(guint8 mr_pd_state, guint8 is_foreign, guint8 is_epd);
static guint8 GetPDHotSpareState(MR_PD_INFO *pPdInfo);
static gint32 GetSataDeviceIdentifyInfo(guint32 ctrl_id, guint16 device_id, guint8 *buf, guint32 buf_size);
static gint32 GetSataDeviceVendorSmartData(guint32 ctrl_id, guint16 device_id, SML_PD_SMART_INFO_S *smart_info);
static gint32 GetSataDeviceVendorSmartThreshold(guint32 ctrl_id, guint16 device_id, SML_PD_SMART_INFO_S *smart_info);
static gint32 GetSataDeviceHWDefinedSmartData(guint32 ctrl_id, guint16 device_id,
    SML_PD_HW_DEFINED_SMART_INFO_S *hw_smart_info);
static gint32 GetSataDeviceSmartInfo(guint32 ctrl_id, guint16 device_id, SML_PD_INFO_S *pPDinfo);
static gint32 GetSasDeviceSmartInfo(guint32 ctrl_id, guint16 device_id, SML_PD_SMART_INFO_S *smart_info);
static gint32 LocatePD(guint32 ctrl_id, guint16 device_id, guint8 duration);
static gint32 StopLocatePD(guint32 ctrl_id, guint16 device_id);
static gint32 GetFreeInfoOfArray(guint32 ctrl_id, guint16 array_ref, guint16 block_size, SML_ARRAY_INFO_S *pArrayInfo,
    SL_ARRAY_INFO_T *i_arrayInfo);
static guint32 GetAvailablePdCount(guint8 prl, guint32 pd_count);

static PD_VENDOR_ID_VENDOR_NAME_S g_vendor_id_vendor_name[] = {
    {PD_VENDOR_ID_SEAGATE,          PD_VENDOR_NAME_SEAGATE},
    {PD_VENDOR_ID_WDC,              PD_VENDOR_NAME_WDC},
    {PD_VENDOR_ID_HGST,             PD_VENDOR_NAME_HITACHI},
    {PD_VENDOR_ID_SANDISK,          PD_VENDOR_NAME_SANDISK},
    {PD_VENDOR_ID_TOSHIBA,          PD_VENDOR_NAME_TOSHIBA},
    {PD_VENDOR_ID_INTEL_1,          PD_VENDOR_NAME_INTEL},
    {PD_VENDOR_ID_INTEL_2,          PD_VENDOR_NAME_INTEL},
    {PD_VENDOR_ID_SAMSUNG,          PD_VENDOR_NAME_SAMSUNG},
    {PD_VENDOR_ID_MICRON,           PD_VENDOR_NAME_MICRON},
    {PD_VENDOR_ID_LITE_ON,          PD_VENDOR_NAME_LITE_ON},
    {PD_VENDOR_ID_SMI,              PD_VENDOR_NAME_SMI},
    {PD_VENDOR_ID_PHISON,           PD_VENDOR_NAME_PHISON},
    {PD_VENDOR_ID_SSSTC,            PD_VENDOR_NAME_SSSTC},
    {PD_VENDOR_ID_HYNIX,            PD_VENDOR_NAME_HYNIX},
    {PD_VENDOR_ID_HUAWEI_1,         PD_VENDOR_NAME_HUAWEI},
    {PD_VENDOR_ID_HUAWEI_2,         PD_VENDOR_NAME_HUAWEI},
    {PD_VENDOR_ID_HUAWEI_3,         PD_VENDOR_NAME_HUAWEI},
    {PD_VENDOR_ID_YANGTZE_MEMORY,   PD_VENDOR_NAME_YANGTZE_MEMORY},
    {PD_VENDOR_ID_RAMAXEL,          PD_VENDOR_NAME_RAMAXEL},
    {PD_VENDOR_ID_UMIS,             PD_VENDOR_NAME_UMIS},
    {PD_VENDOR_ID_DERA,             PD_VENDOR_NAME_DERA},
    {PD_VENDOR_ID_DATSSD,           PD_VENDOR_NAME_DATSSD},
    {PD_VENDOR_ID_SAGE,             PD_VENDOR_NAME_SAGE},
    {PD_VENDOR_ID_HIKSEMI,          PD_VENDOR_NAME_HIKSEMI},
    {PD_VENDOR_ID_INSPUR,           PD_VENDOR_NAME_INSPUR},
    {PD_VENDOR_ID_INNOGRIT,         PD_VENDOR_NAME_INNOGRIT},
    {PD_VENDOR_ID_SOLIDIGM,         PD_VENDOR_NAME_SOLIDIGM},
    {PD_VENDOR_ID_AL,               PD_VENDOR_NAME_AL},
};
/* END */

static PD_VENDOR_PN_VENDOR_NAME_S g_vendor_pn_vendor_name[] = {
    {PD_MODEL_NUMBER_SMI_M2,                      PD_VENDOR_NAME_SMI},
    {PD_MODEL_NUMBER_HUADIAN__H10I_IY_256G,       PD_VENDOR_NAME_HUADIAN},
};

/* 存放从storage传过来的映射数据 */
DRIVE_VENDOR_INFO_EXTEND_S *drive_info_list_lsi = NULL;
size_t drive_info_list_lsi_len = 0;

/*
 * Description: 读取PD的UserDataBlockSize，用于计算Array的空间大小
 * History: 2016年11月8日  () 新生成函数
*/
static void GetBlockSizeOfPDInArray(guint32 ctrl_id, guint16 *pd_ids, guint8 pd_count, guint16 *block_size)
{
    gint32 retval = SML_SUCCESS;
    MR_PD_INFO PdInfo;
    guint32 idx = 0;

    if (NULL == pd_ids || NULL == block_size) {
        return;
    }

    *block_size = 0;

    (void)memset_s(&PdInfo, sizeof(MR_PD_INFO), 0, sizeof(MR_PD_INFO));

    for (idx = 0; idx < pd_count; idx++) {
        retval = GetPDInfo(ctrl_id, pd_ids[idx], &PdInfo);
        if (SML_SUCCESS == retval) {
            if (PdInfo.userDataBlockSize == 0) {
                *block_size = PD_BLOCK_SIZE_512;
                continue;
            } else {
                *block_size = PdInfo.userDataBlockSize;
                break;
            }
        } else {
            *block_size = 0;
        }
    }

    return;
}

/*
 * Description: 从SCSI数据中获取物理盘的SN/FW version/厂商等信息
 * History: 2016年4月7日  新生成函数
*/
static void GetSCSIInquiryDataItem(MR_PD_INFO *pPdInfo, SCSI_INQUIRY_DATA_TYPE_E data_type, gchar *buf,
    guint32 buf_size)
{
    SCSI_INQUIRY_DATA_S *scsi_inquiry_data = NULL;
    errno_t safe_fun_ret = EOK;

    if (pPdInfo == NULL || buf == NULL || buf_size == 0) {
        return;
    }

    (void)memset_s(buf, buf_size, 0, buf_size);

    scsi_inquiry_data = (SCSI_INQUIRY_DATA_S *)&(pPdInfo->inquiryData[8]);

    switch (data_type) {
        case SCSI_INQUIRY_DATA_MANUFACTURER:
            safe_fun_ret = memcpy_s(buf, buf_size, scsi_inquiry_data->vendorID, sizeof(scsi_inquiry_data->vendorID));
            break;

        case SCSI_INQUIRY_DATA_SN:
            safe_fun_ret = memcpy_s(buf, buf_size, scsi_inquiry_data->info, sizeof(scsi_inquiry_data->info));
            break;

        case SCSI_INQUIRY_DATA_MODEL:
            safe_fun_ret = memcpy_s(buf, buf_size, scsi_inquiry_data->productID, sizeof(scsi_inquiry_data->productID));
            break;

        case SCSI_INQUIRY_DATA_FW_VERSION:
            safe_fun_ret =
                memcpy_s(buf, buf_size, scsi_inquiry_data->productRevLevel, sizeof(scsi_inquiry_data->productRevLevel));
            break;

        default:
            safe_fun_ret = strncpy_s(buf, buf_size, "Unknown", strlen("Unknown"));
            break;
    }
    if (safe_fun_ret != EOK) {
        debug_log(DLOG_ERROR, "%s: memcpy_s fail, data_type = %d, ret = %d\n", __FUNCTION__, data_type, safe_fun_ret);
    }

    buf[buf_size - 1] = '\0';
    /* SATA盘的信息，字符串前后可能存在空格，需要去掉 */
    remove_string_space(buf, strlen(buf) + 1);

    return;
}

/*
 * Description: 根据厂商ID匹配厂商名
*/
static const gchar *GetPDVendorName(guint32 vendor_id)
{
    for (guint32 i = 0; i < (sizeof(g_vendor_id_vendor_name)) / (sizeof(g_vendor_id_vendor_name[0])); i++) {
        if (vendor_id == g_vendor_id_vendor_name[i].vendor_id) {
            return g_vendor_id_vendor_name[i].vendor_name;
        }
    }

    if (drive_info_list_lsi_len != 0 && drive_info_list_lsi != NULL) {
        for (size_t i = 0; i < drive_info_list_lsi_len; i++) {
            if (vendor_id == drive_info_list_lsi[i].vendor_id) {
                return drive_info_list_lsi[i].vendor_name;
            }
        }
    }
    return PD_VENDOR_NAME_DEFAULT;
}

/*
 * Description: 为使各个厂商的RAID卡读取的PD状态统一，将MR_PD_STATE转换为SML
 *              定义的PD_STATE
 * History: 2016年4月19日  新生成函数
*/
static guint8 GetPDFwState(guint8 mr_pd_state, guint8 is_foreign, guint8 is_epd)
{
    guint8 pd_state;

    switch (mr_pd_state) {
        case MR_PD_STATE_UNCONFIGURED_GOOD:
            pd_state = is_foreign ? PD_STATE_FOREIGN : PD_STATE_UNCONFIGURED_GOOD;
            break;

        case MR_PD_STATE_UNCONFIGURED_BAD:
            pd_state = PD_STATE_UNCONFIGURED_BAD;
            break;

        case MR_PD_STATE_HOT_SPARE:
            pd_state = PD_STATE_HOT_SPARE;
            break;

        case MR_PD_STATE_OFFLINE:
            pd_state = PD_STATE_OFFLINE;
            break;

        case MR_PD_STATE_FAILED:
            pd_state = PD_STATE_FAILED;
            break;

        case MR_PD_STATE_REBUILD:
            pd_state = PD_STATE_REBUILD;
            break;

        case MR_PD_STATE_ONLINE:
            pd_state = is_epd ? PD_STATE_EPD : PD_STATE_ONLINE;
            break;

        case MR_PD_STATE_COPYBACK:
            pd_state = PD_STATE_COPYBACK;
            break;

        case MR_PD_STATE_SYSTEM:
            pd_state = PD_STATE_SYSTEM;
            break;

        case MR_PD_STATE_SHIELD_UNCONFIGURED:
            pd_state = PD_STATE_SHIELD_UNCONFIGURED;
            break;

        case MR_PD_STATE_SHIELD_HOT_SPARE:
            pd_state = PD_STATE_SHIELD_HOT_SPARE;
            break;

        case MR_PD_STATE_SHIELD_CONFIGURED:
            pd_state = PD_STATE_SHIELD_CONFIGURED;
            break;

        default:
            pd_state = PD_STATE_UNKNOWN;
            break;
    }

    return pd_state;
}

/*
 * Description: 为使各个厂商的RAID卡读取的PD电源状态统一，将MR_PD_POWER_STATE转
 *              换为SML定义的PD_POWER_STATE
 * History: 2016年4月19日  新生成函数
*/
static guint8 GetPDPowerState(guint8 mr_pd_powerstate)
{
    guint8 pd_powerstate;

    switch (mr_pd_powerstate) {
        case MR_PD_POWER_STATE_ACTIVE:
            pd_powerstate = PD_POWER_STATE_ACTIVE;
            break;

        case MR_PD_POWER_STATE_STOP:
            pd_powerstate = PD_POWER_STATE_STOP;
            break;

        case MR_PD_POWER_STATE_TRANSITIONING:
            pd_powerstate = PD_POWER_STATE_TRANSITIONING;
            break;

        default:
            pd_powerstate = PD_POWER_STATE_UNKNOWN;
            break;
    }

    return pd_powerstate;
}

/*
 * Description: 将storelib定义的硬盘接口类型定义转换为华为自定义的接口类型
 * History: 2018年6月8日  新生成函数
*/
static guint8 GetPDInterfaceType(guint8 mr_pd_intf)
{
    guint8 pd_intf_type;

    switch (mr_pd_intf) { // 0-Unknown, 1-parallel SCSI, 2-SAS, 3-SATA, 4-FC, 5-NVME
        case 1:
            pd_intf_type = PD_INTERFACE_TYPE_PARALLEL_SCSI;
            break;

        case 2:
            pd_intf_type = PD_INTERFACE_TYPE_SAS;
            break;

        case 3:
            pd_intf_type = PD_INTERFACE_TYPE_SATA;
            break;

        case 4:
            pd_intf_type = PD_INTERFACE_TYPE_FC;
            break;

        case 5:
            pd_intf_type = PD_INTERFACE_TYPE_PCIE;
            break;

        case 0:
        default:
            pd_intf_type = PD_INTERFACE_TYPE_UNKNOWN;
            break;
    }

    return pd_intf_type;
}

/*
 * Description: 解析物理盘的热备状态
 * History: 2016年4月7日  新生成函数
*/
static guint8 GetPDHotSpareState(MR_PD_INFO *pPdInfo)
{
    if (pPdInfo == NULL) {
        return PD_HOT_SPARE_UNKNOWN;
    }

    if (pPdInfo->state.ddf.pdType.isGlobalSpare) {
        return PD_HOT_SPARE_GLOBAL;
    } else if (pPdInfo->state.ddf.pdType.isSpare) {
        return PD_HOT_SPARE_DEDICATED;
    } else {
        return PD_HOT_SPARE_NONE;
    }
}

/*
 * Description: 检查本地查询的SMART数据是否过期，避免查询过于频繁，导致硬盘性能下降
 * History: 2016年7月1日  新生成函数
 *          2018年5月3日  3004raid卡下硬盘温度间隔30s更新一次
*/
static guint8 check_pd_smart_expired(SML_PD_SMART_INFO_S *smart_info, guint32 ctrl_id)
{
    guint32 interval;
    guint32 max_interval = UPDATE_INTERVAL_ONE_HOUR;
    if (smart_info == NULL) {
        return FALSE;
    }

    if (smart_info->last_update_timestamp == 0) {
        return TRUE;
    }

    guint32 current_timestamp = (guint32)vos_get_cur_time_stamp();
    if (current_timestamp >= smart_info->last_update_timestamp) {
        interval = current_timestamp - smart_info->last_update_timestamp;
    } else {
        interval = G_MAXUINT32 - smart_info->last_update_timestamp + current_timestamp;
    }

    if ((LSI_3004_WITH_IMR & 0x7F) == ((ctrl_id >> 16) & 0x7F)) {
        debug_log(DLOG_DEBUG, "Get SATA Device SMART data interval change to 30s , CtrlId = %d",
            SML_CTRL_ID_VALID_BIT(ctrl_id));
        max_interval = 30;
    }

    if (interval > max_interval) {
        return TRUE;
    }

    return FALSE;
}

static gint32 GetSataDeviceVendorSmartData(guint32 ctrl_id, guint16 device_id, SML_PD_SMART_INFO_S *smart_info)
{
    gint32 retval = SML_SUCCESS;
    guint32 pscsi_passthru_len = (guint32)(sizeof(SL_SCSI_PASSTHRU_T) + ATA_SMART_DATA_LENGTH);
    SL_SCSI_PASSTHRU_T *scsi_passthru_cmd = (SL_SCSI_PASSTHRU_T *)g_malloc0(pscsi_passthru_len);
    if (scsi_passthru_cmd == NULL) {
        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_LENGTH16;
    scsi_passthru_cmd->cdb[0] = 0x85;
    scsi_passthru_cmd->cdb[1] = 0x09;
    scsi_passthru_cmd->cdb[2] = 0x0e;
    scsi_passthru_cmd->cdb[3] = 0xff;
    scsi_passthru_cmd->cdb[4] = 0xd0;
    scsi_passthru_cmd->cdb[6] = 0x01;
    scsi_passthru_cmd->cdb[8] = 0x01;
    scsi_passthru_cmd->cdb[10] = 0x4f;
    scsi_passthru_cmd->cdb[11] = 0x00;
    scsi_passthru_cmd->cdb[12] = 0xc2;
    scsi_passthru_cmd->cdb[13] = 0xa0;
    scsi_passthru_cmd->cdb[14] = 0xb0;
    scsi_passthru_cmd->dataSize = ATA_SMART_DATA_LENGTH;

    retval = FireSCSIPassthruCmd(ctrl_id, scsi_passthru_cmd);
    (void)memset_s(smart_info->SATADevice.smart_data, sizeof(smart_info->SATADevice.smart_data), 0,
        sizeof(smart_info->SATADevice.smart_data));
    if (retval != SML_SUCCESS) {
        debug_log(DLOG_DEBUG,
            "Get SATA Device SMART data by SCSI PASSTHRU failed, DeviceId = %d, CtrlId = %d, return 0x%04X", device_id,
            SML_CTRL_ID_VALID_BIT(ctrl_id), retval);

        g_free(scsi_passthru_cmd);
        return retval;
    }

    errno_t memcpy_ret = memcpy_s(smart_info->SATADevice.smart_data, sizeof(smart_info->SATADevice.smart_data),
        scsi_passthru_cmd->data, scsi_passthru_cmd->dataSize);
    if (memcpy_ret != EOK) {
        debug_log(DLOG_ERROR, "%s: memcpy_s fail, ret = %d\n", __FUNCTION__, memcpy_ret);
    }

    g_free(scsi_passthru_cmd);
    return retval;
}

static gint32 GetSataDeviceVendorSmartThreshold(guint32 ctrl_id, guint16 device_id, SML_PD_SMART_INFO_S *smart_info)
{
    gint32 retval = SML_SUCCESS;
    guint32 pscsi_passthru_len = (guint32)(sizeof(SL_SCSI_PASSTHRU_T) + ATA_SMART_DATA_LENGTH);
    SL_SCSI_PASSTHRU_T *scsi_passthru_cmd = (SL_SCSI_PASSTHRU_T *)g_malloc0(pscsi_passthru_len);
    if (scsi_passthru_cmd == NULL) {
        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_LENGTH16;
    scsi_passthru_cmd->cdb[0] = 0x85;
    scsi_passthru_cmd->cdb[1] = 0x09;
    scsi_passthru_cmd->cdb[2] = 0x0e;
    scsi_passthru_cmd->cdb[3] = 0xff;
    scsi_passthru_cmd->cdb[4] = 0xd1;
    scsi_passthru_cmd->cdb[6] = 0x01;
    scsi_passthru_cmd->cdb[7] = 0x01;
    scsi_passthru_cmd->cdb[8] = 0x01;
    scsi_passthru_cmd->cdb[10] = 0x4f;
    scsi_passthru_cmd->cdb[12] = 0xc2;
    scsi_passthru_cmd->cdb[13] = 0xa0;
    scsi_passthru_cmd->cdb[14] = 0xb0;
    scsi_passthru_cmd->dataSize = ATA_SMART_DATA_LENGTH;

    retval = FireSCSIPassthruCmd(ctrl_id, scsi_passthru_cmd);
    (void)memset_s(smart_info->SATADevice.smart_threshold, sizeof(smart_info->SATADevice.smart_threshold), 0,
        sizeof(smart_info->SATADevice.smart_threshold));
    if (retval != SML_SUCCESS) {
        debug_log(DLOG_DEBUG,
            "Get SATA Device SMART Threshold by SCSI PASSTHRU failed, DeviceId = %d, CtrlId = %d, return 0x%04X",
            device_id, SML_CTRL_ID_VALID_BIT(ctrl_id), retval);

        g_free(scsi_passthru_cmd);
        return retval;
    }

    errno_t memcpy_ret = memcpy_s(smart_info->SATADevice.smart_threshold,
        sizeof(smart_info->SATADevice.smart_threshold), scsi_passthru_cmd->data, scsi_passthru_cmd->dataSize);
    if (memcpy_ret != EOK) {
        debug_log(DLOG_ERROR, "%s: memcpy_s fail, ret = %d\n", __FUNCTION__, memcpy_ret);
    }

    g_free(scsi_passthru_cmd);

    return retval;
}

/*
 * Description: 判断获取的是否为华为自定义Smart信息
 * Note: 条件：当透传获取到page数据且Smart头信息Byte[5:10]为指定标识符
*/
static gboolean IsSupportHWDefinedSmart(guint32 ctrl_id, guint16 device_id, guint8 *data)
{
    guint32 front_half = MAKE_DWORD(0, 0, data[10], data[9]);
    guint32 back_half = MAKE_DWORD(data[8], data[7], data[6], data[5]);
    guint64 identifier = (((guint64)front_half << 32) | ((guint64)back_half));
    if (identifier == SATA_DEVICE_HW_DEFINED_SMART_IDENTIFIER) {
        debug_log(DLOG_INFO, "SATA Device supports HW Defined SMART data, DeviceId = %u, CtrlId = %u",
            device_id, SML_CTRL_ID_VALID_BIT(ctrl_id));
        return TRUE;
    }
    return FALSE;
}

/*
 * Description: 发送透传命令获取SATA盘的华为自定义Smart信息
 * Note: 函数内部申请内存, 成功时外部释放内存, 失败时函数内部释放内存
*/
static gint32 SendScsiPassthruCmd(guint32 ctrl_id, guint16 device_id,
    SL_SCSI_PASSTHRU_T **scsi_passthru_cmd, guint8 log_addr, guint8 sector_num)
{
    guint16 data_len = sector_num * ATA_SMART_DATA_LENGTH;
    guint32 pscsi_passthru_len = sizeof(SL_SCSI_PASSTHRU_T) + data_len;
    *scsi_passthru_cmd = (SL_SCSI_PASSTHRU_T *)g_malloc0(pscsi_passthru_len);
    if (*scsi_passthru_cmd == NULL) {
        debug_log(DLOG_ERROR, "%s, g_malloc0 failed.", __FUNCTION__);
        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_LENGTH16;
    (*scsi_passthru_cmd)->cdb[0] = 0x85;        // cdb[0]:operation code;   0x85:PASS-THROUGH(16)指定操作码
    (*scsi_passthru_cmd)->cdb[1] = 0x09;        // cdb[1]:protocol;         0x09:PIO
    (*scsi_passthru_cmd)->cdb[2] = 0x0e;        // cdb[2]:transfer type;
                                                // 0x0e:The number of ATA logical sector size blocks to be transferred
    (*scsi_passthru_cmd)->cdb[6] = sector_num;  // cdb[6]:count(7:0):sector num;
    (*scsi_passthru_cmd)->cdb[8] = log_addr;    // cdb[8]:LBA(7:0):Log Address;
    (*scsi_passthru_cmd)->cdb[13] = 0xa0;       // cdb[13]:DEVICE;          0xa0:Obsolete
    (*scsi_passthru_cmd)->cdb[14] = 0x2f;       // cdb[14]:COMMAND;         0x2f:PIO command
    (*scsi_passthru_cmd)->dataSize = data_len;  // dataSize

    gint32 ret = FireSCSIPassthruCmd(ctrl_id, *scsi_passthru_cmd);
    if (ret != SML_SUCCESS) {
        sleep(1);   // 等待1s后重试
        ret = FireSCSIPassthruCmd(ctrl_id, *scsi_passthru_cmd);
        if (ret != SML_SUCCESS) {
            debug_log(DLOG_ERROR, "SATA Device get log page: 0x%2X fail, DeviceId = %u, CtrlId = %u",
                log_addr, device_id, SML_CTRL_ID_VALID_BIT(ctrl_id));
            g_free(*scsi_passthru_cmd);
            *scsi_passthru_cmd = NULL;
            return SML_ERR_CTRL_TOO_BUSY_TO_RESP_OOB;
        }
    }
    if ((*scsi_passthru_cmd)->dataSize != data_len) {
        debug_log(DLOG_ERROR, "SATA Device get log page: 0x%2X, return length invalid, DeviceId = %u, CtrlId = %u",
            log_addr, device_id, SML_CTRL_ID_VALID_BIT(ctrl_id));
        g_free(*scsi_passthru_cmd);
        *scsi_passthru_cmd = NULL;
        return SML_ERR_PD_SCSI_CMD_FAIL;
    }
    return SML_SUCCESS;
}

/*
 * 获取log page 0x00, 检查0xCF对应位置的page页长度, 明确是否支持0xCF
 * Note: 介质部明确0x00 page一定存在
 *      1、当0x00 page页请求失败, 不设置标志位, 允许继续探测, 即support_page_cf_flag = 0;
 *      2、当Word[0xCF] page页数为0时, 说明该盘不支持0xCF, 设置标志位x0x01, 不允许继续探测;
 *      3、当page页数不为0时, 说明该盘支持0xCF, 设置标志位为SUPPORT_PAGE_CF_FLAG, 不用继续探测;
 */
static void CheckDeviceSupportPageCF(guint32 ctrl_id, guint16 device_id, guint8 *sector_num,
    guint8 *support_page_cf_flag)
{
#define CF_LOG_PAGE_NUM 414     // page 0xCF支持的sector个数的固定位置
    SL_SCSI_PASSTHRU_T *scsi_passthru_cmd = NULL;
    // 获取log page 0x00的CDB的命令字:85 09 0e 00 00 00 01(sector_num) 00 00(log_addr) 00 00 00 00 a0 2f 00
    gint32 ret = SendScsiPassthruCmd(ctrl_id, device_id, &scsi_passthru_cmd, 0x00, 0x01);
    if (ret != SML_SUCCESS) {
        return;   // 当0x00 page页请求失败, 不设置标志位, 允许继续探测
    }

    guint8 sectors = scsi_passthru_cmd->data[CF_LOG_PAGE_NUM];  // 当前华为自定义smart的sector最大为6, 取一个低字节使用即可
    g_free(scsi_passthru_cmd);
    if (sectors == 0) {     // 当page页数为0时，说明该盘不支持0xCF
        debug_log(DLOG_INFO, "SATA Device does not support page 0xCF, DeviceId = %u, CtrlId = %u",
            device_id, SML_CTRL_ID_VALID_BIT(ctrl_id));
        *support_page_cf_flag = UNSUPPORT_PAGE_CF_FLAG;    // 已经明确不支持0xCF, 不用重复探测
        return;
    }
    *support_page_cf_flag = SUPPORT_PAGE_CF_FLAG;
    *sector_num = sectors;
}

/*
 * 获取log page 0xCF, 明确是否支持华为自定义Smart
 */
static void CheckPageCFSupportHWDefinedSmart(guint32 ctrl_id, guint16 device_id,
    SML_PD_HW_DEFINED_SMART_INFO_S *hw_smart_info)
{
#define MAX_HW_DEFINED_SECTOR_NUM 6
    SL_SCSI_PASSTHRU_T *scsi_passthru_cmd = NULL;
    // 获取0xcf的CDB的命令字:85 09 0e 00 00 00 05(sector_num) 00 cf(log_addr) 00 00 00 00 a0 2f 00
    guint8 sector_num =
        hw_smart_info->sector_num > MAX_HW_DEFINED_SECTOR_NUM ? MAX_HW_DEFINED_SECTOR_NUM : hw_smart_info->sector_num;
    gint32 ret = SendScsiPassthruCmd(ctrl_id, device_id, &scsi_passthru_cmd, 0xCF, sector_num);
    if (ret != SML_SUCCESS) {
        hw_smart_info->support_flag = 0;    // 赋值为0, 允许下次继续探测
        return;
    }

    if (!IsSupportHWDefinedSmart(ctrl_id, device_id, scsi_passthru_cmd->data)) {
        hw_smart_info->support_flag = UNSUPPORT_HUAWEI_DEFINED_SMART;
        g_free(scsi_passthru_cmd);
        debug_log(DLOG_INFO, "SATA Device does not support HW Defined SMART data, DeviceId = %u, CtrlId = %u",
            device_id, SML_CTRL_ID_VALID_BIT(ctrl_id));
        return;
    }
    hw_smart_info->support_flag = SUPPORT_HUAWEI_DEFINED_SMART;
    g_free(scsi_passthru_cmd);
}

static gint32 CheckDeviceSupportHWDefinedSmart(guint32 ctrl_id, guint16 device_id,
    SML_PD_HW_DEFINED_SMART_INFO_S *hw_smart_info)
{
    if (hw_smart_info->support_flag != 0) {
        return hw_smart_info->support_flag;
    }

    for (guint8 i = 0; i < 3; i++) {    // BMC启动后连续获取3次，往后不在获取
        // 请求page 0x00, 探测是否支持page 0xCF, 并记录sector_num, 成功后不再探测。
        if (hw_smart_info->support_page_cf_flag == 0) {    // 初次探测或上次请求0x00 page页失败
            CheckDeviceSupportPageCF(ctrl_id, device_id, &hw_smart_info->sector_num,
                &hw_smart_info->support_page_cf_flag);
            if (hw_smart_info->support_page_cf_flag == 0) {
                continue;   // 如果连续三次请求0x00失败, 未能达到判别目的, 允许下次继续探测
                           // 该场景只有当RAID卡或盘异常时出现, 与其他直通命令处理策略保持一致
            }
        }
        debug_log(DLOG_INFO, "Device support page cf flag: %d", hw_smart_info->support_page_cf_flag);
        if (hw_smart_info->support_page_cf_flag != SUPPORT_PAGE_CF_FLAG) { // 不支持直接退出
            hw_smart_info->support_flag = UNSUPPORT_HUAWEI_DEFINED_SMART;
            break;
        }
        // 请求page 0xCF, 判断是否支持华为自定义, 成功后不再探测
        CheckPageCFSupportHWDefinedSmart(ctrl_id, device_id, hw_smart_info);
        debug_log(DLOG_INFO, "Device support HW Defined Smart support_flag: %d", hw_smart_info->support_flag);
        if (hw_smart_info->support_flag == 0) { // 上次请求0xCF page页失败
            hw_smart_info->support_flag = UNSUPPORT_HUAWEI_DEFINED_SMART;
            continue;
        }
        break;
    }
    return hw_smart_info->support_flag;
}

/*
 * Description: 获取该SATA盘支持华为自定义的Smart信息;
*/
LOCAL gint32 GetSataDeviceHWDefinedSmartData(guint32 ctrl_id, guint16 device_id,
    SML_PD_HW_DEFINED_SMART_INFO_S *hw_smart_info)
{
    if (CheckDeviceSupportHWDefinedSmart(ctrl_id, device_id, hw_smart_info) != SUPPORT_HUAWEI_DEFINED_SMART) {
        return SML_ERR_PD_LOG_PAGE_UNSUPPORT;
    }

    // 获取华为自定义
    SL_SCSI_PASSTHRU_T *scsi_passthru_cmd = NULL;
    gint32 ret = SendScsiPassthruCmd(ctrl_id, device_id, &scsi_passthru_cmd, 0xCF, hw_smart_info->sector_num);
    if (ret != SML_SUCCESS) {
        return ret;
    }

    ret = memcpy_s(hw_smart_info->smart_data, ATA_HW_DEFINED_SMART_DATA_LENGTH, scsi_passthru_cmd->data,
        scsi_passthru_cmd->dataSize);
    if (ret != RET_OK) {
        debug_log(DLOG_ERROR, "memcpy_s failed, ret: %d", ret);
    }
    g_free(scsi_passthru_cmd);
    return ret;
}

/*
 * Description : 读取SATA的wwn，即厂商ID
 */
static gint32 GetSataDeviceWWN(guint32 ctrl_id, guint16 device_id, guint32 *vendor_id)
{
    if (NULL == vendor_id) {
        return SML_ERR_NULL_DATA;
    }

    gint32 retval = SML_SUCCESS;
    guint32 pscsi_passthru_len = (guint32)(sizeof(SL_SCSI_PASSTHRU_T) + ATA_SMART_DATA_LENGTH);
    SL_SCSI_PASSTHRU_T *scsi_passthru_cmd = (SL_SCSI_PASSTHRU_T *)g_malloc0(pscsi_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_LENGTH16;
    scsi_passthru_cmd->cdb[0] = 0x85;
    scsi_passthru_cmd->cdb[1] = 0x08;
    scsi_passthru_cmd->cdb[2] = 0x0e;
    scsi_passthru_cmd->cdb[6] = 0x01;
    scsi_passthru_cmd->cdb[14] = 0xec;
    scsi_passthru_cmd->dataSize = ATA_SMART_DATA_LENGTH;

    retval = FireSCSIPassthruCmd(ctrl_id, scsi_passthru_cmd);
    if (retval != SML_SUCCESS) {
        debug_log(DLOG_DEBUG, "Get SATA Device WWN by SCSI PASSTHRU failed, DeviceId = %d, CtrlId = %d, return 0x%04X",
            device_id, SML_CTRL_ID_VALID_BIT(ctrl_id), retval);
        g_free(scsi_passthru_cmd);
        return retval;
    }

    *vendor_id = (guint32)((scsi_passthru_cmd->data[217] & 0x0F) << 20) |
                 (guint32)(scsi_passthru_cmd->data[216] << 12) | (guint32)(scsi_passthru_cmd->data[219] << 4) |
                 (guint32)((scsi_passthru_cmd->data[218] >> 4) & 0x0F);

    g_free(scsi_passthru_cmd);

    return retval;
}

/*
 * Description: 读取SATA硬盘的SMART信息
 * History: 2016年4月14日  新生成函数
 *          2017年12月28日  AR.SR.SFEA02109385.002.001
*/
static gint32 GetSataDeviceSmartInfo(guint32 ctrl_id, guint16 device_id, SML_PD_INFO_S *pPDinfo)
{
    gint32 retval = SML_SUCCESS;

    if (check_pd_smart_expired(&pPDinfo->smartinfo, ctrl_id) == FALSE) {
        return SML_SUCCESS;
    }

    // 读取SATA硬盘厂商自定义的SMART信息
    if (GetSataDeviceVendorSmartData(ctrl_id, device_id, &pPDinfo->smartinfo) == SML_SUCCESS &&
        GetSataDeviceVendorSmartThreshold(ctrl_id, device_id, &pPDinfo->smartinfo) == SML_SUCCESS) {
        debug_log(DLOG_INFO, "Get SATA Device SMART data, DeviceId = %d, CtrlId = %d", device_id,
            SML_CTRL_ID_VALID_BIT(ctrl_id));
        pPDinfo->smartinfo.valid_flag = TRUE;
        pPDinfo->smartinfo.last_update_timestamp = (guint32)vos_get_cur_time_stamp();
    } else {
        pPDinfo->smartinfo.valid_flag = FALSE;
    }

    if (pPDinfo->pdinfo.media_type != MR_PD_MEDIA_TYPE_SSD) {   // 华为自定义smart信息探测仅涉及SSD SATA盘
        return SML_SUCCESS;
    }
    // 读取SATA硬盘华为自定义的SMART信息
    pPDinfo->hw_defined_smartinfo.valid_flag = FALSE;
    if (GetSataDeviceHWDefinedSmartData(ctrl_id, device_id, &pPDinfo->hw_defined_smartinfo) == SML_SUCCESS) {
        pPDinfo->hw_defined_smartinfo.valid_flag = TRUE;
    } else {
        pPDinfo->hw_defined_smartinfo.valid_flag = FALSE;
    }
    return retval;
}

/*
 * Description: 获取SATA设备的身份信息
 * History: 2016年5月5日  新生成函数
*/
static gint32 GetSataDeviceIdentifyInfo(guint32 ctrl_id, guint16 device_id, guint8 *buf, guint32 buf_size)
{
    if (buf == NULL) {
        return SML_ERR_NULL_DATA;
    }

    errno_t safe_fun_ret = EOK;
    gint32 retval = SML_SUCCESS;
    guint16 *pU16_data = NULL;
    guint16 *pU16_tmp = NULL;
    guint16 u16_data = 0;
    guint32 pscsi_passthru_len = (guint32)(sizeof(SL_SCSI_PASSTHRU_T) + ATA_SMART_DATA_LENGTH);
    SL_SCSI_PASSTHRU_T *scsi_passthru_cmd = (SL_SCSI_PASSTHRU_T *)g_malloc0(pscsi_passthru_len);
    if (NULL == scsi_passthru_cmd) {
        return SML_ERR_CANNOT_ALLOC_MEM;
    }

    if (buf_size < ATA_SMART_DATA_LENGTH) {
        g_free(scsi_passthru_cmd);
        return SML_ERR_BUF_NOT_ENOUGH;
    }

    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_LENGTH16;
    scsi_passthru_cmd->cdb[0] = 0x85;
    scsi_passthru_cmd->cdb[1] = 0x09;
    scsi_passthru_cmd->cdb[2] = 0x0e;
    scsi_passthru_cmd->cdb[3] = 0xff;
    scsi_passthru_cmd->cdb[6] = 0x01;
    scsi_passthru_cmd->cdb[8] = 0x01;
    scsi_passthru_cmd->cdb[13] = 0xa0;
    scsi_passthru_cmd->cdb[14] = ATA_CMD_IDENTIFY_DEVICE;
    scsi_passthru_cmd->dataSize = ATA_SMART_DATA_LENGTH;

    retval = FireSCSIPassthruCmd(ctrl_id, scsi_passthru_cmd);
    if (retval != SML_SUCCESS) {
        debug_log(DLOG_DEBUG,
            "Get SATA Device Identity data by SCSI PASSTHRU failed, DeviceId = %d, CtrlId = %d, return 0x%04X",
            device_id, SML_CTRL_ID_VALID_BIT(ctrl_id), retval);

        g_free(scsi_passthru_cmd);
        return retval;
    }

    pU16_data = (guint16 *)g_malloc0(scsi_passthru_cmd->dataSize);
    if (NULL == pU16_data) {
        g_free(scsi_passthru_cmd);
        return SML_ERR_CANNOT_ALLOC_MEM;
    }

    memcpy_s(pU16_data, scsi_passthru_cmd->dataSize, scsi_passthru_cmd->data, scsi_passthru_cmd->dataSize);

    pU16_tmp = pU16_data;

    for (guint32 idx = 0; idx < scsi_passthru_cmd->dataSize / 2; idx++) {
        swapBytes((void *)pU16_tmp, (void *)&u16_data, sizeof(u16_data));
        *pU16_tmp = u16_data;
        pU16_tmp++;
    }

    if (buf_size >= scsi_passthru_cmd->dataSize) {
        safe_fun_ret = memcpy_s(buf, buf_size, pU16_data, scsi_passthru_cmd->dataSize);
        if (safe_fun_ret != EOK) {
            debug_log(DLOG_ERROR, "%s: memcpy_s fail, ret = %d\n", __FUNCTION__, safe_fun_ret);
        }
    } else {
        retval = SML_ERR_BUF_NOT_ENOUGH;
    }

    g_free(pU16_data);
    g_free(scsi_passthru_cmd);
    return retval;
}

/*
 * Description: 读取SAS硬盘的SMART信息
 * History: 2016年4月14日  新生成函数
*/
static gint32 GetSasDeviceSmartInfo(guint32 ctrl_id, guint16 device_id, SML_PD_SMART_INFO_S *smart_info)
{
    gint32 retval = SML_SUCCESS;
    guint8 smart_lp_support = FALSE;
    SCSI_SENSE_DISECT_S SenInfo = { 0 };
    guint8 *buf = NULL;
    guint32 buf_size = 0;
    guint16 page_length = 0;
    guint16 page_param_code = 0;
    guint16 offset = 0;

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

    if (FALSE == check_pd_smart_expired(smart_info, ctrl_id)) {
        return SML_SUCCESS;
    }

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

    smart_info->valid_flag = FALSE;

#define LOG_PAGE_INFORMATIONAL_EXCEPTIONS_PARAM_CODE 0x0000

    retval = GetSCSIDeviceSupportLogPages(ctrl_id, device_id, SCSI_LOG_PAGE_IE, &smart_lp_support);
    debug_log(DLOG_DEBUG, "Get SCSI %d.%d Support LogPages return 0x%x, Flag is %s\n", SML_CTRL_ID_VALID_BIT(ctrl_id),
        device_id, retval, smart_lp_support ? "TRUE" : "FALSE");

    if (retval == SML_SUCCESS && smart_lp_support == TRUE) {
        retval = SendSCSILogSenseCommand(ctrl_id, device_id, SCSI_LOG_PAGE_IE, 0, &buf, &buf_size, NULL);
        debug_log(DLOG_DEBUG, "Send SCSI %d.%d Log sense command %x return 0x%x\n", SML_CTRL_ID_VALID_BIT(ctrl_id),
            device_id, SCSI_LOG_PAGE_IE, retval);

        if (retval == SML_SUCCESS && buf != NULL) {
            if (buf_size > SCSI_LOG_PAGE_HEADER_SIZE) {
                offset = SCSI_LOG_PAGE_HEADER_SIZE;
                page_length = (buf[2] << 8) + buf[3];

                while ((offset < MIN(page_length + SCSI_LOG_PAGE_HEADER_SIZE, buf_size)) && (offset + 5 < buf_size)) {
                    page_param_code = (buf[offset] << 8) + buf[offset + 1];

                    /* sanity check on response data */
                    if (page_param_code != LOG_PAGE_INFORMATIONAL_EXCEPTIONS_PARAM_CODE) {
                        debug_log(DLOG_DEBUG, "SCSI Log Sense command responsed with incorrect status [0x%04x]",
                            page_param_code);
                        smart_info->valid_flag = FALSE;
                        offset += buf[offset + 3] + SCSI_LOG_PAGE_HEADER_SIZE;
                        continue;
                    }

                    if (1 < buf[offset + 3]) {
                        SenInfo.asc = buf[offset + 4];
                        SenInfo.ascq = buf[offset + 5];
                    }

                    if (0 == SenInfo.asc) {
                        retval = SendSCSIRequestSenseCommand(ctrl_id, device_id, &SenInfo);
                        debug_log(DLOG_DEBUG, "Send SCSI %d.%d request sense return 0x%x\n",
                            SML_CTRL_ID_VALID_BIT(ctrl_id), device_id, retval);

                        if (retval == SML_SUCCESS) {
                            smart_info->valid_flag = TRUE;
                            smart_info->last_update_timestamp = (guint32)vos_get_cur_time_stamp();
                            smart_info->SASDevice.ASC = SenInfo.asc;
                            smart_info->SASDevice.ASCQ = SenInfo.ascq;
                        }
                    } else {
                        smart_info->valid_flag = TRUE;
                        smart_info->last_update_timestamp = (guint32)vos_get_cur_time_stamp();
                        smart_info->SASDevice.ASC = SenInfo.asc;
                        smart_info->SASDevice.ASCQ = SenInfo.ascq;
                    }
                    break;
                }
            } else {
                retval = SML_ERR_DATA_LEN_INVALID;
            }

            g_free(buf);
        }
    }

    if (smart_info->valid_flag == FALSE) {
        smart_info->SASDevice.ASC = 0xFF;
        smart_info->SASDevice.ASCQ = 0xFF;
    }

    return retval;
}

/*
 * Description: 对物理盘进行locate
 * History: 2016年5月7日  新生成函数
*/
static gint32 LocatePD(guint32 ctrl_id, guint16 device_id, guint8 duration)
{
    guint8 libType = 0;
    SL_LIB_CMD_PARAM_T libCmdParam;

    libType = LSI_STORELIB_TYPE_VALID_BIT(ctrl_id);

    (void)memset_s(&libCmdParam, SL_LIB_CMD_PARAM_S, 0, SL_LIB_CMD_PARAM_S);

    libCmdParam.cmdType = SL_PD_CMD_TYPE;
    libCmdParam.cmd = SL_START_LOCATE_PD;
    libCmdParam.ctrlId = ctrl_id;
    libCmdParam.pdRef.deviceId = device_id;
    libCmdParam.cmdParam_1b[0] = duration; // in seconds, 0 - indefinite

    return ProcessLibCommandCall(libType, &libCmdParam);
}

/*
 * Description: 对物理盘进行locate
 * History: 2016年5月7日  新生成函数
*/
static gint32 StopLocatePD(guint32 ctrl_id, guint16 device_id)
{
    guint8 libType = 0;
    SL_LIB_CMD_PARAM_T libCmdParam;

    libType = LSI_STORELIB_TYPE_VALID_BIT(ctrl_id);

    (void)memset_s(&libCmdParam, SL_LIB_CMD_PARAM_S, 0, SL_LIB_CMD_PARAM_S);

    libCmdParam.cmdType = SL_PD_CMD_TYPE;
    libCmdParam.cmd = SL_STOP_LOCATE_PD;
    libCmdParam.ctrlId = ctrl_id;
    libCmdParam.pdRef.deviceId = device_id;

    return ProcessLibCommandCall(libType, &libCmdParam);
}

/*
 * Description: 根据Array上的RAID级别和物理盘数量计算能算做逻辑盘容量的物理盘数量
*/
static guint32 GetAvailablePdCount(guint8 prl, guint32 pd_count)
{
    guint32 available_pd_count = 0;

    switch (prl) {
        case RAID_LEVEL_1:
            available_pd_count = pd_count / 2;
            break;

        case RAID_LEVEL_5:
            available_pd_count = MAX(pd_count, 1) - 1;
            break;

        case RAID_LEVEL_6:
            available_pd_count = MAX(pd_count, 2) - 2;
            break;

        default:
            available_pd_count = pd_count;
            break;
    }

    return available_pd_count;
}

/*
 * Description: 获取指定Array上的剩余信息，总的剩余容量和每个剩余块的容量
 * History: 2016年11月10日  () 新生成函数
 *          增加block_size参数，用来正确计算mb到block的转换
 *          2019年6月29日  函数名 GetFreeSpaceOfArray -> GetFreeInfoOfArray
 *          free_space名字修改成total_free_space，取值修改为所有空闲块之和
 *          free_count和free_blocks_space赋值
 *           i_arrayInfo通过入参传入，GetArrayInfo会比较耗时
*/
static gint32 GetFreeInfoOfArray(guint32 ctrl_id, guint16 array_ref, guint16 block_size, SML_ARRAY_INFO_S *pArrayInfo,
    SL_ARRAY_INFO_T *i_arrayInfo)
{
    gint32 retval = SML_SUCCESS;
    guint32 idx = 0, i = 0, count_idx = 0;
    SML_ARRAY_LIST_S arrayList;
    guint64 temp_size = 0, temp_free = 0, single_free = 0;
    guint32 available_pd_count = 0; // 根据RAID级别计算
    SL_ARRAY_INFO_T arrayInfo;

    if (NULL == pArrayInfo || NULL == i_arrayInfo) {
        return SML_ERR_NULL_DATA;
    }

    // 防止宏里面除数为0
    if (0 == block_size) {
        block_size = PD_BLOCK_SIZE_512;
    }

    memcpy_s(&arrayInfo, sizeof(SL_ARRAY_INFO_T), i_arrayInfo, sizeof(SL_ARRAY_INFO_T));
    (void)memset_s(&arrayList, sizeof(SML_ARRAY_LIST_S), 0, sizeof(SML_ARRAY_LIST_S));

    temp_size = 0;

    if (arrayInfo.freeCount >= SML_MAX_HOLES_IN_ARRAY) {
        return SML_ERR_DATA_INVALID;
    }

    for (idx = 0; idx < arrayInfo.freeCount; idx++) {
        // 计算所有剩余块的容量之和
        if (arrayInfo.freeInfo[idx].numBlocks >= MB2BLK(SML_LD_MIN_SIZE, block_size)) {
            temp_size += arrayInfo.freeInfo[idx].numBlocks;
            pArrayInfo->free_blocks_space[count_idx] = BLK2MB(arrayInfo.freeInfo[idx].numBlocks, block_size);
            count_idx++;
        }
    }
    pArrayInfo->free_blocks_count = count_idx;

    temp_free = temp_size;

    if (arrayInfo.ldCount > 0) {
        retval = GetLDAssociatedArrays(ctrl_id, arrayInfo.ldInfo[0].targetId, &arrayList, NULL);
        // 寻找几个关联的Array中最小的容量
        if (SML_SUCCESS == retval) {
            for (i = 0; i < arrayList.array_count; i++) {
                if (arrayList.array_refs[i] != array_ref) {
                    count_idx = 0;
                    retval = GetArrayInfo(ctrl_id, arrayList.array_refs[i], &arrayInfo);
                    if (SML_SUCCESS != retval) {
                        continue;
                    }

                    if (arrayInfo.freeCount >= SML_MAX_HOLES_IN_ARRAY) {
                        return SML_ERR_DATA_INVALID;
                    }

                    temp_size = 0;
                    for (idx = 0; idx < arrayInfo.freeCount; idx++) {
                        // 计算所有剩余块的容量之和
                        if (arrayInfo.freeInfo[idx].numBlocks >= MB2BLK(SML_LD_MIN_SIZE, block_size)) {
                            temp_size += arrayInfo.freeInfo[idx].numBlocks;
                            if (count_idx < pArrayInfo->free_blocks_count) {
                                single_free = BLK2MB(arrayInfo.freeInfo[idx].numBlocks, block_size);
                                pArrayInfo->free_blocks_space[count_idx] =
                                    MIN(single_free, pArrayInfo->free_blocks_space[count_idx]);
                                count_idx++;
                            }
                        }
                    }

                    if (temp_size < temp_free) {
                        temp_free = temp_size;
                    }
                }
            }
        }
    }

    if (temp_free < MB2BLK(SML_LD_MIN_SIZE, block_size)) {
        temp_free = 0;
    }

    // 计算Array上同一块的容量之和
    available_pd_count = GetAvailablePdCount(arrayInfo.ldInfo[0].PRL, pArrayInfo->pd_count);
    for (idx = 0; idx < pArrayInfo->free_blocks_count; idx++) {
        pArrayInfo->free_blocks_space[idx] *= available_pd_count;
    }

    // 计算Array上所有硬盘的剩余容量
    pArrayInfo->total_free_space = BLK2MB(temp_free, block_size) * available_pd_count;

    return SML_SUCCESS;
}

static gint32 check_hotspare_cmd_ret(gint32 ret, guint32 ctrl_id, guint16 device_id, guint8 expected_state)
{
    if (ret != MFI_STAT_INVALID_SEQUENCE_NUMBER) {
        return ret;
    }

    MR_PD_INFO pdInfo;
    (void)memset_s(&pdInfo, sizeof(pdInfo), 0, sizeof(pdInfo));
    (void)GetPDInfo(ctrl_id, device_id, &pdInfo);

    if (GetPDHotSpareState(&pdInfo) == expected_state) {
        return SML_SUCCESS;
    }

    return SML_ERR_CTRL_STATUS_INVALID;
}

/*
 * Description: 执行设置全局热备盘的LSI libstore命令
 * History: 2016年11月22日  () 新生成函数
*/
static gint32 SetPDGlobalHotSpareCmd(guint32 ctrl_id, guint16 device_id, guint16 seq_num)
{
    guint8 libType = 0;
    SL_LIB_CMD_PARAM_T libCmdParam;

    (void)memset_s(&libCmdParam, SL_LIB_CMD_PARAM_S, 0, SL_LIB_CMD_PARAM_S);

    libType = LSI_STORELIB_TYPE_VALID_BIT(ctrl_id);

    libCmdParam.cmdType = SL_PD_CMD_TYPE;
    libCmdParam.cmd = SL_MAKE_GLOBAL_HOTSPARE;
    libCmdParam.ctrlId = ctrl_id;
    libCmdParam.pdRef.deviceId = device_id;
    libCmdParam.pdRef.seqNum = seq_num;

    gint32 ret = ProcessLibCommandCall(libType, &libCmdParam);

    return check_hotspare_cmd_ret(ret, ctrl_id, device_id, PD_HOT_SPARE_GLOBAL);
}

/*
 * Description: 执行取消热备盘的LSI libstore命令
 * History: 2016年11月22日  () 新生成函数
*/
static gint32 RemovePDHotSpareCmd(guint32 ctrl_id, guint16 device_id, guint16 seq_num)
{
    guint8 libType = 0;
    SL_LIB_CMD_PARAM_T libCmdParam;

    (void)memset_s(&libCmdParam, SL_LIB_CMD_PARAM_S, 0, SL_LIB_CMD_PARAM_S);

    libType = LSI_STORELIB_TYPE_VALID_BIT(ctrl_id);

    libCmdParam.cmdType = SL_PD_CMD_TYPE;
    libCmdParam.cmd = SL_REMOVE_HOTSPARE;
    libCmdParam.ctrlId = ctrl_id;
    libCmdParam.pdRef.deviceId = device_id;
    libCmdParam.pdRef.seqNum = seq_num;

    gint32 ret = ProcessLibCommandCall(libType, &libCmdParam);

    return check_hotspare_cmd_ret(ret, ctrl_id, device_id, PD_HOT_SPARE_NONE);
}

/*
 * Description: 执行取消热备盘的LSI libstore命令
 * History: 2016年11月22日  () 新生成函数
*/
static gint32 SetPDStateCmd(guint32 ctrl_id, guint16 device_id, guint16 seq_num, guint16 pd_state)
{
    guint8 libType = 0;
    SL_LIB_CMD_PARAM_T libCmdParam;
    errno_t memcpy_s_ret = EOK;
    (void)memset_s(&libCmdParam, SL_LIB_CMD_PARAM_S, 0, SL_LIB_CMD_PARAM_S);
    gint32 ret;

    libType = LSI_STORELIB_TYPE_VALID_BIT(ctrl_id);

    libCmdParam.cmdType = SL_PD_CMD_TYPE;
    libCmdParam.cmd = SL_SET_PD_STATE;
    libCmdParam.ctrlId = ctrl_id;
    libCmdParam.pdRef.deviceId = device_id;
    libCmdParam.pdRef.seqNum = seq_num;
    memcpy_s_ret = memcpy_s(&libCmdParam.cmdParam_1b[0], sizeof(guint16), &pd_state, sizeof(guint16));
    if (memcpy_s_ret != EOK) {
        debug_log(DLOG_ERROR, "%s: memcpy_s fail, ret = %d\n", __FUNCTION__, memcpy_s_ret);
    }

    ret = ProcessLibCommandCall(libType, &libCmdParam);
    // 0x11d5(原始值0x802F)可能发生了resume，检查下设置命令是否真的成功
    if (ret == MFI_STAT_INVALID_SEQUENCE_NUMBER || ret == SML_ERR_CTRL_STATUS_INVALID) {
        MR_PD_INFO pdInfo;
        (void)memset_s(&pdInfo, sizeof(pdInfo), 0, sizeof(pdInfo));
        (void)GetPDInfo(ctrl_id, device_id, &pdInfo);
        if (pdInfo.fwState == pd_state) {
            return SML_SUCCESS;
        }

        return SML_ERR_CTRL_STATUS_INVALID;
    }
    return ret;
}

/*
 * Description: 执行取消热备盘的LSI libstore命令
 * History: 2016年11月22日  () 新生成函数
*/
static gint32 SetPDDedicatedHotSpareCmd(guint32 ctrl_id, guint16 device_id, guint16 seq_num, SL_ARRAY_LIST_T *pList)
{
    guint8 libType = 0;
    SL_LIB_CMD_PARAM_T libCmdParam;

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

    (void)memset_s(&libCmdParam, SL_LIB_CMD_PARAM_S, 0, SL_LIB_CMD_PARAM_S);

    libType = LSI_STORELIB_TYPE_VALID_BIT(ctrl_id);

    libCmdParam.cmdType = SL_PD_CMD_TYPE;
    libCmdParam.cmd = SL_MAKE_DEDICATED_HOTSPARE;
    libCmdParam.ctrlId = ctrl_id;
    libCmdParam.pdRef.deviceId = device_id;
    libCmdParam.pdRef.seqNum = seq_num;
    libCmdParam.dataSize = SL_ARRAY_LIST_S;
    libCmdParam.pData = pList;

    gint32 ret = ProcessLibCommandCall(libType, &libCmdParam);

    return check_hotspare_cmd_ret(ret, ctrl_id, device_id, PD_HOT_SPARE_DEDICATED);
}

/*
 * Description: 设置指定的物理盘为全局热备盘
 * History: 2016年11月22日  () 新生成函数
*/
static gint32 SetPDGlobalHotSpare(guint32 ctrl_id, guint16 device_id)
{
    gint32 retval = SML_SUCCESS;
    MR_PD_INFO pdInfo;
    guint16 seq_num = 0;

    retval = GetPDInfo(ctrl_id, device_id, &pdInfo);
    if (SML_SUCCESS != retval) {
        debug_log(DLOG_ERROR,
            "smlib LSI: SetPDGlobalHotSpare -> Get PD info failed, CtrlId = %d, pd ID = %d, return 0x%0x\n",
            SML_CTRL_ID_VALID_BIT(ctrl_id), device_id, retval);
        return retval;
    }

    if (!pdInfo.allowedOps.makeSpare) {
        retval = SML_ERR_PD_MAKESPARE_NOT_ALLOWED;
        debug_log(DLOG_ERROR,
            "smlib LSI: SetPDGlobalHotSpare -> PD not allowed makeSpare, CtrlId = %d, pd ID = %d, return 0x%0x\n",
            SML_CTRL_ID_VALID_BIT(ctrl_id), device_id, retval);
        return retval;
    }

    seq_num = pdInfo.ref.seqNum;
    retval = SetPDGlobalHotSpareCmd(ctrl_id, device_id, seq_num);
    if (SML_SUCCESS != retval) {
        debug_log(DLOG_ERROR,
            "smlib LSI: SetPDGlobalHotSpare -> Set PD Global Hot Spare failed, CtrlId = %d, pd ID = %d, return 0x%0x\n",
            SML_CTRL_ID_VALID_BIT(ctrl_id), device_id, retval);
    }

    return retval;
}

/*
 * Description: 设置物理盘为指定逻辑盘的局部热备盘
 * History: 2016年11月22日  () 新生成函数
*/
static gint32 SetPDDedicatedHotSpare(guint32 ctrl_id, guint16 device_id, guint8 ld_target_id)
{
    gint32 retval = SML_SUCCESS;
    MR_PD_INFO pdInfo = { 0 };
    guint8 pd_media_type = 0, pd_interface_type = 0;
    guint16 seq_num = 0;
    MR_CTRL_INFO ctrlInfo;
    guint8 ssd_mix_allowed = 0, ssd_hdd_mix_allowed = 0;
    SML_ARRAY_LIST_S array_list;
    SL_ARRAY_INFO_T array_info;
    guint16 idx = 0;
    SL_ARRAY_LIST_T spare_array_list;
    MR_LD_INFO ldInfo;

    (void)memset_s(&pdInfo, sizeof(pdInfo), 0, sizeof(pdInfo));
    (void)memset_s(&ctrlInfo, sizeof(ctrlInfo), 0, sizeof(ctrlInfo));
    (void)memset_s(&array_list, sizeof(array_list), 0, sizeof(array_list));
    (void)memset_s(&array_info, sizeof(array_info), 0, sizeof(array_info));

    // 获取物理盘的sequence number, 设置局部热备的命令需要
    retval = GetPDInfo(ctrl_id, device_id, &pdInfo);
    if (SML_SUCCESS != retval) {
        debug_log(DLOG_ERROR,
            "smlib LSI: SetPDDedicatedHotSpare -> Get PD info failed, CtrlId = %d, pd ID = %d, return 0x%0x\n",
            SML_CTRL_ID_VALID_BIT(ctrl_id), device_id, retval);
        return retval;
    }

    seq_num = pdInfo.ref.seqNum;
    pd_media_type = pdInfo.mediaType;
    pd_interface_type = pdInfo.state.ddf.pdType.intf & 0xF;

    // 获取控制器的信息, 判断物理盘是否适合为指定逻辑盘的热备需要用到判断条件
    (void)memset_s(&ctrlInfo, sizeof(MR_CTRL_INFO), 0, sizeof(MR_CTRL_INFO));
    retval = GetCtrlInfo(ctrl_id, &ctrlInfo);
    if (SML_SUCCESS != retval) {
        debug_log(DLOG_ERROR, "smlib LSI: SetPDDedicatedHotSpare -> GetCtrlInfo failed, CtrlId = %d, return 0x%0x\n",
            SML_CTRL_ID_VALID_BIT(ctrl_id), retval);
        return retval;
    }

    ssd_mix_allowed = ctrlInfo.pdMixSupport.allowSSDMixInLD;        // 是否允许不同接口的SDD混合在一个逻辑盘里
    ssd_hdd_mix_allowed = ctrlInfo.pdMixSupport.allowMixSSDHDDInLD; // 是否允许SSD和HDD混合在一个逻辑盘里

    // 获取LD的信息，需要判断RAID级别，RAID0不能设置局部热备，同时LD的信息可以直接传入GetLDAssociatedArrays，避免再从RAID卡读取一次信息
    (void)memset_s(&ldInfo, sizeof(MR_LD_INFO), 0, sizeof(MR_LD_INFO));
    retval = GetLDInfo(ctrl_id, ld_target_id, &ldInfo);
    if (SML_SUCCESS != retval) {
        debug_log(DLOG_ERROR,
            "smlib LSI: SetPDDedicatedHotSpare -> Get LD info failed, CtrlId = %d, LD ID = %d, return 0x%0x\n",
            SML_CTRL_ID_VALID_BIT(ctrl_id), ld_target_id, retval);
        return retval;
    }

    // RAID逻辑盘没有冗余数据，无法进行热备，不能指定局部热备盘
    if (RAID_LEVEL_0 == ldInfo.ldConfig.params.PRL) {
        retval = SML_ERR_PD_SPARE_FOR_RAID0_LD;
        debug_log(DLOG_ERROR,
            "smlib LSI: SetPDDedicatedHotSpare -> Try to make spare for RAID0 LD, CtrlId = %d, PD ID = %d, LD ID = %d, "
            "return 0x%0x\n",
            SML_CTRL_ID_VALID_BIT(ctrl_id), device_id, ld_target_id, retval);
        return retval;
    }

    // 获取指定的逻辑盘关联的Array列表
    retval = GetLDAssociatedArrays(ctrl_id, ld_target_id, &array_list, &ldInfo);
    if (SML_SUCCESS != retval) {
        debug_log(DLOG_ERROR,
            "smlib LSI: SetPDDedicatedHotSpare -> Get LD associated arrays failed, CtrlId = %d, ld id = %d, return "
            "0x%0x\n",
            SML_CTRL_ID_VALID_BIT(ctrl_id), ld_target_id, retval);
        return retval;
    }

    // 判断要设置的物理盘是否适合做为逻辑盘的Array热备
    // 对于局部热备, LSI FW是针对Array做局部热备的

    // 获取第一个Array的信息以获取组成这个Array的物理盘列表
    retval = GetArrayInfo(ctrl_id, array_list.array_refs[0], &array_info);
    if (SML_SUCCESS != retval) {
        debug_log(DLOG_ERROR,
            "smlib LSI: SetPDDedicatedHotSpare -> Get Array Info failed, CtrlId = %d, array id = %d, return 0x%0x\n",
            SML_CTRL_ID_VALID_BIT(ctrl_id), array_list.array_refs[0], retval);
        return retval;
    }

    // 判断物理盘是否适合做这个array的热备, 只需与Array中的第一个盘比较
    retval = GetPDInfo(ctrl_id, array_info.array.pd[0].ref.deviceId, &pdInfo);
    if (SML_SUCCESS != retval) {
        debug_log(DLOG_ERROR,
            "smlib LSI: SetPDDedicatedHotSpare -> Get Array PD info failed, CtrlId = %d, pd ID = %d, return 0x%0x\n",
            SML_CTRL_ID_VALID_BIT(ctrl_id), array_info.array.pd[0].ref.deviceId, retval);
        return retval;
    }

    if ((MR_PD_MEDIA_TYPE_SSD == pd_media_type) && (0 == ssd_mix_allowed)) {
        // 不允许不同接口的SSD混接在一个逻辑盘，热备盘与成员盘接口不一致，无法热备
        if ((MR_PD_MEDIA_TYPE_SSD == pdInfo.mediaType) && (pd_interface_type != (pdInfo.state.ddf.pdType.intf & 0xF))) {
            retval = SML_ERR_PD_SPARE_SDD_SAS_SATA_MIXED;
            debug_log(DLOG_ERROR,
                "smlib LSI: SetPDDedicatedHotSpare -> PD not suitable for spare: SSD mix found, CtrlId = %d, pd = %d, "
                "array pd = %d, return 0x%0x\n",
                SML_CTRL_ID_VALID_BIT(ctrl_id), ld_target_id, array_info.array.pd[0].ref.deviceId, retval);
            return retval;
        }
    }

    // 不允许SSD和HDD混接在一个逻辑盘，热备盘和成员盘介质类型不一致则无法热备
    if ((0 == ssd_hdd_mix_allowed) && (pd_media_type != pdInfo.mediaType)) {
        retval = SML_ERR_PD_SPARE_SDD_HDD_MIXED;
        debug_log(DLOG_ERROR,
            "smlib LSI: SetPDDedicatedHotSpare -> PD not suitable for spare: SSD HDD mix found, CtrlId = %d, pd = %d, "
            "array pd = %d, return 0x%0x\n",
            SML_CTRL_ID_VALID_BIT(ctrl_id), ld_target_id, array_info.array.pd[0].ref.deviceId, retval);
        return retval;
    }

    // 我们设定1个物理盘只能为1个逻辑盘上关联Array做热备, 所以不需要考虑当前物理盘存在的热备，直接覆盖原先设置即可
    // 这个设计是参考DELL的做法
    (void)memset_s(&spare_array_list, sizeof(SL_ARRAY_LIST_S), 0, sizeof(SL_ARRAY_LIST_S));
    spare_array_list.count = array_list.array_count;

    for (idx = 0; idx < array_list.array_count; idx++) {
        spare_array_list.ref[idx] = array_list.array_refs[idx];
    }

    // 发送命令将逻辑盘的所有Array添加关联到物理盘的局部热备列表
    retval = SetPDDedicatedHotSpareCmd(ctrl_id, device_id, seq_num, &spare_array_list);
    if (SML_SUCCESS != retval) {
        debug_log(DLOG_ERROR,
            "smlib LSI: SetPDDedicatedHotSpare -> Set PD Dedicated Hot Spare failed, CtrlId = %d, pd = %d, ld = %d, "
            "return 0x%0x\n",
            SML_CTRL_ID_VALID_BIT(ctrl_id), device_id, ld_target_id, retval);
    }

    return retval;
}

/*
 * Description: 取消指定物理盘的热备盘设定
 * History: 2016年11月22日  () 新生成函数
*/
static gint32 RemovePDHotSpare(guint32 ctrl_id, guint16 device_id)
{
    gint32 retval = SML_SUCCESS;
    MR_PD_INFO pdInfo;
    guint16 seq_num = 0;

    (void)memset_s(&pdInfo, sizeof(pdInfo), 0, sizeof(pdInfo));
    retval = GetPDInfo(ctrl_id, device_id, &pdInfo);
    if (SML_SUCCESS != retval) {
        debug_log(DLOG_ERROR,
            "smlib LSI: RemovePDHotSpare -> Get PD info failed, CtrlId = %d, pd ID = %d, return 0x%0x\n",
            SML_CTRL_ID_VALID_BIT(ctrl_id), device_id, retval);
        return retval;
    }

    seq_num = pdInfo.ref.seqNum;
    retval = RemovePDHotSpareCmd(ctrl_id, device_id, seq_num);
    if (SML_SUCCESS != retval) {
        debug_log(DLOG_ERROR,
            "smlib LSI: RemovePDHotSpare -> Remove Hot Spare failed, CtrlId = %d, pd ID = %d, return 0x%0x\n",
            SML_CTRL_ID_VALID_BIT(ctrl_id), device_id, retval);
    }

    return retval;
}

/*
 * Description: 创建EPD（enhanced physical drive）
 */
static gint32 CreateEPD(guint32 ctrl_id, guint16 device_id, guint16 seq_num)
{
    guint8 libType = 0;
    MR_PD_REF pdRef;
    SL_DCMD_INPUT_T dcmdInput;
    SL_LIB_CMD_PARAM_T libCmdParam;

    (void)memset_s(&pdRef, sizeof(pdRef), 0, sizeof(pdRef));
    (void)memset_s(&dcmdInput, SL_DCMD_INPUT_S, 0, SL_DCMD_INPUT_S);
    (void)memset_s(&libCmdParam, SL_LIB_CMD_PARAM_S, 0, SL_LIB_CMD_PARAM_S);

    libType = LSI_STORELIB_TYPE_VALID_BIT(ctrl_id);

    pdRef.deviceId = device_id;
    pdRef.seqNum = seq_num;
    dcmdInput.opCode = MR_DCMD_CFG_MAKE_EPD;
    dcmdInput.flags = SL_DIR_NONE;
    errno_t securec_rv = memcpy_s(&dcmdInput.mbox.w[0], sizeof(dcmdInput.mbox), &pdRef, sizeof(pdRef));
    if (securec_rv != EOK) {
        debug_log(DLOG_ERROR, "%s: memcpy_s failed, ret = %d", __FUNCTION__, securec_rv);
        return SML_ERR_SEC_FUNC_FAILED;
    }

    libCmdParam.cmdType = SL_PASSTHRU_CMD_TYPE;
    libCmdParam.cmd = SL_DCMD_PASSTHRU;
    libCmdParam.ctrlId = ctrl_id;
    libCmdParam.dataSize = SL_DCMD_INPUT_S;
    libCmdParam.pData = &dcmdInput;

    return ProcessLibCommandCall(libType, &libCmdParam);
}

/*
 * Description: 获取硬盘关联的逻辑盘
 */
static gint32 GetLDOfPD(guint32 ctrl_id, guint16 device_id, SL_LD_OF_PD_T *ld_of_pd)
{
    guint8 libType;
    SL_LIB_CMD_PARAM_T libCmdParam;

    (void)memset_s(&libCmdParam, SL_LIB_CMD_PARAM_S, 0, SL_LIB_CMD_PARAM_S);

    libType = LSI_STORELIB_TYPE_VALID_BIT(ctrl_id);

    libCmdParam.cmdType = SL_PD_CMD_TYPE;
    libCmdParam.cmd = SL_GET_LD_OF_PD;
    libCmdParam.ctrlId = ctrl_id;
    libCmdParam.dataSize = SL_LD_OF_PD_S;
    libCmdParam.pData = ld_of_pd;
    libCmdParam.pdRef.deviceId = device_id;

    return ProcessLibCommandCall(libType, &libCmdParam);
}

/*
 * Description: 删除EPD（enhanced physical drive）
 */
static gint32 DeleteEPD(guint32 ctrl_id, guint16 device_id)
{
    gint32 ret;
    SL_LD_OF_PD_T ld_of_pd;
    guint32 i;

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

    ret = GetLDOfPD(ctrl_id, device_id, &ld_of_pd);
    if (ret != SL_SUCCESS) {
        debug_log(DLOG_ERROR, "%s: GetLDOfPD failed, ret = 0x%x.", __FUNCTION__, ret);
        return ret;
    }

    for (i = 0; i < ld_of_pd.count; i++) {
        ret = DeleteLD(ctrl_id, ld_of_pd.targetId[i]);
        if (ret != SL_SUCCESS) {
            debug_log(DLOG_ERROR, "%s: DeleteLD failed, ret = 0x%x.", __FUNCTION__, ret);
            continue;
        }
    }

    return ret;
}

/*
 * Description: 获取硬盘状态原始值
 */
static guint16 get_pd_raw_state(guint8 state, guint16 *pd_state)
{
    switch (state) {
        case PD_STATE_UNCONFIGURED_GOOD:
            *pd_state = MR_PD_STATE_UNCONFIGURED_GOOD;
            break;

        case PD_STATE_UNCONFIGURED_BAD:
            *pd_state = MR_PD_STATE_UNCONFIGURED_BAD;
            break;

        case PD_STATE_OFFLINE:
            *pd_state = MR_PD_STATE_OFFLINE;
            break;

        case PD_STATE_FAILED:
            *pd_state = MR_PD_STATE_FAILED;
            break;

        case PD_STATE_REBUILD:
            *pd_state = MR_PD_STATE_REBUILD;
            break;

        case PD_STATE_ONLINE:
            *pd_state = MR_PD_STATE_ONLINE;
            break;

        case PD_STATE_SYSTEM:
            *pd_state = MR_PD_STATE_SYSTEM;
            break;

        default:
            return SML_ERR_PD_STATE_UNSUPPORTED_TO_SET;
    }

    return SML_SUCCESS;
}

/*
 * Description: 设置指定物理盘的固件状态
 * History: 2016年11月22日  () 新生成函数
*/
static gint32 SetPDState(guint32 ctrl_id, guint16 device_id, SML_PD_STATE_PARAM_S *state_param)
{
    gint32 retval = SML_SUCCESS;
    MR_PD_INFO pdInfo;
    guint16 seq_num = 0;
    guint16 pd_state = MR_PD_STATE_UNCONFIGURED_GOOD;

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

    retval = get_pd_raw_state(state_param->state, &pd_state);
    if (retval != SML_SUCCESS) {
        debug_log(DLOG_ERROR,
            "smlib LSI: SetPDState -> Unsupported PD state to set, CtrlId = %d, pd ID = %d, state = %d, return "
            "0x%0x\n",
            SML_CTRL_ID_VALID_BIT(ctrl_id), device_id, state_param->state, retval);
        return retval;
    }

    retval = GetPDInfo(ctrl_id, device_id, &pdInfo);
    if (SML_SUCCESS != retval) {
        debug_log(DLOG_ERROR, "smlib LSI: SetPDState -> Get PD info failed, CtrlId = %d, pd ID = %d, return 0x%0x\n",
            SML_CTRL_ID_VALID_BIT(ctrl_id), device_id, retval);
        return retval;
    }

    seq_num = pdInfo.ref.seqNum;

    // 只有物理盘当前的状态和需要设置的状态不一致的时候才需要进行实际的设置动作
    if (pdInfo.fwState != pd_state) {
        if (pd_state == MR_PD_STATE_ONLINE && pdInfo.fwState == MR_PD_STATE_UNCONFIGURED_GOOD &&
            state_param->support_epd) {
            retval = CreateEPD(ctrl_id, device_id, seq_num);
        } else if (pd_state == MR_PD_STATE_UNCONFIGURED_GOOD && pdInfo.fwState == MR_PD_STATE_ONLINE &&
                   pdInfo.properties.isEPD && state_param->support_epd) {
            retval = DeleteEPD(ctrl_id, device_id);
        } else {
            retval = SetPDStateCmd(ctrl_id, device_id, seq_num, pd_state);
        }

        // device not found也可指LD，因此转换不放在ProcessLibCommandCall，在PD命令单独转"PD ID无效"
        if (retval == MFI_STAT_DEVICE_NOT_FOUND) {
            retval = SML_ERR_PD_INVALID_DEVICE_ID;
        }

        if (SML_SUCCESS != retval) {
            debug_log(DLOG_ERROR,
                "smlib LSI: SetPDState -> Set PD State failed, CtrlId = %d, pd ID = %d, state = %d, return 0x%0x\n",
                SML_CTRL_ID_VALID_BIT(ctrl_id), device_id, state_param->state, retval);
            return retval;
        }
    }

    return SML_SUCCESS;
}
/* END:   Added on 2016/11/22 */

/* END:   Added on 2016/11/22 */

/* BEGIN  AR.SR.SFEA02130924.051.001 加密盘安全擦除 , 2018/9/26 */

/*
 * Description: EraseSED
 * History: 2018年9月26日  新生成函数
*/
static gint32 CryptoErase(guint32 ctrl_id, guint16 device_id)
{
    gint32 retval = SML_SUCCESS;
    MR_PD_INFO pdInfo;
    guint16 seq_num = 0;
    guint16 fde_capable = 0;
    guint16 is_reprovision = 0;
    guint8 libType = 0;
    SL_LIB_CMD_PARAM_T libCmdParam;
    SL_DCMD_INPUT_T dcmdInput;

    (void)memset_s(&pdInfo, sizeof(pdInfo), 0, sizeof(pdInfo));
    (void)memset_s(&libCmdParam, SL_LIB_CMD_PARAM_S, 0, SL_LIB_CMD_PARAM_S);
    (void)memset_s(&dcmdInput, sizeof(dcmdInput), 0, sizeof(dcmdInput));

    retval = GetPDInfo(ctrl_id, device_id, &pdInfo);
    if (SML_SUCCESS != retval) {
        debug_log(DLOG_ERROR, "smlib LSI: CryptoErase -> Get PD info failed, CtrlId = %d, pd ID = %d, return 0x%0x\n",
            SML_CTRL_ID_VALID_BIT(ctrl_id), device_id, retval);
        return retval;
    }

    seq_num = pdInfo.ref.seqNum;
    fde_capable = pdInfo.security.fdeCapable;
    is_reprovision = pdInfo.allowedOps.reprovision;
    libCmdParam.pdRef.seqNum = seq_num;
    libCmdParam.pdRef.deviceId = device_id;

    libType = LSI_STORELIB_TYPE_VALID_BIT(ctrl_id);
    debug_log(DLOG_DEBUG, "smlib LSI: CryptoErase: CtrlId = %d, pd ID = %d, fde_capable = %d, is_reprovision = %d",
        SML_CTRL_ID_VALID_BIT(ctrl_id), device_id, fde_capable, is_reprovision);

    // 只有物理盘支持加密的时候才需要进行擦除动作
    if (fde_capable) {
        libCmdParam.ctrlId = ctrl_id;
        libCmdParam.cmd = SL_DCMD_PASSTHRU;
        libCmdParam.dataSize = SL_DCMD_INPUT_S;
        libCmdParam.cmdType = SL_PASSTHRU_CMD_TYPE;

        if (is_reprovision) {
            dcmdInput.opCode = MR_DCMD_PD_REPROVISION;
        } else {
            dcmdInput.opCode = MR_DCMD_PD_RECOVER_LOCKED;
        }

        dcmdInput.mbox.w[0] = (guint32)((seq_num << 16) | device_id);
        libCmdParam.pData = &dcmdInput;

        return ProcessLibCommandCall(libType, &libCmdParam);
    }

    return SML_ERR_PD_OPERATION_NOT_SUPPORT;
}
/* END:   Added on 2018/9/26 */

/*
 * Description: SAS硬盘执行short dst
 * History: 2019年1月5日 新生成函数
*/
static gint32 SetSASShortDST(guint32 ctrl_id, guint16 device_id)
{
    gint32 retval = SML_SUCCESS;
    SCSI_SENSE_DISECT_S SenInfo = { 0 };

    // 获取sense 信息
    retval = SendSCSIRequestSenseCommand(ctrl_id, device_id, &SenInfo);
    debug_log(DLOG_DEBUG, "Send SCSI %d.%d request sense return 0x%x\n", SML_CTRL_ID_VALID_BIT(ctrl_id), device_id,
        retval);

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

    // 校验当前是否正在进行测试
    if (0x04 == SenInfo.asc && 0x09 == SenInfo.ascq) {
        return SML_ERR_PD_IS_SMART_TEST;
    }

    // 发送命令触发short dst
    retval = SendSCSIDiagnosticCommand(ctrl_id, device_id, SCSI_CMD_DIAG_BG_SHORT_SELF_TEST);

    return retval;
}

/*
 * Description: SAS硬盘执行short dst
 * History: 2019年1月5日 新生成函数
*/
static gint32 SetSATAShortDST(guint32 ctrl_id, guint16 device_id)
{
    gint32 retval = SML_SUCCESS;
    // 发送命令触发short dst
    SL_SCSI_PASSTHRU_T *scsi_passthru_cmd = NULL;
    guint32 pscsi_passthru_len = 0;

    pscsi_passthru_len = (guint32)sizeof(SL_SCSI_PASSTHRU_T);

    scsi_passthru_cmd = (SL_SCSI_PASSTHRU_T *)g_malloc0(pscsi_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_NONE;
    scsi_passthru_cmd->timeout = SCSI_CMD_TIME_OUT;
    scsi_passthru_cmd->cdbLength = SCSI_CDB_LENGTH16;
    scsi_passthru_cmd->cdb[0] = 0x85;
    scsi_passthru_cmd->cdb[1] = 0x06; // no data
    scsi_passthru_cmd->cdb[2] = 0x0c; // 忽略数据传输
    scsi_passthru_cmd->cdb[3] = 0x00;
    scsi_passthru_cmd->cdb[4] = 0xd4;
    scsi_passthru_cmd->cdb[5] = 0x00;
    scsi_passthru_cmd->cdb[6] = 0x00;
    scsi_passthru_cmd->cdb[7] = 0x00;
    scsi_passthru_cmd->cdb[8] = 0x01;
    scsi_passthru_cmd->cdb[9] = 0x00;
    scsi_passthru_cmd->cdb[10] = 0x4f;
    scsi_passthru_cmd->cdb[11] = 0x00;
    scsi_passthru_cmd->cdb[12] = 0xc2;
    scsi_passthru_cmd->cdb[13] = 0x00;
    scsi_passthru_cmd->cdb[14] = 0xb0;
    scsi_passthru_cmd->cdb[15] = 0x00;
    scsi_passthru_cmd->dataSize = 0;

    retval = FireSCSIPassthruCmd(ctrl_id, scsi_passthru_cmd);

    g_free(scsi_passthru_cmd);
    return retval;
}

/*
 * Description: 硬盘执行自检，目前主要是sas和sata盘的short dst
 * History: 2019年1月5日 新生成函数
*/
static gint32 SetPDSmartTest(guint32 ctrl_id, guint16 device_id, guint8 test_type)
{
    gint32 retval = SML_SUCCESS;
    switch (test_type) {
        case PD_TEST_SATA_SHORT_OFFLINE:
            retval = SetSATAShortDST(ctrl_id, device_id);
            break;
        case PD_TEST_SAS_BACKGROUND_SHORT:
            retval = SetSASShortDST(ctrl_id, device_id);
            break;
        default:
            break;
    }
    return retval;
}

/*
 * Description: Expander背板通信故障诊断
 * History: 2019年1月7日  新生成函数
*/
gint32 lsi_diag_encl_comm_error(guint32 ctrl_id, guint16 pd_device_id, guint16 encl_device_id, guint8 is_encl_single,
    gpointer data)
{
    gint32 ret = SML_SUCCESS;
    guint32 *encl_comm_error_state = (guint32 *)data;

    ret = DiagnoseEnclCommErr(ctrl_id, pd_device_id, encl_device_id, is_encl_single, encl_comm_error_state);

    return ret;
}

/*
 * Description: 硬盘sense code诊断
 * History: 2019年1月10日  新生成函数
*/
gint32 lsi_diag_pd_sense_error(guint32 ctrl_id, guint16 pd_device_id, gchar *io_buf, gpointer data)
{
    gint32 ret = SML_SUCCESS;
    guint32 *sense_error_state = (guint32 *)data;

    ret = DiagnosePDSenseError(ctrl_id, pd_device_id, io_buf, sense_error_state);

    return ret;
}

/*
 * Description: 获取PHY误码诊断的拓扑结构信息
 * History: 2019年1月22日  新生成函数
*/
gint32 lsi_get_phy_diag_topo_info(guint32 ctrl_id, guint16 pd_device_id, gpointer data)
{
    gint32 ret = SML_SUCCESS;
    SML_CTRL_PHY_DIAG_TOPO_INFO_S *topo_info = NULL;
    void *topology_buffer = NULL;

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

    /* 初始化拓扑解析数据 */
    topo_info = (SML_CTRL_PHY_DIAG_TOPO_INFO_S *)data;
    (void)memset_s(topo_info, sizeof(SML_CTRL_PHY_DIAG_TOPO_INFO_S), 0, sizeof(SML_CTRL_PHY_DIAG_TOPO_INFO_S));

    /* 获取拓扑结构数据 */
    topology_buffer = g_malloc0(SL_TOPOLOGY_BUFFER_SIZE);
    if (topology_buffer == NULL) {
        debug_log(DLOG_ERROR, "%s : Alloc topo buffer failed", __FUNCTION__);
        return SML_ERR_CANNOT_ALLOC_MEM;
    }

    ret = GetTopologyInfo(ctrl_id, SL_TOPOLOGY_BUFFER_SIZE, topology_buffer);
    if (ret != SL_SUCCESS) {
        g_free(topology_buffer);
        debug_log(DLOG_ERROR, "%s : Get topology info failed, ctrl_id = 0x%x, ret = 0x%x", __FUNCTION__, ctrl_id, ret);
        return ret;
    }

    /* 从拓扑结构数据中解析出PHY误码诊断结构信息 */
    ret = PraseTopoForPHYDaig((guint8 *)topology_buffer, topo_info, pd_device_id);
    g_free(topology_buffer);

    return ret;
}

/*
 * Description: 填充阵列的物理盘id信息
 */
static void fill_array_pd_id(SL_ARRAY_INFO_T *array_info, SML_ARRAY_INFO_S *result)
{
    guint32 i = 0;

    for (guint32 idx = 0; idx < result->pd_count; idx++) {
        if (array_info->array.pd[idx].ref.deviceId == MR_PD_INVALID) {
            continue;
        }

        result->pd_ids[i] = array_info->array.pd[idx].ref.deviceId;
        result->pd_slots[i] = array_info->array.pd[idx].encl.slot;
        result->pd_enclosures[i] =
            (array_info->array.pd[idx].encl.pd == 0xFF) ? 0xFFFF : array_info->array.pd[idx].encl.pd;
        i++;
    }
    result->pd_count = i;

    return;
}

/*
 * Description: 通过LSI storelib获取RAID卡管理之下的特定Array信息
 * History: 2016年11月8日  () 新生成函数
 *          校验返回数据有效性，调用该接口的上层APP，在获取异常时不能更新数据
*/
gint32 lsi_get_array_info(guint32 ctrl_id, guint16 array_ref, gpointer data)
{
    if (data == NULL) {
        return SML_ERR_NULL_DATA;
    }

    gint32 retval = SML_SUCCESS;
    guint32 idx = 0;
    guint16 block_size = 0;
    SL_ARRAY_INFO_T arrayInfo;
    SML_ARRAY_INFO_S *pArrayInfo = (SML_ARRAY_INFO_S *)data;
    guint64 used_space = 0;

    (void)memset_s(&arrayInfo, sizeof(SL_ARRAY_INFO_T), 0, sizeof(SL_ARRAY_INFO_T));

    retval = GetArrayInfo(ctrl_id, array_ref, &arrayInfo);
    if (SML_SUCCESS == retval) {
        if (array_ref != arrayInfo.array.arrayRef || arrayInfo.array.numDrives == 0) {
            debug_log(DLOG_DEBUG,
                "smlib: LSI:Get Array Info failed, CtrlId = %d, Array ref = %d, Array number = %d, "
                "returned  Array ref = %d\n",
                SML_CTRL_ID_VALID_BIT(ctrl_id), array_ref, arrayInfo.array.numDrives, arrayInfo.array.arrayRef);
            return SML_ERR_DATA_INVALID;
        }
        // Array上已被占用的空间,已组成Array的所有硬盘计算容量，单位为block
        used_space = arrayInfo.usedBlocks;

        pArrayInfo->ld_count = arrayInfo.ldCount;

        if (pArrayInfo->ld_count > SML_MAX_LOGICAL_DRIVES_PER_ARRAY) {
            pArrayInfo->ld_count = SML_MAX_LOGICAL_DRIVES_PER_ARRAY;
        }

        for (idx = 0; idx < pArrayInfo->ld_count; idx++) {
            pArrayInfo->ld_ids[idx] = (guint16)arrayInfo.ldInfo[idx].targetId;
        }

        pArrayInfo->pd_count = arrayInfo.array.numDrives;

        if (pArrayInfo->pd_count > SML_MAX_PHYSICAL_DRIVES_PER_ARRAY) {
            pArrayInfo->pd_count = SML_MAX_PHYSICAL_DRIVES_PER_ARRAY;
        }

        fill_array_pd_id(&arrayInfo, pArrayInfo);

        // 将容量单位从block转化为MB
        GetBlockSizeOfPDInArray(ctrl_id, pArrayInfo->pd_ids, pArrayInfo->pd_count, &block_size);

        // 获取Array上的剩余可用空间
        retval = GetFreeInfoOfArray(ctrl_id, array_ref, block_size, pArrayInfo, &arrayInfo);
        if (retval != SML_SUCCESS) {
            return retval;
        }

        if (pArrayInfo->pd_count == 0) {
            debug_log(DLOG_ERROR, "smlib: LSI :pArrayInfo->pd_count is zero.\n");
            return SML_ERR_DATA_INVALID;
        }
        pArrayInfo->used_space = BLK2MB_PLUS_HALF_MB(used_space, block_size) *
            GetAvailablePdCount(arrayInfo.ldInfo[0].PRL, pArrayInfo->pd_count) / pArrayInfo->pd_count;
    } else {
        debug_log(DLOG_ERROR, "smlib: LSI:Get Array Info failed, CtrlId = %d, Array ref = %d, return 0x%0x\n",
            SML_CTRL_ID_VALID_BIT(ctrl_id), array_ref, retval);
    }

    return retval;
}
/* END:   Added on 2016/11/8 */

/*
 * Description : 发送直通scsi命令，获取SAS VPD页的信息
 * History：2020-2-27 UADP602576 新增
 */
static gint32 GetSASVPDPage(guint32 ctrl_id, guint16 device_id, guint8 vpd_page, guint8 *vpd_data, guint32 data_len)
{
    gint32 retval;
    SL_SCSI_PASSTHRU_T *scsi_passthru_cmd = NULL;
    guint32 pscsi_passthru_len;
    errno_t securec_rv;
    pscsi_passthru_len = (guint32)(sizeof(SL_SCSI_PASSTHRU_T) + SCSI_LOG_PAGE_RESP_LENGTH);

    scsi_passthru_cmd = (SL_SCSI_PASSTHRU_T *)g_malloc0(pscsi_passthru_len);
    if (scsi_passthru_cmd == NULL) {
        retval = SML_ERR_CANNOT_ALLOC_MEM;
        debug_log(DLOG_ERROR, "smlib LSI: CreateLd -> malloc for pld failed, CtrlId = %d, return 0x%0x\n",
            SML_CTRL_ID_VALID_BIT(ctrl_id), retval);
        return retval;
    }

    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_LENGTH6;
    scsi_passthru_cmd->cdb[0] = SCSI_CMD_INQUIRY;
    scsi_passthru_cmd->cdb[1] = 1;          // set EVPD bit (enable Vital Product Data) (参考smartctl代码)
    scsi_passthru_cmd->cdb[2] = vpd_page;   // byte 2 表示VPD 编号
    scsi_passthru_cmd->cdb[4] = SCSI_LOG_PAGE_RESP_LENGTH; // byte 4 表示VPD 页面长度
    scsi_passthru_cmd->dataSize = SCSI_LOG_PAGE_RESP_LENGTH;

    retval = FireSCSIPassthruCmd(ctrl_id, scsi_passthru_cmd);
    if (retval != SML_SUCCESS) {
        debug_log(DLOG_DEBUG, "Get VPD page %u failed, DeviceId = %u, CtrlId = %d, return 0x%04X", vpd_page, device_id,
            SML_CTRL_ID_VALID_BIT(ctrl_id), retval);
        g_free(scsi_passthru_cmd);
        return retval;
    }
    securec_rv = memcpy_s(vpd_data, data_len, scsi_passthru_cmd->data, MIN(scsi_passthru_cmd->dataSize, data_len));
    if (securec_rv != EOK) {
        debug_log(DLOG_ERROR, "%s: memcpy_s fail, ret = %d", __FUNCTION__, securec_rv);
        g_free(scsi_passthru_cmd);
        return SML_ERR_SEC_FUNC_FAILED;
    }
    g_free(scsi_passthru_cmd);

    return SML_SUCCESS;
}

/*
 * Description : 设置pPDinfo的硬盘转速
 */
static void ParsePDRotationSpeed(SML_PD_INFO_S* pPDinfo, guint16 rotationRate)
{
    /*
        Medium Rotation Rate field(byte4 和 byte5)
        Code            Definition(未定义)
        0000h           Medium rotation rate is not reported(无响应)
        0001h           Non-rotating medium (e.g., solid state)(无转速的介质)
        0002h to 0400h  Reserved(预留)
        0401h to FFFEh  Nominal medium rotation rate in revolutions per minute(e.g., 7 200 rpm = 1C20h)(正常转速范围)
        FFFFh           Reserved(预留)
    */
    if (rotationRate == 1) {  // 1 表示SSD硬盘，转速认为是无效值
        pPDinfo->pdinfo.rotation_speed = STORAGE_INFO_INVALID_WORD;
    } else if ((rotationRate < 0x0401 || rotationRate > 0xFFFE)) { // 有效转速范围，0401h to FFFEh
        pPDinfo->pdinfo.rotation_speed = 0;
    } else {
        pPDinfo->pdinfo.rotation_speed = rotationRate;
    }
}

/*
 * Description : 解析设置硬盘尺寸
 */
static void ParsePDFormFactor(SML_PD_INFO_S* pPDinfo, guint8 form_factor)
{
    /*
        Device Nominal Form Factor Value Description
        0h Nominal form factor not reported
        1h 5.25 inch nominal form factor
        2h 3.5 inch nominal form factor
        3h 2.5 inch nominal form factor
        4h 1.8 inch nominal form factor
        5h Less than 1.8 inch nominal form factor
        6h mSATA (see SATA 3.2)
        7h M.2 (see SATA 3.2)
        8h MicroSSD (see SATA 3.2)
        9h CFast
        Ah-Fh Reserved
    */
    pPDinfo->pdinfo.form_factor = form_factor & 0x0f; // Bits[3:0]  form factor of the device
    if (pPDinfo->pdinfo.form_factor > PD_FORM_FACTOR_CFAST) {
        pPDinfo->pdinfo.form_factor = PD_FORM_FACTOR_INVALID;
    }
}

/*
 * Description : 获取SAS盘硬盘转速和尺寸
 */
static void GetSASRotationSpeedAndFormFactor(guint32 ctrl_id, guint16 device_id, SML_PD_INFO_S* pPDinfo)
{
#define DEVICE_CHARACTERISTICS_PAGE 0xb1
#define VPD_FORM_FACTOR_OFFSET      7
    gint32 retval;
    guint8 vpd_data[64] = {0}; // Block Device Characteristics VPD page 响应长度是64

    // Block Device Characteristics VPD page(B1h)(0xb1)(能获取到转速的VPD页)
    retval = GetSASVPDPage(ctrl_id, device_id, DEVICE_CHARACTERISTICS_PAGE, vpd_data, sizeof(vpd_data));
    if (retval != SML_SUCCESS) {
        return;
    }

    if (pPDinfo->pdinfo.media_type == MR_PD_MEDIA_TYPE_ROTATIONAL) {
        // byte1 PAGE CODE (B1h)(0xb1) 校验page code字段
        if (vpd_data[1] != DEVICE_CHARACTERISTICS_PAGE) {
            debug_log(DLOG_DEBUG, "%s: page code invalid, value: %d", __FUNCTION__, vpd_data[1]);
            return;
        }

        ParsePDRotationSpeed(pPDinfo, MAKE_WORD(vpd_data[4], vpd_data[5])); // byte4和5表示转速值
    } else {
        pPDinfo->pdinfo.rotation_speed = STORAGE_INFO_INVALID_WORD;  // 非HDD转速设置为无效值
    }

    ParsePDFormFactor(pPDinfo, vpd_data[VPD_FORM_FACTOR_OFFSET]);
}


/*
 * Description: 根据匹配型号来解析厂商信息
 */

static void GetVendorByModelNumber(SML_PD_INFO_S *pPDinfo)
{
    errno_t safe_fun_ret = EOK;

    for (guint32 i = 0; i < ((sizeof(g_vendor_pn_vendor_name)) / (sizeof(g_vendor_pn_vendor_name[0]))); i++) {
        if (0 == strncmp(pPDinfo->pdinfo.model, g_vendor_pn_vendor_name[i].vendor_pn,
            strlen(g_vendor_pn_vendor_name[i].vendor_pn))) {
            (void)memset_s(pPDinfo->pdinfo.manufacturer, sizeof(pPDinfo->pdinfo.manufacturer), 0,
                sizeof(pPDinfo->pdinfo.manufacturer));
            safe_fun_ret = strncpy_s(pPDinfo->pdinfo.manufacturer, sizeof(pPDinfo->pdinfo.manufacturer),
                g_vendor_pn_vendor_name[i].vendor_name, sizeof(pPDinfo->pdinfo.manufacturer) - 1);
            if (safe_fun_ret != EOK) {
                debug_log(DLOG_ERROR, "%s: strncpy_s fail, ret = %d", __FUNCTION__, safe_fun_ret);
            }
            break;
        }
    }
}

/*
 * Description: 更新SATA盘的厂商、型号和fw版本
 * History: 2019年8月26日 新生成函数
*/
static void UpdateSATABaseInfo(const guint32 ctrl_id, const guint16 device_id, SML_PD_INFO_S *pPDinfo)
{
    IDE_IDENTIFY_DEVICE_T identify;
    guint32 vendor_id = 0;
    errno_t safe_fun_ret = EOK;

    if (pPDinfo == NULL) {
        return;
    }

    debug_log(DLOG_DEBUG, "[%s] smlib: GetSataDeviceIdentifyInfo begin", __FUNCTION__);

    gint32 retval = GetSataDeviceWWN(ctrl_id, device_id, &vendor_id);
    if (retval == SML_SUCCESS) {
        pPDinfo->pdinfo.vendor_id = vendor_id;
        (void)memset_s(pPDinfo->pdinfo.manufacturer, sizeof(pPDinfo->pdinfo.manufacturer), 0,
            sizeof(pPDinfo->pdinfo.manufacturer));
        safe_fun_ret = strncpy_s(pPDinfo->pdinfo.manufacturer, sizeof(pPDinfo->pdinfo.manufacturer),
            GetPDVendorName(vendor_id), sizeof(pPDinfo->pdinfo.manufacturer) - 1);
        if (safe_fun_ret != EOK) {
            debug_log(DLOG_ERROR, "%s: strncpy_s fail, ret = %d\n", __FUNCTION__, safe_fun_ret);
        }
    }

    (void)memset_s(&identify, IDE_IDENTIFY_DEVICE_S, 0, IDE_IDENTIFY_DEVICE_S);
    retval = GetSataDeviceIdentifyInfo(ctrl_id, device_id, (guint8 *)&identify, IDE_IDENTIFY_DEVICE_S);
    if (retval == SML_SUCCESS) {
        (void)memset_s(pPDinfo->pdinfo.model, sizeof(pPDinfo->pdinfo.model), 0, sizeof(pPDinfo->pdinfo.model));
        safe_fun_ret = memcpy_s(pPDinfo->pdinfo.model, sizeof(pPDinfo->pdinfo.model),
            (const gchar *)(identify.data.modelNumber), sizeof(identify.data.modelNumber));
        if (safe_fun_ret != EOK) {
            debug_log(DLOG_ERROR, "%s: memcpy_s fail, ret = %d\n", __FUNCTION__, safe_fun_ret);
        }
        remove_string_space(pPDinfo->pdinfo.model, strlen(pPDinfo->pdinfo.model) + 1);

        (void)memset_s(pPDinfo->pdinfo.firmware_version, sizeof(pPDinfo->pdinfo.firmware_version), 0,
            sizeof(pPDinfo->pdinfo.firmware_version));
        safe_fun_ret = memcpy_s(pPDinfo->pdinfo.firmware_version, sizeof(pPDinfo->pdinfo.firmware_version),
            (const gchar *)(identify.data.firmwareRevision), sizeof(identify.data.firmwareRevision));
        if (safe_fun_ret != EOK) {
            debug_log(DLOG_ERROR, "%s: memcpy_s fail, ret = %d\n", __FUNCTION__, safe_fun_ret);
        }
        pPDinfo->pdinfo.firmware_version[sizeof(pPDinfo->pdinfo.firmware_version) - 1] = '\0';
        remove_string_space(pPDinfo->pdinfo.firmware_version, strlen(pPDinfo->pdinfo.firmware_version) + 1);
        // word[217] 硬盘转速 需要将原word前后8位移位
        ParsePDRotationSpeed(pPDinfo, (((identify.word[217] & 0xFF00) >> 8) | ((identify.word[217] & 0x00FF) << 8)));

        // word[168] 硬盘尺寸 Bits[3:0]，实际需要将word的高低字节互换
        ParsePDFormFactor(pPDinfo, (guint8)((identify.word[168] >> 8) & 0x00FF));

        debug_log(DLOG_DEBUG,
            "smlib: LSI:GetSataDeviceIdentifyInfo CtrlId = %d, SlotId = %d, SN : %s , Model : %s , FW : %s\n",
            SML_CTRL_ID_VALID_BIT(ctrl_id), pPDinfo->pdinfo.slot_num, pPDinfo->pdinfo.serial_num, pPDinfo->pdinfo.model,
            pPDinfo->pdinfo.firmware_version);
    }

    /* 部分硬盘没有wwn，需要根据匹配型号来解析厂商信息 */
    GetVendorByModelNumber(pPDinfo);
}

/*
 * Description: 通过SCSI passthru读取VPD PAGE, 获取SAS Serial Number
 */
static gint32 GetSasDeviceSerialNumber(guint32 ctrl_id, guint16 device_id, gchar *buf, guint32 buf_size)
{
    SL_SCSI_PASSTHRU_T *scsi_passthru_cmd = NULL;
    errno_t safe_fun_ret;
    gint32 ret;

    if (buf == NULL || buf_size == 0) {
        debug_log(DLOG_ERROR, "%s: Serial Number buf null or empty", __FUNCTION__);
        return SML_ERR_NULL_DATA;
    }

    scsi_passthru_cmd =
        (SL_SCSI_PASSTHRU_T *)g_malloc0(sizeof(SL_SCSI_PASSTHRU_T) + SCSI_UNIT_SERIAL_NUMBER_VPD_PAGE_LEN);
    if (scsi_passthru_cmd == NULL) {
        debug_log(DLOG_ERROR, "%s: malloc fail", __FUNCTION__);
        return SML_ERR_CANNOT_ALLOC_MEM;
    }

    scsi_passthru_cmd->targetId = device_id;
    scsi_passthru_cmd->cmd = SL_CMD_OP_PD_SCSI; /* fire to physical device */
    scsi_passthru_cmd->lun = 0;
    scsi_passthru_cmd->dir = SL_DIR_READ;
    scsi_passthru_cmd->timeout = SCSI_CMD_TIME_OUT; /* in seconds */
    scsi_passthru_cmd->cdbLength = SCSI_CDB_LENGTH6;
    scsi_passthru_cmd->cdb[0] = SCSI_CMD_INQUIRY;
    scsi_passthru_cmd->cdb[1] = 0x01; // 0x01 unit serial number vpd page evpd
    scsi_passthru_cmd->cdb[2] = 0x80; // 0x80 unit serial number vpd page code
    scsi_passthru_cmd->cdb[4] = SCSI_UNIT_SERIAL_NUMBER_VPD_PAGE_LEN;
    scsi_passthru_cmd->dataSize = SCSI_UNIT_SERIAL_NUMBER_VPD_PAGE_LEN;

    ret = FireSCSIPassthruCmd(ctrl_id, scsi_passthru_cmd);
    if (ret != SML_SUCCESS) {
        // 希捷sas盘在ub状态下无法获取sn，不记录ERROR级别日志，避免日志刷屏
        debug_log(DLOG_DEBUG, "Get Unit Serial Number VPD Page data failed, DeviceId = %d, CtrlId = %d, return 0x%04X",
            device_id, ctrl_id, ret);
        g_free(scsi_passthru_cmd);
        return ret;
    }

    (void)memset_s(buf, buf_size, 0, buf_size);
    safe_fun_ret = memcpy_s(buf, buf_size, scsi_passthru_cmd->data + 4,
        scsi_passthru_cmd->data[3]); // 4 page header size 3 byte[3] serial number length
    if (safe_fun_ret != EOK) {
        debug_log(DLOG_ERROR, "%s: memcpy_s fail, ret = %d", __FUNCTION__, safe_fun_ret);
        g_free(scsi_passthru_cmd);
        return RET_ERR;
    }
    if (strlen(buf) > 0) {
        buf[buf_size - 1] = '\0';
        remove_string_space(buf, strlen(buf) + 1);
    }

    g_free(scsi_passthru_cmd);
    return SML_SUCCESS;
}

/*
 * Description:获取硬盘基本信息
 */
static void get_pd_basic_info(SML_PD_INFO_S *pPDinfo, MR_PD_INFO *PdInfo, guint32 ctrl_id, guint16 device_id)
{
    gchar old_seiral_num[SML_PD_SN_LENGTH] = {0};

    (void)strncpy_s(old_seiral_num, sizeof(old_seiral_num), pPDinfo->pdinfo.serial_num,
        sizeof(pPDinfo->pdinfo.serial_num) - 1);
    if (pPDinfo->pdinfo.interface_type == PD_INTERFACE_TYPE_SATA) {
        // 产商识别需要修改
        GetSCSIInquiryDataItem(PdInfo, SCSI_INQUIRY_DATA_SN, pPDinfo->pdinfo.serial_num,
            sizeof(pPDinfo->pdinfo.serial_num));
        if (pPDinfo->pdinfo.power_state == PD_POWER_STATE_ACTIVE) {
            UpdateSATABaseInfo(ctrl_id, device_id, pPDinfo);
        } else {
            if (strlen(old_seiral_num) == 0) {
                GetSCSIInquiryDataItem(PdInfo, SCSI_INQUIRY_DATA_MANUFACTURER, pPDinfo->pdinfo.manufacturer,
                    sizeof(pPDinfo->pdinfo.manufacturer));
                GetSCSIInquiryDataItem(PdInfo, SCSI_INQUIRY_DATA_MODEL, pPDinfo->pdinfo.model,
                    sizeof(pPDinfo->pdinfo.model));
                GetSCSIInquiryDataItem(PdInfo, SCSI_INQUIRY_DATA_FW_VERSION, pPDinfo->pdinfo.firmware_version,
                    sizeof(pPDinfo->pdinfo.firmware_version));
            }
        }
    } else {
        GetSCSIInquiryDataItem(PdInfo, SCSI_INQUIRY_DATA_MANUFACTURER, pPDinfo->pdinfo.manufacturer,
            sizeof(pPDinfo->pdinfo.manufacturer));
        GetSCSIInquiryDataItem(PdInfo, SCSI_INQUIRY_DATA_MODEL, pPDinfo->pdinfo.model, sizeof(pPDinfo->pdinfo.model));
        GetSCSIInquiryDataItem(PdInfo, SCSI_INQUIRY_DATA_FW_VERSION, pPDinfo->pdinfo.firmware_version,
            sizeof(pPDinfo->pdinfo.firmware_version));
        GetSasDeviceSerialNumber(ctrl_id, device_id, pPDinfo->pdinfo.serial_num, sizeof(pPDinfo->pdinfo.serial_num));
        // 若硬盘未更换并且转速已经获取到了，不发送scsi直通命令
        if (strcmp(old_seiral_num, pPDinfo->pdinfo.serial_num) != 0 || pPDinfo->pdinfo.rotation_speed == 0) {
            GetSASRotationSpeedAndFormFactor(ctrl_id, device_id, pPDinfo);
        }
    }
    debug_log(DLOG_DEBUG, "smlib: LSI:GetPDInfo CtrlId = %d, SlotId = %d, SN : %s , Model : %s , FW : %s",
        SML_CTRL_ID_VALID_BIT(ctrl_id), pPDinfo->pdinfo.slot_num, pPDinfo->pdinfo.serial_num, pPDinfo->pdinfo.model,
        pPDinfo->pdinfo.firmware_version);
}

/*
 * Description:填充获取到的硬盘基本信息,拆分函数解决函数超大
 */
static void FillPDInfo(SML_PD_INFO_S *pPDinfo, MR_PD_INFO *PdInfo)
{
    pPDinfo->pdinfo.sequence_num = PdInfo->ref.seqNum;
    pPDinfo->pdinfo.fde_capable = PdInfo->security.fdeCapable;
    pPDinfo->pdinfo.scsi_dev_type = PdInfo->scsiDevType;
    pPDinfo->pdinfo.device_speed = PdInfo->deviceSpeed;
    pPDinfo->pdinfo.link_speed = PdInfo->linkSpeed;
    pPDinfo->pdinfo.encl_device_id = PdInfo->enclDeviceId;
    pPDinfo->pdinfo.slot_num = PdInfo->slotNumber;
    pPDinfo->pdinfo.last_prefail_event_seq_num = PdInfo->lastPredFailEventSeqNum;
    pPDinfo->pdinfo.media_type = PdInfo->mediaType;
    pPDinfo->pdinfo.temperature = PdInfo->temperature;
    pPDinfo->pdinfo.media_err_count = PdInfo->mediaErrCount;
    pPDinfo->pdinfo.other_err_count = PdInfo->otherErrCount;
    pPDinfo->pdinfo.prefail_count = PdInfo->predFailCount;
    pPDinfo->pdinfo.health = 0;

    ASSERT_BIT_IF_TRUE(PdInfo->mediaErrCount != 0, pPDinfo->pdinfo.health, 0);
    ASSERT_BIT_IF_TRUE(PdInfo->predFailCount != 0, pPDinfo->pdinfo.health, 1);
    ASSERT_BIT_IF_TRUE(PdInfo->otherErrCount != 0, pPDinfo->pdinfo.health, 2);

    pPDinfo->pdinfo.proginfo.rebuild_state = PdInfo->progInfo.active.rbld;
    pPDinfo->pdinfo.proginfo.patrol_state = PdInfo->progInfo.active.patrol;

    pPDinfo->pdinfo.proginfo.rebuild_progress = 0;
    if (PdInfo->progInfo.active.rbld) {
        pPDinfo->pdinfo.proginfo.rebuild_progress = (PdInfo->progInfo.rbld.progress * 100 / 0xFFFF); // 0 - 100 %
    }

    pPDinfo->pdinfo.proginfo.patrol_progress = 0;
    if (PdInfo->progInfo.active.patrol) {
        pPDinfo->pdinfo.proginfo.patrol_progress = (PdInfo->progInfo.patrol.progress * 100 / 0xFFFF); // 0 - 100 %
    }
    // 以下为创建逻辑盘还额外需要的信息
    pPDinfo->pdinfo.dev_not_supported = PdInfo->notSupported;
    pPDinfo->pdinfo.fw_state_raw = PdInfo->fwState;
    pPDinfo->pdinfo.is_foreign = PdInfo->state.ddf.pdType.isForeign;
    pPDinfo->pdinfo.coerced_size_blk = PdInfo->coercedSize;
    pPDinfo->pdinfo.block_size = PdInfo->userDataBlockSize;
}

/*
 * Description:获取硬盘Smart信息,拆分函数解决函数超大
 */
static void GetDeviceSmartInfo(guint32 ctrl_id, guint16 device_id, SML_PD_INFO_S *pPDinfo)
{
    // 只有在硬盘spun up的时候才读取SMART信息，避免由于读取SMART信息唤醒硬盘，导致无法进入休眠状态
    if (pPDinfo->pdinfo.power_state == PD_POWER_STATE_ACTIVE) {
        if (pPDinfo->pdinfo.interface_type == PD_INTERFACE_TYPE_SATA) {
            (void)GetSataDeviceSmartInfo(ctrl_id, device_id, pPDinfo);
        } else if (pPDinfo->pdinfo.interface_type == PD_INTERFACE_TYPE_SAS ||
                   pPDinfo->pdinfo.interface_type == PD_INTERFACE_TYPE_PCIE) {
            (void)GetSasDeviceSmartInfo(ctrl_id, device_id, &pPDinfo->smartinfo);
        }
    }
}

/*
 * Description: 通过LSI storelib获取PD的基本信息
 * History: 2016年4月7日  新生成函数
 *          2019年6月12日  校验返回数据有效性，
 *          调用该接口的上层APP，在获取异常时不能更新数据
*/
gint32 lsi_get_pd_info(guint32 ctrl_id, guint16 device_id, gpointer data)
{
    if (data == NULL) {
        return SML_ERR_NULL_DATA;
    }
    MR_PD_INFO PdInfo = { 0 };
    SML_PD_INFO_S *pPDinfo = (SML_PD_INFO_S *)data;

    gint32 retval = GetPDInfo(ctrl_id, device_id, &PdInfo);
    if (retval != SML_SUCCESS) {
        debug_log(DLOG_ERROR,
            "smlib: LSI:GetPDInfo failed, CtrlId = %d, DeviceId = %d, SlotId = %d, return 0x%0x\n",
            SML_CTRL_ID_VALID_BIT(ctrl_id), device_id, pPDinfo->pdinfo.slot_num, retval);
        return retval;
    }

    if (device_id != PdInfo.ref.deviceId) {
        debug_log(DLOG_DEBUG,
            "smlib: LSI:GetPDInfo failed, CtrlId = %d, DeviceId = %d, SlotId = %d, returned DeviceId = %d, device "
            "id is not match\n",
            SML_CTRL_ID_VALID_BIT(ctrl_id), device_id, pPDinfo->pdinfo.slot_num, PdInfo.ref.deviceId);
        return SML_ERR_DATA_INVALID;
    }
    pPDinfo->pdinfo.device_id = device_id;
    FillPDInfo(pPDinfo, &PdInfo);
    pPDinfo->pdinfo.fw_state = GetPDFwState(PdInfo.fwState, PdInfo.state.ddf.pdType.isForeign, PdInfo.properties.isEPD);
    pPDinfo->pdinfo.power_state = GetPDPowerState(PdInfo.powerState);
    pPDinfo->pdinfo.interface_type = GetPDInterfaceType(PdInfo.state.ddf.pdType.intf); // PdInfo.interfaceType;
    // 使用对齐后的PD容量，与逻辑盘创建时使用的保持一致，避免使用者创建逻辑盘时输入最大可用空间时底层判断由于容量超出范围无法创建成功
    pPDinfo->pdinfo.coerced_size =
        BLK2MB(PdInfo.coercedSize, PdInfo.userDataBlockSize); // Capacity(Byte) =  rawSize * sectorSize

    pPDinfo->pdinfo.hot_spare = GetPDHotSpareState(&PdInfo);

    get_pd_basic_info(pPDinfo, &PdInfo, ctrl_id, device_id);

    if (PD_INTERFACE_TYPE_PCIE != pPDinfo->pdinfo.interface_type) {
        format_sas_addr(PdInfo.pathInfo.sasAddr[0], pPDinfo->pdinfo.sas_addr1, sizeof(pPDinfo->pdinfo.sas_addr1));
        format_sas_addr(PdInfo.pathInfo.sasAddr[1], pPDinfo->pdinfo.sas_addr2, sizeof(pPDinfo->pdinfo.sas_addr2));
    }

    GetDeviceSmartInfo(ctrl_id, device_id, pPDinfo);
    (void)GetSpareBlockPercent(pPDinfo);
    (void)GetRemMediaWearOutAndPowerOnHours(ctrl_id, device_id, pPDinfo);
    (void)GetSataDeviceTemperature(ctrl_id, device_id, pPDinfo);
    (void)GetEstimatedLifespanAndWriteAmpFactor(ctrl_id, device_id, pPDinfo);
    return retval;
}

/*
 * Description: 通过LSI storelib获取PD的基本信息
 * History: 2016年4月7日  新生成函数
*/
gint32 lsi_pd_operations(guint32 ctrl_id, guint16 device_id, guint8 operation, gpointer param, guint32 param_length)
{
    gint32 retval = SML_SUCCESS;
    guint8 duration = 0, ld_target_id = 0, test_type = 0;
    SML_PD_STATE_PARAM_S *state_param = NULL;

    guint8 libType = LSI_STORELIB_TYPE_VALID_BIT(ctrl_id);
    if (libType == SL_LIB_TYPE_STORELIBIR || libType == SL_LIB_TYPE_STORELIBIR_2 ||
        libType == SL_LIB_TYPE_STORELIBIR_3 || libType == SL_LIB_TYPE_STORELIBIT ||
        libType == SL_LIB_TYPE_STORELIBCUSTOM) {
        if ((PD_OPERATION_LOCATE != operation) && (PD_OPERATION_STOP_LOCATE != operation)) {
            return SML_ERR_PD_OPERATION_NOT_SUPPORT;
        }
    }

    switch (operation) {
        case PD_OPERATION_LOCATE:
            if (param == NULL || param_length == 0) {
                duration = 0;
            } else {
                duration = *(guint8 *)param;
            }

            retval = LocatePD(ctrl_id, device_id, duration);
            break;

        case PD_OPERATION_STOP_LOCATE:
            retval = StopLocatePD(ctrl_id, device_id);
            break;

        case PD_OPERATION_SET_GLOBAL_HOTSPARE:
            retval = SetPDGlobalHotSpare(ctrl_id, device_id);
            break;

        case PD_OPERATION_SET_DEDICATED_HOTSPARE:
            if ((param == NULL) || (param_length != sizeof(guint16))) {
                return SML_ERR_NULL_DATA;
            }
            ld_target_id = (guint8)(*(guint16 *)param); // 调用者传入的是16bit的逻辑盘ID，但是storelib当前只使用8bit
            retval = SetPDDedicatedHotSpare(ctrl_id, device_id, ld_target_id);
            break;

        case PD_OPERATION_CANCEL_HOTSPARE:
            retval = RemovePDHotSpare(ctrl_id, device_id);
            break;

        case PD_OPERATION_SET_STATE:
            if ((param == NULL) || (param_length != sizeof(SML_PD_STATE_PARAM_S))) {
                return SML_ERR_NULL_DATA;
            }
            state_param = (SML_PD_STATE_PARAM_S *)param;
            retval = SetPDState(ctrl_id, device_id, state_param);
            break;
        /* BEGIN  AR.SR.SFEA02130924.051.001 加密盘安全擦除 , 2018/9/27 */
        case PD_OPERATION_CRYPTO_ERASE:
            retval = CryptoErase(ctrl_id, device_id);
            break;
        /* END:   Added on 2018/9/27 */
        case PD_OPERATION_SMART_TEST:
            if (param == NULL || param_length == 0) {
                test_type = 0;
            } else {
                test_type = *(guint8 *)param;
            }
            retval = SetPDSmartTest(ctrl_id, device_id, test_type);
            break;
        default:
            retval = SML_ERR_PD_OPERATION_NOT_SUPPORT;
            break;
    }

    return retval;
}
/* END:   Modified on 2016/11/22 */

/* 释放为c结构体申请的内存 */
static void free_drive_struct_info(void)
{
    if (drive_info_list_lsi == NULL) {
        drive_info_list_lsi_len = 0;
        return;
    }

    for (size_t i = 0; i < drive_info_list_lsi_len; i++) {
        if (drive_info_list_lsi[i].vendor_name != NULL) {
            g_free(drive_info_list_lsi[i].vendor_name);
            drive_info_list_lsi[i].vendor_name = NULL;
        }
    }

    g_free(drive_info_list_lsi);
    drive_info_list_lsi = NULL;
    drive_info_list_lsi_len = 0;
}

/* 为单个结构体变量赋值 */
static gint32 assign_drive_item(Json *array_item, size_t index)
{
    Json *name_obj = NULL;
    Json *id_obj = NULL;
    Json *attr_obj = NULL;
    char *name_val = NULL;
    gint64 id_val = 0;
    gint64 attr_val = 0;
    gint32 ret = SML_SUCCESS;

    // 解析 vendor_name
    ret = JsonObjectItemGet(array_item, "vendor_name", &name_obj);
    if (ret != JSON_OK || !JsonIsString(name_obj)) {
        debug_log(DLOG_ERROR, "%s: JSON parse vendor_name failed", __FUNCTION__);
        return SML_ERR_DATA_INVALID;
    }
    JsonItemStringValueGet(name_obj, &name_val);
    drive_info_list_lsi[index].vendor_name = g_strdup(name_val);
    if (drive_info_list_lsi[index].vendor_name == NULL) {
        debug_log(DLOG_ERROR, "%s: Memory allocation failed for vendor_name", __FUNCTION__);
        return SML_ERR_CANNOT_ALLOC_MEM;
    }

    // 解析 vendor_id
    ret = JsonObjectItemGet(array_item, "vendor_id", &id_obj);
    if (ret != JSON_OK || !JsonIsInteger(id_obj)) {
        debug_log(DLOG_ERROR, "%s: JSON parse vendor_id failed", __FUNCTION__);
        return SML_ERR_DATA_INVALID;
    }
    JsonItemIntegerValueGet(id_obj, &id_val);
    drive_info_list_lsi[index].vendor_id = GINT32_FROM_LE((gint32)id_val);

    // 解析 attr_id_wear
    ret = JsonObjectItemGet(array_item, "attr_id_wear", &attr_obj);
    if (ret != JSON_OK || !JsonIsInteger(attr_obj)) {
        debug_log(DLOG_ERROR, "%s: JSON parse attr_id_wear failed", __FUNCTION__);
        return SML_ERR_DATA_INVALID;
    }
    JsonItemIntegerValueGet(attr_obj, &attr_val);
    drive_info_list_lsi[index].attr_id_wear = GINT32_FROM_LE((gint32)attr_val);

    return SML_SUCCESS;
}

/* 根据传递的字符串生成json数组 */
static gint32 parse_json_str_to_array(const char *json_raw_str, Json **root, guint32 *len)
{
    if (json_raw_str == NULL || root == NULL || len == NULL) {
        debug_log(DLOG_ERROR, "%s: Invalid parameters", __FUNCTION__);
        return SML_ERR_DATA_INVALID;
    }

    guint32 ret = JsonParse(json_raw_str, root);
    if (ret != JSON_OK) {
        debug_log(DLOG_ERROR, "%s: JSON parse failed, ret is %d", __FUNCTION__, ret);
        return SML_ERR_DATA_INVALID;
    }

    if (JsonIsArray(*root) != TRUE) {
        debug_log(DLOG_ERROR, "%s: Root is not an array", __FUNCTION__);
        JsonDelete(*root);
        *root = NULL;
        return SML_ERR_DATA_INVALID;
    }

    ret = JsonArraySizeGet(*root, len);
    if (ret != JSON_OK) {
        debug_log(DLOG_ERROR, "%s: Get array size failed", __FUNCTION__);
        JsonDelete(*root);
        *root = NULL;
        return SML_ERR_DATA_INVALID;
    }

    if (*len == 0) {
        debug_log(DLOG_ERROR, "%s: Empty array", __FUNCTION__);
        JsonDelete(*root);
        *root = NULL;
        return SML_ERR_DATA_INVALID;
    }

    return SML_SUCCESS;
}

/* 处理从storage传上来的json字符串 */
gint32 lsi_trans_drive_data(const char *json_raw_data)
{
    gint32 ret = SML_SUCCESS;
    Json *json_root = NULL;
    guint32 drive_vendor_len = 0;

    if (json_raw_data == NULL) {
        return SML_ERR_DATA_INVALID;
    }
    free_drive_struct_info();
    ret = parse_json_str_to_array(json_raw_data, &json_root, &drive_vendor_len);
    if (ret != SML_SUCCESS) {
        goto cleanup;
    }

    drive_info_list_lsi = g_malloc0_n(drive_vendor_len, sizeof(DRIVE_VENDOR_INFO_EXTEND_S));
    if (drive_info_list_lsi == NULL) {
        debug_log(DLOG_ERROR, "%s: Memory alloc failed for %u items", __FUNCTION__, drive_vendor_len);
        ret = SML_ERR_CANNOT_ALLOC_MEM;
        goto cleanup;
    }
    drive_info_list_lsi_len = drive_vendor_len;

    for (guint32 i = 0; i < drive_vendor_len; i++) {
        Json *array_item = NULL;

        ret = JsonArrayItemGet(json_root, i, &array_item);
        if (ret != JSON_OK) {
            goto item_cleanup;
        }

        ret = assign_drive_item(array_item, i);
        if (ret != SML_SUCCESS) {
            goto item_cleanup;
        }
    }

    ret = SML_SUCCESS;
    goto cleanup;

item_cleanup:
    debug_log(DLOG_ERROR, "%s: process json data failed", __FUNCTION__);
    free_drive_struct_info();
cleanup:
    if (json_root != NULL) {
        JsonDelete(json_root);
    }
    return ret;
}