/* 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.
 */

// don't change the include order
#include "adapter.h"
#include "ctrl.h"
#include "sml_errcodes.h"
#include "diagnose.h"

#ifdef ITEST_ENABLED
#include "ibmc_itest_sml.h"
#endif

static gint32 fd_collect_ctrl_phy_err_count(guint8 ctrl_index, SML_SASPHY_INFO_S *ctrl);
static gint32 fd_get_link_phy_diag_topo_info(guint8 ctrl_index, guint16 pd_device_id,
    SML_CTRL_PHY_DIAG_TOPO_INFO_S *topo_info);
static guint8 fd_check_phy_err_count_is_exceed(SML_SASPHY_ERR_COUNT_S *old_data, SML_SASPHY_ERR_COUNT_S *new_data,
    guint8 is_pd_error, guint32 mesc_diff, guint32 threshold, guint32 *rate);
static guint8 fd_check_ctrl_phy_history_topo(SML_SASPHY_INFO_S *ctrl_info, SML_CTRL_PHY_DIAG_TOPO_INFO_S *topo_info);
static guint8 fd_check_exp_phy_history_topo(SML_CTRL_EXP_SASPHY_INFO_S *exp_info,
    SML_CTRL_PHY_DIAG_TOPO_INFO_S *topo_info);
static guint8 fd_set_ctrl_phy_err_link_type(SML_PHY_DIAG_INFO_S *phy_info, SML_PD_FAULT_ANALYSIS *diag_info);
static guint8 fd_set_exp_phy_err_link_type(SML_EXP_PHY_DIAG_INFO_S *exp_info, SML_PHY_DIAG_INFO_S *phy_info,
    SML_PD_FAULT_ANALYSIS *diag_info);
static gint32 fd_diag_serial_ctrl_phy_error(SML_PHY_DIAG_HISTORY_INFO_S *phy_history,
    SML_CTRL_PHY_DIAG_TOPO_INFO_S *topo_info, SML_PD_FAULT_ANALYSIS *diag_info, guint32 phy_threshold);
static gint32 fd_diag_serial_exp_phy_error(SML_PHY_DIAG_HISTORY_INFO_S *phy_history,
    SML_CTRL_PHY_DIAG_TOPO_INFO_S *topo_info, SML_PD_FAULT_ANALYSIS *diag_info, guint32 phy_threshold);
static gint32 fd_diag_serial_link_phy_error(SML_PHY_DIAG_HISTORY_INFO_S *phy_history,
    SML_CTRL_PHY_DIAG_TOPO_INFO_S *topo_info, SML_PD_FAULT_ANALYSIS *diag_info, guint32 phy_threshold);
static gint32 fd_diag_recent_ctrl_phy_error(SML_PHY_DIAG_HISTORY_INFO_S *phy_history,
    SML_CTRL_PHY_DIAG_TOPO_INFO_S *topo_info, SML_PD_FAULT_ANALYSIS *diag_info, guint32 phy_threshold);
static gint32 fd_diag_recent_exp_phy_error(SML_PHY_DIAG_HISTORY_INFO_S *phy_history,
    SML_CTRL_PHY_DIAG_TOPO_INFO_S *topo_info, SML_PD_FAULT_ANALYSIS *diag_info, guint32 phy_threshold);
static gint32 fd_collect_recent_phy_error(SML_PHY_DIAG_HISTORY_INFO_S *phy_history,
    SML_CTRL_PHY_DIAG_TOPO_INFO_S *topo_info, guint8 ctrl_index);
static gint32 fd_diag_recent_link_phy_error(SML_PHY_DIAG_HISTORY_INFO_S *phy_history,
    SML_CTRL_PHY_DIAG_TOPO_INFO_S *topo_info, SML_PD_FAULT_ANALYSIS *diag_info, guint32 phy_threshold);
/*
 * Description: 记录实时的RAID卡的PHY误码数据
 * History: 1.2019年1月22日,
 *          新生成函数
 */
static gint32 fd_collect_ctrl_phy_err_count(guint8 ctrl_index, SML_SASPHY_INFO_S *ctrl)
{
    gint32 ret = SML_SUCCESS;
    SML_CTRL_S* sml_ctrl = NULL;

    /* 入参检查 */
    ret = check_input_parameters(ctrl_index, ctrl);
    if (ret != SML_SUCCESS) {
        return ret;
    }

    /* 获取RAID卡PHY误码 */
    lock_ctrl_list_mutex(ctrl_index);
    sml_ctrl = get_ctrl_by_index(ctrl_index);
    if (sml_ctrl == NULL) {
        unlock_ctrl_list_mutex(ctrl_index);
        return SML_ERR_CTRL_NOT_REGISTERED;
    }
    g_mutex_lock(&sml_ctrl->ctrl_mutex);
    if (sml_ctrl->pfn_get_ctrl_phy_err_count != NULL) {
        ret = sml_ctrl->pfn_get_ctrl_phy_err_count(sml_ctrl->controller_id, ctrl);
    } else {
        ret = SML_ERR_NULL_INFTERFACE;
    }
    g_mutex_unlock(&sml_ctrl->ctrl_mutex);

    /* 获取PHY误码成功后，记录到历史数据中 */
    if (ret == SML_SUCCESS) {
        fd_record_ctrl_phy_err_info(&sml_ctrl->controller.phy_history, ctrl, vos_tick_get());
    }
    unlock_ctrl_list_mutex(ctrl_index);

    return ret;
}

/*
 * Description: 取得链路PHY误码诊断的拓扑结构信息
 * History: 1.2019年2月23日,
 *          新生成函数
 */
static gint32 fd_get_link_phy_diag_topo_info(guint8 ctrl_index, guint16 pd_device_id,
    SML_CTRL_PHY_DIAG_TOPO_INFO_S *topo_info)
{
    gint32 ret = SML_SUCCESS;
    SML_CTRL_S* sml_ctrl = NULL;

    ret = check_input_parameters(ctrl_index, topo_info);
    if (ret != SML_SUCCESS) {
        return ret;
    }

    lock_ctrl_list_mutex(ctrl_index);
    sml_ctrl = get_ctrl_by_index(ctrl_index);
    if (sml_ctrl == NULL) {
        unlock_ctrl_list_mutex(ctrl_index);
        return SML_ERR_CTRL_NOT_REGISTERED;
    }
    g_mutex_lock(&sml_ctrl->ctrl_mutex);

    if (sml_ctrl->pfn_get_phy_diag_topo_info != NULL) {
        ret = sml_ctrl->pfn_get_phy_diag_topo_info(sml_ctrl->controller_id, pd_device_id,
            topo_info);
    } else {
        ret = SML_ERR_NULL_INFTERFACE;
    }

    g_mutex_unlock(&sml_ctrl->ctrl_mutex);
    unlock_ctrl_list_mutex(ctrl_index);

    if (ret != SML_SUCCESS) {
        debug_log(DLOG_INFO, "%s : get phy topo info failed, ctrl_index = %d, pd_device_id = %d", __FUNCTION__,
            ctrl_index, pd_device_id);
    }

    return ret;
}

/*
 * Description: 诊断单位分钟的PHY误码增量是否超过阈值
 * History: 1.2019年1月24日,
 *          新生成函数
 */
static guint8 fd_check_phy_err_count_is_exceed(SML_SASPHY_ERR_COUNT_S *old_data, SML_SASPHY_ERR_COUNT_S *new_data,
    guint8 is_pd_error, guint32 mesc_diff, guint32 threshold, guint32 *rate)
{
    guint64 count1 = 0; // 误码差值
    guint64 count2 = 0; // 单位分钟的误码增量

#define MSEC_PER_MINUTE 60000

    /* 入参判断 */
    if (old_data == NULL || new_data == NULL || mesc_diff == 0) {
        return FALSE;
    }

    /* 诊断前三项PHY误码数据，判断单位分钟的增量是否超过限制 */
    count1 = new_data->invalid_dword_count > old_data->invalid_dword_count ?
        new_data->invalid_dword_count - old_data->invalid_dword_count :
        0;
    count2 = count1 * MSEC_PER_MINUTE / mesc_diff;
    if (count2 > threshold) {
        *rate = (guint32)count2;
        return TRUE;
    }

    count1 = new_data->loss_dword_sync_count > old_data->loss_dword_sync_count ?
        new_data->loss_dword_sync_count - old_data->loss_dword_sync_count :
        0;
    count2 = count1 * MSEC_PER_MINUTE / mesc_diff;
    if (count2 > threshold) {
        *rate = (guint32)count2;
        return TRUE;
    }

    count1 = new_data->running_disparity_error_count > old_data->running_disparity_error_count ?
        new_data->running_disparity_error_count - old_data->running_disparity_error_count :
        0;
    count2 = count1 * MSEC_PER_MINUTE / mesc_diff;
    if (count2 > threshold) {
        *rate = (guint32)count2;
        return TRUE;
    }

    /* 如果硬盘有故障，第四项phy_reset_problem_Count不诊断 */
    if (is_pd_error == TRUE) {
        return FALSE;
    }

    count1 = new_data->phy_reset_problem_count > old_data->phy_reset_problem_count ?
        new_data->phy_reset_problem_count - old_data->phy_reset_problem_count :
        0;
    count2 = count1 * MSEC_PER_MINUTE / mesc_diff;
    if (count2 > threshold) {
        *rate = (guint32)count2;
        return TRUE;
    }

    return FALSE;
}

/*
 * Description: 判断RAID卡PHY误码故障所在的链路类型
 * History: 1.2019年1月24日,
 *          新生成函数
 */
static guint8 fd_set_ctrl_phy_err_link_type(SML_PHY_DIAG_INFO_S *phy_info, SML_PD_FAULT_ANALYSIS *diag_info)
{
    if (phy_info == NULL || diag_info == NULL) {
        return FALSE;
    }

    if (phy_info->connect_type == SML_PHY_CONNECT_END_DEVICE && diag_info->o_fault_bitmap.ctrl_pd_link_error == FALSE) {
        /* PHY连接设备类型为硬盘 */
        diag_info->o_fault_bitmap.ctrl_pd_link_error = TRUE;
        return TRUE;
    } else if (phy_info->connect_type == SML_PHY_CONNECT_ENCL_DEVICE &&
        diag_info->o_fault_bitmap.ctrl_encl_link_error == FALSE) {
        /* PHY连接设备类型为Expander */
        diag_info->o_fault_bitmap.ctrl_encl_link_error = TRUE;
        return TRUE;
    }

    return FALSE;
}

/*
 * Description: 判断Expander的PHY误码故障所在的链路类型
 * History: 1.2019年1月24日,
 *          新生成函数
 */
static guint8 fd_set_exp_phy_err_link_type(SML_EXP_PHY_DIAG_INFO_S *exp_info, SML_PHY_DIAG_INFO_S *phy_info,
    SML_PD_FAULT_ANALYSIS *diag_info)
{
    guint8 encl_error_level = 0;
    guint32 encl_encl_link_error = 0;

    if (exp_info == NULL || phy_info == NULL || diag_info == NULL) {
        return FALSE;
    }

    if (phy_info->connect_type == SML_PHY_CONNECT_END_DEVICE && diag_info->o_fault_bitmap.encl_pd_link_error == FALSE) {
        /* PHY连接设备为硬盘 */
        diag_info->o_fault_bitmap.encl_pd_link_error = TRUE;
        return TRUE;
    } else if (phy_info->connect_type == SML_PHY_CONNECT_RAID_DEVICE &&
        diag_info->o_fault_bitmap.ctrl_encl_link_error == FALSE) {
        /* PHY连接设备为RAID */
        diag_info->o_fault_bitmap.ctrl_encl_link_error = TRUE;
        return TRUE;
    } else if (phy_info->connect_type == SML_PHY_CONNECT_ENCL_DEVICE) {
        /* Expander级联情况，需确认encl_encl链路的位置，exp层级为0为非法数据 */
        if (exp_info->exp_level == 0) {
            debug_log(DLOG_INFO, "%s : exp_level is 0", __FUNCTION__);
            return FALSE;
        }

        /* 如果phy为上行phy，则链路层级为当前Expander的层级，如果phy为下行phy，则链路层级为下一层Expander的层级 */
        encl_error_level = phy_info->phy_type == SML_PHY_TYPE_UP ? exp_info->exp_level : exp_info->exp_level - 1;

        /* 判断允许的最大层级为最大Expander数量 - 1 */
        if (encl_error_level > SML_MAX_EXPANDER_PER_CONTROLLER - 1) {
            return FALSE;
        }

        /* 诊断结果用bit位区分哪一级链路故障, 例如B010标识硬盘的第二级链路故障 */
        encl_encl_link_error = diag_info->o_fault_bitmap.encl_encl_link_error | (1 << (encl_error_level - 1));
        if (encl_encl_link_error == (guint32)diag_info->o_fault_bitmap.encl_encl_link_error) {
            return FALSE;
        }

        /* 设置encl_encl链路告警标志 */
        diag_info->o_fault_bitmap.encl_encl_link_error = encl_encl_link_error;
        return TRUE;
    }

    return FALSE;
}

