-- Copyright (c) 2024 Huawei Technologies Co., Ltd.
-- 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.


-- Description: FPGA升级信号监听及回调处理
local skynet = require 'skynet'
local log = require 'mc.logging'
local fw_mgmt_client = require 'general_hardware.client'
local context = require 'mc.context'
local fructl = require 'mcu.upgrade.fructl_handler'
local cmn = require 'common'
local defs = require 'unit_manager.class.logic_fw.comm_defs'
local fpga_upgrade = require 'unit_manager.class.logic_fw.fpga.fpga_upgrade'
local fpga_package = require 'unit_manager.class.logic_fw.fpga.fpga_package'
local fpga_process = require 'unit_manager.class.logic_fw.fpga.fpga_process'
local fpga_self_test = require 'unit_manager.class.logic_fw.fpga.fpga_self_test'
local factory = require 'factory'
local upgrade_subject = require 'upgrade_subject.upgrade_subject'
local process = require 'unit_manager.class.logic_fw.upgrade.process'

local fpga_active_register_list<const> ={
    {Key = 'FirmwareId', Value = 'FPGA'},
    {Key = 'FirmwareType', Value = 'FPGA'},
    {Key = 'ActiveCondition', Value = 'ChassisPowerOff'},
    {Key = 'ActiveMode', Value = 'None'},
    {Key = 'ActiveStatus', Value = 'Idle'}
}

local ret_ok <const> = 0
local ret_err <const> = -1
local fpga_signal = {}

function fpga_signal.init(bus, db, fw)
    fpga_signal.bus = bus
    fpga_signal.db = db
    fpga_signal.fw_list = fw
    fpga_signal.upg_cfg_list = {}
    fpga_signal.is_need_register_active = false
    fpga_signal.fpga_upgrade_type = 'JTAG'
    fpga_package.new(db)
    fpga_process.new(db)
    fpga_self_test.new(db, fw)

    -- 注册固件升级监测
    upgrade_subject.get_instance():register_upgrade_observer(fpga_signal, 'FPGA')
    -- 注册固件生效监测
    upgrade_subject.get_instance():register_active_observer(fpga_signal, 'FPGA')
    -- 切换fpga选通到默认方式
    local ok = pcall(function()
        process:initial_set_mux(fpga_signal)
    end)
    if not ok then
        log:error('set fpga initial mux failed')
    end

    return fpga_signal
end

local function check_chassis_status_power_on()
    local fru_state = fructl.get_chassis_power_status(fpga_signal.bus, 1)
    if fru_state == 'ON' or fru_state == 'ONING' or fru_state == 'OFFING' then
        return true
    end
    return false
end

function fpga_signal.init_fpga_proc(device_name)

    -- 启动fpga周期自检
    fpga_self_test.get_instance():task_update_fpga_status(device_name)
end

local function update_fpga_version()
    log:notice('[FPGA]start update fpga version')
    local manager = factory.get_obj("unit_manager")
    for _, fw in pairs(manager.logic_fw) do
        if not string.find(fw.csr.Name, 'FPGA') then
            goto next
        end
        for _, v in pairs(manager.exp_boards) do
            if v.position ~= fw.position then
                goto continue
            end
            log:notice('[FPGA]Update %s version, position = %s', fw.csr.Name, fw.position)
            skynet.fork_once(function()
                v:task_update_logic_version()
                fw:check_update_ver()
            end)
            break
            ::continue::
        end
        ::next::
    end
end

function fpga_signal.upgrade_prepare_callback(ctx, system_id, firmware_type, cfg_path, hpm_path, parameters)
    log:notice('[fpga] prepare upgrade, system[%s] firmware_type:%s', system_id, firmware_type)
    local ok, rsp = fw_mgmt_client:PFileFileChown(context.new(), nil, cfg_path, 104, 104)  --设置配置文件属主为secbox
    if not ok then
        log:error('[bios] chown cfg file failed, error %s', rsp)
        fw_mgmt_client:UpdateServiceUpdateServicePrepareReply(context.new(), system_id, firmware_type, '', ret_err, parameters)
        return
    end

    ok, rsp = fw_mgmt_client:PFileFileChown(context.new(), nil, hpm_path, 104, 104)  --设置升级文件属主为secbox
    if not ok then
        log:error('[bios] chown hpm file failed, error %s', rsp)
        fw_mgmt_client:UpdateServiceUpdateServicePrepareReply(context.new(), system_id, firmware_type, '', ret_err, parameters)
        return
    end

    fw_mgmt_client:UpdateServiceUpdateServiceUpdateUpgradeStatus(context.new(),
        system_id, firmware_type, ret_ok, 20, 'Running', parameters)
    skynet.fork(function()
        ok, rsp = pcall(fpga_upgrade.prepare_upgrade, fpga_signal, system_id, firmware_type, cfg_path, hpm_path, parameters)
        if not ok then
            log:error('upgrade prepare stage failed, err: %s', rsp)
            fw_mgmt_client:UpdateServiceUpdateServicePrepareReply(context.new(), system_id, firmware_type, '', ret_err, parameters)
        end
    end)
end

