/* 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 <stdio.h>
#include "platform.h"
#include "sml_errcodes.h"
#include "sml.h"
#include "sml_common.h"
#include "sl_diagnose.h"

/* 解决包含scsi-t10.h的编译问题 */
typedef U8 uchar;
typedef U16 ushort;
typedef U32 uint;
typedef U8 uint8;
typedef U64 uint64;


#include <errno.h>
#include "lsi/scsi-t10.h"

typedef struct _tag_controller_event {
    gulong receive_timestamp;
    MR_EVT_DETAIL details;
} SL_CONTROLLER_EVENT_S;

typedef struct _tag_sml_diagnose_event {
    guint32 ctrl_id;
    GList *encl;     // 指向SL_CONTROLLER_EVENT_S的链表
    GList *pd_state; // 指向SL_CONTROLLER_EVENT_S的链表
    GList *pd_sense; // 指向SL_CONTROLLER_EVENT_S的链表
} SL_DIAGNOSE_EVENT_S;

/* ----------------------------------------------*
 * 外部变量说明                                 *
 * ---------------------------------------------- */

/* ----------------------------------------------*
 * 外部函数原型说明                             *
 * ---------------------------------------------- */

/* ----------------------------------------------*
 * 内部函数原型说明                             *
 * ---------------------------------------------- */

static void RemoveInvaildCtrlEvent(GList **event_list, gulong current_timestamp, gulong time_interval);
static void RemoveLastPDStateEvent(GList **event_list, guint16 pd_device_id);
static void RemoveCtrlEventByDID(SL_DIAGNOSE_EVENT_S *diag_event, guint16 pd_device_id);
static guint8 CheckTimestampIsVaild(gulong event_timestamp, gulong diag_timestamp, gulong time_interval);
static guint8 CheckPDStateChangeEvent(MR_EVT_DETAIL *event_detail);
static guint8 CheckPDSenseCodeEvent(MR_EVT_DETAIL *event_detail, SCSI_SENSE_DISECT_S *sense_data,
    const gchar *vendor_name);
static guint8 CheckDiagEventIsUseful(MR_EVT_DETAIL *pEventDetail);
static gint32 GetEnclDiagEventCount(GList *encl_list, guint16 encl_device_id, guint8 is_encl_single,
    guint32 *event_count_arr, gulong diag_timestamp, gulong time_interval);
static gint32 GetEnclDiagTimestamp(GList *pd_state_list, guint16 pd_device_id, gulong *time_stamp);
static gint32 GetDiagEventByCtrlId(guint32 ctrl_id, SL_DIAGNOSE_EVENT_S **diag_event);
static guint8 CheckExpIsVisited(guint8 *topo_data, gint32 device_offset, SML_CTRL_PHY_DIAG_TOPO_INFO_S *topo_info,
    guint8 *exp_index);
static guint8 VisitExpDFS(guint8 *topo_data, gint32 device_offset, SML_CTRL_PHY_DIAG_TOPO_INFO_S *topo_info,
    guint16 pd_device_id, guint8 *is_pd_found, guint8 *exp_level);

/* ----------------------------------------------*
 * 全局变量                                     *
 * ---------------------------------------------- */

static GMutex gCtrlDiagMutex;       // 用于操作链表的互斥量
static GList *gCtrlDiagList = NULL; // 用于记录每张RAID卡的诊断信息的链表

/* 用于诊断硬盘sense code */
static SCSI_SENSE_DISECT_S g_error_sense_info[] = {
    // medium error
    { 0xff, SCSI_KEY_MEDIUM_ERROR, SCSI_ASC_UNRECOVERED_READ,     SCSI_ASCQ_CAUSE_NOT_REPORTABLE },
    { 0xff, SCSI_KEY_MEDIUM_ERROR, SCSI_ASC_UNRECOVERED_READ,     SCSI_ASCQ_FORMAT_IN_PROGRESS },
    { 0xff, SCSI_KEY_MEDIUM_ERROR, SCSI_ASC_DATA_SYNC_MARK_ERROR, SCSI_ASCQ_CAUSE_NOT_REPORTABLE },
    // Hardware error
    { 0xff, SCSI_KEY_HARDWARE_ERROR, SCSI_ASC_DATA_SYNC_MARK_ERROR, SCSI_ASCQ_CAUSE_NOT_REPORTABLE },
    { 0xff, SCSI_KEY_HARDWARE_ERROR, SCSI_ASC_LOGICAL_UNIT_FAILURE, SCSI_ASCQ_MANUAL_INTERVENTION_REQUIRED },
    // other error
    { 0xff, SCSI_KEY_MISCOMPARE, SCSI_ASC_DURING_VERIFY_BYTE_CHECK_OPEARTION, SCSI_ASCQ_CAUSE_NOT_REPORTABLE }
};

static SCSI_SENSE_DISECT_S g_error_sense_info_vendor_specific[][6] = {
    // 其他厂家
    {
        { 0xff, 0xff, SCSI_ASC_FAILURE_THRESHOLD_EXCEEDED, 0xff },
    },
    // vendor: HUAWEI/RAMAXEL/UMIS
    {
        { 0xff, 0xff, SCSI_ASC_FAILURE_THRESHOLD_EXCEEDED, SCSI_ASCQ_GENERAL_HARD_DRIVE_FAILURE },
        { 0xff, 0xff, SCSI_ASC_FAILURE_THRESHOLD_EXCEEDED, SCSI_ASCQ_HW_TOO_MANY_BLOCK_REASSIGNED },
        { 0xff, 0xff, SCSI_ASC_FAILURE_THRESHOLD_EXCEEDED, SCSI_ASCQ_FAILURE_CONTROLLER_DETECTED },
        { 0xff, 0xff, SCSI_ASC_FAILURE_THRESHOLD_EXCEEDED, SCSI_ASCQ_FW_TOO_MANY_BLOCK_REASSIGN },
        { 0xff, 0xff, SCSI_ASC_FAILURE_THRESHOLD_EXCEEDED, SCSI_ASCQ_HARD_DIRVE_FAILURE },
        { 0xff, 0xff, SCSI_ASC_FAILURE_THRESHOLD_EXCEEDED, SCSI_ASCQ_THROUGHPUT_PERFORMANCE },
    }
};

/* ----------------------------------------------*
 * 模块级变量                                   *
 * ---------------------------------------------- */

/* ----------------------------------------------*
 * 常量定义                                     *
 * ---------------------------------------------- */

/* ----------------------------------------------*
 * 宏定义                                       *
 * ---------------------------------------------- */

#define CTRL_EVENT_TIME_DIAG_INTERVAL (2 * 60 * 1000)

/* ----------------------------------------------*
 * 外部函数实现                                       *
 * ---------------------------------------------- */

