Skip to content
Snippets Groups Projects
ecnt_prvt.c 17.2 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*
     * ecnt_prvt.c - Econet switch private utilities
     *
     * Copyright (C) 2022 iopsys Software Solutions AB. All rights reserved.
     *
     * Author: maxim.menshikov@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
     */
    #include <ctype.h>
    #include <fcntl.h>
    #include <linux/mii.h>
    #include <net/if.h>
    #include <stdarg.h>
    #include <stdbool.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/ioctl.h>
    #include <sys/socket.h>
    #include <sys/types.h>
    #include <unistd.h>
    
    #include <easy/easy.h>
    
    #include <sys/ioctl.h>
    #include <net/if.h>
    #include <linux/sockios.h>
    
    
    #include "../ethernet.h"
    
    #include "ecnt_prvt.h"
    
    #ifdef STATIC_ANALYSIS
    #include "stub_libapi_lib_switchmgr.h"
    #else
    #include "libapi_lib_switchmgr.h"
    #endif
    
    
    #define TC3162_MAX_LINE_LEN     (100)
    #define TC3162_DUPLEX_MODE_LEN  (32)
    #define TC3162_SPEED_MODE_LEN   (32)
    #define TC3162_ETH_PORTMAP_PATH "/proc/tc3162/eth_portmap"
    #define IFNAME_ETH0             "eth0."
    #define IFNAME_NAS              "nas"
    #define IFNAME_AE_WAN           "ae_wan"
    
    Markus Gothe's avatar
    Markus Gothe committed
    #define IFNAME_PON              "pon"
    
    #define DRIVER_NAME "hsgmii_lan"
    
    #define DRIVER_NAME_LEN 20
    
    struct hsgmii_lookup_table {
    	const unsigned int idx;
    	char *ifnames[IFNAMSIZ];
    
    	const char iftype[IFNAMSIZ];
    
    };
    
    const struct hsgmii_lookup_table hsgmii_lookup_tbl[] = {
    
    	{ 2, { "eth0.5", "eth1", NULL }, "usb" },
    	{ 0, { "eth0.6", "eth2", NULL }, "pcie0" },
    	{ 1, { "eth0.7", "eth3", NULL }, "pcie1" },
    	{ 3, { "eth0.8", "eth4", NULL }, "eth" },
    
    
    /* Not defined in Econet library */
    ECNT_SWITCHMGR_RET switchmgr_lib_get_port_link_state(u8 port,
    	ECNT_SWITCHMGR_LINK_STATE *p_link_state,
    	ECNT_SWITCHMGR_LINK_SPEED *p_speed);
    
    
    static int get_drv_info_by_ifname(char *ifname, char *buffer)
    {
    	int                     fd;
    	int                     ret = -1;
    	struct ifreq            ifr;
    	struct ethtool_drvinfo  info;
    
    	if (ifname == NULL || buffer == NULL)
    		return ret;
    
    	fd = socket(AF_INET, SOCK_DGRAM, 0);
    	if (fd < 0)
    		return ret;
    
    	memset(&info, 0, sizeof(info));
    	info.cmd = ETHTOOL_GDRVINFO;
    
    	memset(&ifr, 0, sizeof(ifr));
    	strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name) - 1);
    	ifr.ifr_data = (void *)&info;
    
    	if (ioctl(fd, SIOCETHTOOL, &ifr) != 0)
    		goto exit;
    
    
    Rahul Thakur's avatar
    Rahul Thakur committed
    	strncpy(buffer, info.driver, DRIVER_NAME_LEN - 1);
    
    static int get_pause_stats_from_proc(const char *ifprefix, const char *iftype,
    				     uint64_t *rx_pause_packets, uint64_t *tx_pause_packets)
    {
    	uint64_t rx_pause_on, rx_pause_off, tx_pause_on, tx_pause_off;
    	char cmdbuf[512] = {0};
    	int ret;
    	chrCmd(cmdbuf, sizeof(cmdbuf),
    		"cat /proc/tc3162/%s%s_mac_dbg | grep PAUSE",
    		ifprefix, iftype);
    
    	if (cmdbuf[0] == '\0')
    		return -1;
    
    	ret = sscanf(cmdbuf, /* Flawfinder: ignore */
    		"TXMBI_PAUSEON_CNT\t\t= %" SCNx64 ", TXMBI_PAUSEOFF_CNT\t\t= %" SCNx64
    		" RX_PAUSEON_CNT\t\t\t= %" SCNx64 ", RX_PAUSEOFF_CNT\t\t\t= %" SCNx64,
    		&tx_pause_on, &tx_pause_off, &rx_pause_on, &rx_pause_off);
    
    	if (ret < 4)
    		return -1;
    
    	*tx_pause_packets = tx_pause_on + tx_pause_off;
    	*rx_pause_packets = rx_pause_on + rx_pause_off;
    
    	return 0;
    }
    
    static void fill_stats_tx_from_gdma(struct eth_stats *stats, struct eth_rmon_stats *rstats,
    				    ECNT_FEMGR_GDMA2_TX_STATISTICS *tx_stats)
    {
    	if (tx_stats == NULL)
    		return;
    
    	if (stats != NULL) {
    
    		stats->tx_bytes = tx_stats->frame_len;
    
    		stats->tx_packets = tx_stats->frame_cnt;
    		stats->tx_errors = 0;
    
    		stats->tx_ucast_packets = tx_stats->frame_cnt - (tx_stats->broadcast + tx_stats->multicast);
    
    		stats->tx_bcast_packets = tx_stats->broadcast;
    		stats->tx_mcast_packets = tx_stats->multicast;
    
    		stats->tx_discard_packets = tx_stats->drop_cnt;
    	}
    
    	if (rstats != NULL) {
    		rstats->tx.packets = tx_stats->frame_cnt;
    
    		rstats->tx.bytes = tx_stats->frame_len;
    
    		rstats->tx.bcast_packets = tx_stats->broadcast;
    		rstats->tx.mcast_packets = tx_stats->multicast;
    		rstats->tx.crc_err_packets = 0;
    		rstats->tx.under_sz_packets = 0;
    		rstats->tx.over_sz_packets = 0;
    		rstats->tx.packets_64bytes = tx_stats->eq_64;
    		rstats->tx.packets_65to127bytes = tx_stats->from_65_to_127;
    		rstats->tx.packets_256to511bytes = tx_stats->from_256_to_511;
    		rstats->tx.packets_512to1023bytes = tx_stats->from_512_to_1023;
    		rstats->tx.packets_1024to1518bytes = tx_stats->from_1024_to_1518;
    	}
    }
    
    static void fill_stats_rx_from_gdma(struct eth_stats *stats, struct eth_rmon_stats *rstats,
    
    				    ECNT_FEMGR_GDMA2_RX_STATISTICS *rx_stats, bool oversize_is_ok)
    
    {
    	if (rx_stats == NULL)
    		return;
    
    	if (stats != NULL) {
    
    		stats->rx_bytes = rx_stats->frame_len;
    
    		stats->rx_packets = rx_stats->frame_cnt;
    
    		stats->rx_errors = rx_stats->crc + rx_stats->jabber + rx_stats->fragment + rx_stats->undersize + (oversize_is_ok == false ? rx_stats->oversize : 0);
    
    		stats->rx_ucast_packets = rx_stats->frame_cnt - (rx_stats->broadcast + rx_stats->multicast);
    
    		stats->rx_bcast_packets = rx_stats->broadcast;
    		stats->rx_mcast_packets = rx_stats->multicast;
    
    		stats->rx_discard_packets = rx_stats->drop_cnt;
    		stats->rx_unknown_packets = 0;
    	}
    
    	if (rstats != NULL) {
    		rstats->rx.packets = rx_stats->frame_cnt;
    
    		rstats->rx.bytes = rx_stats->frame_len;
    
    		rstats->rx.bcast_packets = rx_stats->broadcast;
    		rstats->rx.mcast_packets = rx_stats->multicast;
    
    		rstats->rx.crc_err_packets = rx_stats->crc;
    		rstats->rx.under_sz_packets = rx_stats->undersize;
    		rstats->rx.over_sz_packets = rx_stats->oversize;
    
    		rstats->rx.packets_64bytes = rx_stats->eq_64;
    		rstats->rx.packets_65to127bytes = rx_stats->from_65_to_127;
    		rstats->rx.packets_256to511bytes = rx_stats->from_256_to_511;
    		rstats->rx.packets_512to1023bytes = rx_stats->from_512_to_1023;
    		rstats->rx.packets_1024to1518bytes = rx_stats->from_1024_to_1518;
    	}
    }
    
    
    int hsgmii_lan_prvt_get_port_statistics(char *ifname, struct eth_stats *stats, struct eth_rmon_stats *rstats) {
    	ECNT_FEMGR_GDMA2_TX_STATISTICS tx_stats;
    	ECNT_FEMGR_GDMA2_RX_STATISTICS rx_stats;
    
    
    	char driver_name[DRIVER_NAME_LEN] = {0};
    
    	if (get_drv_info_by_ifname(ifname, driver_name))
    		return -1;
    
    	if (!strncmp(driver_name, DRIVER_NAME, DRIVER_NAME_LEN)) {
    
    		int i = 0, hsgmii_index = -1;
    
    		const char *hsgmii_iftype;
    
    		char cmdbuf[512] = {0};
    
    
    		for (; i < ARRAY_SIZE(hsgmii_lookup_tbl); i++) {
    			int j = 0;
    
    			while (hsgmii_lookup_tbl[i].ifnames[j] != NULL) {
    				if (!strcmp(ifname, hsgmii_lookup_tbl[i].ifnames[j])) {
    					hsgmii_index = hsgmii_lookup_tbl[i].idx;
    
    					hsgmii_iftype = hsgmii_lookup_tbl[i].iftype;
    
    					break;
    				}
    				j++;
    			}
    
    			if (hsgmii_index != -1)
    				break;
    		}
    
    		if (hsgmii_index == -1)
    			return -1;
    
    		memset(&tx_stats, 0, sizeof(ECNT_FEMGR_GDMA2_TX_STATISTICS));
    		memset(&rx_stats, 0, sizeof(ECNT_FEMGR_GDMA2_RX_STATISTICS));
    
    		/* Handle unbound interface statistics */
    		chrCmd(cmdbuf, sizeof(cmdbuf), "cat /proc/tc3162/%s_rebind | awk '{ print $3 }'", ifname);
    		if (cmdbuf[0] != '\0' && strtoul(cmdbuf, NULL, 0) == 1)
    			return 0;
    
    
    		if (!fe_lib_get_hsgmii_tx_statistics(&tx_stats, hsgmii_index)) {
    
    			fill_stats_tx_from_gdma(stats, rstats, &tx_stats);
    		}
    		if (!fe_lib_get_hsgmii_rx_statistics(&rx_stats, hsgmii_index)) {
    
    			fill_stats_rx_from_gdma(stats, rstats, &rx_stats, false);
    
    		if (rstats != NULL) {
    			get_pause_stats_from_proc("hsgmii_", hsgmii_iftype, &rstats->rx.pause_packets,
    						  &rstats->tx.pause_packets);
    
    int ae_wan_prvt_get_port_statistics(struct eth_stats *stats, struct eth_rmon_stats *rstats) {
    
    	char cmdbuf[512] = {0};
    
    	ECNT_FEMGR_GDMA2_TX_STATISTICS tx_stats;
    	ECNT_FEMGR_GDMA2_RX_STATISTICS rx_stats;
    
    	memset(&tx_stats, 0, sizeof(ECNT_FEMGR_GDMA2_TX_STATISTICS));
    	memset(&rx_stats, 0, sizeof(ECNT_FEMGR_GDMA2_RX_STATISTICS));
    
    
    	chrCmd(cmdbuf, sizeof(cmdbuf), "cat /proc/tc3162/ae_wan_switch_hsgmii_lan");
    
    Markus Gothe's avatar
    Markus Gothe committed
    	if (cmdbuf[0] == '\0')  {
    		return -1;
    	} else if (strcmp(cmdbuf, "pon") != 0) {
    
    		int i = 0, hsgmii_index = -1;
    		for (; i < ARRAY_SIZE(hsgmii_lookup_tbl); i++) {
    			if (!strcmp(cmdbuf, hsgmii_lookup_tbl[i].iftype)) {
    				hsgmii_index = hsgmii_lookup_tbl[i].idx;
    				break;
    			}
    		}
    
    		if (hsgmii_index == -1)
    			return -1;
    
    		if (!fe_lib_get_hsgmii_tx_statistics(&tx_stats, hsgmii_index)) {
    			fill_stats_tx_from_gdma(stats, rstats, &tx_stats);
    		}
    		if (!fe_lib_get_hsgmii_rx_statistics(&rx_stats, hsgmii_index)) {
    
    			fill_stats_rx_from_gdma(stats, rstats, &rx_stats, false);
    
    		}
    
    		if (rstats != NULL) {
    			get_pause_stats_from_proc("hsgmii_", cmdbuf, &rstats->rx.pause_packets,
    						  &rstats->tx.pause_packets);
    		}
    	} else {
    		if (!fe_lib_get_gdma2_tx_statistics(&tx_stats)) {
    			fill_stats_tx_from_gdma(stats, rstats, &tx_stats);
    		}
    
    		if (!fe_lib_get_gdma2_rx_statistics(&rx_stats)) {
    
    			fill_stats_rx_from_gdma(stats, rstats, &rx_stats, false);
    
    		}
    
    		if (rstats != NULL) {
    			get_pause_stats_from_proc("", "ae_wan", &rstats->rx.pause_packets,
    						  &rstats->tx.pause_packets);
    		}
    
    Markus Gothe's avatar
    Markus Gothe committed
    int pon_prvt_get_port_statistics(struct eth_stats *stats, struct eth_rmon_stats *rstats) {
    	ECNT_FEMGR_GDMA2_TX_STATISTICS tx_stats;
    	ECNT_FEMGR_GDMA2_RX_STATISTICS rx_stats;
    	char driver_name[DRIVER_NAME_LEN] = {0};
    
    	if (get_drv_info_by_ifname(IFNAME_PON, driver_name))
    		return -1;
    
    	memset(&tx_stats, 0, sizeof(ECNT_FEMGR_GDMA2_TX_STATISTICS));
    	memset(&rx_stats, 0, sizeof(ECNT_FEMGR_GDMA2_RX_STATISTICS));
    
    	if (!fe_lib_get_gdma2_tx_statistics(&tx_stats)) {
    		fill_stats_tx_from_gdma(stats, rstats, &tx_stats);
    	}
    
    	if (!fe_lib_get_gdma2_rx_statistics(&rx_stats)) {
    
    		fill_stats_rx_from_gdma(stats, rstats, &rx_stats, true);
    
    int ecnt_prvt_get_port_statistics(uint32_t port,
    								  struct eth_stats *stats,
    								  struct eth_rmon_stats *rstats)
    {
    	int ret;
    	ECNT_SWITCHMGR_PORT_STATISTICS portcnt;
    
    	if (port == ECNT_PRVT_PORT_NUM_INVALID ||
    		(stats == NULL && rstats == NULL)) {
    		return -1;
    	}
    
    	if (port >= 256) {
    		return -1;
    	}
    
    	memset(&portcnt, 0, sizeof(portcnt));
    
    
    	ret = switchmgr_lib_get_port_statistics((uint8_t)port, &portcnt);
    
    	if (ret != ECNT_SWITCHMGR_SUCCESS) {
    		return -1;
    	}
    
    	if (stats != NULL) {
    
    		stats->tx_bytes = (((uint64_t)portcnt.TxBytesCnt_Hi) << 32) + portcnt.TxBytesCnt_Lo;
    		stats->rx_bytes = (((uint64_t)portcnt.RxBytesCnt_Hi) << 32) + portcnt.RxBytesCnt_Lo;
    
    		stats->tx_packets = portcnt.TxPktsCnt;
    		stats->rx_packets = portcnt.RxPktsCnt;
    		stats->tx_errors = 0;
    
    		stats->rx_errors = portcnt.RxAlignmentErrorCnt + portcnt.RxCRCFramesCnt + portcnt.RxUnderSizePktsCnt + portcnt.RxFragmentErrorCnt + portcnt.RxOverSizePktsCnt;
    
    		stats->tx_ucast_packets = portcnt.TxUniPktsCnt;
    		stats->rx_ucast_packets = portcnt.RxUniPktsCnt;
    		stats->tx_mcast_packets = portcnt.TxMultiPktsCnt;
    		stats->rx_mcast_packets = portcnt.RxMultiPktsCnt;
    		stats->tx_bcast_packets = portcnt.TxBroadPktsCnt;
    		stats->rx_bcast_packets = portcnt.RxBroadPktsCnt;
    		stats->tx_discard_packets = portcnt.TxDropFramesCnt;
    		stats->rx_discard_packets = portcnt.RxDropFramesCnt;
    		stats->rx_unknown_packets = 0;
    	}
    
    	if (rstats != NULL) {
    #define FILL_RSTATS_FOR_DIRECTION(__rmon_field, __ecnt_prefix) \
    		do { \
    
    			rstats->__rmon_field.drop_events = \
    
    				portcnt.__ecnt_prefix ## DropFramesCnt; \
    			rstats->__rmon_field.bytes = \
    
    				(((uint64_t)portcnt.__ecnt_prefix ## BytesCnt_Hi) << 32) \
    				+ portcnt.__ecnt_prefix ## BytesCnt_Lo; \
    
    			rstats->__rmon_field.packets = portcnt.__ecnt_prefix ## PktsCnt;\
    			rstats->__rmon_field.bcast_packets = \
    				portcnt.__ecnt_prefix ## BroadPktsCnt; \
    			rstats->__rmon_field.mcast_packets = \
    				portcnt.__ecnt_prefix ## MultiPktsCnt; \
    			rstats->__rmon_field.crc_err_packets = \
    				portcnt.__ecnt_prefix ## CRCFramesCnt; \
    			rstats->__rmon_field.under_sz_packets = \
    				portcnt.__ecnt_prefix ## UnderSizePktsCnt; \
    			rstats->__rmon_field.over_sz_packets = \
    				portcnt.__ecnt_prefix ## OverSizePktsCnt; \
    
    			rstats->__rmon_field.pause_packets = \
    				portcnt.__ecnt_prefix ## PauseFramesCnt; \
    
    			rstats->__rmon_field.packets_64bytes = \
    				portcnt.__ecnt_prefix ## 64BytePktsCnt; \
    			rstats->__rmon_field.packets_65to127bytes = \
    				portcnt.__ecnt_prefix ## 65_127BytePktsCnt; \
    			rstats->__rmon_field.packets_256to511bytes = \
    				portcnt.__ecnt_prefix ## 256_511BytePktsCnt; \
    			rstats->__rmon_field.packets_512to1023bytes = \
    				portcnt.__ecnt_prefix ## 512_1023BytePktsCnt; \
    			rstats->__rmon_field.packets_1024to1518bytes = \
    				portcnt.__ecnt_prefix ## 1024_1518BytePktsCnt; \
    		} while (0)
    
    		FILL_RSTATS_FOR_DIRECTION(tx, Tx);
    		FILL_RSTATS_FOR_DIRECTION(rx, Rx);
    
    #undef FILL_RSTATS_FOR_DIRECTION
    	}
    
    	return 0;
    }
    
    /* Read port number from proc interface */
    static uint32_t read_port_from_proc(bool wan, uint32_t eth_port_num)
    {
    	FILE    *f;
    	int      tmp;
    	int      ret;
    	char     buf[TC3162_MAX_LINE_LEN];
    	uint32_t new_port_num = ECNT_PRVT_PORT_NUM_INVALID;
    
    	f = fopen(TC3162_ETH_PORTMAP_PATH, "r");
    	if (f == NULL) {
    		return ECNT_PRVT_PORT_NUM_INVALID;
    	}
    
    	if (wan) {
    		/* WAN is the very first in the proc dump */
    		ret = fscanf(f, "%d", &tmp);
    		if (ret != 1) {
    			goto out;
    		}
    
    		new_port_num = (uint32_t)tmp;
    	} else {
    		char   *retstr;
    		bool    inside_map = false;
    
    		/* First we search for the beginning of switch port map */
    		while (true) {
    			retstr = fgets(buf, sizeof(buf), f);
    			if (retstr == NULL) {
    				break;
    			}
    
    			inside_map = strstr(buf, "switch_port_map") != NULL;
    			if (inside_map) {
    				break;
    			}
    		}
    
    		if (!inside_map) {
    			goto out;
    		}
    
    		/*
    		 * Then we sequentially read all ports until the needed port line is
    		 * found
    		 * Line format: <virtual port> <switch port>
    		 */
    		while (true) {
    			int port;
    			int swport;
    
    			if (fscanf(f, "%d %d", &port, &swport) != 2) {
    				break;
    			}
    
    			if (port == eth_port_num) {
    
    				new_port_num = (uint32_t)swport;
    
    				goto out;
    			}
    		}
    	}
    
    out:
    	fclose(f);
    
    	return new_port_num;
    }
    
    uint32_t ecnt_prvt_get_port_num(const char *ifname)
    {
    	bool     is_wan;
    	uint32_t eth_port_num = 0;
    	/*
    	 * This algorithm is not accurate if numbers differ for target hardware.
    	 * There must be a generic way to get port IDs reliably based on interface
    	 * names.
    	 */
    	if (strncmp(ifname, IFNAME_ETH0, strlen(IFNAME_ETH0)) == 0) {
    		char *end;
    
    		is_wan = false;
    
    		eth_port_num = (uint32_t)strtol(&ifname[strlen(IFNAME_ETH0)], &end, 10);
    
    
    		if (end == NULL || *end != '\0') {
    			return ECNT_PRVT_PORT_NUM_INVALID;
    		}
    	} else if (strncmp(ifname, IFNAME_NAS, strlen(IFNAME_NAS)) == 0 ||
    
    Markus Gothe's avatar
    Markus Gothe committed
    			   strncmp(ifname, IFNAME_AE_WAN, strlen(IFNAME_AE_WAN)) == 0 ||
    			   strncmp(ifname, IFNAME_PON, strlen(IFNAME_PON)) == 0) {
    
    		is_wan = true;
    	} else {
    		return ECNT_PRVT_PORT_NUM_INVALID;
    	}
    
    	return read_port_from_proc(is_wan, eth_port_num);
    }
    
    
    enum eth_speed
    ecnt_prvt_link_speed2lib(int speed)
    {
    	switch (speed)
    	{
    #define MAP_ECNT_LINK_SPEED(__val, __speed, __duplex) \
    		case (__val): \
    		{ \
    			return (__speed); \
    		}
    #include "map_link_speed.def"
    #undef MAP_ECNT_LINK_SPEED
    
    		default:
    			return ETH_10_Half;
    	}
    }
    
    uint32_t
    ecnt_prvt_bitrate2capability(char *speed_str, char *duplex_mode_str)
    {
    	int   speed;
    	bool  full_duplex;
    	char *end;
    
    	full_duplex = strcmp(duplex_mode_str, "Full") == 0;
    
    	if (strcmp(speed_str, "Auto") == 0) {
    		goto err;
    	}
    
    	speed = strtol(speed_str, &end, 10);
    	if (end == NULL || *end != '\0') {
    		goto err;
    	}
    
    	/* Convert bitrate and duplex mode to actual capability */
    #define MAP_ECNT_BITRATE_RANGE_BOUNDARY(__bound, __full_cap, __half_cap) \
    	do { \
    		if (speed >= (__bound)) { \
    			if (full_duplex) { \
    				return __full_cap; \
    			} else { \
    				return __half_cap; \
    			} \
    		} \
    	} while (0);
    #include "map_bitrate.def"
    #undef MAP_ECNT_BITRATE_RANGE_BOUNDARY
    
    err:
    	/* Fail-safe defaults */
    	return full_duplex ? ETH_100_Full : ETH_100_Half;
    }
    
    int ecnt_prvt_get_link_settings(uint32_t port_num, struct eth_link *link)
    {
    	uint8_t  up;
    	uint8_t  autoneg;
    	char     duplex_mode[TC3162_DUPLEX_MODE_LEN];
    	char     speed_str[TC3162_SPEED_MODE_LEN];
    	ECNT_SWITCHMGR_LINK_STATE link_state;
    	ECNT_SWITCHMGR_LINK_SPEED link_speed;
    
    	if (port_num >= 256) {
    		return -1;
    	}
    
    
    	if (switchmgr_lib_get_port_admin((uint8_t)port_num, &up) !=
    
    			ECNT_SWITCHMGR_SUCCESS ||
    
    		switchmgr_lib_get_port_autoneg_enable((uint8_t)port_num, &autoneg) !=
    
    			ECNT_SWITCHMGR_SUCCESS ||
    
    		switchmgr_lib_get_port_link_state((uint8_t)port_num, &link_state, &link_speed) !=
    
    			ECNT_SWITCHMGR_SUCCESS ||
    
    		switchmgr_lib_get_port_duplex((uint8_t)port_num, duplex_mode) !=
    
    			ECNT_SWITCHMGR_SUCCESS ||
    
    		switchmgr_lib_get_port_max_bitrate((uint8_t)port_num, speed_str) !=
    
    			ECNT_SWITCHMGR_SUCCESS) {
    		return -1;
    	}
    
    
    	link->portid = (int)port_num;
    
    	link->capability = ecnt_prvt_bitrate2capability(speed_str, duplex_mode);
    	link->autoneg = autoneg;
    	link->speed = ecnt_prvt_link_speed2lib(link_speed);
    	link->fullduplex = (strcmp(duplex_mode, "Full") == 0) ? true : false;
    	link->down = (link_state == ECNT_SWITCHMGR_LINK_UP) ? false : true;
    	link->prio_tagged = false;
    
    	return 0;
    }
    
    int ecnt_prvt_set_port_state(uint32_t port_num, bool state)
    {
    	int ret;
    
    	if (port_num >= 256) {
    		return -1;
    	}
    
    
    	ret = switchmgr_lib_set_port_admin((uint8_t)port_num, state ? 1 : 0);
    
    
    	return (ret == ECNT_SWITCHMGR_SUCCESS) ? 0 : (-1);
    }