/*
 * Description: 判断RAID卡PHY误码历史数据是否能和拓扑结构对应
 * History: 1.2019年1月29日,
 *          新生成函数
 */
static guint8 fd_check_ctrl_phy_history_topo(SML_SASPHY_INFO_S *ctrl_info, SML_CTRL_PHY_DIAG_TOPO_INFO_S *topo_info)
{
    if (ctrl_info == NULL || topo_info == NULL) {
        return FALSE;
    }

    /* 判断RAID卡下的phy数目 */
    if (ctrl_info->phy_count != topo_info->ctrl_phy_count) {
        return FALSE;
    }

    return TRUE;
}

/*
 * Description: 判断Expander的PHY误码历史数据是否能和拓扑结构对应
 * History: 1.2019年1月29日,
 *          新生成函数
 */
static guint8 fd_check_exp_phy_history_topo(SML_CTRL_EXP_SASPHY_INFO_S *exp_info,
    SML_CTRL_PHY_DIAG_TOPO_INFO_S *topo_info)
{
    gint32 i;

    if (exp_info == NULL || topo_info == NULL) {
        return FALSE;
    }

    /* 判断RAID卡下的Expander数目 */
    if (exp_info->expander_count != topo_info->exp_count) {
        return FALSE;
    }

    /* 判断每块Expander下的phy数目 */
    for (i = 0; i < exp_info->expander_count; i++) {
        if (exp_info->expander_phy[i].phy_count != topo_info->exp_info[i].phy_count) {
            return FALSE;
        }
    }

    return TRUE;
}

/*
 * Description: 检查phy诊断数据是否过期
 */
static gboolean phy_diag_data_overdue(guint32 current_timestamp, guint32 collect_timestamp)
{
    if (current_timestamp > collect_timestamp &&
        current_timestamp - collect_timestamp > SMLIB_PHY_DIAG_HISTORY_TIME_TOLERANCE) {
        return TRUE;
    }

    if (current_timestamp < collect_timestamp &&
        G_MAXUINT32 - collect_timestamp + current_timestamp > SMLIB_PHY_DIAG_HISTORY_TIME_TOLERANCE) {
        return TRUE;
    }

    return FALSE;
}

/*
 * Description: 进行RAID卡的连续phy误码诊断
 * History: 1.2019年2月21日,
 *          新生成函数
 */
static gint32 fd_diag_serial_ctrl_phy_error(SML_PHY_DIAG_HISTORY_INFO_S *phy_history,
    SML_CTRL_PHY_DIAG_TOPO_INFO_S *topo_info, SML_PD_FAULT_ANALYSIS *diag_info, guint32 phy_threshold)
{
    gint32 ret = SML_SUCCESS;
    guint32 list_length = 0;
    GList *node_temp1 = NULL;
    GList *node_temp2 = NULL;
    SML_SASPHY_INFO_S *ctrl_info = NULL;
    SML_SASPHY_INFO_S *ctrl_info2 = NULL;
    gchar phy_err_info[SML_MAX_STR_BUF_SIZE] = { 0 };
    guint32 current_timestamp = vos_tick_get();
    guint32 rate = 0;
    guint8 diag_ret = FALSE;
    guint8 diag_ret2 = FALSE;
    gint32 i = 0;

    /* 入参检查 */
    if (phy_history == NULL || topo_info == NULL || diag_info == NULL) {
        return SML_ERR_NULL_DATA;
    }

    /* 检查RAID卡PHY历史误码数据的记录次数是否满足诊断条件 */
    list_length = g_list_length(phy_history->ctrl_phy_list);
    if (list_length != SMLIB_PHY_DIAG_HISTORY_RECORD_COUNT) {
        return SML_ERR_DIAG_LACK_PHY_DIAG_DATA;
    }

    /* 检查RAID卡PHY历史误码数据的时间戳是否满足诊断条件 */
    node_temp1 = g_list_first(phy_history->ctrl_phy_list);
    if (node_temp1 == NULL) {
        return SML_ERR_DIAG_LACK_PHY_DIAG_DATA;
    }
    ctrl_info = (SML_SASPHY_INFO_S *)node_temp1->data;
    if (phy_diag_data_overdue(current_timestamp, ctrl_info->collect_timestamp)) {
        return SML_ERR_DIAG_DATA_OVER_TIME;
    }

    /* 遍历RAID卡的PHY */
    for (i = 0; i < topo_info->ctrl_phy_count; i++) {
        /* 如果PHY被标记为要诊断 */
        if (topo_info->ctrl_phy_info[i].diag_mark == TRUE) {
            node_temp1 = g_list_first(phy_history->ctrl_phy_list);
            node_temp2 = g_list_next(node_temp1);
            diag_ret = TRUE;

            /* 遍历历史数据，连续5次满足条件，才认为该PHY有问题 */
            while (node_temp2 != NULL) {
                /* 进行PHY误码诊断 */
                ctrl_info = (SML_SASPHY_INFO_S *)node_temp1->data;
                ctrl_info2 = (SML_SASPHY_INFO_S *)node_temp2->data;
                /* 判断拓扑是否能匹配 */
                if (fd_check_ctrl_phy_history_topo(ctrl_info, topo_info) != TRUE ||
                    fd_check_ctrl_phy_history_topo(ctrl_info2, topo_info) != TRUE) {
                    diag_ret = FALSE;
                    break;
                }
                diag_ret2 = fd_check_phy_err_count_is_exceed(&ctrl_info->phy_err[i], &ctrl_info2->phy_err[i],
                    diag_info->i_pd_error, ctrl_info2->collect_timestamp - ctrl_info->collect_timestamp, phy_threshold,
                    &rate);
                /* 如果有一次诊断结果不为TRUE，该PHY的最终诊断结果就没有问题 */
                if (diag_ret2 != TRUE) {
                    diag_ret = FALSE;
                    break;
                }

                node_temp1 = node_temp2;
                node_temp2 = g_list_next(node_temp1);
            }

            /* 未检测出异常，继续循环遍历 */
            if (diag_ret != TRUE) {
                continue;
            }

            /* 如果诊断出有异常，根据PHY类型设置错误类型 */
            if (fd_set_ctrl_phy_err_link_type(&topo_info->ctrl_phy_info[i], diag_info) == TRUE) {
                /* 增加维护日志的记录 */
                (void)memset_s(phy_err_info, SML_MAX_STR_BUF_SIZE, 0, SML_MAX_STR_BUF_SIZE);
                if (0 <= snprintf_s(phy_err_info, SML_MAX_STR_BUF_SIZE, SML_MAX_STR_BUF_SIZE - 1,
                    ", c%hhu(PHY%d):%u>%u", diag_info->i_controller_index, i, rate, phy_threshold)) {
                    (void)strncat_s(diag_info->io_buf, SML_MAX_DETAIL_DESC_SIZE, phy_err_info,
                        SML_MAX_STR_BUF_SIZE);
                }
            }
        }
    }

    return ret;
}

/*
 * Description: 进行Expander的连续phy误码诊断
 * History: 1.2019年2月21日,
 *          新生成函数
 */
static gint32 fd_diag_serial_exp_phy_error(SML_PHY_DIAG_HISTORY_INFO_S *phy_history,
    SML_CTRL_PHY_DIAG_TOPO_INFO_S *topo_info, SML_PD_FAULT_ANALYSIS *diag_info, guint32 phy_threshold)
{
    gint32 ret = SML_SUCCESS;
    guint32 list_length = 0;
    GList *node_temp1 = NULL;
    GList *node_temp2 = NULL;
    SML_CTRL_EXP_SASPHY_INFO_S *exp_info = NULL;
    SML_CTRL_EXP_SASPHY_INFO_S *exp_info2 = NULL;
    gchar phy_err_info[SML_MAX_STR_BUF_SIZE] = { 0 };
    guint32 current_timestamp = vos_tick_get();
    guint32 rate = 0;
    guint8 diag_ret = FALSE;
    guint8 diag_ret2 = FALSE;
    gint32 i, j;

    /* 判断历史数据的记录次数是否满足条件 */
    list_length = g_list_length(phy_history->exp_phy_list);
    if (list_length != SMLIB_PHY_DIAG_HISTORY_RECORD_COUNT) {
        return SML_ERR_DIAG_LACK_PHY_DIAG_DATA;
    }

    /* 检查历史误码数据的时间戳是否满足诊断条件 */
    node_temp1 = g_list_first(phy_history->exp_phy_list);
    if (node_temp1 == NULL) {
        return SML_ERR_DIAG_LACK_PHY_DIAG_DATA;
    }
    exp_info = (SML_CTRL_EXP_SASPHY_INFO_S *)node_temp1->data;
    if (phy_diag_data_overdue(current_timestamp, exp_info->collect_timestamp)) {
        return SML_ERR_DIAG_DATA_OVER_TIME;
    }

    /* 遍历RAID卡下所有Expander */
    for (i = 0; i < topo_info->exp_count; i++) {
        /* Expander如果未标记诊断则直接跳过 */
        if (topo_info->exp_info[i].diag_mark != TRUE) {
            continue;
        }

        /* 遍历Expander的所有PHY */
        for (j = 0; j < topo_info->exp_info[i].phy_count; j++) {
            /* 如果PHY被标记诊断 */
            if (topo_info->exp_info[i].phy_info[j].diag_mark == TRUE) {
                node_temp1 = g_list_first(phy_history->exp_phy_list);
                node_temp2 = g_list_next(node_temp1);
                diag_ret = TRUE;

                /* 遍历历史数据，连续5次满足条件，才认为该PHY有问题 */
                while (node_temp2 != NULL) {
                    /* 进行PHY误码诊断 */
                    exp_info = (SML_CTRL_EXP_SASPHY_INFO_S *)node_temp1->data;
                    exp_info2 = (SML_CTRL_EXP_SASPHY_INFO_S *)node_temp2->data;
                    /* 判断拓扑是否能匹配 */
                    if (fd_check_exp_phy_history_topo(exp_info, topo_info) != TRUE ||
                        fd_check_exp_phy_history_topo(exp_info2, topo_info) != TRUE) {
                        diag_ret = FALSE;
                        break;
                    }
                    diag_ret2 = fd_check_phy_err_count_is_exceed(&exp_info->expander_phy[i].phy_err[j],
                        &exp_info2->expander_phy[i].phy_err[j], diag_info->i_pd_error,
                        exp_info2->collect_timestamp - exp_info->collect_timestamp, phy_threshold, &rate);
                    /* 如果有一次诊断结果不为TRUE，该PHY的最终诊断结果就没有问题 */
                    if (diag_ret2 != TRUE) {
                        diag_ret = FALSE;
                        break;
                    }

                    node_temp1 = node_temp2;
                    node_temp2 = g_list_next(node_temp1);
                }

                /* 未检测出异常，继续循环遍历 */
                if (diag_ret != TRUE) {
                    continue;
                }

                /* 如果诊断出有异常，根据PHY类型设置错误类型 */
                if (fd_set_exp_phy_err_link_type(&topo_info->exp_info[i], &topo_info->exp_info[i].phy_info[j],
                    diag_info) == TRUE) {
                    /* 增加维护日志的记录 */
                    (void)memset_s(phy_err_info, SML_MAX_STR_BUF_SIZE, 0, SML_MAX_STR_BUF_SIZE);
                    if (0 <= snprintf_s(phy_err_info, SML_MAX_STR_BUF_SIZE, SML_MAX_STR_BUF_SIZE - 1,
                        ", e%d(PHY%d):%u>%u", i, j, rate, phy_threshold)) {
                        (void)strncat_s(diag_info->io_buf, SML_MAX_DETAIL_DESC_SIZE, phy_err_info,
                            SML_MAX_STR_BUF_SIZE);
                    }
                }
            }
        }
    }

    return ret;
}

/*
 * Description: 进行连续PHY误码诊断
 * History: 1.2019年2月21日,
 *          新生成函数
 */
static gint32 fd_diag_serial_link_phy_error(SML_PHY_DIAG_HISTORY_INFO_S *phy_history,
    SML_CTRL_PHY_DIAG_TOPO_INFO_S *topo_info, SML_PD_FAULT_ANALYSIS *diag_info, guint32 phy_threshold)
{
    gint32 ret = SML_SUCCESS;

    /* 入参检查 */
    if (phy_history == NULL || topo_info == NULL || diag_info == NULL) {
        return SML_ERR_NULL_DATA;
    }

    /* 进行RAID卡的PHY误码诊断 */
    ret = fd_diag_serial_ctrl_phy_error(phy_history, topo_info, diag_info, phy_threshold);
    if (ret != SML_SUCCESS) {
        return ret;
    }

    /* 如果拓扑结构中没有Expander，则不进行Expander诊断 */
    if (topo_info->exp_count == 0) {
        return ret;
    }

    /* 进行Expander的PHY误码诊断 */
    ret = fd_diag_serial_exp_phy_error(phy_history, topo_info, diag_info, phy_threshold);

    return ret;
}

