/* Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
*  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.
*/

#include <mtd/mtd-abi.h>
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
#include <secure/securec.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <glib-2.0/glib.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "liblsw_drv.h"
#include "logging.h"

static int l_getAsicReg(lua_State *L)
{
    gint32 ret = RET_ERROR;
    GE_SWITCHOEM_E oem = (GE_SWITCHOEM_E)luaL_checkinteger(L, 1);
    guint8 switch_unit = (guint8)luaL_checkinteger(L, 2);
    guint32 switch_addr = (guint32)luaL_checkinteger(L, 3);
    guint32 value = 0;

    ret = lsw_switch_reg_get(switch_unit, switch_addr, &value, oem);
    if (ret != RET_OK) {
        debug_log(DLOG_ERROR, "rtk rtl8367c getAsicReg fail, ret = %d", ret);
        return 0;
    }
    lua_pushinteger(L, value);
    return 1;
}

static int l_setAsicReg(lua_State *L)
{
    gint32 ret = RET_ERROR;
    GE_SWITCHOEM_E oem = (GE_SWITCHOEM_E)luaL_checkinteger(L, 1);
    guint8 switch_unit = (guint8)luaL_checkinteger(L, 2);
    guint32 switch_addr = (guint32)luaL_checkinteger(L, 3);
    guint32 value = (guint32)luaL_checkinteger(L, 4);

    ret = lsw_switch_reg_set(switch_unit, switch_addr, value, oem);
    if (ret != RET_OK) {
        debug_log(DLOG_ERROR, "rtk rtl8367c setAsicReg fail, ret = %d", ret);
        return 0;
    }
    lua_pushinteger(L, RET_OK);
    return 1;
}

/* 读取8367 switch port寄存器 */
static int l_port_phyReg_get(lua_State *L)
{
    gint32 ret = RET_ERROR;
    GE_SWITCHOEM_E oem = (GE_SWITCHOEM_E)luaL_checkinteger(L, 1);
    guint8 switch_unit = (guint8)luaL_checkinteger(L, 2);
    guint8 switch_port = (guint8)luaL_checkinteger(L, 3);
    guint32 switch_addr = (guint32)luaL_checkinteger(L, 4);
    guint32 value = 0;

    ret = lsw_port_phy_reg_get(switch_unit, switch_port, switch_addr, &value, oem);
    if (ret != RET_OK) {
        debug_log(DLOG_ERROR, "rtk rtl8367c port phyReg get fail ret = %d", ret);
        return 0;
    }
    lua_pushinteger(L, value);
    return 1;
}

/* 设置8367 switch port寄存器 */
static int l_port_phyReg_set(lua_State *L)
{
    gint32 ret = RET_ERROR;
    GE_SWITCHOEM_E oem = (GE_SWITCHOEM_E)luaL_checkinteger(L, 1);
    guint8 switch_unit = (guint8)luaL_checkinteger(L, 2);
    guint8 switch_port = (guint8)luaL_checkinteger(L, 3);
    guint32 switch_addr = (guint32)luaL_checkinteger(L, 4);
    guint32 value = (guint32)luaL_checkinteger(L, 5);

    ret = lsw_port_phy_reg_set(switch_unit, switch_port, switch_addr, value, oem);
    if (ret != RET_OK) {
        debug_log(DLOG_ERROR, "rtk rtl8367c port phyReg set fail, ret = %d", ret);
        return 0;
    }
    lua_pushinteger(L, RET_OK);
    return 1;
}

/* 初始化 */
static int l_bus_init(lua_State *L)
{
    gint32 ret = RET_ERROR;
    guint8 bus_type = (guint8)luaL_checkinteger(L, 1);
    ret = rtl_manage_bus_init(bus_type, mdio_write_lsw, mdio_read_lsw);
    ret += sf2507_manage_bus_init(bus_type, mdio_write_lsw, mdio_read_lsw);
    mdio_init_lsw();
    if (ret != RET_OK) {
        debug_log(DLOG_ERROR, "rtl manage bus init fail, ret = %d", ret);
        return 0;
    }

    lua_pushinteger(L, RET_OK);
    return 1;
}

