-- Copyright (c) 2025 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 utils = require 'mc.utils'

local NetSwift_ncsi = {
    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 = {
                -- 运行时需传入channel_id(也就是port id)
                packet_type = 0x15,
                expect_rsp_packet_type = 0x95
            },
            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,
                    firmware_version_1:8,
                    firmware_version_2:8,
                    firmware_version_3:8,
                    firmware_version_4:8,
                    pci_did:16/big,
                    pci_vid:16/big,
                    pci_ssid:16/big,
                    pci_svid:16/big,
                    manufacturer_id:32/big
                >>]]):unpack(data, true)

                local firmware_version
                if r.firmware_version_1 ~= 0 then
                    firmware_version = string.format('%X.%02X.%02X.%02X',
                        r.firmware_version_1,
                        r.firmware_version_2,
                        r.firmware_version_3,
                        r.firmware_version_4)
                elseif r.firmware_version_2 ~= 0 then
                    firmware_version = string.format('%X.%02X.%02X',
                        r.firmware_version_2,
                        r.firmware_version_3,
                        r.firmware_version_4)
                elseif r.firmware_version_3 ~= 0 then
                    firmware_version = string.format('%X.%02X',
                        r.firmware_version_3,
                        r.firmware_version_4)
                elseif r.firmware_version_4 ~= 0 then
                    firmware_version = string.format('0.%02X', r.firmware_version_4)
                else
                    firmware_version = nil
                end
                    
                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
        },
        PortMetrics = {
            protocol = 'ncsi_standard',
            action = 'on_schedule',
            period_in_sec = 60,
            request = {
                -- 运行时需传入channel_id(也就是port id)
                packet_type = 0x18,
                expect_rsp_packet_type = 0x98
            },
            response = function(data)
                local r = bs.new([[<<
                    response_code:16/big,
                    reason_code:16/big,
                    counters_cleared_from_last_read_ms:32/big,
                    counters_cleared_from_last_read_ls:32/big,
                    total_bytes_received:64/big,
                    total_bytes_transmitted:64/big,
                    total_unicast_packets_received:64/big,
                    total_multicast_packets_received:64/big,
                    total_broadcast_packets_received:64/big,
                    total_unicast_packets_transmitted:64/big,
                    total_multicast_packets_transmitted:64/big,
                    total_broadcast_packets_transmitted:64/big,
                    fcs_received_errors:32/big,
                    alignment_errors:32/big,
                    false_carrier_detections:32/big,
                    runt_packets_received:32/big,
                    jabber_packets_received:32/big,
                    pause_xon_frames_received:32/big,
                    pause_xoff_frames_received:32/big,
                    pause_xon_frames_transmitted:32/big,
                    pause_xoff_frames_transmitted:32/big,
                    single_collision_transmit_frames:32/big,
                    multiple_collision_transmit_frames:32/big,
                    late_collision_frames:32/big,
                    excessive_collision_frames:32/big,
                    control_frames_received:32/big,
                    byte_64_frames_received:32/big,
                    byte_65_127_frames_received:32/big,
                    byte_128_255_frames_received:32/big,
                    byte_256_511_frames_received:32/big,
                    byte_512_1023_frames_received:32/big,
                    byte_1024_1522_frames_received:32/big,
                    byte_1523_9022_frames_received:32/big,
                    byte_64_frames_transmitted:32/big,
                    byte_65_127_frames_transmitted:32/big,
                    byte_128_255_frames_transmitted:32/big,
                    byte_256_511_frames_transmitted:32/big,
                    byte_512_1023_frames_transmitted:32/big,
                    byte_1024_1522_frames_transmitted:32/big,
                    byte_1523_9022_frames_transmitted:32/big,
                    valid_bytes_received:64/big,
                    error_runt_packets_received:32/big,
                    error_jabber_packets_received:32/big
                >>]]):unpack(data, true)
                return {
                    RXFrames = r.total_bytes_received,
                    TXFrames = r.total_bytes_transmitted,
                    RXUnicastFrames = r.total_unicast_packets_received,
                    RXMulticastFrames = r.total_multicast_packets_received,
                    RXBroadcastFrames = r.total_broadcast_packets_received,
                    TXUnicastFrames = r.total_unicast_packets_transmitted,
                    TXMulticastFrames = r.total_multicast_packets_transmitted,
                    TXBroadcastFrames = r.total_broadcast_packets_transmitted,
                    RXFCSErrors = r.fcs_received_errors,
                    RXUndersizeFrames = r.runt_packets_received,
                    RXOversizeFrames = r.jabber_packets_received,
                    TXSingleCollisions = r.single_collision_transmit_frames,
                    TXMultipleCollisions = r.multiple_collision_transmit_frames,
                    TXLateCollisions = r.late_collision_frames,
                    TXExcessiveCollisions = r.excessive_collision_frames,
                    RXFrameAlignmentErrors = r.alignment_errors,
                    RXPauseXONFrames = r.pause_xon_frames_received,
                    RXPauseXOFFFrames = r.pause_xoff_frames_received,
                    TXPauseXONFrames = r.pause_xon_frames_transmitted,
                    TXPauseXOFFFrames = r.pause_xoff_frames_transmitted,
                    RXFalseCarrierErrors = r.false_carrier_detections
                }
            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个字节
                    extended_speed_duplex: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, 1000, 200000, 400000, 800000
                }
                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' }

                local speed_mps = speed[r.speed_duplex] or 0
                if r.speed_duplex == 0xF then
                    speed_mps = speed[r.extended_speed_duplex] or 0
                end

                return {
                    LinkStatus = link_switch[r.link_flag] or 'N/A',
                    SpeedMbps = speed_mps,
                    AutoSpeedNegotiation = r.negotiate_flag == 1,
                    FullDuplex = full_duplex_flag[r.speed_duplex] or false
                }
            end
        },
        DefaultMacAddrNCSI = {
            protocol = 'ncsi_standard',
            action = 'on_schedule',
            period_in_sec = 120,
            request = {
                -- 运行时需传入channel_id(也就是port id)
                packet_type = 0x50,
                expect_rsp_packet_type = 0xD0,
                data = '\x57\x58\x4B\x4A\x01\x13\x00\x00'  -- OEM Get Mac (0x13)
            },
            response = function(data)
                local r = bs.new([[<<
                    response_code:16/big,
                    reason_code:16/big,
                    manufacturer_id:32/big,
                    command_revision:8,
                    oem_command:8,
                    mac1:8,
                    mac2:8,
                    mac3:8,
                    mac4:8,
                    mac5:8,
                    mac6:8
                >>]]):unpack(data, true)
                local mac = string.format("%02X:%02X:%02X:%02X:%02X:%02X",
                    r.mac1, r.mac2, r.mac3, r.mac4, r.mac5, r.mac6)
                return mac
            end
        },
        MacAddrNCSI = {
            protocol = 'ncsi_standard',
            action = 'on_schedule',
            period_in_sec = 10,
            request = {
                -- 运行时需传入channel_id(也就是port id)
                packet_type = 0x50,
                expect_rsp_packet_type = 0xD0,
                data = '\x57\x58\x4B\x4A\x01\x13\x00\x00'  -- OEM Get Mac (0x13)
            },
            response = function(data)
                local r = bs.new([[<<
                    response_code:16/big,
                    reason_code:16/big,
                    manufacturer_id:32/big,
                    command_revision:8,
                    oem_command:8,
                    mac1:8,
                    mac2:8,
                    mac3:8,
                    mac4:8,
                    mac5:8,
                    mac6:8
                >>]]):unpack(data, true)
                local mac = string.format("%02X:%02X:%02X:%02X:%02X:%02X",
                    r.mac1, r.mac2, r.mac3, r.mac4, r.mac5, r.mac6)
                return mac
               
            end
        },
        ChipTemp = {
            protocol = 'ncsi_standard',
            action = 'on_schedule',
            period_in_sec = 5,
            request = {
                packet_type = 0x50,              -- NCSI OEM命令
                expect_rsp_packet_type = 0xD0,   -- NCSI OEM响应
                data = '\x57\x58\x4B\x4A\x01\x14\x00\x00'  -- 网迅OEM温度命令数据
            },
            response = function(data)
                local r = bs.new([[<<   
                    response_code:16/big,
                    reason_code:16/big,
                    manufacturer_id:32/big,
                    command_revision:8,
                    oem_command:8,
                    max_temperature:8,
                    current_temperature:8,
                    checksum:32/big
                >>]]):unpack(data, true)
                
                -- 返回当前芯片温度（摄氏度）
                return r.current_temperature
            end
        }
    }
}

return {
    smbus = nil,
    mctp = function(endpoint)
        local obj = utils.table_copy(NetSwift_ncsi)
        obj.protocol_dependencies.ncsi_standard.endpoint = endpoint
        return obj
    end
}