/* SPDX-License-Identifier: (BSD-2-Clause OR GPL-2.0-or-later)
 *
 * vim:set noexpandtab shiftwidth=8 softtabstop=8 fileencoding=utf-8:
 *
 * Layer 2 network upstream uapi access library
 */

#include <string.h>
#include <stdbool.h>
#include <inttypes.h>
#include <unistd.h>
#include <linux/if_ether.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <err.h>

#include <net_upstream_ioctl.h>

#include "net_upstream_api.h"

int net_us_clear_master(int fd)
{
	if (ioctl(fd, NET_US_IOC_CLR_MASTER, 0) < 0) {
		warn("ioctl(NET_US_IOC_CLR_MASTER): failed");
		return -1;
	}
	return 0;
}

int net_us_set_master(int fd, int ifindex)
{
	if (ioctl(fd, NET_US_IOC_SET_MASTER, (int32_t) ifindex) < 0) {
		warn("ioctl(NET_US_IOC_SET_MASTER): failed");
		return -1;
	}
	return 0;
}

int net_us_rx_clear_all(int fd)
{
	if (ioctl(fd, NET_US_IOC_RXACT_CLEAR_ALL, NULL) < 0) {
		warn("ioctl(NET_US_IOC_RXACT_CLEAR_ALL): failed");
		return -1;
	}
	return 0;
}

static int net_us_rx_add_mac_passthrough(int fd, uint8_t mac[ETH_ALEN], bool check_dest)
{
	struct net_us_mac_passthru param;
	memcpy(&param.mac, mac, ETH_ALEN);
	param.check_dest = check_dest;

	if (ioctl(fd, NET_US_IOC_RXACT_ADD_MAC_PASSTHRU, &param) < 0) {
		warn("ioctl(NET_US_IOC_RXACT_ADD_MAC_PASSTHRU): failed");
		return -1;
	}
	return 0;
}

int net_us_rx_add_mac_passthrough_src(int fd, uint8_t mac[ETH_ALEN])
{
	return net_us_rx_add_mac_passthrough(fd, mac, false);
}

int net_us_rx_add_mac_passthrough_dest(int fd, uint8_t mac[ETH_ALEN])
{
	return net_us_rx_add_mac_passthrough(fd, mac, true);
}

int net_us_tx_clear_all(int fd)
{
	if (ioctl(fd, NET_US_IOC_TXACT_CLEAR_ALL, NULL) < 0) {
		warn("ioctl(NET_US_IOC_TXACT_CLEAR_ALL): failed");
		return -1;
	}
	return 0;
}

int net_us_tx_add_vlanprio_map(int fd, int vlan_start, int vlan_end, uint8_t *map, int map_len)
{
	struct net_us_vlan_prio_map arg;

	if (map_len != 8)
		return -1;

	arg.vlan_start = vlan_start;
	arg.vlan_end = vlan_end;
	memcpy(&arg.priomap, map, map_len);

	if (ioctl(fd, NET_US_IOC_TXACT_ADD_VLANPRIO_MAP, &arg) < 0) {
		warn("ioctl(NET_US_IOC_ADD_VLANPRIO_MAP): failed");
		return -1;
	}
	return 0;
}

int net_us_get_ifindex(int fd)
{
	int32_t ifindex;

	if (ioctl(fd, NET_US_IOC_GET_IFINDEX, &ifindex) < 0) {
		warn("ioctl(NET_US_IOC_GET_IFINDEX): failed");
		return -1;
	} else if (ifindex <= 0) {
		warn("bad ifindex %d", ifindex);
		return -1;
	}

	return ifindex;
}

int net_us_open(void)
{
	int fd = open("/dev/net_us", O_RDWR | O_CLOEXEC);
	if (fd == -1) {
		warn("Can't open /dev/net_us");
		return -1;
	}
	return fd;
}

void net_us_close(int fd)
{
	if (fd != -1)
		close(fd);
}