[PATCH] starfire net driver update

Ion Badulescu (ionut@cs.columbia.edu)
Tue, 10 Jul 2001 09:36:40 -0700 (PDT)


Hi,

This patch fixes two real problems: missing initialization of a register
which broke 100mbit half-duplex, and dereferencing of freed memory. It
also massages the whitespace a bit.

Please apply.

Thanks,
Ion

-- 
  It is better to keep your mouth shut and be thought a fool,
            than to open it and remove all doubt.
---------------------------
--- ../linux-2.4/drivers/net/starfire.c	Fri Jul  6 00:32:24 2001
+++ linux-2.4/drivers/net/starfire.c	Fri Jul  6 00:46:26 2001
@@ -85,13 +85,18 @@
 	- Fixed 2.2.x compatibility issues introduced in 1.3.1
 	- Fixed ethtool ioctl returning uninitialized memory
 
+	LK1.3.3 (Ion Badulescu)
+	- Initialize the TxMode register properly
+	- Set the MII registers _after_ resetting it
+	- Don't dereference dev->priv after unregister_netdev() has freed it
+
 TODO:
 	- implement tx_timeout() properly
 */
 
 #define DRV_NAME	"starfire"
-#define DRV_VERSION	"1.03+LK1.3.2"
-#define DRV_RELDATE	"June 04, 2001"
+#define DRV_VERSION	"1.03+LK1.3.3"
+#define DRV_RELDATE	"July 05, 2001"
 
 /*
  * Adaptec's license for their Novell drivers (which is where I got the
@@ -192,12 +197,6 @@
 #define skb_first_frag_len(skb)	(skb->len)
 #endif /* not ZEROCOPY */
 
-#if !defined(__OPTIMIZE__)
-#warning  You must compile this file with the correct options!
-#warning  See the last lines of the source file.
-#error You must compile this driver with "-O".
-#endif
-
 #include <linux/version.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
@@ -208,7 +207,6 @@
 #include <linux/delay.h>
 #include <asm/processor.h>		/* Processor type for cache alignment. */
 #include <asm/uaccess.h>
-#include <asm/io.h>
 
 #ifdef HAS_FIRMWARE
 #include "starfire_firmware.h"
@@ -559,7 +557,7 @@
 	unsigned int cur_tx, dirty_tx;
 	unsigned int rx_buf_sz;		/* Based on MTU+slack. */
 	unsigned int tx_full:1,		/* The Tx queue is full. */
-	/* These values are keep track of the transceiver/media in use. */
+	/* These values keep track of the transceiver/media in use. */
 		autoneg:1,		/* Autonegotiation allowed. */
 		full_duplex:1,		/* Full-duplex operation. */
 		speed100:1;		/* Set if speed == 100MBit. */
@@ -572,6 +570,7 @@
 	unsigned char phys[PHY_CNT];	/* MII device addresses. */
 };
 
+
 static int	mdio_read(struct net_device *dev, int phy_id, int location);
 static void	mdio_write(struct net_device *dev, int phy_id, int location, int value);
 static int	netdev_open(struct net_device *dev);
@@ -589,7 +588,7 @@
 static int	netdev_close(struct net_device *dev);
 static void	netdev_media_change(struct net_device *dev);
 
-
+
 
 static int __devinit starfire_init_one(struct pci_dev *pdev,
 				       const struct pci_device_id *ent)
@@ -615,9 +614,9 @@
 	if (pci_enable_device (pdev))
 		return -EIO;
 
-	ioaddr = pci_resource_start (pdev, 0);
-	io_size = pci_resource_len (pdev, 0);
-	if (!ioaddr || ((pci_resource_flags (pdev, 0) & IORESOURCE_MEM) == 0)) {
+	ioaddr = pci_resource_start(pdev, 0);
+	io_size = pci_resource_len(pdev, 0);
+	if (!ioaddr || ((pci_resource_flags(pdev, 0) & IORESOURCE_MEM) == 0)) {
 		printk (KERN_ERR DRV_NAME " %d: no PCI MEM resources, aborting\n", card_idx);
 		return -ENODEV;
 	}
@@ -777,14 +776,13 @@
 err_out_free_res:
 	pci_release_regions (pdev);
 err_out_free_netdev:
-	unregister_netdev (dev);
-	kfree (dev);
+	unregister_netdev(dev);
+	kfree(dev);
 	return -ENODEV;
 }
 
-
-/* Read the MII Management Data I/O (MDIO) interfaces. */
 
+/* Read the MII Management Data I/O (MDIO) interfaces. */
 static int mdio_read(struct net_device *dev, int phy_id, int location)
 {
 	long mdio_addr = dev->base_addr + MIICtrl + (phy_id<<7) + (location<<2);
@@ -800,6 +798,7 @@
 	return result & 0xffff;
 }
 
+
 static void mdio_write(struct net_device *dev, int phy_id, int location, int value)
 {
 	long mdio_addr = dev->base_addr + MIICtrl + (phy_id<<7) + (location<<2);
@@ -808,7 +807,7 @@
 	return;
 }
 