/*
 * Description: 进行RAID卡的最近一次phy误码诊断
 * History: 1.2019年1月24日,
 *          新生成函数
 */
static gint32 fd_diag_recent_ctrl_phy_error(SML_PHY_DIAG_HISTORY_INFO_S *phy_history,
    SML_CTRL_PHY_DIAG_TOPO_INFO_S *topo_info, SML_PD_FAULT_ANALYSIS *diag_info, guint32 phy_threshold)
{
    gint32 ret = SML_SUCCESS;
    guint32 list_length = 0;
    guint8 diag_ret = FALSE;
    GList *node_temp1 = NULL;
    GList *node_temp2 = NULL;
    SML_SASPHY_INFO_S *ctrl_info = NULL;
    SML_SASPHY_INFO_S *ctrl_info2 = NULL;
    gchar phy_err_info[SML_MAX_STR_BUF_SIZE] = { 0 };
    guint32 current_timestamp = vos_tick_get();
    guint32 rate = 0;
    gint32 i = 0;

    /* 入参检查 */
    if (phy_history == NULL || topo_info == NULL || diag_info == NULL) {
        return SML_ERR_NULL_DATA;
    }

    /* 检查RAID卡PHY历史误码数据的记录次数是否满足诊断条件 */
    list_length = g_list_length(phy_history->ctrl_phy_list);
    if (list_length < 2) {
        return SML_ERR_DIAG_LACK_PHY_DIAG_DATA;
    }

    /* 检查RAID卡PHY历史误码数据的时间戳是否满足诊断条件，获取最近的数据，new_data */
    node_temp2 = g_list_last(phy_history->ctrl_phy_list);
    if (node_temp2 == NULL) {
        return SML_ERR_DIAG_LACK_PHY_DIAG_DATA;
    }
    ctrl_info2 = (SML_SASPHY_INFO_S *)node_temp2->data;
    /* 获取倒数第二近的数据，old_data */
    node_temp1 = g_list_previous(node_temp2);
    if (node_temp1 == NULL) {
        return SML_ERR_DIAG_LACK_PHY_DIAG_DATA;
    }
    ctrl_info = (SML_SASPHY_INFO_S *)node_temp1->data;
    if (phy_diag_data_overdue(current_timestamp, ctrl_info->collect_timestamp)) {
        return SML_ERR_DIAG_DATA_OVER_TIME;
    }

    /* 判断拓扑是否能匹配 */
    if (TRUE != fd_check_ctrl_phy_history_topo(ctrl_info, topo_info) ||
        TRUE != fd_check_ctrl_phy_history_topo(ctrl_info2, topo_info)) {
        return SML_ERR_DIAG_TOPO_NOT_MATCH;
    }

    /* 遍历RAID卡的PHY */
    for (i = 0; i < topo_info->ctrl_phy_count; i++) {
        /* 如果PHY被标记为要诊断 */
        if (topo_info->ctrl_phy_info[i].diag_mark == TRUE) {
            /* 对最近两次的PHY误码进行诊断 */
            diag_ret =
                fd_check_phy_err_count_is_exceed(&ctrl_info->phy_err[i], &ctrl_info2->phy_err[i], diag_info->i_pd_error,
                ctrl_info2->collect_timestamp - ctrl_info->collect_timestamp, phy_threshold, &rate);
            /* 未检测出异常，继续循环遍历 */
            if (diag_ret != TRUE) {
                continue;
            }

            /* 如果诊断出有异常，根据PHY类型设置错误类型 */
            if (TRUE == fd_set_ctrl_phy_err_link_type(&topo_info->ctrl_phy_info[i], diag_info)) {
                /* 增加维护日志的记录 */
                (void)memset_s(phy_err_info, SML_MAX_STR_BUF_SIZE, 0, SML_MAX_STR_BUF_SIZE);
                if (0 <= snprintf_s(phy_err_info, SML_MAX_STR_BUF_SIZE, SML_MAX_STR_BUF_SIZE - 1,
                    ", c%hhu(PHY%d):%u>%u", diag_info->i_controller_index, i, rate, phy_threshold)) {
                    (void)strncat_s(diag_info->io_buf, SML_MAX_DETAIL_DESC_SIZE, phy_err_info,
                        SML_MAX_STR_BUF_SIZE);
                }
            }
        }
    }

    return ret;
}

/*
 * Description: 进行Expander的最近一次phy误码诊断
 * History: 1.2019年1月24日,
 *          新生成函数
 */
