-- 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 bs = require 'mc.bitstring'
local log = require 'mc.logging'
local skynet = require 'skynet'
local context = require 'mc.context'
local client = require 'general_hardware.client'
local crc8 = require 'mc.crc8'
local smbus = require 'protocol.smbus'
local utils = require 'dpu_service.utils'
local mc_utils = require 'mc.utils'
local file_sec = require 'utils.file'

local STD_SMBUS_REQ_COMMAND_CODE <const> = 0x20 -- smbus读/写请求命令码
local STD_SMBUS_RSP_COMMAND_CODE <const> = 0x21 -- smbus读/写响应命令码
local HEADER_LEN = 12

local MCU_ERROR_LOG_MAX_LEN      <const> = 200 * 1024 -- MCU故障日志大小限制200K
local MCU_OPERATE_LOG_MAX_LEN    <const> = 100 * 1024 -- MCU操作日志大小限制100K
local MCU_MAINTAINCE_LOG_MAX_LEN <const> = 100 * 1024 -- MCU维护日志大小限制100K

local MAX_SEND_LEN               <const> = 100000 -- 单次调用HWPROXY插件发送最大长度
local request_header_bs <const> = bs.new([[<<
    lun,
    arg,
    opcode:16,
    offset:32,
    length:32,
    data/string >>]])

local response_data_bs <const> = bs.new([[<<
    error_code:16,
    opcode:16,
    total_length:32,
    length:32,
    data/string >>]])

local get_manufacturer_response_data_bs <const> = bs.new([[<<
    other:48,
    high_hex:8,
    low_hex:8
>>]])

local success_code <const> = 0
local error_code <const> = {
    [0x1] = 'opcode not support',
    [0x2] = 'parameter error',
    [0x3] = 'internal error',
    [0xF] = 'bus busy'
}

local elabel_args <const> = {
    ChassisType = 0x10,
    ChassisPartNumber = 0x11,
    ChassisSerialNumber = 0x12,
    MfgDate = 0x20,
    BoardManufacturer = 0x21,
    BoardProductName = 0x22,
    BoardSerialNumber = 0x23,
    BoardPartNumber = 0x24,
    BoardFRUFileID = 0x25,
    BoardCustomInfo = 0x26,
    Board_IssueNumber = 0x27,
    Board_CLEICode = 0x28,
    Board_BOM = 0x29,
    Board_Model = 0x2A,
    ManufacturerName = 0x30,
    ProductName = 0x31,
    ProductPartNumber = 0x32,
    ProductVersion = 0x33,
    ProductSerialNumber = 0x34,
    AssetTag = 0x35,
    ProductFRUFileID = 0x36,
    SystemManufacturerName = 0x60,
    SystemProductName = 0x61,
    SystemVersion = 0x62,
    SystemSerialNumber = 0x63
}

local cpu_archs <const> = {
    [0] = 'NA',
    [1] = 'x86_64',
    [2] = 'aarch64'
}

local std_smbus = { slave_address = 0xD4 }

std_smbus.__index = std_smbus

function std_smbus.new(chip, buffer_len)
    return setmetatable({ chip = chip, buffer_len = buffer_len, capability = { [0x00] = 1 }, process_queue = {} },
        std_smbus)
end

-- 封装crc8校验，与read_data比较
local function check_data(check_sum, data)
    local crc = crc8(data)
    if crc ~= check_sum then
        log:debug('get crc: %s, real crc: %s', check_sum, crc)
        return false
    end
    return true
end

-- chip_write_and_read 写入指定长的数据, 并读取指定长的数据
local function chip_write_read(chip, addr, write_cmd, data, read_cmd, read_len)
    -- write data
    local check_buf = table.concat({ string.char(addr), string.char(write_cmd), data })
    local crc = crc8(check_buf)
    local value = chip:ComboWriteRead(context.get_context_or_default(),
        write_cmd, data .. string.pack('B', crc), read_cmd, read_len)
    -- read_data
    check_buf = table.concat({ string.char(addr), string.char(read_cmd), string.char(addr | 0x01),
        value:sub(1, #value - 1) })
    if not value or not check_data(value:sub(#value, #value):byte(), check_buf) then
        error(table.concat({ '[smbus]read commad(0x', string.format('%02x', read_cmd), ') failed' }))
    end
    return value
end

local function chip_blkwrite_read(chip, addr, write_cmd, data, read_cmd, read_len)
    local value = chip_write_read(chip, addr, write_cmd, string.pack('B', #data) .. data, read_cmd, read_len + 2)
    return value:sub(2, #value - 1)
end

function std_smbus:send_and_receive_custom_retry(head, read_len, retries)
    local retry_count = retries
    local ok, recv_data
    for _ = 1, retry_count do
        ok, recv_data = pcall(function()
            local recv_data_tmp = response_data_bs:unpack(chip_blkwrite_read(self.chip, self.slave_address,
                STD_SMBUS_REQ_COMMAND_CODE, request_header_bs:pack(head), STD_SMBUS_RSP_COMMAND_CODE,
                read_len + HEADER_LEN))
            if recv_data_tmp.error_code ~= success_code then
                error(error_code[recv_data_tmp.error_code])
            end
            return recv_data_tmp
        end)
        if ok then
            return recv_data
        end
    end
    return false, recv_data
end

-- 读取指定长度
function std_smbus:_send_and_receive_request_in_fixed(head, read_len)
    local data = ''
    if not self.capability[head.opcode] then
        log:debug('not support opcode = 0x%x', head.opcode)
        return data
    end
    head.length = self.buffer_len - HEADER_LEN
    local recv_data
    repeat
        recv_data = self:send_and_receive(head, self.buffer_len - HEADER_LEN)
        if not recv_data then
            error('send and receive failed')
        end
        head.offset = head.offset + recv_data.length
        data = data .. recv_data.data:sub(1, recv_data.length)
        read_len = math.min(read_len, recv_data.total_length)
    until #data >= read_len
    return data:sub(1, read_len)
end

-- 协议正常读取
function std_smbus:send_and_receive(head, read_len)
    return self:send_and_receive_custom_retry(head, read_len, 3)
end

function std_smbus:_send_and_receive_request_in_frames(head, read_len)
    local data = ''
    if not self.capability[head.opcode] then
        log:debug('not support opcode = 0x%x', head.opcode)
        return data
    end
    local recv_data, msg
    repeat
        recv_data, msg = self:send_and_receive(head, read_len)
        if not recv_data then
            error('send and receive failed, msg: ' .. msg)
        end
        head.offset = head.offset + recv_data.length
        data = data .. recv_data.data:sub(1, recv_data.length)
        -- 获取剩余长度
        read_len = math.min(recv_data.total_length - head.offset, self.buffer_len)
        head.length = read_len
    until read_len <= 0
    return data
end

function std_smbus:GetCapability()
    if self.capability[0x01] == 1 then
        return
    end
    local recv_data = self:_send_and_receive_request_in_frames({
        lun = 0x80,
        arg = 0x00,
        opcode = 0x0000,
        offset = 0x00,
        length = self.buffer_len - HEADER_LEN,
        data = ''
    }, self.buffer_len - HEADER_LEN)

    local resp = bs.new([[<<identify:12,version:4,type,number:16,opcodes/string>>]]):unpack(recv_data, true)
    local tmp = { string.unpack(string.rep('I2', #resp.opcodes // 2), resp.opcodes) }
    for _, value in ipairs(tmp) do
        if self.capability[value] ~= 1 then
            log:notice('get capability opcode %s', value)
        end
        self.capability[value] = 1
    end
end

function std_smbus:GetHealth()
    return self:_send_and_receive_request_in_frames({
        lun = 0x80,
        arg = 0x00,
        opcode = 0x0001,
        offset = 0x00,
        length = 1,
        data = ''
    }, 1)
end

function std_smbus:GetPower()
    local recv_data = self:_send_and_receive_request_in_frames({
        lun = 0x80,
        arg = 0x00,
        opcode = 0x0004,
        offset = 0x00,
        length = 2,
        data = ''
    }, 2)

    return bs.new([[<<power:16>>]]):unpack(recv_data, true)
end

function std_smbus:GetMcuFwVer()
    local recv_data = self:_send_and_receive_request_in_frames({
        lun = 0x80,
        arg = 0x00,
        opcode = 0x0005,
        offset = 0x00,
        length = 3,
        data = ''
    }, 3)

    return string.format('%s.%s.%s', string.unpack('BBB', recv_data))
end

function std_smbus:GetMcuUpgradeStatus()
    local recv_data = self:_send_and_receive_request_in_frames({
        lun = 0x80,
        arg = 0x00,
        opcode = 0x1A,
        offset = 0x00,
        length = 2,
        data = ''
    }, 2)

    return bs.new([[<<status:8, percent:8>>]]):unpack(recv_data, true)
end

function std_smbus:GetVrdFwVer()
    local recv_data = self:_send_and_receive_request_in_frames({
        lun = 0x80,
        arg = 0x00,
        opcode = 0x1032,
        offset = 0x00,
        length = 4,
        data = ''
    }, 4)

    return string.format('%s', string.unpack('B', recv_data:sub(1, 1)))
end

function std_smbus:SetBiosLogLevel(data)
    return self:_send_and_receive_request_in_frames({
        lun = 0x80,
        arg = 0x00,
        opcode = 0x101E,
        offset = 0x00,
        length =  #data,
        data = data
    }, 0)
end

function std_smbus:GetBiosLogLevel(data)
    local recv_data = self:_send_and_receive_request_in_frames({
        lun = 0x80,
        arg = 0x00,
        opcode = 0x101F,
        offset = 0x00,
        length = #data,
        data = data
    }, self.buffer_len - HEADER_LEN)
    return bs.new([[<<level:8>>]]):unpack(recv_data, true)
end

local function notify_upgrade_status(system_id, firmware_type, progress, parameters)
    skynet.fork(function()
        local ok = false
        for try = 1, 5 do
            local ok, _ = client:PUpdateServiceUpdateServiceUpdateUpgradeStatus(
            context.new(), system_id, firmware_type, 0, progress,'Running', parameters)
            log:notice("notify_upgrade_status _ type : ")
            log:notice(type(_))
            if ok then
                return
            end
            skynet.sleep(100)
        end
    end)
end

function std_smbus:UpgradeMcu(fw_stream, ratio_numerator, ratio_denominator, system_id, firmware_type, retry,
    parameters)
    local max_payload_size = self.buffer_len - HEADER_LEN
    local req = {
        lun = 0,
        arg = 0,
        opcode = 0x18,
        offset = 0,
        length = 0,
        data = ''
    }
    local offset = 0
    local fw_stream_len = #fw_stream
    local write_data_batch = {}
    local ok
    -- 更新升级进度,共升级ratio_denominator个mcu，每个mcu最多重试5次
    local progress = 15 + 80 * ratio_numerator // ratio_denominator * retry // 5
    notify_upgrade_status(system_id, firmware_type, progress, parameters)
    while offset < fw_stream_len do
        req.offset = offset
        if offset + max_payload_size >= fw_stream_len then
            req.lun = 0x80
            req.length = fw_stream_len - offset
        else
            req.lun = 0
            req.length = max_payload_size
        end
        local sent_payload_len = req.length
        req.data = fw_stream:sub(req.offset + 1, req.offset + req.length)
        write_data_batch[#write_data_batch + 1] = {STD_SMBUS_REQ_COMMAND_CODE, request_header_bs:pack(req)}
        offset = offset + sent_payload_len
    end

    if next(write_data_batch) then
        log:notice('[DPU] Start to write data, fw_len:%s, list size:%s', fw_stream_len, #write_data_batch)
        ok = utils.retry_func(10, 10,function ()
            return pcall(smbus.chip_batch_write, self.chip, self.slave_address, write_data_batch)
        end)
        if not ok then
            log:error('std smbus send data to sdi mcu failed')
            return false
        end
        write_data_batch = {}
    else
        log:error('write data batch is empty')
        return false
    end
    return  true
end

-- 不是 MCU reset，是 CPU reset
function std_smbus:ResetCpu()
    return self:_send_and_receive_request_in_frames({
        lun = 0x80,
        arg = 0x00,
        opcode = 0x1018,
        offset = 0x00,
        length = 0,
        data = ''
    }, 0)
end

function std_smbus:GetMcuResetTime()
    local recv_data = self:_send_and_receive_request_in_frames({
        lun = 0x80,
        arg = 0x00,
        opcode = 0x0011,
        offset = 0x00,
        length = 2,
        data = ''
    }, 2)

    return string.unpack('H', recv_data)
end

function std_smbus:GetBootOption()
    local recv_data = self:_send_and_receive_request_in_frames({
        lun = 0x80,
        arg = 0x00, -- 使用的是V1版本
        opcode = 0x1001,
        offset = 0x00,
        length = 2,
        data = '\x01\x02' -- 获取配置固化选项和BIOS引导OS启动方式
    }, 4)

    return bs.new([[<<type1,save_option,type2,boot_option>>]]):unpack(recv_data, true)
end

function std_smbus:GetPxeOption()
    local recv_data = self:_send_and_receive_request_in_frames({
        lun = 0x80,
        arg = 0x00,
        opcode = 0x1056,
        offset = 0x00,
        length = 1,
        data = '\x04'
    }, 2)

    return bs.new([[<<type,pxe_option>>]]):unpack(recv_data, true)
end

function std_smbus:SetPxeOption(pxe_option)
    -- 4表示组合pxe功能选项开关
    local req = table.concat({
        string.char(4),
        string.char(pxe_option)
    })
    return self:_send_and_receive_request_in_frames({
        lun = 0x80,
        arg = 0x00,
        opcode = 0x1055,
        offset = 0x00,
        length = #req,
        data = req
    }, 0)
end

function std_smbus:GetUUID()
    local recv_data = self:_send_and_receive_request_in_frames({
        lun = 0x80,
        arg = 0x07, -- 获取UUID字段
        opcode = 0x105D,
        offset = 0x00,
        length = self.buffer_len - HEADER_LEN,
        data = ''
    }, self.buffer_len - HEADER_LEN)

    -- 0表示UUID未上报
    if #recv_data == 1 and string.byte(recv_data, 1, 1) == 0 then
        return 0
    end

    local uuid = ''
    for i = 1, string.len(recv_data) do
        uuid = uuid..string.format("%02X", string.byte(recv_data,i,i))
    end

    return uuid
end

function std_smbus:GetPresence()
    local recv_data = self:_send_and_receive_request_in_frames({
        lun = 0x80,
        arg = 0x00,
        opcode = 0x1020,
        offset = 0x00,
        length = self.buffer_len - HEADER_LEN,
        data = ''
    }, self.buffer_len - HEADER_LEN)

    return { string.unpack(string.rep('B', #recv_data), recv_data) }
end

function std_smbus:GetMacAddress(arg)
    local recv_data = self:_send_and_receive_request_in_frames({
        lun = 0x80,
        arg = arg,
        opcode = 0x101B,
        offset = 0x00,
        length = self.buffer_len - HEADER_LEN,
        data = ''
    }, self.buffer_len - HEADER_LEN)
    if not recv_data then
        return {}
    end

    local mac_info = {}
    for i = 1, #recv_data, 9 do
        local data = bs.new([[<<port_id,pf_id:16/big,mac_addr:6/string>>]]):unpack(recv_data:sub(i, i + 8), true)
        table.insert(mac_info,
            { data.port_id, data.pf_id,
                string.format('%02X:%02X:%02X:%02X:%02X:%02X', string.unpack(string.rep('B', 6), data.mac_addr)) })
    end
    return mac_info
end

function std_smbus:SetSysTime(data)
    return self:_send_and_receive_request_in_frames({
        lun = 0x80,
        arg = 0x00,
        opcode = 0x1017,
        offset = 0x00,
        length = #data,
        data = data
    }, 0)
end

function std_smbus:GetSdiIp(data)
    local recv_data = self:_send_and_receive_request_in_frames({
        lun = 0x80,
        arg = 0x00,
        opcode = 0x1011,
        offset = 0x00,
        length = 0x0A,
        data = data
    }, 0x0A)
    local ip_data = bs.new([[<<ipv4:32,mask:32,vlan:16>>]]):unpack(recv_data, true)
    return {
        ipv4 = ip_data.ipv4,
        mask = ip_data.mask,
        vlan = ip_data.vlan
    }
end

function std_smbus:SetSdiIpv6(data)
    return self:_send_and_receive_request_in_frames({
        lun = 0x80,
        arg = 0xF0,
        opcode = 0x1010,
        offset = 0x00,
        length = #data,
        data = data
    }, 0)
end

function std_smbus:SetSdiIpv4(data)
    return self:_send_and_receive_request_in_frames({
        lun = 0x80,
        arg = 0x00,
        opcode = 0x1010,
        offset = 0x00,
        length = #data,
        data = data
    }, 0)
end

function std_smbus:SetSolSwitch(data)
    return self:_send_and_receive_request_in_frames({
        lun = 0x80,
        arg = 0x00,
        opcode = 0x1006,
        offset = 0x00,
        length = #data,
        data = data
    }, 0x00)
end

function std_smbus:GetSolSwitch()
    local recv_data = self:_send_and_receive_request_in_frames({
        lun = 0x80,
        arg = 0x00,
        opcode = 0x1007,
        offset = 0x00,
        length = 2,
        data = '\x05'
    }, self.buffer_len - HEADER_LEN)
    local uart_data = bs.new([[<<sol_type:8,sol_value:8>>]]):unpack(recv_data, true)
    return {
        type = uart_data.sol_type,
        value = uart_data.sol_value
    }
end

function std_smbus:SetSdiSlot(data)
    return self:_send_and_receive_request_in_frames({
        lun = 0x80,
        arg = 0x00,
        opcode = 0x100E,
        offset = 0x00,
        length = #data,
        data = data
    }, 0)
end

-- 获取电子标签
function std_smbus:GetElabel(callback)
    local elabels = {}
    local elabel_args_cp = utils.table_copy(elabel_args)
    local elabel_args_cp_msg_flag = utils.table_copy(elabel_args)
    local ok, data
    while true do
        for elabel_prop, arg in pairs(elabel_args) do
            ok, data = pcall(function()
                return self:_send_and_receive_request_in_frames({
                    lun = 0x80,
                    arg = arg,
                    opcode = 0x0015,
                    offset = 0x00,
                    length = self.buffer_len - HEADER_LEN,
                    data = ''
                }, self.buffer_len - HEADER_LEN)
            end)
            if ok and data then
                elabels[elabel_prop] = data
                if elabel_prop == "MfgDate" then
                    self:GetElabelMfgDate(elabels, elabel_prop, data)
                end
                if elabel_args_cp[elabel_prop] then
                    log:notice("GetElabel %s %s", elabel_prop, data)
                end
                elabel_args_cp[elabel_prop] = nil
            elseif not ok then
                if elabel_args_cp_msg_flag[elabel_prop] then
                    log:error('GetElabel %s failed, rsp: %s', elabel_prop, data)
                    -- 防止刷屏
                    elabel_args_cp_msg_flag[elabel_prop] = nil
                end
            end
            log:debug("GetElabel %s %s", elabel_prop, data)
        end

        if type(callback) == "function" then
            callback(elabels)
        end
        if not next(elabel_args_cp) then
            return elabels
        end
        elabels = {}
        skynet.sleep(100)
    end
end

-- 转换从mcu获得的电子标签日期格式。拼接成3字节的时间戳，6个字符。
function std_smbus:GetElabelMfgDate(elabels, elabel_prop, data)
    local date_ok, date_str = pcall(function()
        return string.sub(string.gsub(utils.to_hex(data), '%s+', ''), 1, 6)
    end)
    if date_ok then
        log:notice("GetElabel MfgDate date_str: %s", date_str)
        elabels[elabel_prop] = date_str
    else
        log:error("GetElabel MfgDate failed. %s", date_str)
    end
end

function std_smbus:GetErrorLog()
    return self:_send_and_receive_request_in_fixed({
        lun = 0x80,
        arg = 0x00,
        opcode = 0x000C,
        offset = 0x00,
        length = self.buffer_len - HEADER_LEN,
        data = ''
    }, MCU_ERROR_LOG_MAX_LEN)
end

function std_smbus:GetOperateLog()
    return self:_send_and_receive_request_in_fixed({
        lun = 0x80,
        arg = 0x00,
        opcode = 0x0026,
        offset = 0x00,
        length = self.buffer_len - HEADER_LEN,
        data = ''
    }, MCU_OPERATE_LOG_MAX_LEN)
end

function std_smbus:GetMaintainceLog()
    return self:_send_and_receive_request_in_fixed({
        lun = 0x80,
        arg = 0x00,
        opcode = 0x0027,
        offset = 0x00,
        length = self.buffer_len - HEADER_LEN,
        data = ''
    }, MCU_MAINTAINCE_LOG_MAX_LEN)
end

function std_smbus:SetBootOption(data)
    return self:_send_and_receive_request_in_frames({
        lun = 0x80,
        arg = 0x00,
        opcode = 0x1000,
        offset = 0x00,
        length = #data,
        data = data
    }, 0)
end

function std_smbus:SetHostOsStatus(data)
    return self:_send_and_receive_request_in_frames({
        lun = 0x80,
        arg = 0x00,
        opcode = 0x1054,
        offset = 0x00,
        length = #data,
        data = data
    }, 0)
end

function std_smbus:ResetSystem()
    return self:_send_and_receive_request_in_frames({
        lun = 0x80,
        arg = 0x00,
        opcode = 0x1018,
        offset = 0x00,
        length =  0,
        data = ''
    }, 0)
end

function std_smbus:SetNMI()
    return self:_send_and_receive_request_in_frames({
        lun = 0x80,
        arg = 0xf0,
        opcode = 0x1042,
        offset = 0x00,
        length =  0,
        data = ''
    }, 0)
end

function std_smbus:GetCpldInfo()
    local recv_data = self:_send_and_receive_request_in_frames({
        lun = 0x80,
        arg = 0x00,
        opcode = 0x1045,
        offset = 0x00,
        length = self.buffer_len - HEADER_LEN,
        data = ''
    }, self.buffer_len - HEADER_LEN)
    local cpld_table = {}
    -- MCU返回的data部分含义：0 3 4 2，代表offset0 = 3， offset4 = 2
    for i = 1, #recv_data // 2 do
        cpld_table[recv_data:byte(2 * i - 1) + 1] = string.format("%02X ", recv_data:byte(2 * i))
        log:debug('offset = %s, data = %s', recv_data:byte(2 * i - 1), recv_data:byte(2 * i))
    end
    --[[
    MCU返回的offset不连续，未返回的使用NA补充，每行16个，最终效果参考：
    0000: 15 5C NA NA NA 17 06 14 11 00 05 04 01 NA NA NA NA
    0010: NA 00 80 00 NA F1 04 05 NA 03 07 05 NA NA NA 01 NA
    0020：......
    --]]
    for i = 1, math.ceil((recv_data:byte(#recv_data - 1) + 1) / 16) * 16 do
        cpld_table[i] = cpld_table[i] or 'NA '
        if i % 16 == 0 then
            cpld_table[i] = cpld_table[i] .. '\n'
            cpld_table[i - 15] = string.format("%03X0:  %s", i // 16 - 1, cpld_table[i - 15])
        end
    end
    return table.concat(cpld_table)
end

function std_smbus:SetSystemAC(data)
    return self:_send_and_receive_request_in_frames({
        lun = 0x80,
        arg = 0x00,
        opcode = 0x1019,
        offset = 0x00,
        length = #data,
        data = data
    }, 0)
end

function std_smbus:SetResetLinkage(data)
    return self:_send_and_receive_request_in_frames({
        lun = 0x80,
        arg = 0x00,
        opcode = 0x1023,
        offset = 0x00,
        length = #data,
        data = data
    }, 0)
end

function std_smbus:GetResetLinkage()
    local recv_data = self:_send_and_receive_request_in_frames({
        lun = 0x80,
        arg = 0x00,
        opcode = 0x1024,
        offset = 0x00,
        length = 1,
        data = ''
    }, 1)
    return bs.new([[<<linkage:8>>]]):unpack(recv_data, true)
end

function std_smbus:GetSystemStatus(data)
    local recv_data = self:_send_and_receive_request_in_frames({
        lun = 0x80,
        arg = 0x00,
        opcode = 0x1005,
        offset = 0x00,
        length = #data,
        data = data
    }, 2)
    return bs.new([[<<type,status>>]]):unpack(recv_data, true)
end

function std_smbus:GetMPUBusyStatus()
    local recv_data = self:_send_and_receive_request_in_frames({
        lun = 0x80,
        arg = 0x00,
        opcode = 0x1046,
        offset = 0x00,
        length = 1,
        data = ''
    }, 1)
    return bs.new([[<<status:8>>]]):unpack(recv_data, true).status
end

function std_smbus:GetErrorCode()
    local recv_data = self:_send_and_receive_request_in_frames({
        lun = 0x80,
        arg = 0x00,
        opcode = 0x0002,
        offset = 0x00,
        length = self.buffer_len - HEADER_LEN,
        data = ''
    }, self.buffer_len - HEADER_LEN)
    local error_codes = {}
    for i = 1, #recv_data // 2 do
        local code = string.unpack('H', recv_data:sub(i * 2 - 1, i * 2))
        error_codes[code] = code
    end
    return error_codes[0x00] == 0x00 and {} or error_codes
end

function std_smbus:GetDeviceTemperature()
    local recv_data = self:_send_and_receive_request_in_frames({
        lun = 0x80,
        arg = 0x00,
        opcode = 0x001D,
        offset = 0x00,
        length = self.buffer_len - HEADER_LEN,
        data = ''
    }, self.buffer_len - HEADER_LEN)
    local resp_data_bs <const> = bs.new([[<<
        sensor_name:8/string,
        value:16/integer-signed
    >>]])
    local temperature_len = recv_data:byte(1)
    local device_temperature = {}
    for index = 1, temperature_len do
        local sensor_data = resp_data_bs:unpack(recv_data:sub((index - 1) * 10 + 2, index * 10 + 1), true)
        local sensor_name = string.gsub(sensor_data.sensor_name, '\x00', '')
        device_temperature[sensor_name] = sensor_data.value
    end
    return device_temperature
end

-- command: 0关闭写保护，1打开写保护
function std_smbus:WriteProtext(command)
    local req =  table.concat({
        string.char(3),
        string.char(command)})
    local val = self:_send_and_receive_request_in_frames({
        lun = 0x80,
        arg = 0x00,
        opcode = 0x1055,
        offset = 0x00,
        length =  #req,
        data = req
    }, 0)
    return val
end

-- 查询CPLD厂商
function std_smbus:GetCpldManufacture(arg)
    local recv_data = self:_send_and_receive_request_in_frames({
        lun = 0x80,
        arg = arg,
        opcode = 0x104A,
        offset = 0x00,
        length = self.buffer_len - HEADER_LEN,
        data = ''
    }, self.buffer_len - HEADER_LEN)
    return bs.new([[<<manufacture_id:8>>]]):unpack(recv_data, true)
end

-- 查询Mcu厂商
function std_smbus:GetMcuManufacture()
    local recv_data = self:_send_and_receive_request_in_frames({
        lun = 0x80,
        arg = 0x00,
        opcode = 0x1021,
        offset = 0x00,
        length = self.buffer_len - HEADER_LEN,
        data = ''
    }, self.buffer_len - HEADER_LEN)
    local data = get_manufacturer_response_data_bs:unpack(recv_data, true)
    local high_hex = tonumber(string.char(data.high_hex), 16)
    log:debug("get mcu manufacture, high_hex is %d", high_hex)
    local low_hex = tonumber(string.char(data.low_hex), 16)
    log:debug("get mcu manufacture, low_hex is %d", low_hex)
    return (high_hex << 4) + low_hex
end

-- 查询CPLD升级状态
function std_smbus:GetCpldUpgradeStatus(arg)
    local recv_data = self:_send_and_receive_request_in_frames({
        lun = 0x80,
        arg = arg,
        opcode = 0x1050,
        offset = 0x00,
        length = self.buffer_len - HEADER_LEN,
        data = ''
    }, self.buffer_len - HEADER_LEN)
    -- 返回状态接口包括升级状态、升级进度，暂时丢弃升级进度
    return bs.new([[<<status:8, percent:8>>]]):unpack(recv_data, true)
end

-- 查询VRD升级状态
function std_smbus:GetVrdUpgradeStatus()
    local recv_data = self:_send_and_receive_request_in_frames({
        lun = 0x80,
        arg = 0x00,
        opcode = 0x1052,
        offset = 0x00,
        length = self.buffer_len - HEADER_LEN,
        data = ''
    }, self.buffer_len - HEADER_LEN)
    -- 返回状态接口包括升级状态、升级进度，暂时丢弃升级进度
    return bs.new([[<<status:8, percent:8>>]]):unpack(recv_data, true)
end

-- 查询CPLD版本
function std_smbus:GetCpldFwVer(arg)
    local rsp = self:_send_and_receive_request_in_frames({
        lun = 0x80,
        arg = arg,
        opcode = 0x1047,
        offset = 0x00,
        length = self.buffer_len - HEADER_LEN,
        data = ''
    }, 2)

    return string.format('%x.%02x', string.unpack('BB', rsp))

end

function std_smbus:GetCpuCores()
    --获取CPU核数
    local rsp = self:GetHardwareInfoByArg(0x02, 1)
    if not rsp then
        return ''
    end
    local res = bs.new([[<<cpu_core:8>>]]):unpack(rsp, true)
    return tonumber(res.cpu_core) or 0
end

function std_smbus:GetCpuArch()
    --获取CPU架构
    local rsp = self:GetHardwareInfoByArg(0x04, 1)
    if not rsp then
        return ''
    end
    local res = bs.new([[<<cpu_arch:8>>]]):unpack(rsp, true)
    return cpu_archs[res.cpu_arch]
end

function std_smbus:GetMemSize()
    --获取内存大小
    local rsp = self:GetHardwareInfoByArg(0x05, 2)
    if not rsp then
        return ''
    end
    local res = bs.new([[<<mem_size:16>>]]):unpack(rsp, true)
    return tonumber(res.mem_size) or 0
end

function std_smbus:GetDiskSize()
    --获取存储大小
    local rsp = self:GetHardwareInfoByArg(0x06, 2)
    if not rsp then
        return ''
    end
    local res = bs.new([[<<disk_size:16>>]]):unpack(rsp, true)
    return tonumber(res.disk_size) or 0
end

function std_smbus:GetBIOSVersion()
    --获取BIOS字段
    local rsp = self:GetHardwareInfoByArg(0x08, 48)
    if not rsp then
        return ''
    end
    --0表示未上报
    if#rsp == 1 and string.byte(rsp, 1, 1) == 0 then
        return 'NA'
    end
    local res = bs.new([[<<data/string>>]]):unpack(rsp, true)
    return res.data
end

--单板硬件信息查询（Opcode: 0x105D）
function std_smbus:GetHardwareInfoByArg(arg, len)
    local rsp = self:_send_and_receive_request_in_frames({
        lun = 0x80,
        arg = arg,
        opcode = 0x105D,
        offset = 0x00,
        length = len,
        data = ''
    }, len)

    return rsp
end

-- Firmware升级
function std_smbus:UpgradeFirmware(
        fw_path, ratio_numerator, ratio_denominator, system_id, firmware_type, parameters, cpld_id)
    log:notice("start to upgrade %s", firmware_type)
    local NOTIFY_BOUNDARY <const> = 15
    local fp = file_sec.open_s(fw_path, 'rb')
    if not fp then
        log:error('[DPU] open firmware file fail')
        return false
    end
    local fw_stream = mc_utils.close(fp, pcall(fp.read, fp, 'a'))
    local fw_stream_len = #fw_stream

    local max_payload_size = self.buffer_len - HEADER_LEN
    local ctx = context.new()
    ctx.timeout = 240
    local offset = 0

    while offset < fw_stream_len do
        local ok = skynet.unpack(self.chip:PluginRequest(ctx, 'general_hardware', 'upgrade',
            skynet.packstring(fw_path, firmware_type, self.buffer_len, self.slave_address,
                offset, MAX_SEND_LEN, cpld_id)))
        if not ok then
            log:notice("[DPU] Upgrade %s fail", firmware_type)
            return false
        end
        offset = offset + MAX_SEND_LEN
        local progress = offset % (100 * max_payload_size) ~= 0 and 0 or offset * 100 //
            fw_stream_len * ratio_numerator // ratio_denominator
        if progress > NOTIFY_BOUNDARY then
            notify_upgrade_status(system_id, firmware_type, progress, parameters)
        end
    end

    log:notice("end to upgrade %s", firmware_type)
    return true
end

-- 生效CPLD
function std_smbus:VaildCpld(arg)
    return self:_send_and_receive_request_in_frames({
        lun = 0x80,
        arg = arg,
        opcode = 0x1049,
        offset = 0x00,
        length =  self.buffer_len - HEADER_LEN,
        data = ''
    }, self.buffer_len - HEADER_LEN)
end

function std_smbus:GetLogUpdateStatus()
    local recv_data = self:_send_and_receive_request_in_frames({
        lun = 0x80,
        arg = 0x00,
        opcode = 0x1031,
        offset = 0x00,
        length = 1,
        data = ''
    }, 1)
    return bs.new([[<<status:8>>]]):unpack(recv_data, true).status
end

function std_smbus:GetSecureBoot()
    local recv_data = self:_send_and_receive_request_in_frames({
        lun = 0x80,
        arg = 0x00,
        opcode = 0x1056,
        offset = 0x00,
        length = self.buffer_len - HEADER_LEN,
        data = '\x02'
    }, 2)
    return bs.new([[<<type,secure_boot>>]]):unpack(recv_data, true)
end

function std_smbus:SetSecureBoot(secure_boot)
    -- 2表示DPU安全启动项开关
    local req = table.concat({
        string.char(2),
        string.char(secure_boot)
    })
    return self:_send_and_receive_request_in_frames({
        lun = 0x80,
        arg = 0x00,
        opcode = 0x1055,
        offset = 0x00,
        length = #req,
        data = req
    }, 0)
end

function std_smbus:ImportPublicKey(key_stream)
    local max_payload_size = self.buffer_len - HEADER_LEN
    local req = {
        lun = 0x0,
        arg = 0x00,
        opcode = 0x1059,
        offset = 0x00,
        length = 1,
        data = ''
    }
    local offset = 0
    local key_stream_len = #key_stream
    local write_data_batch = {}
    local sent_payload_len, ok, err
    while offset < key_stream_len do
        req.offset = offset
        if offset + max_payload_size >= key_stream_len then
            req.lun = 0x80
            req.length = key_stream_len - offset
        else
            req.lun = 0
            req.length = max_payload_size
        end
        sent_payload_len = req.length
        req.data = key_stream:sub(req.offset + 1, req.offset + req.length)
        write_data_batch[#write_data_batch + 1] = {STD_SMBUS_REQ_COMMAND_CODE, request_header_bs:pack(req)}
        offset = offset + sent_payload_len
    end

    if next(write_data_batch) then
        log:notice('[DPU] Start to write data, key_len:%s, list size:%s', key_stream_len, #write_data_batch)
        ok, err = pcall(smbus.chip_batch_write, self.chip, self.slave_address, write_data_batch)
        if not ok then
            log:error('std smbus send public key to sdi mcu failed, err is %s', err)
            return false
        end
        write_data_batch = {}
    else
        log:error('while import public key, write data batch is empty')
        return false
    end

    return true
end

function std_smbus:GetLLDPStatus()
    local recv_data = self:_send_and_receive_request_in_frames({
        lun = 0x80,
        arg = 0x00,
        opcode = 0x1056,
        offset = 0x00,
        length = self.buffer_len - HEADER_LEN,
        data = '\x05'
    }, 2)
    return bs.new([[<<type,status>>]]):unpack(recv_data, true)
end

function std_smbus:SetLLDPStatus(status)
    -- 5表示DPU的LLDP开关
    local req = table.concat({
        string.char(5),
        string.char(status)
    })
    return self:_send_and_receive_request_in_frames({
        lun = 0x80,
        arg = 0x00,
        opcode = 0x1055,
        offset = 0x00,
        length = #req,
        data = req
    }, 0)
end

return std_smbus