static int l_chip_init(lua_State *L)
{
    GE_SWITCHOEM_E oem = (GE_SWITCHOEM_E)luaL_checkinteger(L, 1);
    guint8 switch_unit = (guint8)luaL_checkinteger(L, 2);
    guint8 init_type = (guint8)luaL_checkinteger(L, 3);

    int ret1 = lsw_switch_init(switch_unit, NULL, init_type, oem);
    int ret2 = lsw_l2_init(switch_unit, oem);
    if (ret1 + ret2 != RET_OK) {
        debug_log(DLOG_ERROR, "lsw chip init failed, unit=%u", switch_unit);
        return 0;
    }

    lua_pushinteger(L, RET_OK);
    return 1;
}


static int l_phy_init(lua_State *L)
{
    gint32 ret;
    guint32 phyType = (guint32)luaL_checkinteger(L, 1);
    guint32 phy_cfg_type = (guint32)luaL_checkinteger(L, 2);
    unsigned char phy_addr = (unsigned char)luaL_checkinteger(L, 3);
    guint32 init_type = (guint32)luaL_checkinteger(L, 4);
    ret = cfg_phy_8211_mode(phyType, phy_cfg_type, phy_addr, init_type);
    if (ret == RET_OK) {
        lua_pushinteger(L, RET_OK);
        return 1;
    }
    return 0;
}

static int l_lsw_vlan_init(lua_State *L)
{
    gint32 ret = RET_ERROR;
    GE_SWITCHOEM_E oem = (GE_SWITCHOEM_E)luaL_checkinteger(L, 1);
    guint8 switch_unit = (guint8)luaL_checkinteger(L, 2);
    ret = lsw_vlan_init(switch_unit, oem);
    if (ret != RET_OK) {
        debug_log(DLOG_ERROR, "vlan init fail, ret = %d", ret);
        return 0;
    }
    lua_pushinteger(L, RET_OK);
    return 1;
}

static int l_lsw_single_vlan_init(lua_State *L)
{
    gint32 ret = RET_ERROR;
    rtk_vlan_cfg_t vlan_cfg;
    GE_SWITCHOEM_E oem = (GE_SWITCHOEM_E)luaL_checkinteger(L, 1);
    guint8 switch_unit = (guint8)luaL_checkinteger(L, 2);
    guint32 vid = (guint32)luaL_checkinteger(L, 3);
    guint32 member = (guint32)luaL_checkinteger(L, 4);
    guint32 untaged = (guint32)luaL_checkinteger(L, 5);

    (void)memset_s(&vlan_cfg, sizeof(vlan_cfg), 0, sizeof(vlan_cfg));
    vlan_cfg.mbr.bits[0] = member;
    vlan_cfg.untag.bits[0] = untaged;
    vlan_cfg.ivl_en = ENABLED;
    ret = lsw_vlan_set(switch_unit, vid, &vlan_cfg, oem);
    if (ret != RET_OK) {
        debug_log(DLOG_ERROR, "single vlan init fail, ret = %d", ret);
        return 0;
    }
    lua_pushinteger(L, RET_OK);
    return 1;
}

static int l_lsw_port_vlan_init(lua_State *L)
{
    gint32 ret = RET_ERROR;
    GE_SWITCHOEM_E oem = (GE_SWITCHOEM_E)luaL_checkinteger(L, 1);
    guint8 switch_unit = (guint8)luaL_checkinteger(L, 2);
    guint32 physical_id = (guint32)luaL_checkinteger(L, 3);
    guint32 pvid = (guint32)luaL_checkinteger(L, 4);
    guint32 acc_frame_type = (guint32)luaL_checkinteger(L, 5);

    ret = lsw_vlan_port_pvid_set(switch_unit, physical_id, pvid, 0, oem);
    ret += lsw_vlan_port_accept_frame_type_set(switch_unit, physical_id, acc_frame_type, oem);
    if (ret != RET_OK) {
        debug_log(DLOG_ERROR, "port vlan init fail, ret = %d", ret);
        return 0;
    }
    lua_pushinteger(L, RET_OK);
    return 1;
}

