-- 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 core = require 'network.core'
local ncsi_protocol_intf = require 'ncsi_protocol_intf'
local ncsi_utils = require 'ncsi.ncsi_protocol.ncsi_utils'
local ncsi_def = require 'ncsi.ncsi_protocol.ncsi_def'
local ncsi_packet = require 'ncsi.ncsi_protocol.ncsi_packet'
local ncsi_parameter = require 'ncsi.ncsi_protocol.ncsi_parameter'

local ncsi_channel = {}

-- 命令类型常量定义
local ENABLE_CHANNEL = 0x03
local ENABLE_CHANNEL_RSP = 0x83
local DISABLE_CHANNEL = 0x04
local DISABLE_CHANNEL_RSP = 0x84
local ENABLE_CHANNEL_TX = 0x06
local ENABLE_CHANNEL_TX_RSP = 0x86
local DISABLE_CHANNEL_TX = 0x07
local DISABLE_CHANNEL_TX_RSP = 0x87

-- 请求长度
local ENABLE_CHANNEL_REQ_LEN = 0
local DISABLE_CHANNEL_REQ_LEN = 4
local ENABLE_CHANNEL_TX_REQ_LEN = 0
local DISABLE_CHANNEL_TX_REQ_LEN = 0

-- 填充长度
local ENABLE_CHANNEL_REQ_PAD_LEN = 26
local DISABLE_CHANNEL_REQ_PAD_LEN = 22
local ENABLE_CHANNEL_TX_REQ_PAD_LEN = 26
local DISABLE_CHANNEL_TX_REQ_PAD_LEN = 26

-- 报文总大小 (payload + checksum + padding + FCS)
local ENABLE_CHANNEL_PKT_SIZE = ENABLE_CHANNEL_REQ_LEN + 4 + ENABLE_CHANNEL_REQ_PAD_LEN + 4  -- 34
local DISABLE_CHANNEL_PKT_SIZE = DISABLE_CHANNEL_REQ_LEN + 4 + DISABLE_CHANNEL_REQ_PAD_LEN + 4  -- 34
local ENABLE_CHANNEL_TX_PKT_SIZE = ENABLE_CHANNEL_TX_REQ_LEN + 4 + ENABLE_CHANNEL_TX_REQ_PAD_LEN + 4  -- 34
local DISABLE_CHANNEL_TX_PKT_SIZE = DISABLE_CHANNEL_TX_REQ_LEN + 4 + DISABLE_CHANNEL_TX_REQ_PAD_LEN + 4  -- 34

-- 请求和响应的位域结构定义
local enable_channel_req_bs = bs.new([[<<
    check_sum:32,
    data:26/string,
    fcs:32
>>]])

local disable_channel_req_bs = bs.new([[<<
    allow_link_down:1,
    reserved:31,
    check_sum:32,
    data:22/string,
    fcs:32
>>]])

local enable_channel_tx_req_bs = bs.new([[<<
    check_sum:32,
    data:26/string,
    fcs:32
>>]])

local disable_channel_tx_req_bs = bs.new([[<<
    check_sum:32,
    data:26/string,
    fcs:32
>>]])

-- 写入启用通道请求
local function write_enable_channel_req(req_packet, eth_name)
    ncsi_utils.ncsi_cmd_common_config(req_packet)
    req_packet.packet_head.payload_len_hi = (ENABLE_CHANNEL_REQ_LEN >> 8) & 0x0f
    req_packet.packet_head.payload_len_lo = ENABLE_CHANNEL_REQ_LEN & 0xff
    local check_sum = ncsi_utils.get_checksum(req_packet, ncsi_def.PACKET_HEAD_LEN + ENABLE_CHANNEL_REQ_LEN)
    local crc32 = ncsi_utils.get_crc32(req_packet, ncsi_def.PACKET_HEAD_LEN + ENABLE_CHANNEL_PKT_SIZE - 4)
    local payload_data = enable_channel_req_bs:pack({
        check_sum = core.htonl(check_sum),
        data = '',
        fcs = core.htonl(crc32)
    })
    req_packet.payload = payload_data
    local req_data = ncsi_utils.ncsi_packet_bs:pack(req_packet)
    return ncsi_protocol_intf.send_ncsi_cmd(req_data,
        ENABLE_CHANNEL_PKT_SIZE + ncsi_def.ETHERNET_HEAD_LEN + ncsi_def.PACKET_HEAD_LEN, eth_name)
end

-- 写入禁用通道请求
local function write_disable_channel_req(req_packet, eth_name)
    ncsi_utils.ncsi_cmd_common_config(req_packet)
    req_packet.packet_head.payload_len_hi = (DISABLE_CHANNEL_REQ_LEN >> 8) & 0x0f
    req_packet.packet_head.payload_len_lo = DISABLE_CHANNEL_REQ_LEN & 0xff
    local check_sum = ncsi_utils.get_checksum(req_packet, ncsi_def.PACKET_HEAD_LEN + DISABLE_CHANNEL_REQ_LEN)
    local crc32 = ncsi_utils.get_crc32(req_packet, ncsi_def.PACKET_HEAD_LEN + DISABLE_CHANNEL_PKT_SIZE - 4)
    local payload_data = disable_channel_req_bs:pack({
        allow_link_down = 0,
        reserved = 0,
        check_sum = core.htonl(check_sum),
        data = '',
        fcs = core.htonl(crc32)
    })
    req_packet.payload = payload_data
    local req_data = ncsi_utils.ncsi_packet_bs:pack(req_packet)
    return ncsi_protocol_intf.send_ncsi_cmd(req_data,
        DISABLE_CHANNEL_PKT_SIZE + ncsi_def.ETHERNET_HEAD_LEN + ncsi_def.PACKET_HEAD_LEN, eth_name)
