-- Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
--
-- this file licensed under the 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.
--
-- Create: 2023-03-14
local bs = require 'mc.bitstring'
local utils = require 'mc.utils'
local log = require 'mc.logging'
local libmgmt_protocol = require 'libmgmt_protocol'

-- SP382
local BroadCom = {
    protocol_dependencies = {ncsi_standard = {endpoint = nil}},
    properties = {
        InitializeNCSIChannel = {
            protocol = 'ncsi_standard',
            action = 'on_demand',
            request = {
                -- 运行时需传入channel_id(也就是port id)
                packet_type = 0x00,
                expect_rsp_packet_type = 0x80
            },
            response = function(data)
                return bs.new([[<<
                    response_code:16/big,
                    reason_code:16/big
                >>]]):unpack(data, true)
            end
        },
        VendorID = {
            protocol = 'ncsi_standard',
            action = 'on_demand',
            request = {
                packet_type = 0x15,
                expect_rsp_packet_type = 0x95,
                channel_id = 0x0 -- 默认从port 0获取
            },
            response = function(data)

                local r = bs.new([[<<
                    response_code:16/big,
                    reason_code:16/big,
                    ncsi_version_major:8,
                    ncsi_version_minor:8,
                    ncsi_version_update:8,
                    ncsi_version_alpha1:8,
                    rsvd0:24,
                    ncsi_version_alpha2:8,
                    firmware_name:12/string,
                    # V2 mellanox firmware version显示为十进制 xx.yy.zzzz,最后一位是16bit大端
                    firmware_version_1:8,
                    firmware_version_2:8,
                    firmware_version_3:16/big,
                    pci_did:16/big,
                    pci_vid:16/big,
                    pci_ssid:16/big,
                    pci_svid:16/big,
                    manufacturer_id:32/big
                >>]]):unpack(data, true)
                local bsd_to_string = function(v)
                    if v & 0xF0 == 0xF0 then
                        return tostring(v)
                    elseif v < 0x10 then
                        return '0' .. tostring(v)
                    end
                    return tostring((v // 16) * 10)
                end
                local ncsi_version = bsd_to_string(r.ncsi_version_major) ..
                                         bsd_to_string(r.ncsi_version_minor)
                if r.ncsi_version_update ~= 0xFF then
                    ncsi_version = ncsi_version .. bsd_to_string(r.ncsi_version_update)
                end
                ncsi_version = ncsi_version .. string.char(r.ncsi_version_alpha1) ..
                                   string.char(r.ncsi_version_alpha2)
                local firmware_version = string.format('%02d.%02d.%04d', r.firmware_version_1,
                    r.firmware_version_2, r.firmware_version_3)
                log:error('get firmware by ncsi over mctp, value is %s', firmware_version)
                return {
                    FirmwareVersion = firmware_version,
                    VendorID = string.format('0x%04x', r.pci_vid),
                    DeviceID = string.format('0x%04x', r.pci_did),
                    SubsystemVendorID = string.format('0x%04x', r.pci_svid),
                    SubsystemDeviceID = string.format('0x%04x', r.pci_ssid)
                }
            end
        },
        LinkStatus = {
            protocol = 'ncsi_standard',
            action = 'on_schedule',
            period_in_sec = 5,
            request = {
                -- 运行时需传入channel_id(也就是port id)
                packet_type = 0x0A,
                expect_rsp_packet_type = 0x8A
            },
            response = function(data)
                local r = bs.new([[<<
                    response_code:16/big,
                    reason_code:16/big,

                    # link status 有4个字节，大字节序
                    # link status 第1个字节
                    rsvd0:8,

                    # link status 第二个字节
                    tx_flow_control:1,
                    rx_flow_control:1,
                    link_partner8:2,
                    serdes_link:1,
                    oem_link_speed:1,
                    rsvd1:2,

                    # link status 第三个字节
                    rsvd2:1,
                    link_partner1:1,
                    link_partner2:1,
                    link_partner3:1,
                    link_partner4:1,
                    link_partner5:1,
                    link_partner6:1,
                    link_partner7:1,

                    # link status 第四个字节
                    link_flag:1,
                    speed_duplex:4,
                    negotiate_flag:1,
                    negotiate_complete:1,
                    parallel_detection:1,

                    # other indication 有4个字节
                    rsvd3:24,
                    host_nc_driver_status_indication:1,
                    rsvd4:7,

                    oem_link_status:32/big
                >>]]):unpack(data, true)

                local speed<const> = {
                    10, 10, 100, 100, 100, 1000, 1000, 10000, 20000, 25000, 40000, 50000, 100000,
                    2500, 0, 0
                }
                local full_duplex_flag<const> = {
                    false, true, false, false, true, false, true, true, true, true, true, true,
                    true, true, false, false
                }
                local link_switch<const> = {[0x0] = 'Disconnected', [0x1] = 'Connected', [0xFF] = 'N/A'}

                return {
                    LinkStatus = link_switch[r.link_flag] or 'N/A',
                    SpeedMbps = speed[r.speed_duplex] or 0,
                    FullDuplex = full_duplex_flag[r.speed_duplex] or false
                }
            end
        },
        DefaultMacAddrNCSI = {
            protocol = 'ncsi_standard',
            action = 'on_schedule',
            period_in_sec = 5,
            request = {
                -- 运行时需传入channel_id(也就是port id)
                packet_type = 0x2B,
                expect_rsp_packet_type = 0xAB,
                data = '\x00\x00\x00\x00'
            },
            response = function(data)
                local r = bs.new([[<<
                    _:26/string,
                    addr_tlv:16/big,
                    addr:1/MAC_ADDRESS,
                    addr2_tlv:16/big,
                    add2:1/MAC_ADDRESS,
                    reserved:2/string
                >>]], libmgmt_protocol.common_bs_helper):unpack(data, true)
                return r.addr.mac_address
            end
        },
        MacAddrNCSI = {
            protocol = 'ncsi_standard',
            action = 'on_schedule',
            period_in_sec = 5,
            request = {
                -- 运行时需传入channel_id(也就是port id)
                packet_type = 0x2B,
                expect_rsp_packet_type = 0xAB,
                data = '\x00\x00\x00\x00'
            },
            response = function(data)
                local r = bs.new([[<<
                    _:26/string,
                    addr_tlv:16/big,
                    addr:1/MAC_ADDRESS,
                    addr2_tlv:16/big,
                    addr2:1/MAC_ADDRESS,
                    reserved:2/string
                >>]], libmgmt_protocol.common_bs_helper):unpack(data, true)
                return r.addr2.mac_address
            end
        }
    }
}

return {
    mctp = function(endpoint)
        local obj = utils.table_copy(BroadCom)
        obj.protocol_dependencies.ncsi_standard.endpoint = endpoint
        return obj
    end
}