/*
 * Description: 初始化存储诊断信息
 * History: 2019年1月3日   新生成函数
*/
void InitCtrlDiagEvent(guint32 ctrl_id)
{
    gint32 ret = SML_SUCCESS;
    SL_DIAGNOSE_EVENT_S *diag_event = NULL;

    /* 若已存在该ctrl_id的诊断事件，则不进行初始化 */
    ret = GetDiagEventByCtrlId(ctrl_id, &diag_event);
    if (ret == SML_SUCCESS) {
        return;
    }

    diag_event = (SL_DIAGNOSE_EVENT_S *)g_malloc0(sizeof(SL_DIAGNOSE_EVENT_S));
    if (diag_event == NULL) {
        debug_log(DLOG_ERROR, "%s : Malloc diagnose event struct failed, ctrl_id = %d", __FUNCTION__, ctrl_id);
        return;
    }

    /* 初始化诊断信息的数据 */
    diag_event->ctrl_id = ctrl_id;
    diag_event->encl = NULL;
    diag_event->pd_state = NULL;
    diag_event->pd_sense = NULL;

    g_mutex_lock(&gCtrlDiagMutex);

    gCtrlDiagList = g_list_prepend(gCtrlDiagList, (gpointer)diag_event);

    g_mutex_unlock(&gCtrlDiagMutex);

    return;
}

/*
 * Description: 清除存储诊断信息
 * History: 2019年1月3日   新生成函数
*/
void RemoveCtrlDiagEvent(guint32 ctrl_id)
{
    GList *list_temp = NULL;
    GList *remove_node = NULL;
    SL_DIAGNOSE_EVENT_S *diag_event = NULL;

    if (gCtrlDiagList == NULL) {
        return;
    }

    g_mutex_lock(&gCtrlDiagMutex);

    list_temp = gCtrlDiagList;

    while (list_temp != NULL) {
        diag_event = (SL_DIAGNOSE_EVENT_S *)list_temp->data;

        /* 如果ctrl_id匹配 */
        if (diag_event != 0 && diag_event->ctrl_id == ctrl_id) {
            /* 释放节点内的链表数据 */
            if (diag_event->encl != NULL) {
                g_list_free_full(diag_event->encl, g_free);
            }
            if (diag_event->pd_state != NULL) {
                g_list_free_full(diag_event->pd_state, g_free);
            }
            if (diag_event->pd_sense != NULL) {
                g_list_free_full(diag_event->pd_sense, g_free);
            }

            remove_node = list_temp;
            list_temp = g_list_next(list_temp);

            /* 从链表中移除对应数据的节点 */
            gCtrlDiagList = g_list_remove_link(gCtrlDiagList, remove_node);
            g_list_free_full(remove_node, g_free);
            break;
        } else {
            list_temp = g_list_next(list_temp);
        }
    }

    g_mutex_unlock(&gCtrlDiagMutex);

    return;
}

/*
 * Description: 只删除记录的日志信息，不删除节点
 * History: 2019年3月4日   新生成函数
*/
void ClearCtrlDiagEvent(guint32 ctrl_id)
{
    GList *list_temp = NULL;
    SL_DIAGNOSE_EVENT_S *diag_event = NULL;

    if (gCtrlDiagList == NULL) {
        return;
    }

    g_mutex_lock(&gCtrlDiagMutex);

    list_temp = gCtrlDiagList;

    while (list_temp != NULL) {
        diag_event = (SL_DIAGNOSE_EVENT_S *)list_temp->data;

        /* 如果ctrl_id匹配 */
        if (diag_event != 0 && diag_event->ctrl_id == ctrl_id) {
            /* 释放节点内的链表数据 */
            if (diag_event->encl != NULL) {
                g_list_free_full(diag_event->encl, g_free);
            }
            if (diag_event->pd_state != NULL) {
                g_list_free_full(diag_event->pd_state, g_free);
            }
            if (diag_event->pd_sense != NULL) {
                g_list_free_full(diag_event->pd_sense, g_free);
            }
            diag_event->encl = NULL;
            diag_event->pd_state = NULL;
            diag_event->pd_sense = NULL;
            break;
        }

        list_temp = g_list_next(list_temp);
    }

    g_mutex_unlock(&gCtrlDiagMutex);

    return;
}

/*
 * Description: 记录存储诊断事件
 * History: 2019年1月3日   新生成函数
*/
void RecordCtrlDiagEvent(guint32 ctrl_id, MR_EVT_DETAIL *pEventDetail)
{
    SL_DIAGNOSE_EVENT_S *diag_event = NULL;
    SL_CONTROLLER_EVENT_S *ctrl_event = NULL;
    gulong current_timestamp = vos_tick_get();
    guint16 pd_device_id = 0;

    if (pEventDetail == NULL) {
        return;
    }

    /* 如果事件未被用到，则直接返回 */
    if (CheckDiagEventIsUseful(pEventDetail) != TRUE) {
        return;
    }

    /* 获取指定ctrl_id的诊断数据 */
    (void)GetDiagEventByCtrlId(ctrl_id, &diag_event);

    /* 未找到指定ctrl_id的诊断数据，返回打印错误信息 */
    if (diag_event == NULL) {
        debug_log(DLOG_ERROR, "%s : Not found diagnose event, ctrl_id = %d", __FUNCTION__, ctrl_id);
        return;
    }

    ctrl_event = (SL_CONTROLLER_EVENT_S *)g_malloc0(sizeof(SL_CONTROLLER_EVENT_S));
    if (ctrl_event == NULL) {
        debug_log(DLOG_ERROR, "%s : Malloc ctrl_event memory failed", __FUNCTION__);
        return;
    }

    /* 记录时间戳和事件数据 */
    ctrl_event->receive_timestamp = current_timestamp;
    if (EOK != memcpy_s(&ctrl_event->details, sizeof(MR_EVT_DETAIL), pEventDetail, sizeof(MR_EVT_DETAIL))) {
        debug_log(DLOG_DEBUG, "%s : memcpy_s pEventDetail failed", __FUNCTION__);
    }

    /* 获取操作锁，根据事件类型在链表中记录事件 */
    g_mutex_lock(&gCtrlDiagMutex);

    switch (pEventDetail->code) {
        case MR_EVT_SAS_TOPOLOGY_SMP_TIMEOUT:
        case MR_EVT_ENCL_COMMUNICATION_LOST:
        case MR_EVT_PD_COMMAND_TIMEOUT:
            /* Expander通信故障事件，在链表头记录encl相关的事件 */
            diag_event->encl = g_list_prepend(diag_event->encl, ctrl_event);

            /* 去除链表中两分钟以前的事件 */
            RemoveInvaildCtrlEvent(&diag_event->encl, current_timestamp, CTRL_EVENT_TIME_DIAG_INTERVAL);
            break;

        case MR_EVT_PD_STATE_CHANGE:
            /* 硬盘状态改变事件，获得硬盘device_id */
            pd_device_id = pEventDetail->args.pd.deviceId;

            /* 去除链表中两分钟以前的事件 */
            RemoveInvaildCtrlEvent(&diag_event->pd_state, current_timestamp, CTRL_EVENT_TIME_DIAG_INTERVAL);

            /* 移除上一个相同device_id的记录并替换 */
            RemoveLastPDStateEvent(&diag_event->pd_state, pd_device_id);
            diag_event->pd_state = g_list_prepend(diag_event->pd_state, ctrl_event);
            break;

        case MR_EVT_PD_SENSE:
            /* 硬盘sense事件，在链表头部记录硬盘sense_code相关的事件 */
            diag_event->pd_sense = g_list_prepend(diag_event->pd_sense, ctrl_event);

            /* 去除链表中两分钟以前的事件 */
            RemoveInvaildCtrlEvent(&diag_event->pd_sense, current_timestamp, CTRL_EVENT_TIME_DIAG_INTERVAL);
            break;

        case MR_EVT_PD_REMOVED:
            /* 硬盘拔出事件，需要清除对应的PD_STATE_CHAGNE和PD_SENSE事件 */
            RemoveCtrlEventByDID(diag_event, pEventDetail->args.pd.deviceId);

            /* 拔出事件不需要记录，释放申请的内存 */
            g_free(ctrl_event);
            break;

        default:
            g_free(ctrl_event);
            break;
    }

    /* 释放操作锁 */
    g_mutex_unlock(&gCtrlDiagMutex);

    return;
}

