[1994] in linux-net channel archive

home help back first fref pref prev next nref lref last post

Re: The rewritten eexpress.c driver

daemon@ATHENA.MIT.EDU (Alan Cox)
Thu Feb 29 17:43:49 1996

From: alan@lxorguk.ukuu.org.uk (Alan Cox)
To: amartin@engr.csulb.edu (Anthony Martin)
Date: 	Thu, 29 Feb 1996 00:33:12 +0000 (GMT)
Cc: linux-net@vger.rutgers.edu
In-Reply-To: <Pine.SGI.3.91.960228154602.3968A-100000@heart.engr.csulb.edu> from "Anthony Martin" at Feb 28, 96 03:54:46 pm

> OK, I give up.  Where can I find the new eexpress.c?  This is the first I
> have heard of it.  I struggle everyday with the old driver and would
> gladly try a new one.
> 
> Please, where can I find it?

Here ...

> From thor.cam.ac.uk!pjb27 Sat Feb  3 13:13:03 1996
> Date: Sat, 3 Feb 1996 13:08:36 +0000 (GMT)
> From: Philip Blundell <pjb27@cam.ac.uk>
> X-Sender: pjb27@hammer.thor.cam.ac.uk
> To: Alan Cox <iialan@www.linux.org.uk>, 
>     Donald Becker <becker@cesdis.gsfc.nasa.gov>
> Subject: New EtherExpress driver
> Message-ID: <Pine.SOL.3.91.960203130132.13010A-100000@hammer.thor.cam.ac.uk>
> MIME-Version: 1.0
> Content-Type: TEXT/PLAIN; charset=US-ASCII
> Sender: Philip Blundell <pjb27@thor.cam.ac.uk>
> 
> This patch is a replacement Linux driver for the Intel EtherExpress 
> card.  It was originally written by John Sullivan, based on Donald's 
> sources.  I think he did submit it for inclusion in the kernel at the 
> time, but it doesn't seem to have got merged in, and he now seems to have 
> lost interest.  It would be good to get it in before 1.4, since it does 
> seem to represent a major improvement over the old driver. 
> 
> I have tidied it up a bit and made a few small changes to allow it to 
> work with the latest kernels.  There is still some breakage remaining -- 
> in particular in the initialisation/detection code, which I will look at 
> when I get a chance. 
> 
> You should remove drivers/net/eexpress.c before applying this patch. 
> 
> --- /dev/null	Wed Jan 17 15:22:50 1996
> +++ eexpress.c	Sat Feb  3 13:02:21 1996
> @@ -0,0 +1,1186 @@
> +/*
> + * eexpress2.c: Intel EtherExpress device driver for Linux
> + *
> + * Original version written 1993 by Donald Becker
> + * Modularized by Pauline Middelink <middelin@polyware.iaf.nl>
> + * Changed to support io= irq= by Alan Cox <Alan.Cox@linux.org>
> + * Reworked 1995 by John Sullivan <js10039@cam.ac.uk>
> + * 
> + *  31jan96 Philip Blundell <pjb27@cam.ac.uk>
> + *     - Tidy up
> + *     - Some debugging.  Now works with 1.3 kernels.
> + *
> + *     Still to do:
> + *     - rationalise debugging
> + *     - fix detect/autoprobe and module routines
> + *     - test under high load, try to chase CU lockups
> + *     - look at RAM size check
> + *
> + * ToDo:
> + *   Move private globals into net_local structure
> + *   Multicast/Promiscuous mode handling
> + *   Put back debug reporting?
> + *   More documentation
> + *   Some worry about whether statistics are reported accurately
> + *
> + */
> +
> +/*
> + * The original EtherExpress driver was just about useable, but
> + * suffered from a long startup delay, a hard limit of 16k memory
> + * usage on the card (EtherExpress 16s have either 32k or 64k),
> + * and random locks under load. The last was particularly annoying
> + * and made running eXceed/W preferable to Linux/XFree. After hacking
> + * through the driver for a couple of days, I had fixed most of the
> + * card handling errors, at the expense of turning the code into
> + * a complete jungle, but still hadn't tracked down the lock-ups.
> + * I had hoped these would be an IP bug, but failed to reproduce them
> + * under other drivers, so decided to start from scratch and rewrite
> + * the driver cleanly. And here it is.
> + *
> + * It's still not quite there, but self-corrects a lot more problems.
> + * the 'CU wedged, resetting...' message shouldn't happen at all, but
> + * at least we recover. It still locks occasionally, any ideas welcome.
> + *
> + * The original startup delay experienced by some people was due to the
> + * first ARP request for the address of the default router getting lost.
> + * (mostly the reply we were getting back was arriving before our
> + * hardware address was set up, or before the configuration sequence
> + * had told the card NOT to strip of the frame header). If you a long
> + * startup delay, you may have lost this ARP request/reply, although
> + * the original cause has been fixed. However, it is more likely that
> + * you've just locked under this version.
> + *
> + * The main changes are in the 586 initialization procedure (which was
> + * just broken before - the EExp is a strange beasty and needs careful
> + * handling) the receive buffer handling (we now use a non-terminating
> + * circular list of buffers, which stops the card giving us out-of-
> + * resources errors), and the transmit code. The driver is also more
> + * structured, and I have tried to keep the kernel interface separate
> + * from the hardware interface (although some routines naturally want
> + * to do both).
> + *
> + * John Sullivan
> + *
> + * 18/5/95:
> + *
> + * The lock-ups seem to happen when you access card memory after a 586
> + * reset. This happens only 1 in 12 resets, on a random basis, and
> + * completely locks the machine. As far as I can see there is no
> + * workaround possible - the only thing to be done is make sure we
> + * never reset the card *after* booting the kernel - once at probe time
> + * must be sufficient, and we'll just have to put up with that failing
> + * occasionally (or buy a new NIC). By the way, this looks like a 
> + * definate card bug, since Intel's own driver for DOS does exactly the
> + * same.
> + */
> +
> +/*
> + * Sources:
> + *
> + * The original eexpress.c by Donald Becker
> + *   Sources: the Crynwr EtherExpress driver source.
> + *            the Intel Microcommunications Databook Vol.1 1990
> + *
> + * wavelan.c and i82586.h
> + *   This was invaluable for the complete '586 configuration details
> + *   and command format.
> + *
> + * The Crynwr sources (again)
> + *   Not as useful as the Wavelan driver, but then I had eexpress.c to
> + *   go off.
> + *
> + * The Intel EtherExpress 16 ethernet card
> + *   Provided the only reason I want to see a working etherexpress driver.
> + *   A lot of fixes came from just observing how the card (mis)behaves when
> + *   you prod it.
> + *
> + */
> +
> +static char version[] = 
> +"eexpress.c: v0.07 1/19/94 Donald Becker <becker@super.org>\n"
> +"            v0.10 4th May 1995 John Sullivan <js10039@cam.ac.uk>\n";
> +
> +#include <linux/config.h>
> +
> +#ifdef MODULE
> +#include <linux/module.h>
> +#include <linux/version.h>
> +#endif
> +
> +#include <linux/kernel.h>
> +#include <linux/sched.h>
> +#include <linux/types.h>
> +#include <linux/fcntl.h>
> +#include <linux/interrupt.h>
> +#include <linux/ptrace.h>
> +#include <linux/ioport.h>
> +#include <linux/string.h>
> +#include <linux/in.h>
> +#include <asm/system.h>
> +#include <asm/bitops.h>
> +#include <asm/io.h>
> +#include <asm/dma.h>
> +#include <linux/errno.h>
> +
> +#include <linux/netdevice.h>
> +#include <linux/etherdevice.h>
> +#include <linux/skbuff.h>
> +#include <linux/malloc.h>
> +
> +/*
> + * Not actually used yet - may be implemented when the driver has
> + * been debugged!
> + *
> + * Debug Level		Driver Status
> + *	0		Final release
> + *	1		Beta test
> + *	2
> + *	3
> + * 	4		Report timeouts & 586 errors (normal debug level)
> + *	5		Report all major events
> + *	6		Dump sent/received packet contents
> + *	7		Report function entry/exit
> + */
> +
> +#undef NET_DEBUG
> +
> +#ifndef NET_DEBUG
> +#define NET_DEBUG 4
> +#endif
> +static unsigned int net_debug = NET_DEBUG;
> +
> +#undef F_DEB
> +
> +#include "eth82586.h"
> +
> +/*
> + * Private data declarations
> + */
> +
> +struct net_local {
> +  struct enet_statistics stats;
> +  unsigned long init_time;        /* jiffies when eexp_hw_init586 called */
> +  unsigned short rx_first;        /* first rx buf, same as RX_BUF_START */
> +  unsigned short rx_last;         /* last rx buf */
> +  unsigned short tx_head;         /* next free tx buf */
> +  unsigned short tx_reap;         /* first in-use tx buf */
> +  unsigned short tx_tail;         /* previous tx buf to tx_head */
> +  unsigned short tx_link;         /* last known-executing tx buf */
> +  unsigned short last_tx_restart; /* set to tx_link when we restart the CU */
> +};
> +
> +unsigned short start_code[] = {
> +  0x0000,                 /* SCP: set bus to 16 bits */
> +  0x0000,0x0000,          /* junk */
> +  0x0000,0x0000,          /* address of ISCP (lo,hi) */
> +
> +  0x0001,                 /* ISCP: busy - cleared after reset */
> +  0x0008,0x0000,0x0000,   /* offsett,address (lo,hi) of SCB */
> +
> +  0x0000,0x0000,          /* SCB: status, commands */
> +  0x0000,0x0000,          /* links to first command block, first receive descriptor */
> +  0x0000,0x0000,          /* CRC error, alignment error counts */
> +  0x0000,0x0000,          /* out of resources, overrun error counts */
> +
> +  0x0000,0x0000,          /* pad */
> +  0x0000,0x0000,
> +
> +  0x0000,Cmd_Config,      /* startup configure sequence, at 0x0020 */
> +  0x0032,                 /* link to next command */
> +  0x080c,                 /* 12 bytes follow : fifo threshold=8 */
> +  0x2e40,                 /* don't rx bad frames : SRDY/ARDY => ext. sync. : preamble len=8
> +                           * take addresses from data buffers : 6 bytes/address */
> +  0x6000,                 /* default backoff method & priority : interframe spacing = 0x60 */
> +  0xf200,                 /* slot time=0x200 : max collision retry = 0xf */
> +  0x0000,                 /* no HDLC : normal CRC : enable broadcast : disable promiscuous/multicast modes */
> +  0x003c,                 /* minimum frame length = 60 octets) */
> +
> +  0x0000,Cmd_INT|Cmd_SetAddr,
> +  0x003e,                 /* link to next command */
> +  0x0000,0x0000,0x0000,   /* hardware address placed here, 0x0038 */
> +
> +  0x0000,Cmd_END|Cmd_Nop, /* end of configure sequence */
> +  0x003e,
> +
> +  0x0000
> +
> +};
> +
> +#define CONF_LINK 0x0020
> +#define CONF_HW_ADDR 0x0038
> +
> +/* maps irq number to EtherExpress magic value */
> +static char irqrmap[] = { 0,0,1,2,3,4,0,0,0,1,5,6,0,0,0,0 };
> +
> +static unsigned char started=0;
> +
> +/*
> + * Prototypes for Linux interface
> + */
> +
> +extern int                  express_probe(struct device *dev);
> +static int                     eexp_open (struct device *dev);
> +static int                     eexp_close(struct device *dev);
> +static struct enet_statistics *eexp_stats(struct device *dev);
> +static int                     eexp_xmit (struct sk_buff *buf, struct device *dev);
> +
> +static void                    eexp_irq  (int irq, struct pt_regs *regs);
> +
> +/*
> + * Prototypes for hardware access functions
> + */
> +
> +static void           eexp_hw_rx        (struct device *dev);
> +static void           eexp_hw_tx        (struct device *dev, unsigned short *buf, unsigned short len);
> +static int            eexp_hw_probe     (struct device *dev,unsigned short ioaddr);
> +static unsigned short eexp_hw_readeeprom(unsigned short ioaddr, unsigned char location);
> +
> +static unsigned short eexp_hw_lasttxstat(struct device *dev);
> +static void           eexp_hw_txrestart (struct device *dev);
> +
> +static void           eexp_hw_txinit    (struct device *dev);
> +static void           eexp_hw_rxinit    (struct device *dev);
> +static void           eexp_hw_rxmap     (struct device *dev,unsigned short rx_buf);
> +
> +static void           eexp_hw_init586   (struct device *dev);
> +static void           eexp_hw_ASICrst   (struct device *dev);
> +
> +/*
> + * Linux interface
> + */
> +
> +/*
> + * checks for presence of EtherExpress card
> + */
> +int express_probe(struct device *dev)
> +{
> +  unsigned short *port,ports[] = { 0x0300,0x0270,0x0320,0x0340,0 };
> +  unsigned short ioaddr = dev->base_addr;
> +
> +  if (ioaddr&0xfe00) return eexp_hw_probe(dev,ioaddr);
> +  else if (ioaddr) return ENXIO;
> +
> +  for ( port=&ports[0] ; *port ; port++ ) {
> +    unsigned short sum = 0;
> +    int i;
> +    
> +    for ( i=0 ; i<4 ; i++ ) {
> +      unsigned short t;
> + 
> +      t = inb(*port + ID_PORT);
> +      sum |= (t>>4) << ((t & 0x03)<<2);
> +    }
> +
> +    if (sum==0xbaba && !eexp_hw_probe(dev,*port)) return 0;
> +  }
> +
> +  return ENODEV;
> +}
> +
> +/*
> + * open and initialize the adapter, ready for use
> + */
> +static int eexp_open(struct device *dev)
> +{
> +  int irq = dev->irq;
> +  unsigned short ioaddr = dev->base_addr;
> +
> +#if NET_DEBUG > 6
> +  printk("%s: eexp_open()\n", dev->name);
> +#endif
> +
> +  if (!irq || !irqrmap[irq]) return -ENXIO;
> +
> +  if (irq2dev_map[irq] ||
> +      /* more consistent, surely? */
> +      ((irq2dev_map[irq]=dev),0) ||
> +      request_irq(irq,&eexp_irq,0,"EExpress")) return -EAGAIN;
> +
> +  request_region(ioaddr,16,"EExpress");
> +
> +  dev->tbusy = 0;
> +  dev->interrupt = 0;
> +
> +  eexp_hw_init586(dev);
> +
> +  dev->start = 1;
> +
> +  return 0;
> +}
> +
> +/*
> + * close and disable the interface, leaving
> + * the 586 in reset
> + */
> +static int eexp_close(struct device *dev)
> +{
> +  unsigned short ioaddr = dev->base_addr;
> +  int irq = dev->irq;
> +
> +  dev->tbusy = 1;
> +  dev->start = 0;
> +  
> +  outb(SIRQ_dis|irqrmap[irq],ioaddr+SET_IRQ);
> +  started = 0;
> +
> +  outw(SCB_CUsuspend|SCB_RUsuspend,ioaddr+SCB_CMD);
> +  outb(0,ioaddr+SIGNAL_CA);
> +
> +  free_irq(irq);
> +  irq2dev_map[irq] = NULL;
> +
> +  outb(i586_RST,ioaddr+EEPROM_Ctrl);
> +  release_region(ioaddr,16);
> +
> +  return 0;
> +}
> +
> +/*
> + * Return interface stats
> + */
> +static struct enet_statistics *eexp_stats(struct device *dev)
> +{
> +  struct net_local *lp = (struct net_local *)dev->priv;
> +
> +  /* 
> +   * Hmmm, this looks a little too easy... The card maintains
> +   * some stats in the SCB, and I'm not convinced we're
> +   * incrementing the most sensible statistics when the card
> +   * returns an error (esp. slow DMA, out-of-resources)
> +   */
> +
> +  return &lp->stats;
> +}
> +
> +/*
> + * Called to transmit a packet, or to allow us to right ourselves
> + * if the kernel thinks we've died.
> + */
> +static int eexp_xmit(struct sk_buff *buf, struct device *dev)
> +{
> +  struct net_local *lp = (struct net_local *)dev->priv;
> +  unsigned short ioaddr = dev->base_addr;
> +
> +#if NET_DEBUG > 6
> +  printk("%s: eexp_xmit()\n", dev->name);
> +#endif
> +
> +  outb(SIRQ_dis|irqrmap[dev->irq],ioaddr+SET_IRQ);
> +
> +  if (dev->tbusy) {
> +    /* This will happen, but hopefully not as often as when
> +     * tbusy==0. If it happens too much, we probably ought
> +     * to think about unwedging ourselves...
> +     */
> +
> +    if (test_bit(0,(void *)&started)) {
> +      if ((jiffies - dev->trans_start)>5) {
> +	if (lp->tx_link==lp->last_tx_restart) {
> +	  unsigned short boguscount=200,rsst;
> +	  printk("%s: Retransmit timed out, status %04x, resetting...\n",dev->name,inw(ioaddr+SCB_STATUS));
> +	  eexp_hw_txinit(dev);
> +	  lp->last_tx_restart = 0;
> +	  outw(lp->tx_link,ioaddr+SCB_CBL);
> +	  outw(0,ioaddr+SCB_STATUS);
> +	  outw(SCB_CUstart,ioaddr+SCB_CMD);
> +	  outb(0,ioaddr+SIGNAL_CA);
> +	  while (!SCB_complete(rsst=inw(ioaddr+SCB_STATUS))) {
> +	    if (!--boguscount) {
> +	      boguscount=200;
> +	      printk("%s: Reset timed out status %04x, retrying...\n",dev->name,rsst);
> +	      outw(lp->tx_link,ioaddr+SCB_CBL);
> +	      outw(0,ioaddr+SCB_STATUS);
> +	      outw(SCB_CUstart,ioaddr+SCB_CMD);
> +	      outb(0,ioaddr+SIGNAL_CA);
> +	    }
> +	  }
> +	  dev->tbusy = 0;
> +	  mark_bh(NET_BH);
> +	} else {
> +	  unsigned short status = inw(ioaddr+SCB_STATUS);
> +	  if (SCB_CUdead(status)) {
> +	    unsigned short txstatus = eexp_hw_lasttxstat(dev);
> +	    printk("%s: Transmit timed out, CU not active status %04x %04x, restarting...\n",
> +		   dev->name, status, txstatus);
> +	    eexp_hw_txrestart(dev);
> +	  } else {
> +	    unsigned short txstatus = eexp_hw_lasttxstat(dev);
> +	    if (dev->tbusy && !txstatus) {
> +	      printk("%s: CU wedged, status %04x %04x, resetting...\n",dev->name,status,txstatus);
> +	      eexp_hw_init586(dev); 
> +	      dev->tbusy = 0;
> +	      mark_bh(NET_BH);
> +	    }
> +	  }
> +	}
> +      }
> +    } else {
> +      if ((jiffies-lp->init_time)>10) {
> +	unsigned short status = inw(ioaddr+SCB_STATUS);
> +	printk("%s: i82586 startup timed out, status %04x, resetting...\n",
> +	       dev->name, status);
> +	eexp_hw_init586(dev);
> +	dev->tbusy = 0;
> +	mark_bh(NET_BH);
> +      }
> +    }
> +  }
> +    
> +  if (buf==NULL) {
> +    unsigned short status = inw(ioaddr+SCB_STATUS);
> +    unsigned short txstatus = eexp_hw_lasttxstat(dev);
> +    if (SCB_CUdead(status)) {
> +      printk("%s: CU has died! status %04x %04x, attempting to restart...\n",
> +	     dev->name, status, txstatus);
> +      lp->stats.tx_errors++;
> +      eexp_hw_txrestart(dev);
> +    }
> +    dev_tint(dev);
> +    outb(SIRQ_en|irqrmap[dev->irq],ioaddr+SET_IRQ);
> +
> +    return 0;
> +  }
> +
> +  if (set_bit(0,(void *)&dev->tbusy)) {
> +/*    printk("%s: Transmitter busy or access conflict\n",dev->name); */
> +    lp->stats.tx_dropped++;
> +  } else {
> +    unsigned short length = (ETH_ZLEN < buf->len) ? buf->len : ETH_ZLEN;
> +    unsigned short *data = (unsigned short *)buf->data;
> +      
> +    outb(SIRQ_dis|irqrmap[dev->irq],ioaddr+SET_IRQ);
> +    eexp_hw_tx(dev,data,length);
> +    outb(SIRQ_en|irqrmap[dev->irq],ioaddr+SET_IRQ);
> +  }
> +
> +  dev_kfree_skb(buf, FREE_WRITE);
> +  outb(SIRQ_en|irqrmap[dev->irq],ioaddr+SET_IRQ);
> +  
> +  return 0;
> +}
> +
> +/*
> + * Handle an EtherExpress interrupt
> + * If we've finished initializing, start the RU and CU up.
> + * If we've already started, reap tx buffers, handle any received packets,
> + * check to make sure we've not become wedged.
> + */
> +static void eexp_irq(int irq, struct pt_regs *regs)
> +{
> +  struct device *dev = irq2dev_map[irq];
> +  struct net_local *lp;
> +  unsigned short ioaddr,status,ack_cmd;
> +  unsigned short old_rp,old_wp;
> +
> +  if (dev==NULL) {
> +    printk("net_interrupt(): irq %d for unknown device caught by EExpress\n",irq);
> +    return;
> +  }
> +
> +#if NET_DEBUG > 6
> +  printk("%s: interrupt\n", dev->name);
> +#endif
> +
> +  dev->interrupt = 1; /* should this be reset on exit? */
> +  
> +  lp = (struct net_local *)dev->priv;
> +  ioaddr = dev->base_addr;
> +
> +  outb(SIRQ_dis|irqrmap[irq],ioaddr+SET_IRQ);
> +
> +  old_rp = inw(ioaddr+READ_PTR);
> +  old_wp = inw(ioaddr+WRITE_PTR);
> +  status = inw(ioaddr+SCB_STATUS);
> +  ack_cmd = SCB_ack(status);
> +
> +  if (started==0 && SCB_complete(status)) {
> +    if (SCB_CUstat(status)==2) while (SCB_CUstat(inw(ioaddr+SCB_STATUS))==2) ;
> +    started=1;
> +    outw(lp->tx_link,ioaddr+SCB_CBL);
> +    outw(RX_BUF_START,ioaddr+SCB_RFA);
> +    ack_cmd |= SCB_CUstart | SCB_RUstart;
> +  } else if (started) {
> +    unsigned short txstatus;
> +
> +    txstatus = eexp_hw_lasttxstat(dev);
> +  }
> +  
> +  if (SCB_rxdframe(status)) {
> +    eexp_hw_rx(dev);
> +  }
> +
> +  if ((started&2)!=0 && SCB_RUstat(status)!=4) {
> +    printk("%s: RU stopped status %04x, restarting...\n",dev->name,status);
> +    lp->stats.rx_errors++;
> +    eexp_hw_rxinit(dev);
> +    outw(RX_BUF_START,ioaddr+SCB_RFA);
> +    ack_cmd |= SCB_RUstart;
> +  } else if (started==1 && SCB_RUstat(status)==4) started|=2;
> +
> +  outw(ack_cmd,ioaddr+SCB_CMD);
> +  outb(0,ioaddr+SIGNAL_CA);
> +
> +  outw(old_rp,ioaddr+READ_PTR);
> +  outw(old_wp,ioaddr+WRITE_PTR);
> +  outb(SIRQ_en|irqrmap[irq],ioaddr+SET_IRQ);
> +
> +  return;
> +}
> +
> +/*
> + * Hardware access functions
> + */
> +
> +/*
> + * Check all the receive buffers, and hand any received packets
> + * to the upper levels. Basic sanity check on each frame
> + * descriptor
> + */
> +static void eexp_hw_rx(struct device *dev)
> +{
> +  struct net_local *lp = (struct net_local *)dev->priv;
> +  unsigned short ioaddr = dev->base_addr;
> +  unsigned short old_wp = inw(ioaddr+WRITE_PTR);
> +  unsigned short old_rp = inw(ioaddr+READ_PTR);
> +  unsigned short rx_block = lp->rx_first;
> +  unsigned short boguscount = NUM_RX_BUFS;
> +
> +#if NET_DEBUG > 6
> +  printk("%s: eexp_hw_rx()\n", dev->name);
> +#endif
> +
> +  while (outw(rx_block,ioaddr+READ_PTR),boguscount--) {
> +    unsigned short status = inw(ioaddr);
> +    unsigned short rfd_cmd = inw(ioaddr);
> +    unsigned short rx_next = inw(ioaddr);
> +    unsigned short pbuf = inw(ioaddr);
> +    unsigned short pkt_len;
> +
> +    if (FD_Done(status)) {
> +      outw(pbuf,ioaddr+READ_PTR);
> +      pkt_len = inw(ioaddr);
> +
> +      if (rfd_cmd!=0x0000 || pbuf!=rx_block+0x16
> +	  || (pkt_len & 0xc000)!=0xc000) {
> +	printk("%s: Rx frame at %04x corrupted, status %04x, cmd %04x, "
> +	       "next %04x, pbuf %04x, len %04x\n",dev->name,rx_block,
> +	       status,rfd_cmd,rx_next,pbuf,pkt_len);
> +	eexp_hw_rxmap(dev,rx_block);
> +	boguscount++;
> +	continue;
> +      } else if (!FD_OK(status)) {
> +	lp->stats.rx_errors++;
> +        if (FD_CRC(status)) lp->stats.rx_crc_errors++;
> +	if (FD_Align(status)) lp->stats.rx_frame_errors++;
> +	if (FD_Resrc(status)) lp->stats.rx_fifo_errors++;
> +	if (FD_DMA(status)) lp->stats.rx_over_errors++;
> +	if (FD_Short(status)) lp->stats.rx_length_errors++;
> +      } else {
> +	struct sk_buff *skb;
> +
> +	pkt_len &= 0x3fff;
> +	skb = dev_alloc_skb(pkt_len+16);
> +	if (skb == NULL) {
> +	  printk("%s: Memory squeeze, dropping packet\n",dev->name);
> +	  lp->stats.rx_dropped++;
> +	  break;
> +	}
> +	skb->dev = dev;
> +	skb_reserve(skb, 2);
> +	
> +	outw(pbuf+10,ioaddr+READ_PTR);
> +	insw(ioaddr,skb_put(skb,pkt_len),(pkt_len+1)>>1);
> +	skb->protocol = eth_type_trans(skb,dev);
> +	netif_rx(skb);
> +	lp->stats.rx_packets++;
> +      }
> +      
> +      outw(rx_block,ioaddr+WRITE_PTR);
> +      outw(0x0000,ioaddr);
> +      outw(0x0000,ioaddr);
> +    }
> +    rx_block = rx_next;
> +  }
> +  
> +  outw(old_rp,ioaddr+READ_PTR);
> +  outw(old_wp,ioaddr+WRITE_PTR);
> +}
> +
> +/*
> + * Hand a packet to the card for transmission
> + * If we get here, we MUST have already checked
> + * to make sure there is room in the transmit
> + * buffer region
> + */
> +static void eexp_hw_tx(struct device *dev, unsigned short *buf, unsigned short len)
> +{
> +  struct net_local *lp = (struct net_local *)dev->priv;
> +  unsigned short ioaddr = dev->base_addr;
> +  unsigned short old_wp = inw(ioaddr+WRITE_PTR);
> +
> +  outw(lp->tx_head,ioaddr+WRITE_PTR);
> +
> +  outw(0x0000,ioaddr);
> +  outw(Cmd_INT|Cmd_Xmit,ioaddr);
> +  outw(lp->tx_head+0x08,ioaddr);
> +  outw(lp->tx_head+0x0e,ioaddr);
> +
> +  outw(0x0000,ioaddr);
> +  outw(0x0000,ioaddr);
> +  outw(lp->tx_head+0x08,ioaddr);
> +
> +  outw(0x8000|len,ioaddr);
> +  outw(-1,ioaddr);
> +  outw(lp->tx_head+0x16,ioaddr);
> +  outw(0,ioaddr);
> +  
> +  outsw(ioaddr,buf,(len+1)>>1);
> +
> +  outw(lp->tx_tail+0x0c,ioaddr+WRITE_PTR);
> +  outw(lp->tx_head,ioaddr);
> +  dev->trans_start = jiffies;
> +
> +  lp->tx_tail = lp->tx_head;
> +  if (lp->tx_head==TX_BUF_START+((NUM_TX_BUFS-1)*TX_BUF_SIZE)) lp->tx_head = TX_BUF_START;
> +  else lp->tx_head += TX_BUF_SIZE;
> +
> +  if (lp->tx_head != lp->tx_reap) dev->tbusy = 0;
> +
> +  outw(old_wp,ioaddr+WRITE_PTR);
> +}
> +
> +/*
> + * Sanity check the suspected EtherExpress card
> + * Read hardware address, reset card, size memory and
> + * initialize buffer memory pointers. These should
> + * probably be held in dev->priv, incase someone has 2
> + * differently configured cards in their box (Arghhh!)
> + */
> +static int eexp_hw_probe(struct device *dev, unsigned short ioaddr)
> +{
> +  unsigned short hw_addr[3];
> +  int i;
> +  unsigned char *chw_addr = (unsigned char *)hw_addr;
> +
> +  printk("%s: EtherExpress at %#x, ",dev->name,ioaddr);
> +
> +  hw_addr[0] = eexp_hw_readeeprom(ioaddr,2);
> +  hw_addr[1] = eexp_hw_readeeprom(ioaddr,3);
> +  hw_addr[2] = eexp_hw_readeeprom(ioaddr,4);
> +
> +  if (hw_addr[2]!=0x00aa || ((hw_addr[1] & 0xff00)!=0x0000)) {
> +    printk("rejected: invalid address %04x%04x%04x\n",
> +	   hw_addr[2],hw_addr[1],hw_addr[0]);
> +    return ENODEV;
> +  }
> +
> +  dev->base_addr = ioaddr;
> +  
> +  for ( i=0 ; i<6 ; i++ ) dev->dev_addr[i] = chw_addr[5-i];
> +
> +  {
> +    char irqmap[]={0, 9, 3, 4, 5, 10, 11, 0};
> +    char *ifmap[]={"AUI", "BNC", "10baseT"};
> +    enum iftype {AUI=0, BNC=1, TP=2};
> +    unsigned short setupval = eexp_hw_readeeprom(ioaddr,0);
> +
> +    dev->irq = irqmap[setupval>>13];
> +    dev->if_port = !(setupval & 0x1000) ? AUI :
> +      eexp_hw_readeeprom(ioaddr,5) & 0x1 ? TP : BNC;
> +
> +    printk("IRQ %d, Interface %s, ",dev->irq,ifmap[dev->if_port]);
> +
> +    outb(SIRQ_dis|irqrmap[dev->irq],ioaddr+SET_IRQ);
> +    outb(0,ioaddr+SET_IRQ);
> +  }
> +
> +  eexp_hw_ASICrst(dev);
> +
> +  {
> +    unsigned short i586mso = 0x023e;
> +    unsigned short old_wp,old_rp,old_a0,old_a1;
> +    unsigned short a0_0,a1_0,a0_1,a1_1;
> +		
> +    old_wp = inw(ioaddr+WRITE_PTR);
> +    old_rp = inw(ioaddr+READ_PTR);
> +
> +    outw(0x8000+i586mso,ioaddr+READ_PTR);
> +    old_a1 = inw(ioaddr);
> +    outw(i586mso,ioaddr+READ_PTR);
> +    old_a0 = inw(ioaddr);
> +
> +    outw(i586mso,ioaddr+WRITE_PTR);
> +    outw(0x55aa,ioaddr);
> +    outw(i586mso,ioaddr+READ_PTR);
> +    a0_0 = inw(ioaddr);
> +    outw(0x8000+i586mso,ioaddr+WRITE_PTR);
> +    outw(0x5a5a,ioaddr);
> +    outw(0x8000+i586mso,ioaddr+READ_PTR);
> +    a1_0 = inw(ioaddr);
> +    outw(i586mso,ioaddr+READ_PTR);
> +    a0_1 = inw(ioaddr);
> +    outw(i586mso,ioaddr+WRITE_PTR);
> +    outw(0x1234,ioaddr);
> +    outw(0x8000+i586mso,ioaddr+READ_PTR);
> +    a1_1 = inw(ioaddr);
> +
> +    if ((a0_0 != a0_1) || (a1_0 != a1_1) || (a1_0 != 0x5a5a) || (a0_0 != 0x55aa)) {
> +      printk("32k\n");
> +      RX_BUF_END = 0x7ff6;
> +    } else {
> +      printk("64k\n");
> +      NUM_TX_BUFS = 8;
> +      RX_BUF_START = TX_BUF_START + (NUM_TX_BUFS*TX_BUF_SIZE);
> +      RX_BUF_END = 0xfff6;
> +    }
> +
> +    outw(0x8000+i586mso,ioaddr+WRITE_PTR);
> +    outw(old_a1,ioaddr);
> +    outw(i586mso,ioaddr+WRITE_PTR);
> +    outw(old_a0,ioaddr);
> +    outw(old_wp,ioaddr+WRITE_PTR);
> +    outw(old_rp,ioaddr+READ_PTR);
> +  }
> +  
> +  if (net_debug) printk(version);
> +
> +  dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
> +  memset(dev->priv, 0, sizeof(struct net_local));
> +
> +  dev->open = eexp_open;
> +  dev->stop = eexp_close;
> +  dev->hard_start_xmit = eexp_xmit;
> +  dev->get_stats = eexp_stats;
> +
> +  ether_setup(dev);
> +
> +  return 0;
> +}
> +
> +/*
> + * Read a word from eeprom location (0-63?)
> + */
> +static unsigned short eexp_hw_readeeprom(unsigned short ioaddr, unsigned char location)
> +{
> +  unsigned short cmd = 0x180|(location&0x7f);
> +  unsigned short rval = 0,wval = EC_CS|i586_RST;
> +  int i;
> +  
> +  outb(EC_CS|i586_RST,ioaddr+EEPROM_Ctrl);
> +  
> +  for ( i=0x100 ; i ; i>>=1 ) {
> +    if (cmd&i) wval |= EC_Wr;
> +    else wval &= ~EC_Wr;
> +
> +    outb(wval,ioaddr+EEPROM_Ctrl);
> +    outb(wval|EC_Clk,ioaddr+EEPROM_Ctrl);
> +    eeprom_delay();
> +    outb(wval,ioaddr+EEPROM_Ctrl);
> +    eeprom_delay();
> +  }
> +  wval &= ~EC_Wr;
> +
> +  outb(wval,ioaddr+EEPROM_Ctrl);
> +
> +  for ( i=0x8000 ; i ; i>>=1 ) {
> +    outb(wval|EC_Clk,ioaddr+EEPROM_Ctrl);
> +    eeprom_delay();
> +    if (inb(ioaddr+EEPROM_Ctrl)&EC_Rd) rval |= i;
> +    outb(wval,ioaddr+EEPROM_Ctrl);
> +    eeprom_delay();
> +  }
> +
> +  wval &= ~EC_CS;
> +  outb(wval|EC_Clk,ioaddr+EEPROM_Ctrl);
> +  eeprom_delay();
> +  outb(wval,ioaddr+EEPROM_Ctrl);
> +  eeprom_delay();
> +
> +  return rval;
> +}
> +
> +/*
> + * Reap tx buffers and return last transmit status.
> + * if ==0 then either:
> + *    a) we're not transmitting anything, so why are we here?
> + *    b) we've died.
> + * otherwise, Stat_Busy(return) means we've still got some packets
> + * to transmit, Stat_Done(return) means our buffers should be empty
> + * again
> + */
> +static unsigned short eexp_hw_lasttxstat(struct device *dev)
> +{
> +  struct net_local *lp = (struct net_local *)dev->priv;
> +  unsigned short ioaddr = dev->base_addr;
> +  unsigned short old_rp = inw(ioaddr+READ_PTR);
> +  unsigned short old_wp = inw(ioaddr+WRITE_PTR);
> +  unsigned short tx_block = lp->tx_reap;
> +  unsigned short status;
> +  
> +  if (!test_bit(0,(void *)&dev->tbusy) && lp->tx_head==lp->tx_reap) return 0x0000;
> +
> +  do {
> +    outw(tx_block,ioaddr+READ_PTR);
> +    status = inw(ioaddr);
> +    if (!Stat_Done(status)) {
> +      lp->tx_link = tx_block;
> +      outw(old_rp,ioaddr+READ_PTR);
> +      outw(old_wp,ioaddr+WRITE_PTR);
> +      return status;
> +    } else {
> +      lp->last_tx_restart = 0;
> +      lp->stats.collisions += Stat_NoColl(status);
> +      if (!Stat_OK(status)) {
> +	if (Stat_Abort(status)) lp->stats.tx_aborted_errors++;
> +	if (Stat_TNoCar(status) || Stat_TNoCTS(status)) lp->stats.tx_carrier_errors++;
> +	if (Stat_TNoDMA(status)) lp->stats.tx_fifo_errors++;
> +      } else lp->stats.tx_packets++;
> +    }
> +    if (tx_block == TX_BUF_START+((NUM_TX_BUFS-1)*TX_BUF_SIZE)) lp->tx_reap = tx_block = TX_BUF_START;
> +    else lp->tx_reap = tx_block += TX_BUF_SIZE;
> +    dev->tbusy = 0;
> +    mark_bh(NET_BH);
> +  } while (lp->tx_reap != lp->tx_head);
> +
> +  lp->tx_link = lp->tx_tail + 0x08;
> +
> +  outw(old_rp,ioaddr+READ_PTR);
> +  outw(old_wp,ioaddr+WRITE_PTR);
> +
> +  return status;
> +}
> +
> +/* 
> + * This should never happen. It is called when some higher
> + * routine detects the CU has stopped, to try and restart
> + * it from the last packet we knew we were working on,
> + * or the idle loop if we had finished for the time.
> + */
> +static void eexp_hw_txrestart(struct device *dev)
> +{
> +  struct net_local *lp = (struct net_local *)dev->priv;
> +  unsigned short ioaddr = dev->base_addr;
> +  
> +  lp->last_tx_restart = lp->tx_link;
> +  outw(lp->tx_link,ioaddr+SCB_CBL);
> +  outw(SCB_CUstart,ioaddr+SCB_CMD);
> +  outw(0,ioaddr+SCB_STATUS);
> +  outb(0,ioaddr+SIGNAL_CA);
> +
> +  {
> +    unsigned short boguscount=50,failcount=5;
> +    while (!inw(ioaddr+SCB_STATUS)) {
> +      if (!--boguscount) {
> +	if (--failcount) {
> +	  printk("%s: CU start timed out, status %04x, cmd %04x\n",
> +		 dev->name, inw(ioaddr+SCB_STATUS), inw(ioaddr+SCB_CMD));
> +	  outw(lp->tx_link,ioaddr+SCB_CBL);
> +	  outw(0,ioaddr+SCB_STATUS);
> +	  outw(SCB_CUstart,ioaddr+SCB_CMD);
> +	  outb(0,ioaddr+SIGNAL_CA);
> +	  boguscount = 100;
> +	} else {
> +	  printk("%s: Failed to restart CU, resetting board...\n",dev->name);
> +	  eexp_hw_init586(dev);
> +	  dev->tbusy = 0;
> +	  mark_bh(NET_BH);
> +	  return;
> +	}
> +      }
> +    }
> +  }
> +
> +}
> +
> +/*
> + * Writes down the list of transmit buffers into card
> + * memory. Initial seperate, repeated transmits link
> + * them into a circular list, such that the CU can
> + * be constantly active, and unlink them as we reap
> + * transmitted packet buffers, so the CU doesn't loop
> + * and endlessly transmit packets. (Try hacking the driver
> + * to send continuous broadcast messages, say ARP requests
> + * on a subnet with Windows boxes running on Novell and
> + * LAN Workplace with EMM386. Amusing to watch them all die
> + * horribly leaving the Linux boxes up!)
> + */
> +static void eexp_hw_txinit(struct device *dev)
> +{
> +  struct net_local *lp = (struct net_local *)dev->priv;
> +  unsigned short ioaddr = dev->base_addr;
> +  unsigned short old_wp = inw(ioaddr+WRITE_PTR);
> +  unsigned short tx_block = TX_BUF_START;
> +  unsigned short curtbuf;
> +
> +  for ( curtbuf=0 ; curtbuf<NUM_TX_BUFS ; curtbuf++ ) {
> +    outw(tx_block,ioaddr+WRITE_PTR);
> +    
> +    outw(0x0000,ioaddr);
> +    outw(Cmd_INT|Cmd_Xmit,ioaddr);
> +    outw(tx_block+0x08,ioaddr);
> +    outw(tx_block+0x0e,ioaddr);
> +
> +    outw(0x0000,ioaddr);
> +    outw(0x0000,ioaddr);
> +    outw(tx_block+0x08,ioaddr);
> +    
> +    outw(0x8000,ioaddr);
> +    outw(-1,ioaddr);
> +    outw(tx_block+0x16,ioaddr);
> +    outw(0x0000,ioaddr);
> +
> +    tx_block += TX_BUF_SIZE;
> +  }
> +
> +  lp->tx_head = TX_BUF_START;
> +  lp->tx_reap = TX_BUF_START;
> +  lp->tx_tail = tx_block - TX_BUF_SIZE;
> +  lp->tx_link = lp->tx_tail + 0x08;
> +  RX_BUF_START = tx_block;
> +
> +  outw(old_wp,ioaddr+WRITE_PTR);
> +
> +}
> +
> +/* is this a standard test pattern, or dbecker randomness? */
> +unsigned short rx_words[] = { 0xfeed,0xf00d,0xf001,0x0505,0x2424,0x6565,0xdeaf };
> +/*
> + * Write the circular list of receive buffer descriptors to
> + * card memory. Note, we no longer mark the end of the list,
> + * so if all the buffers fill up, the 82586 will loop until
> + * we free one. This may sound dodgy, but it works, and
> + * it makes the error detection in the interrupt handler
> + * a lot simpler.
> + */
> +static void eexp_hw_rxinit(struct device *dev)
> +{
> +  struct net_local *lp = (struct net_local *)dev->priv;
> +  unsigned short ioaddr = dev->base_addr;
> +  unsigned short old_wp = inw(ioaddr+WRITE_PTR);
> +  unsigned short rx_block = RX_BUF_START;
> +
> +  NUM_RX_BUFS = 0;
> +  lp->rx_first = rx_block;
> +  do {
> +    NUM_RX_BUFS++;
> +    outw(rx_block,ioaddr+WRITE_PTR);
> +
> +    outw(0x0000,ioaddr);
> +    outw(0x0000,ioaddr);
> +    outw(rx_block+RX_BUF_SIZE,ioaddr);
> +    outw(rx_block+0x16,ioaddr);
> +    outsw(ioaddr, rx_words, sizeof(rx_words)>>1);
> +
> +    outw(0x8000,ioaddr);
> +    outw(-1,ioaddr);
> +    outw(rx_block+0x20,ioaddr);
> +    outw(0x0000,ioaddr);
> +    outw(0x8000|(RX_BUF_SIZE-0x20),ioaddr);
> +
> +    lp->rx_last = rx_block;
> +    rx_block += RX_BUF_SIZE;
> +  } while (rx_block <= RX_BUF_END-RX_BUF_SIZE);
> +
> +  outw(lp->rx_last+4,ioaddr+WRITE_PTR);
> +  outw(lp->rx_first,ioaddr);
> +
> +  outw(old_wp,ioaddr+WRITE_PTR);
> +}
> +
> +/*
> + * This really ought not to be necessary now. Repairs a single
> + * damaged receive buffer. If buffer memory is getting bashed
> + * enough to call this, we probably have bigger problems that can
> + * be fixed here.
> + */
> +static void eexp_hw_rxmap(struct device *dev, unsigned short rx_buf)
> +{
> +  struct net_local *lp = (struct net_local *)dev->priv;
> +  unsigned short ioaddr = dev->base_addr;
> +  unsigned short old_wp = inw(ioaddr+WRITE_PTR);
> +
> +  outw(rx_buf,ioaddr+WRITE_PTR);
> +  
> +  outw(0x0000,ioaddr);
> +  outw(0x0000,ioaddr);
> +  outw((rx_buf==lp->rx_last)?lp->rx_first:(rx_buf+RX_BUF_SIZE),ioaddr);
> +  outw(rx_buf+0x16,ioaddr);
> +  outsw(ioaddr, rx_words, sizeof(rx_words)>>1);
> +
> +  outw(0x8000,ioaddr);
> +  outw(-1,ioaddr);
> +  outw(rx_buf+0x20,ioaddr);
> +  outw(0x0000,ioaddr);
> +  outw(0x8000|(RX_BUF_SIZE-0x20),ioaddr);
> +
> +  outw(old_wp,ioaddr+WRITE_PTR);
> +}
> +
> +/*
> + * Reset the 586, fill memory (including calls to
> + * eexp_hw_[(rx)(tx)]init()) unreset, and start
> + * the configuration sequence. We don't wait for this
> + * to finish, but allow the interrupt handler to start
> + * the CU and RU for us. We can't start the receive/
> + * transmission system up before we know that the
> + * hardware is configured correctly
> + */
> +static void eexp_hw_init586(struct device *dev)
> +{
> +  struct net_local *lp = (struct net_local *)dev->priv;
> +  unsigned short ioaddr = dev->base_addr;
> +
> +  started = 0;
> +  set_loopback;
> +
> +  outb(SIRQ_dis|irqrmap[dev->irq],ioaddr+SET_IRQ);
> +
> +  outb(i586_RST,ioaddr+EEPROM_Ctrl);
> +
> +  {
> +    unsigned short wcnt;
> +
> +    wcnt = 0;
> +    outw(0,ioaddr+WRITE_PTR);
> +    while ((wcnt+=2) != RX_BUF_END+12) outw(0,ioaddr);
> +  } 
> +  
> +  outw(RX_BUF_END,ioaddr+WRITE_PTR);
> +  outsw(ioaddr, start_code, sizeof(start_code)>>1);
> +
> +  outw(CONF_HW_ADDR,ioaddr+WRITE_PTR);
> +  outsw(ioaddr,dev->dev_addr,3);
> +
> +  eexp_hw_txinit(dev);
> +  eexp_hw_rxinit(dev);
> +
> +  outw(0,ioaddr+WRITE_PTR);
> +  outw(1,ioaddr);
> +  outb(0,ioaddr+EEPROM_Ctrl);
> +
> +  outw(0,ioaddr+SCB_CMD);
> +  outb(0,ioaddr+SIGNAL_CA);
> +  
> +  {
> +    unsigned short rboguscount=50,rfailcount=5;
> +    while (outw(0,ioaddr+READ_PTR),inw(ioaddr)) {
> +      if (!--rboguscount) {
> +	printk("%s: i82586 reset timed out, kicking...\n",
> +	       dev->name);
> +	outw(0,ioaddr+SCB_CMD);
> +	outb(0,ioaddr+SIGNAL_CA);
> +	rboguscount = 100;
> +	if (!--rfailcount) {
> +	  printk("%s: i82586 not responding, giving up.\n",
> +		 dev->name);
> +	  return;
> +	}
> +      }
> +    }
> +  }
> +
> +  outw(CONF_LINK,ioaddr+SCB_CBL);
> +  outw(0,ioaddr+SCB_STATUS);
> +  outw(0xf000|SCB_CUstart,ioaddr+SCB_CMD);
> +  outb(0,ioaddr+SIGNAL_CA);
> +
> +  {
> +    unsigned short iboguscount=50,ifailcount=5;
> +    while (!inw(ioaddr+SCB_STATUS)) {
> +      if (!--iboguscount) {
> +	if (--ifailcount) {
> +	  printk("%s: i82586 initialization timed out, status %04x, cmd %04x\n",
> +		 dev->name, inw(ioaddr+SCB_STATUS), inw(ioaddr+SCB_CMD));
> +	  outw(CONF_LINK,ioaddr+SCB_CBL);
> +	  outw(0,ioaddr+SCB_STATUS);
> +	  outw(0xf000|SCB_CUstart,ioaddr+SCB_CMD);
> +	  outb(0,ioaddr+SIGNAL_CA);
> +	  iboguscount = 100;
> +	} else {
> +	  printk("%s: Failed to initialize i82586, giving up.\n",dev->name);
> +	  return;
> +	}
> +      }
> +    }
> +  }
> +  
> +  outb(SIRQ_en|irqrmap[dev->irq],ioaddr+SET_IRQ);
> +  clear_loopback;
> +  
> +  lp->init_time = jiffies;
> +  if (started) printk("%s: Uh? We haven't started yet\n",dev->name);
> +
> +  return;
> +}
> +
> +/* 
> + * completely reset the EtherExpress hardware. We will most likely get
> + * an interrupt during this whether we want one or not. It is best,
> + * therefore, to call this while we don't have a request_irq() on.
> + */
> +static void eexp_hw_ASICrst(struct device *dev)
> +{
> +  unsigned short ioaddr = dev->base_addr;
> +  unsigned short wrval = 0x0001,succount=0,boguscount=500;
> +
> +  outb(SIRQ_dis|irqrmap[dev->irq],ioaddr+SET_IRQ);
> +
> +  set_loopback; /* yet more paranoia - since we're resetting the ASIC
> +                 * that controls this function, how can it possibly work?
> +		 */
> +  started = 0;
> +
> +  outb(ASIC_RST|i586_RST,ioaddr+EEPROM_Ctrl);
> +
> +  while (succount<20) {
> +    if (wrval == 0xffff) wrval = 0x0001;
> +    outw(0,ioaddr+WRITE_PTR);
> +    outw(wrval,ioaddr);
> +    outw(0,ioaddr+READ_PTR);
> +    if (wrval++ == inw(ioaddr)) succount++;
> +    else {
> +      succount = 0;
> +      if (!boguscount--) {
> +	boguscount = 500;
> +	printk("%s: Having problems resetting EtherExpress ASIC, continuing...\n",
> +	       dev->name);
> +	wrval = 0x0001;
> +	outb(ASIC_RST|i586_RST,ioaddr+EEPROM_Ctrl);
> +      }
> +    }
> +  }
> +
> +  outb(i586_RST,ioaddr+EEPROM_Ctrl);
> +}
> +
> +/*
> + * MODULE stuff
> + */
> +
> +#ifdef MODULE
> +char kernel_version[] = UTS_RELEASE;
> +static struct device dev_eexpress = {
> + "EExpress", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, eexp_probe };
> +
> +int irq = 0;
> +int io = 0;
> +
> +int init_module(void)
> +{
> +  dev_eexpress.base_addr = io;
> +  dev_eexpress.irq = irq;
> +  if (register_netdev(&dev_eexpress) != 0) return -EIO;
> +  return 0;
> +}
> +
> +void cleanup_module(void)
> +{
> +  if (MOD_IN_USE) printk("EExpress: device busy, remove delayed\n");
> +  else {
> +    unregister_netdev(&dev_eexpress);
> +    kfree_s(dev_eexpress.priv,sizeof(struct net_local));
> +    dev_eexpress.priv = NULL;
> +  }
> +}
> +#endif
> --- /dev/null	Wed Jan 17 15:22:50 1996
> +++ eth82586.h	Wed Jan 31 18:56:57 1996
> @@ -0,0 +1,176 @@
> +/*
> + * eth82586.h: Intel EtherExpress defines
> + *
> + * Written 1995 by John Sullivan
> + * See eexpress2.c for furthur details
> + * documentation and usage to do.
> + */
> +
> +/*
> + * EtherExpress card register addresses
> + * as offsets from the base IO region (dev->base_addr)
> + */
> +
> +#define DATAPORT 0x0000
> +#define WRITE_PTR 0x0002
> +#define READ_PTR 0x0004
> +#define SIGNAL_CA 0x0006
> +#define SET_IRQ 0x0007
> +#define SM_PTR 0x0008
> +#define MEM_Ctrl 0x000b
> +#define MEM_Page_Ctrl 0x000c
> +#define Config 0x000d
> +#define EEPROM_Ctrl 0x000e
> +#define ID_PORT 0x000f
> +
> +/*
> + * offset to shadowed memory, 0 <= x <= 31. We don't use this yet,
> + * but may in the future. Is shadow memory access any faster than
> + * dataport access?
> + */
> +#define SM_ADDR(x) (0x4000+((x&0x10)<<10)+(x&0xf))
> +
> +/* Always mirrors eexp-memory at 0x0008-0x000f */
> +#define SCB_STATUS 0xc008
> +#define SCB_CMD 0xc00a
> +#define SCB_CBL 0xc00c
> +#define SCB_RFA 0xc00e
> +
> +
> +
> +/*
> + * card register defines
> + */
> +
> +/* SET_IRQ */
> +#define SIRQ_en 0x08
> +#define SIRQ_dis 0x00
> +
> +/* Config */
> +#define set_loopback outb(inb(ioaddr+Config)|0x02,ioaddr+Config)
> +#define clear_loopback outb(inb(ioaddr+Config)&0xfd,ioaddr+Config)
> +
> +/* EEPROM_Ctrl */
> +#define EC_Clk 0x01
> +#define EC_CS  0x02
> +#define EC_Wr  0x04
> +#define EC_Rd  0x08
> +#define ASIC_RST 0x40
> +#define i586_RST  0x80
> +
> +#define eeprom_delay() { int _i = 40; while (--_i>0) { __SLOW_DOWN_IO; }}
> +
> +/*
> + * i82586 Memory Configuration
> + */
> +
> +/* (System Configuration Pointer) System start up block, read after 586_RST */
> +#define SCP_START 0xfff6
> +
> +
> +/* Intermediate System Configuration Pointer */
> +#define ISCP_START 0x0000
> +/* System Command Block */
> +#define SCB_START 0x0008
> +
> +/*
> + * Start of buffer region. If we have 64k memory, eexp_hw_probe() may raise
> + * NUM_TX_BUFS. RX_BUF_END is set to the end of memory, and all space between
> + * the transmit buffer region and end of memory used for as many receive buffers
> + * as we can fit. See eexp_hw_[(rx)(tx)]init().
> + */
> +#define TX_BUF_START 0x0100
> +#define TX_BUF_SIZE ((24+ETH_FRAME_LEN+31)&~0x1f)
> +unsigned short NUM_TX_BUFS=4;
> +unsigned short RX_BUF_START;
> +#define RX_BUF_SIZE ((32+ETH_FRAME_LEN+31)&~0x1f)
> +unsigned short RX_BUF_END=0x7ff6; /* updated automatically to 0xfff6 on 64k cards */
> +unsigned short NUM_RX_BUFS = 4;
> +
> +
> +
> +/*
> + * SCB defines
> + */
> +
> +/* these functions take the SCB status word and test the relavent status bit */
> +#define SCB_complete(s) ((s&0x8000)!=0)
> +#define SCB_rxdframe(s) ((s&0x4000)!=0)
> +#define SCB_CUdead(s) ((s&0x2000)!=0)
> +#define SCB_RUdead(s) ((s&0x1000)!=0)
> +#define SCB_ack(s) (s & 0xf000)
> +
> +/* Command unit status: 0=idle, 1=suspended, 2=active */
> +#define SCB_CUstat(s) ((s&0x0300)>>8)
> +
> +/* Receive unit status: 0=idle, 1=suspended, 2=out of resources, 4=ready */
> +#define SCB_RUstat(s) ((s&0x0070)>>4)
> +
> +/* SCB commands */
> +#define SCB_CUnop     0x0000
> +#define SCB_CUstart   0x0100
> +#define SCB_CUresume  0x0200
> +#define SCB_CUsuspend 0x0300
> +#define SCB_CUabort   0x0400
> +
> +/* ? */
> +#define SCB_resetchip 0x0080
> +
> +#define SCB_RUnop     0x0000
> +#define SCB_RUstart   0x0010
> +#define SCB_RUresume  0x0020
> +#define SCB_RUsuspend 0x0030
> +#define SCB_RUabort   0x0040
> +
> +
> +/*
> + * Command block defines
> + */
> +
> +#define Stat_Done(s)   ((s&0x8000)!=0)
> +#define Stat_Busy(s)   ((s&0x4000)!=0)
> +#define Stat_OK(s)     ((s&0x2000)!=0)
> +#define Stat_Abort(s)  ((s&0x1000)!=0)
> +#define Stat_STFail    ((s&0x0800)!=0)
> +#define Stat_TNoCar(s) ((s&0x0400)!=0)
> +#define Stat_TNoCTS(s) ((s&0x0200)!=0)
> +#define Stat_TNoDMA(s) ((s&0x0100)!=0)
> +#define Stat_TDefer(s) ((s&0x0080)!=0)
> +#define Stat_TColl(s)  ((s&0x0040)!=0)
> +#define Stat_TXColl(s) ((s&0x0020)!=0)
> +#define Stat_NoColl(s) (s&0x000f)
> +
> +/* Cmd_END will end AFTER the command if this is the first
> + * command block after an SCB_CUstart, but BEFORE the command
> + * for all subsequent commands. Best strategy is to place
> + * Cmd_INT on the last command in the sequence, followed by a
> + * dummy Cmd_Nop with Cmd_END after this.
> + */
> +#define Cmd_END     0x8000
> +#define Cmd_SUS     0x4000
> +#define Cmd_INT     0x2000
> +
> +#define Cmd_Nop     0x0000
> +#define Cmd_SetAddr 0x0001
> +#define Cmd_Config  0x0002
> +#define Cmd_MCast   0x0003
> +#define Cmd_Xmit    0x0004
> +#define Cmd_TDR     0x0005
> +#define Cmd_Dump    0x0006
> +#define Cmd_Diag    0x0007
> +
> +
> +/*
> + * Frame Descriptor (Receive block) defines
> + */
> +
> +#define FD_Done(s)  ((s&0x8000)!=0)
> +#define FD_Busy(s)  ((s&0x4000)!=0)
> +#define FD_OK(s)    ((s&0x2000)!=0)
> +
> +#define FD_CRC(s)   ((s&0x0800)!=0)
> +#define FD_Align(s) ((s&0x0400)!=0)
> +#define FD_Resrc(s) ((s&0x0200)!=0)
> +#define FD_DMA(s)   ((s&0x0100)!=0)
> +#define FD_Short(s) ((s&0x0080)!=0)
> +#define FD_NoEOF(s) ((s&0x0040)!=0)
> 
> Phil
> --
> philip blundell trinity college cambridge 01223 356256 pjb27@cam.ac.uk
> 
> 




home help back first fref pref prev next nref lref last post