static int e1000_set_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) { struct e1000_adapter *adapter = netdev->priv; struct e1000_hw *hw = &adapter->hw; if(ecmd->autoneg == AUTONEG_ENABLE) { hw->autoneg = 1; hw->autoneg_advertised = 0x002F; ecmd->advertising = 0x002F; } else if(e1000_set_spd_dplx(adapter, ecmd->speed + ecmd->duplex)) return -EINVAL; /* reset the link */ if(netif_running(adapter->netdev)) { e1000_down(adapter); e1000_reset(adapter); e1000_up(adapter); } else e1000_reset(adapter); return 0; }
static int e1000_set_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *pause) { struct e1000_adapter *adapter = netdev->priv; struct e1000_hw *hw = &adapter->hw; adapter->fc_autoneg = pause->autoneg; if(pause->rx_pause && pause->tx_pause) hw->fc = e1000_fc_full; else if(pause->rx_pause && !pause->tx_pause) hw->fc = e1000_fc_rx_pause; else if(!pause->rx_pause && pause->tx_pause) hw->fc = e1000_fc_tx_pause; else if(!pause->rx_pause && !pause->tx_pause) hw->fc = e1000_fc_none; hw->original_fc = hw->fc; if(adapter->fc_autoneg == AUTONEG_ENABLE) { if(netif_running(adapter->netdev)) { e1000_down(adapter); e1000_up(adapter); } else e1000_reset(adapter); } else return ((hw->media_type == e1000_media_type_fiber) ? e1000_setup_link(hw) : e1000_force_mac_fc(hw)); return 0; }
static void e1000_diag_test(struct net_device *netdev, struct ethtool_test *eth_test, uint64_t *data) { struct e1000_adapter *adapter = netdev->priv; boolean_t if_running = netif_running(netdev); if(eth_test->flags == ETH_TEST_FL_OFFLINE) { /* 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 * interfere with test result */ if(e1000_link_test(adapter, &data[4])) eth_test->flags |= ETH_TEST_FL_FAILED; if(if_running) e1000_down(adapter); else e1000_reset(adapter); if(e1000_reg_test(adapter, &data[0])) eth_test->flags |= ETH_TEST_FL_FAILED; e1000_reset(adapter); if(e1000_eeprom_test(adapter, &data[1])) eth_test->flags |= ETH_TEST_FL_FAILED; e1000_reset(adapter); if(e1000_intr_test(adapter, &data[2])) eth_test->flags |= ETH_TEST_FL_FAILED; e1000_reset(adapter); if(e1000_loopback_test(adapter, &data[3])) eth_test->flags |= ETH_TEST_FL_FAILED; /* restore speed, duplex, autoneg settings */ adapter->hw.autoneg_advertised = autoneg_advertised; adapter->hw.forced_speed_duplex = forced_speed_duplex; adapter->hw.autoneg = autoneg; e1000_reset(adapter); if(if_running) e1000_up(adapter); } else { /* Online tests */ if(e1000_link_test(adapter, &data[4])) eth_test->flags |= ETH_TEST_FL_FAILED; /* Offline tests aren't run; pass by default */ data[0] = 0; data[1] = 0; data[2] = 0; data[3] = 0; } }
static int e1000_ethtool_spause(struct e1000_adapter *adapter, struct ethtool_pauseparam *epause) { struct e1000_hw *hw = &adapter->hw; adapter->fc_autoneg = epause->autoneg; if(epause->rx_pause && epause->tx_pause) hw->fc = e1000_fc_full; else if(epause->rx_pause && !epause->tx_pause) hw->fc = e1000_fc_rx_pause; else if(!epause->rx_pause && epause->tx_pause) hw->fc = e1000_fc_tx_pause; else if(!epause->rx_pause && !epause->tx_pause) hw->fc = e1000_fc_none; hw->original_fc = hw->fc; if(adapter->fc_autoneg == AUTONEG_ENABLE) { if(netif_running(adapter->netdev)) { e1000_down(adapter); e1000_up(adapter); } else e1000_reset(adapter); } else return e1000_force_mac_fc(hw); return 0; }
static int e1000_nway_reset(struct net_device *netdev) { struct e1000_adapter *adapter = netdev->priv; if(netif_running(netdev)) { e1000_down(adapter); e1000_up(adapter); } return 0; }
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 int e1000_ethtool_test(struct e1000_adapter *adapter, struct ethtool_test *eth_test, uint64_t *data) { boolean_t if_running = netif_running(adapter->netdev); if(eth_test->flags == ETH_TEST_FL_OFFLINE) { /* Offline tests */ /* Link test performed before hardware reset so autoneg doesn't * interfere with test result */ if(e1000_link_test(adapter, &data[4])) eth_test->flags |= ETH_TEST_FL_FAILED; if(if_running) e1000_down(adapter); else e1000_reset(adapter); if(e1000_reg_test(adapter, &data[0])) eth_test->flags |= ETH_TEST_FL_FAILED; e1000_reset(adapter); if(e1000_eeprom_test(adapter, &data[1])) eth_test->flags |= ETH_TEST_FL_FAILED; e1000_reset(adapter); if(e1000_intr_test(adapter, &data[2])) eth_test->flags |= ETH_TEST_FL_FAILED; e1000_reset(adapter); if(e1000_loopback_test(adapter, &data[3])) eth_test->flags |= ETH_TEST_FL_FAILED; e1000_reset(adapter); if(if_running) e1000_up(adapter); } else { /* Online tests */ if(e1000_link_test(adapter, &data[4])) eth_test->flags |= ETH_TEST_FL_FAILED; /* Offline tests aren't run; pass by default */ data[0] = 0; data[1] = 0; data[2] = 0; data[3] = 0; } return 0; }
static int e1000_set_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring) { struct e1000_adapter *adapter = netdev->priv; e1000_mac_type mac_type = adapter->hw.mac_type; struct e1000_desc_ring *txdr = &adapter->tx_ring; struct e1000_desc_ring *rxdr = &adapter->rx_ring; struct e1000_desc_ring tx_old, tx_new, rx_old, rx_new; int err; tx_old = adapter->tx_ring; rx_old = adapter->rx_ring; if ((ring->rx_mini_pending) || (ring->rx_jumbo_pending)) return -EINVAL; if(netif_running(adapter->netdev)) e1000_down(adapter); rxdr->count = max(ring->rx_pending,(uint32_t)E1000_MIN_RXD); rxdr->count = min(rxdr->count,(uint32_t)(mac_type < e1000_82544 ? E1000_MAX_RXD : E1000_MAX_82544_RXD)); E1000_ROUNDUP(rxdr->count, REQ_RX_DESCRIPTOR_MULTIPLE); txdr->count = max(ring->tx_pending,(uint32_t)E1000_MIN_TXD); txdr->count = min(txdr->count,(uint32_t)(mac_type < e1000_82544 ? E1000_MAX_TXD : E1000_MAX_82544_TXD)); E1000_ROUNDUP(txdr->count, REQ_TX_DESCRIPTOR_MULTIPLE); if(netif_running(adapter->netdev)) { /* Try to get new resources before deleting old */ if((err = e1000_setup_rx_resources(adapter))) goto err_setup_rx; if((err = e1000_setup_tx_resources(adapter))) goto err_setup_tx; /* save the new, restore the old in order to free it, * then restore the new back again */ rx_new = adapter->rx_ring; tx_new = adapter->tx_ring; adapter->rx_ring = rx_old; adapter->tx_ring = tx_old; e1000_free_rx_resources(adapter); e1000_free_tx_resources(adapter); adapter->rx_ring = rx_new; adapter->tx_ring = tx_new; if((err = e1000_up(adapter))) return err; } return 0; err_setup_tx: e1000_free_rx_resources(adapter); err_setup_rx: adapter->rx_ring = rx_old; adapter->tx_ring = tx_old; e1000_up(adapter); return err; }
int e1000_ethtool_ioctl(struct net_device *netdev, struct ifreq *ifr) { struct e1000_adapter *adapter = netdev->priv; void *addr = ifr->ifr_data; uint32_t cmd; 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(®s, addr, sizeof(regs))) return -EFAULT; e1000_ethtool_gregs(adapter, ®s, regs_buff); if(copy_to_user(addr, ®s, 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_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; 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: { 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) return -EFAULT; return 0; } case ETHTOOL_GRXCSUM: { struct ethtool_value edata = { ETHTOOL_GRXCSUM }; edata.data = adapter->rx_csum; if (copy_to_user(addr, &edata, sizeof(edata))) return -EFAULT; return 0; } case ETHTOOL_SRXCSUM: { struct ethtool_value edata; if (copy_from_user(&edata, addr, sizeof(edata))) return -EFAULT; adapter->rx_csum = edata.data; if(netif_running(netdev)) { e1000_down(adapter); e1000_up(adapter); } else e1000_reset(adapter); return 0; } case ETHTOOL_GTXCSUM: { struct ethtool_value edata = { ETHTOOL_GTXCSUM }; edata.data = (netdev->features & NETIF_F_HW_CSUM) != 0; if (copy_to_user(addr, &edata, sizeof(edata))) return -EFAULT; return 0; } case ETHTOOL_STXCSUM: { struct ethtool_value edata; if (copy_from_user(&edata, addr, sizeof(edata))) return -EFAULT; if(adapter->hw.mac_type < e1000_82543) { if (edata.data != 0) return -EINVAL; return 0; } 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; } #ifdef NETIF_F_TSO case ETHTOOL_GTSO: { struct ethtool_value edata = { ETHTOOL_GTSO }; 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 default: return -EOPNOTSUPP; } }