diff options
Diffstat (limited to 'platform/linux-generic/pktio/socket_common.c')
-rw-r--r-- | platform/linux-generic/pktio/socket_common.c | 139 |
1 files changed, 137 insertions, 2 deletions
diff --git a/platform/linux-generic/pktio/socket_common.c b/platform/linux-generic/pktio/socket_common.c index 4fbf2f041..2eb3b4001 100644 --- a/platform/linux-generic/pktio/socket_common.c +++ b/platform/linux-generic/pktio/socket_common.c @@ -1,5 +1,5 @@ /* Copyright (c) 2018, Linaro Limited - * Copyright (c) 2019, Nokia + * Copyright (c) 2019-2020, Nokia * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause @@ -14,7 +14,9 @@ #include <sys/types.h> #include <sys/ioctl.h> #include <net/if.h> +#include <linux/ethtool.h> #include <linux/if_packet.h> +#include <linux/sockios.h> #include <errno.h> #include <odp_debug_internal.h> #include <odp_errno_define.h> @@ -155,8 +157,141 @@ int link_status_fd(int fd, const char *name) __odp_errno = errno; ODP_DBG("ioctl(SIOCGIFFLAGS): %s: \"%s\".\n", strerror(errno), ifr.ifr_name); + return ODP_PKTIO_LINK_STATUS_UNKNOWN; + } + + if (ifr.ifr_flags & IFF_RUNNING) + return ODP_PKTIO_LINK_STATUS_UP; + return ODP_PKTIO_LINK_STATUS_DOWN; +} + +int link_info_fd(int fd, const char *name, odp_pktio_link_info_t *info) +{ + struct ethtool_link_settings hcmd = {.cmd = ETHTOOL_GLINKSETTINGS}; + struct ethtool_link_settings *ecmd; + struct ethtool_pauseparam pcmd = {.cmd = ETHTOOL_GPAUSEPARAM}; + struct ifreq ifr; + int status; + + status = link_status_fd(fd, name); + if (status < 0) + return -1; + + snprintf(ifr.ifr_name, IF_NAMESIZE, "%s", name); + + /* Link pause status */ + ifr.ifr_data = (void *)&pcmd; + if (ioctl(fd, SIOCETHTOOL, &ifr) && errno != EOPNOTSUPP) { + __odp_errno = errno; + ODP_ERR("ioctl(SIOCETHTOOL): %s: \"%s\".\n", strerror(errno), + ifr.ifr_name); + return -1; + } + + /* Try to perform handshake and fall back to old API if failed */ + ifr.ifr_data = (void *)&hcmd; + if (ioctl(fd, SIOCETHTOOL, &ifr) < 0) { + struct ethtool_cmd ecmd_old = {.cmd = ETHTOOL_GSET}; + + ifr.ifr_data = (void *)&ecmd_old; + if (ioctl(fd, SIOCETHTOOL, &ifr) < 0) { + __odp_errno = errno; + ODP_ERR("ioctl(SIOCETHTOOL): %s: \"%s\".\n", strerror(errno), ifr.ifr_name); + return -1; + } + + memset(info, 0, sizeof(odp_pktio_link_info_t)); + info->speed = ethtool_cmd_speed(&ecmd_old); + if (info->speed == (uint32_t)SPEED_UNKNOWN) + info->speed = ODP_PKTIO_LINK_SPEED_UNKNOWN; + + if (ecmd_old.autoneg == AUTONEG_ENABLE) + info->autoneg = ODP_PKTIO_LINK_AUTONEG_ON; + else if (ecmd_old.autoneg == AUTONEG_DISABLE) + info->autoneg = ODP_PKTIO_LINK_AUTONEG_OFF; + else + info->autoneg = ODP_PKTIO_LINK_AUTONEG_UNKNOWN; + + if (ecmd_old.duplex == DUPLEX_HALF) + info->duplex = ODP_PKTIO_LINK_DUPLEX_HALF; + else if (ecmd_old.duplex == DUPLEX_FULL) + info->duplex = ODP_PKTIO_LINK_DUPLEX_FULL; + else + info->duplex = ODP_PKTIO_LINK_DUPLEX_UNKNOWN; + + info->pause_rx = pcmd.rx_pause ? ODP_PKTIO_LINK_PAUSE_ON : ODP_PKTIO_LINK_PAUSE_OFF; + info->pause_tx = pcmd.tx_pause ? ODP_PKTIO_LINK_PAUSE_ON : ODP_PKTIO_LINK_PAUSE_OFF; + + if (ecmd_old.port == PORT_TP) + info->media = "copper"; + else if (ecmd_old.port == PORT_FIBRE) + info->media = "fiber"; + else if (ecmd_old.port == PORT_OTHER) + info->media = "other"; + else + info->media = "unknown"; + + info->status = status; + + return 0; + } + + if (hcmd.link_mode_masks_nwords >= 0 || hcmd.cmd != ETHTOOL_GLINKSETTINGS) { + ODP_ERR("ETHTOOL_GLINKSETTINGS handshake failed\n"); + return -1; + } + /* Absolute value indicates kernel recommended 'link_mode_masks_nwords' value. */ + hcmd.link_mode_masks_nwords = -hcmd.link_mode_masks_nwords; + + /* Reserve space for the three bitmasks (map_supported, map_advertising, map_lp_advertising) + * at the end of struct ethtool_link_settings. 'link_mode_masks_nwords' defines the bitmask + * length in 32-bit words. */ + uint8_t ODP_ALIGNED_CACHE data[offsetof(struct ethtool_link_settings, link_mode_masks) + + (3 * sizeof(uint32_t) * hcmd.link_mode_masks_nwords)]; + + ecmd = (void *)data; + *ecmd = hcmd; + ifr.ifr_data = (void *)ecmd; + if (ioctl(fd, SIOCETHTOOL, &ifr) < 0) { + __odp_errno = errno; + ODP_ERR("ioctl(SIOCETHTOOL): %s: \"%s\".\n", strerror(errno), + ifr.ifr_name); return -1; } - return !!(ifr.ifr_flags & IFF_RUNNING); + memset(info, 0, sizeof(odp_pktio_link_info_t)); + if (ecmd->speed == (uint32_t)SPEED_UNKNOWN) + info->speed = ODP_PKTIO_LINK_SPEED_UNKNOWN; + else + info->speed = ecmd->speed; + + if (ecmd->autoneg == AUTONEG_ENABLE) + info->autoneg = ODP_PKTIO_LINK_AUTONEG_ON; + else if (ecmd->autoneg == AUTONEG_DISABLE) + info->autoneg = ODP_PKTIO_LINK_AUTONEG_OFF; + else + info->autoneg = ODP_PKTIO_LINK_AUTONEG_UNKNOWN; + + if (ecmd->duplex == DUPLEX_HALF) + info->duplex = ODP_PKTIO_LINK_DUPLEX_HALF; + else if (ecmd->duplex == DUPLEX_FULL) + info->duplex = ODP_PKTIO_LINK_DUPLEX_FULL; + else + info->duplex = ODP_PKTIO_LINK_DUPLEX_UNKNOWN; + + info->pause_rx = pcmd.rx_pause ? ODP_PKTIO_LINK_PAUSE_ON : ODP_PKTIO_LINK_PAUSE_OFF; + info->pause_tx = pcmd.tx_pause ? ODP_PKTIO_LINK_PAUSE_ON : ODP_PKTIO_LINK_PAUSE_OFF; + + if (ecmd->port == PORT_TP) + info->media = "copper"; + else if (ecmd->port == PORT_FIBRE) + info->media = "fiber"; + else if (ecmd->port == PORT_OTHER) + info->media = "other"; + else + info->media = "unknown"; + + info->status = status; + + return 0; } |