-- 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: M88RT51632 CS81532 retimer升级
local skynet = require 'skynet'
local utils = require 'mc.utils'
local utils_core = require 'utils.core'
local log = require 'mc.logging'
local cmn = require 'common'
local class = require 'mc.class'
local RETIMER_CONSTANTS = require 'retimer.retimer_constants'
local file_sec = require 'utils.file'
local fructl = require 'mcu.upgrade.fructl_handler'

local LOCK_FOREVER <const> = 0xffff

local M = class()

local ROOT_USER_GID<const> = 0 -- root用户组id 0
local OPERATOR_GID<const> = 200 -- 操作者用户组id 200

local RETRY_UPGRADE_TIMES<const> = 3
local SWITCH_TO_RETIMER<const> = 4
local SWITCH_TO_BMC<const> = 3

function M:ctor()
end

function M:init()
end

function M.upgrade_prepare_func(upgrade_service, dir, component_idex)
    local file_path = dir .. RETIMER_CONSTANTS.UPGRADE_FILE_NAME[component_idex]
    -- 文件权限修改
    utils_core.chown_s(file_path, ROOT_USER_GID, OPERATOR_GID)
    utils_core.chmod_s(file_path, utils.S_IRUSR | utils.S_IRGRP)
    -- retimer芯片不涉及多system id的HPC场景
    local retimer = upgrade_service.retimer_upgrade_list[1]
    -- 确保下电状态，并添加上电锁
    local power_status = retimer.agent:get_power_status(1)
    if power_status ~= "OFF" then
        log:error("upgrade retimer %s failed, power status is %s", retimer.name, power_status)
        return false
    end
    log:notice("%s power status ok,power status is %s", retimer.name, power_status)

    -- 下电后添加固件升级上电锁，防止升级过程中意外上电造成系统挂死
    fructl.set_poweron_lock_until_success(retimer.bus, 1, true, LOCK_FOREVER, 'RETIMER_UPGRADE')
    return true
end

local function set_retimer_fw_channel(retimer, channel_id)
    local ok, err
    -- retimer芯片固件存储在独立的eeprom中
    -- 升级时需要切换eeprom链路通道到bmc，用于bmc写入升级文件
    -- 带内上电时需要切换eeprom链路通道到Retimer芯片，用于加载固件配置
    for _ = 1, RETRY_UPGRADE_TIMES do
        ok, err = pcall(function()
            -- 通过资源树私有属性Switch关联Accessor，写入SMC命令字实现通道切换
            retimer.retimer_obj.Switch = channel_id
        end)
        if ok then
            return ok
        end
    end
    log:error("%s switch to upgrade channel %s failed, err: %s",retimer.name, channel_id, err)
    return ok
end

-- retimer升级需要切换升级通道到BMC
local function send_retimer_request_upg(retimer, dir, component_idex)
    -- 切换升级通道到BMC
    local ok = set_retimer_fw_channel(retimer, SWITCH_TO_BMC)
    if not ok then
        log:error("%s set upgrade channel failed", retimer.name)
        return false
    end
    log:notice("%s set upgrade channel succeed", retimer.name)
    return true
end

-- 将固件以1024字节为单位写入芯片的固件eeprom中
local function trans_fw_to_chip(retimer, dir, component_idex)
    local file_path = dir .. RETIMER_CONSTANTS.UPGRADE_FILE_NAME[component_idex]
    local file = file_sec.open_s(file_path, "rb")
    if not file then
        return false
    end
    local data = utils.close(file, pcall(file.read, file, 'a'))
    log:info("upgrade bin data len:%s", #data)
    return retimer:trans_fw_to_ref_eeprom(data)
end

local function upgrade_single_retimer(retimer, dir, component_idex)
    local trans_steps = {
        send_retimer_request_upg,
        trans_fw_to_chip
    }
    local ok
    for _ = 1, RETRY_UPGRADE_TIMES do
        for i, stepfunc in ipairs(trans_steps) do 
            ok = stepfunc(retimer, dir, component_idex)
            if not ok then 
                log:error("Retimer [%s] upgrade step(%s) failed.", retimer.name, i)
                goto continue
            end
        end
        break
        ::continue::
    end
    return ok
end

function M.upgrade_single_retimer_func(retimer, dir, component_idex)
    for _ = 1, RETRY_UPGRADE_TIMES do 
        local upgrade_ret = upgrade_single_retimer(retimer, dir, component_idex)
        if upgrade_ret then
            return true
        end
        skynet.sleep(100)
    end
    return false
end

-- 单个retimer升级结束函数，无论升级失败成功都会执行，需要将Eeprom通道切换到Retimer芯片
function M.upgrade_single_retimer_finish_func(upgrade_service, retimer)
    set_retimer_fw_channel(retimer, SWITCH_TO_RETIMER)
    return true
end

-- 固件升级结束函数，解除上电锁
function M.upgrade_finish_func(upgrade_service)
    -- retimer芯片不涉及多system id的HPC场景
    fructl.set_poweron_lock_until_success(upgrade_service.retimer_upgrade_list[1].bus,
        1, false, LOCK_FOREVER, 'RETIMER_UPGRADE')
    return true
end

return M
