patch-2.0.1 linux/drivers/net/de4x5.c

Next file: linux/drivers/net/net_init.c
Previous file: linux/drivers/isdn/teles/card.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.0.0/linux/drivers/net/de4x5.c linux/drivers/net/de4x5.c
@@ -1,4 +1,4 @@
-/*  de4x5.c: A DIGITAL DE425/DE434/DE435/DE500 ethernet driver for Linux.
+/*  de4x5.c: A DIGITAL DE425/DE434/DE435/DE450/DE500 ethernet driver for Linux.
 
     Copyright 1994, 1995 Digital Equipment Corporation.
 
@@ -191,15 +191,25 @@
                           Fix bug in dc21040 and dc21041 autosense code.
 			  Remove buffer copies on receive for Intels.
 			  Change sk_buff handling during media disconnects to
-			  eliminate DUP packets.
+			   eliminate DUP packets.
 			  Add dynamic TX thresholding.
 			  Change all chips to use perfect multicast filtering.
 			  Fix alloc_device() bug <jari@markkus2.fimr.fi>
+      0.43   21-Jun-96    Fix unconnected media TX retry bug.
+                          Add Accton to the list of broken cards.
+			  Fix TX under-run bug for non DC21140 chips.
+			  Fix boot command probe bug in alloc_device() as
+			   reported by <koen.gadeyne@barco.com> and 
+			   <orava@nether.tky.hut.fi>.
+			  Add cache locks to prevent a race condition as
+			   reported by <csd@microplex.com> and 
+			   <baba@beckman.uiuc.edu>.
+			  Upgraded alloc_device() code.
 
     =========================================================================
 */
 
-static const char *version = "de4x5.c:v0.42 96/4/26 davies@wanton.lkg.dec.com\n";
+static const char *version = "de4x5.c:v0.43 96/6/21 davies@wanton.lkg.dec.com\n";
 
 #include <linux/module.h>
 
@@ -226,6 +236,7 @@
 #include <linux/time.h>
 #include <linux/types.h>
 #include <linux/unistd.h>
+#include <linux/ctype.h>
 
 #include "de4x5.h"
 
@@ -274,10 +285,12 @@
 ** Define special SROM detection cases
 */
 static c_char enet_det[][ETH_ALEN] = {
-    {0x00, 0x00, 0xc0, 0x00, 0x00, 0x00}
+    {0x00, 0x00, 0xc0, 0x00, 0x00, 0x00},
+    {0x00, 0x00, 0xe8, 0x00, 0x00, 0x00}
 };
 
-#define SMC 1
+#define SMC    1
+#define ACCTON 2
 
 
 #ifdef DE4X5_DEBUG
@@ -511,6 +524,7 @@
     struct {
 	void *priv;                         /* Original kmalloc'd mem addr  */
 	void *buf;                          /* Original kmalloc'd mem addr  */
+	int lock;                           /* Lock the cache accesses      */
 	s32 csr0;                           /* Saved Bus Mode Register      */
 	s32 csr6;                           /* Saved Operating Mode Reg.    */
 	s32 csr7;                           /* Saved IRQ Mask Register      */
@@ -633,12 +647,15 @@
 static void    eisa_probe(struct device *dev, u_long iobase);
 static void    pci_probe(struct device *dev, u_long iobase);
 static struct  device *alloc_device(struct device *dev, u_long iobase);
+static struct  device *insert_device(struct device *dev, u_long iobase,
+				     int (*init)(struct device *));
 static char    *build_setup_frame(struct device *dev, int mode);
 static void    disable_ast(struct device *dev);
 static void    enable_ast(struct device *dev, u32 time_out);
 static long    de4x5_switch_to_srl(struct device *dev);
 static long    de4x5_switch_to_mii(struct device *dev);
 static void    timeout(struct device *dev, void (*fn)(u_long data), u_long data, u_long msec);
+static int     de4x5_dev_index(char *s);
 static void    de4x5_dbg_open(struct device *dev);
 static void    de4x5_dbg_mii(struct device *dev, int k);
 static void    de4x5_dbg_media(struct device *dev);
@@ -683,7 +700,7 @@
 {
     int tmp = num_de4x5s, status = -ENODEV;
     u_long iobase = dev->base_addr;
-    
+
     eisa_probe(dev, iobase);
     pci_probe(dev, iobase);
     
@@ -691,7 +708,7 @@
 	printk("%s: de4x5_probe() cannot find device at 0x%04lx.\n", dev->name, 
 	       iobase);
     }
-    
+
     /*
     ** Walk the device list to check that at least one device
     ** initialised OK
@@ -1076,6 +1093,7 @@
 	return 0;
     }
 
+    set_bit(0, (void*)&dev->tbusy);              /* Stop send re-tries */
     if (lp->tx_enable == NO) {                   /* Cannot send for now */
 	return -1;                                
     }
@@ -1085,11 +1103,13 @@
     ** interrupts are lost by delayed descriptor status updates relative to
     ** the irq assertion, especially with a busy PCI bus.
     */
-    set_bit(0, (void*)&dev->tbusy);
     cli();
     de4x5_tx(dev);
     sti();
-    
+
+    /* Test if cache is already locked - requeue skb if so */
+    if (set_bit(0, (void *)&lp->cache.lock) && !dev->interrupt) return -1;
+
     /* Transmit descriptor ring full or stale skb */
     if (dev->tbusy || lp->tx_skb[lp->tx_new]) {
 	if (dev->interrupt) {
@@ -1130,6 +1150,8 @@
 	}
     }
     
+    lp->cache.lock = 0;
+
     return status;
 }
 
