-- 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_vlan_mode = require 'ncsi.ncsi_protocol.ncsi_vlan_mode'
local ncsi_packet = require 'ncsi.ncsi_protocol.ncsi_packet'

local ncsi_vlan_filter = {}

-- 命令类型常量定义
local SET_VLAN_FILTER = 0x0B
local SET_VLAN_FILTER_RSP = 0x8B

-- 请求长度
local SET_VLAN_FILTER_REQ_LEN = 8

-- 填充长度
local SET_VLAN_FILTER_REQ_PAD_LEN = 18

-- 报文大小为payload + checksum + pad + FCS
local SET_VLAN_FILTER_PKT_SIZE = SET_VLAN_FILTER_REQ_LEN + 4 + SET_VLAN_FILTER_REQ_PAD_LEN + 4

-- 设置VLAN过滤请求报文结构
local set_vlan_filter_req_bs = bs.new([[<<
    reserved1:16,
    vlan_id_hi:4,
    user_priority:4,
    vlan_id_low:8,
    reserved2:16,
    filter:8,
    enable:1,
    reserved3:7,
    check_sum:32,
    pad_data:18/string,
    fcs:32
>>]])

-- 填充设置VLAN过滤请求的payload
local function fill_set_vlan_filter_payload(req_packet, vlan_filter, vlan_state, vlan_id)
    -- 计算VLAN ID的高4位和低8位
    local vlan_id_hi = (vlan_id >> 8) & 0x0F
    local vlan_id_low = vlan_id & 0xFF

    -- 定义公共的payload数据结构
    local payload_data = {
        reserved1 = 0,
        vlan_id_hi = vlan_id_hi,
        user_priority = 0,
        vlan_id_low = vlan_id_low,
        reserved2 = 0,
        filter = (vlan_filter == 0) and 1 or vlan_filter,
        enable = vlan_state,
        reserved3 = 0,
        check_sum = 0, -- 初始值
        pad_data = string.rep('\0', SET_VLAN_FILTER_REQ_PAD_LEN),
        fcs = 0        -- 初始值
    }

    -- 首先创建一个check_sum为0的初始payload
    req_packet.payload = set_vlan_filter_req_bs:pack(payload_data)

    -- 计算校验和和CRC32
    local check_sum = ncsi_utils.get_checksum(req_packet, ncsi_def.PACKET_HEAD_LEN + SET_VLAN_FILTER_REQ_LEN)
    local crc32 = ncsi_utils.get_crc32(req_packet, ncsi_def.PACKET_HEAD_LEN + SET_VLAN_FILTER_PKT_SIZE - 4)

    -- 更新checksum和FCS字段
    payload_data.check_sum = core.htonl(check_sum)
    payload_data.fcs = core.htonl(crc32)

    -- 返回最终的payload
    return set_vlan_filter_req_bs:pack(payload_data)
end

-- 写入设置VLAN过滤请求
local function write_set_vlan_filter_req(req_packet, eth_name, vlan_filter, vlan_state, vlan_id)
    ncsi_utils.ncsi_cmd_common_config(req_packet)
    req_packet.packet_head.payload_len_hi = (SET_VLAN_FILTER_REQ_LEN >> 8) & 0x0f
    req_packet.packet_head.payload_len_lo = SET_VLAN_FILTER_REQ_LEN & 0xff
    req_packet.payload = fill_set_vlan_filter_payload(req_packet, vlan_filter, vlan_state, vlan_id)

    local req_data = ncsi_utils.ncsi_packet_bs:pack(req_packet)
    return ncsi_protocol_intf.send_ncsi_cmd(req_data,
        SET_VLAN_FILTER_PKT_SIZE + ncsi_def.ETHERNET_HEAD_LEN + ncsi_def.PACKET_HEAD_LEN, eth_name)
end

-- 读取设置VLAN过滤响应
local function read_set_vlan_filter_rsp(rsp)
    return ncsi_packet.read_common_rsp(rsp, 'set vlan filter')
end

-- 命令处理表
local vlan_filter_table = {
    [SET_VLAN_FILTER] = write_set_vlan_filter_req,
    [SET_VLAN_FILTER_RSP] = read_set_vlan_filter_rsp
}

-- 设置VLAN过滤
function ncsi_vlan_filter.ncsi_set_vlan_filter(package_id, channel_id, vlan_filter, vlan_state, vlan_id, eth_name)
    local req_packet = ncsi_packet.create_request_packet(package_id, channel_id, SET_VLAN_FILTER)

    local custom_cmd_table = ncsi_utils.create_custom_cmd_table(
        vlan_filter_table, SET_VLAN_FILTER, write_set_vlan_filter_req,
        vlan_filter, vlan_state, vlan_id
    )

    local ret = ncsi_utils.ncsi_cmd_ctrl(package_id, channel_id, req_packet, eth_name, custom_cmd_table)
    if ret ~= ncsi_def.NCSI_SUCCESS then
        log:error(
            'ncsi cmd ctrl set vlan filter failed, package_id = %s, channel_id = %s, eth_name = %s, vlan_id = %s',
            package_id, channel_id, eth_name, vlan_id)
    end

    -- 启用VLAN模式
    if vlan_state == 1 then
        ncsi_vlan_mode.ncsi_enable_vlan_req(package_id, channel_id, eth_name, ncsi_vlan_mode.VLAN_ONLY)
    else
        ncsi_vlan_mode.ncsi_disable_vlan_req(package_id, channel_id, eth_name)
    end
    return ret
end

return ncsi_vlan_filter