Commit 7190c16a authored by Ganesh Venkatesan's avatar Ganesh Venkatesan Committed by Jeff Garzik

[PATCH] e1000 6/7: ethtool_ops support

parent 8e101155
...@@ -245,5 +245,6 @@ struct e1000_adapter { ...@@ -245,5 +245,6 @@ struct e1000_adapter {
uint32_t pci_state[16]; uint32_t pci_state[16];
int msg_enable;
}; };
#endif /* _E1000_H_ */ #endif /* _E1000_H_ */
...@@ -53,7 +53,7 @@ struct e1000_stats { ...@@ -53,7 +53,7 @@ struct e1000_stats {
#define E1000_STAT(m) sizeof(((struct e1000_adapter *)0)->m), \ #define E1000_STAT(m) sizeof(((struct e1000_adapter *)0)->m), \
offsetof(struct e1000_adapter, m) offsetof(struct e1000_adapter, m)
static struct e1000_stats e1000_gstrings_stats[] = { static const struct e1000_stats e1000_gstrings_stats[] = {
{ "rx_packets", E1000_STAT(net_stats.rx_packets) }, { "rx_packets", E1000_STAT(net_stats.rx_packets) },
{ "tx_packets", E1000_STAT(net_stats.tx_packets) }, { "tx_packets", E1000_STAT(net_stats.tx_packets) },
{ "rx_bytes", E1000_STAT(net_stats.rx_bytes) }, { "rx_bytes", E1000_STAT(net_stats.rx_bytes) },
...@@ -89,20 +89,22 @@ static struct e1000_stats e1000_gstrings_stats[] = { ...@@ -89,20 +89,22 @@ static struct e1000_stats e1000_gstrings_stats[] = {
{ "tx_flow_control_xon", E1000_STAT(stats.xontxc) }, { "tx_flow_control_xon", E1000_STAT(stats.xontxc) },
{ "tx_flow_control_xoff", E1000_STAT(stats.xofftxc) }, { "tx_flow_control_xoff", E1000_STAT(stats.xofftxc) },
{ "rx_csum_offload_good", E1000_STAT(hw_csum_good) }, { "rx_csum_offload_good", E1000_STAT(hw_csum_good) },
{ "rx_csum_offload_errors", E1000_STAT(hw_csum_err) } { "rx_csum_offload_errors", E1000_STAT(hw_csum_err) },
{ "rx_long_byte_count", E1000_STAT(stats.gorcl) }
}; };
#define E1000_STATS_LEN \ #define E1000_STATS_LEN \
sizeof(e1000_gstrings_stats) / sizeof(struct e1000_stats) sizeof(e1000_gstrings_stats) / sizeof(struct e1000_stats)
static char e1000_gstrings_test[][ETH_GSTRING_LEN] = { static const char e1000_gstrings_test[][ETH_GSTRING_LEN] = {
"Register test (offline)", "Eeprom test (offline)", "Register test (offline)", "Eeprom test (offline)",
"Interrupt test (offline)", "Loopback test (offline)", "Interrupt test (offline)", "Loopback test (offline)",
"Link test (on/offline)" "Link test (on/offline)"
}; };
#define E1000_TEST_LEN sizeof(e1000_gstrings_test) / ETH_GSTRING_LEN #define E1000_TEST_LEN sizeof(e1000_gstrings_test) / ETH_GSTRING_LEN
static void static int
e1000_ethtool_gset(struct e1000_adapter *adapter, struct ethtool_cmd *ecmd) e1000_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
{ {
struct e1000_adapter *adapter = netdev->priv;
struct e1000_hw *hw = &adapter->hw; struct e1000_hw *hw = &adapter->hw;
if(hw->media_type == e1000_media_type_copper) { if(hw->media_type == e1000_media_type_copper) {
...@@ -169,11 +171,13 @@ e1000_ethtool_gset(struct e1000_adapter *adapter, struct ethtool_cmd *ecmd) ...@@ -169,11 +171,13 @@ e1000_ethtool_gset(struct e1000_adapter *adapter, struct ethtool_cmd *ecmd)
} }
ecmd->autoneg = (hw->autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE); ecmd->autoneg = (hw->autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE);
return 0;
} }
static int static int
e1000_ethtool_sset(struct e1000_adapter *adapter, struct ethtool_cmd *ecmd) e1000_set_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
{ {
struct e1000_adapter *adapter = netdev->priv;
struct e1000_hw *hw = &adapter->hw; struct e1000_hw *hw = &adapter->hw;
if(ecmd->autoneg == AUTONEG_ENABLE) { if(ecmd->autoneg == AUTONEG_ENABLE) {
...@@ -195,42 +199,41 @@ e1000_ethtool_sset(struct e1000_adapter *adapter, struct ethtool_cmd *ecmd) ...@@ -195,42 +199,41 @@ e1000_ethtool_sset(struct e1000_adapter *adapter, struct ethtool_cmd *ecmd)
return 0; return 0;
} }
static int static void
e1000_ethtool_gpause(struct e1000_adapter *adapter, e1000_get_pauseparam(struct net_device *netdev,
struct ethtool_pauseparam *epause) struct ethtool_pauseparam *pause)
{ {
struct e1000_adapter *adapter = netdev->priv;
struct e1000_hw *hw = &adapter->hw; struct e1000_hw *hw = &adapter->hw;
pause->autoneg =
epause->autoneg =
(adapter->fc_autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE); (adapter->fc_autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE);
if(hw->fc == e1000_fc_rx_pause) if(hw->fc == e1000_fc_rx_pause)
epause->rx_pause = 1; pause->rx_pause = 1;
else if(hw->fc == e1000_fc_tx_pause) else if(hw->fc == e1000_fc_tx_pause)
epause->tx_pause = 1; pause->tx_pause = 1;
else if(hw->fc == e1000_fc_full) { else if(hw->fc == e1000_fc_full) {
epause->rx_pause = 1; pause->rx_pause = 1;
epause->tx_pause = 1; pause->tx_pause = 1;
} }
return 0;
} }
static int static int
e1000_ethtool_spause(struct e1000_adapter *adapter, e1000_set_pauseparam(struct net_device *netdev,
struct ethtool_pauseparam *epause) struct ethtool_pauseparam *pause)
{ {
struct e1000_adapter *adapter = netdev->priv;
struct e1000_hw *hw = &adapter->hw; struct e1000_hw *hw = &adapter->hw;
adapter->fc_autoneg = epause->autoneg; adapter->fc_autoneg = pause->autoneg;
if(epause->rx_pause && epause->tx_pause) if(pause->rx_pause && pause->tx_pause)
hw->fc = e1000_fc_full; hw->fc = e1000_fc_full;
else if(epause->rx_pause && !epause->tx_pause) else if(pause->rx_pause && !pause->tx_pause)
hw->fc = e1000_fc_rx_pause; hw->fc = e1000_fc_rx_pause;
else if(!epause->rx_pause && epause->tx_pause) else if(!pause->rx_pause && pause->tx_pause)
hw->fc = e1000_fc_tx_pause; hw->fc = e1000_fc_tx_pause;
else if(!epause->rx_pause && !epause->tx_pause) else if(!pause->rx_pause && !pause->tx_pause)
hw->fc = e1000_fc_none; hw->fc = e1000_fc_none;
hw->original_fc = hw->fc; hw->original_fc = hw->fc;
...@@ -248,28 +251,124 @@ e1000_ethtool_spause(struct e1000_adapter *adapter, ...@@ -248,28 +251,124 @@ e1000_ethtool_spause(struct e1000_adapter *adapter,
return 0; return 0;
} }
static uint32_t
e1000_get_rx_csum(struct net_device *netdev)
{
struct e1000_adapter *adapter = netdev->priv;
return adapter->rx_csum;
}
static int
e1000_set_rx_csum(struct net_device *netdev, uint32_t data)
{
struct e1000_adapter *adapter = netdev->priv;
adapter->rx_csum = data;
if(netif_running(netdev)) {
e1000_down(adapter);
e1000_up(adapter);
} else
e1000_reset(adapter);
return 0;
}
static uint32_t
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, uint32_t data)
{
struct e1000_adapter *adapter = netdev->priv;
if(adapter->hw.mac_type < e1000_82543) {
if (!data)
return -EINVAL;
return 0;
}
if (data)
netdev->features |= NETIF_F_HW_CSUM;
else
netdev->features &= ~NETIF_F_HW_CSUM;
return 0;
}
static uint32_t
e1000_get_sg(struct net_device *netdev)
{
return (netdev->features & NETIF_F_SG) != 0;
}
static int
e1000_set_sg(struct net_device *netdev, uint32_t data)
{
if (data)
netdev->features |= NETIF_F_SG;
else
netdev->features &= ~NETIF_F_SG;
return 0;
}
#ifdef NETIF_F_TSO
static uint32_t
e1000_get_tso(struct net_device *netdev)
{
return (netdev->features & NETIF_F_TSO) != 0;
}
static int
e1000_set_tso(struct net_device *netdev, uint32_t data)
{
struct e1000_adapter *adapter = netdev->priv;
if ((adapter->hw.mac_type < e1000_82544) ||
(adapter->hw.mac_type == e1000_82547))
return data ? -EINVAL : 0;
if (data)
netdev->features |= NETIF_F_TSO;
else
netdev->features &= ~NETIF_F_TSO;
return 0;
}
#endif /* NETIF_F_TSO */
static uint32_t
e1000_get_msglevel(struct net_device *netdev)
{
struct e1000_adapter *adapter = netdev->priv;
return adapter->msg_enable;
}
static void static void
e1000_ethtool_gdrvinfo(struct e1000_adapter *adapter, e1000_set_msglevel(struct net_device *netdev, uint32_t data)
struct ethtool_drvinfo *drvinfo) {
struct e1000_adapter *adapter = netdev->priv;
adapter->msg_enable = data;
}
static int
e1000_get_regs_len(struct net_device *netdev)
{ {
strncpy(drvinfo->driver, e1000_driver_name, 32);
strncpy(drvinfo->version, e1000_driver_version, 32);
strncpy(drvinfo->fw_version, "N/A", 32);
strncpy(drvinfo->bus_info, pci_name(adapter->pdev), 32);
drvinfo->n_stats = E1000_STATS_LEN;
drvinfo->testinfo_len = E1000_TEST_LEN;
#define E1000_REGS_LEN 32 #define E1000_REGS_LEN 32
drvinfo->regdump_len = E1000_REGS_LEN * sizeof(uint32_t); return E1000_REGS_LEN * sizeof(uint32_t);
drvinfo->eedump_len = adapter->hw.eeprom.word_size * 2;
} }
static void static void
e1000_ethtool_gregs(struct e1000_adapter *adapter, e1000_get_regs(struct net_device *netdev,
struct ethtool_regs *regs, uint32_t *regs_buff) struct ethtool_regs *regs, void *p)
{ {
struct e1000_adapter *adapter = netdev->priv;
struct e1000_hw *hw = &adapter->hw; struct e1000_hw *hw = &adapter->hw;
uint32_t *regs_buff = p;
uint16_t phy_data; uint16_t phy_data;
memset(p, 0, E1000_REGS_LEN * sizeof(uint32_t));
regs->version = (1 << 24) | (hw->revision_id << 16) | hw->device_id; regs->version = (1 << 24) | (hw->revision_id << 16) | hw->device_id;
regs_buff[0] = E1000_READ_REG(hw, CTRL); regs_buff[0] = E1000_READ_REG(hw, CTRL);
...@@ -342,37 +441,39 @@ e1000_ethtool_gregs(struct e1000_adapter *adapter, ...@@ -342,37 +441,39 @@ e1000_ethtool_gregs(struct e1000_adapter *adapter,
e1000_read_phy_reg(hw, PHY_1000T_STATUS, &phy_data); e1000_read_phy_reg(hw, PHY_1000T_STATUS, &phy_data);
regs_buff[24] = (uint32_t)phy_data; /* phy local receiver status */ regs_buff[24] = (uint32_t)phy_data; /* phy local receiver status */
regs_buff[25] = regs_buff[24]; /* phy remote receiver status */ regs_buff[25] = regs_buff[24]; /* phy remote receiver status */
}
return; static int
e1000_get_eeprom_len(struct net_device *netdev)
{
struct e1000_adapter *adapter = netdev->priv;
return adapter->hw.eeprom.word_size * 2;
} }
static int static int
e1000_ethtool_geeprom(struct e1000_adapter *adapter, e1000_get_eeprom(struct net_device *netdev,
struct ethtool_eeprom *eeprom, uint16_t *eeprom_buff) struct ethtool_eeprom *eeprom, uint8_t *bytes)
{ {
struct e1000_adapter *adapter = netdev->priv;
struct e1000_hw *hw = &adapter->hw; struct e1000_hw *hw = &adapter->hw;
uint16_t *eeprom_buff;
int first_word, last_word; int first_word, last_word;
int ret_val = 0; int ret_val = 0;
uint16_t i; uint16_t i;
if(eeprom->len == 0) { if(eeprom->len == 0)
ret_val = -EINVAL; return -EINVAL;
goto geeprom_error;
}
eeprom->magic = hw->vendor_id | (hw->device_id << 16); eeprom->magic = hw->vendor_id | (hw->device_id << 16);
if(eeprom->offset > eeprom->offset + eeprom->len) {
ret_val = -EINVAL;
goto geeprom_error;
}
if((eeprom->offset + eeprom->len) > (hw->eeprom.word_size * 2))
eeprom->len = ((hw->eeprom.word_size * 2) - eeprom->offset);
first_word = eeprom->offset >> 1; first_word = eeprom->offset >> 1;
last_word = (eeprom->offset + eeprom->len - 1) >> 1; last_word = (eeprom->offset + eeprom->len - 1) >> 1;
eeprom_buff = kmalloc(sizeof(uint16_t) *
(last_word - first_word + 1), GFP_KERNEL);
if (!eeprom_buff)
return -ENOMEM;
if(hw->eeprom.type == e1000_eeprom_spi) if(hw->eeprom.type == e1000_eeprom_spi)
ret_val = e1000_read_eeprom(hw, first_word, ret_val = e1000_read_eeprom(hw, first_word,
last_word - first_word + 1, last_word - first_word + 1,
...@@ -388,14 +489,19 @@ e1000_ethtool_geeprom(struct e1000_adapter *adapter, ...@@ -388,14 +489,19 @@ e1000_ethtool_geeprom(struct e1000_adapter *adapter,
for (i = 0; i < last_word - first_word + 1; i++) for (i = 0; i < last_word - first_word + 1; i++)
le16_to_cpus(&eeprom_buff[i]); le16_to_cpus(&eeprom_buff[i]);
geeprom_error:
memcpy(bytes, (uint8_t *)eeprom_buff + (eeprom->offset%2),
eeprom->len);
kfree(eeprom_buff);
return ret_val; return ret_val;
} }
static int static int
e1000_ethtool_seeprom(struct e1000_adapter *adapter, e1000_set_eeprom(struct net_device *netdev,
struct ethtool_eeprom *eeprom, void *user_data) struct ethtool_eeprom *eeprom, uint8_t *bytes)
{ {
struct e1000_adapter *adapter = netdev->priv;
struct e1000_hw *hw = &adapter->hw; struct e1000_hw *hw = &adapter->hw;
uint16_t *eeprom_buff; uint16_t *eeprom_buff;
void *ptr; void *ptr;
...@@ -410,9 +516,6 @@ e1000_ethtool_seeprom(struct e1000_adapter *adapter, ...@@ -410,9 +516,6 @@ e1000_ethtool_seeprom(struct e1000_adapter *adapter,
max_len = hw->eeprom.word_size * 2; max_len = hw->eeprom.word_size * 2;
if((eeprom->offset + eeprom->len) > max_len)
eeprom->len = (max_len - eeprom->offset);
first_word = eeprom->offset >> 1; first_word = eeprom->offset >> 1;
last_word = (eeprom->offset + eeprom->len - 1) >> 1; last_word = (eeprom->offset + eeprom->len - 1) >> 1;
eeprom_buff = kmalloc(max_len, GFP_KERNEL); eeprom_buff = kmalloc(max_len, GFP_KERNEL);
...@@ -439,11 +542,7 @@ e1000_ethtool_seeprom(struct e1000_adapter *adapter, ...@@ -439,11 +542,7 @@ e1000_ethtool_seeprom(struct e1000_adapter *adapter,
for (i = 0; i < last_word - first_word + 1; i++) for (i = 0; i < last_word - first_word + 1; i++)
le16_to_cpus(&eeprom_buff[i]); le16_to_cpus(&eeprom_buff[i]);
if((ret_val != 0) || copy_from_user(ptr, user_data, eeprom->len)) { memcpy(ptr, bytes, eeprom->len);
ret_val = -EFAULT;
goto seeprom_error;
}
for (i = 0; i < last_word - first_word + 1; i++) for (i = 0; i < last_word - first_word + 1; i++)
eeprom_buff[i] = cpu_to_le16(eeprom_buff[i]); eeprom_buff[i] = cpu_to_le16(eeprom_buff[i]);
...@@ -454,15 +553,31 @@ e1000_ethtool_seeprom(struct e1000_adapter *adapter, ...@@ -454,15 +553,31 @@ e1000_ethtool_seeprom(struct e1000_adapter *adapter,
if((ret_val == 0) && first_word <= EEPROM_CHECKSUM_REG) if((ret_val == 0) && first_word <= EEPROM_CHECKSUM_REG)
e1000_update_eeprom_checksum(hw); e1000_update_eeprom_checksum(hw);
seeprom_error:
kfree(eeprom_buff); kfree(eeprom_buff);
return ret_val; return ret_val;
} }
static int static void
e1000_ethtool_gring(struct e1000_adapter *adapter, e1000_get_drvinfo(struct net_device *netdev,
struct ethtool_drvinfo *drvinfo)
{
struct e1000_adapter *adapter = netdev->priv;
strncpy(drvinfo->driver, e1000_driver_name, 32);
strncpy(drvinfo->version, e1000_driver_version, 32);
strncpy(drvinfo->fw_version, "N/A", 32);
strncpy(drvinfo->bus_info, pci_name(adapter->pdev), 32);
drvinfo->n_stats = E1000_STATS_LEN;
drvinfo->testinfo_len = E1000_TEST_LEN;
drvinfo->regdump_len = e1000_get_regs_len(netdev);
drvinfo->eedump_len = e1000_get_eeprom_len(netdev);
}
static void
e1000_get_ringparam(struct net_device *netdev,
struct ethtool_ringparam *ring) struct ethtool_ringparam *ring)
{ {
struct e1000_adapter *adapter = netdev->priv;
e1000_mac_type mac_type = adapter->hw.mac_type; e1000_mac_type mac_type = adapter->hw.mac_type;
struct e1000_desc_ring *txdr = &adapter->tx_ring; struct e1000_desc_ring *txdr = &adapter->tx_ring;
struct e1000_desc_ring *rxdr = &adapter->rx_ring; struct e1000_desc_ring *rxdr = &adapter->rx_ring;
...@@ -477,14 +592,14 @@ e1000_ethtool_gring(struct e1000_adapter *adapter, ...@@ -477,14 +592,14 @@ e1000_ethtool_gring(struct e1000_adapter *adapter,
ring->tx_pending = txdr->count; ring->tx_pending = txdr->count;
ring->rx_mini_pending = 0; ring->rx_mini_pending = 0;
ring->rx_jumbo_pending = 0; ring->rx_jumbo_pending = 0;
return 0;
} }
static int static int
e1000_ethtool_sring(struct e1000_adapter *adapter, e1000_set_ringparam(struct net_device *netdev,
struct ethtool_ringparam *ring) struct ethtool_ringparam *ring)
{ {
int err; int err;
struct e1000_adapter *adapter = netdev->priv;
e1000_mac_type mac_type = adapter->hw.mac_type; e1000_mac_type mac_type = adapter->hw.mac_type;
struct e1000_desc_ring *txdr = &adapter->tx_ring; struct e1000_desc_ring *txdr = &adapter->tx_ring;
struct e1000_desc_ring *rxdr = &adapter->rx_ring; struct e1000_desc_ring *rxdr = &adapter->rx_ring;
...@@ -538,6 +653,7 @@ e1000_ethtool_sring(struct e1000_adapter *adapter, ...@@ -538,6 +653,7 @@ e1000_ethtool_sring(struct e1000_adapter *adapter,
return err; return err;
} }
#define REG_PATTERN_TEST(R, M, W) \ #define REG_PATTERN_TEST(R, M, W) \
{ \ { \
uint32_t pat, value; \ uint32_t pat, value; \
...@@ -628,6 +744,7 @@ e1000_reg_test(struct e1000_adapter *adapter, uint64_t *data) ...@@ -628,6 +744,7 @@ e1000_reg_test(struct e1000_adapter *adapter, uint64_t *data)
for(i = 0; i < E1000_MC_TBL_SIZE; i++) for(i = 0; i < E1000_MC_TBL_SIZE; i++)
REG_PATTERN_TEST(MTA + (i << 2), 0xFFFFFFFF, 0xFFFFFFFF); REG_PATTERN_TEST(MTA + (i << 2), 0xFFFFFFFF, 0xFFFFFFFF);
*data = 0;
return 0; return 0;
} }
...@@ -939,8 +1056,6 @@ e1000_phy_disable_receiver(struct e1000_adapter *adapter) ...@@ -939,8 +1056,6 @@ e1000_phy_disable_receiver(struct e1000_adapter *adapter)
e1000_write_phy_reg(&adapter->hw, 30, 0x8FFC); e1000_write_phy_reg(&adapter->hw, 30, 0x8FFC);
e1000_write_phy_reg(&adapter->hw, 29, 0x001A); e1000_write_phy_reg(&adapter->hw, 29, 0x001A);
e1000_write_phy_reg(&adapter->hw, 30, 0x8FF0); e1000_write_phy_reg(&adapter->hw, 30, 0x8FF0);
return;
} }
static void static void
...@@ -1219,16 +1334,16 @@ e1000_run_loopback_test(struct e1000_adapter *adapter) ...@@ -1219,16 +1334,16 @@ e1000_run_loopback_test(struct e1000_adapter *adapter)
for(i = 0; i < 64; i++) { for(i = 0; i < 64; i++) {
e1000_create_lbtest_frame(txdr->buffer_info[i].skb, 1024); e1000_create_lbtest_frame(txdr->buffer_info[i].skb, 1024);
pci_dma_sync_single_for_device(pdev, txdr->buffer_info[i].dma, pci_dma_sync_single(pdev, txdr->buffer_info[i].dma,
txdr->buffer_info[i].length, txdr->buffer_info[i].length,
PCI_DMA_TODEVICE); PCI_DMA_TODEVICE);
} }
E1000_WRITE_REG(&adapter->hw, TDT, i); E1000_WRITE_REG(&adapter->hw, TDT, i);
msec_delay(200); msec_delay(200);
pci_dma_sync_single_for_cpu(pdev, rxdr->buffer_info[0].dma, pci_dma_sync_single(pdev, rxdr->buffer_info[0].dma,
rxdr->buffer_info[0].length, PCI_DMA_FROMDEVICE); rxdr->buffer_info[0].length, PCI_DMA_FROMDEVICE);
return e1000_check_lbtest_frame(rxdr->buffer_info[0].skb, 1024); return e1000_check_lbtest_frame(rxdr->buffer_info[0].skb, 1024);
} }
...@@ -1257,15 +1372,27 @@ e1000_link_test(struct e1000_adapter *adapter, uint64_t *data) ...@@ -1257,15 +1372,27 @@ e1000_link_test(struct e1000_adapter *adapter, uint64_t *data)
return *data; return *data;
} }
static int static int
e1000_ethtool_test(struct e1000_adapter *adapter, e1000_diag_test_count(struct net_device *netdev)
{
return E1000_TEST_LEN;
}
static void
e1000_diag_test(struct net_device *netdev,
struct ethtool_test *eth_test, uint64_t *data) struct ethtool_test *eth_test, uint64_t *data)
{ {
boolean_t if_running = netif_running(adapter->netdev); struct e1000_adapter *adapter = netdev->priv;
boolean_t if_running = netif_running(netdev);
if(eth_test->flags == ETH_TEST_FL_OFFLINE) { if(eth_test->flags == ETH_TEST_FL_OFFLINE) {
/* Offline tests */ /* Offline tests */
/* save speed, duplex, autoneg settings */
uint16_t autoneg_advertised = adapter->hw.autoneg_advertised;
uint8_t forced_speed_duplex = adapter->hw.forced_speed_duplex;
uint8_t autoneg = adapter->hw.autoneg;
/* Link test performed before hardware reset so autoneg doesn't /* Link test performed before hardware reset so autoneg doesn't
* interfere with test result */ * interfere with test result */
if(e1000_link_test(adapter, &data[4])) if(e1000_link_test(adapter, &data[4]))
...@@ -1291,6 +1418,10 @@ e1000_ethtool_test(struct e1000_adapter *adapter, ...@@ -1291,6 +1418,10 @@ e1000_ethtool_test(struct e1000_adapter *adapter,
if(e1000_loopback_test(adapter, &data[3])) if(e1000_loopback_test(adapter, &data[3]))
eth_test->flags |= ETH_TEST_FL_FAILED; eth_test->flags |= ETH_TEST_FL_FAILED;
/* restore Autoneg/speed/duplex settings */
adapter->hw.autoneg_advertised = autoneg_advertised;
adapter->hw.forced_speed_duplex = forced_speed_duplex;
adapter->hw.autoneg = autoneg;
e1000_reset(adapter); e1000_reset(adapter);
if(if_running) if(if_running)
e1000_up(adapter); e1000_up(adapter);
...@@ -1305,12 +1436,12 @@ e1000_ethtool_test(struct e1000_adapter *adapter, ...@@ -1305,12 +1436,12 @@ e1000_ethtool_test(struct e1000_adapter *adapter,
data[2] = 0; data[2] = 0;
data[3] = 0; data[3] = 0;
} }
return 0;
} }
static void static void
e1000_ethtool_gwol(struct e1000_adapter *adapter, struct ethtool_wolinfo *wol) e1000_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
{ {
struct e1000_adapter *adapter = netdev->priv;
struct e1000_hw *hw = &adapter->hw; struct e1000_hw *hw = &adapter->hw;
switch(adapter->hw.device_id) { switch(adapter->hw.device_id) {
...@@ -1350,8 +1481,9 @@ e1000_ethtool_gwol(struct e1000_adapter *adapter, struct ethtool_wolinfo *wol) ...@@ -1350,8 +1481,9 @@ e1000_ethtool_gwol(struct e1000_adapter *adapter, struct ethtool_wolinfo *wol)
} }
static int static int
e1000_ethtool_swol(struct e1000_adapter *adapter, struct ethtool_wolinfo *wol) e1000_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
{ {
struct e1000_adapter *adapter = netdev->priv;
struct e1000_hw *hw = &adapter->hw; struct e1000_hw *hw = &adapter->hw;
switch(adapter->hw.device_id) { switch(adapter->hw.device_id) {
...@@ -1387,7 +1519,6 @@ e1000_ethtool_swol(struct e1000_adapter *adapter, struct ethtool_wolinfo *wol) ...@@ -1387,7 +1519,6 @@ e1000_ethtool_swol(struct e1000_adapter *adapter, struct ethtool_wolinfo *wol)
return 0; return 0;
} }
/* toggle LED 4 times per second = 2 "blinks" per second */ /* toggle LED 4 times per second = 2 "blinks" per second */
#define E1000_ID_INTERVAL (HZ/4) #define E1000_ID_INTERVAL (HZ/4)
...@@ -1408,8 +1539,13 @@ e1000_led_blink_callback(unsigned long data) ...@@ -1408,8 +1539,13 @@ e1000_led_blink_callback(unsigned long data)
} }
static int static int
e1000_ethtool_led_blink(struct e1000_adapter *adapter, struct ethtool_value *id) e1000_phys_id(struct net_device *netdev, uint32_t data)
{ {
struct e1000_adapter *adapter = netdev->priv;
if(!data || data > (uint32_t)(MAX_SCHEDULE_TIMEOUT / HZ))
data = (uint32_t)(MAX_SCHEDULE_TIMEOUT / HZ);
if(!adapter->blink_timer.function) { if(!adapter->blink_timer.function) {
init_timer(&adapter->blink_timer); init_timer(&adapter->blink_timer);
adapter->blink_timer.function = e1000_led_blink_callback; adapter->blink_timer.function = e1000_led_blink_callback;
...@@ -1420,11 +1556,8 @@ e1000_ethtool_led_blink(struct e1000_adapter *adapter, struct ethtool_value *id) ...@@ -1420,11 +1556,8 @@ e1000_ethtool_led_blink(struct e1000_adapter *adapter, struct ethtool_value *id)
mod_timer(&adapter->blink_timer, jiffies); mod_timer(&adapter->blink_timer, jiffies);
set_current_state(TASK_INTERRUPTIBLE); set_current_state(TASK_INTERRUPTIBLE);
if(id->data)
schedule_timeout(id->data * HZ);
else
schedule_timeout(MAX_SCHEDULE_TIMEOUT);
schedule_timeout(data * HZ);
del_timer_sync(&adapter->blink_timer); del_timer_sync(&adapter->blink_timer);
e1000_led_off(&adapter->hw); e1000_led_off(&adapter->hw);
clear_bit(E1000_LED_ON, &adapter->led_status); clear_bit(E1000_LED_ON, &adapter->led_status);
...@@ -1433,345 +1566,102 @@ e1000_ethtool_led_blink(struct e1000_adapter *adapter, struct ethtool_value *id) ...@@ -1433,345 +1566,102 @@ e1000_ethtool_led_blink(struct e1000_adapter *adapter, struct ethtool_value *id)
return 0; return 0;
} }
int static int
e1000_ethtool_ioctl(struct net_device *netdev, struct ifreq *ifr) e1000_nway_reset(struct net_device *netdev)
{ {
struct e1000_adapter *adapter = netdev->priv; struct e1000_adapter *adapter = netdev->priv;
void *addr = ifr->ifr_data; if(netif_running(netdev)) {
uint32_t cmd; e1000_down(adapter);
e1000_up(adapter);
if(get_user(cmd, (uint32_t *) addr))
return -EFAULT;
switch(cmd) {
case ETHTOOL_GSET: {
struct ethtool_cmd ecmd = {ETHTOOL_GSET};
e1000_ethtool_gset(adapter, &ecmd);
if(copy_to_user(addr, &ecmd, sizeof(ecmd)))
return -EFAULT;
return 0;
}
case ETHTOOL_SSET: {
struct ethtool_cmd ecmd;
if(copy_from_user(&ecmd, addr, sizeof(ecmd)))
return -EFAULT;
return e1000_ethtool_sset(adapter, &ecmd);
}
case ETHTOOL_GDRVINFO: {
struct ethtool_drvinfo drvinfo = {ETHTOOL_GDRVINFO};
e1000_ethtool_gdrvinfo(adapter, &drvinfo);
if(copy_to_user(addr, &drvinfo, sizeof(drvinfo)))
return -EFAULT;
return 0;
}
case ETHTOOL_GSTRINGS: {
struct ethtool_gstrings gstrings = { ETHTOOL_GSTRINGS };
char *strings = NULL;
int err = 0;
if(copy_from_user(&gstrings, addr, sizeof(gstrings)))
return -EFAULT;
switch(gstrings.string_set) {
case ETH_SS_TEST:
gstrings.len = E1000_TEST_LEN;
strings = kmalloc(E1000_TEST_LEN * ETH_GSTRING_LEN,
GFP_KERNEL);
if(!strings)
return -ENOMEM;
memcpy(strings, e1000_gstrings_test, E1000_TEST_LEN *
ETH_GSTRING_LEN);
break;
case ETH_SS_STATS: {
int i;
gstrings.len = E1000_STATS_LEN;
strings = kmalloc(E1000_STATS_LEN * ETH_GSTRING_LEN,
GFP_KERNEL);
if(!strings)
return -ENOMEM;
for(i=0; i < E1000_STATS_LEN; i++) {
memcpy(&strings[i * ETH_GSTRING_LEN],
e1000_gstrings_stats[i].stat_string,
ETH_GSTRING_LEN);
}
break;
}
default:
return -EOPNOTSUPP;
}
if(copy_to_user(addr, &gstrings, sizeof(gstrings)))
err = -EFAULT;
addr += offsetof(struct ethtool_gstrings, data);
if(!err && copy_to_user(addr, strings,
gstrings.len * ETH_GSTRING_LEN))
err = -EFAULT;
kfree(strings);
return err;
}
case ETHTOOL_GREGS: {
struct ethtool_regs regs = {ETHTOOL_GREGS};
uint32_t regs_buff[E1000_REGS_LEN];
if(copy_from_user(&regs, addr, sizeof(regs)))
return -EFAULT;
memset(regs_buff, 0, sizeof(regs_buff));
if (regs.len > E1000_REGS_LEN)
regs.len = E1000_REGS_LEN;
e1000_ethtool_gregs(adapter, &regs, regs_buff);
if(copy_to_user(addr, &regs, sizeof(regs)))
return -EFAULT;
addr += offsetof(struct ethtool_regs, data);
if(copy_to_user(addr, regs_buff, regs.len))
return -EFAULT;
return 0;
}
case ETHTOOL_NWAY_RST: {
if(netif_running(netdev)) {
e1000_down(adapter);
e1000_up(adapter);
}
return 0;
}
case ETHTOOL_PHYS_ID: {
struct ethtool_value id;
if(copy_from_user(&id, addr, sizeof(id)))
return -EFAULT;
return e1000_ethtool_led_blink(adapter, &id);
}
case ETHTOOL_GLINK: {
struct ethtool_value link = {ETHTOOL_GLINK};
link.data = netif_carrier_ok(netdev);
if(copy_to_user(addr, &link, sizeof(link)))
return -EFAULT;
return 0;
}
case ETHTOOL_GWOL: {
struct ethtool_wolinfo wol = {ETHTOOL_GWOL};
e1000_ethtool_gwol(adapter, &wol);
if(copy_to_user(addr, &wol, sizeof(wol)) != 0)
return -EFAULT;
return 0;
}
case ETHTOOL_SWOL: {
struct ethtool_wolinfo wol;
if(copy_from_user(&wol, addr, sizeof(wol)) != 0)
return -EFAULT;
return e1000_ethtool_swol(adapter, &wol);
}
case ETHTOOL_GEEPROM: {
struct ethtool_eeprom eeprom = {ETHTOOL_GEEPROM};
struct e1000_hw *hw = &adapter->hw;
uint16_t *eeprom_buff;
void *ptr;
int err = 0;
if(copy_from_user(&eeprom, addr, sizeof(eeprom)))
return -EFAULT;
eeprom_buff = kmalloc(hw->eeprom.word_size * 2, GFP_KERNEL);
if(!eeprom_buff)
return -ENOMEM;
if((err = e1000_ethtool_geeprom(adapter, &eeprom,
eeprom_buff)))
goto err_geeprom_ioctl;
if(copy_to_user(addr, &eeprom, sizeof(eeprom))) {
err = -EFAULT;
goto err_geeprom_ioctl;
}
addr += offsetof(struct ethtool_eeprom, data);
ptr = ((void *)eeprom_buff) + (eeprom.offset & 1);
if(copy_to_user(addr, ptr, eeprom.len))
err = -EFAULT;
err_geeprom_ioctl:
kfree(eeprom_buff);
return err;
}
case ETHTOOL_SEEPROM: {
struct ethtool_eeprom eeprom;
if(copy_from_user(&eeprom, addr, sizeof(eeprom)))
return -EFAULT;
addr += offsetof(struct ethtool_eeprom, data);
return e1000_ethtool_seeprom(adapter, &eeprom, addr);
}
case ETHTOOL_GRINGPARAM: {
struct ethtool_ringparam ering = {ETHTOOL_GRINGPARAM};
e1000_ethtool_gring(adapter, &ering);
if(copy_to_user(addr, &ering, sizeof(ering)))
return -EFAULT;
return 0;
}
case ETHTOOL_SRINGPARAM: {
struct ethtool_ringparam ering;
if(copy_from_user(&ering, addr, sizeof(ering)))
return -EFAULT;
return e1000_ethtool_sring(adapter, &ering);
}
case ETHTOOL_GPAUSEPARAM: {
struct ethtool_pauseparam epause = {ETHTOOL_GPAUSEPARAM};
e1000_ethtool_gpause(adapter, &epause);
if(copy_to_user(addr, &epause, sizeof(epause)))
return -EFAULT;
return 0;
}
case ETHTOOL_SPAUSEPARAM: {
struct ethtool_pauseparam epause;
if(copy_from_user(&epause, addr, sizeof(epause)))
return -EFAULT;
return e1000_ethtool_spause(adapter, &epause);
}
case ETHTOOL_GSTATS: {
struct {
struct ethtool_stats eth_stats;
uint64_t data[E1000_STATS_LEN];
} stats = { {ETHTOOL_GSTATS, E1000_STATS_LEN} };
int i;
e1000_update_stats(adapter);
for(i = 0; i < E1000_STATS_LEN; i++)
stats.data[i] = (e1000_gstrings_stats[i].sizeof_stat ==
sizeof(uint64_t)) ?
*(uint64_t *)((char *)adapter +
e1000_gstrings_stats[i].stat_offset) :
*(uint32_t *)((char *)adapter +
e1000_gstrings_stats[i].stat_offset);
if(copy_to_user(addr, &stats, sizeof(stats)))
return -EFAULT;
return 0;
} }
case ETHTOOL_TEST: { return 0;
struct { }
struct ethtool_test eth_test;
uint64_t data[E1000_TEST_LEN];
} test = { {ETHTOOL_TEST} };
int err;
if(copy_from_user(&test.eth_test, addr, sizeof(test.eth_test)))
return -EFAULT;
test.eth_test.len = E1000_TEST_LEN;
if((err = e1000_ethtool_test(adapter, &test.eth_test,
test.data)))
return err;
if(copy_to_user(addr, &test, sizeof(test)) != 0) static uint32_t
return -EFAULT; e1000_get_link(struct net_device *netdev)
return 0; {
} return netif_carrier_ok(netdev);
case ETHTOOL_GRXCSUM: { }
struct ethtool_value edata = { ETHTOOL_GRXCSUM };
edata.data = adapter->rx_csum; static int
if (copy_to_user(addr, &edata, sizeof(edata))) e1000_get_stats_count(struct net_device *netdev)
return -EFAULT; {
return 0; return E1000_STATS_LEN;
} }
case ETHTOOL_SRXCSUM: {
struct ethtool_value edata;
if (copy_from_user(&edata, addr, sizeof(edata))) static void
return -EFAULT; e1000_get_ethtool_stats(struct net_device *netdev,
adapter->rx_csum = edata.data; struct ethtool_stats *stats, uint64_t *data)
if(netif_running(netdev)) { {
e1000_down(adapter); struct e1000_adapter *adapter = netdev->priv;
e1000_up(adapter); int i;
} else
e1000_reset(adapter);
return 0;
}
case ETHTOOL_GTXCSUM: {
struct ethtool_value edata = { ETHTOOL_GTXCSUM };
edata.data = e1000_update_stats(adapter);
(netdev->features & NETIF_F_HW_CSUM) != 0; for(i = 0; i < E1000_STATS_LEN; i++) {
if (copy_to_user(addr, &edata, sizeof(edata))) char *p = (char *)adapter+e1000_gstrings_stats[i].stat_offset;
return -EFAULT; data[i] = (e1000_gstrings_stats[i].sizeof_stat == sizeof(uint64_t))
return 0; ? *(uint64_t *)p : *(uint32_t *)p;
} }
case ETHTOOL_STXCSUM: { }
struct ethtool_value edata;
if (copy_from_user(&edata, addr, sizeof(edata))) static void
return -EFAULT; e1000_get_strings(struct net_device *netdev, uint32_t stringset, uint8_t *data)
{
int i;
if(adapter->hw.mac_type < e1000_82543) { switch(stringset) {
if (edata.data != 0) case ETH_SS_TEST:
return -EINVAL; memcpy(data, *e1000_gstrings_test,
return 0; E1000_TEST_LEN*ETH_GSTRING_LEN);
break;
case ETH_SS_STATS:
for (i=0; i < E1000_STATS_LEN; i++) {
memcpy(data + i * ETH_GSTRING_LEN,
e1000_gstrings_stats[i].stat_string,
ETH_GSTRING_LEN);
} }
break;
if (edata.data)
netdev->features |= NETIF_F_HW_CSUM;
else
netdev->features &= ~NETIF_F_HW_CSUM;
return 0;
}
case ETHTOOL_GSG: {
struct ethtool_value edata = { ETHTOOL_GSG };
edata.data =
(netdev->features & NETIF_F_SG) != 0;
if (copy_to_user(addr, &edata, sizeof(edata)))
return -EFAULT;
return 0;
} }
case ETHTOOL_SSG: { }
struct ethtool_value edata;
if (copy_from_user(&edata, addr, sizeof(edata)))
return -EFAULT;
if (edata.data)
netdev->features |= NETIF_F_SG;
else
netdev->features &= ~NETIF_F_SG;
return 0; struct ethtool_ops e1000_ethtool_ops = {
} .get_settings = e1000_get_settings,
.set_settings = e1000_set_settings,
.get_drvinfo = e1000_get_drvinfo,
.get_regs_len = e1000_get_regs_len,
.get_regs = e1000_get_regs,
.get_wol = e1000_get_wol,
.set_wol = e1000_set_wol,
.get_msglevel = e1000_get_msglevel,
.set_msglevel = e1000_set_msglevel,
.nway_reset = e1000_nway_reset,
.get_link = e1000_get_link,
.get_eeprom_len = e1000_get_eeprom_len,
.get_eeprom = e1000_get_eeprom,
.set_eeprom = e1000_set_eeprom,
.get_ringparam = e1000_get_ringparam,
.set_ringparam = e1000_set_ringparam,
.get_pauseparam = e1000_get_pauseparam,
.set_pauseparam = e1000_set_pauseparam,
.get_rx_csum = e1000_get_rx_csum,
.set_rx_csum = e1000_set_rx_csum,
.get_tx_csum = e1000_get_tx_csum,
.set_tx_csum = e1000_set_tx_csum,
.get_sg = e1000_get_sg,
.set_sg = e1000_set_sg,
#ifdef NETIF_F_TSO #ifdef NETIF_F_TSO
case ETHTOOL_GTSO: { .get_tso = e1000_get_tso,
struct ethtool_value edata = { ETHTOOL_GTSO }; .set_tso = e1000_set_tso,
edata.data = (netdev->features & NETIF_F_TSO) != 0;
if (copy_to_user(addr, &edata, sizeof(edata)))
return -EFAULT;
return 0;
}
case ETHTOOL_STSO: {
struct ethtool_value edata;
if (copy_from_user(&edata, addr, sizeof(edata)))
return -EFAULT;
if ((adapter->hw.mac_type < e1000_82544) ||
(adapter->hw.mac_type == e1000_82547)) {
if (edata.data != 0)
return -EINVAL;
return 0;
}
if (edata.data)
netdev->features |= NETIF_F_TSO;
else
netdev->features &= ~NETIF_F_TSO;
return 0;
}
#endif #endif
default: .self_test_count = e1000_diag_test_count,
return -EOPNOTSUPP; .self_test = e1000_diag_test,
} .get_strings = e1000_get_strings,
} .phys_id = e1000_phys_id,
.get_stats_count = e1000_get_stats_count,
.get_ethtool_stats = e1000_get_ethtool_stats,
};
void set_ethtool_ops(struct net_device *netdev)
{
SET_ETHTOOL_OPS(netdev, &e1000_ethtool_ops);
}
...@@ -170,6 +170,7 @@ static void e1000_alloc_rx_buffers(struct e1000_adapter *adapter); ...@@ -170,6 +170,7 @@ static void e1000_alloc_rx_buffers(struct e1000_adapter *adapter);
static int e1000_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd); static int e1000_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd);
static int e1000_mii_ioctl(struct net_device *netdev, struct ifreq *ifr, static int e1000_mii_ioctl(struct net_device *netdev, struct ifreq *ifr,
int cmd); int cmd);
void set_ethtool_ops(struct net_device *netdev);
static void e1000_enter_82542_rst(struct e1000_adapter *adapter); static void e1000_enter_82542_rst(struct e1000_adapter *adapter);
static void e1000_leave_82542_rst(struct e1000_adapter *adapter); static void e1000_leave_82542_rst(struct e1000_adapter *adapter);
static inline void e1000_rx_checksum(struct e1000_adapter *adapter, static inline void e1000_rx_checksum(struct e1000_adapter *adapter,
...@@ -206,7 +207,7 @@ struct notifier_block e1000_notifier_reboot = { ...@@ -206,7 +207,7 @@ struct notifier_block e1000_notifier_reboot = {
/* Exported from other modules */ /* Exported from other modules */
extern void e1000_check_options(struct e1000_adapter *adapter); extern void e1000_check_options(struct e1000_adapter *adapter);
extern int e1000_ethtool_ioctl(struct net_device *netdev, struct ifreq *ifr);
static struct pci_driver e1000_driver = { static struct pci_driver e1000_driver = {
.name = e1000_driver_name, .name = e1000_driver_name,
...@@ -446,6 +447,7 @@ e1000_probe(struct pci_dev *pdev, ...@@ -446,6 +447,7 @@ e1000_probe(struct pci_dev *pdev,
netdev->set_mac_address = &e1000_set_mac; netdev->set_mac_address = &e1000_set_mac;
netdev->change_mtu = &e1000_change_mtu; netdev->change_mtu = &e1000_change_mtu;
netdev->do_ioctl = &e1000_ioctl; netdev->do_ioctl = &e1000_ioctl;
set_ethtool_ops(netdev);
netdev->tx_timeout = &e1000_tx_timeout; netdev->tx_timeout = &e1000_tx_timeout;
netdev->watchdog_timeo = 5 * HZ; netdev->watchdog_timeo = 5 * HZ;
#ifdef CONFIG_E1000_NAPI #ifdef CONFIG_E1000_NAPI
...@@ -2497,8 +2499,6 @@ e1000_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) ...@@ -2497,8 +2499,6 @@ e1000_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
case SIOCGMIIREG: case SIOCGMIIREG:
case SIOCSMIIREG: case SIOCSMIIREG:
return e1000_mii_ioctl(netdev, ifr, cmd); return e1000_mii_ioctl(netdev, ifr, cmd);
case SIOCETHTOOL:
return e1000_ethtool_ioctl(netdev, ifr);
default: default:
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment