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

Next file: linux/drivers/net/de4x5.h
Previous file: linux/drivers/net/cs89x0.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.36/linux/drivers/net/de4x5.c linux/drivers/net/de4x5.c
@@ -3,8 +3,30 @@
 
     Copyright 1994, 1995 Digital Equipment Corporation.
 
-    This software may be used and distributed according to the terms of
-    the GNU Public License, incorporated herein by reference.
+    Testing resources for this driver have been made available
+    in part by NASA Ames Research Center (mjacob@nas.nasa.gov).
+
+    The author may be reached at davies@maniac.ultranet.com.
+
+    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 Software Foundation;  either version 2 of the  License, or (at your
+    option) any later version.
+
+    THIS  SOFTWARE  IS PROVIDED   ``AS  IS'' AND   ANY  EXPRESS OR   IMPLIED
+    WARRANTIES,   INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF
+    MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
+    NO  EVENT  SHALL   THE AUTHOR  BE    LIABLE FOR ANY   DIRECT,  INDIRECT,
+    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+    NOT LIMITED   TO, PROCUREMENT OF  SUBSTITUTE GOODS  OR SERVICES; LOSS OF
+    USE, DATA,  OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+    ANY THEORY OF LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT
+    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+    THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+    You should have received a copy of the  GNU General Public License along
+    with this program; if not, write  to the Free Software Foundation, Inc.,
+    675 Mass Ave, Cambridge, MA 02139, USA.
 
     Originally,   this  driver  was    written  for the  Digital   Equipment
     Corporation series of EtherWORKS ethernet cards:
@@ -15,8 +37,8 @@
 	DE450 TP/COAX/AUI PCI
 	DE500 10/100 PCI Fasternet
 
-    but it  will now  attempt  to support all  cards   which conform  to the
-    Digital  Semiconductor SROM    Specification.   The   driver   currently
+    but it  will  now attempt  to  support all  cards which   conform to the
+    Digital Semiconductor   SROM   Specification.    The  driver   currently
     recognises the following chips:
 
         DC21040     (no SROM)
@@ -32,6 +54,7 @@
 	SMC8432
 	SMC9332 (w/new SROM)
 	ZNYX31[45]
+	ZNYX346 10/100 4 port (can act as a 10/100 bridge!) 
 
     The driver has been tested on a relatively busy network using the DE425,
     DE434, DE435 and DE500 cards and benchmarked with 'ttcp': it transferred
@@ -48,13 +71,11 @@
     measurement. Their error is +/-20k on a quiet (private) network and also
     depend on what load the CPU has.
 
-    The author may be reached at davies@maniac.ultranet.com.
-
     =========================================================================
-    This driver  has been written substantially  from scratch,  although its
+    This driver  has been written substantially  from  scratch, although its
     inheritance of style and stack interface from 'ewrk3.c' and in turn from
     Donald Becker's 'lance.c' should be obvious. With the module autoload of
-    every  usable DECchip board,  I pinched Donald's  'next_module' field to
+    every  usable DECchip board,  I  pinched Donald's 'next_module' field to
     link my modules together.
 
     Upto 15 EISA cards can be supported under this driver, limited primarily
@@ -80,7 +101,7 @@
     1) copy de4x5.c from the  /linux/drivers/net directory to your favourite
     temporary directory.
     2) for fixed  autoprobes (not  recommended),  edit the source code  near
-    line 4927 to reflect the I/O address  you're using, or assign these when
+    line 5005 to reflect the I/O address  you're using, or assign these when
     loading by:
 
                    insmod de4x5 io=0xghh           where g = bus number
@@ -113,7 +134,7 @@
     By  default,  the driver will  now   autodetect any  DECchip based card.
     Should you have a need to restrict the driver to DIGITAL only cards, you
     can compile with a  DEC_ONLY define, or if  loading as a module, use the
-    'dec_only=1'  parameter.
+    'dec_only=1'  parameter. 
 
     I've changed the timing routines to  use the kernel timer and scheduling
     functions  so that the  hangs  and other assorted problems that occurred
@@ -145,6 +166,19 @@
     (quad 21041 MAC)  cards also  appear  to work despite their  incorrectly
     wired IRQs.
 
+    I have added a temporary fix for interrupt problems when some SCSI cards
+    share the same interrupt as the DECchip based  cards. The problem occurs
+    because  the SCSI card wants to  grab the interrupt  as a fast interrupt
+    (runs the   service routine with interrupts turned   off) vs.  this card
+    which really needs to run the service routine with interrupts turned on.
+    This driver will  now   add the interrupt service   routine  as  a  fast
+    interrupt if it   is bounced from the   slow interrupt.  THIS IS NOT   A
+    RECOMMENDED WAY TO RUN THE DRIVER  and has been done  for a limited time
+    until  people   sort  out their  compatibility    issues and the  kernel
+    interrupt  service code  is  fixed.   YOU  SHOULD SEPARATE OUT  THE FAST
+    INTERRUPT CARDS FROM THE SLOW INTERRUPT CARDS to ensure that they do not
+    run on the same interrupt. PCMCIA/CardBus is another can of worms...
+
     TO DO:
     ------
 
@@ -234,7 +268,7 @@
       0.442   9-Sep-96    Include AUI in dc21041 media printout. Bug reported
                            by <bhat@mundook.cs.mu.OZ.AU>
       0.45    8-Dec-96    Include endian functions for PPC use, from work 
-                           by <cort@cs.nmt.edu>.
+                           by <cort@cs.nmt.edu> and <g.thomas@opengroup.org>.
       0.451  28-Dec-96    Added fix to allow autoprobe for modules after
                            suggestion from <mjacob@feral.com>.
       0.5    30-Jan-97    Added SROM decoding functions.
@@ -247,11 +281,30 @@
 			  Added attempt to use an SMC9332 with broken SROM.
 			  Added fix for ZYNX multi-mac cards that didn't
 			   get their IRQs wired correctly.
+      0.51   13-Feb-97    Added endian fixes for the SROM accesses from
+			   <paubert@iram.es>
+			  Fix init_connection() to remove extra device reset.
+			  Fix MAC/PHY reset ordering in dc21140m_autoconf().
+			  Fix initialisation problem with lp->timeout in
+			   typeX_infoblock() from <paubert@iram.es>.
+			  Fix MII PHY reset problem from work done by
+			   <paubert@iram.es>.
+      0.52   26-Apr-97    Some changes may not credit the right people -
+                           a disk crash meant I lost some mail.
+			  Change RX interrupt routine to drop rather than 
+			   defer packets to avoid hang reported by 
+			   <g.thomas@opengroup.org>.
+			  Fix srom_exec() to return for COMPACT and type 1
+			   infoblocks.
+			  Added DC21142 and DC21143 functions.
+			  Added byte counters from <phil@tazenda.demon.co.uk>
+			  Added SA_INTERRUPT temporary fix from 
+			   <mjacob@feral.com>.
 
     =========================================================================
 */
 
-static const char *version = "de4x5.c:V0.5 97/1/30 davies@maniac.ultranet.com\n";
+static const char *version = "de4x5.c:V0.52 1997/4/26 davies@maniac.ultranet.com\n";
 
 #include <linux/module.h>
 
@@ -270,8 +323,8 @@
 #include <asm/bitops.h>
 #include <asm/io.h>
 #include <asm/dma.h>
-#include <asm/uaccess.h>
 #include <asm/byteorder.h>
+#include <asm/unaligned.h>
 
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
@@ -286,14 +339,37 @@
 
 #define c_char const char
 
