patch-2.4.21 linux-2.4.21/drivers/net/e1000/e1000_ethtool.c

Next file: linux-2.4.21/drivers/net/e1000/e1000_hw.c
Previous file: linux-2.4.21/drivers/net/e1000/e1000.h
Back to the patch index
Back to the overall index

diff -urN linux-2.4.20/drivers/net/e1000/e1000_ethtool.c linux-2.4.21/drivers/net/e1000/e1000_ethtool.c
@@ -1,7 +1,7 @@
 /*******************************************************************************
 
   
-  Copyright(c) 1999 - 2002 Intel Corporation. All rights reserved.
+  Copyright(c) 1999 - 2003 Intel Corporation. All rights reserved.
   
   This program is free software; you can redistribute it and/or modify it 
   under the terms of the GNU General Public License as published by the Free 
@@ -38,6 +38,17 @@
 extern int e1000_up(struct e1000_adapter *adapter);
 extern void e1000_down(struct e1000_adapter *adapter);
 extern void e1000_reset(struct e1000_adapter *adapter);
+extern int e1000_set_spd_dplx(struct e1000_adapter *adapter, uint16_t spddplx);
+
+static char e1000_gstrings_stats[][ETH_GSTRING_LEN] = {
+	"rx_packets", "tx_packets", "rx_bytes", "tx_bytes", "rx_errors",
+	"tx_errors", "rx_dropped", "tx_dropped", "multicast", "collisions",
+	"rx_length_errors", "rx_over_errors", "rx_crc_errors",
+	"rx_frame_errors", "rx_fifo_errors", "rx_missed_errors",
+	"tx_aborted_errors", "tx_carrier_errors", "tx_fifo_errors",
+	"tx_heartbeat_errors", "tx_window_errors",
+};
+#define E1000_STATS_LEN	sizeof(e1000_gstrings_stats) / ETH_GSTRING_LEN
 
 static void
 e1000_ethtool_gset(struct e1000_adapter *adapter, struct ethtool_cmd *ecmd)
@@ -119,30 +130,9 @@
 		hw->autoneg = 1;
 		hw->autoneg_advertised = 0x002F;
 		ecmd->advertising = 0x002F;
-	} else {
-		hw->autoneg = 0;
-		switch(ecmd->speed + ecmd->duplex) {
-		case SPEED_10 + DUPLEX_HALF:
-			hw->forced_speed_duplex = e1000_10_half;
-			break;
-		case SPEED_10 + DUPLEX_FULL:
-			hw->forced_speed_duplex = e1000_10_full;
-			break;
-		case SPEED_100 + DUPLEX_HALF:
-			hw->forced_speed_duplex = e1000_100_half;
-			break;
-		case SPEED_100 + DUPLEX_FULL:
-			hw->forced_speed_duplex = e1000_100_full;
-			break;
-		case SPEED_1000 + DUPLEX_FULL:
-			hw->autoneg = 1;
-			hw->autoneg_advertised = ADVERTISE_1000_FULL;
-			break;
-		case SPEED_1000 + DUPLEX_HALF: /* not supported */
-		default:
+	} else
+		if(e1000_set_spd_dplx(adapter, ecmd->speed + ecmd->duplex))
 			return -EINVAL;
-		}
-	}
 
 	/* reset the link */
 
@@ -155,16 +145,6 @@
 	return 0;
 }
 
-static inline int
-e1000_eeprom_size(struct e1000_hw *hw)
-{
-	if((hw->mac_type > e1000_82544) &&
-	   (E1000_READ_REG(hw, EECD) & E1000_EECD_SIZE))
-		return 512;
-	else
-		return 128;
-}
-
 static void
 e1000_ethtool_gdrvinfo(struct e1000_adapter *adapter,
                        struct ethtool_drvinfo *drvinfo)
@@ -173,9 +153,10 @@
 	strncpy(drvinfo->version, e1000_driver_version, 32);
 	strncpy(drvinfo->fw_version, "N/A", 32);
 	strncpy(drvinfo->bus_info, adapter->pdev->slot_name, 32);
+	drvinfo->n_stats = E1000_STATS_LEN;
 #define E1000_REGS_LEN 32
 	drvinfo->regdump_len  = E1000_REGS_LEN * sizeof(uint32_t);
-	drvinfo->eedump_len  = e1000_eeprom_size(&adapter->hw);
+	drvinfo->eedump_len = adapter->hw.eeprom.word_size * 2;
 }
 
 static void
@@ -194,7 +175,7 @@
 	regs_buff[4]  = E1000_READ_REG(hw, RDH);
 	regs_buff[5]  = E1000_READ_REG(hw, RDT);
 	regs_buff[6]  = E1000_READ_REG(hw, RDTR);
-	
+
 	regs_buff[7]  = E1000_READ_REG(hw, TCTL);
 	regs_buff[8]  = E1000_READ_REG(hw, TDLEN);
 	regs_buff[9]  = E1000_READ_REG(hw, TDH);
@@ -209,38 +190,50 @@
                       struct ethtool_eeprom *eeprom, uint16_t *eeprom_buff)
 {
 	struct e1000_hw *hw = &adapter->hw;
-	int i, max_len, first_word, last_word;
+	int first_word, last_word;
+	int ret_val = 0;
 
-	if(eeprom->len == 0)
-		return -EINVAL;
+	if(eeprom->len == 0) {
+		ret_val = -EINVAL;
+		goto geeprom_error;
+	}
 
 	eeprom->magic = hw->vendor_id | (hw->device_id << 16);
 
-	max_len = e1000_eeprom_size(hw);
+	if(eeprom->offset > eeprom->offset + eeprom->len) {
+		ret_val = -EINVAL;
+		goto geeprom_error;
+	}
 
-	if(eeprom->offset > eeprom->offset + eeprom->len)
-		return -EINVAL;
-	
-	if((eeprom->offset + eeprom->len) > max_len)
-		eeprom->len = (max_len - eeprom->offset);
+	if((eeprom->offset + eeprom->len) > (hw->eeprom.word_size * 2))
+		eeprom->len = ((hw->eeprom.word_size * 2) - eeprom->offset);
 
 	first_word = eeprom->offset >> 1;
 	last_word = (eeprom->offset + eeprom->len - 1) >> 1;
 
-	for(i = 0; i <= (last_word - first_word); i++)
-		e1000_read_eeprom(hw, first_word + i, &eeprom_buff[i]);
-
-	return 0;
+	if(hw->eeprom.type == e1000_eeprom_spi)
+		ret_val = e1000_read_eeprom(hw, first_word,
+					    last_word - first_word + 1,
+					    eeprom_buff);
+	else {
+		uint16_t i;
+		for (i = 0; i < last_word - first_word + 1; i++)
+			if((ret_val = e1000_read_eeprom(hw, first_word + i, 1,
+						       &eeprom_buff[i])))
+				break;
+	}
+geeprom_error:
+	return ret_val;
 }
 
-static int 
+static int
 e1000_ethtool_seeprom(struct e1000_adapter *adapter,
                       struct ethtool_eeprom *eeprom, void *user_data)
 {
 	struct e1000_hw *hw = &adapter->hw;
-	uint16_t eeprom_buff[256];
-	int i, max_len, first_word, last_word;
+	uint16_t *eeprom_buff;
 	void *ptr;
+	int max_len, first_word, last_word, ret_val = 0;
 
 	if(eeprom->len == 0)
 		return -EOPNOTSUPP;
@@ -248,38 +241,47 @@
 	if(eeprom->magic != (hw->vendor_id | (hw->device_id << 16)))
 		return -EFAULT;
 
-	max_len = e1000_eeprom_size(hw);
+	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;
 	last_word = (eeprom->offset + eeprom->len - 1) >> 1;
+	eeprom_buff = kmalloc(max_len, GFP_KERNEL);
+	if(eeprom_buff == NULL)
+		return -ENOMEM;
+
 	ptr = (void *)eeprom_buff;
 
 	if(eeprom->offset & 1) {
 		/* need read/modify/write of first changed EEPROM word */
 		/* only the second byte of the word is being modified */
-		e1000_read_eeprom(hw, first_word, &eeprom_buff[0]);
+		ret_val = e1000_read_eeprom(hw, first_word, 1,
+					    &eeprom_buff[0]);
 		ptr++;
 	}
-	if((eeprom->offset + eeprom->len) & 1) {
+	if(((eeprom->offset + eeprom->len) & 1) && (ret_val == 0)) {
 		/* need read/modify/write of last changed EEPROM word */
 		/* only the first byte of the word is being modified */
-		e1000_read_eeprom(hw, last_word,
+		ret_val = e1000_read_eeprom(hw, last_word, 1,
 		                  &eeprom_buff[last_word - first_word]);
 	}
-	if(copy_from_user(ptr, user_data, eeprom->len))
-		return -EFAULT;
+	if((ret_val != 0) || copy_from_user(ptr, user_data, eeprom->len)) {
+		ret_val = -EFAULT;
+		goto seeprom_error;
+	}
 
-	for(i = 0; i <= (last_word - first_word); i++)
-		e1000_write_eeprom(hw, first_word + i, eeprom_buff[i]);
+	ret_val = e1000_write_eeprom(hw, first_word,
+				     last_word - first_word + 1, eeprom_buff);
 
 	/* Update the checksum over the first part of the EEPROM if needed */
-	if(first_word <= EEPROM_CHECKSUM_REG)
+	if((ret_val == 0) && first_word <= EEPROM_CHECKSUM_REG)
 		e1000_update_eeprom_checksum(hw);
 
-	return 0;
+seeprom_error:
+	kfree(eeprom_buff);
+	return ret_val;
 }
 
 static void
@@ -306,12 +308,10 @@
 		/* Fall Through */
 
 	default:
-		wol->supported = WAKE_PHY | WAKE_UCAST | 
-				 WAKE_MCAST | WAKE_BCAST | WAKE_MAGIC;
-		
+		wol->supported = WAKE_UCAST | WAKE_MCAST |
+				 WAKE_BCAST | WAKE_MAGIC;
+
 		wol->wolopts = 0;
-		if(adapter->wol & E1000_WUFC_LNKC)
-			wol->wolopts |= WAKE_PHY;
 		if(adapter->wol & E1000_WUFC_EX)
 			wol->wolopts |= WAKE_UCAST;
 		if(adapter->wol & E1000_WUFC_MC)
@@ -343,13 +343,11 @@
 		/* Fall Through */
 
 	default:
-		if(wol->wolopts & (WAKE_ARP | WAKE_MAGICSECURE))
+		if(wol->wolopts & (WAKE_PHY | WAKE_ARP | WAKE_MAGICSECURE))
 			return -EOPNOTSUPP;
 
 		adapter->wol = 0;
 
-		if(wol->wolopts & WAKE_PHY)
-			adapter->wol |= E1000_WUFC_LNKC;
 		if(wol->wolopts & WAKE_UCAST)
 			adapter->wol |= E1000_WUFC_EX;
 		if(wol->wolopts & WAKE_MCAST)
@@ -374,7 +372,7 @@
 e1000_led_blink_callback(unsigned long data)
 {
 	struct e1000_adapter *adapter = (struct e1000_adapter *) data;
-	
+
 	if(test_and_change_bit(E1000_LED_ON, &adapter->led_status))
 		e1000_led_off(&adapter->hw);
 	else
@@ -394,13 +392,13 @@
 
 	e1000_setup_led(&adapter->hw);
 	mod_timer(&adapter->blink_timer, jiffies);
-	
+
 	set_current_state(TASK_INTERRUPTIBLE);
 	if(id->data)
 		schedule_timeout(id->data * HZ);
 	else
 		schedule_timeout(MAX_SCHEDULE_TIMEOUT);
-	
+
 	del_timer_sync(&adapter->blink_timer);
 	e1000_led_off(&adapter->hw);
 	clear_bit(E1000_LED_ON, &adapter->led_status);
@@ -442,6 +440,28 @@
 			return -EFAULT;
 		return 0;
 	}
+	case ETHTOOL_GSTRINGS: {
+		struct ethtool_gstrings gstrings = { ETHTOOL_GSTRINGS };
+		char *strings = NULL;
+
+		if(copy_from_user(&gstrings, addr, sizeof(gstrings)))
+			return -EFAULT;
+		switch(gstrings.string_set) {
+		case ETH_SS_STATS:
+			gstrings.len = E1000_STATS_LEN;
+			strings = *e1000_gstrings_stats;
+			break;
+		default:
+			return -EOPNOTSUPP;
+		}
+		if(copy_to_user(addr, &gstrings, sizeof(gstrings)))
+			return -EFAULT;
+		addr += offsetof(struct ethtool_gstrings, data);
+		if(copy_to_user(addr, strings,
+		   gstrings.len * ETH_GSTRING_LEN))
+			return -EFAULT;
+		return 0;
+	}
 	case ETHTOOL_GREGS: {
 		struct ethtool_regs regs = {ETHTOOL_GREGS};
 		uint32_t regs_buff[E1000_REGS_LEN];
@@ -497,26 +517,39 @@
 	}
 	case ETHTOOL_GEEPROM: {
 		struct ethtool_eeprom eeprom = {ETHTOOL_GEEPROM};
-		uint16_t eeprom_buff[256];
+		struct e1000_hw *hw = &adapter->hw;
+		uint16_t *eeprom_buff;
 		void *ptr;
-		int err;
+		int err = 0;
 
-		if(copy_from_user(&eeprom, addr, sizeof(eeprom)))
-			return -EFAULT;
+		eeprom_buff = kmalloc(hw->eeprom.word_size * 2, GFP_KERNEL);
 
-		if((err = e1000_ethtool_geeprom(adapter, 
-			&eeprom, eeprom_buff)))
-			return err;
+		if(eeprom_buff == NULL)
+			return -ENOMEM;
 
-		if(copy_to_user(addr, &eeprom, sizeof(eeprom)))
-			return -EFAULT;
+		if(copy_from_user(&eeprom, addr, sizeof(eeprom))) {
+			err = -EFAULT;
+			goto err_geeprom_ioctl;
+		}
+
+		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))
-			return -EFAULT;
-		return 0;
+			err = -EFAULT;
+
+err_geeprom_ioctl:
+		kfree(eeprom_buff);
+		return err;
 	}
 	case ETHTOOL_SEEPROM: {
 		struct ethtool_eeprom eeprom;
@@ -530,6 +563,20 @@
 		addr += offsetof(struct ethtool_eeprom, data);
 		return e1000_ethtool_seeprom(adapter, &eeprom, addr);
 	}
+	case ETHTOOL_GSTATS: {
+		struct {
+			struct ethtool_stats cmd;
+			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] =
+				((unsigned long *)&adapter->net_stats)[i];
+		if(copy_to_user(addr, &stats, sizeof(stats)))
+			return -EFAULT;
+		return 0;
+	}
 	default:
 		return -EOPNOTSUPP;
 	}

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)