end

-- 写入启用通道传输请求
local function write_enable_channel_tx_req(req_packet, eth_name)
    ncsi_utils.ncsi_cmd_common_config(req_packet)
    req_packet.packet_head.payload_len_hi = (ENABLE_CHANNEL_TX_REQ_LEN >> 8) & 0x0f
    req_packet.packet_head.payload_len_lo = ENABLE_CHANNEL_TX_REQ_LEN & 0xff
    local check_sum = ncsi_utils.get_checksum(req_packet, ncsi_def.PACKET_HEAD_LEN + ENABLE_CHANNEL_TX_REQ_LEN)
    local crc32 = ncsi_utils.get_crc32(req_packet, ncsi_def.PACKET_HEAD_LEN + ENABLE_CHANNEL_TX_PKT_SIZE - 4)
    local payload_data = enable_channel_tx_req_bs:pack({
        check_sum = core.htonl(check_sum),
        data = '',
        fcs = core.htonl(crc32)
    })
    req_packet.payload = payload_data
    local req_data = ncsi_utils.ncsi_packet_bs:pack(req_packet)
    return ncsi_protocol_intf.send_ncsi_cmd(req_data,
        ENABLE_CHANNEL_TX_PKT_SIZE + ncsi_def.ETHERNET_HEAD_LEN + ncsi_def.PACKET_HEAD_LEN, eth_name)
end

-- 写入禁用通道传输请求
local function write_disable_channel_tx_req(req_packet, eth_name)
    ncsi_utils.ncsi_cmd_common_config(req_packet)
    req_packet.packet_head.payload_len_hi = (DISABLE_CHANNEL_TX_REQ_LEN >> 8) & 0x0f
    req_packet.packet_head.payload_len_lo = DISABLE_CHANNEL_TX_REQ_LEN & 0xff
    local check_sum = ncsi_utils.get_checksum(req_packet, ncsi_def.PACKET_HEAD_LEN + DISABLE_CHANNEL_TX_REQ_LEN)
    local crc32 = ncsi_utils.get_crc32(req_packet, ncsi_def.PACKET_HEAD_LEN + DISABLE_CHANNEL_TX_PKT_SIZE - 4)
    local payload_data = disable_channel_tx_req_bs:pack({
        check_sum = core.htonl(check_sum),
        data = '',
        fcs = core.htonl(crc32)
    })
    req_packet.payload = payload_data
    local req_data = ncsi_utils.ncsi_packet_bs:pack(req_packet)
    return ncsi_protocol_intf.send_ncsi_cmd(req_data,
        DISABLE_CHANNEL_TX_PKT_SIZE + ncsi_def.ETHERNET_HEAD_LEN + ncsi_def.PACKET_HEAD_LEN, eth_name)
end

-- 读取通道响应通用方法
local function read_channel_response(rsp)
    return ncsi_packet.read_common_rsp(rsp, 'channel')
end

-- 命令处理表
local ncsi_channel_table = {
    [ENABLE_CHANNEL] = write_enable_channel_req,
    [ENABLE_CHANNEL_RSP] = read_channel_response,
    [DISABLE_CHANNEL] = write_disable_channel_req,
    [DISABLE_CHANNEL_RSP] = read_channel_response,
    [ENABLE_CHANNEL_TX] = write_enable_channel_tx_req,
    [ENABLE_CHANNEL_TX_RSP] = read_channel_response,
    [DISABLE_CHANNEL_TX] = write_disable_channel_tx_req,
    [DISABLE_CHANNEL_TX_RSP] = read_channel_response
}

-- 通用通道处理函数
local function handle_channel_operation(package_id, channel_id, eth_name, command_type, operation_name)
    local req_packet = ncsi_packet.create_request_packet(package_id, channel_id, command_type)
    local ret = ncsi_utils.ncsi_cmd_ctrl(package_id, channel_id, req_packet, eth_name, ncsi_channel_table)
    if ret ~= ncsi_def.NCSI_SUCCESS then
        log:error('ncsi cmd ctrl %s failed, package_id = %s, channel_id = %s, eth_name = %s',
            operation_name, package_id, channel_id, eth_name)
    end
    return ret
end

-- 启用通道
function ncsi_channel.ncsi_enable_channel(package_id, channel_id, eth_name)
    return handle_channel_operation(package_id, channel_id, eth_name, ENABLE_CHANNEL, "enable channel")
end

-- 禁用通道
function ncsi_channel.ncsi_disable_channel(package_id, channel_id, eth_name)
    return handle_channel_operation(package_id, channel_id, eth_name, DISABLE_CHANNEL, "disable channel")
end

-- 启用通道传输
function ncsi_channel.ncsi_enable_channel_tx(package_id, channel_id, eth_name)
    ncsi_parameter.get_instance():get_ncsi_parameter().current_channel = channel_id
    return handle_channel_operation(package_id, channel_id, eth_name, ENABLE_CHANNEL_TX, "enable channel tx")
end

-- 禁用通道传输
function ncsi_channel.ncsi_disable_channel_tx(package_id, channel_id, eth_name)
    return handle_channel_operation(package_id, channel_id, eth_name, DISABLE_CHANNEL_TX, "disable channel tx")
end

return ncsi_channel
