[1994] in linux-net channel archive
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
>
>