static int l_port_init(lua_State *L)
{
    gint32 ret = RET_ERROR;
    GE_SWITCHOEM_E oem = (GE_SWITCHOEM_E)luaL_checkinteger(L, 1);
    unsigned char switch_unit = (unsigned char)luaL_checkinteger(L, 2);
    unsigned char physical_id = (unsigned char)luaL_checkinteger(L, 3);
    guint8 port_type = (guint8)luaL_checkinteger(L, 4);

    if (oem == GE_SWITCHOEM_SF2507) {
        ret = l2sw_switch_ensure_fc(switch_unit);
        if (ret != RET_OK) {
            debug_log(DLOG_ERROR, "port init fail, ret = %d", ret);
            return 0;
        }
    }

    if (switch_unit != 0xff) {
        ret = rtl_8367_cfg_port_attr(switch_unit, physical_id, port_type, oem);
    }

    if (ret != RET_OK) {
        debug_log(DLOG_ERROR, "port init fail, ret = %d", ret);
        return 0;
    }

    lua_pushinteger(L, RET_OK);
    return 1;
}

static int l_cfg_port_iso(lua_State *L)
{
    gint32 ret = RET_ERROR;
    GE_SWITCHOEM_E oem = (GE_SWITCHOEM_E)luaL_checkinteger(L, 1);
    unsigned char switch_unit = (unsigned char)luaL_checkinteger(L, 2);
    unsigned char physical_id = (unsigned char)luaL_checkinteger(L, 3);
    guint8 port_iso = (guint8)luaL_checkinteger(L, 4);

    ret = rtl_8367_cfg_port_iso(switch_unit, physical_id, port_iso, oem);
    if (ret != RET_OK) {
        debug_log(DLOG_ERROR, "port cfg iso fail, ret = %d", ret);
        return 0;
    }

    lua_pushinteger(L, RET_OK);
    return 1;
}

static int l_cfg_port_storm(lua_State *L)
{
    gint32 ret = RET_ERROR;
    GE_SWITCHOEM_E oem = (GE_SWITCHOEM_E)luaL_checkinteger(L, 1);
    unsigned char switch_unit = (unsigned char)luaL_checkinteger(L, 2);
    unsigned char physical_id = (unsigned char)luaL_checkinteger(L, 3);

    ret = rtl_8367_cfg_storm_ctrl(switch_unit, physical_id, oem);
    if (ret != RET_OK) {
        debug_log(DLOG_ERROR, "port cfg port storm fail, ret = %d", ret);
        return 0;
    }

    lua_pushinteger(L, RET_OK);
    return 1;
}

static int l_lsw_switch_soft_reset(lua_State *L)
{
    gint32 ret = RET_ERROR;
    GE_SWITCHOEM_E oem = (GE_SWITCHOEM_E)luaL_checkinteger(L, 1);
    unsigned char switch_unit = (unsigned char)luaL_checkinteger(L, 2);

    ret = lsw_switch_soft_reset(switch_unit, oem);
    if (ret != RET_OK) {
        debug_log(DLOG_ERROR, "soft reset switch fail, ret = %d", ret);
        return 0;
    }
    lua_pushinteger(L, RET_OK);
    return 1;
}


static int l_get_oem_type_by_unit(lua_State *L)
{
    gint32 ret;
    guint32 chip_id = GE_SWITCHOEM_BUTT;
    guint8 unit_index = (guint8)luaL_checkinteger(L, 1);
    
    for (guint8 retry = 0; retry < RTL_MAX_RETRY_TIMES; retry++) {
        ret = lsw_get_switch_chip_id(unit_index, &chip_id);
        if (ret == RET_OK) {
            break;
        }
        vos_task_delay(RTK_DELAY_1000_MSEC);
    }
    if (ret != RET_OK) {
        debug_log(DLOG_ERROR, "Get unit %u lsw type failed, ret %d.", unit_index, ret);
        return 0;
    }
 
    if (is_rtl_8367_chip(chip_id)) {
        lua_pushinteger(L, GE_SWITCHOEM_RTL8367);
        return 1;
    } else if (chip_id == L2SW_CHIP_ID) {
        lua_pushinteger(L, GE_SWITCHOEM_SF2507);
        return 1;
    } else {
        debug_log(DLOG_ERROR, "Lsw unit %u chipid(0x%X) cannot identify .", unit_index, chip_id);
        return 0;
    }
}

static int l_get_phy_type(lua_State *L)
{
    guint32 phyAddr = (guint32)luaL_checkinteger(L, 1);
    int ret;
    const unsigned int phyIdfRegL = 0x2;
    const unsigned int phyIdfRegH = 0x3;
    const unsigned int rtkPhyIdf = 0x32001c;
    const guint32 phy_id_hval_shift_bits = 6;
    unsigned int regLVal = 0;
    unsigned int regHVal = 0;
    unsigned int phyIdf;
    unsigned int retry;
 
    for (retry = 0; retry < RETRY_3TIMES; retry++) {
        ret = mdio_read_lsw(0, phyAddr, phyIdfRegL, &regLVal);
        ret += mdio_read_lsw(0, phyAddr, phyIdfRegH, &regHVal);
        if ((ret == RET_OK) && (regLVal != INVALID_VALUE16) && (regHVal != INVALID_VALUE16)) {
            /* mdio部分故障接口不会返回成功，读取到的值为0xffff时，也按故障处理 */
            break;
        }
        debug_log(DLOG_ERROR, "Get phy type ret=%d, regLVal:0x%x, regHVal:0x%x", ret, regLVal, regHVal);
        vos_task_delay(RTL8367_RST_HOLDTIME);
    }
    if (retry == RETRY_3TIMES) {
        return 0;
    }
 
    if (ret == RET_OK) {
        phyIdf = ((regHVal << phy_id_hval_shift_bits) & 0x3F0000) | (regLVal & 0xffff);  // 通过读取的寄存器值组合PHY ID
        if (phyIdf == rtkPhyIdf) {
            lua_pushinteger(L, PHY_8211_TYPE_RTK);
            return 1;
        } else {
            lua_pushinteger(L, PHY_8211_TYPE_YT);
            return 1;
        }
    }
    
    return 0;
}


static int l_get_port_status(lua_State *L)
{
    gint32 ret = RET_ERROR;
    guint8 sgmii_link = 0xff;
    GE_SWITCHOEM_E oem = (GE_SWITCHOEM_E)luaL_checkinteger(L, 1);
    unsigned char switch_unit = (unsigned char)luaL_checkinteger(L, 2);
    rtk_port_t physical_id = (rtk_port_t)luaL_checkinteger(L, 3);
    port_type_t port_type = (port_type_t)luaL_checkinteger(L, 4);
    unsigned char conPhyAddr = (unsigned char)luaL_checkinteger(L, 5);
    PHY_8211_TYPE phyType = (PHY_8211_TYPE)luaL_checkinteger(L, 6);

    switch_port_status_s switch_port_status;
    switch_port_status.port_link = (rtk_port_linkStatus_t)0xff;
    switch_port_status.port_speed = (rtk_port_speed_t)0xff;
    switch_port_status.port_duplex = (rtk_port_duplex_t)0xff;

    ret = rtl_get_switch_port_status(switch_unit, physical_id, port_type, &switch_port_status, oem);
    if (ret != RET_OK) {
        debug_log(DLOG_ERROR,
            "Get switch(%u) port(%d) status failed, ret=%d).", switch_unit, physical_id, ret);
        return 0;
    }

    if (conPhyAddr != INVALID_VALUE8) {
        if (switch_port_status.port_link != PORT_LINKUP) {
            debug_log(DLOG_ERROR, "Switch(%u) port(%u) link down", switch_unit, physical_id);
        }
        ret = lsw_get_port_attached_phy_link(switch_unit, conPhyAddr, phyType, &sgmii_link);
        if (ret != RET_OK) {
            debug_log(DLOG_ERROR, "Get switch(%u) port(%u) attached phy link failed, ret(%d)",
                switch_unit, physical_id, ret);
            return 0;
        }
    }
    switch_port_status.port_link &= sgmii_link;

    lua_pushinteger(L, switch_port_status.port_link);
    lua_pushinteger(L, switch_port_status.port_speed);
    lua_pushinteger(L, switch_port_status.port_duplex);
    lua_pushinteger(L, switch_port_status.port_errors);
    return 4;
}

static int l_rtl_8367_is_phy_abnormal(lua_State *L)
{
    gboolean is_abnormal;
    Phy8211Info_S phy8211Info = { 0 };
    unsigned char switch_unit = (unsigned char)luaL_checkinteger(L, 1);
    guint8 physicalId = (guint8)luaL_checkinteger(L, 2);
    guint32 phy_addr = (guint32)luaL_checkinteger(L, 3);
    PHY_8211_TYPE phy_type = (PHY_8211_TYPE)luaL_checkinteger(L, 4);
    guint8 connect_node_slotId = (guint8)luaL_checkinteger(L, 5);
    guint8 presence = (guint8)luaL_checkinteger(L, 6);

    phy8211Info.unit = switch_unit;
    phy8211Info.phyId = physicalId;
    phy8211Info.phyAddr = phy_addr;
    phy8211Info.phyType = phy_type;
    phy8211Info.connectNodeSlotId = connect_node_slotId;
    phy8211Info.presence = presence;
    is_abnormal = rtl_8367_is_phy_abnormal(&phy8211Info);
    if (is_abnormal != FALSE) {
        return 0;
    }

    lua_pushinteger(L, RET_OK);
    return 1;
}

