📄 ethtool.c
字号:
/******************************************************************************* Intel PRO/1000 Linux driver Copyright(c) 1999 - 2008 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: Linux NICS <linux.nics@intel.com> e1000-devel Mailing List <e1000-devel@lists.sourceforge.net> Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497*******************************************************************************//* ethtool support for e1000 */#include <linux/netdevice.h>#ifdef SIOCETHTOOL#include <linux/ethtool.h>#include <linux/pci.h>#include <linux/delay.h>#include "e1000.h"#ifdef NETIF_F_HW_VLAN_TX#include <linux/if_vlan.h>#endif#ifdef ETHTOOL_OPS_COMPAT#include "kcompat_ethtool.c"#endifstruct e1000_stats { char stat_string[ETH_GSTRING_LEN]; int sizeof_stat; int stat_offset;};#define E1000_STAT(m) sizeof(((struct e1000_adapter *)0)->m), \ offsetof(struct e1000_adapter, m)static const struct e1000_stats e1000_gstrings_stats[] = { { "rx_packets", E1000_STAT(stats.gprc) }, { "tx_packets", E1000_STAT(stats.gptc) }, { "rx_bytes", E1000_STAT(stats.gorc) }, { "tx_bytes", E1000_STAT(stats.gotc) }, { "rx_broadcast", E1000_STAT(stats.bprc) }, { "tx_broadcast", E1000_STAT(stats.bptc) }, { "rx_multicast", E1000_STAT(stats.mprc) }, { "tx_multicast", E1000_STAT(stats.mptc) }, { "rx_errors", E1000_STAT(net_stats.rx_errors) }, { "tx_errors", E1000_STAT(net_stats.tx_errors) }, { "tx_dropped", E1000_STAT(net_stats.tx_dropped) }, { "multicast", E1000_STAT(stats.mprc) }, { "collisions", E1000_STAT(stats.colc) }, { "rx_length_errors", E1000_STAT(net_stats.rx_length_errors) }, { "rx_over_errors", E1000_STAT(net_stats.rx_over_errors) }, { "rx_crc_errors", E1000_STAT(stats.crcerrs) }, { "rx_frame_errors", E1000_STAT(net_stats.rx_frame_errors) }, { "rx_no_buffer_count", E1000_STAT(stats.rnbc) }, { "rx_missed_errors", E1000_STAT(stats.mpc) }, { "tx_aborted_errors", E1000_STAT(stats.ecol) }, { "tx_carrier_errors", E1000_STAT(stats.tncrs) }, { "tx_fifo_errors", E1000_STAT(net_stats.tx_fifo_errors) }, { "tx_heartbeat_errors", E1000_STAT(net_stats.tx_heartbeat_errors) }, { "tx_window_errors", E1000_STAT(stats.latecol) }, { "tx_abort_late_coll", E1000_STAT(stats.latecol) }, { "tx_deferred_ok", E1000_STAT(stats.dc) }, { "tx_single_coll_ok", E1000_STAT(stats.scc) }, { "tx_multi_coll_ok", E1000_STAT(stats.mcc) }, { "tx_timeout_count", E1000_STAT(tx_timeout_count) }, { "tx_restart_queue", E1000_STAT(restart_queue) }, { "rx_long_length_errors", E1000_STAT(stats.roc) }, { "rx_short_length_errors", E1000_STAT(stats.ruc) }, { "rx_align_errors", E1000_STAT(stats.algnerrc) }, { "tx_tcp_seg_good", E1000_STAT(stats.tsctc) }, { "tx_tcp_seg_failed", E1000_STAT(stats.tsctfc) }, { "rx_flow_control_xon", E1000_STAT(stats.xonrxc) }, { "rx_flow_control_xoff", E1000_STAT(stats.xoffrxc) }, { "tx_flow_control_xon", E1000_STAT(stats.xontxc) }, { "tx_flow_control_xoff", E1000_STAT(stats.xofftxc) }, { "rx_long_byte_count", E1000_STAT(stats.gorc) }, { "rx_csum_offload_good", E1000_STAT(hw_csum_good) }, { "rx_csum_offload_errors", E1000_STAT(hw_csum_err) }, { "rx_header_split", E1000_STAT(rx_hdr_split) }, { "alloc_rx_buff_failed", E1000_STAT(alloc_rx_buff_failed) }, { "tx_smbus", E1000_STAT(stats.mgptc) }, { "rx_smbus", E1000_STAT(stats.mgprc) }, { "dropped_smbus", E1000_STAT(stats.mgpdc) }, { "rx_dma_failed", E1000_STAT(rx_dma_failed) }, { "tx_dma_failed", E1000_STAT(tx_dma_failed) },};#define E1000_GLOBAL_STATS_LEN \ sizeof(e1000_gstrings_stats) / sizeof(struct e1000_stats)#define E1000_STATS_LEN (E1000_GLOBAL_STATS_LEN)static const char e1000_gstrings_test[][ETH_GSTRING_LEN] = { "Register test (offline)", "Eeprom test (offline)", "Interrupt test (offline)", "Loopback test (offline)", "Link test (on/offline)"};#define E1000_TEST_LEN ARRAY_SIZE(e1000_gstrings_test)static int e1000_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd){ struct e1000_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; u32 status; if (hw->phy.media_type == e1000_media_type_copper) { ecmd->supported = (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | SUPPORTED_TP); if (hw->phy.type == e1000_phy_ife) ecmd->supported &= ~SUPPORTED_1000baseT_Full; ecmd->advertising = ADVERTISED_TP; if (hw->mac.autoneg == 1) { ecmd->advertising |= ADVERTISED_Autoneg; /* the e1000 autoneg seems to match ethtool nicely */ ecmd->advertising |= hw->phy.autoneg_advertised; } ecmd->port = PORT_TP; ecmd->phy_address = hw->phy.addr; ecmd->transceiver = XCVR_INTERNAL; } else { ecmd->supported = (SUPPORTED_1000baseT_Full | SUPPORTED_FIBRE | SUPPORTED_Autoneg); ecmd->advertising = (ADVERTISED_1000baseT_Full | ADVERTISED_FIBRE | ADVERTISED_Autoneg); ecmd->port = PORT_FIBRE; ecmd->transceiver = XCVR_EXTERNAL; } status = er32(STATUS); if (status & E1000_STATUS_LU) { if (status & E1000_STATUS_SPEED_1000) ecmd->speed = 1000; else if (status & E1000_STATUS_SPEED_100) ecmd->speed = 100; else ecmd->speed = 10; if (status & E1000_STATUS_FD) ecmd->duplex = DUPLEX_FULL; else ecmd->duplex = DUPLEX_HALF; } else { ecmd->speed = -1; ecmd->duplex = -1; } ecmd->autoneg = ((hw->phy.media_type == e1000_media_type_fiber) || hw->mac.autoneg) ? AUTONEG_ENABLE : AUTONEG_DISABLE; return 0;}static u32 e1000_get_link(struct net_device *netdev){ struct e1000_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; u32 status; status = er32(STATUS); return (status & E1000_STATUS_LU);}static int e1000_set_spd_dplx(struct e1000_adapter *adapter, u16 spddplx){ struct e1000_mac_info *mac = &adapter->hw.mac; mac->autoneg = 0; /* Fiber NICs only allow 1000 gbps Full duplex */ if ((adapter->hw.phy.media_type == e1000_media_type_fiber) && spddplx != (SPEED_1000 + DUPLEX_FULL)) { e_err("Unsupported Speed/Duplex configuration\n"); return -EINVAL; } switch (spddplx) { case SPEED_10 + DUPLEX_HALF: mac->forced_speed_duplex = ADVERTISE_10_HALF; break; case SPEED_10 + DUPLEX_FULL: mac->forced_speed_duplex = ADVERTISE_10_FULL; break; case SPEED_100 + DUPLEX_HALF: mac->forced_speed_duplex = ADVERTISE_100_HALF; break; case SPEED_100 + DUPLEX_FULL: mac->forced_speed_duplex = ADVERTISE_100_FULL; break; case SPEED_1000 + DUPLEX_FULL: mac->autoneg = 1; adapter->hw.phy.autoneg_advertised = ADVERTISE_1000_FULL; break; case SPEED_1000 + DUPLEX_HALF: /* not supported */ default: e_err("Unsupported Speed/Duplex configuration\n"); return -EINVAL; } return 0;}static int e1000_set_settings(struct net_device *netdev, struct ethtool_cmd *ecmd){ struct e1000_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; /* * When SoL/IDER sessions are active, autoneg/speed/duplex * cannot be changed */ if (hw->phy.ops.check_reset_block && hw->phy.ops.check_reset_block(&adapter->hw)) { e_err("Cannot change link characteristics when SoL/IDER" " is active.\n"); return -EINVAL; } while (test_and_set_bit(__E1000_RESETTING, &adapter->state)) msleep(1); if (ecmd->autoneg == AUTONEG_ENABLE) { hw->mac.autoneg = 1; if (hw->phy.media_type == e1000_media_type_fiber) hw->phy.autoneg_advertised = ADVERTISED_1000baseT_Full | ADVERTISED_FIBRE | ADVERTISED_Autoneg; else hw->phy.autoneg_advertised = ecmd->advertising | ADVERTISED_TP | ADVERTISED_Autoneg; ecmd->advertising = hw->phy.autoneg_advertised; if (adapter->fc_autoneg) hw->fc.original_type = e1000_fc_default; } else { if (e1000_set_spd_dplx(adapter, ecmd->speed + ecmd->duplex)) { clear_bit(__E1000_RESETTING, &adapter->state); return -EINVAL; } } /* reset the link */ if (netif_running(adapter->netdev)) { e1000_down(adapter); e1000_up(adapter); } else { e1000_reset(adapter); } clear_bit(__E1000_RESETTING, &adapter->state); return 0;}static void e1000_get_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *pause){ struct e1000_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; pause->autoneg = (adapter->fc_autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE); if (hw->fc.type == e1000_fc_rx_pause) { pause->rx_pause = 1; } else if (hw->fc.type == e1000_fc_tx_pause) { pause->tx_pause = 1; } else if (hw->fc.type == e1000_fc_full) { pause->rx_pause = 1; pause->tx_pause = 1; }}static int e1000_set_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *pause){ struct e1000_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; int retval = 0; adapter->fc_autoneg = pause->autoneg; while (test_and_set_bit(__E1000_RESETTING, &adapter->state)) msleep(1); if (pause->rx_pause && pause->tx_pause) hw->fc.type = e1000_fc_full; else if (pause->rx_pause && !pause->tx_pause) hw->fc.type = e1000_fc_rx_pause; else if (!pause->rx_pause && pause->tx_pause) hw->fc.type = e1000_fc_tx_pause; else if (!pause->rx_pause && !pause->tx_pause) hw->fc.type = e1000_fc_none; hw->fc.original_type = hw->fc.type; if (adapter->fc_autoneg == AUTONEG_ENABLE) { hw->fc.type = e1000_fc_default; if (netif_running(adapter->netdev)) { e1000_down(adapter); e1000_up(adapter); } else { e1000_reset(adapter); } } else { retval = ((hw->phy.media_type == e1000_media_type_fiber) ? hw->mac.ops.setup_link(hw) : e1000_force_mac_fc_generic(hw)); } clear_bit(__E1000_RESETTING, &adapter->state); return retval;}static u32 e1000_get_rx_csum(struct net_device *netdev){ struct e1000_adapter *adapter = netdev_priv(netdev); return (adapter->flags & FLAG_RX_CSUM_ENABLED);}static int e1000_set_rx_csum(struct net_device *netdev, u32 data){ struct e1000_adapter *adapter = netdev_priv(netdev); if (data) adapter->flags |= FLAG_RX_CSUM_ENABLED; else adapter->flags &= ~FLAG_RX_CSUM_ENABLED; if (netif_running(netdev)) e1000_reinit_locked(adapter); else e1000_reset(adapter); return 0;}static u32 e1000_get_tx_csum(struct net_device *netdev){ return ((netdev->features & NETIF_F_HW_CSUM) != 0);}static int e1000_set_tx_csum(struct net_device *netdev, u32 data){ if (data) netdev->features |= NETIF_F_HW_CSUM; else netdev->features &= ~NETIF_F_HW_CSUM; return 0;}#ifdef NETIF_F_TSOstatic int e1000_set_tso(struct net_device *netdev, u32 data){ struct e1000_adapter *adapter = netdev_priv(netdev); int i; struct net_device *v_netdev; if (data) { netdev->features |= NETIF_F_TSO;#ifdef NETIF_F_TSO6 netdev->features |= NETIF_F_TSO6;#endif } else { netdev->features &= ~NETIF_F_TSO;#ifdef NETIF_F_TSO6 netdev->features &= ~NETIF_F_TSO6;#endif#ifdef NETIF_F_HW_VLAN_TX /* disable TSO on all VLANs if they're present */ if (!adapter->vlgrp) goto tso_out; for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) { v_netdev = vlan_group_get_device(adapter->vlgrp, i); if (!v_netdev) continue; v_netdev->features &= ~NETIF_F_TSO;#ifdef NETIF_F_TSO6 v_netdev->features &= ~NETIF_F_TSO6;#endif vlan_group_set_device(adapter->vlgrp, i, v_netdev); }#endif } tso_out: e_info("TSO is %s\n", data ? "Enabled" : "Disabled"); adapter->flags |= FLAG_TSO_FORCE; return 0;}#endifstatic u32 e1000_get_msglevel(struct net_device *netdev){ struct e1000_adapter *adapter = netdev_priv(netdev); return adapter->msg_enable;}static void e1000_set_msglevel(struct net_device *netdev, u32 data){ struct e1000_adapter *adapter = netdev_priv(netdev); adapter->msg_enable = data;}static int e1000_get_regs_len(struct net_device *netdev){#define E1000_REGS_LEN 32 /* overestimate */ return E1000_REGS_LEN * sizeof(u32);}static void e1000_get_regs(struct net_device *netdev, struct ethtool_regs *regs, void *p){ struct e1000_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; u32 *regs_buff = p; u16 phy_data; u8 revision_id; memset(p, 0, E1000_REGS_LEN * sizeof(u32)); pci_read_config_byte(adapter->pdev, PCI_REVISION_ID, &revision_id); regs->version = (1 << 24) | (revision_id << 16) | adapter->pdev->device; regs_buff[0] = er32(CTRL); regs_buff[1] = er32(STATUS); regs_buff[2] = er32(RCTL); regs_buff[3] = er32(RDLEN(0)); regs_buff[4] = er32(RDH(0)); regs_buff[5] = er32(RDT(0)); regs_buff[6] = er32(RDTR); regs_buff[7] = er32(TCTL); regs_buff[8] = er32(TDLEN(0)); regs_buff[9] = er32(TDH(0)); regs_buff[10] = er32(TDT(0)); regs_buff[11] = er32(TIDV); regs_buff[12] = adapter->hw.phy.type; /* PHY type (IGP=1, M88=0) */ if (hw->phy.type == e1000_phy_m88) { hw->phy.ops.read_reg(hw, M88E1000_PHY_SPEC_STATUS, &phy_data); regs_buff[13] = (u32)phy_data; /* cable length */ regs_buff[14] = 0; /* Dummy (to align w/ IGP phy reg dump) */ regs_buff[15] = 0; /* Dummy (to align w/ IGP phy reg dump) */ regs_buff[16] = 0; /* Dummy (to align w/ IGP phy reg dump) */ hw->phy.ops.read_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data); regs_buff[17] = (u32)phy_data; /* extended 10bt distance */ regs_buff[18] = regs_buff[13]; /* cable polarity */ regs_buff[19] = 0; /* Dummy (to align w/ IGP phy reg dump) */ regs_buff[20] = regs_buff[17]; /* polarity correction */ /* phy receive errors */ regs_buff[22] = adapter->phy_stats.receive_errors; regs_buff[23] = regs_buff[13]; /* mdix mode */ } regs_buff[21] = adapter->phy_stats.idle_errors; /* phy idle errors */ hw->phy.ops.read_reg(hw, PHY_1000T_STATUS, &phy_data); regs_buff[24] = (u32)phy_data; /* phy local receiver status */ regs_buff[25] = regs_buff[24]; /* phy remote receiver status */}static int e1000_get_eeprom_len(struct net_device *netdev){ struct e1000_adapter *adapter = netdev_priv(netdev); return adapter->hw.nvm.word_size * 2;}static int e1000_get_eeprom(struct net_device *netdev, struct ethtool_eeprom *eeprom, u8 *bytes){ struct e1000_adapter *adapter = netdev_priv(netdev);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -