-- 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 crc8 = require 'mc.crc8'
local context = require 'mc.context'
local utils = require 'mc.utils'

local smbus = {}

local E_ERR = 1
local E_OK  = 0

-- 封装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 E_ERR
    end
    return E_OK
end

-- chip_write 写入指定长的数据
function smbus.chip_write(chip, addr, cmd, data)
    if not chip then
        log:error('smbus.chip_write: unkonwn chip')
        return
    end

    local check_buf = table.concat({string.char(addr), string.char(cmd), data})
    local crc = crc8(check_buf)
    chip:Write(context.new(), cmd, data .. string.pack('B', crc))
end

-- chip_batch_write 批量写入数据
function smbus.chip_batch_write(chip, addr, data)
    if not chip then
        log:error('smbus.chip_batch_write: unkonwn chip')
        return
    end

    for _, v in ipairs(data) do
        local cmd = v[1]
        v[2] = string.pack('B', #v[2]) .. v[2]
        local check_buf = table.concat({string.char(addr), string.char(cmd), v[2]})
        local crc = crc8(check_buf)
        v[2] = v[2] .. string.pack('B', crc)
    end

    chip:BatchWrite(context.new(), data)
end

-- chip_wordwrite 从Chip对象中写一个Word的信息
function smbus.chip_wordwrite(chip, addr, cmd, data)
    smbus.chip_write(chip, addr, cmd, string.pack('H', data))
end

-- chip_blkwrite 从Chip对象中写一个块的信息，第一位为数据长度
function smbus.chip_blkwrite(chip, addr, cmd, data)
    smbus.chip_write(chip, addr, cmd, string.pack('B', #data) .. data)
end

-- chip_read 读取指定长的数据
function smbus.chip_read(chip, addr, cmd, len)
    if not chip then
        log:error('smbus.chip_read: unkonwn chip')
        return nil
    end

    local value = chip:Read(context.get_context_or_default(), cmd, len)
    local check_buf = { string.char(addr), string.char(cmd), string.char(addr | 0x01), value:sub(1, #value - 1) }
    if not value or check_data(value:sub(#value, #value):byte(), table.concat(check_buf)) ~= E_OK then
        error(table.concat({ '[smbus]read commad(0x', string.format('%02x', cmd), ') failed, value:',
            utils.to_hex(value), 'check_buf:', utils.to_hex(table.concat(check_buf)) }))
    end
    return value
end

-- chip_wordread 读取一个16位的数据
function smbus.chip_wordread(chip, addr, cmd)
    return string.unpack('H', smbus.chip_read(chip, addr, cmd, 3):sub(1, 2))
end

-- chip_wordread 读取一个16位的数据
function smbus.chip_byteread(chip, addr, cmd)
    return string.unpack('B', smbus.chip_read(chip, addr, cmd, 2):sub(1, 1))
end

-- chip_blkread 读取一个块的信息
function smbus.chip_blkread(chip, addr, cmd, len)
    local value = smbus.chip_read(chip, addr, cmd, len + 2)
    if not value then
        return nil
    end
    return value:sub(2, #value - 1)
end

-- chip_write_and_read 写入指定长的数据, 并读取指定长的数据
function smbus.chip_write_read(chip, addr, write_cmd, data, read_cmd, read_len)
    if not chip then
        log:error('smbus.chip_read: unkonwn chip')
        return nil
    end
    -- 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

-- chip_blkwrite_read 写入后并读取一个块的信息
function smbus.chip_blkwrite_read(chip, addr, write_cmd, data, read_cmd, len)
    local value = smbus.chip_write_read(chip, addr, write_cmd, string.pack('B', #data) .. data, read_cmd, len + 2)
    if not value then
        return nil
    end
    return value:sub(2, #value - 1)
end

return smbus