static int l_lsw_port_isolation_get(lua_State *L)
{
    gint32 ret = RET_ERROR;
    rtk_portmask_t mask = { 0 };
    GE_SWITCHOEM_E oem = (GE_SWITCHOEM_E)luaL_checkinteger(L, 1);
    unsigned char switch_unit = (unsigned char)luaL_checkinteger(L, 2);
    guint8 physicalId = (guint8)luaL_checkinteger(L, 3);

    ret = lsw_port_isolation_get(switch_unit, physicalId, &mask, oem);
    if (ret != RET_OK) {
        return 0;
    }

    lua_pushinteger(L, mask.bits[0]);
    return 1;
}

static int l_lsw_get_switch_port_auto_nego(lua_State *L)
{
    gint32 ret = RET_ERROR;
    rtk_port_auto_nego_t auto_nego = 0;
    GE_SWITCHOEM_E oem = (GE_SWITCHOEM_E)luaL_checkinteger(L, 1);
    unsigned char switch_unit = (unsigned char)luaL_checkinteger(L, 2);
    rtk_port_t physicalId = (rtk_port_t)luaL_checkinteger(L, 3);
    port_type_t portType = (port_type_t)luaL_checkinteger(L, 4);
    
    auto_nego = (rtk_port_auto_nego_t)0xff;
    ret = lsw_get_switch_port_auto_nego(switch_unit, physicalId, portType, &auto_nego, oem);
    if (ret != RET_OK) {
        return 0;
    }

    lua_pushinteger(L, auto_nego);
    return 1;
}

static int l_lsw_stp_mstpState_get(lua_State *L)
{
    gint32 ret = RET_ERROR;
    rtk_stp_state_t stp_state;
    GE_SWITCHOEM_E oem = (GE_SWITCHOEM_E)luaL_checkinteger(L, 1);
    unsigned char switch_unit = (unsigned char)luaL_checkinteger(L, 2);
    rtk_port_t physicalId = (rtk_port_t)luaL_checkinteger(L, 3);
    
    ret = lsw_stp_mstpState_get(switch_unit, physicalId, oem, &stp_state);
    if (ret != RET_OK) {
        return 0;
    }

    lua_pushinteger(L, stp_state);
    return 1;
}

static int l_rtl_8367_routine_port_pkt_cnt(lua_State *L)
{
    gint32 ret = RET_ERROR;
    SwitchPortRunInfo_S port_run_info;
    GE_SWITCHOEM_E oem = (GE_SWITCHOEM_E)luaL_checkinteger(L, 1);
    unsigned char switch_unit = (unsigned char)luaL_checkinteger(L, 2);
    rtk_port_t physicalId = (rtk_port_t)luaL_checkinteger(L, 3);
    
    ret = rtl_8367_routine_port_pkt_cnt(switch_unit, physicalId, &port_run_info, oem);
    if (ret != RET_OK) {
        return 0;
    }

    lua_pushinteger(L, port_run_info.rxErrors);
    lua_pushinteger(L, port_run_info.txErrors);
    lua_pushinteger(L, port_run_info.rxPkts);
    lua_pushinteger(L, port_run_info.txPkts);
    return 4;
}

static int l_get_port_enable(lua_State *L)
{
    gint32 ret = RET_ERROR;
    rtk_enable_t enable = RTK_ENABLE_END;

    GE_SWITCHOEM_E oem = (GE_SWITCHOEM_E)luaL_checkinteger(L, 1);
    guint8 switch_unit = (guint8)luaL_checkinteger(L, 2);
    guint8 port = (guint8)luaL_checkinteger(L, 3);

    ret = lsw_port_enable_get(switch_unit, port, &enable, oem);
    if (ret != RET_OK) {
        debug_log(DLOG_ERROR,
            "Get switch(%u) port(%d) enable failed, ret=%d).", switch_unit, port, ret);
        return 0;
    }

    lua_pushinteger(L, enable);
    return 1;
}