/*
 * Description: 从拓扑结构数据中解析出PHY误码诊断结构信息
 * History: 2019年1月23日   新生成函数
*/
gint32 PraseTopoForPHYDaig(guint8 *topo_data, SML_CTRL_PHY_DIAG_TOPO_INFO_S *topo_info, guint16 pd_device_id)
{
    gint32 i;
    SL_TOPOLOGY_HEAD_PHY_T *head_phy = NULL;
    SL_TOPOLOGY_HEAD_NODE_T *head = NULL;
    SL_TOPOLOGY_END_DEVICE_NODE_T *end_node = NULL;
    gint32 ret = SML_SUCCESS;
    guint8 ret2 = FALSE;
    guint8 is_pd_found = FALSE;
    guint8 exp_level = 0;

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

    head = (SL_TOPOLOGY_HEAD_NODE_T *)topo_data;
    head_phy = head->phyList;
    topo_info->ctrl_phy_count = head->numPhy;

    /* 遍历RAID卡的每个PHY */
    for (i = 0; i < head->numPhy; i++) {
        switch (head_phy->attachedDeviceType) {
            case SL_END_DEVICE:
                /* 连接设备类型为END_DEVICE，则判断device_id是否相同 */
                topo_info->ctrl_phy_info[i].connect_type = SML_PHY_CONNECT_END_DEVICE;
                topo_info->ctrl_phy_info[i].phy_type = SML_PHY_TYPE_DOWN;
                end_node = (SL_TOPOLOGY_END_DEVICE_NODE_T *)(topo_data + head_phy->attachedDeviceOffset);
                if (end_node->deviceId == pd_device_id) {
                    topo_info->ctrl_phy_info[i].diag_mark = TRUE;
                    is_pd_found = TRUE;
                }
                break;

            case SL_EDGE_EXPANDER:
            case SL_FANOUT_EXPANDER:
                /* 连接设备类型为ENCL_DEVICE，则向下遍历 */
                topo_info->ctrl_phy_info[i].connect_type = SML_PHY_CONNECT_ENCL_DEVICE;
                topo_info->ctrl_phy_info[i].phy_type = SML_PHY_TYPE_DOWN;
                ret2 = VisitExpDFS(topo_data, head_phy->attachedDeviceOffset, topo_info, pd_device_id, &is_pd_found,
                    &exp_level);
                topo_info->ctrl_phy_info[i].diag_mark = ret2;
                break;

            case SL_NO_DEVICE:
                /* 连接设备类型为NO_DEVICE，直接标记不诊断 */
                topo_info->ctrl_phy_info[i].connect_type = SML_PHY_CONNECT_NO_DEVICE;
                topo_info->ctrl_phy_info[i].phy_type = SML_PHY_TYPE_DOWN;
                topo_info->ctrl_phy_info[i].diag_mark = FALSE;
                break;

            default:
                /* 连接设备类型为OTHER_DEVICE，直接标记不诊断 */
                topo_info->ctrl_phy_info[i].connect_type = SML_PHY_CONNECT_OTHER_DEVICE;
                topo_info->ctrl_phy_info[i].phy_type = SML_PHY_TYPE_DOWN;
                topo_info->ctrl_phy_info[i].diag_mark = FALSE;
                break;
        }

        head_phy++;
    }

    /* 如果拓扑中获取的Expander的phy数目为0，则可能RAID卡FW不支持，返回错误 */
    for (i = 0; i < topo_info->exp_count; i++) {
        if (topo_info->exp_info[i].phy_count == 0) {
            return SML_ERR_EXP_NO_PHY;
        }
    }

    /* 如果在拓扑中找不到指定的硬盘，则返回错误 */
    if (is_pd_found != TRUE) {
        return SML_ERR_DIAG_TOPO_CAN_NOT_FIND_PD;
    }

    return ret;
}

/*
 * Description: 诊断Expander背板通信故障
 * History: 2019年2月23日   新生成函数
*/
gint32 DiagnoseEnclCommErr(guint32 ctrl_id, guint16 pd_device_id, guint16 encl_device_id, guint8 is_encl_single,
    guint32 *encl_comm_error_state)
{
    gint32 ret = SML_SUCCESS;
    SL_DIAGNOSE_EVENT_S *diag_event = NULL;
    gulong diag_timestamp = 0;
    guint32 event_count_arr[3] = { 0 };

#define SMP_TIMEOUT_DEBOUNCE_TIME 10
#define ENCL_COMM_LOST_DEBOUNCE_TIME 1
#define ENCL_CMD_TIMEOUT_DEBOUNCE_TIME 10

    if (encl_comm_error_state == NULL) {
        return SML_ERR_NULL_DATA;
    }
    if (gCtrlDiagList == NULL) {
        debug_log(DLOG_ERROR, "%s : gCtrlDiagList is null", __FUNCTION__);
        return SML_ERR_NULL_DATA;
    }

    /* 获取指定ctrl_id的诊断信息 */
    (void)GetDiagEventByCtrlId(ctrl_id, &diag_event);

    if (diag_event == NULL) {
        debug_log(DLOG_ERROR, "%s : can not find diag event, ctrl_id = %d", __FUNCTION__, ctrl_id);
        return SML_ERR_NULL_DATA;
    }

    /* 获取操作锁 */
    g_mutex_lock(&gCtrlDiagMutex);

    /* 获取诊断时间戳 */
    (void)GetEnclDiagTimestamp(diag_event->pd_state, pd_device_id, &diag_timestamp);

    /* 获取三种背板事件的计数 */
    GetEnclDiagEventCount(diag_event->encl, encl_device_id, is_encl_single, event_count_arr, diag_timestamp,
        CTRL_EVENT_TIME_DIAG_INTERVAL);

    /* 释放操作锁 */
    g_mutex_unlock(&gCtrlDiagMutex);

    /* 初始化标志位为0 */
    *encl_comm_error_state = 0;

    /* 判断三种事件是否达到告警阈值 */
    if (event_count_arr[0] >= SMP_TIMEOUT_DEBOUNCE_TIME || event_count_arr[1] >= ENCL_COMM_LOST_DEBOUNCE_TIME ||
        event_count_arr[2] >= ENCL_CMD_TIMEOUT_DEBOUNCE_TIME) {
        *encl_comm_error_state = 1;
    }

    return ret;
}

