/*
 * ethernet.h - libethernet header file
 *
 * Copyright (C) 2020 iopsys Software Solutions AB. All rights reserved.
 *
 * Author: anjan.chanda@iopsys.eu
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 */
#ifndef _ETHERNET_H
#define _ETHERNET_H

#include <stdint.h>
#include <stdbool.h>
#include <linux/types.h>


#ifdef __cplusplus
extern "C" {
#endif

#define libethernet_err(...)	pr_error("libethernet: " __VA_ARGS__)
#define libethernet_warn(...)	pr_warn("libethernet: " __VA_ARGS__)
#define libethernet_info(...)	pr_info("libethernet: " __VA_ARGS__)
#define libethernet_dbg(...)	pr_debug("libethernet: " __VA_ARGS__)

/* enum eth_duplex - duplex modes */
enum eth_duplex {
	AUTO_DUPLEX,
	HALF_DUPLEX,
	FULL_DUPLEX,
};

/* enum eth_rmon_status - rmon status */
enum rmon_status {
	RMON_STATUS_DISABLED,
	RMON_STATUS_ENABLED,
	RMON_STATUS_ERR_MISCONF,
	RMON_STATUS_ERR,
};

/* struct eth_rmon_stats - ethernet rmon counters */
struct eth_rmon_stats {
#define ETH_TXQ_ALL	-1
	int txq;                        /* tx priority queue number or -1 */
	uint16_t vlanid;                /* 0..4094 */
	enum rmon_status status;
	struct {
		uint64_t drop_events;
		uint64_t bytes;
		uint64_t packets;
		uint64_t bcast_packets;
		uint64_t mcast_packets;
		uint64_t crc_err_packets;
		uint64_t under_sz_packets;
		uint64_t over_sz_packets;
		uint64_t packets_64bytes;
		uint64_t packets_65to127bytes;
		uint64_t packets_128to255bytes;
		uint64_t packets_256to511bytes;
		uint64_t packets_512to1023bytes;
		uint64_t packets_1024to1518bytes;
		uint64_t pause_packets;
	} tx, rx;
};


#define ETH_PORT_UNDEFINED	-1

enum eth_speed {
	ETH_10_Half	= (1 << 0),
	ETH_10_Full	= (1 << 1),
	ETH_100_Half	= (1 << 2),
	ETH_100_Full	= (1 << 3),
	ETH_1000_Half	= (1 << 4),
	ETH_1000_Full	= (1 << 5),
	ETH_2500_Full	= (1 << 7),
	ETH_5000_Full	= (1 << 11),
	ETH_10000_Full	= (1 << 15),
	ETH_20000_Full	= (1 << 23),
};

struct eth_phy {
	uint8_t address;
	int portidx;
};

struct eth_info {
	int phy_portmap;
	int phy_numports;
};

enum eth_callstatus {
	ETH_SUCCESS,
	ETH_ERROR,
	ETH_EINVAL,
	ETH_EUNSUPPORTED,
	ETH_EUNKNOWN
};

struct eth_stats {
	uint64_t tx_bytes;
	uint64_t rx_bytes;
	uint64_t tx_packets;
	uint64_t rx_packets;
	uint64_t tx_errors;                 /* error packets */
	uint64_t rx_errors;
	uint64_t tx_ucast_packets;     /* unicast packets */
	uint64_t rx_ucast_packets;
	uint64_t tx_mcast_packets;     /* multicast packets */
	uint64_t rx_mcast_packets;
	uint64_t tx_bcast_packets;     /* broadcast packets */
	uint64_t rx_bcast_packets;
	uint64_t tx_discard_packets;        /* no error packets dropped */
	uint64_t rx_discard_packets;        /* no error packets dropped */
	uint64_t rx_unknown_packets;        /* unknown protocol packets */
};

/* struct eth_vlan - vlan over eth_link */
struct eth_vlan {
	ifopstatus_t status;
	uint16_t id;                       /* vlan id [1..4094] */
	uint16_t tpid;                     /* tag protocol id, t.x. 0x8100 */
	struct eth_stats stats;
};

//TODO: revisit
/* struct eth_link - logical link per ethernet port */
struct eth_link {
	int portid;
	uint32_t capability;
	bool autoneg;
	uint32_t speed;
	bool fullduplex;
	bool down;
	bool prio_tagged;
	struct eth_stats stats;
};

/* struct eth_iface - ethernet interface */
struct eth_iface {
	ifopstatus_t status;
	uint32_t lastchange;              /* in secs */
	bool is_upstream;
	uint8_t macaddr[6];
	uint32_t max_bitrate;             /* in Mbps */
	uint32_t curr_bitrate;            /* in Mbps */
	enum eth_duplex duplex;
	bool eee_supported;
	bool eee_enabled;
	struct eth_stats stats;
};

/* struct eth_device - ethernet device */
struct eth_device {
	bool wol_supported;
	uint32_t num_ifaces;
	uint32_t num_links;
	uint32_t num_vlans;
	struct eth_rmon_stats rmon;
	struct eth_iface *ifs;
	struct eth_link *links;
	struct eth_vlan *vlans;
};



#ifndef if_mii
static inline struct mii_ioctl_data *if_mii(struct ifreq *rq)
{
	return (struct mii_ioctl_data *) &rq->ifr_ifru;
}
#endif


/* TODO: eth_device/sw_ops, eth_iface_ops, eth_link_ops */

struct eth_ops {
	/** interface name/prefix to match */
	const char *ifname;

	int (*up)(const char *ifname);
	int (*down)(const char *ifname);

	int (*get_link_settings)(const char *ifname, struct eth_link *link);
	int (*set_link_settings)(const char *ifname, struct eth_link link);

	int (*get_operstate)(const char *ifname, ifopstatus_t *s);
	int (*set_operstate)(const char *ifname, ifopstatus_t s);

	int (*reset_phy)(const char *ifname, int phy_id);
	int (*poweroff_phy)(const char *ifname, struct eth_phy p);
	int (*poweron_phy)(const char *ifname, struct eth_phy p);
	int (*get_phy_id)(const char *ifname, int port, int *phy_id);

	int (*get_stats)(const char *ifname, struct eth_stats *s);
	int (*get_info)(const char *ifname, struct eth_info *info);
	int (*get_rmon_stats)(const char *ifname, struct eth_rmon_stats *rmon);

	int (*clear_stats)(const char *ifname);
};


/* API list */
int eth_up(const char *ifname);
int eth_down(const char *ifname);
int eth_get_link_settings(const char *ifname, struct eth_link *link);
int eth_set_link_settings(const char *ifname, struct eth_link link);
int eth_get_operstate(const char *ifname, ifopstatus_t *s);
int eth_set_operstate(const char *ifname, ifopstatus_t s);
int eth_get_stats(const char *ifname, struct eth_stats *c);
int eth_get_info(const char *ifname, struct eth_info *info);
int eth_get_rmon_stats(const char *ifname, struct eth_rmon_stats *rmon);
int eth_clear_stats(const char *ifname);

int eth_reset_phy(const char *ifname, int phy_id);
int eth_poweroff_phy(const char *ifname, struct eth_phy p);
int eth_poweron_phy(const char *ifname, struct eth_phy p);

int eth_get_phy_id(const char *ifname, int port, int *phy_id);

int eth_ioctl(const char *ifname, int cmd, void *in, int len);
int eth_mii_read(const char *ifname, int phy_id, int reg, int *out);
int eth_mii_write(const char *ifname, int phy_id, int reg, int in);
int eth_mii_get_phy_id(const char *ifname, int port, int *phy_id);
int eth_mii_reset_phy(const char *ifname, int phy_id);

#ifdef __cplusplus
}
#endif
#endif /* _ETHERNET_H */