static int l_set_port_enable(lua_State *L)
{
    gint32 ret = RET_ERROR;
    GE_SWITCHOEM_E oem = (GE_SWITCHOEM_E)luaL_checkinteger(L, 1);
    guint8 switch_unit = (guint8)luaL_checkinteger(L, 2);
    guint8 port = (guint8)luaL_checkinteger(L, 3);
    guint8 enable = (guint8)luaL_checkinteger(L, 4);

    ret = lsw_port_enable_set(switch_unit, port, enable, oem);
    if (ret != RET_OK) {
        debug_log(DLOG_ERROR,
            "Get switch(%u) port(%d) enable(%d) failed, ret=%d).", switch_unit, port, enable, ret);
        return 0;
    }

    lua_pushinteger(L, RET_OK);
    return 1;
}

static int l_get_port_stp_state(lua_State *L)
{
    gint32 ret = RET_ERROR;
    rtk_stp_state_t state = STP_STATE_END;
    GE_SWITCHOEM_E oem = (GE_SWITCHOEM_E)luaL_checkinteger(L, 1);
    guint8 switch_unit = (guint8)luaL_checkinteger(L, 2);
    guint8 port = (guint8)luaL_checkinteger(L, 3);
    debug_log(DLOG_ERROR, "l_get_port_stp_state unit(%u), port(%u)", switch_unit, port);

    ret = lsw_stp_mstpState_get(switch_unit, port, oem, &state);
    if (ret != RET_OK) {
        debug_log(DLOG_ERROR, "Get switch(%u) port(%u) stp state failed\n", switch_unit, port);
        return 0;
    }

    lua_pushinteger(L, state);
    return 1;
}

static int l_set_port_stp_state(lua_State *L)
{
    gint32 ret = RET_ERROR;
    GE_SWITCHOEM_E oem = (GE_SWITCHOEM_E)luaL_checkinteger(L, 1);
    guint8 switch_unit = (guint8)luaL_checkinteger(L, 2);
    guint8 port = (guint8)luaL_checkinteger(L, 3);
    rtk_stp_state_t state = (rtk_stp_state_t)luaL_checkinteger(L, 4);

    ret = lsw_stp_mstpState_set(switch_unit, 0, port, state, oem);
    if (ret != RET_OK) {
        debug_log(DLOG_ERROR, "Set switch(%u) port(%u) stp state = 0x%x failed\n", switch_unit, port, state);
        return 0;
    }

    lua_pushinteger(L, RET_OK);
    return 1;
}


static int l_get_switch_error_code(lua_State *L)
{
    gint32 ret = RET_ERROR;
    rtk_stat_counter_t cnt = 0;

    GE_SWITCHOEM_E oem = (GE_SWITCHOEM_E)luaL_checkinteger(L, 1);
    guint8 switch_unit = (guint8)luaL_checkinteger(L, 2);
    guint8 utp_num = (guint8)luaL_checkinteger(L, 3);

    ret = lsw_stat_port_get(switch_unit, utp_num, STAT_Dot3StatsFCSErrors, &cnt, oem);
    if (ret != RET_OK) {
        debug_log(DLOG_ERROR,
            "get switch_unit(%d) utp_num(%d) error counter failed(ret=%d)", switch_unit, utp_num, ret);
        return 0;
    }

    lua_pushinteger(L, cnt);
    return 1;
}