/*
 * Description: 诊断硬盘sense code故障
 * History: 2019年2月23日   新生成函数
*/
gint32 DiagnosePDSenseError(guint32 ctrl_id, guint16 pd_device_id, gchar *io_buf, guint32 *sense_error_state)
{
    gint32 ret = SML_SUCCESS;
    gulong diag_timestamp = 0;
    GList *list_temp = NULL;
    SL_DIAGNOSE_EVENT_S *diag_event = NULL;
    SL_CONTROLLER_EVENT_S *event_temp = NULL;
    SCSI_SENSE_DISECT_S sense_data = { 0 };

    if (io_buf == NULL || sense_error_state == NULL) {
        return SML_ERR_NULL_DATA;
    }
    if (gCtrlDiagList == NULL) {
        debug_log(DLOG_ERROR, "%s : gCtrlDiagList is null", __FUNCTION__);
        return SML_ERR_NULL_DATA;
    }

    /* 获取指定ctrl_id的诊断信息 */
    (void)GetDiagEventByCtrlId(ctrl_id, &diag_event);

    if (diag_event == NULL) {
        debug_log(DLOG_ERROR, "%s : can not find diag event, ctrl_id = %d", __FUNCTION__, ctrl_id);
        return SML_ERR_NULL_DATA;
    }
    /* 获取锁 */
    g_mutex_lock(&gCtrlDiagMutex);

    /* 获取诊断时间戳 */
    (void)GetEnclDiagTimestamp(diag_event->pd_state, pd_device_id, &diag_timestamp);

    /* 初始化sense_error_state为0 */
    *sense_error_state = 0;

    /* 进行pd sense诊断 */
    list_temp = diag_event->pd_sense;
    while (list_temp != NULL) {
        event_temp = (SL_CONTROLLER_EVENT_S *)list_temp->data;

        /* 事件的时间戳在范围内，且pd_device_id相同和sense code判断为有问题，才诊断为故障 */
        if (event_temp != NULL &&
            CheckTimestampIsVaild(event_temp->receive_timestamp, diag_timestamp, CTRL_EVENT_TIME_DIAG_INTERVAL) ==
            TRUE &&
            event_temp->details.args.cdbSense.pd.deviceId == pd_device_id &&
            CheckPDSenseCodeEvent(&event_temp->details, &sense_data, io_buf) == TRUE) {
            *sense_error_state = 1;
            /* 传出Sense code诊断信息，用于记录维护日志 */
            (void)memset_s(io_buf, SML_MAX_DETAIL_DESC_SIZE, 0, SML_MAX_DETAIL_DESC_SIZE);
            if (0 > snprintf_s(io_buf, SML_MAX_DETAIL_DESC_SIZE, SML_MAX_DETAIL_DESC_SIZE - 1,
                "sense: %02x/%02x/%02x", sense_data.sense_key, sense_data.asc, sense_data.ascq)) {
                debug_log(DLOG_DEBUG, "%s : snprintf_s detail desc failed", __FUNCTION__);
            }
            break;
        }

        list_temp = g_list_next(list_temp);
    }

    /* 释放锁 */
    g_mutex_unlock(&gCtrlDiagMutex);

    return ret;
}

/*
 * Description: 模拟插入RAID卡事件
 * History: 2019年2月23日   新生成函数
*/
gint32 MockInsertCtrlEvent(guint32 ctrl_id, SML_MOCK_CTRL_EVENT_S *mock_event)
{
    gint32 ret = SML_SUCCESS;
    SCSI_SENSE *scsi_sense = NULL;
    MR_EVT_DETAIL detail = { 0 };

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

    debug_log(DLOG_DEBUG, "lsi_mock_diag_ctrl_event, code=%d pd_id=%d encl_id=%d p_s=%d n_s=%d sense=%d %d %d",
        mock_event->event_code, mock_event->pd_device_id, mock_event->encl_device_id, mock_event->pd_prev_state,
        mock_event->pd_new_state, mock_event->pd_sense_key, mock_event->pd_sense_asc, mock_event->pd_sense_ascq);

    /* 模拟事件只支持以下几类事件 */
    if (mock_event->event_code != MR_EVT_SAS_TOPOLOGY_SMP_TIMEOUT &&
        mock_event->event_code != MR_EVT_ENCL_COMMUNICATION_LOST && mock_event->event_code != MR_EVT_PD_REMOVED &&
        mock_event->event_code != MR_EVT_PD_COMMAND_TIMEOUT && mock_event->event_code != MR_EVT_PD_STATE_CHANGE &&
        mock_event->event_code != MR_EVT_PD_SENSE) {
        return SML_ERR_DIAG_MOCK_NOT_SUPPORT_CODE;
    }

    /* 填充事件码 */
    detail.code = mock_event->event_code;

    /* 根据事件码填充其他模拟信息 */
    switch (mock_event->event_code) {
        case MR_EVT_SAS_TOPOLOGY_SMP_TIMEOUT:
            detail.argType = MR_EVT_ARGS_NONE;
            break;

        case MR_EVT_ENCL_COMMUNICATION_LOST:
            detail.argType = MR_EVT_ARGS_PD;
            detail.args.pd.deviceId = mock_event->encl_device_id;
            break;

        case MR_EVT_PD_COMMAND_TIMEOUT:
            detail.argType = MR_EVT_ARGS_CDB_SENSE;
            detail.args.cdbSense.pd.deviceId = mock_event->encl_device_id;
            detail.args.cdbSense.pd.enclDeviceId = mock_event->encl_device_id;
            break;

        case MR_EVT_PD_STATE_CHANGE:
            detail.argType = MR_EVT_ARGS_PD_STATE;
            detail.args.pdState.pd.deviceId = mock_event->pd_device_id;
            detail.args.pdState.prevState = mock_event->pd_prev_state;
            detail.args.pdState.newState = mock_event->pd_new_state;
            break;

        case MR_EVT_PD_SENSE:
            detail.argType = MR_EVT_ARGS_CDB_SENSE;
            detail.args.cdbSense.pd.deviceId = mock_event->pd_device_id;
            scsi_sense = (SCSI_SENSE *)detail.args.cdbSense.sense;
            scsi_sense->fixedFormat.senseKey = mock_event->pd_sense_key & 0x0F;
            scsi_sense->fixedFormat.asc = mock_event->pd_sense_asc;
            scsi_sense->fixedFormat.ascq = mock_event->pd_sense_ascq;
            break;

        case MR_EVT_PD_REMOVED:
            detail.argType = MR_EVT_ARGS_PD;
            detail.args.pd.deviceId = mock_event->pd_device_id;
            break;
        default:
            break;
    }

    /* 记录事件到链表中 */
    RecordCtrlDiagEvent(ctrl_id, &detail);

    return ret;
}

