-- 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.

local log = require 'mc.logging'
local context = require 'mc.context'
local class = require 'mc.class'
local skynet = require 'skynet'

local smc = class()

function smc:ctor(psu)
    self.ps_id = psu['SlotNumber']
    self.chip = psu['RefSMCChip']
end

local BLACKBOX_FRAME_MAX_LEN<const> = 128
local MAX_BLACK_BOX_NUMBER_SMC<const> = 8
local BLACK_BOX_MAX_LEN_SMC<const> = 1664
local BLACKBOX_FRAME_MAX_COUNT<const> = 13

local smc_cmd = {
    GET_BLACK_BOX = 0x24005500,
    SET_BLACK_BOX = 0x24005600,
    BLACK_BOX_MAX_LEN = 0x24009900
}
smc.cmd = smc_cmd

-- 向SMC写命令+数据，SMC返回完成码
function smc:chip_blkwrite(cmd, data)
    self.chip:Write(context.get_context_or_default(), cmd, data)
end

-- 向SMC发送读命令+数据长度，SMC返回数据
function smc:chip_blkread(cmd, len)
    return self.chip:Read(context.get_context_or_default(), cmd, len)
end

function smc:get_black_box_data()
    local frame_count, frame_len
    local black_box_data_table = {}
    -- 帧数量以及帧长度，2个字节
    local ok, data = pcall(function ()
        return self:chip_blkread(self.cmd.BLACK_BOX_MAX_LEN, 2)
    end)
    if not ok or not data then
        log:notice('get smc black_box_data count failed, ok %s, data %s, use default value', ok, data)
        -- 兼容不支持smc命令获取帧数量以及帧长度的情况，默认是8*128
        frame_count = 8
        frame_len = 128
    else
        -- 第1个字节代表帧数量，第2个字节代表帧长度
        frame_count = string.sub(data, 1, 1):byte()
        frame_len = string.sub(data, 2, 2):byte()
    end
    log:notice('get_black_box_data, frame_count %s, frame_len %s', frame_count, frame_len)
    if frame_count < 1 or frame_count > BLACKBOX_FRAME_MAX_COUNT or frame_len < 1 or
        frame_len > BLACKBOX_FRAME_MAX_LEN then
        return BLACK_BOX_MAX_LEN_SMC, ''
    end

    ok, data = pcall(function ()
        for i = 1, frame_count do
            log:notice('get block %x smc black_box_data', i)
            self:chip_blkwrite(self.cmd.SET_BLACK_BOX, string.pack('B', i))
            skynet.sleep(20)
            table.insert(black_box_data_table, self:chip_blkread(self.cmd.GET_BLACK_BOX, frame_len))
            skynet.sleep(20)
        end
    end)
    local black_box_data = table.concat(black_box_data_table)
    if not ok then
        log:error('[smc]ps%d get block smc black_box_data failed, msg: %s', self.ps_id, data)
    end
    log:notice('get_black_box_data_smc end')
    return BLACK_BOX_MAX_LEN_SMC, black_box_data
end

return smc