static gint32 fd_diag_recent_exp_phy_error(SML_PHY_DIAG_HISTORY_INFO_S *phy_history,
    SML_CTRL_PHY_DIAG_TOPO_INFO_S *topo_info, SML_PD_FAULT_ANALYSIS *diag_info, guint32 phy_threshold)
{
    gint32 ret = SML_SUCCESS;
    guint32 list_length = 0;
    GList *node_temp1 = NULL;
    GList *node_temp2 = NULL;
    SML_CTRL_EXP_SASPHY_INFO_S *exp_info = NULL;
    SML_CTRL_EXP_SASPHY_INFO_S *exp_info2 = NULL;
    gchar phy_err_info[SML_MAX_STR_BUF_SIZE] = { 0 };
    guint32 current_timestamp = vos_tick_get();
    guint32 rate = 0;
    guint8 diag_ret = FALSE;
    gint32 i = 0;
    gint32 j = 0;

    /* 判断历史数据的记录次数是否满足条件 */
    list_length = g_list_length(phy_history->exp_phy_list);
    if (list_length < 2) {
        return SML_ERR_DIAG_LACK_PHY_DIAG_DATA;
    }

    /* 检查历史误码数据的时间戳是否满足诊断条件，获取最近的数据，new_data */
    node_temp2 = g_list_last(phy_history->exp_phy_list);
    if (node_temp2 == NULL) {
        return SML_ERR_DIAG_LACK_PHY_DIAG_DATA;
    }
    exp_info2 = (SML_CTRL_EXP_SASPHY_INFO_S *)node_temp2->data;
    /* 获取倒数第二近的数据，old_data */
    node_temp1 = g_list_previous(node_temp2);
    if (node_temp1 == NULL) {
        return SML_ERR_DIAG_LACK_PHY_DIAG_DATA;
    }
    exp_info = (SML_CTRL_EXP_SASPHY_INFO_S *)node_temp1->data;
    if (phy_diag_data_overdue(current_timestamp, exp_info->collect_timestamp)) {
        return SML_ERR_DIAG_DATA_OVER_TIME;
    }

    /* 判断拓扑是否能匹配 */
    if (TRUE != fd_check_exp_phy_history_topo(exp_info, topo_info) ||
        TRUE != fd_check_exp_phy_history_topo(exp_info2, topo_info)) {
        return SML_ERR_DIAG_TOPO_NOT_MATCH;
    }

    /* 遍历RAID卡下所有Expander */
    for (i = 0; i < topo_info->exp_count; i++) {
        /* Expander如果未标记诊断则直接跳过 */
        if (topo_info->exp_info[i].diag_mark != TRUE) {
            continue;
        }

        /* 遍历Expander的所有PHY */
        for (j = 0; j < topo_info->exp_info[i].phy_count; j++) {
            /* 如果PHY被标记诊断 */
            if (topo_info->exp_info[i].phy_info[j].diag_mark == TRUE) {
                /* 对最近两次的PHY误码进行诊断 */
                diag_ret = fd_check_phy_err_count_is_exceed(&exp_info->expander_phy[i].phy_err[j],
                    &exp_info2->expander_phy[i].phy_err[j], diag_info->i_pd_error,
                    exp_info2->collect_timestamp - exp_info->collect_timestamp, phy_threshold, &rate);
                /* 未检测出异常，继续循环遍历 */
                if (diag_ret != TRUE) {
                    continue;
                }

                /* 如果诊断出有异常，根据PHY类型设置错误类型 */
                if (TRUE == fd_set_exp_phy_err_link_type(&topo_info->exp_info[i], &topo_info->exp_info[i].phy_info[j],
                    diag_info)) {
                    /* 增加维护日志的记录 */
                    (void)memset_s(phy_err_info, SML_MAX_STR_BUF_SIZE, 0, SML_MAX_STR_BUF_SIZE);
                    if (0 <= snprintf_s(phy_err_info, SML_MAX_STR_BUF_SIZE, SML_MAX_STR_BUF_SIZE - 1,
                        ", e%d(PHY%d):%u>%u", i, j, rate, phy_threshold)) {
                        (void)strncat_s(diag_info->io_buf, SML_MAX_DETAIL_DESC_SIZE, phy_err_info,
                            SML_MAX_STR_BUF_SIZE);
                    }
                }
            }
        }
    }

    return ret;
}

/*
 * Description: 实时收集PHY误码历史数据以满足最近一次诊断的条件
 * 该函数仅被fd_diag_recent_link_phy_error调用，且调用前已经存在锁，调用后解锁
 * fd_collect_ctrl_phy_err_count、smlib_get_ctrl_exp_phy_err_count内部会重新加锁
 * 为避免死锁，以上两个函数调用前后均先解锁再加锁，函数内部判空
 */
static gint32 fd_collect_recent_phy_error(SML_PHY_DIAG_HISTORY_INFO_S *phy_history,
    SML_CTRL_PHY_DIAG_TOPO_INFO_S *topo_info, guint8 ctrl_index)
{
    gint32 ret = SML_SUCCESS;
    guint32 list_length1 = 0;
    guint32 list_length2 = 0;
    SML_SASPHY_INFO_S *ctrl_phy_info = NULL;
    SML_CTRL_EXP_SASPHY_INFO_S *exp_phy_info = NULL;
    guint8 is_exp_existed = FALSE;

    if (phy_history == NULL || topo_info == NULL) {
        return SML_ERR_NULL_DATA;
    }

    /* 拓扑中Expander数量大于0时，才需要对Expander进行诊断 */
    is_exp_existed = topo_info->exp_count > 0 ? TRUE : FALSE;

    /* 对RAID卡PHY误码历史记录数目进行判断，如果少于两次则触发收集 */
    list_length1 = g_list_length(phy_history->ctrl_phy_list);
    if (list_length1 < 2) {
        ctrl_phy_info = (SML_SASPHY_INFO_S *)g_malloc0(sizeof(SML_SASPHY_INFO_S));
        if (ctrl_phy_info == NULL) {
            ret = SML_ERR_CANNOT_ALLOC_MEM;
            debug_log(DLOG_ERROR, "%s : alloc ctrl phy info memory failed", __FUNCTION__);
            goto exit;
        }

        debug_log(DLOG_INFO, "%s : start first collect phy err, ctrl_index = %d", __FUNCTION__, ctrl_index);

        /* 触发一次RAID卡PHY误码收集 */
        unlock_ctrl_list_mutex(ctrl_index);
        ret = fd_collect_ctrl_phy_err_count(ctrl_index, ctrl_phy_info);
        lock_ctrl_list_mutex(ctrl_index);
        if (ret != SML_SUCCESS) {
            debug_log(DLOG_INFO, "%s : collect ctrl phy err failed, ctrl_index = %d, ret = 0x%x", __FUNCTION__,
                ctrl_index, ret);
            goto exit;
        }
    }

    /* 对Expander的PHY误码历史记录数目进行判断，如果少于两次且Expander存在才触发收集 */
    list_length2 = g_list_length(phy_history->exp_phy_list);
    if (is_exp_existed == TRUE && list_length2 < 2) {
        exp_phy_info = (SML_CTRL_EXP_SASPHY_INFO_S *)g_malloc0(sizeof(SML_CTRL_EXP_SASPHY_INFO_S));
        if (exp_phy_info == NULL) {
            ret = SML_ERR_CANNOT_ALLOC_MEM;
            debug_log(DLOG_ERROR, "%s : alloc exp phy info memory failed", __FUNCTION__);
            goto exit;
        }

        debug_log(DLOG_INFO, "%s : start first collect exp phy err, ctrl_index = %d", __FUNCTION__, ctrl_index);

        /* 触发一次Expander PHY误码收集 */
        unlock_ctrl_list_mutex(ctrl_index);
        ret = smlib_get_ctrl_exp_phy_err_count(ctrl_index, exp_phy_info);
        lock_ctrl_list_mutex(ctrl_index);
        if (ret != SML_SUCCESS) {
            debug_log(DLOG_INFO, "%s : collect exp phy err failed, ctrl_index = %d, ret = 0x%x", __FUNCTION__,
                ctrl_index, ret);
            goto exit;
        }
    }

    /* 如果存在历史记录数目为0的情况，则等待30s */
    if (list_length1 == 0 || (is_exp_existed == TRUE && list_length2 == 0)) {
        vos_task_delay(SMLIB_DELAY_HALF_MINUTE);
    }

    /* 触发第二次收集RAID卡PHY误码 */
    if (list_length1 == 0) {
        debug_log(DLOG_INFO, "%s : start second collect phy err, ctrl_index = %d", __FUNCTION__, ctrl_index);

        (void)memset_s(ctrl_phy_info, sizeof(SML_SASPHY_INFO_S), 0, sizeof(SML_SASPHY_INFO_S));
        unlock_ctrl_list_mutex(ctrl_index);
        ret = fd_collect_ctrl_phy_err_count(ctrl_index, ctrl_phy_info);
        lock_ctrl_list_mutex(ctrl_index);
        if (ret != SML_SUCCESS) {
            debug_log(DLOG_INFO, "%s : collect ctrl phy err failed, ctrl_index = %d, ret = 0x%x", __FUNCTION__,
                ctrl_index, ret);
            goto exit;
        }
    }

    /* 触发第二次收集Expander PHY误码 */
    if (is_exp_existed == TRUE && list_length2 == 0) {
        debug_log(DLOG_INFO, "%s : start second collect exp phy err, ctrl_index = %d", __FUNCTION__, ctrl_index);

        (void)memset_s(exp_phy_info, sizeof(SML_CTRL_EXP_SASPHY_INFO_S), 0, sizeof(SML_CTRL_EXP_SASPHY_INFO_S));
        unlock_ctrl_list_mutex(ctrl_index);
        ret = smlib_get_ctrl_exp_phy_err_count(ctrl_index, exp_phy_info);
        lock_ctrl_list_mutex(ctrl_index);
        if (ret != SML_SUCCESS) {
            debug_log(DLOG_INFO, "%s : collect exp phy err failed, ctrl_index = %d, ret = 0x%x", __FUNCTION__,
                ctrl_index, ret);
            goto exit;
        }
    }

exit:

    if (ctrl_phy_info != NULL) {
        g_free(ctrl_phy_info);
    }
    if (exp_phy_info != NULL) {
        g_free(exp_phy_info);
    }

    return ret;
}