/*
 * Description: 根据sense code诊断设备故障
 * History: 2018年12月27日  新生成函数 PN:UADP135084
*/
static guint8 CheckPDSenseCodeCommon(SCSI_SENSE_DISECT_S *sense_info)
{
    guint32 i;
    guint32 num = (guint32)(sizeof(g_error_sense_info) / sizeof(g_error_sense_info[0]));
    guint8 key_match;
    guint8 asc_match;
    guint8 ascq_match;
    guint8 retval = FALSE;

    for (i = 0; i < num; i++) {
        key_match = TRUE;
        asc_match = TRUE;
        ascq_match = TRUE;
        // 0xff表示任意数值都匹配直接标记match，无需比较，否则进行比较标记
        if (0xff != g_error_sense_info[i].sense_key) {
            key_match = sense_info->sense_key == g_error_sense_info[i].sense_key ? TRUE : FALSE;
        }
        if (0xff != g_error_sense_info[i].asc) {
            asc_match = sense_info->asc == g_error_sense_info[i].asc ? TRUE : FALSE;
        }
        if (0xff != g_error_sense_info[i].ascq) {
            ascq_match = sense_info->ascq == g_error_sense_info[i].ascq ? TRUE : FALSE;
        }
        // 如果三项都匹配，说明硬盘故障，直接返回异常
        if (key_match == TRUE && asc_match == TRUE && ascq_match == TRUE) {
            retval = TRUE;
            break;
        }
    }

    return retval;
}

/*
 * Description: 根据sense code诊断设备故障，优先从厂家自定义找，如果匹配不上再从公共故障码匹配。
*/
guint8 CheckPDSenseCode(SCSI_SENSE_DISECT_S *sense_info, const gchar *vendor_name)
{
    guint32 i = 0;
    guint32 num = (guint32)(sizeof(g_error_sense_info_vendor_specific[0]) /
        sizeof(g_error_sense_info_vendor_specific[0][0]));
    guint8 key_match = 1;
    guint8 asc_match = 1;
    guint8 ascq_match = 1;
    guint8 retval = FALSE;
    guint8 index = 0;

    if (vendor_name != NULL) {
        if (strstr(vendor_name, PD_VENDOR_NAME_RAMAXEL) != NULL ||
            strstr(vendor_name, PD_VENDOR_NAME_HUAWEI) != NULL ||
            strstr(vendor_name, PD_VENDOR_NAME_UMIS) != NULL) {
            index = 1;
        }
    }

    for (i = 0; i < num; i++) {
        key_match = TRUE;
        asc_match = TRUE;
        ascq_match = TRUE;
        // 0xff表示任意数值都匹配直接标记match，无需比较，否则进行比较标记

        if (g_error_sense_info_vendor_specific[index][i].sense_key != 0xff) {
            key_match = (sense_info->sense_key == g_error_sense_info_vendor_specific[index][i].sense_key) ?
                TRUE : FALSE;
        }
        if (g_error_sense_info_vendor_specific[index][i].asc != 0xff) {
            asc_match = (sense_info->asc == g_error_sense_info_vendor_specific[index][i].asc) ? TRUE : FALSE;
        }
        if (g_error_sense_info_vendor_specific[index][i].ascq != 0xff) {
            ascq_match = (sense_info->ascq == g_error_sense_info_vendor_specific[index][i].ascq) ? TRUE : FALSE;
        }
        // 如果三项都匹配，说明硬盘故障，直接返回异常
        if (key_match == TRUE && asc_match == TRUE && ascq_match == TRUE) {
            retval = TRUE;
            break;
        }
    }

    return (retval == TRUE) || (CheckPDSenseCodeCommon(sense_info) == TRUE);
}

/*
 * Description: 判断PHY是不是虚拟PHY
 * History: 2019年1月23日   新生成函数
*/
guint8 CheckPHYIsVirtual(SL_TOPOLOGY_END_DEVICE_NODE_T *end_node)
{
    if (end_node == NULL) {
        return FALSE;
    }

    if (((end_node->deviceBitMap.SMPInitiator == 1) || (end_node->deviceBitMap.SSPInitiator == 1) ||
        (end_node->deviceBitMap.STPInitiator == 1)) &&
        ((end_node->deviceBitMap.SMPTarget == 1) || (end_node->deviceBitMap.SSPTarget == 1) ||
        (end_node->deviceBitMap.STPTarget == 1))) {
        return TRUE;
    }

    return FALSE;
}

/* ----------------------------------------------*
 * 本地函数实现                                       *
 * ---------------------------------------------- */

/*
 * Description: 根据时间戳移除掉过期失效的诊断数据, 需要在外部加锁
 * History: 2019年1月3日   新生成函数
*/
static void RemoveInvaildCtrlEvent(GList **event_list, gulong current_timestamp, gulong time_interval)
{
    GList *list_temp = *event_list;
    SL_CONTROLLER_EVENT_S *event_temp = NULL;

    /* 遍历链表，将失效的事件去除 */
    while (list_temp != NULL) {
        event_temp = (SL_CONTROLLER_EVENT_S *)list_temp->data;
        /* 找到时间戳失效的事件，在链表中删除该节点及之后的节点 */
        if (event_temp != NULL &&
            CheckTimestampIsVaild(event_temp->receive_timestamp, current_timestamp, time_interval) != TRUE) {
            /* 将上一个节点的next指向NULL，变成链表尾 */
            if (g_list_previous(list_temp) != NULL) {
                g_list_previous(list_temp)->next = NULL;
            }

            /* 将该节点的prev指向NULL，变成一个新的链表的头节点，删除该链表 */
            list_temp->prev = NULL;
            g_list_free_full(list_temp, g_free);

            /* 如果清除的是初始链表头节点，则设置初始链表为NULL */
            if (list_temp == *event_list) {
                *event_list = NULL;
            }
            break;
        }

        list_temp = g_list_next(list_temp);
    }

    return;
}