-
+
 static int netdev_open(struct net_device *dev)
 {
 	struct netdev_private *np = dev->priv;
@@ -931,6 +930,8 @@
 	/* Initialize other registers. */
 	/* Configure the PCI bus bursts and FIFO thresholds. */
 	np->tx_mode = 0x0C04;		/* modified when link is up. */
+	writel(0x8000 | np->tx_mode, ioaddr + TxMode);
+	writel(np->tx_mode, ioaddr + TxMode);
 	np->tx_threshold = 4;
 	writel(np->tx_threshold, ioaddr + TxThreshold);
 
@@ -986,12 +987,12 @@
 	struct netdev_private *np = dev->priv;
 	u16 reg0;
 
-	mdio_write(dev, np->phys[0], MII_ADVERTISE, np->advertising);
 	mdio_write(dev, np->phys[0], MII_BMCR, BMCR_RESET);
 	udelay(500);
 	while (mdio_read(dev, np->phys[0], MII_BMCR) & BMCR_RESET);
 
 	reg0 = mdio_read(dev, np->phys[0], MII_BMCR);
+	mdio_write(dev, np->phys[0], MII_ADVERTISE, np->advertising);
 
 	if (np->autoneg) {
 		reg0 |= BMCR_ANENABLE | BMCR_ANRESTART;
@@ -1098,6 +1099,7 @@
 	return;
 }
 
+
 static int start_tx(struct sk_buff *skb, struct net_device *dev)
 {
 	struct netdev_private *np = dev->priv;
@@ -1214,6 +1216,7 @@
 	return 0;
 }
 
+
 /* The interrupt handler does all of the Rx thread work and cleans up
    after the Tx thread. */
 static void intr_handler(int irq, void *dev_instance, struct pt_regs *rgs)
@@ -1350,6 +1353,7 @@
 #endif
 }
 
+
 /* This routine is logically part of the interrupt handler, but separated
    for clarity and better register allocation. */
 static int netdev_rx(struct net_device *dev)
@@ -1407,11 +1411,9 @@
 			memcpy(skb_put(skb, pkt_len), np->rx_info[entry].skb->tail, pkt_len);
 #endif
 		} else {
-			char *temp;
-
 			pci_unmap_single(np->pci_dev, np->rx_info[entry].mapping, np->rx_buf_sz, PCI_DMA_FROMDEVICE);
 			skb = np->rx_info[entry].skb;
-			temp = skb_put(skb, pkt_len);
+			skb_put(skb, pkt_len);
 			np->rx_info[entry].skb = NULL;
 			np->rx_info[entry].mapping = 0;
 		}
@@ -1570,6 +1572,7 @@
 		np->stats.tx_fifo_errors++;
 }
 
+
 static struct net_device_stats *get_stats(struct net_device *dev)
 {
 	long ioaddr = dev->base_addr;
@@ -1596,6 +1599,7 @@
 	return &np->stats;
 }
 
+
 /* The little-endian AUTODIN II ethernet CRC calculations.
    A big-endian version is also available.
    This is slow but compact code.  Do not use this routine for bulk data,
@@ -1622,6 +1626,7 @@
 	return crc;
 }
 
+
 static void set_rx_mode(struct net_device *dev)
 {
 	long ioaddr = dev->base_addr;
@@ -1658,7 +1663,7 @@
 
 		memset(mc_filter, 0, sizeof(mc_filter));
 		for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
-			 i++, mclist = mclist->next) {
+		     i++, mclist = mclist->next) {
 			int bit_nr = ether_crc_le(ETH_ALEN, mclist->dmi_addr) >> 23;
 			__u32 *fptr = (__u32 *) &mc_filter[(bit_nr >> 4) & ~1];
 
@@ -1679,7 +1684,6 @@
 }
 
 
-
 static int netdev_ethtool_ioctl(struct net_device *dev, void *useraddr)
 {
 	struct ethtool_cmd ecmd;
@@ -1779,7 +1783,6 @@
 }
 
 
-
 static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 {
 	struct netdev_private *np = dev->priv;
@@ -1808,10 +1811,11 @@
 			u16 value = data->val_in;
 			switch (data->reg_num) {
 			case 0:
-				if (value & 0x9000)	/* Autonegotiation. */
+				if (value & (BMCR_RESET | BMCR_ANENABLE))
+					/* Autonegotiation. */
 					np->autoneg = 1;
 				else {
-					np->full_duplex = (value & 0x0100) ? 1 : 0;
+					np->full_duplex = (value & BMCR_FULLDPLX) ? 1 : 0;
 					np->autoneg = 0;
 				}
 				break;
@@ -1921,27 +1925,26 @@
 		BUG();
 
 	np = dev->priv;
-
-	unregister_netdev(dev);
-	iounmap((char *)dev->base_addr);
-	pci_release_regions(pdev);
-
 	if (np->tx_done_q)
-		pci_free_consistent(np->pci_dev, PAGE_SIZE,
+		pci_free_consistent(pdev, PAGE_SIZE,
 				    np->tx_done_q, np->tx_done_q_dma);
 	if (np->rx_done_q)
-		pci_free_consistent(np->pci_dev, PAGE_SIZE,
+		pci_free_consistent(pdev,
+				    sizeof(struct rx_done_desc) * DONE_Q_SIZE,
 				    np->rx_done_q, np->rx_done_q_dma);
 	if (np->tx_ring)
-		pci_free_consistent(np->pci_dev, PAGE_SIZE,
+		pci_free_consistent(pdev, PAGE_SIZE,
 				    np->tx_ring, np->tx_ring_dma);
 	if (np->rx_ring)
-		pci_free_consistent(np->pci_dev, PAGE_SIZE,
+		pci_free_consistent(pdev, PAGE_SIZE,
 				    np->rx_ring, np->rx_ring_dma);
 
-	kfree(dev);
+	unregister_netdev(dev);			/* Will also free np!! */
+	iounmap((char *)dev->base_addr);
+	pci_release_regions(pdev);
 
 	pci_set_drvdata(pdev, NULL);
+	kfree(dev);
 }
 
 

- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/