+#include <linux/version.h>
+#if	LINUX_VERSION_CODE < ((2 << 16) | (1 << 8))
+#define        net_device_stats      enet_statistics
+#define copy_to_user(a,b,c)   memcpy_tofs(a,b,c)
+#define copy_from_user(a,b,c) memcpy_fromfs(a,b,c)
+#define le16_to_cpu(a) cpu_to_le16(a) 
+#define le32_to_cpu(a) cpu_to_le32(a) 
+#ifdef __powerpc__
+#define cpu_to_le16(a) ((((a) & 0x00ffU) << 8) | (((a) & 0xff00U) >> 8))
+#define cpu_to_le32(a) ((((a) & 0x000000ffU) << 24) |\
+		        (((a) & 0x0000ff00U) <<  8) |\
+		        (((a) & 0x00ff0000U) >>  8) |\
+		        (((a) & 0xff000000U) >> 24))
+#else
+#define cpu_to_le16(a) (a)
+#define cpu_to_le32(a) (a)
+#endif  /* __powerpc__ */
+#include	<asm/segment.h>
+#else
+#include	<asm/uaccess.h>
+#endif  /* LINUX_VERSION_CODE */
+#define TWIDDLE(a) (u_short)le16_to_cpu(get_unaligned((u_short *)(a)))
+
 /*
 ** MII Information
 */
 struct phy_table {
-    int reset;              /* Hard reset required? */
-    int id;                 /* IEEE OUI */
+    int reset;              /* Hard reset required?                         */
+    int id;                 /* IEEE OUI                                     */
     int ta;                 /* One cycle TA time - 802.3u is confusing here */
-    struct {                /* Non autonegotiation (parallel) speed det. */
+    struct {                /* Non autonegotiation (parallel) speed det.    */
 	int reg;
 	int mask;
 	int value;
@@ -301,25 +377,35 @@
 };
 
 struct mii_phy {
-    int reset;              /* Hard reset required? */
-    int id;                 /* IEEE OUI */
-    int ta;                 /* One cycle TA time */
+    int reset;              /* Hard reset required?                      */
+    int id;                 /* IEEE OUI                                  */
+    int ta;                 /* One cycle TA time                         */
     struct {                /* Non autonegotiation (parallel) speed det. */
 	int reg;
 	int mask;
 	int value;
     } spd;
-    int addr;               /* MII address for the PHY */
-    u_char  *gep;           /* Start of GEP sequence block in SROM */
-    u_char  *rst;           /* Start of reset sequence in SROM */
-    u_int mc;               /* Media Capabilities */
-    u_int ana;              /* NWay Advertisement */
-    u_int fdx;              /* Full DupleX capabilites for each media */
-    u_int ttm;              /* Transmit Threshold Mode for each media */
+    int addr;               /* MII address for the PHY                   */
+    u_char  *gep;           /* Start of GEP sequence block in SROM       */
+    u_char  *rst;           /* Start of reset sequence in SROM           */
+    u_int mc;               /* Media Capabilities                        */
+    u_int ana;              /* NWay Advertisement                        */
+    u_int fdx;              /* Full DupleX capabilites for each media    */
+    u_int ttm;              /* Transmit Threshold Mode for each media    */
 };
 
 #define DE4X5_MAX_PHY 8     /* Allow upto 8 attached PHY devices per board */
 
+struct sia_phy {
+    u_char mc;              /* Media Code                                */
+    u_char ext;             /* csr13-15 valid when set                   */
+    int csr13;              /* SIA Connectivity Register                 */
+    int csr14;              /* SIA TX/RX Register                        */
+    int csr15;              /* SIA General Register                      */
+    int gepc;               /* SIA GEP Control Information               */
+    int gep;                /* SIA GEP Data                              */
+};
+
 /*
 ** Define the know universe of PHY devices that can be
 ** recognised by this driver
@@ -327,8 +413,8 @@
 static struct phy_table phy_info[] = {
     {0, NATIONAL_TX, 1, {0x19, 0x40, 0x00}},   /* National TX */
     {1, BROADCOM_T4, 1, {0x10, 0x02, 0x02}},   /* Broadcom T4 */
-    {0, SEEQ_T4    , 1, {0x12, 0x10, 0x10}},   /* SEEQ T4 */
-    {0, CYPRESS_T4 , 1, {0x05, 0x20, 0x20}}    /* Cypress T4 */
+    {0, SEEQ_T4    , 1, {0x12, 0x10, 0x10}},   /* SEEQ T4     */
+    {0, CYPRESS_T4 , 1, {0x05, 0x20, 0x20}}    /* Cypress T4  */
 };
 
 /*
@@ -354,7 +440,6 @@
      0x00,0x18,}
 };
 
-#undef DE4X5_VERBOSE     /* define to get more verbose startup messages */
 
 #ifdef DE4X5_DEBUG
 static int de4x5_debug = DE4X5_DEBUG;
@@ -581,6 +666,7 @@
     int  setup_f;                           /* Setup frame filtering type   */
     int  local_state;                       /* State within a 'media' state */
     struct mii_phy phy[DE4X5_MAX_PHY];      /* List of attached PHY devices */
+    struct sia_phy sia;                     /* SIA PHY Information          */
     int  active;                            /* Index to active PHY device   */
     int  mii_cnt;                           /* Number of attached PHY's     */
     int  timeout;                           /* Scheduling counter           */
@@ -770,9 +856,6 @@
 static void    yawn(struct device *dev, int state);
 static int     de4x5_dev_index(char *s);
 static void    link_modules(struct device *dev, struct device *tmp);
-#ifdef MODULE
-static struct  device *unlink_modules(struct device *p);
-#endif
 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);
@@ -794,6 +877,7 @@
 #ifdef MODULE
 int  init_module(void);
 void cleanup_module(void);
+static struct  device *unlink_modules(struct device *p);
 static int autoprobed = 0, loading_module = 1;
 # else
 static int autoprobed = 0, loading_module = 0;
@@ -898,7 +982,7 @@
 				  PCI_CFDA_PSM, WAKEUP);
     }
     de4x5_ms_delay(10);
-    
+
     RESET_DE4X5;
     
     if ((inl(DE4X5_STS) & (STS_TS | STS_RS)) != 0) {
@@ -964,6 +1048,9 @@
 	lp->chipset = bus.chipset;
 	lp->cache.priv = tmp;
 	lp->cache.gepc = GEP_INIT;
+	lp->asBit = GEP_SLNK;
+	lp->asPolarity = GEP_SLNK;
+	lp->asBitValid = TRUE;
 	lp->timeout = -1;
 	lp->useSROM = useSROM;
 	memcpy((char *)&lp->srom,(char *)&bus.srom,sizeof(struct de4x5_srom));
@@ -1071,11 +1158,6 @@
 	
 	printk("      and requires IRQ%d (provided by %s).\n", dev->irq,
 	       ((lp->bus == PCI) ? "PCI BIOS" : "EISA CNFG"));
-
-#ifdef DE4X5_VERBOSE
-	printk("%s: INFOLEAF_SIZE: %ld, COMPACT: %ld\n", dev->name, 
-	       INFOLEAF_SIZE, COMPACT);
-#endif
     }
     
     if (de4x5_debug & DEBUG_VERSION) {
@@ -1133,18 +1215,29 @@
     
     if (request_irq(dev->irq, (void *)de4x5_interrupt, SA_SHIRQ, 
 		                                     lp->adapter_name, dev)) {
-	printk("de4x5_open(): Requested IRQ%d is busy\n",dev->irq);
-	status = -EAGAIN;
-    } else {
-	dev->tbusy = 0;                         
-	dev->start = 1;
-	dev->interrupt = UNMASK_INTERRUPTS;
-	dev->trans_start = jiffies;
-	
-	START_DE4X5;
-	
-	de4x5_setup_intr(dev);
+	printk("de4x5_open(): Requested IRQ%d is busy - attemping FAST/SHARE...", dev->irq);
+	if (request_irq(dev->irq, de4x5_interrupt, SA_INTERRUPT | SA_SHIRQ,
+			                             lp->adapter_name, dev)) {
+	    printk("\n              Cannot get IRQ- reconfigure your hardware.\n");
+	    disable_ast(dev);
+	    de4x5_free_rx_buffs(dev);
+	    de4x5_free_tx_buffs(dev);
+	    yawn(dev, SLEEP);
+	    lp->state = CLOSED;
+	    return -EAGAIN;
+	} else {
+	    printk("\n              Succeeded, but you should reconfigure your hardware to avoid this.\n");
+	    printk("WARNING: there may be IRQ related problems in heavily loaded systems.\n");
+	}
     }
+    dev->tbusy = 0;                         
+    dev->start = 1;
+    dev->interrupt = UNMASK_INTERRUPTS;
+    dev->trans_start = jiffies;
+    
+    START_DE4X5;
+	
+    de4x5_setup_intr(dev);
     
     if (de4x5_debug & DEBUG_OPEN) {
 	printk("\tsts:  0x%08x\n", inl(DE4X5_STS));
@@ -1287,7 +1380,7 @@
     sti();
 
     /* Test if cache is already locked - requeue skb if so */
-    if (set_bit(0, (void *)&lp->cache.lock) && !dev->interrupt) return -1;
+    if (test_and_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]) {
@@ -1300,9 +1393,6 @@
 	    printk("%s: transmit busy, lost media or stale skb found:\n  STS:%08x\n  tbusy:%ld\n  IMR:%08x\n  OMR:%08x\n Stale skb: %s\n",dev->name, inl(DE4X5_STS), dev->tbusy, inl(DE4X5_IMR), inl(DE4X5_OMR), (lp->tx_skb[lp->tx_new] ? "YES" : "NO"));
 	}
     } else if (skb->len > 0) {
-	/* Update the byte counter */
-	lp->stats.tx_bytes += skb->len;
-
 	/* If we already have stuff queued locally, use that first */
 	if (lp->cache.skb && !dev->interrupt) {
 	    de4x5_put_cache(dev, skb);
@@ -1313,6 +1403,9 @@
 	    cli();
 	    set_bit(0, (void*)&dev->tbusy);
 	    load_packet(dev, skb->data, TD_IC | TD_LS | TD_FS | skb->len, skb);
+#if	LINUX_VERSION_CODE >= ((2 << 16) | (1 << 8))
+ 	    lp->stats.tx_bytes += skb->len;
+#endif
 	    outl(POLL_DEMAND, DE4X5_TPD);/* Start the TX */
 		
 	    lp->tx_new = (++lp->tx_new) % lp->txRingSize;
@@ -1326,7 +1419,7 @@
 	}
 	if (skb) de4x5_putb_cache(dev, skb);
     }
-
+    
     lp->cache.lock = 0;
 
     return status;
@@ -1360,9 +1453,11 @@
 	
     if (dev->interrupt)
       printk("%s: Re-entering the interrupt handler.\n", dev->name);
-
+	
     DISABLE_IRQs;                        /* Ensure non re-entrancy */
+#if	LINUX_VERSION_CODE >= ((2 << 16) | (1 << 8))
     synchronize_irq();
+#endif
     dev->interrupt = MASK_INTERRUPTS;
 	
     for (limit=0; limit<8; limit++) {
@@ -1394,8 +1489,8 @@
     }
 
     /* Load the TX ring with any locally stored packets */
-    if (!set_bit(0, (void *)&lp->cache.lock)) {
-	if (lp->cache.skb && !dev->tbusy && lp->tx_enable) {
+    if (!test_and_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;
@@ -1450,19 +1545,21 @@
 		if ((skb = de4x5_alloc_rx_buff(dev, entry, pkt_len)) == NULL) {
 		    printk("%s: Insufficient memory; nuking packet.\n", 
 			                                            dev->name);
-		    lp->stats.rx_dropped++;   /* Really, deferred. */
-		    break;
-		}
-		de4x5_dbg_rx(skb, pkt_len);
+		    lp->stats.rx_dropped++;
+		} else {
+		    de4x5_dbg_rx(skb, pkt_len);
 
-		/* Push up the protocol stack */
-		skb->protocol=eth_type_trans(skb,dev);
-		netif_rx(skb);
+		    /* Push up the protocol stack */
+		    skb->protocol=eth_type_trans(skb,dev);
+		    netif_rx(skb);
 		    
-		/* Update stats */
-		lp->stats.rx_packets++;
-		lp->stats.rx_bytes += pkt_len;
-		de4x5_local_stats(dev, skb->data, pkt_len);
+		    /* Update stats */
+		    lp->stats.rx_packets++;
+#if	LINUX_VERSION_CODE >= ((2 << 16) | (1 << 8))
+ 		    lp->stats.rx_bytes += pkt_len;
+#endif
+		    de4x5_local_stats(dev, skb->data, pkt_len);
+		}
 	    }
 	    
 	    /* Change buffer ownership for this frame, back to the adapter */
@@ -1575,10 +1672,7 @@
 	if ((omr & OMR_TR) < OMR_TR) {
 	    omr += 0x4000;
 	} else {
-	    if (omr & OMR_TTM)
-		omr &= ~OMR_TTM;
-	    else
-		omr |= OMR_SF;
+	    omr |= OMR_SF;
 	}
 	outl(omr | OMR_ST | OMR_SR, DE4X5_OMR);
     }
@@ -1645,7 +1739,8 @@
     return 0;
 }
 
-static struct net_device_stats *de4x5_get_stats(struct device *dev)
+static struct net_device_stats *
+de4x5_get_stats(struct device *dev)
 {
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
     u_long iobase = dev->base_addr;
@@ -1880,7 +1975,7 @@
 {
     u_char irq;
     u_char pb, pbus, dev_num, dnum, dev_fn;
-    u_short vendor, index, status;
+    u_short dev_id, vendor, index, status;
     u_int class = DE4X5_CLASS_CODE;
     u_int device, iobase;
     struct bus_type *lp = &bus;
@@ -1908,7 +2003,8 @@
 	if ((!pbus && !dnum) || ((pbus == pb) && (dnum == dev_num))) {
 	    device = 0;
 	    pcibios_read_config_word(pb, PCI_DEVICE, PCI_VENDOR_ID, &vendor);
-	    pcibios_read_config_word(pb, PCI_DEVICE, PCI_DEVICE_ID, (u_short *)&device);
+	    pcibios_read_config_word(pb, PCI_DEVICE, PCI_DEVICE_ID, &dev_id);
+	    device = dev_id;
 	    device <<= 8;
 	    if (!(is_DC21040 || is_DC21041 || is_DC21140 || is_DC2114x)) {
 		continue;
@@ -2091,31 +2187,6 @@
     return;
 }
 
-#ifdef MODULE
-static struct device *
-unlink_modules(struct device *p)
-{
-    struct device *next = NULL;
-
-    if (p->priv) {                          /* Private areas allocated?  */
-	struct de4x5_private *lp = (struct de4x5_private *)p->priv;
-
-	next = lp->next_module;
-	if (lp->cache.buf) {                /* MAC buffers allocated?    */
-	    kfree(lp->cache.buf);           /* Free the MAC buffers      */
-	}
-	kfree(lp->cache.priv);              /* Free the private area     */
-	release_region(p->base_addr, (lp->bus == PCI ? 
-				      DE4X5_PCI_TOTAL_SIZE :
-				      DE4X5_EISA_TOTAL_SIZE));
-    }
-    unregister_netdev(p);
-    kfree(p);                               /* Free the device structure */
-    
-    return next;
-}
-#endif
-
 /*
 ** Auto configure the media here rather than setting the port at compile
 ** time. This routine is called by de4x5_init() and when a loss of media is
@@ -2300,6 +2371,7 @@
 	    lp->media = prev_state;
 	} else {
 	    lp->media = INIT;
+	    lp->tcount++;
 	}
     }
 
@@ -2527,16 +2599,22 @@
 
     switch(lp->media) {
       case INIT: 
-	DISABLE_IRQs;
-	lp->tx_enable = FALSE;
-        lp->linkOK = 0;
-/*	lp->timeout = -1;*/
+        if (lp->timeout < 0) {
+	    DISABLE_IRQs;
+	    lp->tx_enable = FALSE;
+	    lp->linkOK = 0;
+	    de4x5_save_skbs(dev);          /* Save non transmitted skb's */
+	}
 	if ((next_tick = de4x5_reset_phy(dev)) < 0) {
 	    next_tick &= ~TIMER_CB;
 	} else {
-	    de4x5_save_skbs(dev);          /* Save non transmitted skb's */
 	    if (lp->useSROM) {
 		srom_map_media(dev);
+		srom_exec(dev, lp->phy[lp->active].gep);
+		if (lp->infoblock_media == ANS) {
+		    ana = lp->phy[lp->active].ana | MII_ANA_CSMA;
+		    mii_wr(ana, MII_ANA, lp->phy[lp->active].addr, DE4X5_MII);
+		}
 	    } else {
 		lp->tmp = MII_SR_ASSC;     /* Fake out the MII speed set */
 		SET_10Mb;
@@ -2752,9 +2830,9 @@
         de4x5_dbg_media(dev);
 	lp->c_media = lp->media;          /* Stop scrolling media messages */
     }
-    de4x5_restore_skbs(dev);
+
     cli();
-    de4x5_rx(dev);
+    de4x5_restore_skbs(dev);
     de4x5_setup_intr(dev);
     lp->tx_enable = YES;
     dev->tbusy = 0;
@@ -2766,7 +2844,9 @@
 }
 
 /*
-** General PHY reset function.
+** General PHY reset function. Some MII devices don't reset correctly
+** since their MII address pins can float at voltages that are dependent
+** on the signal pin use. Do a double reset to ensure a reset.
 */
 static int
 de4x5_reset_phy(struct device *dev)
@@ -2780,8 +2860,10 @@
 	    if (lp->useSROM) {
 		if (lp->phy[lp->active].rst) { /* MII device specific reset */
 		    srom_exec(dev, lp->phy[lp->active].rst);
+		    srom_exec(dev, lp->phy[lp->active].rst);
 		} else if (lp->rst) {          /* Type 5 infoblock reset */
 		    srom_exec(dev, lp->rst);
+		    srom_exec(dev, lp->rst);
 		}
 	    } else {
 		PHY_HARD_RESET;
@@ -3151,13 +3233,26 @@
 {
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
     u_long iobase = dev->base_addr;
+    int i;
     s32 omr;
 
     if (lp->cache.save_cnt) {
 	STOP_DE4X5;
-	de4x5_cache_state(dev, DE4X5_SAVE_STATE);
-	de4x5_sw_reset(dev);
-	de4x5_cache_state(dev, DE4X5_RESTORE_STATE);
+	outl(virt_to_bus(lp->rx_ring), DE4X5_RRBA);
+	outl(virt_to_bus(lp->tx_ring), DE4X5_TRBA);
+    
+	lp->rx_new = lp->rx_old = 0;
+	lp->tx_new = lp->tx_old = 0;
+    
+	for (i = 0; i < lp->rxRingSize; i++) {
+	    lp->rx_ring[i].status = cpu_to_le32(R_OWN);
+	}
+    
+	for (i = 0; i < lp->txRingSize; i++) {
+	    lp->tx_ring[i].status = cpu_to_le32(0);
+	}
+    
+	barrier();
 	lp->cache.save_cnt--;
 	START_DE4X5;
     }
@@ -3446,9 +3541,10 @@
     if (lp->chipset == DC21040) {
 	outl(0, aprom_addr);           /* Reset Ethernet Address ROM Pointer */
     } else {                           /* Read new srom */
-	short *p = (short *)&lp->srom;
+	u_short tmp, *p = (short *)&lp->srom;
 	for (i=0; i<(sizeof(struct de4x5_srom)>>1); i++) {
-	    *p++ = srom_rd(aprom_addr, i);
+	    tmp = srom_rd(aprom_addr, i);
+	    *p++ = le16_to_cpu(tmp);
 	}
 	de4x5_dbg_srom((struct de4x5_srom *)&lp->srom);
     }
@@ -3756,7 +3852,7 @@
 	}
     }
 
-    lp->infoleaf_offset = (u_short)*((u_short *)(p+1));
+    lp->infoleaf_offset = TWIDDLE(p+1);
 
     return 0;
 }
@@ -3776,14 +3872,10 @@
     u_char *p = (u_char *)&lp->srom + lp->infoleaf_offset;
     u_char count;
 
+    p+=2;
     if (lp->chipset == DC21140) {
-	p+=2;
 	lp->cache.gepc = (*p++ | GEP_CTRL);
 	outl(lp->cache.gepc, DE4X5_GEP);
-    } else if (lp->chipset == DC21142) {
-	p+=2;
-    } else if (lp->chipset == DC21143) {
-	p+=2;
     }
 
     /* Block count */
@@ -3815,7 +3907,7 @@
 {
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
     u_long iobase = dev->base_addr;
-    u_char count = *p++;
+    u_char count = (p ? *p++ : 0);
 
     while (count--) {
 	if (lp->chipset == DC21140) {
@@ -3910,7 +4002,7 @@
 	}
     }
 
-    if (lp->media == INIT) {
+    if ((lp->media == INIT) && (lp->timeout < 0)) {
         outl(lp->cache.gepc, DE4X5_GEP);
 	lp->infoblock_media = (*p++) & COMPACT_MC;
 	lp->cache.gep = *p++;
@@ -3949,7 +4041,7 @@
 	}
     }
 
-    if (lp->media == INIT) {
+    if ((lp->media == INIT) && (lp->timeout < 0)) {
         outl(lp->cache.gepc, DE4X5_GEP);
 	p+=2;
 	lp->infoblock_media = (*p++) & BLOCK0_MC;
@@ -3976,9 +4068,7 @@
 type1_infoblock(struct device *dev, u_char count, u_char *p)
 {
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
-    u_long iobase = dev->base_addr;
     u_char len = (*p & BLOCK_LEN)+1;
-    int ana;
 
     /* Recursively figure out the info blocks */
     if (--count > lp->tcount) {
@@ -3989,22 +4079,20 @@
 	}
     }
 
+    p += 2;
     if (lp->state == INITIALISED) {
-	lp->ibn = 1; p += 2;
+	lp->ibn = 1;
 	lp->active = *p++;
 	lp->phy[lp->active].gep = (*p ? p : 0); p += (*p + 1);
 	lp->phy[lp->active].rst = (*p ? p : 0); p += (*p + 1);
-	lp->phy[lp->active].mc  = *(u_short *)p; p += 2;
-	lp->phy[lp->active].ana = *(u_short *)p; p += 2;
-	lp->phy[lp->active].fdx = *(u_short *)p; p += 2;
-	lp->phy[lp->active].ttm = *(u_short *)p;
+	lp->phy[lp->active].mc  = TWIDDLE(p); p += 2;
+	lp->phy[lp->active].ana = TWIDDLE(p); p += 2;
+	lp->phy[lp->active].fdx = TWIDDLE(p); p += 2;
+	lp->phy[lp->active].ttm = TWIDDLE(p);
 	return 0;
-    } else if (lp->media == INIT) {
-	if (lp->phy[lp->active].gep) {
-	    srom_exec(dev, lp->phy[lp->active].gep);
-	}
-        ana = lp->phy[lp->active].ana | MII_ANA_CSMA;
-	mii_wr(ana, MII_ANA, lp->phy[lp->active].addr, DE4X5_MII);
+    } else if ((lp->media == INIT) && (lp->timeout < 0)) {
+        lp->ibn = 1;
+        lp->active = *p;
 	lp->infoblock_csr6 = OMR_PS | OMR_HBD;
 	lp->useMII = TRUE;
 	lp->infoblock_media = ANS;
@@ -4040,10 +4128,10 @@
         lp->active = *p++;
 	lp->phy[lp->active].gep = (*p ? p : 0); p += (*p + 1);
 	lp->phy[lp->active].rst = (*p ? p : 0); p += (*p + 1);
-	lp->phy[lp->active].mc  = *(u_short *)p; p += 2;
-	lp->phy[lp->active].ana = *(u_short *)p; p += 2;
-	lp->phy[lp->active].fdx = *(u_short *)p; p += 2;
-	lp->phy[lp->active].ttm = *(u_short *)p;
+	lp->phy[lp->active].mc  = TWIDDLE(p); p += 2;
+	lp->phy[lp->active].ana = TWIDDLE(p); p += 2;
+	lp->phy[lp->active].fdx = TWIDDLE(p); p += 2;
+	lp->phy[lp->active].ttm = TWIDDLE(p);
 	return 0;
     } else if (lp->media == INIT) {
 	p+=2;
@@ -4672,18 +4760,12 @@
     u_long iobase = dev->base_addr;
     int i, j, status = 0;
     s32 omr;
-    struct {
-	u8  *addr;
-	u16 *sval;
-	u32 *lval;
+    union {
+	u8  addr[144];
+	u16 sval[72];
+	u32 lval[36];
     } tmp;
-
-    tmp.addr = tmp.sval = tmp.lval = kmalloc(HASH_TABLE_LEN * ETH_ALEN, GFP_KERNEL);
-    if (!tmp.addr){ 
-      printk("%s ioctl: memory squeeze.\n", dev->name);
-      return -ENOMEM;
-    }
-
+    
     switch(ioc->cmd) {
       case DE4X5_GET_HWADDR:           /* Get the hardware address */
 	ioc->len = ETH_ALEN;
@@ -4710,7 +4792,7 @@
 	}
 	build_setup_frame(dev, PHYS_ADDR_ONLY);
 	/* Set up the descriptor and give ownership to the card */
-	while (set_bit(0, (void *)&dev->tbusy) != 0);/* Wait for lock to free*/
+	while (test_and_set_bit(0, (void *)&dev->tbusy) != 0);/* Wait for lock to free*/
 	load_packet(dev, lp->setup_frame, TD_IC | PERFECT_F | TD_SET | 
 		                                        SETUP_FRAME_LEN, NULL);
 	lp->tx_new = (++lp->tx_new) % lp->txRingSize;
@@ -4743,39 +4825,12 @@
 	
 	break;
       case DE4X5_GET_MCA:              /* Get the multicast address table */
-	ioc->len = (HASH_TABLE_LEN >> 3);
-	status = verify_area(VERIFY_WRITE, ioc->data, ioc->len);
-	if (!status) {
-	    copy_to_user(ioc->data, lp->setup_frame, ioc->len); 
-	}
-	
 	break;
       case DE4X5_SET_MCA:              /* Set a multicast address */
-	if (suser()) {
-	    /******* FIX ME! ********/
-	    if (ioc->len != HASH_TABLE_LEN) {         /* MCA changes */
-		if (!(status = verify_area(VERIFY_READ, (void *)ioc->data, ETH_ALEN * ioc->len))) {
-		    copy_from_user(tmp.addr, ioc->data, ETH_ALEN * ioc->len);
-		    set_multicast_list(dev);
-		}
-	    } else {
-		set_multicast_list(dev);
-	    }
-	} else {
-	    status = -EPERM;
-	}
-	
 	break;
       case DE4X5_CLR_MCA:              /* Clear all multicast addresses */
-	if (suser()) {
-	    /******* FIX ME! ********/
-	    set_multicast_list(dev);
-	} else {
-	    status = -EPERM;
-	}
-	
 	break;
-      case DE4X5_MCA_EN:               /* Enable pass all multicast addressing */
+      case DE4X5_MCA_EN:               /* Enable pass all multicast addresses*/
 	if (suser()) {
 	    omr = inl(DE4X5_OMR);
 	    omr |= OMR_PM;
@@ -4840,7 +4895,8 @@
 	}
 	break;
 	
-#define DE4X5_DUMP              0x0f /* Dump the DE4X5 Status */
+/*
+#define DE4X5_DUMP              0x0f / * Dump the DE4X5 Status * /
 	
       case DE4X5_DUMP:
 	j = 0;
@@ -4931,12 +4987,11 @@
 	}
 	
 	break;
+*/
       default:
 	status = -EOPNOTSUPP;
     }
-
-    kfree(tmp.addr);
-
+    
     return status;
 }
 
@@ -4975,6 +5030,30 @@
 
     return;
 }
+
+static struct device *
+unlink_modules(struct device *p)
+{
+    struct device *next = NULL;
+
+    if (p->priv) {                          /* Private areas allocated?  */
+	struct de4x5_private *lp = (struct de4x5_private *)p->priv;
+
+	next = lp->next_module;
+	if (lp->cache.buf) {                /* MAC buffers allocated?    */
+	    kfree(lp->cache.buf);           /* Free the MAC buffers      */
+	}
+	kfree(lp->cache.priv);              /* Free the private area     */
+	release_region(p->base_addr, (lp->bus == PCI ? 
+				      DE4X5_PCI_TOTAL_SIZE :
+				      DE4X5_EISA_TOTAL_SIZE));
+    }
+    unregister_netdev(p);
+    kfree(p);                               /* Free the device structure */
+    
+    return next;
+}
+
 #endif /* MODULE */
 
 

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