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(&regs, addr, sizeof(regs)))
			return -EFAULT;
		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_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;
	}
}