/*
 * Description: 进行最近一次的连续PHY误码诊断
 * History: 1.2019年1月24日,
 *          新生成函数
 */
static gint32 fd_diag_recent_link_phy_error(SML_PHY_DIAG_HISTORY_INFO_S *phy_history,
    SML_CTRL_PHY_DIAG_TOPO_INFO_S *topo_info, SML_PD_FAULT_ANALYSIS *diag_info, guint32 phy_threshold)
{
    gint32 ret = SML_SUCCESS;

    /* 入参检查 */
    if (phy_history == NULL || topo_info == NULL || diag_info == NULL) {
        return SML_ERR_NULL_DATA;
    }

    /* 检查PHY误码历史数据是否满足诊断，如果不满足，则进行实时收集以达到诊断条件 */
    ret = fd_collect_recent_phy_error(phy_history, topo_info, diag_info->i_controller_index);
    if (ret != SML_SUCCESS) {
        return ret;
    }

    /* RAID卡PHY误码诊断 */
    ret = fd_diag_recent_ctrl_phy_error(phy_history, topo_info, diag_info, phy_threshold);
    if (ret != SML_SUCCESS) {
        return ret;
    }

    /* 如果拓扑结构中没有Expander，则不进行Expander诊断 */
    if (topo_info->exp_count == 0) {
        return ret;
    }

    /* 进行Expander的PHY误码诊断 */
    ret = fd_diag_recent_exp_phy_error(phy_history, topo_info, diag_info, phy_threshold);

    return ret;
}

/*
 * Description: 诊断Expander与RAID卡之间的通信故障
 * History: 1.2019年1月7日,
 *          新生成函数
 */
gint32 smlib_diagnose_encl_comm_error(guint8 ctrl_index, guint16 pd_device_id, guint16 encl_device_id,
    guint32 *comm_error_state)
{
    gint32 ret = SML_SUCCESS;
    guint8 is_encl_single = TRUE; // 硬盘所在控制器下的encl设备是否唯一
    SML_CTRL_S* sml_ctrl = NULL;

    ret = check_input_parameters(ctrl_index, comm_error_state);
    if (ret != SML_SUCCESS) {
        return ret;
    }

    lock_ctrl_list_mutex(ctrl_index);
    sml_ctrl = get_ctrl_by_index(ctrl_index);
    if (sml_ctrl == NULL) {
        unlock_ctrl_list_mutex(ctrl_index);
        return SML_ERR_CTRL_NOT_REGISTERED;
    }
    g_mutex_lock(&sml_ctrl->ctrl_mutex);

    if (sml_ctrl->pfn_diag_encl_comm_error != NULL) {
        is_encl_single = sml_ctrl->controller.expander.expander_count == 1 ? TRUE : FALSE;
        ret = sml_ctrl->pfn_diag_encl_comm_error(sml_ctrl->controller_id, pd_device_id,
            encl_device_id, is_encl_single, comm_error_state);
    } else {
        ret = SML_ERR_NULL_INFTERFACE;
    }

    g_mutex_unlock(&sml_ctrl->ctrl_mutex);
    unlock_ctrl_list_mutex(ctrl_index);

    return ret;
}

/*
 * Description: 诊断硬盘的sense error
 * History: 1.2019年1月10日,
 *          新生成函数
 */
gint32 smlib_diagnose_pd_sense_error(guint8 ctrl_index, guint16 pd_device_id, gchar *io_buf,
    guint32 *sense_error_state)
{
    gint32 ret = SML_SUCCESS;
    SML_CTRL_S* sml_ctrl = NULL;

    ret = check_input_parameters(ctrl_index, sense_error_state);
    if (ret != SML_SUCCESS) {
        return ret;
    }

    lock_ctrl_list_mutex(ctrl_index);
    sml_ctrl = get_ctrl_by_index(ctrl_index);
    if (sml_ctrl == NULL) {
        unlock_ctrl_list_mutex(ctrl_index);
        return SML_ERR_CTRL_NOT_REGISTERED;
    }
    g_mutex_lock(&sml_ctrl->ctrl_mutex);

    if (sml_ctrl->pfn_diag_pd_sense_error != NULL) {
        ret = sml_ctrl->pfn_diag_pd_sense_error(ctrl_index, pd_device_id, io_buf, sense_error_state);
    } else {
        ret = SML_ERR_NULL_INFTERFACE;
    }

    g_mutex_unlock(&sml_ctrl->ctrl_mutex);
    unlock_ctrl_list_mutex(ctrl_index);

    return ret;
}

/*
 * Description: 诊断链路PHY误码故障
 * History: 1.2019年1月21日,
 *          新生成函数
 */
gint32 smlib_diagnose_link_phy_error(SML_PD_FAULT_ANALYSIS *diag_info, guint32 serial_threshold,
    guint32 recent_threshold)
{
    gint32 ret = SML_SUCCESS;
    gint32 ret2 = SML_SUCCESS;
    SML_CTRL_PHY_DIAG_TOPO_INFO_S *topo_info = NULL;
    SML_CTRL_S* sml_ctrl = NULL;

    /* 入参检查 */
    if (diag_info == NULL) {
        return SML_ERR_NULL_DATA;
    }

    debug_log(DLOG_INFO, "%s : Start diagnose link phy error, ctrl_index = %d, pd_device_id = %d", __FUNCTION__,
        diag_info->i_controller_index, diag_info->i_pd_device_id);

    ret = check_input_parameters(diag_info->i_controller_index, diag_info);
    if (ret != SML_SUCCESS) {
        return ret;
    }

    /* 维护描述信息数组初始化 */
    (void)memset_s(diag_info->io_buf, SML_MAX_DETAIL_DESC_SIZE, 0, SML_MAX_DETAIL_DESC_SIZE);

    /* 获取拓扑结构信息 */
    topo_info = (SML_CTRL_PHY_DIAG_TOPO_INFO_S *)g_malloc0(sizeof(SML_CTRL_PHY_DIAG_TOPO_INFO_S));
    if (topo_info == NULL) {
        debug_log(DLOG_ERROR, "%s : malloc topo info failed", __FUNCTION__);
        return SML_ERR_CANNOT_ALLOC_MEM;
    }

    ret = fd_get_link_phy_diag_topo_info(diag_info->i_controller_index, diag_info->i_pd_device_id, topo_info);
    if (ret != SML_SUCCESS) {
        g_free(topo_info);
        debug_log(DLOG_INFO, "%s : get phy diag topo info failed, ret = 0x%x", __FUNCTION__, ret);
        return ret;
    }

    /* 获取连续5次的诊断结果 */
    lock_ctrl_list_mutex(diag_info->i_controller_index);
    sml_ctrl = get_ctrl_by_index(diag_info->i_controller_index);
    if (sml_ctrl == NULL) {
        g_free(topo_info);
        unlock_ctrl_list_mutex(diag_info->i_controller_index);
        return SML_ERR_CTRL_NOT_REGISTERED;
    }
    ret = fd_diag_serial_link_phy_error(&sml_ctrl->controller.phy_history, topo_info,
        diag_info, serial_threshold);
    if (ret != SML_SUCCESS) {
        debug_log(DLOG_INFO, "%s : diagnose serial link phy error failed, ctrl_index = %d, ret = 0x%x", __FUNCTION__,
            diag_info->i_controller_index, ret);
    }

    /* 获取最近1次的诊断结果 */
    ret2 = fd_diag_recent_link_phy_error(&sml_ctrl->controller.phy_history, topo_info,
        diag_info, recent_threshold);
    if (ret2 != SML_SUCCESS) {
        debug_log(DLOG_INFO, "%s : diagnose recent link phy error failed, ctrl_index = %d, ret = 0x%x", __FUNCTION__,
            diag_info->i_controller_index, ret2);
    }

    debug_log(DLOG_INFO, "%s : End diagnose link phy error, ret1 = 0x%x, ret2 = 0x%x, diag_bitmap(%d/%d/%d/%d)",
        __FUNCTION__, ret, ret2, diag_info->o_fault_bitmap.ctrl_encl_link_error,
        diag_info->o_fault_bitmap.encl_pd_link_error, diag_info->o_fault_bitmap.ctrl_pd_link_error,
        diag_info->o_fault_bitmap.encl_encl_link_error);

    /* 只要有一项诊断成功，则最终结果为成功 */
    if (ret == SML_SUCCESS || ret2 == SML_SUCCESS) {
        ret = SML_SUCCESS;
    } else {
        /* 如果诊断失败，返回最近1次诊断的返回码，并设置链路故障标志为0 */
        ret = ret2;
        diag_info->o_fault_bitmap.ctrl_encl_link_error = 0;
        diag_info->o_fault_bitmap.encl_pd_link_error = 0;
        diag_info->o_fault_bitmap.ctrl_pd_link_error = 0;
        diag_info->o_fault_bitmap.encl_encl_link_error = 0;
    }

    g_free(topo_info);
    unlock_ctrl_list_mutex(diag_info->i_controller_index);

    return ret;
}

/*
 * Description: 模拟RAID卡事件，用于诊断
 * History: 1.2019年1月11日,
 *          新生成函数
 */
gint32 smlib_mock_ctrl_event(guint8 ctrl_index, guint8 mock_count, SML_MOCK_CTRL_EVENT_S *mock_event)
{
    gint32 ret = SML_SUCCESS;
    gint32 i = 0;
    SML_CTRL_S* sml_ctrl = NULL;

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

    ret = check_input_parameters(ctrl_index, (gpointer)1);
    if (ret != SML_SUCCESS) {
        return ret;
    }

    lock_ctrl_list_mutex(ctrl_index);
    sml_ctrl = get_ctrl_by_index(ctrl_index);
    if (sml_ctrl == NULL) {
        unlock_ctrl_list_mutex(ctrl_index);
        return SML_ERR_CTRL_NOT_REGISTERED;
    }
    g_mutex_lock(&sml_ctrl->ctrl_mutex);

    if (sml_ctrl->pfn_mock_diag_ctrl_event != NULL) {
        for (i = 0; i < mock_count; i++) {
            ret = sml_ctrl->pfn_mock_diag_ctrl_event(sml_ctrl->controller_id, mock_event);
        }
    }
    g_mutex_unlock(&sml_ctrl->ctrl_mutex);
    unlock_ctrl_list_mutex(ctrl_index);

    return ret;
}