function fpga_signal:get_fpga_type(file_path)
    local release_path, fw_name = process:extract_upgrade_file(file_path)
    local fpga_fw_list = process:find_fpga_fw_with_uid(fpga_signal, fw_name)
    if #fpga_fw_list == 0 then
        log:error('get fpga type failed')
        return
    end
    fpga_signal.fpga_upgrade_type = fpga_fw_list[1].csr.UpgradeType
end

function fpga_signal.upgrade_process_callback(ctx, system_id, firmware_type, file_path, parameters)
    log:notice("[FPGA]Start the system[%s] %s upgrade process phase", system_id, firmware_type)
    local ok, rsp = fw_mgmt_client:PFileFileChown(context.new(), nil, file_path, 104, 104)
    if not ok then
        log:error('[bios] chown file failed, error %s', rsp)
        fw_mgmt_client:UpdateServiceUpdateServiceProcessReply(context.new(), system_id, firmware_type, ret_err, parameters)
        return
    end

    fpga_signal:get_fpga_type(file_path)
    -- fpga升级在os上电状态、且在jtag升级方式下，直接返回升级成功
    if check_chassis_status_power_on() and fpga_signal.fpga_upgrade_type == defs.UPGRADE_FPGA_BY_JTAG then
        log:notice('[FPGA]The power status is not off, not start upgrade process')
        fw_mgmt_client:UpdateServiceUpdateServiceProcessReply(context.new(), system_id, firmware_type, ret_ok, parameters)
        fpga_signal.is_need_register_active = true
        return
    end

    fw_mgmt_client:UpdateServiceUpdateServiceUpdateUpgradeStatus(context.new(),
        system_id, firmware_type, ret_ok, 40, 'Running', parameters)

    skynet.fork(function()
        ok, rsp = pcall(fpga_upgrade.process_upgrade, fpga_signal, system_id, firmware_type, file_path, parameters)
        if not ok then
            log:error('upgrade process stage failed, err: %s', rsp)
            fw_mgmt_client:UpdateServiceUpdateServiceProcessReply(context.new(), system_id, firmware_type, ret_err, parameters)
        end
    end)
end

local function fpga_register_active_action()
    local ok, err = pcall(function()
        fw_mgmt_client:FirmwareActiveFirmwareActiveRegisterActiveAction(context.new(), fpga_active_register_list)
    end)
    if not ok then
        log:error('[FPGA]register active action fail, err = %s', err)
    else
        log:notice('[FPGA]register active action successfully')
    end
    fpga_signal.is_need_register_active = false
end

function fpga_signal.upgrade_finish_callback(ctx, system_id, firmware_type, parameters)
    log:notice("[FPGA]Start the system[%d] %s upgrade finish phase", system_id, firmware_type)
    -- fpga升级完成后更新fpga版本号
    update_fpga_version()
    skynet.fork(fpga_upgrade.finish_upgrade, system_id, firmware_type, parameters)

    -- FPGA注册生效策略
    if fpga_signal.is_need_register_active then
        fpga_register_active_action()
    end
end

local function update_active_status(firmware_id, firmware_type, status)
    log:notice('[FPGA]update active status ')
    local param = {}
    param[#param+1] = {Key = 'FirmwareId', Value = firmware_id}
    param[#param+1] = {Key = 'FirmwareType', Value = firmware_type}
    param[#param+1] = {Key = 'ActiveStatus', Value = status}
    fw_mgmt_client:FirmwareActiveFirmwareActiveUpdateActiveStatus(context.new(), param)
end

function fpga_signal.active_callback(ctx, system_id, firmware_type)
    log:notice('get active signal, system_id: %s, firmware_type: %s', system_id, firmware_type)
    local ret = ret_ok
    log:notice('[FPGA]----------Start Active Fpga----------')
    update_active_status(firmware_type, firmware_type, 'Apply')
    skynet.fork(function ()
        local ok, value = pcall(function ()
            return fpga_upgrade.active_process(fpga_signal)
        end)
        if not ok or value == defs.RET.ERR then
            log:error("active fpga failed, error:%s", value)
            ret = defs.RET.ERR
        end
        log:notice('[FPGA]----------End Active Fpga----------')
        -- 等待10s，防止相应过快firmware_mgmt没有收到回应
        cmn.skynet.sleep(1000)
        -- fpga生效完成后更新fpga版本号
        update_fpga_version()
        fw_mgmt_client:FirmwareActiveFirmwareActiveActiveProcessReply(context.new(), system_id, firmware_type, ret)
    end
    )
end

function fpga_signal:on_upgrade_prepare(ctx, system_id, firmware_type, cfg_path, hpm_path, parameters)
    fpga_signal.upgrade_prepare_callback(ctx, system_id, firmware_type, cfg_path, hpm_path, parameters)
end

function fpga_signal:on_upgrade_process(ctx, system_id, firmware_type, file_path, parameters)
    fpga_signal.upgrade_process_callback(ctx, system_id, firmware_type, file_path, parameters)
end

function fpga_signal:on_upgrade_finish(ctx, system_id, firmware_type, parameters)
    fpga_signal.upgrade_finish_callback(ctx, system_id, firmware_type, parameters)
end

function fpga_signal:on_active_process(ctx, system_id, firmware_type)
    fpga_signal.active_callback(ctx, system_id, firmware_type)
end

return fpga_signal