-- 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: 5902L 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 RETIMER_CONSTANTS = require 'retimer.retimer_constants'
local file_sec = require 'utils.file'

local M = {}

M.chip_type = RETIMER_CONSTANTS.CHIP_TYPE.TYPE_5902L

local UPGRADE_FILE_NAME<const> = 'retimer.bin'

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

local RETRY_UPGRADE_TIMES<const> = 10
local NOTIFY_DELAY_TIME<const> = 120

local RETIMER_5902_UPG_ERR_STATUS = {
    IDLE = 0x0,
    OK = 0x1,
    NOT_PER = 0x2,
    INV_PAR = 0x3,
    PKT_VERSION_FAIL = 0x4,
    FW_VERSION_FAIL = 0x5,
    INVALID_HDR = 0x6,
    FLASH_EARASE_FAIL = 0x7,
    FLASH_WRITE_FAIL = 0x8,
    UPG_MACHINE_FAIL = 0x9,
    WRITE_PKG_HDR_FAIL = 0xa,
    FW_CRC_CHK = 0xb,
    INVALID_TOT_LEN = 0xc,
    UKOWN = 0xf
}

local RETIMER_5902_UPG_STATUS = {
    IDLE = 0x0,
    ON_GOING = 0x1,
    DONE = 0x2,
    VALID = 0x3
}

function M.upgrade_prepare_func(upgrade_service, dir, firmware_chip_type)
    local file_path = dir .. UPGRADE_FILE_NAME
    -- 文件权限修改
    utils_core.chown_s(file_path, ROOT_USER_GID, OPERATOR_GID)
    utils_core.chmod_s(file_path, utils.S_IRUSR | utils.S_IRGRP)
    return true
end

-- 接口文档1.4.7第三步：确保为idle状态，发送升级控制命令
local function send_5902_request_upg(retimer, dir)
    local RETRY_REQ_TIMES<const> = 3
    -- 等待状态idle
    local ok = retimer:wait_5902_chip_idle()
    if not ok then
        log:error("Wait retimer idle failed, retimer=%s", retimer.name)
        return false
    end

    -- 发送升级控制命令
    ok = retimer:set_5902_upgrade_force(RETRY_REQ_TIMES)
    if not ok then
        log:error("Set upgrade failed, retimer=%s", retimer.name)
        return false
    end

    -- 确认5902已经准备好升级
    local inner_err, in_upg_state = retimer:read_5902_upg_state()
    if inner_err ~= RETIMER_5902_UPG_ERR_STATUS.OK or in_upg_state ~= RETIMER_5902_UPG_STATUS.ON_GOING then
        log:error("%s not ready to upgrade, inner_err=%s, in_upg_state=%s", retimer.name, inner_err, in_upg_state)
        return false
    end

    -- 设置升级状态
    retimer:set_upgrade_status(RETIMER_CONSTANTS.UPGRADE_STATUS.ON_GOING)

    return true
end

-- 接口文档1.4.7第六步，将固件数据分多次发送给5902
local function trans_fw_to_5902(retimer, dir)
    local file_path = dir .. UPGRADE_FILE_NAME
    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'))
    return retimer:trans_to_5902_by_single_frame(data)
end

-- 接口文档1.4.7第七步，确保为idle状态，发送获取升级状态命令
local function upgrade_5902_succeed(retimer, dir)
    local inner_err, in_upg_state = retimer:read_5902_upg_state()
    if inner_err ~= RETIMER_5902_UPG_ERR_STATUS.OK or in_upg_state ~= RETIMER_5902_UPG_STATUS.DONE then
        log:error("%s upgrade not done, inner_err=%s, in_upg_state=%s", retimer.name, inner_err, in_upg_state)
        return false
    end

    retimer:set_upgrade_status(RETIMER_CONSTANTS.UPGRADE_STATUS.DONE)
    return true
end

local function upgrade_single_retimer(retimer, dir)
    local ok = true
    local is_upgrade = RETIMER_CONSTANTS.NOTIFY_STATUS.UPGRADING
    for _ = 1, RETRY_UPGRADE_TIMES do
        is_upgrade = retimer.retimer_obj.ReqAccNotify
        if is_upgrade == RETIMER_CONSTANTS.NOTIFY_STATUS.IDLE then
            break
        end
        cmn.skynet.sleep(NOTIFY_DELAY_TIME)
    end
    if is_upgrade ~= RETIMER_CONSTANTS.NOTIFY_STATUS.IDLE then
        log:error("Retimer [%s] is not idle.", retimer.name)
        return false
    end

    for _ = 1, RETRY_UPGRADE_TIMES do
        retimer:notify_upgrading(RETIMER_CONSTANTS.NOTIFY_STATUS.UPGRADING)
        cmn.skynet.sleep(NOTIFY_DELAY_TIME)
        retimer:channel_switch(RETIMER_CONSTANTS.CHANNEL_STATUS.OPEN)

        local trans_steps = {
            send_5902_request_upg, -- 接口文档1.4.7第三步：确保为idle状态，发送升级控制命令
            trans_fw_to_5902, -- 接口文档1.4.7第六步，将固件数据分多次发送给5902
            upgrade_5902_succeed -- 接口文档1.4.7第七步，确保为idle状态，发送获取升级状态命令
        }
        for i, stepfunc in ipairs(trans_steps) do
            ok = stepfunc(retimer, dir)
            if not ok then
                log:error("Retimer [%s] upgrade step(%s) failed.", retimer.name, i)
                goto upgrade_again
            end
        end
        goto upgrade_exit
        ::upgrade_again::
    end
    ::upgrade_exit::
    retimer:channel_switch(RETIMER_CONSTANTS.CHANNEL_STATUS.CLOSE)
    retimer:notify_upgrading(RETIMER_CONSTANTS.NOTIFY_STATUS.IDLE)
    return ok
end

function M.upgrade_single_retimer_func(retimer, dir, firmware_chip_type)
    local lock_time_out = 600
    local ok, ret_code
    while lock_time_out > 0 do
        ok, ret_code = retimer:chip_lock(require 'mc.context'.new(), 600)
        if ok and ret_code == 0 then
            local upgrade_ret = upgrade_single_retimer(retimer, dir)
            -- 硬件总线解锁
            ok, ret_code = retimer:chip_unlock(require 'mc.context'.new())
            if not ok or ret_code ~= 0 then
                log:error("[retimer upgrade] retimer(name:%s) set lock status to 0 failed, ret code:%s", retimer.name,
                    ret_code)
            end
            return upgrade_ret
        end
        log:debug("[retimer upgrade] get chip lock failed")
        lock_time_out = lock_time_out - 1
        skynet.sleep(100)
    end

    log:error("[timer upgrade] upgrade failed because can not get chip lock")
    return false
end

function M.upgrade_single_retimer_finish_func(upgrade_service, retimer)
    return true
end

function M.upgrade_finish_func(upgrade_service)
    return true
end

return M