/*
 * Description: 去除指定硬盘的上一个状态变化事件
 * History: 2019年1月7日   新生成函数
*/
static void RemoveLastPDStateEvent(GList **event_list, guint16 pd_device_id)
{
    GList *list_temp = *event_list;
    GList *remove_node = NULL;
    SL_CONTROLLER_EVENT_S *event = NULL;

    /* 遍历链表，将失效的事件去除 */
    while (list_temp != NULL) {
        event = (SL_CONTROLLER_EVENT_S *)list_temp->data;
        /* 找到相同pd_device_id的事件，在链表中删除该节点 */
        if (event->details.args.pdState.pd.deviceId == pd_device_id) {
            remove_node = list_temp;
            list_temp = g_list_next(list_temp);
            *event_list = g_list_remove_link(*event_list, remove_node);
            g_list_free_full(remove_node, g_free);
        } else {
            list_temp = g_list_next(list_temp);
        }
    }

    return;
}
/*
 * Description: 根据PD的DeviceID移除PD_STATE_CHANGE和PD_SENSE事件
 * History: 2019年1月14日   新生成函数
*/
static void RemoveCtrlEventByDID(SL_DIAGNOSE_EVENT_S *diag_event, guint16 pd_device_id)
{
    GList *list_temp = NULL;
    GList *remove_node = NULL;
    SL_CONTROLLER_EVENT_S *event_temp = NULL;

    if (diag_event == NULL) {
        return;
    }

    /* 移除PD_STATE_CHANGE事件 */
    list_temp = diag_event->pd_state;
    while (list_temp != NULL) {
        event_temp = (SL_CONTROLLER_EVENT_S *)list_temp->data;
        /* 找到相同DeviceID的事件，在链表中删除该节点 */
        if (event_temp != NULL && event_temp->details.args.pd.deviceId == pd_device_id) {
            remove_node = list_temp;
            list_temp = g_list_next(list_temp);
            diag_event->pd_state = g_list_remove_link(diag_event->pd_state, remove_node);
            g_list_free_full(remove_node, g_free);
        } else {
            list_temp = g_list_next(list_temp);
        }
    }

    /* 移除PD_SENSE事件 */
    list_temp = diag_event->pd_sense;
    while (list_temp != NULL) {
        event_temp = (SL_CONTROLLER_EVENT_S *)list_temp->data;
        /* 找到相同DeviceID的事件，在链表中删除该节点 */
        if (event_temp != NULL && event_temp->details.args.cdbSense.pd.deviceId == pd_device_id) {
            remove_node = list_temp;
            list_temp = g_list_next(list_temp);
            diag_event->pd_sense = g_list_remove_link(diag_event->pd_sense, remove_node);
            g_list_free_full(remove_node, g_free);
        } else {
            list_temp = g_list_next(list_temp);
        }
    }

    return;
}

/*
 * Description: 判断RAID事件的时间戳是否有效, 事件时间戳在诊断事件戳正负范围内认为有效
 * History: 2019年1月10日   新生成函数
*/
static guint8 CheckTimestampIsVaild(gulong event_timestamp, gulong diag_timestamp, gulong time_interval)
{
    if (diag_timestamp >= event_timestamp && (diag_timestamp - event_timestamp) <= time_interval) {
        return TRUE;
    } else if (event_timestamp > diag_timestamp && (event_timestamp - diag_timestamp) <= time_interval) {
        return TRUE;
    } else if (event_timestamp > diag_timestamp && G_MAXUINT32 - event_timestamp + diag_timestamp <= time_interval) {
        /* 时间戳产生了绕回 */
        return TRUE;
    } else {
        return FALSE;
    }
}

/*
 * Description: 查PDStateChange事件是否从GOOD/ONLINE变成BAD/FAILED/OFFLINE
 * History: 2019年1月10日   新生成函数
*/
static guint8 CheckPDStateChangeEvent(MR_EVT_DETAIL *event_detail)
{
    /* 当传入参数异常情况返回，FALSE */
    if (event_detail == NULL) {
        return FALSE;
    }
    if (event_detail->code != MR_EVT_PD_STATE_CHANGE) {
        return FALSE;
    }
    if (event_detail->argType != MR_EVT_ARGS_PD_STATE) {
        return FALSE;
    }

    /* 当硬盘状态从UGOOD/ONLINE/JBOD变成UBAD/FAILED/OFFLINE时，返回TRUE */
    if ((event_detail->args.pdState.prevState == MR_PD_STATE_UNCONFIGURED_GOOD ||
        event_detail->args.pdState.prevState == MR_PD_STATE_ONLINE ||
        event_detail->args.pdState.prevState == MR_PD_STATE_SYSTEM) &&
        (event_detail->args.pdState.newState == MR_PD_STATE_UNCONFIGURED_BAD ||
        event_detail->args.pdState.newState == MR_PD_STATE_FAILED ||
        event_detail->args.pdState.newState == MR_PD_STATE_OFFLINE)) {
        return TRUE;
    }

    return FALSE;
}

/*
 * Description: 判断PDSenseCode事件是否为硬盘故障事件
 * History: 2019年2月21日   新生成函数
*/
static guint8 CheckPDSenseCodeEvent(MR_EVT_DETAIL *event_detail, SCSI_SENSE_DISECT_S *sense_data,
    const gchar *vendor_name)
{
    guint8 ret = FALSE;
    SCSI_SENSE *scsi_sense = NULL;

    /* 当传入参数异常情况返回FALSE */
    if (event_detail == NULL || sense_data == NULL || event_detail->code != MR_EVT_PD_SENSE ||
        event_detail->argType != MR_EVT_ARGS_CDB_SENSE) {
        return FALSE;
    }

    /* 获取事件中的sense_code参数 */
    scsi_sense = (SCSI_SENSE *)event_detail->args.cdbSense.sense;
    sense_data->sense_key = (SCSI_SENSE_KEY)scsi_sense->fixedFormat.senseKey;
    sense_data->asc = (SCSI_ASENSE)scsi_sense->fixedFormat.asc;
    sense_data->ascq = scsi_sense->fixedFormat.ascq;

    /* 判断PDSenseCode是否能诊断出硬盘故障 */
    ret = CheckPDSenseCode(sense_data, vendor_name);

    return ret;
}

/*
 * Description: 判断诊断事件是否为有用的
 * History: 2019年1月14日   新生成函数
*/
static guint8 CheckDiagEventIsUseful(MR_EVT_DETAIL *pEventDetail)
{
    SCSI_SENSE_DISECT_S sense_data = { 0 };

    if (pEventDetail == NULL) {
        return FALSE;
    }

    /* 只用到以下几种事件码的事件, 排除掉其他事件码的事件 */
    if (pEventDetail->code != MR_EVT_SAS_TOPOLOGY_SMP_TIMEOUT && pEventDetail->code != MR_EVT_ENCL_COMMUNICATION_LOST &&
        pEventDetail->code != MR_EVT_PD_COMMAND_TIMEOUT && pEventDetail->code != MR_EVT_PD_STATE_CHANGE &&
        pEventDetail->code != MR_EVT_PD_SENSE && pEventDetail->code != MR_EVT_PD_REMOVED) {
        return FALSE;
    }

    /* 排除掉PD_COMMAND_TIMEOUT事件类型中非ENCL的事件 */
    if (pEventDetail->code == MR_EVT_PD_COMMAND_TIMEOUT && pEventDetail->argType != MR_EVT_ARGS_CDB_SENSE) {
        return FALSE;
    }
    if (pEventDetail->code == MR_EVT_PD_COMMAND_TIMEOUT &&
        pEventDetail->args.cdbSense.pd.deviceId != pEventDetail->args.cdbSense.pd.enclDeviceId) {
        return FALSE;
    }

    /* 排除掉PD_STATE_CHANGE事件中不是从UG/ONLINE/JBOD变为UB/FAILED/OFFLINE的事件 */
    if (pEventDetail->code == MR_EVT_PD_STATE_CHANGE && CheckPDStateChangeEvent(pEventDetail) != TRUE) {
        return FALSE;
    }

    /* 排除掉不能诊断出有故障的PD_SENSE事件 */
    if (pEventDetail->code == MR_EVT_PD_SENSE && CheckPDSenseCodeEvent(pEventDetail, &sense_data, NULL) != TRUE) {
        return FALSE;
    }

    return TRUE;
}

