/* 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.
 */
#define LUA_LIB

#define GNU_SOURCE /* See feature_test_macros(7) */
#include <errno.h>
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <glib-2.0/glib.h>
#include <glib-2.0/glib/gstdio.h>
#include <secure/securec.h>
#include <lua.h>
#include <lauxlib.h>

#include "libncsi_protocol.h"
#include "logging.h"

#define RECV_BUF_LEN 2048
#define NCSI_ETHER_TYPE 0x88F8

static guint32 l_get_ncsi_check_sum(lua_State *L)
{
    guchar *data = (guchar *)luaL_checkstring(L, 1);
    guint32 len = luaL_checkinteger(L, 2);
    guint32 check_sum = 0;
    guint32 i;
    for (i = 0; i < len; i += 2) {
        if (i + 1 < len) {
            guint16 tmp = ((data[i] & 0xFF) << 8) | ((data[i + 1] & 0xFF) >> 0);
            check_sum += tmp;
        } else {
            check_sum += data[i];
        }
    }
    check_sum = (~check_sum) + 1;
    lua_pushinteger(L, check_sum);
    return 1; // 返回1个参数
}

static int l_send_ncsi_cmd(lua_State *L)
{
    guchar *req_packet = (guchar *)luaL_checkstring(L, 1);
    guint32 len = luaL_checkinteger(L, 2);
    gchar *eth_name = (gchar *)luaL_checkstring(L, 3);
    static gint32 sock_fd = -1;
    struct sockaddr_ll sock_addr;
    if (sock_fd == -1) {
        sock_fd = socket(PF_PACKET, SOCK_RAW, htons(NCSI_ETHER_TYPE));
        if (sock_fd < 0) {
            debug_log(DLOG_ERROR, "create ncsi sock failed\n");
            lua_pushinteger(L, 1);
            return 1;
        }
    }
    guint32 if_index = if_nametoindex(eth_name);
    (void)memset_s(&sock_addr, sizeof(sock_addr), 0, sizeof(sock_addr));
    sock_addr.sll_family = AF_PACKET;
    sock_addr.sll_protocol = htons(NCSI_ETHER_TYPE);
    sock_addr.sll_ifindex = if_index;
    if (sendto(sock_fd, (void *)req_packet, len, MSG_DONTWAIT, (struct sockaddr *)&sock_addr, sizeof(sock_addr)) < 0) {
        if (errno == EAGAIN || errno == EWOULDBLOCK) {
            debug_log(DLOG_WARN, "ncsi send buffer is full, errno: %d\n", errno);
        } else {
            debug_log(DLOG_ERROR, "send ncsi cmd failed, errno: %d\n", errno);
        }
        lua_pushinteger(L, 1);
        return 1;
    }
    lua_pushinteger(L, 0);
    return 1; // 返回1个参数
}

static int l_ncsi_recv_packet(lua_State *L)
{
    gint32 sock_fd = luaL_checkinteger(L, 1);

    struct sockaddr_ll sock_addr;
    guint32 sock_addr_len = sizeof(sock_addr);
    guchar recv_data[RECV_BUF_LEN] = {0};
    
    gint32 len = recvfrom(sock_fd, recv_data, RECV_BUF_LEN - 1, 0, (struct sockaddr *)&sock_addr, &sock_addr_len);
    if (len > 0) {
        lua_pushlstring(L, recv_data, len);
    } else {
        lua_pushstring(L, "");
    }
    return 1; // 返回1个参数
}

LUAMOD_API int luaopen_ncsi_protocol_intf(lua_State *L)
{
    luaL_checkversion(L);
    luaL_Reg l[] = {
        {"get_ncsi_check_sum", l_get_ncsi_check_sum},
        {"send_ncsi_cmd", l_send_ncsi_cmd},
        {"ncsi_recv_packet", l_ncsi_recv_packet},
        {NULL, NULL},
    };
    luaL_newlib(L, l);

    return 1;
}