static int l_get_port_mac_info(lua_State *L)
{
    gint32 ret = RET_OK;
    rtk_l2_ucastAddr_t l2_entry;
    guint32 address_id = 0;
    guint32 mac_count = 0;
    gchar mac_info[MAX_PORT_INFO_SIZE] = {0};

    GE_SWITCHOEM_E oem = (GE_SWITCHOEM_E)luaL_checkinteger(L, 1);
    guint8 switch_unit = (guint8)luaL_checkinteger(L, 2);
    guint8 port = (guint8)luaL_checkinteger(L, 3);

    lua_newtable(L);
    while (ret == RET_OK) {
        (void)memset_s(&l2_entry, sizeof(rtk_l2_ucastAddr_t), 0, sizeof(rtk_l2_ucastAddr_t));
        ret = lsw_port_mac_info_get(switch_unit, port, &l2_entry, &address_id, oem);
        if (ret == RET_OK) {
            (void)memset_s(mac_info, sizeof(mac_info), 0, sizeof(mac_info));
            (void)sprintf_s(mac_info, sizeof(mac_info), " [ === Entry: %08X, MAC: %02X%02X%02X%02X%02X%02X, \
                ivl: %-12u, cvid: %-12u, fid: %-12u, efid: %-12u, port: %-12u, sa_block: %-12u, da_block: %-12u, \
                auth: %-12u, is_static: %-12u, priority: %-12u, sa_pri_en: %-12u, fwd_pri_en: %-12u === ] ",
                l2_entry.address, l2_entry.mac.octet[IDX0], l2_entry.mac.octet[IDX1], l2_entry.mac.octet[IDX2],
                l2_entry.mac.octet[IDX3], l2_entry.mac.octet[IDX4], l2_entry.mac.octet[IDX5], l2_entry.ivl,
                l2_entry.cvid, l2_entry.fid, l2_entry.efid, l2_entry.port, l2_entry.sa_block, l2_entry.da_block,
                l2_entry.auth, l2_entry.is_static, l2_entry.priority, l2_entry.sa_pri_en, l2_entry.fwd_pri_en);

            mac_count++;
            address_id++;
            lua_pushinteger(L, mac_count);
            lua_pushstring(L, mac_info);
            lua_settable(L, -3);
        } else if (mac_count == 0) {
            debug_log(DLOG_NOTICE, "l_get_port_mac_info not ok, ret=%d", ret);
        }
    }
    return 1;
}

static int l_set_port_mac_addr(lua_State *L)
{
    gint32 ret = RET_ERROR;
    gchar mac_str_upper[MAC_STR_BUF_LEN] = {0};
    gint32 sscanf_cnt;
    rtk_mac_t mac_addr;

    GE_SWITCHOEM_E oem = (GE_SWITCHOEM_E)luaL_checkinteger(L, 1);
    guint8 switch_unit = (guint8)luaL_checkinteger(L, 2);
    guint8 port = (guint8)luaL_checkinteger(L, 3);
    const gchar *mac_str = (gchar *)luaL_checkstring(L, 4);

    if (strlen(mac_str) != strlen("00:11:22:33:44:55")) {
        debug_log(DLOG_ERROR, "input mac adress format error");
        return 0;
    }

    for (gsize i = 0; i < strlen(mac_str); i++) {
        mac_str_upper[i] = g_ascii_toupper(mac_str[i]);
    }
    sscanf_cnt = sscanf_s(mac_str_upper, "%02X:%02X:%02X:%02X:%02X:%02X", &mac_addr.octet[IDX0], &mac_addr.octet[IDX1],
        &mac_addr.octet[IDX2], &mac_addr.octet[IDX3], &mac_addr.octet[IDX4], &mac_addr.octet[IDX5]);
    if (sscanf_cnt != RTK_MAC_ADDR_LEN) {
        debug_log(DLOG_ERROR, "input mac adress format error");
        return 0;
    }

    ret = lsw_port_mac_address_set(switch_unit, port, &mac_addr, oem);
    if (ret != RET_OK) {
        debug_log(DLOG_ERROR, "Set switch(%u) port(%u) static MAC address %s failed(%d)",
            switch_unit, port, mac_str, ret);
        return 0;
    }

    lua_pushinteger(L, RET_OK);
    return 1;
}

static int l_get_port_l2_addr(lua_State *L)
{
    gint32 ret = RET_ERROR;
    rtk_l2_ucastAddr_t l2_data;
    guint32 p_address = 0;
    guint32 addr_count = 0;
    gchar l2_addr[MAX_PORT_L2_ADDR_SIZE] = {0};
    guint8 switch_unit = (guint8)luaL_checkinteger(L, 2);
    guint8 port = (guint8)luaL_checkinteger(L, 3);

    lua_newtable(L);
    while (TRUE) {
        (void)memset_s(&l2_data, sizeof(rtk_l2_ucastAddr_t), 0, sizeof(rtk_l2_ucastAddr_t));
        ret = rtk_rtl8367c_l2_addr_next_get((guint8)switch_unit, READMETHOD_NEXT_L2UCSPA, port, &p_address, &l2_data);
        if (ret != RET_OK) {
            debug_log(DLOG_ERROR, "l_get_port_l2_addr not ok");
            break;
        }

        (void)memset_s(l2_addr, sizeof(l2_addr), 0, sizeof(l2_addr));
        ret = sprintf_s(l2_addr, sizeof(l2_addr), " [ === portid: %-11u, mac: %02x:%02x:%02x:%02x:%02x:%02x, \
            vlan: %-5u, is_static: %-9s ===] ", port, l2_data.mac.octet[0], l2_data.mac.octet[1], l2_data.mac.octet[2],
            l2_data.mac.octet[3], l2_data.mac.octet[4], l2_data.mac.octet[5],
            l2_data.cvid, ((l2_data.is_static == ENABLED) ? "Y" : "N"));
        if (ret <= 0) {
            debug_log(DLOG_ERROR, "sprintf_s fail, ret = %d", ret);
            break;
        }

        p_address++;
        addr_count++;
        lua_pushinteger(L, addr_count);
        lua_pushstring(L, l2_addr);
        lua_settable(L, -3);
    }
    return 1;
}

static int l_flush_port_l2_addr(lua_State *L)
{
    gint32 ret = RET_ERROR;
    rtk_l2_flushCfg_t flush_cfg = {0};
    guint8 switch_unit = (guint8)luaL_checkinteger(L, 2);
    guint8 port = (guint8)luaL_checkinteger(L, 3);

    flush_cfg.flushByPort = ENABLED;
    flush_cfg.port = port;
    flush_cfg.flushStaticAddr = DISABLED;

    ret = rtk_rtl8367c_l2_ucastAddr_flush((guint8)switch_unit, &flush_cfg);
    if (ret != RET_OK) {
        debug_log(DLOG_ERROR, "Flush switch(%u) port(%u) l2 addr failed, ret=0x%x\n", switch_unit, port, ret);
        return ret;
    }

    lua_pushinteger(L, RET_OK);
    return 1;
}

LUAMOD_API int luaopen_lsw_drv(lua_State *L)
{
    luaL_checkversion(L);
    luaL_Reg lsw_methods[] = {
        {"l_getAsicReg", l_getAsicReg},
        {"l_setAsicReg", l_setAsicReg},
        {"l_port_phyReg_get", l_port_phyReg_get},
        {"l_port_phyReg_set", l_port_phyReg_set},
        {"l_bus_init", l_bus_init},
        {"l_chip_init", l_chip_init},
        {"l_phy_init", l_phy_init},
        {"l_lsw_vlan_init", l_lsw_vlan_init},
        {"l_lsw_single_vlan_init", l_lsw_single_vlan_init},
        {"l_lsw_port_vlan_init", l_lsw_port_vlan_init},
        {"l_port_init", l_port_init},
        {"l_lsw_switch_soft_reset", l_lsw_switch_soft_reset},
        {"l_get_oem_type_by_unit", l_get_oem_type_by_unit},
        {"l_get_phy_type", l_get_phy_type},
        {"l_get_port_status", l_get_port_status},
        {"l_rtl_8367_is_phy_abnormal", l_rtl_8367_is_phy_abnormal},
        {"l_lsw_port_isolation_get", l_lsw_port_isolation_get},
        {"l_lsw_get_switch_port_auto_nego", l_lsw_get_switch_port_auto_nego},
        {"l_lsw_stp_mstpState_get", l_lsw_stp_mstpState_get},
        {"l_rtl_8367_routine_port_pkt_cnt", l_rtl_8367_routine_port_pkt_cnt},
        {"l_cfg_port_iso", l_cfg_port_iso},
        {"l_cfg_port_storm", l_cfg_port_storm},
        {"l_get_port_enable", l_get_port_enable},
        {"l_set_port_enable", l_set_port_enable},
        {"l_get_port_stp_state", l_get_port_stp_state},
        {"l_set_port_stp_state", l_set_port_stp_state},
        {"l_get_switch_error_code", l_get_switch_error_code},
        {"l_get_port_mac_info", l_get_port_mac_info},
        {"l_set_port_mac_addr", l_set_port_mac_addr},
        {"l_get_port_l2_addr", l_get_port_l2_addr},
        {"l_flush_port_l2_addr", l_flush_port_l2_addr},
        {NULL, NULL}
    };
    luaL_newlib(L, lsw_methods);
    return 1;
}