@@ -1195,8 +1217,11 @@
     }
 
     /* Load the TX ring with any locally stored packets */
-    while (lp->cache.skb && !dev->tbusy && lp->tx_enable) {
-	de4x5_queue_pkt(de4x5_get_cache(dev), dev);
+    if (!set_bit(0, (void *)&lp->cache.lock)) {
+	while (lp->cache.skb && !dev->tbusy && lp->tx_enable) {
+	    de4x5_queue_pkt(de4x5_get_cache(dev), dev);
+	}
+	lp->cache.lock = 0;
     }
 
     dev->interrupt = UNMASK_INTERRUPTS;
@@ -1376,7 +1401,7 @@
     int omr;
 
     omr = inl(DE4X5_OMR);
-    if (!(omr & OMR_SF)) {
+    if (!(omr & OMR_SF) || (lp->chipset==DC21041) || (lp->chipset==DC21040)) {
 	omr &= ~(OMR_ST|OMR_SR);
 	outl(omr, DE4X5_OMR);
 	while (inl(DE4X5_STS) & STS_TS);
@@ -1564,6 +1589,7 @@
     u_long iobase;
     struct bus_type *lp = &bus;
     char name[DE4X5_STRLEN];
+    struct device *tmp;
     
     if (!ioaddr && autoprobed) return;     /* Been here before ! */
     
@@ -1589,15 +1615,14 @@
 	    DevicePresent(EISA_APROM);
 	    /* Write the PCI Configuration Registers */
 	    outl(PCI_COMMAND_IO | PCI_COMMAND_MASTER, PCI_CFCS);
-	    outl(0x00004000, PCI_CFLT);
+	    outl(0x00006000, PCI_CFLT);
 	    outl(iobase, PCI_CBIO);
 	    
 	    if (check_region(iobase, DE4X5_EISA_TOTAL_SIZE) == 0) {
-		if ((dev = alloc_device(dev, iobase)) != NULL) {
-		    if ((status = de4x5_hw_init(dev, iobase)) == 0) {
+		if ((tmp = alloc_device(dev, iobase)) != NULL) {
+		    if ((status = de4x5_hw_init(tmp, iobase)) == 0) {
 			num_de4x5s++;
 		    }
-		    num_eth++;
 		}
 	    } else if (autoprobed) {
 		printk("%s: region already allocated at 0x%04lx.\n", dev->name,iobase);
@@ -1632,6 +1657,7 @@
     u_int class = DE4X5_CLASS_CODE;
     u_int iobase;
     struct bus_type *lp = &bus;
+    struct device *tmp;
 
     if ((!ioaddr || !loading_module) && autoprobed) return;
     
@@ -1639,14 +1665,14 @@
     
     lp->bus = PCI;
     
-    if (ioaddr < 0x1000) {
+    if ((ioaddr < 0x1000) && loading_module) {
 	pbus = (u_short)(ioaddr >> 8);
 	dnum = (u_short)(ioaddr & 0xff);
     } else {
 	pbus = 0;
 	dnum = 0;
     }
-    
+
     for (index=0; 
 	 (pcibios_find_class(class, index, &pb, &dev_fn)!= PCIBIOS_DEVICE_NOT_FOUND);
 	 index++) {
@@ -1667,7 +1693,7 @@
 	    /* Get the board I/O address */
 	    pcibios_read_config_dword(pb, PCI_DEVICE, PCI_BASE_ADDRESS_0, &iobase);
 	    iobase &= CBIO_MASK;
-	    
+
 	    /* Fetch the IRQ to be used */
 	    pcibios_read_config_byte(pb, PCI_DEVICE, PCI_INTERRUPT_LINE, &irq);
 	    if ((irq == 0) || (irq == (u_char) 0xff)) continue;
@@ -1684,12 +1710,11 @@
 
 	    DevicePresent(DE4X5_APROM);
 	    if (check_region(iobase, DE4X5_PCI_TOTAL_SIZE) == 0) {
-		if ((dev = alloc_device(dev, iobase)) != NULL) {
-		    dev->irq = irq;
-		    if ((status = de4x5_hw_init(dev, iobase)) == 0) {
+		if ((tmp = alloc_device(dev, iobase)) != NULL) {
+		    tmp->irq = irq;
+		    if ((status = de4x5_hw_init(tmp, iobase)) == 0) {
 			num_de4x5s++;
 		    }
-		    num_eth++;
 		}
 	    } else if (autoprobed) {
 		printk("%s: region already allocated at 0x%04x.\n", dev->name, 
@@ -1702,109 +1727,95 @@
 }
 
 /*
-** Allocate the device by pointing to the next available space in the
-** device structure. Should one not be available, it is created.
+** Search the entire 'eth' device list for a fixed probe. If a match isn't
+** found then check for an autoprobe or unused device location. If they
+** are not available then insert a new device structure at the end of
+** the current list.
 */
 static struct device *
 alloc_device(struct device *dev, u_long iobase)
 {
-    int addAutoProbe = 0;
-    struct device *tmp = NULL, *ret;
-    int (*init)(struct device *) = NULL;
-    
+    struct device *adev = NULL;
+    int fixed = 0, new_dev = 0;
+
+    num_eth = de4x5_dev_index(dev->name);
     if (loading_module) return dev;
     
-    /*
-    ** Check the device structures for an end of list or unused device
-    */
-    while (dev->next != NULL) {
-	if ((dev->base_addr == DE4X5_NDA) || (dev->base_addr == 0)) break;
-	dev = dev->next;                   /* walk through eth device list */
-	num_eth++;                         /* increment eth device number */
+    while (1) {
+	if (((dev->base_addr == DE4X5_NDA) || (dev->base_addr==0)) && !adev) {
+	    adev=dev;
+	} else if ((dev->priv == NULL) && (dev->base_addr==iobase)) {
+	    fixed = 1;
+	} else {
+	    if (dev->next == NULL) {
+		new_dev = 1;
+	    } else if (strncmp(dev->next->name, "eth", 3) != 0) {
+		new_dev = 1;
+	    }
+	}
+	if ((dev->next == NULL) || new_dev || fixed) break;
+	dev = dev->next;
+	num_eth++;
     }
-    
-    /*
-    ** If an autoprobe is requested for another device, we must re-insert
-    ** the request later in the list. Remember the current position first.
-    */
-    if ((dev->base_addr == 0) && (num_de4x5s > 0)) {
-	addAutoProbe++;
-	tmp = dev->next;                   /* point to the next device */
-	init = dev->init;                  /* remember the probe function */
+    if (adev && !fixed) {
+	dev = adev;
+	num_eth = de4x5_dev_index(dev->name);
+	new_dev = 0;
+    }
+
+    if (((dev->next == NULL) &&  
+	((dev->base_addr != DE4X5_NDA) && (dev->base_addr != 0)) && !fixed) ||
+	new_dev) {
+	num_eth++;                         /* New device */
+	dev = insert_device(dev, iobase, de4x5_probe);
     }
     
-    /*
-    ** If at end of list and can't use current entry, malloc one up. 
-    ** If memory could not be allocated, print an error message.
-    */
-    if ((dev->next == NULL) &&  
-	!((dev->base_addr == DE4X5_NDA) || (dev->base_addr == 0))) {
-	dev->next = (struct device *)kmalloc(sizeof(struct device)+8, GFP_KERNEL);
-	dev = dev->next;                   /* point to the new device */
-	if (dev == NULL) {
-	    printk("eth%d: Device not initialised, insufficient memory\n", num_eth);
+    return dev;
+}
+
+/*
+** If at end of eth device list and can't use current entry, malloc
+** one up. If memory could not be allocated, print an error message.
+*/
+static struct device *
+insert_device(struct device *dev, u_long iobase, int (*init)(struct device *))
+{
+    struct device *new;
+
+    new = (struct device *)kmalloc(sizeof(struct device)+8, GFP_KERNEL);
+    if (new == NULL) {
+	printk("eth%d: Device not initialised, insufficient memory\n",num_eth);
+	return NULL;
+    } else {
+	new->next = dev->next;
+	dev->next = new;
+	dev = dev->next;               /* point to the new device */
+	dev->name = (char *)(dev + 1);
+	if (num_eth > 9999) {
+	    sprintf(dev->name,"eth????");/* New device name */
 	} else {
-	    /*
-	    ** If the memory was allocated, point to the new memory area
-	    ** and initialize it (name, I/O address, next device (NULL) and
-	    ** initialisation probe routine).
-	    */
-	    dev->name = (char *)(dev + 1);
-	    if (num_eth > 9999) {
-		sprintf(dev->name,"eth????");/* New device name */
-	    } else {
-		sprintf(dev->name,"eth%d", num_eth);/* New device name */
-	    }
-	    dev->base_addr = iobase;       /* assign the io address */
-	    dev->next = NULL;              /* mark the end of list */
-	    dev->init = &de4x5_probe;      /* initialisation routine */
-	    num_de4x5s++;
+	    sprintf(dev->name,"eth%d", num_eth);/* New device name */
 	}
+	dev->base_addr = iobase;       /* assign the io address */
+	dev->init = init;              /* initialisation routine */
     }
-    ret = dev;                             /* return current struct, or NULL */
-     
-    /*
-    ** Now figure out what to do with the autoprobe that has to be inserted.
-    ** Firstly, search the (possibly altered) list for an empty space.
-    */
-    if (ret != NULL) {
-	if (addAutoProbe) {
-	    for (; (tmp->next!=NULL) && (tmp->base_addr!=DE4X5_NDA); tmp=tmp->next);
-	    /*
-	    ** If no more device structures and can't use the current one, 
-	    ** malloc one up. If memory could not be allocated, print an error
-	    **message.
-	    */
-	    if ((tmp->next == NULL) && !(tmp->base_addr == DE4X5_NDA)) {
-		tmp->next = (struct device *)kmalloc(sizeof(struct device) + 8,
-						     GFP_KERNEL);
-		tmp = tmp->next;           /* point to the new device */
-		if (tmp == NULL) {
-		    printk("%s: Insufficient memory to extend the device list.\n", 
-			   dev->name);
-		} else {
-		    /*
-		    ** If the memory was allocated, point to the new memory
-		    ** area and initialize it (name, I/O address, next device
-		    ** (NULL) and initialisation probe routine).
-		    */
-		    tmp->name = (char *)(tmp + 1);
-		    if (num_eth > 9999) {
-			sprintf(tmp->name,"eth????");
-		    } else {               /* New device name */
-			sprintf(tmp->name,"eth%d", num_eth);
-		    }
-		    tmp->base_addr = 0;    /* re-insert the io address */
-		    tmp->next = NULL;      /* mark the end of list */
-		    tmp->init = init;      /* initialisation routine */
-		}
-	    } else {                       /* structure already exists */
-		tmp->base_addr = 0;        /* re-insert the io address */
-	    }
-	}
+
+    return dev;
+}
+
+static int
+de4x5_dev_index(char *s)
+{
+    int i=0, j=0;
+
+    for (;*s; s++) {
+	if (isdigit(*s)) {
+	    j=1;
+	    i = (i * 10) + (*s - '0');
+	} else if (j) break;
     }
-    
-    return ret;
+
+    return i;
 }
 
 /*
@@ -2381,8 +2392,10 @@
     de4x5_setup_intr(dev);
     lp->lostMedia = 0;
     lp->tx_enable = YES;
+    dev->tbusy = 0;
     sti();
     outl(POLL_DEMAND, DE4X5_TPD);
+    mark_bh(NET_BH);
 
     return;
 }
@@ -2643,8 +2656,9 @@
     ret = lp->rx_skb[index];
     lp->rx_skb[index] = p;
 
-    if ((unsigned long) ret > 1)
-	    skb_put(ret, len);
+    if ((u_long) ret > 1) {
+	skb_put(ret, len);
+    }
 
     return ret;
 
@@ -2675,7 +2689,7 @@
     int i;
 
     for (i=0; i<lp->rxRingSize; i++) {
-	if ((unsigned long) lp->rx_skb[i] > 1) {
+	if ((u_long) lp->rx_skb[i] > 1) {
 	    dev_kfree_skb(lp->rx_skb[i], FREE_WRITE);
 	}
 	lp->rx_ring[i].status = 0;
@@ -2728,7 +2742,6 @@
 	de4x5_cache_state(dev, DE4X5_SAVE_STATE);
 	de4x5_sw_reset(dev);
 	de4x5_cache_state(dev, DE4X5_RESTORE_STATE);
-	dev->tbusy = 0;
 	lp->cache.save_cnt++;
 	START_DE4X5;
     }
@@ -2748,7 +2761,6 @@
 	de4x5_cache_state(dev, DE4X5_SAVE_STATE);
 	de4x5_sw_reset(dev);
 	de4x5_cache_state(dev, DE4X5_RESTORE_STATE);
-	dev->tbusy = 0;
 	lp->cache.save_cnt--;
 	START_DE4X5;
     }
@@ -3074,7 +3086,7 @@
 	    } else if (!broken) {
 		dev->dev_addr[i] = (u_char) lp->srom.ieee_addr[i]; i++;
 		dev->dev_addr[i] = (u_char) lp->srom.ieee_addr[i]; i++;
-	    } else if (broken == SMC) {           /* Assume SMC9332 for now */
+	    } else if ((broken == SMC) || (broken == ACCTON)) {
 		dev->dev_addr[i] = *((u_char *)&lp->srom + i); i++;
 		dev->dev_addr[i] = *((u_char *)&lp->srom + i); i++;
 	    }
@@ -3118,7 +3130,11 @@
     for (i=0; i<sizeof(enet_det)/ETH_ALEN; i++) {
 	if (!de4x5_strncmp((char *)&lp->srom, (char *)&enet_det[i], 3) &&
 	    !de4x5_strncmp((char *)&lp->srom+0x10, (char *)&enet_det[i], 3)) {
-	    status = SMC;
+	    if (i == 0) {
+		status = SMC;
+	    } else if (i == 1) {
+		status = ACCTON;
+	    }
 	    break;
 	}
     }
@@ -4123,8 +4139,8 @@
 
 /*
  * Local variables:
- *  compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O2 -m486 -c de4x5.c"
+ *  compile-command: "gcc -D__KERNEL__ -I/linux/include -Wall -Wstrict-prototypes -fomit-frame-pointer -fno-strength-reduce -malign-loops=2 -malign-jumps=2 -malign-functions=2 -O2 -m486 -c de4x5.c"
  *
- *  compile-command: "gcc -D__KERNEL__ -DMODULE -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O2 -m486 -c de4x5.c"
+ *  compile-command: "gcc -D__KERNEL__ -DMODULE -I/linux/include -Wall -Wstrict-prototypes -fomit-frame-pointer -fno-strength-reduce -malign-loops=2 -malign-jumps=2 -malign-functions=2 -O2 -m486 -c de4x5.c"
  * End:
  */

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov