Newer
Older
/*
* 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 <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
#include "libapi_lib_fe.h"
#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"
#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;
ret = 0;
exit:
close(fd);
return ret;
}
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
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;
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);
return 0;
}
int ae_wan_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;
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");
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);
}
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 { \
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; \
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
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 ||
strncmp(ifname, IFNAME_AE_WAN, strlen(IFNAME_AE_WAN)) == 0 ||
strncmp(ifname, IFNAME_PON, strlen(IFNAME_PON)) == 0) {
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
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) !=
switchmgr_lib_get_port_autoneg_enable((uint8_t)port_num, &autoneg) !=
switchmgr_lib_get_port_link_state((uint8_t)port_num, &link_state, &link_speed) !=
switchmgr_lib_get_port_duplex((uint8_t)port_num, duplex_mode) !=
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);
}