/*
 * Description: 根据时间戳获取背板诊断事件的计数
 * History: 2019年1月8日   新生成函数
*/
static gint32 GetEnclDiagEventCount(GList *encl_list, guint16 encl_device_id, guint8 is_encl_single,
    guint32 *event_count_arr, gulong diag_timestamp, gulong time_interval)
{
    GList *list_temp = encl_list;
    SL_CONTROLLER_EVENT_S *event = NULL;

    if (encl_list == NULL || event_count_arr == NULL) {
        return RET_ERR;
    }

    /* 遍历链表对事件计数 */
    while (list_temp != NULL) {
        event = (SL_CONTROLLER_EVENT_S *)list_temp->data;

        if (event == NULL) {
            continue;
        }

        /* 只有当RAID卡下只有一个Encl设备的时候才判断SMP_TIMEOUT事件 */
        if (event->details.code == MR_EVT_SAS_TOPOLOGY_SMP_TIMEOUT && is_encl_single == TRUE &&
            CheckTimestampIsVaild(event->receive_timestamp, diag_timestamp, time_interval) == TRUE) {
            event_count_arr[0]++;
        }

        if (event->details.code == MR_EVT_ENCL_COMMUNICATION_LOST &&
            CheckTimestampIsVaild(event->receive_timestamp, diag_timestamp, time_interval) == TRUE &&
            event->details.args.pd.deviceId == encl_device_id) {
            event_count_arr[1]++;
        }

        if (event->details.code == MR_EVT_PD_COMMAND_TIMEOUT &&
            CheckTimestampIsVaild(event->receive_timestamp, diag_timestamp, time_interval) == TRUE &&
            event->details.args.cdbSense.pd.deviceId == encl_device_id) {
            event_count_arr[2]++;
        }

        list_temp = g_list_next(list_temp);
    }

    debug_log(DLOG_DEBUG, "%s : encl_device_id = 0x%x, event_count:%d %d %d", __FUNCTION__, encl_device_id,
        event_count_arr[0], event_count_arr[1], event_count_arr[2]);

    return RET_OK;
}

/*
 * Description: 获取背板诊断的时间戳，取最近一次PD_STATE_CHAGNE的时间戳
 * History: 2019年1月8日   新生成函数
*/
static gint32 GetEnclDiagTimestamp(GList *pd_state_list, guint16 pd_device_id, gulong *time_stamp)
{
    gint32 ret = RET_OK;
    GList *list_temp = pd_state_list;
    SL_CONTROLLER_EVENT_S *event_temp = NULL;

    if (time_stamp == NULL) {
        return RET_ERR;
    }

    /* 初始化时间戳为当前时间戳 */
    *time_stamp = vos_tick_get();

    /* 如果能在pd_state的链表中找到最近一次的状态变化事件，则选择此时间戳，否则为当前时间戳 */
    while (list_temp != NULL) {
        event_temp = (SL_CONTROLLER_EVENT_S *)list_temp->data;
        if (event_temp != NULL && event_temp->details.args.pdState.pd.deviceId == pd_device_id) {
            *time_stamp = event_temp->receive_timestamp;
            break;
        }
        list_temp = g_list_next(list_temp);
    }

    return ret;
}

/*
 * Description: 从诊断信息链表中找到指定ctrl_id的信息
 * History: 2019年1月9日   新生成函数
*/
static gint32 GetDiagEventByCtrlId(guint32 ctrl_id, SL_DIAGNOSE_EVENT_S **diag_event)
{
    GList *list_temp = gCtrlDiagList;
    SL_DIAGNOSE_EVENT_S *event_temp = NULL;

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

    g_mutex_lock(&gCtrlDiagMutex);

    /* 遍历诊断信息列表，找到相同ctrl_id的诊断信息 */
    while (list_temp != NULL) {
        event_temp = (SL_DIAGNOSE_EVENT_S *)list_temp->data;

        if (event_temp != NULL && event_temp->ctrl_id == ctrl_id) {
            *diag_event = event_temp;
            g_mutex_unlock(&gCtrlDiagMutex);

            return SML_SUCCESS;
        }

        list_temp = g_list_next(list_temp);
    }

    g_mutex_unlock(&gCtrlDiagMutex);

    return SML_ERR_NULL_DATA;
}

/*
 * Description: 拓扑结构中判断Expander是否已被遍历过
 * History: 2019年1月23日   新生成函数
*/
static guint8 CheckExpIsVisited(guint8 *topo_data, gint32 device_offset, SML_CTRL_PHY_DIAG_TOPO_INFO_S *topo_info,
    guint8 *exp_index)
{
    SL_TOPOLOGY_EXPANDER_NODE_T *topo_exp = NULL;
    guint8 is_exp_exist = FALSE;
    gint32 i;

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

    topo_exp = (SL_TOPOLOGY_EXPANDER_NODE_T *)(topo_data + device_offset);

    /* 遍历已记录的Expander，判断是否已经遍历过 */
    for (i = 0; i < topo_info->exp_count; i++) {
        if (topo_info->exp_info[i].sas_addr == topo_exp->sasAddr) {
            is_exp_exist = TRUE;
            break;
        }
    }

    /* 如果找到，则记录索引 */
    if (is_exp_exist == TRUE) {
        *exp_index = i;
    }

    return is_exp_exist;
}

/*
 * Description: 拓扑结构中深度遍历Expander
 * History: 2019年1月23日   新生成函数
*/
static guint8 VisitExpDFS(guint8 *topo_data, gint32 device_offset, SML_CTRL_PHY_DIAG_TOPO_INFO_S *topo_info,
    guint16 pd_device_id, guint8 *is_pd_found, guint8 *exp_level)
{
    guint8 ret = FALSE;
    guint8 ret2 = FALSE;
    guint8 exp_index = 0;
    guint8 exp_level_temp = 0;
    SL_TOPOLOGY_EXPANDER_NODE_T *topo_exp = NULL;
    SL_TOPOLOGY_END_DEVICE_NODE_T *end_node = NULL;
    gint32 i = 0;
    gint32 j = 0;

    if (topo_data == NULL || topo_info == NULL || is_pd_found == NULL || exp_level == NULL) {
        return FALSE;
    }

    /* 判断已访问的Expander数量是否已达到了最大值 */
    if (topo_info->exp_count >= SML_MAX_EXPANDER_PER_CONTROLLER) {
        debug_log(DLOG_DEBUG, "%s : Expander count exceed the limit of %d", __FUNCTION__,
            SML_MAX_EXPANDER_PER_CONTROLLER);
        return FALSE;
    }

    /* 如果该Expander已经遍历过，则直接返回该Expander的diag_mark */
    if (TRUE == CheckExpIsVisited(topo_data, device_offset, topo_info, &exp_index)) {
        return topo_info->exp_info[exp_index].diag_mark;
    }

    /* 如果该Expander未遍历过，则记录信息，并继续向下遍历 */
    topo_exp = (SL_TOPOLOGY_EXPANDER_NODE_T *)(topo_data + device_offset);

    j = topo_info->exp_count;
    topo_info->exp_info[j].sas_addr = topo_exp->sasAddr;
    topo_info->exp_info[j].phy_count =
        topo_exp->numPhy > SML_MAX_SAS_PHY_PER_EXPANDER ? SML_MAX_SAS_PHY_PER_EXPANDER : topo_exp->numPhy;
    topo_info->exp_count = topo_info->exp_count + 1;

    /* 遍历该Expander的每个PHY，标记为上行phy还是下行phy */
    for (i = 0; i < topo_info->exp_info[j].phy_count; i++) {
        switch (topo_exp->phyList[i].attachedDeviceType) {
            case SL_END_DEVICE:
                /* 连接设备类型为END_DEVICE */
                topo_info->exp_info[j].phy_info[i].connect_type = SML_PHY_CONNECT_END_DEVICE;
                topo_info->exp_info[j].phy_info[i].phy_type = SML_PHY_TYPE_DOWN;
                end_node = (SL_TOPOLOGY_END_DEVICE_NODE_T *)(topo_data + topo_exp->phyList[i].attachedDeviceOffset);
                /* 判断device_id是否相同，如果相同，则标记为要诊断 */
                if (*is_pd_found != TRUE && end_node->deviceId == pd_device_id) {
                    ret = TRUE;
                    *is_pd_found = TRUE;
                    topo_info->exp_info[j].phy_info[i].diag_mark = TRUE;

                    /* 设置于硬盘直连的Expander层级为1 */
                    topo_info->exp_info[j].exp_level = 1;
                }

                /* 判断phy是否为虚拟phy */
                if (CheckPHYIsVirtual(end_node) == TRUE) {
                    topo_info->exp_info[j].virtual_phy_count++;
                }
                break;

            case SL_EDGE_EXPANDER:
            case SL_FANOUT_EXPANDER:
                /* 连接的设备类型为Expander，则先判断是否已经遍历过 */
                topo_info->exp_info[j].phy_info[i].connect_type = SML_PHY_CONNECT_ENCL_DEVICE;
                if (TRUE ==
                    CheckExpIsVisited(topo_data, topo_exp->phyList[i].attachedDeviceOffset, topo_info, &exp_index)) {
                    /* 如果下层Expander已经遍历过，则相连的phy为上行phy */
                    topo_info->exp_info[j].phy_info[i].phy_type = SML_PHY_TYPE_UP;
                } else {
                    /* 如果下层Expander未遍历过，则相连的phy为下行phy */
                    topo_info->exp_info[j].phy_info[i].phy_type = SML_PHY_TYPE_DOWN;
                }
                break;

            case SL_IOC:
                /* 连接RAID设备，为上行phy */
                topo_info->exp_info[j].phy_info[i].connect_type = SML_PHY_CONNECT_RAID_DEVICE;
                topo_info->exp_info[j].phy_info[i].phy_type = SML_PHY_TYPE_UP;
                break;

            case SL_NO_DEVICE:
                /* 未连接设备，为下行phy */
                topo_info->exp_info[j].phy_info[i].connect_type = SML_PHY_CONNECT_NO_DEVICE;
                topo_info->exp_info[j].phy_info[i].phy_type = SML_PHY_TYPE_DOWN;
                break;

            default:
                /* 未连接设备或为其他设备，为下行phy */
                topo_info->exp_info[j].phy_info[i].connect_type = SML_PHY_CONNECT_OTHER_DEVICE;
                topo_info->exp_info[j].phy_info[i].phy_type = SML_PHY_TYPE_DOWN;
                break;
        }
    }

    /* 设置phy_count为实际的PHY数目（减去虚拟PHY） */
    topo_info->exp_info[j].phy_count = topo_info->exp_info[j].phy_count - topo_info->exp_info[j].virtual_phy_count;

    /* 对所有的下行phy的Expander设备进行遍历，得到结果 */
    for (i = 0; i < topo_info->exp_info[j].phy_count; i++) {
        if ((topo_exp->phyList[i].attachedDeviceType == SL_EDGE_EXPANDER ||
            topo_exp->phyList[i].attachedDeviceType == SL_FANOUT_EXPANDER) &&
            topo_info->exp_info[j].phy_info[i].phy_type == SML_PHY_TYPE_DOWN) {
            ret2 = VisitExpDFS(topo_data, topo_exp->phyList[i].attachedDeviceOffset, topo_info, pd_device_id,
                is_pd_found, &exp_level_temp);
        }
        if (ret2 == TRUE) {
            topo_info->exp_info[j].phy_info[i].diag_mark = TRUE;
            ret = TRUE;
        }
        /* 如果从下层获取到的exp_level不为0，说明在下层exp中找到了需要诊断的目标硬盘，返回的exp_level有效 */
        if (exp_level_temp != 0) {
            topo_info->exp_info[j].exp_level = exp_level_temp;
        }
    }

    /* 如果Expander的任意一个下行PHY被标记诊断，则标记该Expander的诊断标记为TRUE */
    if (ret == TRUE) {
        topo_info->exp_info[j].diag_mark = TRUE;
    }

    /* 遍历上行phy，将上行phy的诊断标记设置为该Expander的诊断标记 */
    for (i = 0; i < topo_info->exp_info[j].phy_count; i++) {
        if (topo_info->exp_info[j].phy_info[i].phy_type == SML_PHY_TYPE_UP) {
            topo_info->exp_info[j].phy_info[i].diag_mark = topo_info->exp_info[j].diag_mark;
        }
    }
    /* 如果当前Expander的层级有效，则设置上一层Expander的层级为本层级加1 */
    if (topo_info->exp_info[j].exp_level != 0) {
        *exp_level = topo_info->exp_info[j].exp_level + 1;
    }
    return ret;
}
