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

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

#define PMC_STORAGECORE_LIB "/usr/lib64/libsyscore.so"

typedef guint32 (*ProcessCoreCommandFunc)(SC_LIB_CMD_PARAM_T *plcp);
typedef void (*RegisterMCTPFunc)(MCTP_WRITEREAD_FUNC mctp_writeread_func);
typedef void (*DebugCallbackFunction)(guint8 level, guint32 mask, const gchar *mesg);
typedef DebugCallbackFunction (*SetDebugCallback)(DebugCallbackFunction callback);

typedef struct _ERROR_CODE_MAP {
    guint32 sc_error_code;
    guint32 sml_error_code;
} ERROR_CODE_MAP;

/* ----------------------------------------------*
 * 本地全局变量                                   *
 * ---------------------------------------------- */

LOCAL void *g_lib_handle = NULL;
LOCAL ProcessCoreCommandFunc g_pfn_process_command = NULL;
LOCAL pthread_mutex_t g_cmd_mutex = PTHREAD_MUTEX_INITIALIZER;
LOCAL gboolean g_cmd_doing = FALSE;
LOCAL gboolean g_ctrl_busy = FALSE;
LOCAL gboolean g_data_invalid = FALSE;
LOCAL ERROR_CODE_MAP g_error_code_map[] = {
    {SC_ERR_NULL_DATA,                       SML_ERR_NULL_DATA},
    {SC_ERR_INVALID_CMD,                     SML_ERR_INVALID_CMD},
    {SC_ERR_CANNOT_ALLOC_MEM,                SML_ERR_CANNOT_ALLOC_MEM},
    {SC_ERR_DATA_INVALID,                    SML_ERR_DATA_INVALID},
    {SC_ERR_CDA_NOT_INIT,                    SML_ERR_CTRL_INIT_NOT_COMPLETED},
    {SC_ERR_INVALID_CTRL_INDEX,              SML_ERR_CTRL_INDEX_INVALID},
    {SC_ERR_CTRL_STATUS_NOT_OK,              SML_ERR_CTRL_STATUS_INVALID},
    {SC_ERR_CTRL_OPER_NOT_SUPPORTED,         SML_ERR_CTRL_OPERATION_NOT_SUPPORT},
    {SC_ERR_CTRL_BBU_STATUS_ABNORMAL,        SML_ERR_CTRL_BBU_STATUS_ABNORMAL},
    {SC_ERR_CTRL_NO_EDITABLE_LD,             SML_ERR_CTRL_NO_EDITABLE_LD},
    {SC_ERR_CTRL_RCP_NOT_IN_RANGE,           SML_ERR_CTRL_RCP_NOT_IN_RANGE},
    {SC_ERR_LD_INVALID,                      SML_ERR_LD_INVALID_TARGET_ID},
    {SC_ERR_LD_OPERATION_NOT_SUPPORT,        SML_ERR_LD_OPERATION_NOT_SUPPORT},
    {SC_ERR_LD_INIT_IN_PROGRESS,             SML_ERR_LD_INIT_IN_PROGRESS},
    {SC_ERR_LD_PROPERTY_SET_NOT_ALLOWED,     SML_ERR_LD_PROPERTY_SET_NOT_ALLOWED},
    {SC_ERR_LD_SIZE_SHRINK_NOT_ALLOWED,      SML_ERR_LD_SIZE_SHRINK_NOT_ALLOWED},
    {SC_ERR_LD_NOT_SSD,                      SML_ERR_CONFIG_INVALID_PD_NON_SDD_FOR_CACHECADE},
    {SC_ERR_LD_STATE_UNSUPPORTED_TO_SET,     SML_ERR_LD_STATE_UNSUPPORTED_TO_SET},
    {SC_ERR_CONFIG_PRESENT,                  SML_ERR_CONFIG_PRESENT_ERROR},
    {SC_ERR_CONFIG_OPERATION_NOT_SUPPORTED,  SML_ERR_CONFIG_OPERATION_NOT_SUPPORT},
    {SC_ERR_MAX_LD_NUM_REACHED,              SML_ERR_CONFIG_TARGET_LD_ID_EXHAUSTED},
    {SC_ERR_DATA_DRIVE_INVALID,              SML_ERR_CONFIG_INVALID_PD_OTHER_ERROR},
    {SC_ERR_DUPLICATE_DRIVES_IN_LIST,        SML_ERR_CONFIG_INVALID_PARAM_REPEATED_PD_ID},
    {SC_ERR_DRIVE_NOT_UNASSIGNED,            SML_ERR_CONFIG_INVALID_PD_IN_USE},
    {SC_ERR_DRIVE_TOO_SMALL,                 SML_ERR_CONFIG_INVALID_PARAM_CAPACITY_TOO_SMALL},
    {SC_ERR_DRIVE_TYPE_NOT_MATCH,            SML_ERR_CONFIG_INVALID_PD_SDD_HDD_MIXED},
    {SC_ERR_DRIVE_BLOCK_SIZE_INCOMPATIBLE,   SML_ERR_CONFIG_BLOCK_SIZE_NOT_SAME},
    {SC_ERR_NO_AVAILABLE_PHYSICAL_SPACE,     SML_ERR_CONFIG_ARRAY_NO_AVAILABLE_SPACE},
    {SC_ERR_INVALID_RAID_LEVEL,              SML_ERR_CONFIG_INVALID_PARAM_RAID_LEVEL},
    {SC_ERR_CAPACITY_TOO_SMALL,              SML_ERR_CONFIG_INVALID_PARAM_CAPACITY_TOO_SMALL},
    {SC_ERR_CAPACITY_TOO_LARGE,              SML_ERR_CONFIG_INVALID_PARAM_CAPACITY_TOO_LARGE},
    {SC_ERR_ARRAY_INVALID,                   SML_ERR_ARRAY_INVALID_ARRAY_REF},
    {SC_ERR_ASSOCIATED_LD_SIZE_OUT_OF_RANGE, SML_ERR_CONFIG_ASSOCIATED_LD_SIZE_OUT_OF_RANGE},
    {SC_ERR_PD_INVALID,                      SML_ERR_PD_INVALID_DEVICE_ID},
    {SC_ERR_PD_OPERATION_NOT_SUPPORT,        SML_ERR_PD_OPERATION_NOT_SUPPORT},
    {SC_ERR_PD_MAKESPARE_NOT_ALLOWED,        SML_ERR_PD_MAKESPARE_NOT_ALLOWED},
    {SC_ERR_PD_SPARE_FOR_RAID0_LD,           SML_ERR_PD_SPARE_FOR_RAID0_LD},
    {SC_ERR_PD_STATE_UNSUPPORTED_TO_SET,     SML_ERR_PD_STATE_UNSUPPORTED_TO_SET},
    {SC_ERR_PD_SPARE_SDD_HDD_MIXED,          SML_ERR_PD_SPARE_SDD_HDD_MIXED},
};

LOCAL gboolean is_ctrl_busy(const gchar *mesg)
{
    // 与厂商FAE确认，x0b表示控制器处于繁忙状态，暂时无法处理OOB命令
    if (strstr(mesg, "Received MCTP error response") != NULL && strstr(mesg, "cmd:x0b") != NULL) {
        return TRUE;
    }
 
    return FALSE;
}
 
LOCAL gboolean is_data_invalid(const gchar *mesg)
{
    // 获取到的connector个数<=0，这种情况不可能发生，说明数据不可信
    if (strstr(mesg, "SA_GetControllerModeInfo") != NULL && strstr(mesg, "Assertion") != NULL &&
        strstr(mesg, "num_connectors > 0") != NULL) {
        return TRUE;
    }
 
    if (strstr(mesg, "SA_GetControllerConnectorInfo") != NULL && strstr(mesg, "Assertion") != NULL &&
        strstr(mesg, "No controller ports found") != NULL) {
        return TRUE;
    }
 
    return FALSE;
}
 
/*
 * Description: storageCore调试日志打印回调函数
 */
LOCAL void debug_log_callback(guint8 level, guint32 mask, const gchar *mesg)
{
    // storageCore官方打印的掩码都为0，过滤掉。BMC新增打印的掩码不为0，可以正常记录
    if (mask == 0) {
        if (!g_cmd_doing) {
            return;
        }
 
        if (is_ctrl_busy(mesg)) {
            g_ctrl_busy = TRUE;  // 标记当前正在执行命令的对应控制器处于繁忙状态
        }
        if (is_data_invalid(mesg)) {
            g_data_invalid = TRUE;
        }
        return;
    }

    if (level == 1) {     //  1对应ERROR级别
        debug_log(DLOG_ERROR, "%s", mesg);
    } else if (level == 2) {   //  2对应INFO级别
        debug_log(DLOG_INFO, "%s", mesg);
    } else {
        debug_log(DLOG_DEBUG, "%s", mesg);
    }

    return;
}

/*
 * Description: 初始化storageCore
 */
LOCAL guint32 init_lib(void)
{
    SC_LIB_CMD_PARAM_T lcp;

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

    lcp.cmdType = SC_SYSTEM_CMD_TYPE;
    lcp.cmd = SC_INIT_LIB;

    return ProcessSCCommandCall(&lcp);
}

/*
 * Description: 动态加载storageCore、完成回调函数注册、完成初始化
 */
gint32 load_storagecore(MCTP_WRITEREAD_FUNC mctp_writeread_func)
{
    if (g_lib_handle != NULL) {
        return SML_SUCCESS;
    }

    g_lib_handle = dlopen(PMC_STORAGECORE_LIB, RTLD_LAZY);
    if (g_lib_handle == NULL) {
        debug_log(DLOG_ERROR, "%s: dlopen %s failed, %s.", __FUNCTION__, PMC_STORAGECORE_LIB, dlerror());
        return SML_ERR_CTRL_PMC_SC_LOAD_FAILED;
    }

    /* 1、注册打印调试日志的回调函数 */
    SetDebugCallback pfuncSetDebugCallback = dlsym(g_lib_handle, "SC_SetDebugCallback");
    if (pfuncSetDebugCallback == NULL) {
        debug_log(DLOG_ERROR, "%s: no SC_SetDebugCallback.", __FUNCTION__);
        dlclose(g_lib_handle);
        g_lib_handle = NULL;
        return SML_ERR_CTRL_PMC_SC_GET_FUNC_ADDR_FAILED;
    }

    pfuncSetDebugCallback(debug_log_callback);

    /* 2、注册收发MCTP报文的回调函数 */
    RegisterMCTPFunc pfn_register_mctp_func = dlsym(g_lib_handle, "RegisterMCTPFunc");
    if (pfn_register_mctp_func == NULL) {
        debug_log(DLOG_ERROR, "%s: no RegisterMCTPFunc.", __FUNCTION__);
        dlclose(g_lib_handle);
        g_lib_handle = NULL;
        return SML_ERR_CTRL_PMC_SC_GET_FUNC_ADDR_FAILED;
    }

    pfn_register_mctp_func(mctp_writeread_func);

    /* 3、找到libsyscore.so处理命令的函数地址并赋给全局指针 */
    ProcessCoreCommandFunc pfnProcessCoreCommand = (ProcessCoreCommandFunc)dlsym(g_lib_handle, "ProcessLibCommand");
    if (pfnProcessCoreCommand == NULL) {
        debug_log(DLOG_ERROR, "%s: no ProcessLibCommand.", __FUNCTION__);
        dlclose(g_lib_handle);
        g_lib_handle = NULL;
        return SML_ERR_CTRL_PMC_SC_GET_FUNC_ADDR_FAILED;
    }

    g_pfn_process_command = pfnProcessCoreCommand;

    /* 4、初始化storageCore */
    if (init_lib() != 0) {
        debug_log(DLOG_ERROR, "%s: init storageCore failed.", __FUNCTION__);
        dlclose(g_lib_handle);
        g_lib_handle = NULL;
        return SML_ERR_CTRL_PMC_SC_LOAD_FAILED;
    }

    return SML_SUCCESS;
}

/*
 * Description: 去初始化storageCore
 */
LOCAL guint32 uninit_lib(void)
{
    SC_LIB_CMD_PARAM_T lcp;

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

    lcp.cmdType = SC_SYSTEM_CMD_TYPE;
    lcp.cmd = SC_UNINIT_LIB;

    return ProcessSCCommandCall(&lcp);
}

/*
 * Description: 去初始化storageCore、动态卸载storageCore
 */
gint32 unload_storagecore(void)
{
    if (g_lib_handle == NULL) {
        return SML_SUCCESS;
    }

    if (uninit_lib() != SML_SUCCESS) {
        return SML_ERR_CTRL_PMC_SC_UNLOAD_FAILED;
    }

    dlclose(g_lib_handle);
    g_lib_handle = NULL;
    g_pfn_process_command = NULL;

    return SML_SUCCESS;
}

/*
 * Description: 转成sml层的错误码
 */
LOCAL gint32 convert_error_code(guint32 ret)
{
    for (guint16 i = 0; i < G_N_ELEMENTS(g_error_code_map); i++) {
        if (ret == g_error_code_map[i].sc_error_code) {
            return (gint32)g_error_code_map[i].sml_error_code;
        }
    }

    return (gint32)ret;
}

/*
 * Description: storageCore处理函数入口
 */
gint32 ProcessSCCommandCall(SC_LIB_CMD_PARAM_T *plcp)
{
    if (g_pfn_process_command == NULL) {
        return SML_ERR_CTRL_PMC_SC_GET_FUNC_ADDR_FAILED;
    }

    pthread_mutex_lock(&g_cmd_mutex);
    g_cmd_doing = TRUE;
    guint32 ret = g_pfn_process_command(plcp);
    g_cmd_doing = FALSE;
 
    gint32 retval = SML_SUCCESS;
    if (ret == RET_OK) {
        // 当返回值是0但实际上控制器处于繁忙状态时，收到的报文是不符合预期的，返回错误码
        if (g_ctrl_busy && plcp->cmdType == SC_PD_CMD_TYPE &&
            plcp->cmd == SC_START_LOCATE_PD) {
            retval = SML_ERR_CTRL_TOO_BUSY_TO_RESP_OOB;
        } else if (g_data_invalid) {
            retval = SML_ERR_DATA_INVALID;
        }
    } else {
        retval = convert_error_code(ret);
    }
    // 退出前恢复标志位
    g_ctrl_busy = FALSE;
    g_data_invalid = FALSE;
    pthread_mutex_unlock(&g_cmd_mutex);
    return retval;
}

