aboutsummaryrefslogtreecommitdiff
path: root/i386/i386at/gpl/linux/net
diff options
context:
space:
mode:
Diffstat (limited to 'i386/i386at/gpl/linux/net')
-rw-r--r--i386/i386at/gpl/linux/net/3c501.c860
-rw-r--r--i386/i386at/gpl/linux/net/3c503.c627
-rw-r--r--i386/i386at/gpl/linux/net/3c503.h91
-rw-r--r--i386/i386at/gpl/linux/net/3c505.c1518
-rw-r--r--i386/i386at/gpl/linux/net/3c505.h245
-rw-r--r--i386/i386at/gpl/linux/net/3c507.c923
-rw-r--r--i386/i386at/gpl/linux/net/3c509.c739
-rw-r--r--i386/i386at/gpl/linux/net/3c59x.c1066
-rw-r--r--i386/i386at/gpl/linux/net/8390.c727
-rw-r--r--i386/i386at/gpl/linux/net/8390.h168
-rw-r--r--i386/i386at/gpl/linux/net/Space.c400
-rw-r--r--i386/i386at/gpl/linux/net/ac3200.c385
-rw-r--r--i386/i386at/gpl/linux/net/apricot.c1046
-rw-r--r--i386/i386at/gpl/linux/net/at1700.c677
-rw-r--r--i386/i386at/gpl/linux/net/atp.c787
-rw-r--r--i386/i386at/gpl/linux/net/atp.h264
-rw-r--r--i386/i386at/gpl/linux/net/de4x5.c2788
-rw-r--r--i386/i386at/gpl/linux/net/de4x5.h645
-rw-r--r--i386/i386at/gpl/linux/net/de600.c853
-rw-r--r--i386/i386at/gpl/linux/net/de620.c1045
-rw-r--r--i386/i386at/gpl/linux/net/de620.h117
-rw-r--r--i386/i386at/gpl/linux/net/depca.c1901
-rw-r--r--i386/i386at/gpl/linux/net/depca.h185
-rw-r--r--i386/i386at/gpl/linux/net/dev.c1413
-rw-r--r--i386/i386at/gpl/linux/net/e2100.c456
-rw-r--r--i386/i386at/gpl/linux/net/eepro.c1169
-rw-r--r--i386/i386at/gpl/linux/net/eexpress.c1034
-rw-r--r--i386/i386at/gpl/linux/net/eth16i.c1214
-rw-r--r--i386/i386at/gpl/linux/net/ewrk3.c1933
-rw-r--r--i386/i386at/gpl/linux/net/ewrk3.h322
-rw-r--r--i386/i386at/gpl/linux/net/hp-plus.c483
-rw-r--r--i386/i386at/gpl/linux/net/hp.c451
-rw-r--r--i386/i386at/gpl/linux/net/hp100.c1144
-rw-r--r--i386/i386at/gpl/linux/net/hp100.h374
-rw-r--r--i386/i386at/gpl/linux/net/i82586.h408
-rw-r--r--i386/i386at/gpl/linux/net/iow.h6
-rw-r--r--i386/i386at/gpl/linux/net/lance.c1129
-rw-r--r--i386/i386at/gpl/linux/net/ne.c733
-rw-r--r--i386/i386at/gpl/linux/net/net_init.c380
-rw-r--r--i386/i386at/gpl/linux/net/ni52.c1110
-rw-r--r--i386/i386at/gpl/linux/net/ni52.h284
-rw-r--r--i386/i386at/gpl/linux/net/ni65.c648
-rw-r--r--i386/i386at/gpl/linux/net/ni65.h130
-rw-r--r--i386/i386at/gpl/linux/net/seeq8005.c760
-rw-r--r--i386/i386at/gpl/linux/net/seeq8005.h156
-rw-r--r--i386/i386at/gpl/linux/net/sk_g16.c2111
-rw-r--r--i386/i386at/gpl/linux/net/sk_g16.h171
-rw-r--r--i386/i386at/gpl/linux/net/smc-ultra.c419
-rw-r--r--i386/i386at/gpl/linux/net/tulip.c782
-rw-r--r--i386/i386at/gpl/linux/net/wavelan.c2526
-rw-r--r--i386/i386at/gpl/linux/net/wavelan.h252
-rw-r--r--i386/i386at/gpl/linux/net/wd.c513
-rw-r--r--i386/i386at/gpl/linux/net/znet.c746
53 files changed, 41314 insertions, 0 deletions
diff --git a/i386/i386at/gpl/linux/net/3c501.c b/i386/i386at/gpl/linux/net/3c501.c
new file mode 100644
index 00000000..6f8dceb6
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/3c501.c
@@ -0,0 +1,860 @@
+/* 3c501.c: A 3Com 3c501 ethernet driver for linux. */
+/*
+ Written 1992,1993,1994 Donald Becker
+
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency. This software may be used and
+ distributed according to the terms of the GNU Public License,
+ incorporated herein by reference.
+
+ This is a device driver for the 3Com Etherlink 3c501.
+ Do not purchase this card, even as a joke. It's performance is horrible,
+ and it breaks in many ways.
+
+ The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
+ Center of Excellence in Space Data and Information Sciences
+ Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
+
+ Fixed (again!) the missing interrupt locking on TX/RX shifting.
+ Alan Cox <Alan.Cox@linux.org>
+
+ Removed calls to init_etherdev since they are no longer needed, and
+ cleaned up modularization just a bit. The driver still allows only
+ the default address for cards when loaded as a module, but that's
+ really less braindead than anyone using a 3c501 board. :)
+ 19950208 (invid@msen.com)
+
+ Added traps for interrupts hitting the window as we clear and TX load
+ the board. Now getting 150K/second FTP with a 3c501 card. Still playing
+ with a TX-TX optimisation to see if we can touch 180-200K/second as seems
+ theoretically maximum.
+ 19950402 Alan Cox <Alan.Cox@linux.org>
+
+ Some notes on this thing if you have to hack it. [Alan]
+
+ 1] Some documentation is available from 3Com. Due to the boards age
+ standard responses when you ask for this will range from 'be serious'
+ to 'give it to a museum'. The documentation is incomplete and mostly
+ of historical interest anyway.
+
+ 2] The basic system is a single buffer which can be used to receive or
+ transmit a packet. A third command mode exists when you are setting
+ things up.
+
+ 3] If it's transmitting it's not receiving and vice versa. In fact the
+ time to get the board back into useful state after an operation is
+ quite large.
+
+ 4] The driver works by keeping the board in receive mode waiting for a
+ packet to arrive. When one arrives it is copied out of the buffer
+ and delivered to the kernel. The card is reloaded and off we go.
+
+ 5] When transmitting dev->tbusy is set and the card is reset (from
+ receive mode) [possibly losing a packet just received] to command
+ mode. A packet is loaded and transmit mode triggered. The interrupt
+ handler runs different code for transmit interrupts and can handle
+ returning to receive mode or retransmissions (yes you have to help
+ out with those too).
+
+ Problems:
+ There are a wide variety of undocumented error returns from the card
+ and you basically have to kick the board and pray if they turn up. Most
+ only occur under extreme load or if you do something the board doesn't
+ like (eg touching a register at the wrong time).
+
+ The driver is less efficient than it could be. It switches through
+ receive mode even if more transmits are queued. If this worries you buy
+ a real ethernet card.
+
+ The combination of slow receive restart and no real multicast
+ filter makes the board unusable with a kernel compiled for IP
+ multicasting in a real multicast environment. Thats down to the board,
+ but even with no multicast programs running a multicast IP kernel is
+ in group 224.0.0.1 and you will therefore be listening to all multicasts.
+ One nv conference running over that ethernet and you can give up.
+
+*/
+
+static const char *version =
+ "3c501.c: 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov).\n";
+
+/*
+ * Braindamage remaining:
+ * The 3c501 board.
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/fcntl.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/errno.h>
+#include <linux/config.h> /* for CONFIG_IP_MULTICAST */
+
+#include <asm/bitops.h>
+#include <asm/io.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#define BLOCKOUT_2
+
+/* A zero-terminated list of I/O addresses to be probed.
+ The 3c501 can be at many locations, but here are the popular ones. */
+static unsigned int netcard_portlist[] =
+ { 0x280, 0x300, 0};
+
+
+/*
+ * Index to functions.
+ */
+
+int el1_probe(struct device *dev);
+static int el1_probe1(struct device *dev, int ioaddr);
+static int el_open(struct device *dev);
+static int el_start_xmit(struct sk_buff *skb, struct device *dev);
+static void el_interrupt(int irq, struct pt_regs *regs);
+static void el_receive(struct device *dev);
+static void el_reset(struct device *dev);
+static int el1_close(struct device *dev);
+static struct enet_statistics *el1_get_stats(struct device *dev);
+static void set_multicast_list(struct device *dev);
+
+#define EL1_IO_EXTENT 16
+
+#ifndef EL_DEBUG
+#define EL_DEBUG 0 /* use 0 for production, 1 for devel., >2 for debug */
+#endif /* Anything above 5 is wordy death! */
+static int el_debug = EL_DEBUG;
+
+/*
+ * Board-specific info in dev->priv.
+ */
+
+struct net_local
+{
+ struct enet_statistics stats;
+ int tx_pkt_start; /* The length of the current Tx packet. */
+ int collisions; /* Tx collisions this packet */
+ int loading; /* Spot buffer load collisions */
+};
+
+
+#define RX_STATUS (ioaddr + 0x06)
+#define RX_CMD RX_STATUS
+#define TX_STATUS (ioaddr + 0x07)
+#define TX_CMD TX_STATUS
+#define GP_LOW (ioaddr + 0x08)
+#define GP_HIGH (ioaddr + 0x09)
+#define RX_BUF_CLR (ioaddr + 0x0A)
+#define RX_LOW (ioaddr + 0x0A)
+#define RX_HIGH (ioaddr + 0x0B)
+#define SAPROM (ioaddr + 0x0C)
+#define AX_STATUS (ioaddr + 0x0E)
+#define AX_CMD AX_STATUS
+#define DATAPORT (ioaddr + 0x0F)
+#define TX_RDY 0x08 /* In TX_STATUS */
+
+#define EL1_DATAPTR 0x08
+#define EL1_RXPTR 0x0A
+#define EL1_SAPROM 0x0C
+#define EL1_DATAPORT 0x0f
+
+/*
+ * Writes to the ax command register.
+ */
+
+#define AX_OFF 0x00 /* Irq off, buffer access on */
+#define AX_SYS 0x40 /* Load the buffer */
+#define AX_XMIT 0x44 /* Transmit a packet */
+#define AX_RX 0x48 /* Receive a packet */
+#define AX_LOOP 0x0C /* Loopback mode */
+#define AX_RESET 0x80
+
+/*
+ * Normal receive mode written to RX_STATUS. We must intr on short packets
+ * to avoid bogus rx lockups.
+ */
+
+#define RX_NORM 0xA8 /* 0x68 == all addrs, 0xA8 only to me. */
+#define RX_PROM 0x68 /* Senior Prom, uhmm promiscuous mode. */
+#define RX_MULT 0xE8 /* Accept multicast packets. */
+#define TX_NORM 0x0A /* Interrupt on everything that might hang the chip */
+
+/*
+ * TX_STATUS register.
+ */
+
+#define TX_COLLISION 0x02
+#define TX_16COLLISIONS 0x04
+#define TX_READY 0x08
+
+#define RX_RUNT 0x08
+#define RX_MISSED 0x01 /* Missed a packet due to 3c501 braindamage. */
+#define RX_GOOD 0x30 /* Good packet 0x20, or simple overflow 0x10. */
+
+
+/*
+ * The boilerplate probe code.
+ */
+
+#ifdef HAVE_DEVLIST
+struct netdev_entry el1_drv = {"3c501", el1_probe1, EL1_IO_EXTENT, netcard_portlist};
+#else
+
+int el1_probe(struct device *dev)
+{
+ int i;
+ int base_addr = dev ? dev->base_addr : 0;
+
+ if (base_addr > 0x1ff) /* Check a single specified location. */
+ return el1_probe1(dev, base_addr);
+ else if (base_addr != 0) /* Don't probe at all. */
+ return ENXIO;
+
+ for (i = 0; netcard_portlist[i]; i++)
+ {
+ int ioaddr = netcard_portlist[i];
+ if (check_region(ioaddr, EL1_IO_EXTENT))
+ continue;
+ if (el1_probe1(dev, ioaddr) == 0)
+ return 0;
+ }
+
+ return ENODEV;
+}
+#endif
+
+/*
+ * The actual probe.
+ */
+
+static int el1_probe1(struct device *dev, int ioaddr)
+{
+#ifndef MODULE
+
+ const char *mname; /* Vendor name */
+ unsigned char station_addr[6];
+ int autoirq = 0;
+ int i;
+
+ /*
+ * Read the station address PROM data from the special port.
+ */
+
+ for (i = 0; i < 6; i++)
+ {
+ outw(i, ioaddr + EL1_DATAPTR);
+ station_addr[i] = inb(ioaddr + EL1_SAPROM);
+ }
+ /*
+ * Check the first three octets of the S.A. for 3Com's prefix, or
+ * for the Sager NP943 prefix.
+ */
+
+ if (station_addr[0] == 0x02 && station_addr[1] == 0x60
+ && station_addr[2] == 0x8c)
+ {
+ mname = "3c501";
+ } else if (station_addr[0] == 0x00 && station_addr[1] == 0x80
+ && station_addr[2] == 0xC8)
+ {
+ mname = "NP943";
+ }
+ else
+ return ENODEV;
+
+ /*
+ * Grab the region so we can find the another board if autoIRQ fails.
+ */
+
+ request_region(ioaddr, EL1_IO_EXTENT,"3c501");
+
+ /*
+ * We auto-IRQ by shutting off the interrupt line and letting it float
+ * high.
+ */
+
+ if (dev->irq < 2)
+ {
+ autoirq_setup(2);
+ inb(RX_STATUS); /* Clear pending interrupts. */
+ inb(TX_STATUS);
+ outb(AX_LOOP + 1, AX_CMD);
+
+ outb(0x00, AX_CMD);
+
+ autoirq = autoirq_report(1);
+
+ if (autoirq == 0)
+ {
+ printk("%s probe at %#x failed to detect IRQ line.\n",
+ mname, ioaddr);
+ return EAGAIN;
+ }
+ }
+
+ outb(AX_RESET+AX_LOOP, AX_CMD); /* Loopback mode. */
+ dev->base_addr = ioaddr;
+ memcpy(dev->dev_addr, station_addr, ETH_ALEN);
+
+ if (dev->mem_start & 0xf)
+ el_debug = dev->mem_start & 0x7;
+ if (autoirq)
+ dev->irq = autoirq;
+
+ printk("%s: %s EtherLink at %#lx, using %sIRQ %d.\n", dev->name, mname, dev->base_addr,
+ autoirq ? "auto":"assigned ", dev->irq);
+
+#ifdef CONFIG_IP_MULTICAST
+ printk("WARNING: Use of the 3c501 in a multicast kernel is NOT recommended.\n");
+#endif
+
+ if (el_debug)
+ printk("%s", version);
+
+ /*
+ * Initialize the device structure.
+ */
+
+ dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
+ if (dev->priv == NULL)
+ return -ENOMEM;
+ memset(dev->priv, 0, sizeof(struct net_local));
+
+ /*
+ * The EL1-specific entries in the device structure.
+ */
+
+ dev->open = &el_open;
+ dev->hard_start_xmit = &el_start_xmit;
+ dev->stop = &el1_close;
+ dev->get_stats = &el1_get_stats;
+ dev->set_multicast_list = &set_multicast_list;
+
+ /*
+ * Setup the generic properties
+ */
+
+ ether_setup(dev);
+
+#endif /* !MODULE */
+
+ return 0;
+}
+
+/*
+ * Open/initialize the board.
+ */
+
+static int el_open(struct device *dev)
+{
+ int ioaddr = dev->base_addr;
+
+ if (el_debug > 2)
+ printk("%s: Doing el_open()...", dev->name);
+
+ if (request_irq(dev->irq, &el_interrupt, 0, "3c501"))
+ return -EAGAIN;
+
+ irq2dev_map[dev->irq] = dev;
+ el_reset(dev);
+
+ dev->start = 1;
+
+ outb(AX_RX, AX_CMD); /* Aux control, irq and receive enabled */
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int el_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+ unsigned long flags;
+
+ if(dev->interrupt) /* May be unloading, don't stamp on */
+ return 1; /* the packet buffer this time */
+
+ if (dev->tbusy)
+ {
+ if (jiffies - dev->trans_start < 20)
+ {
+ if (el_debug > 2)
+ printk(" transmitter busy, deferred.\n");
+ return 1;
+ }
+ if (el_debug)
+ printk ("%s: transmit timed out, txsr %#2x axsr=%02x rxsr=%02x.\n",
+ dev->name, inb(TX_STATUS), inb(AX_STATUS), inb(RX_STATUS));
+ lp->stats.tx_errors++;
+ outb(TX_NORM, TX_CMD);
+ outb(RX_NORM, RX_CMD);
+ outb(AX_OFF, AX_CMD); /* Just trigger a false interrupt. */
+ outb(AX_RX, AX_CMD); /* Aux control, irq and receive enabled */
+ dev->tbusy = 0;
+ dev->trans_start = jiffies;
+ }
+
+ if (skb == NULL)
+ {
+ dev_tint(dev);
+ return 0;
+ }
+
+ save_flags(flags);
+
+ /*
+ * Avoid incoming interrupts between us flipping tbusy and flipping
+ * mode as the driver assumes tbusy is a faithful indicator of card
+ * state
+ */
+
+ cli();
+
+ /*
+ * Avoid timer-based retransmission conflicts.
+ */
+
+ if (set_bit(0, (void*)&dev->tbusy) != 0)
+ {
+ restore_flags(flags);
+ printk("%s: Transmitter access conflict.\n", dev->name);
+ }
+ else
+ {
+ int gp_start = 0x800 - (ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN);
+ unsigned char *buf = skb->data;
+
+load_it_again_sam:
+ lp->tx_pkt_start = gp_start;
+ lp->collisions = 0;
+
+ /*
+ * Command mode with status cleared should [in theory]
+ * mean no more interrupts can be pending on the card.
+ */
+
+#ifdef BLOCKOUT_1
+ disable_irq(dev->irq);
+#endif
+ outb_p(AX_SYS, AX_CMD);
+ inb_p(RX_STATUS);
+ inb_p(TX_STATUS);
+
+ lp->loading=1;
+
+ /*
+ * Turn interrupts back on while we spend a pleasant afternoon
+ * loading bytes into the board
+ */
+
+ restore_flags(flags);
+ outw(0x00, RX_BUF_CLR); /* Set rx packet area to 0. */
+ outw(gp_start, GP_LOW); /* aim - packet will be loaded into buffer start */
+ outsb(DATAPORT,buf,skb->len); /* load buffer (usual thing each byte increments the pointer) */
+ outw(gp_start, GP_LOW); /* the board reuses the same register */
+#ifndef BLOCKOUT_1
+ if(lp->loading==2) /* A receive upset our load, despite our best efforts */
+ {
+ if(el_debug>2)
+ printk("%s: burped during tx load.\n", dev->name);
+ goto load_it_again_sam; /* Sigh... */
+ }
+#endif
+ outb(AX_XMIT, AX_CMD); /* fire ... Trigger xmit. */
+ lp->loading=0;
+#ifdef BLOCKOUT_1
+ enable_irq(dev->irq);
+#endif
+ dev->trans_start = jiffies;
+ }
+
+ if (el_debug > 2)
+ printk(" queued xmit.\n");
+ dev_kfree_skb (skb, FREE_WRITE);
+ return 0;
+}
+
+
+/*
+ * The typical workload of the driver:
+ * Handle the ether interface interrupts.
+ */
+
+static void el_interrupt(int irq, struct pt_regs *regs)
+{
+ struct device *dev = (struct device *)(irq2dev_map[irq]);
+ struct net_local *lp;
+ int ioaddr;
+ int axsr; /* Aux. status reg. */
+
+ if (dev == NULL || dev->irq != irq)
+ {
+ printk ("3c501 driver: irq %d for unknown device.\n", irq);
+ return;
+ }
+
+ ioaddr = dev->base_addr;
+ lp = (struct net_local *)dev->priv;
+
+ /*
+ * What happened ?
+ */
+
+ axsr = inb(AX_STATUS);
+
+ /*
+ * Log it
+ */
+
+ if (el_debug > 3)
+ printk("%s: el_interrupt() aux=%#02x", dev->name, axsr);
+ if (dev->interrupt)
+ printk("%s: Reentering the interrupt driver!\n", dev->name);
+ dev->interrupt = 1;
+#ifndef BLOCKOUT_1
+ if(lp->loading==1 && !dev->tbusy)
+ printk("%s: Inconsistent state loading while not in tx\n",
+ dev->name);
+#endif
+#ifdef BLOCKOUT_3
+ lp->loading=2; /* So we can spot loading interruptions */
+#endif
+
+ if (dev->tbusy)
+ {
+
+ /*
+ * Board in transmit mode. May be loading. If we are
+ * loading we shouldn't have got this.
+ */
+
+ int txsr = inb(TX_STATUS);
+#ifdef BLOCKOUT_2
+ if(lp->loading==1)
+ {
+ if(el_debug > 2)
+ {
+ printk("%s: Interrupt while loading [", dev->name);
+ printk(" txsr=%02x gp=%04x rp=%04x]\n", txsr, inw(GP_LOW),inw(RX_LOW));
+ }
+ lp->loading=2; /* Force a reload */
+ dev->interrupt = 0;
+ return;
+ }
+#endif
+ if (el_debug > 6)
+ printk(" txsr=%02x gp=%04x rp=%04x", txsr, inw(GP_LOW),inw(RX_LOW));
+
+ if ((axsr & 0x80) && (txsr & TX_READY) == 0)
+ {
+ /*
+ * FIXME: is there a logic to whether to keep on trying or
+ * reset immediately ?
+ */
+ printk("%s: Unusual interrupt during Tx, txsr=%02x axsr=%02x"
+ " gp=%03x rp=%03x.\n", dev->name, txsr, axsr,
+ inw(ioaddr + EL1_DATAPTR), inw(ioaddr + EL1_RXPTR));
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+ }
+ else if (txsr & TX_16COLLISIONS)
+ {
+ /*
+ * Timed out
+ */
+ if (el_debug)
+ printk("%s: Transmit failed 16 times, ethernet jammed?\n",dev->name);
+ outb(AX_SYS, AX_CMD);
+ lp->stats.tx_aborted_errors++;
+ }
+ else if (txsr & TX_COLLISION)
+ {
+ /*
+ * Retrigger xmit.
+ */
+
+ if (el_debug > 6)
+ printk(" retransmitting after a collision.\n");
+ /*
+ * Poor little chip can't reset its own start pointer
+ */
+
+ outb(AX_SYS, AX_CMD);
+ outw(lp->tx_pkt_start, GP_LOW);
+ outb(AX_XMIT, AX_CMD);
+ lp->stats.collisions++;
+ dev->interrupt = 0;
+ return;
+ }
+ else
+ {
+ /*
+ * It worked.. we will now fall through and receive
+ */
+ lp->stats.tx_packets++;
+ if (el_debug > 6)
+ printk(" Tx succeeded %s\n",
+ (txsr & TX_RDY) ? "." : "but tx is busy!");
+ /*
+ * This is safe the interrupt is atomic WRT itself.
+ */
+
+ dev->tbusy = 0;
+ mark_bh(NET_BH); /* In case more to transmit */
+ }
+ }
+ else
+ {
+ /*
+ * In receive mode.
+ */
+
+ int rxsr = inb(RX_STATUS);
+ if (el_debug > 5)
+ printk(" rxsr=%02x txsr=%02x rp=%04x", rxsr, inb(TX_STATUS),inw(RX_LOW));
+ /*
+ * Just reading rx_status fixes most errors.
+ */
+ if (rxsr & RX_MISSED)
+ lp->stats.rx_missed_errors++;
+ else if (rxsr & RX_RUNT)
+ { /* Handled to avoid board lock-up. */
+ lp->stats.rx_length_errors++;
+ if (el_debug > 5)
+ printk(" runt.\n");
+ }
+ else if (rxsr & RX_GOOD)
+ {
+ /*
+ * Receive worked.
+ */
+ el_receive(dev);
+ }
+ else
+ {
+ /*
+ * Nothing? Something is broken!
+ */
+ if (el_debug > 2)
+ printk("%s: No packet seen, rxsr=%02x **resetting 3c501***\n",
+ dev->name, rxsr);
+ el_reset(dev);
+ }
+ if (el_debug > 3)
+ printk(".\n");
+ }
+
+ /*
+ * Move into receive mode
+ */
+
+ outb(AX_RX, AX_CMD);
+ outw(0x00, RX_BUF_CLR);
+ inb(RX_STATUS); /* Be certain that interrupts are cleared. */
+ inb(TX_STATUS);
+ dev->interrupt = 0;
+ return;
+}
+
+
+/*
+ * We have a good packet. Well, not really "good", just mostly not broken.
+ * We must check everything to see if it is good.
+ */
+
+static void el_receive(struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+ int pkt_len;
+ struct sk_buff *skb;
+
+ pkt_len = inw(RX_LOW);
+
+ if (el_debug > 4)
+ printk(" el_receive %d.\n", pkt_len);
+
+ if ((pkt_len < 60) || (pkt_len > 1536))
+ {
+ if (el_debug)
+ printk("%s: bogus packet, length=%d\n", dev->name, pkt_len);
+ lp->stats.rx_over_errors++;
+ return;
+ }
+
+ /*
+ * Command mode so we can empty the buffer
+ */
+
+ outb(AX_SYS, AX_CMD);
+ skb = dev_alloc_skb(pkt_len+2);
+
+ /*
+ * Start of frame
+ */
+
+ outw(0x00, GP_LOW);
+ if (skb == NULL)
+ {
+ printk("%s: Memory squeeze, dropping packet.\n", dev->name);
+ lp->stats.rx_dropped++;
+ return;
+ }
+ else
+ {
+ skb_reserve(skb,2); /* Force 16 byte alignment */
+ skb->dev = dev;
+ /*
+ * The read increments through the bytes. The interrupt
+ * handler will fix the pointer when it returns to
+ * receive mode.
+ */
+ insb(DATAPORT, skb_put(skb,pkt_len), pkt_len);
+ skb->protocol=eth_type_trans(skb,dev);
+ netif_rx(skb);
+ lp->stats.rx_packets++;
+ }
+ return;
+}
+
+static void el_reset(struct device *dev)
+{
+ int ioaddr = dev->base_addr;
+
+ if (el_debug> 2)
+ printk("3c501 reset...");
+ outb(AX_RESET, AX_CMD); /* Reset the chip */
+ outb(AX_LOOP, AX_CMD); /* Aux control, irq and loopback enabled */
+ {
+ int i;
+ for (i = 0; i < 6; i++) /* Set the station address. */
+ outb(dev->dev_addr[i], ioaddr + i);
+ }
+
+ outw(0, RX_BUF_CLR); /* Set rx packet area to 0. */
+ cli(); /* Avoid glitch on writes to CMD regs */
+ outb(TX_NORM, TX_CMD); /* tx irq on done, collision */
+ outb(RX_NORM, RX_CMD); /* Set Rx commands. */
+ inb(RX_STATUS); /* Clear status. */
+ inb(TX_STATUS);
+ dev->interrupt = 0;
+ dev->tbusy = 0;
+ sti();
+}
+
+static int el1_close(struct device *dev)
+{
+ int ioaddr = dev->base_addr;
+
+ if (el_debug > 2)
+ printk("%s: Shutting down ethercard at %#x.\n", dev->name, ioaddr);
+
+ dev->tbusy = 1;
+ dev->start = 0;
+
+ /*
+ * Free and disable the IRQ.
+ */
+
+ free_irq(dev->irq);
+ outb(AX_RESET, AX_CMD); /* Reset the chip */
+ irq2dev_map[dev->irq] = 0;
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static struct enet_statistics *el1_get_stats(struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ return &lp->stats;
+}
+
+/*
+ * Set or clear the multicast filter for this adaptor.
+ * best-effort filtering.
+ */
+
+static void set_multicast_list(struct device *dev)
+{
+ int ioaddr = dev->base_addr;
+
+ if(dev->flags&IFF_PROMISC)
+ {
+ outb(RX_PROM, RX_CMD);
+ inb(RX_STATUS);
+ }
+ else if (dev->mc_list || dev->flags&IFF_ALLMULTI)
+ {
+ outb(RX_MULT, RX_CMD); /* Multicast or all multicast is the same */
+ inb(RX_STATUS); /* Clear status. */
+ }
+ else
+ {
+ outb(RX_NORM, RX_CMD);
+ inb(RX_STATUS);
+ }
+}
+
+#ifdef MODULE
+
+static char devicename[9] = { 0, };
+
+static struct device dev_3c501 =
+{
+ devicename, /* device name is inserted by linux/drivers/net/net_init.c */
+ 0, 0, 0, 0,
+ 0x280, 5,
+ 0, 0, 0, NULL, el1_probe
+};
+
+static int io=0x280;
+static int irq=5;
+
+int init_module(void)
+{
+ dev_3c501.irq=irq;
+ dev_3c501.base_addr=io;
+ if (register_netdev(&dev_3c501) != 0)
+ return -EIO;
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ /*
+ * No need to check MOD_IN_USE, as sys_delete_module() checks.
+ */
+
+ unregister_netdev(&dev_3c501);
+
+ /*
+ * Free up the private structure, or leak memory :-)
+ */
+
+ kfree(dev_3c501.priv);
+ dev_3c501.priv = NULL; /* gets re-allocated by el1_probe1 */
+
+ /*
+ * If we don't do this, we can't re-insmod it later.
+ */
+ release_region(dev_3c501.base_addr, EL1_IO_EXTENT);
+}
+
+#endif /* MODULE */
+
+/*
+ * Local variables:
+ * compile-command: "gcc -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer -m486 -c -o 3c501.o 3c501.c"
+ * kept-new-versions: 5
+ * End:
+ */
diff --git a/i386/i386at/gpl/linux/net/3c503.c b/i386/i386at/gpl/linux/net/3c503.c
new file mode 100644
index 00000000..9a0c0b9e
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/3c503.c
@@ -0,0 +1,627 @@
+/* 3c503.c: A shared-memory NS8390 ethernet driver for linux. */
+/*
+ Written 1992-94 by Donald Becker.
+
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency. This software may be used and
+ distributed according to the terms of the GNU Public License,
+ incorporated herein by reference.
+
+ The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
+ Center of Excellence in Space Data and Information Sciences
+ Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
+
+ This driver should work with the 3c503 and 3c503/16. It should be used
+ in shared memory mode for best performance, although it may also work
+ in programmed-I/O mode.
+
+ Sources:
+ EtherLink II Technical Reference Manual,
+ EtherLink II/16 Technical Reference Manual Supplement,
+ 3Com Corporation, 5400 Bayfront Plaza, Santa Clara CA 95052-8145
+
+ The Crynwr 3c503 packet driver.
+
+ Changelog:
+
+ Paul Gortmaker : add support for the 2nd 8kB of RAM on 16 bit cards.
+ Paul Gortmaker : multiple card support for module users.
+
+*/
+
+static const char *version =
+ "3c503.c:v1.10 9/23/93 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include "8390.h"
+#include "3c503.h"
+
+
+int el2_probe(struct device *dev);
+int el2_pio_probe(struct device *dev);
+int el2_probe1(struct device *dev, int ioaddr);
+
+/* A zero-terminated list of I/O addresses to be probed in PIO mode. */
+static unsigned int netcard_portlist[] =
+ { 0x300,0x310,0x330,0x350,0x250,0x280,0x2a0,0x2e0,0};
+
+#define EL2_IO_EXTENT 16
+
+#ifdef HAVE_DEVLIST
+/* The 3c503 uses two entries, one for the safe memory-mapped probe and
+ the other for the typical I/O probe. */
+struct netdev_entry el2_drv =
+{"3c503", el2_probe, EL1_IO_EXTENT, 0};
+struct netdev_entry el2pio_drv =
+{"3c503pio", el2_pioprobe1, EL1_IO_EXTENT, netcard_portlist};
+#endif
+
+static int el2_open(struct device *dev);
+static int el2_close(struct device *dev);
+static void el2_reset_8390(struct device *dev);
+static void el2_init_card(struct device *dev);
+static void el2_block_output(struct device *dev, int count,
+ const unsigned char *buf, const start_page);
+static void el2_block_input(struct device *dev, int count, struct sk_buff *skb,
+ int ring_offset);
+static void el2_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
+ int ring_page);
+
+
+/* This routine probes for a memory-mapped 3c503 board by looking for
+ the "location register" at the end of the jumpered boot PROM space.
+ This works even if a PROM isn't there.
+
+ If the ethercard isn't found there is an optional probe for
+ ethercard jumpered to programmed-I/O mode.
+ */
+int
+el2_probe(struct device *dev)
+{
+ int *addr, addrs[] = { 0xddffe, 0xd9ffe, 0xcdffe, 0xc9ffe, 0};
+ int base_addr = dev->base_addr;
+
+ if (base_addr > 0x1ff) /* Check a single specified location. */
+ return el2_probe1(dev, base_addr);
+ else if (base_addr != 0) /* Don't probe at all. */
+ return ENXIO;
+
+ for (addr = addrs; *addr; addr++) {
+ int i;
+ unsigned int base_bits = readb(*addr);
+ /* Find first set bit. */
+ for(i = 7; i >= 0; i--, base_bits >>= 1)
+ if (base_bits & 0x1)
+ break;
+ if (base_bits != 1)
+ continue;
+ if (check_region(netcard_portlist[i], EL2_IO_EXTENT))
+ continue;
+ if (el2_probe1(dev, netcard_portlist[i]) == 0)
+ return 0;
+ }
+#if ! defined(no_probe_nonshared_memory) && ! defined (HAVE_DEVLIST)
+ return el2_pio_probe(dev);
+#else
+ return ENODEV;
+#endif
+}
+
+#ifndef HAVE_DEVLIST
+/* Try all of the locations that aren't obviously empty. This touches
+ a lot of locations, and is much riskier than the code above. */
+int
+el2_pio_probe(struct device *dev)
+{
+ int i;
+ int base_addr = dev ? dev->base_addr : 0;
+
+ if (base_addr > 0x1ff) /* Check a single specified location. */
+ return el2_probe1(dev, base_addr);
+ else if (base_addr != 0) /* Don't probe at all. */
+ return ENXIO;
+
+ for (i = 0; netcard_portlist[i]; i++) {
+ int ioaddr = netcard_portlist[i];
+ if (check_region(ioaddr, EL2_IO_EXTENT))
+ continue;
+ if (el2_probe1(dev, ioaddr) == 0)
+ return 0;
+ }
+
+ return ENODEV;
+}
+#endif
+
+/* Probe for the Etherlink II card at I/O port base IOADDR,
+ returning non-zero on success. If found, set the station
+ address and memory parameters in DEVICE. */
+int
+el2_probe1(struct device *dev, int ioaddr)
+{
+ int i, iobase_reg, membase_reg, saved_406, wordlength;
+ static unsigned version_printed = 0;
+ unsigned long vendor_id;
+
+ /* Reset and/or avoid any lurking NE2000 */
+ if (inb(ioaddr + 0x408) == 0xff) {
+ udelay(1000);
+ return ENODEV;
+ }
+
+ /* We verify that it's a 3C503 board by checking the first three octets
+ of its ethernet address. */
+ iobase_reg = inb(ioaddr+0x403);
+ membase_reg = inb(ioaddr+0x404);
+ /* ASIC location registers should be 0 or have only a single bit set. */
+ if ( (iobase_reg & (iobase_reg - 1))
+ || (membase_reg & (membase_reg - 1))) {
+ return ENODEV;
+ }
+ saved_406 = inb_p(ioaddr + 0x406);
+ outb_p(ECNTRL_RESET|ECNTRL_THIN, ioaddr + 0x406); /* Reset it... */
+ outb_p(ECNTRL_THIN, ioaddr + 0x406);
+ /* Map the station addr PROM into the lower I/O ports. We now check
+ for both the old and new 3Com prefix */
+ outb(ECNTRL_SAPROM|ECNTRL_THIN, ioaddr + 0x406);
+ vendor_id = inb(ioaddr)*0x10000 + inb(ioaddr + 1)*0x100 + inb(ioaddr + 2);
+ if ((vendor_id != OLD_3COM_ID) && (vendor_id != NEW_3COM_ID)) {
+ /* Restore the register we frobbed. */
+ outb(saved_406, ioaddr + 0x406);
+ return ENODEV;
+ }
+
+ /* We should have a "dev" from Space.c or the static module table. */
+ if (dev == NULL) {
+ printk("3c503.c: Passed a NULL device.\n");
+ dev = init_etherdev(0, 0);
+ }
+
+ if (ei_debug && version_printed++ == 0)
+ printk(version);
+
+ dev->base_addr = ioaddr;
+
+ /* Allocate dev->priv and fill in 8390 specific dev fields. */
+ if (ethdev_init(dev)) {
+ printk ("3c503: unable to allocate memory for dev->priv.\n");
+ return -ENOMEM;
+ }
+
+ printk("%s: 3c503 at i/o base %#3x, node ", dev->name, ioaddr);
+
+ /* Retrieve and print the ethernet address. */
+ for (i = 0; i < 6; i++)
+ printk(" %2.2x", dev->dev_addr[i] = inb(ioaddr + i));
+
+ /* Map the 8390 back into the window. */
+ outb(ECNTRL_THIN, ioaddr + 0x406);
+
+ /* Check for EL2/16 as described in tech. man. */
+ outb_p(E8390_PAGE0, ioaddr + E8390_CMD);
+ outb_p(0, ioaddr + EN0_DCFG);
+ outb_p(E8390_PAGE2, ioaddr + E8390_CMD);
+ wordlength = inb_p(ioaddr + EN0_DCFG) & ENDCFG_WTS;
+ outb_p(E8390_PAGE0, ioaddr + E8390_CMD);
+
+ /* Probe for, turn on and clear the board's shared memory. */
+ if (ei_debug > 2) printk(" memory jumpers %2.2x ", membase_reg);
+ outb(EGACFR_NORM, ioaddr + 0x405); /* Enable RAM */
+
+ /* This should be probed for (or set via an ioctl()) at run-time.
+ Right now we use a sleazy hack to pass in the interface number
+ at boot-time via the low bits of the mem_end field. That value is
+ unused, and the low bits would be discarded even if it was used. */
+#if defined(EI8390_THICK) || defined(EL2_AUI)
+ ei_status.interface_num = 1;
+#else
+ ei_status.interface_num = dev->mem_end & 0xf;
+#endif
+ printk(", using %sternal xcvr.\n", ei_status.interface_num == 0 ? "in" : "ex");
+
+ if ((membase_reg & 0xf0) == 0) {
+ dev->mem_start = 0;
+ ei_status.name = "3c503-PIO";
+ } else {
+ dev->mem_start = ((membase_reg & 0xc0) ? 0xD8000 : 0xC8000) +
+ ((membase_reg & 0xA0) ? 0x4000 : 0);
+
+#define EL2_MEMSIZE (EL2_MB1_STOP_PG - EL2_MB1_START_PG)*256
+#ifdef EL2MEMTEST
+ /* This has never found an error, but someone might care.
+ Note that it only tests the 2nd 8kB on 16kB 3c503/16
+ cards between card addr. 0x2000 and 0x3fff. */
+ { /* Check the card's memory. */
+ unsigned long mem_base = dev->mem_start;
+ unsigned int test_val = 0xbbadf00d;
+ writel(0xba5eba5e, mem_base);
+ for (i = sizeof(test_val); i < EL2_MEMSIZE; i+=sizeof(test_val)) {
+ writel(test_val, mem_base + i);
+ if (readl(mem_base) != 0xba5eba5e
+ || readl(mem_base + i) != test_val) {
+ printk("3c503.c: memory failure or memory address conflict.\n");
+ dev->mem_start = 0;
+ ei_status.name = "3c503-PIO";
+ break;
+ }
+ test_val += 0x55555555;
+ writel(0, mem_base + i);
+ }
+ }
+#endif /* EL2MEMTEST */
+
+ dev->mem_end = dev->rmem_end = dev->mem_start + EL2_MEMSIZE;
+
+ if (wordlength) { /* No Tx pages to skip over to get to Rx */
+ dev->rmem_start = dev->mem_start;
+ ei_status.name = "3c503/16";
+ } else {
+ dev->rmem_start = TX_PAGES*256 + dev->mem_start;
+ ei_status.name = "3c503";
+ }
+ }
+
+ /*
+ Divide up the memory on the card. This is the same regardless of
+ whether shared-mem or PIO is used. For 16 bit cards (16kB RAM),
+ we use the entire 8k of bank1 for an Rx ring. We only use 3k
+ of the bank0 for 2 full size Tx packet slots. For 8 bit cards,
+ (8kB RAM) we use 3kB of bank1 for two Tx slots, and the remaining
+ 5kB for an Rx ring. */
+
+ if (wordlength) {
+ ei_status.tx_start_page = EL2_MB0_START_PG;
+ ei_status.rx_start_page = EL2_MB1_START_PG;
+ } else {
+ ei_status.tx_start_page = EL2_MB1_START_PG;
+ ei_status.rx_start_page = EL2_MB1_START_PG + TX_PAGES;
+ }
+
+ /* Finish setting the board's parameters. */
+ ei_status.stop_page = EL2_MB1_STOP_PG;
+ ei_status.word16 = wordlength;
+ ei_status.reset_8390 = &el2_reset_8390;
+ ei_status.get_8390_hdr = &el2_get_8390_hdr;
+ ei_status.block_input = &el2_block_input;
+ ei_status.block_output = &el2_block_output;
+
+ request_region(ioaddr, EL2_IO_EXTENT, ei_status.name);
+
+ if (dev->irq == 2)
+ dev->irq = 9;
+ else if (dev->irq > 5 && dev->irq != 9) {
+ printk("3c503: configured interrupt %d invalid, will use autoIRQ.\n",
+ dev->irq);
+ dev->irq = 0;
+ }
+
+ ei_status.saved_irq = dev->irq;
+
+ dev->start = 0;
+ dev->open = &el2_open;
+ dev->stop = &el2_close;
+
+ if (dev->mem_start)
+ printk("%s: %s - %dkB RAM, 8kB shared mem window at %#6lx-%#6lx.\n",
+ dev->name, ei_status.name, (wordlength+1)<<3,
+ dev->mem_start, dev->mem_end-1);
+
+ else
+ printk("\n%s: %s, %dkB RAM, using programmed I/O (REJUMPER for SHARED MEMORY).\n",
+ dev->name, ei_status.name, (wordlength+1)<<3);
+
+ return 0;
+}
+
+static int
+el2_open(struct device *dev)
+{
+
+ if (dev->irq < 2) {
+ int irqlist[] = {5, 9, 3, 4, 0};
+ int *irqp = irqlist;
+
+ outb(EGACFR_NORM, E33G_GACFR); /* Enable RAM and interrupts. */
+ do {
+ if (request_irq (*irqp, NULL, 0, "bogus") != -EBUSY) {
+ /* Twinkle the interrupt, and check if it's seen. */
+ autoirq_setup(0);
+ outb_p(0x04 << ((*irqp == 9) ? 2 : *irqp), E33G_IDCFR);
+ outb_p(0x00, E33G_IDCFR);
+ if (*irqp == autoirq_report(0) /* It's a good IRQ line! */
+ && request_irq (dev->irq = *irqp, &ei_interrupt, 0, ei_status.name) == 0)
+ break;
+ }
+ } while (*++irqp);
+ if (*irqp == 0) {
+ outb(EGACFR_IRQOFF, E33G_GACFR); /* disable interrupts. */
+ return -EAGAIN;
+ }
+ } else {
+ if (request_irq(dev->irq, &ei_interrupt, 0, ei_status.name)) {
+ return -EAGAIN;
+ }
+ }
+
+ el2_init_card(dev);
+ ei_open(dev);
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int
+el2_close(struct device *dev)
+{
+ free_irq(dev->irq);
+ dev->irq = ei_status.saved_irq;
+ irq2dev_map[dev->irq] = NULL;
+ outb(EGACFR_IRQOFF, E33G_GACFR); /* disable interrupts. */
+
+ ei_close(dev);
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+/* This is called whenever we have a unrecoverable failure:
+ transmit timeout
+ Bad ring buffer packet header
+ */
+static void
+el2_reset_8390(struct device *dev)
+{
+ if (ei_debug > 1) {
+ printk("%s: Resetting the 3c503 board...", dev->name);
+ printk("%#lx=%#02x %#lx=%#02x %#lx=%#02x...", E33G_IDCFR, inb(E33G_IDCFR),
+ E33G_CNTRL, inb(E33G_CNTRL), E33G_GACFR, inb(E33G_GACFR));
+ }
+ outb_p(ECNTRL_RESET|ECNTRL_THIN, E33G_CNTRL);
+ ei_status.txing = 0;
+ outb_p(ei_status.interface_num==0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL);
+ el2_init_card(dev);
+ if (ei_debug > 1) printk("done\n");
+}
+
+/* Initialize the 3c503 GA registers after a reset. */
+static void
+el2_init_card(struct device *dev)
+{
+ /* Unmap the station PROM and select the DIX or BNC connector. */
+ outb_p(ei_status.interface_num==0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL);
+
+ /* Set ASIC copy of rx's first and last+1 buffer pages */
+ /* These must be the same as in the 8390. */
+ outb(ei_status.rx_start_page, E33G_STARTPG);
+ outb(ei_status.stop_page, E33G_STOPPG);
+
+ /* Point the vector pointer registers somewhere ?harmless?. */
+ outb(0xff, E33G_VP2); /* Point at the ROM restart location 0xffff0 */
+ outb(0xff, E33G_VP1);
+ outb(0x00, E33G_VP0);
+ /* Turn off all interrupts until we're opened. */
+ outb_p(0x00, dev->base_addr + EN0_IMR);
+ /* Enable IRQs iff started. */
+ outb(EGACFR_NORM, E33G_GACFR);
+
+ /* Set the interrupt line. */
+ outb_p((0x04 << (dev->irq == 9 ? 2 : dev->irq)), E33G_IDCFR);
+ outb_p(8, E33G_DRQCNT); /* Set burst size to 8 */
+ outb_p(0x20, E33G_DMAAH); /* Put a valid addr in the GA DMA */
+ outb_p(0x00, E33G_DMAAL);
+ return; /* We always succeed */
+}
+
+/* Either use the shared memory (if enabled on the board) or put the packet
+ out through the ASIC FIFO. The latter is probably much slower. */
+static void
+el2_block_output(struct device *dev, int count,
+ const unsigned char *buf, const start_page)
+{
+ int i; /* Buffer index */
+ int boguscount = 0; /* timeout counter */
+
+ if (ei_status.word16) /* Tx packets go into bank 0 on EL2/16 card */
+ outb(EGACFR_RSEL|EGACFR_TCM, E33G_GACFR);
+ else
+ outb(EGACFR_NORM, E33G_GACFR);
+
+ if (dev->mem_start) { /* Shared memory transfer */
+ unsigned long dest_addr = dev->mem_start +
+ ((start_page - ei_status.tx_start_page) << 8);
+ memcpy_toio(dest_addr, buf, count);
+ outb(EGACFR_NORM, E33G_GACFR); /* Back to bank1 in case on bank0 */
+ return;
+ }
+ /* No shared memory, put the packet out the slow way. */
+ /* Set up then start the internal memory transfer to Tx Start Page */
+ outb(0x00, E33G_DMAAL);
+ outb_p(start_page, E33G_DMAAH);
+ outb_p((ei_status.interface_num ? ECNTRL_AUI : ECNTRL_THIN ) | ECNTRL_OUTPUT
+ | ECNTRL_START, E33G_CNTRL);
+
+ /* This is the byte copy loop: it should probably be tuned for
+ speed once everything is working. I think it is possible
+ to output 8 bytes between each check of the status bit. */
+ for(i = 0; i < count; i++) {
+ if (i % 8 == 0)
+ while ((inb(E33G_STATUS) & ESTAT_DPRDY) == 0)
+ if (++boguscount > (i<<3) + 32) {
+ printk("%s: FIFO blocked in el2_block_output (at %d of %d, bc=%d).\n",
+ dev->name, i, count, boguscount);
+ outb(EGACFR_NORM, E33G_GACFR); /* To MB1 for EL2/16 */
+ return;
+ }
+ outb(buf[i], E33G_FIFOH);
+ }
+ outb_p(ei_status.interface_num==0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL);
+ outb(EGACFR_NORM, E33G_GACFR); /* Back to bank1 in case on bank0 */
+ return;
+}
+
+/* Read the 4 byte, page aligned 8390 specific header. */
+static void
+el2_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+ unsigned int i;
+ unsigned long hdr_start = dev->mem_start + ((ring_page - EL2_MB1_START_PG)<<8);
+ unsigned long fifo_watchdog;
+
+ if (dev->mem_start) { /* Use the shared memory. */
+#ifdef notdef
+ /* Officially this is what we are doing, but the readl() is faster */
+ memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr));
+#else
+ ((unsigned int*)hdr)[0] = readl(hdr_start);
+#endif
+ return;
+ }
+
+ /* No shared memory, use programmed I/O. Ugh. */
+ outb(0, E33G_DMAAL);
+ outb_p(ring_page & 0xff, E33G_DMAAH);
+ outb_p((ei_status.interface_num == 0 ? ECNTRL_THIN : ECNTRL_AUI) | ECNTRL_INPUT
+ | ECNTRL_START, E33G_CNTRL);
+
+ /* Header is < 8 bytes, so only check the FIFO at the beginning. */
+ fifo_watchdog = jiffies;
+ while ((inb(E33G_STATUS) & ESTAT_DPRDY) == 0) {
+ if (jiffies - fifo_watchdog > 2*HZ/100) {
+ printk("%s: FIFO blocked in el2_get_8390_hdr.\n", dev->name);
+ break;
+ }
+ }
+
+ for(i = 0; i < sizeof(struct e8390_pkt_hdr); i++)
+ ((char *)(hdr))[i] = inb_p(E33G_FIFOH);
+
+ outb_p(ei_status.interface_num == 0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL);
+}
+
+/* Returns the new ring pointer. */
+static void
+el2_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset)
+{
+ int boguscount = 0;
+ int end_of_ring = dev->rmem_end;
+ unsigned int i;
+
+ /* Maybe enable shared memory just be to be safe... nahh.*/
+ if (dev->mem_start) { /* Use the shared memory. */
+ ring_offset -= (EL2_MB1_START_PG<<8);
+ if (dev->mem_start + ring_offset + count > end_of_ring) {
+ /* We must wrap the input move. */
+ int semi_count = end_of_ring - (dev->mem_start + ring_offset);
+ memcpy_fromio(skb->data, dev->mem_start + ring_offset, semi_count);
+ count -= semi_count;
+ memcpy_fromio(skb->data + semi_count, dev->rmem_start, count);
+ } else {
+ /* Packet is in one chunk -- we can copy + cksum. */
+ eth_io_copy_and_sum(skb, dev->mem_start + ring_offset, count, 0);
+ }
+ return;
+ }
+ /* No shared memory, use programmed I/O. */
+ outb(ring_offset & 0xff, E33G_DMAAL);
+ outb_p((ring_offset >> 8) & 0xff, E33G_DMAAH);
+ outb_p((ei_status.interface_num == 0 ? ECNTRL_THIN : ECNTRL_AUI) | ECNTRL_INPUT
+ | ECNTRL_START, E33G_CNTRL);
+
+ /* This is the byte copy loop: it should probably be tuned for
+ speed once everything is working. */
+ for(i = 0; i < count; i++) {
+ if (i % 8 == 0)
+ while ((inb(E33G_STATUS) & ESTAT_DPRDY) == 0)
+ if (++boguscount > (i<<3) + 32) {
+ printk("%s: FIFO blocked in el2_block_input() (at %d of %d, bc=%d).\n",
+ dev->name, i, count, boguscount);
+ boguscount = 0;
+ break;
+ }
+ (skb->data)[i] = inb_p(E33G_FIFOH);
+ }
+ outb_p(ei_status.interface_num == 0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL);
+}
+
+
+#ifdef MODULE
+#define MAX_EL2_CARDS 4 /* Max number of EL2 cards per module */
+#define NAMELEN 8 /* # of chars for storing dev->name */
+static char namelist[NAMELEN * MAX_EL2_CARDS] = { 0, };
+static struct device dev_el2[MAX_EL2_CARDS] = {
+ {
+ NULL, /* assign a chunk of namelist[] below */
+ 0, 0, 0, 0,
+ 0, 0,
+ 0, 0, 0, NULL, NULL
+ },
+};
+
+static int io[MAX_EL2_CARDS] = { 0, };
+static int irq[MAX_EL2_CARDS] = { 0, };
+static int xcvr[MAX_EL2_CARDS] = { 0, }; /* choose int. or ext. xcvr */
+
+/* This is set up so that only a single autoprobe takes place per call.
+ISA device autoprobes on a running machine are not recommended. */
+int
+init_module(void)
+{
+ int this_dev, found = 0;
+
+ for (this_dev = 0; this_dev < MAX_EL2_CARDS; this_dev++) {
+ struct device *dev = &dev_el2[this_dev];
+ dev->name = namelist+(NAMELEN*this_dev);
+ dev->irq = irq[this_dev];
+ dev->base_addr = io[this_dev];
+ dev->mem_end = xcvr[this_dev]; /* low 4bits = xcvr sel. */
+ dev->init = el2_probe;
+ if (io[this_dev] == 0) {
+ if (this_dev != 0) break; /* only autoprobe 1st one */
+ printk(KERN_NOTICE "3c503.c: Presently autoprobing (not recommended) for a single card.\n");
+ }
+ if (register_netdev(dev) != 0) {
+ printk(KERN_WARNING "3c503.c: No 3c503 card found (i/o = 0x%x).\n", io[this_dev]);
+ if (found != 0) return 0; /* Got at least one. */
+ return -ENXIO;
+ }
+ found++;
+ }
+
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ int this_dev;
+
+ for (this_dev = 0; this_dev < MAX_EL2_CARDS; this_dev++) {
+ struct device *dev = &dev_el2[this_dev];
+ if (dev->priv != NULL) {
+ /* NB: el2_close() handles free_irq + irq2dev map */
+ kfree(dev->priv);
+ dev->priv = NULL;
+ release_region(dev->base_addr, EL2_IO_EXTENT);
+ unregister_netdev(dev);
+ }
+ }
+}
+#endif /* MODULE */
+
+/*
+ * Local variables:
+ * version-control: t
+ * kept-new-versions: 5
+ * c-indent-level: 4
+ * End:
+ */
diff --git a/i386/i386at/gpl/linux/net/3c503.h b/i386/i386at/gpl/linux/net/3c503.h
new file mode 100644
index 00000000..b9f8a46f
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/3c503.h
@@ -0,0 +1,91 @@
+/* Definitions for the 3Com 3c503 Etherlink 2. */
+/* This file is distributed under the GPL.
+ Many of these names and comments are directly from the Crynwr packet
+ drivers, which are released under the GPL. */
+
+#define EL2H (dev->base_addr + 0x400)
+#define EL2L (dev->base_addr)
+
+/* Vendor unique hardware addr. prefix. 3Com has 2 because they ran
+ out of available addresses on the first one... */
+
+#define OLD_3COM_ID 0x02608c
+#define NEW_3COM_ID 0x0020af
+
+/* Shared memory management parameters. NB: The 8 bit cards have only
+ one bank (MB1) which serves both Tx and Rx packet space. The 16bit
+ cards have 2 banks, MB0 for Tx packets, and MB1 for Rx packets.
+ You choose which bank appears in the sh. mem window with EGACFR_MBSn */
+
+#define EL2_MB0_START_PG (0x00) /* EL2/16 Tx packets go in bank 0 */
+#define EL2_MB1_START_PG (0x20) /* First page of bank 1 */
+#define EL2_MB1_STOP_PG (0x40) /* Last page +1 of bank 1 */
+
+/* 3Com 3c503 ASIC registers */
+#define E33G_STARTPG (EL2H+0) /* Start page, matching EN0_STARTPG */
+#define E33G_STOPPG (EL2H+1) /* Stop page, must match EN0_STOPPG */
+#define E33G_DRQCNT (EL2H+2) /* DMA burst count */
+#define E33G_IOBASE (EL2H+3) /* Read of I/O base jumpers. */
+ /* (non-useful, but it also appears at the end of EPROM space) */
+#define E33G_ROMBASE (EL2H+4) /* Read of memory base jumpers. */
+#define E33G_GACFR (EL2H+5) /* Config/setup bits for the ASIC GA */
+#define E33G_CNTRL (EL2H+6) /* Board's main control register */
+#define E33G_STATUS (EL2H+7) /* Status on completions. */
+#define E33G_IDCFR (EL2H+8) /* Interrupt/DMA config register */
+ /* (Which IRQ to assert, DMA chan to use) */
+#define E33G_DMAAH (EL2H+9) /* High byte of DMA address reg */
+#define E33G_DMAAL (EL2H+10) /* Low byte of DMA address reg */
+/* "Vector pointer" - if this address matches a read, the EPROM (rather than
+ shared RAM) is mapped into memory space. */
+#define E33G_VP2 (EL2H+11)
+#define E33G_VP1 (EL2H+12)
+#define E33G_VP0 (EL2H+13)
+#define E33G_FIFOH (EL2H+14) /* FIFO for programmed I/O moves */
+#define E33G_FIFOL (EL2H+15) /* ... low byte of above. */
+
+/* Bits in E33G_CNTRL register: */
+
+#define ECNTRL_RESET (0x01) /* Software reset of the ASIC and 8390 */
+#define ECNTRL_THIN (0x02) /* Onboard xcvr enable, AUI disable */
+#define ECNTRL_AUI (0x00) /* Onboard xcvr disable, AUI enable */
+#define ECNTRL_SAPROM (0x04) /* Map the station address prom */
+#define ECNTRL_DBLBFR (0x20) /* FIFO configuration bit */
+#define ECNTRL_OUTPUT (0x40) /* PC-to-3C503 direction if 1 */
+#define ECNTRL_INPUT (0x00) /* 3C503-to-PC direction if 0 */
+#define ECNTRL_START (0x80) /* Start the DMA logic */
+
+/* Bits in E33G_STATUS register: */
+
+#define ESTAT_DPRDY (0x80) /* Data port (of FIFO) ready */
+#define ESTAT_UFLW (0x40) /* Tried to read FIFO when it was empty */
+#define ESTAT_OFLW (0x20) /* Tried to write FIFO when it was full */
+#define ESTAT_DTC (0x10) /* Terminal Count from PC bus DMA logic */
+#define ESTAT_DIP (0x08) /* DMA In Progress */
+
+/* Bits in E33G_GACFR register: */
+
+#define EGACFR_NIM (0x80) /* NIC interrupt mask */
+#define EGACFR_TCM (0x40) /* DMA term. count interrupt mask */
+#define EGACFR_RSEL (0x08) /* Map a bank of card mem into system mem */
+#define EGACFR_MBS2 (0x04) /* Memory bank select, bit 2. */
+#define EGACFR_MBS1 (0x02) /* Memory bank select, bit 1. */
+#define EGACFR_MBS0 (0x01) /* Memory bank select, bit 0. */
+
+#define EGACFR_NORM (0x49) /* TCM | RSEL | MBS0 */
+#define EGACFR_IRQOFF (0xc9) /* TCM | RSEL | MBS0 | NIM */
+
+/*
+ MBS2 MBS1 MBS0 Sh. mem windows card mem at:
+ ---- ---- ---- -----------------------------
+ 0 0 0 0x0000 -- bank 0
+ 0 0 1 0x2000 -- bank 1 (only choice for 8bit card)
+ 0 1 0 0x4000 -- bank 2, not used
+ 0 1 1 0x6000 -- bank 3, not used
+
+There was going to be a 32k card that used bank 2 and 3, but it
+never got produced.
+
+*/
+
+
+/* End of 3C503 parameter definitions */
diff --git a/i386/i386at/gpl/linux/net/3c505.c b/i386/i386at/gpl/linux/net/3c505.c
new file mode 100644
index 00000000..63ccc9cf
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/3c505.c
@@ -0,0 +1,1518 @@
+/*
+ * Linux ethernet device driver for the 3Com Etherlink Plus (3C505)
+ * By Craig Southeren and Juha Laiho
+ *
+ * 3c505.c This module implements an interface to the 3Com
+ * Etherlink Plus (3c505) ethernet card. Linux device
+ * driver interface reverse engineered from the Linux 3C509
+ * device drivers. Some 3C505 information gleaned from
+ * the Crynwr packet driver. Still this driver would not
+ * be here without 3C505 technical reference provided by
+ * 3Com.
+ *
+ * Version: @(#)3c505.c 0.8.4 17-Dec-95
+ *
+ * Authors: Linux 3c505 device driver by
+ * Craig Southeren, <craigs@ineluki.apana.org.au>
+ * Final debugging by
+ * Andrew Tridgell, <tridge@nimbus.anu.edu.au>
+ * Auto irq/address, tuning, cleanup and v1.1.4+ kernel mods by
+ * Juha Laiho, <jlaiho@ichaos.nullnet.fi>
+ * Linux 3C509 driver by
+ * Donald Becker, <becker@super.org>
+ * Crynwr packet driver by
+ * Krishnan Gopalan and Gregg Stefancik,
+ * Clemson University Engineering Computer Operations.
+ * Portions of the code have been adapted from the 3c505
+ * driver for NCSA Telnet by Bruce Orchard and later
+ * modified by Warren Van Houten and krus@diku.dk.
+ * 3C505 technical information provided by
+ * Terry Murphy, of 3Com Network Adapter Division
+ * Linux 1.3.0 changes by
+ * Alan Cox <Alan.Cox@linux.org>
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/ioport.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include "3c505.h"
+
+/*********************************************************
+ *
+ * define debug messages here as common strings to reduce space
+ *
+ *********************************************************/
+
+static const char * filename = __FILE__;
+
+static const char * null_msg = "*** NULL at %s:%s (line %d) ***\n";
+#define CHECK_NULL(p) \
+ if (!p) printk(null_msg, filename,__FUNCTION__,__LINE__)
+
+static const char * timeout_msg = "*** timeout at %s:%s (line %d) ***\n";
+#define TIMEOUT_MSG(lineno) \
+ printk(timeout_msg, filename,__FUNCTION__,(lineno))
+
+static const char * invalid_pcb_msg =
+ "*** invalid pcb length %d at %s:%s (line %d) ***\n";
+#define INVALID_PCB_MSG(len) \
+ printk(invalid_pcb_msg, (len),filename,__FUNCTION__,__LINE__)
+
+static const char * search_msg = "%s: Looking for 3c505 adapter at address %#x...";
+
+static const char * stilllooking_msg = "still looking...";
+
+static const char * found_msg = "found.\n";
+
+static const char * notfound_msg = "not found (reason = %d)\n";
+
+static const char * couldnot_msg = "%s: 3c505 not found\n";
+
+/*********************************************************
+ *
+ * various other debug stuff
+ *
+ *********************************************************/
+
+#ifdef ELP_DEBUG
+static int elp_debug = ELP_DEBUG;
+#else
+static int elp_debug = 0;
+#endif
+
+/*
+ * 0 = no messages (well, some)
+ * 1 = messages when high level commands performed
+ * 2 = messages when low level commands performed
+ * 3 = messages when interrupts received
+ */
+
+#define ELP_VERSION "0.8.4"
+
+#ifdef MACH
+#define ELP_NEED_HARD_RESET 0
+#endif
+
+/*****************************************************************
+ *
+ * useful macros
+ *
+ *****************************************************************/
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+
+/*****************************************************************
+ *
+ * List of I/O-addresses we try to auto-sense
+ * Last element MUST BE 0!
+ *****************************************************************/
+
+const int addr_list[]={0x300,0x280,0x310,0};
+
+/*****************************************************************
+ *
+ * Functions for I/O (note the inline !)
+ *
+ *****************************************************************/
+
+static inline unsigned char
+inb_status (unsigned int base_addr)
+{
+ return inb(base_addr+PORT_STATUS);
+}
+
+static inline unsigned char
+inb_control (unsigned int base_addr)
+{
+ return inb(base_addr+PORT_CONTROL);
+}
+
+static inline int
+inb_command (unsigned int base_addr)
+{
+ return inb(base_addr+PORT_COMMAND);
+}
+
+static inline void
+outb_control (unsigned char val, unsigned int base_addr)
+{
+ outb(val, base_addr+PORT_CONTROL);
+}
+
+static inline void
+outb_command (unsigned char val, unsigned int base_addr)
+{
+ outb(val, base_addr+PORT_COMMAND);
+}
+
+static inline unsigned int
+inw_data (unsigned int base_addr)
+{
+ return inw(base_addr+PORT_DATA);
+}
+
+static inline void
+outw_data (unsigned int val, unsigned int base_addr)
+{
+ outw(val, base_addr+PORT_DATA);
+}
+
+
+/*****************************************************************
+ *
+ * structure to hold context information for adapter
+ *
+ *****************************************************************/
+
+typedef struct {
+ volatile short got[NUM_TRANSMIT_CMDS]; /* flags for command completion */
+ pcb_struct tx_pcb; /* PCB for foreground sending */
+ pcb_struct rx_pcb; /* PCB for foreground receiving */
+ pcb_struct itx_pcb; /* PCB for background sending */
+ pcb_struct irx_pcb; /* PCB for background receiving */
+ struct enet_statistics stats;
+} elp_device;
+
+static int reset_count=0;
+
+/*****************************************************************
+ *
+ * useful functions for accessing the adapter
+ *
+ *****************************************************************/
+
+/*
+ * use this routine when accessing the ASF bits as they are
+ * changed asynchronously by the adapter
+ */
+
+/* get adapter PCB status */
+#define GET_ASF(addr) \
+ (get_status(addr)&ASF_PCB_MASK)
+
+static inline int
+get_status (unsigned int base_addr)
+{
+ int timeout = jiffies + 10;
+ register int stat1;
+ do {
+ stat1 = inb_status(base_addr);
+ } while (stat1 != inb_status(base_addr) && jiffies < timeout);
+ if (jiffies >= timeout)
+ TIMEOUT_MSG(__LINE__);
+ return stat1;
+}
+
+static inline void
+set_hsf (unsigned int base_addr, int hsf)
+{
+ cli();
+ outb_control((inb_control(base_addr)&~HSF_PCB_MASK)|hsf, base_addr);
+ sti();
+}
+
+#define WAIT_HCRE(addr,toval) wait_hcre((addr),(toval),__LINE__)
+static inline int
+wait_hcre (unsigned int base_addr, int toval, int lineno)
+{
+ int timeout = jiffies + toval;
+ while (((inb_status(base_addr)&HCRE)==0) && (jiffies <= timeout))
+ ;
+ if (jiffies >= timeout) {
+ TIMEOUT_MSG(lineno);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static inline int
+wait_fast_hcre (unsigned int base_addr, int toval, int lineno)
+{
+ int timeout = 0;
+ while (((inb_status(base_addr)&HCRE)==0) && (timeout++ < toval))
+ ;
+ if (timeout >= toval) {
+ sti();
+ TIMEOUT_MSG(lineno);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static int start_receive (struct device *, pcb_struct *);
+static void adapter_hard_reset (struct device *);
+
+inline static void
+adapter_reset (struct device * dev)
+{
+ int timeout;
+ unsigned char orig_hcr=inb_control(dev->base_addr);
+
+ elp_device * adapter=dev->priv;
+
+ outb_control(0,dev->base_addr);
+
+ if (inb_status(dev->base_addr)&ACRF) {
+ do {
+ inb_command(dev->base_addr);
+ timeout=jiffies+2;
+ while ((jiffies<=timeout) && !(inb_status(dev->base_addr)&ACRF))
+ ;
+ } while (inb_status(dev->base_addr)&ACRF);
+ set_hsf(dev->base_addr,HSF_PCB_NAK);
+ }
+
+ outb_control(inb_control(dev->base_addr)|ATTN|DIR,dev->base_addr);
+ timeout=jiffies+1;
+ while (jiffies<=timeout)
+ ;
+ outb_control(inb_control(dev->base_addr)&~ATTN,dev->base_addr);
+ timeout=jiffies+1;
+ while (jiffies<=timeout)
+ ;
+ outb_control(inb_control(dev->base_addr)|FLSH,dev->base_addr);
+ timeout=jiffies+1;
+ while (jiffies<=timeout)
+ ;
+ outb_control(inb_control(dev->base_addr)&~FLSH,dev->base_addr);
+ timeout=jiffies+1;
+ while (jiffies<=timeout)
+ ;
+
+ outb_control(orig_hcr, dev->base_addr);
+ if (!start_receive(dev, &adapter->tx_pcb))
+ printk("%s: start receive command failed \n", dev->name);
+}
+
+/*****************************************************************
+ *
+ * send_pcb
+ * Send a PCB to the adapter.
+ *
+ * output byte to command reg --<--+
+ * wait until HCRE is non zero |
+ * loop until all bytes sent -->--+
+ * set HSF1 and HSF2 to 1
+ * output pcb length
+ * wait until ASF give ACK or NAK
+ * set HSF1 and HSF2 to 0
+ *
+ *****************************************************************/
+
+static int
+send_pcb (struct device * dev, pcb_struct * pcb)
+{
+ int i;
+ int timeout;
+ int cont;
+
+ /*
+ * load each byte into the command register and
+ * wait for the HCRE bit to indicate the adapter
+ * had read the byte
+ */
+ set_hsf(dev->base_addr,0);
+ if ((cont = WAIT_HCRE(dev->base_addr,5))) {
+ cli();
+ if (pcb->command==CMD_TRANSMIT_PACKET)
+ outb_control(inb_control(dev->base_addr)&~DIR,dev->base_addr);
+ outb_command(pcb->command, dev->base_addr);
+ sti();
+ cont = WAIT_HCRE(dev->base_addr,5);
+ }
+
+ if (cont) {
+ outb_command(pcb->length, dev->base_addr);
+ cont = WAIT_HCRE(dev->base_addr,5);
+ }
+
+ cli();
+ for (i = 0; cont && (i < pcb->length); i++) {
+ outb_command(pcb->data.raw[i], dev->base_addr);
+ cont = wait_fast_hcre(dev->base_addr,20000,__LINE__);
+ } /* if wait_fast_hcre() failed, has already done sti() */
+
+ /* set the host status bits to indicate end of PCB */
+ /* send the total packet length as well */
+ /* wait for the adapter to indicate that it has read the PCB */
+ if (cont) {
+ set_hsf(dev->base_addr,HSF_PCB_END);
+ outb_command(2+pcb->length, dev->base_addr);
+ sti();
+ timeout = jiffies + 7;
+ while (jiffies < timeout) {
+ i = GET_ASF(dev->base_addr);
+ if ((i == ASF_PCB_ACK) || (i == ASF_PCB_NAK))
+ break;
+ }
+
+ if (i == ASF_PCB_ACK) {
+ reset_count=0;
+ return TRUE;
+ }
+ else if (i == ASF_PCB_NAK) {
+ printk("%s: PCB send was NAKed\n", dev->name);
+ } else {
+ printk("%s: timeout after sending PCB\n", dev->name);
+ }
+ } else {
+ sti();
+ printk("%s: timeout in middle of sending PCB\n", dev->name);
+ }
+
+ adapter_reset(dev);
+ return FALSE;
+}
+
+/*****************************************************************
+ *
+ * receive_pcb
+ * Read a PCB to the adapter
+ *
+ * wait for ACRF to be non-zero ---<---+
+ * input a byte |
+ * if ASF1 and ASF2 were not both one |
+ * before byte was read, loop --->---+
+ * set HSF1 and HSF2 for ack
+ *
+ *****************************************************************/
+
+static int
+receive_pcb (struct device * dev, pcb_struct * pcb)
+{
+ int i, j;
+ int total_length;
+ int stat;
+ int timeout;
+
+ CHECK_NULL(pcb);
+ CHECK_NULL(dev);
+
+ set_hsf(dev->base_addr,0);
+
+ /* get the command code */
+ timeout = jiffies + 2;
+ while (((stat = get_status(dev->base_addr))&ACRF) == 0 && jiffies < timeout)
+ ;
+ if (jiffies >= timeout) {
+ TIMEOUT_MSG(__LINE__);
+ return FALSE;
+ }
+
+ pcb->command = inb_command(dev->base_addr);
+
+ /* read the data length */
+ timeout = jiffies + 3;
+ while (((stat = get_status(dev->base_addr)) & ACRF) == 0 && jiffies < timeout)
+ ;
+ if (jiffies >= timeout) {
+ TIMEOUT_MSG(__LINE__);
+ return FALSE;
+ }
+ pcb->length = inb_command(dev->base_addr);
+
+ if (pcb->length > MAX_PCB_DATA) {
+ INVALID_PCB_MSG(pcb->length);
+ adapter_reset(dev);
+ return FALSE;
+ }
+
+ /* read the data */
+ cli();
+ i = 0;
+ do {
+ j = 0;
+ while (((stat = get_status(dev->base_addr))&ACRF) == 0 && j++ < 20000)
+ ;
+ pcb->data.raw[i++] = inb_command(dev->base_addr);
+ if (i > MAX_PCB_DATA)
+ INVALID_PCB_MSG(i);
+ } while ((stat & ASF_PCB_MASK) != ASF_PCB_END && j < 20000);
+ sti();
+ if (j >= 20000) {
+ TIMEOUT_MSG(__LINE__);
+ return FALSE;
+ }
+
+ /* woops, the last "data" byte was really the length! */
+ total_length = pcb->data.raw[--i];
+
+ /* safety check total length vs data length */
+ if (total_length != (pcb->length + 2)) {
+ if (elp_debug >= 2)
+ printk("%s: mangled PCB received\n", dev->name);
+ set_hsf(dev->base_addr,HSF_PCB_NAK);
+ return FALSE;
+ }
+
+ set_hsf(dev->base_addr,HSF_PCB_ACK);
+ reset_count=0;
+ return TRUE;
+}
+
+static void
+adapter_hard_reset (struct device * dev)
+{
+ int timeout;
+ long flags;
+
+ CHECK_NULL(dev);
+
+ save_flags(flags);
+ sti();
+
+ if (elp_debug > 0)
+ printk("%s: Resetting the adapter, please wait (approx 20 s)\n",
+ dev->name);
+ /*
+ * take FLSH and ATTN high
+ */
+ outb_control(ATTN|FLSH, dev->base_addr);
+
+ /*
+ * wait for a little bit
+ */
+ for (timeout = jiffies + 20; jiffies <= timeout; )
+ ;
+
+ /*
+ * now take them low
+ */
+ outb_control(0, dev->base_addr);
+
+ /*
+ * wait for a little bit
+ */
+ for (timeout = jiffies + 20; jiffies <= timeout; )
+ ;
+
+ /*
+ * now hang around until the board gets it's act together
+ */
+ for (timeout = jiffies + (100 * 15); jiffies <= timeout; )
+ if (GET_ASF(dev->base_addr) != ASF_PCB_END)
+ break;
+ restore_flags(flags);
+}
+
+/******************************************************
+ *
+ * queue a receive command on the adapter so we will get an
+ * interrupt when a packet is received.
+ *
+ ******************************************************/
+
+static int
+start_receive (struct device * dev, pcb_struct * tx_pcb)
+{
+ CHECK_NULL(dev);
+ CHECK_NULL(tx_pcb);
+
+ if (elp_debug >= 3)
+ printk("%s: restarting receiver\n", dev->name);
+ tx_pcb->command = CMD_RECEIVE_PACKET;
+ tx_pcb->length = sizeof(struct Rcv_pkt);
+ tx_pcb->data.rcv_pkt.buf_seg
+ = tx_pcb->data.rcv_pkt.buf_ofs = 0; /* Unused */
+ tx_pcb->data.rcv_pkt.buf_len = 1600;
+ tx_pcb->data.rcv_pkt.timeout = 0; /* set timeout to zero */
+ return send_pcb(dev, tx_pcb);
+}
+
+/******************************************************
+ *
+ * extract a packet from the adapter
+ * this routine is only called from within the interrupt
+ * service routine, so no cli/sti calls are needed
+ * note that the length is always assumed to be even
+ *
+ ******************************************************/
+
+static void
+receive_packet (struct device * dev, int len)
+{
+ register int i;
+ unsigned short * ptr;
+ int timeout;
+ int rlen;
+ struct sk_buff *skb;
+ elp_device * adapter;
+
+ CHECK_NULL(dev);
+ adapter=dev->priv;
+
+ if (len <= 0 || ((len & ~1) != len))
+ if (elp_debug >= 3) {
+ sti();
+ printk("*** bad packet len %d at %s(%d)\n",len,filename,__LINE__);
+ cli();
+ }
+
+ rlen = (len+1) & ~1;
+
+ skb = dev_alloc_skb(rlen+2);
+
+ /*
+ * make sure the data register is going the right way
+ */
+
+ outb_control(inb_control(dev->base_addr)|DIR, dev->base_addr);
+
+ /*
+ * if buffer could not be allocated, swallow it
+ */
+ if (skb == NULL) {
+ for (i = 0; i < (rlen/2); i++) {
+ timeout = 0;
+ while ((inb_status(dev->base_addr)&HRDY) == 0 && timeout++ < 20000)
+ ;
+ if (timeout >= 20000) {
+ sti();
+ TIMEOUT_MSG(__LINE__);
+ break;
+ }
+
+ inw_data(dev->base_addr);
+ }
+ adapter->stats.rx_dropped++;
+
+ } else {
+ skb_reserve(skb,2); /* 16 byte alignment */
+ skb->dev = dev;
+
+ /*
+ * now read the data from the adapter
+ */
+ ptr = (unsigned short *)skb_put(skb,len);
+ for (i = 0; i < (rlen/2); i++) {
+ timeout = 0;
+ while ((inb_status(dev->base_addr)&HRDY) == 0 && timeout++ < 20000)
+ ;
+ if (timeout >= 20000) {
+ sti();
+ printk("*** timeout at %s(%d) reading word %d of %d ***\n",
+ filename,__LINE__, i, rlen/2);
+ kfree_skb(skb, FREE_WRITE);
+ return;
+ }
+
+ *ptr = inw_data(dev->base_addr);
+ ptr++;
+ }
+
+ sti();
+ skb->protocol=eth_type_trans(skb,dev);
+ netif_rx(skb);
+ }
+
+ outb_control(inb_control(dev->base_addr)&~DIR, dev->base_addr);
+}
+
+
+/******************************************************
+ *
+ * interrupt handler
+ *
+ ******************************************************/
+
+static void
+elp_interrupt (int irq, struct pt_regs *reg_ptr)
+{
+ int len;
+ int dlen;
+ struct device *dev;
+ elp_device * adapter;
+ int timeout;
+
+ if (irq < 0 || irq > 15) {
+ printk ("elp_interrupt(): illegal IRQ number found in interrupt routine (%i)\n", irq);
+ return;
+ }
+
+ dev = irq2dev_map[irq];
+
+ if (dev == NULL) {
+ printk ("elp_interrupt(): irq %d for unknown device.\n", irq);
+ return;
+ }
+
+ adapter = (elp_device *) dev->priv;
+
+ CHECK_NULL(adapter);
+
+ if (dev->interrupt)
+ if (elp_debug >= 2)
+ printk("%s: Re-entering the interrupt handler.\n", dev->name);
+ dev->interrupt = 1;
+
+ /*
+ * allow interrupts (we need timers!)
+ */
+ sti();
+
+ /*
+ * receive a PCB from the adapter
+ */
+ timeout = jiffies + 3;
+ while ((inb_status(dev->base_addr)&ACRF) != 0 && jiffies < timeout) {
+
+ if (receive_pcb(dev, &adapter->irx_pcb)) {
+
+ switch (adapter->irx_pcb.command) {
+
+ /*
+ * received a packet - this must be handled fast
+ */
+ case CMD_RECEIVE_PACKET_COMPLETE:
+ /* if the device isn't open, don't pass packets up the stack */
+ if (dev->start == 0)
+ break;
+ cli();
+ /* Set direction of adapter FIFO */
+ outb_control(inb_control(dev->base_addr)|DIR,
+ dev->base_addr);
+ len = adapter->irx_pcb.data.rcv_resp.pkt_len;
+ dlen = adapter->irx_pcb.data.rcv_resp.buf_len;
+ if (adapter->irx_pcb.data.rcv_resp.timeout != 0) {
+ printk("%s: interrupt - packet not received correctly\n", dev->name);
+ sti();
+ } else {
+ if (elp_debug >= 3) {
+ sti();
+ printk("%s: interrupt - packet received of length %i (%i)\n", dev->name, len, dlen);
+ cli();
+ }
+ receive_packet(dev, dlen);
+ sti();
+ if (elp_debug >= 3)
+ printk("%s: packet received\n", dev->name);
+ }
+ if (dev->start && !start_receive(dev, &adapter->itx_pcb))
+ if (elp_debug >= 2)
+ printk("%s: interrupt - failed to send receive start PCB\n", dev->name);
+ if (elp_debug >= 3)
+ printk("%s: receive procedure complete\n", dev->name);
+
+ break;
+
+ /*
+ * 82586 configured correctly
+ */
+ case CMD_CONFIGURE_82586_RESPONSE:
+ adapter->got[CMD_CONFIGURE_82586] = 1;
+ if (elp_debug >= 3)
+ printk("%s: interrupt - configure response received\n", dev->name);
+ break;
+
+ /*
+ * Adapter memory configuration
+ */
+ case CMD_CONFIGURE_ADAPTER_RESPONSE:
+ adapter->got[CMD_CONFIGURE_ADAPTER_MEMORY] = 1;
+ if (elp_debug >= 3)
+ printk("%s: Adapter memory configuration %s.\n",dev->name,
+ adapter->irx_pcb.data.failed?"failed":"succeeded");
+ break;
+
+ /*
+ * Multicast list loading
+ */
+ case CMD_LOAD_MULTICAST_RESPONSE:
+ adapter->got[CMD_LOAD_MULTICAST_LIST] = 1;
+ if (elp_debug >= 3)
+ printk("%s: Multicast address list loading %s.\n",dev->name,
+ adapter->irx_pcb.data.failed?"failed":"succeeded");
+ break;
+
+ /*
+ * Station address setting
+ */
+ case CMD_SET_ADDRESS_RESPONSE:
+ adapter->got[CMD_SET_STATION_ADDRESS] = 1;
+ if (elp_debug >= 3)
+ printk("%s: Ethernet address setting %s.\n",dev->name,
+ adapter->irx_pcb.data.failed?"failed":"succeeded");
+ break;
+
+
+ /*
+ * received board statistics
+ */
+ case CMD_NETWORK_STATISTICS_RESPONSE:
+ adapter->stats.rx_packets += adapter->irx_pcb.data.netstat.tot_recv;
+ adapter->stats.tx_packets += adapter->irx_pcb.data.netstat.tot_xmit;
+ adapter->stats.rx_crc_errors += adapter->irx_pcb.data.netstat.err_CRC;
+ adapter->stats.rx_frame_errors += adapter->irx_pcb.data.netstat.err_align;
+ adapter->stats.rx_fifo_errors += adapter->irx_pcb.data.netstat.err_ovrrun;
+ adapter->got[CMD_NETWORK_STATISTICS] = 1;
+ if (elp_debug >= 3)
+ printk("%s: interrupt - statistics response received\n", dev->name);
+ break;
+
+ /*
+ * sent a packet
+ */
+ case CMD_TRANSMIT_PACKET_COMPLETE:
+ if (elp_debug >= 3)
+ printk("%s: interrupt - packet sent\n", dev->name);
+ if (dev->start == 0)
+ break;
+ if (adapter->irx_pcb.data.xmit_resp.c_stat != 0)
+ if (elp_debug >= 2)
+ printk("%s: interrupt - error sending packet %4.4x\n",
+ dev->name, adapter->irx_pcb.data.xmit_resp.c_stat);
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+ break;
+
+ /*
+ * some unknown PCB
+ */
+ default:
+ printk("%s: unknown PCB received - %2.2x\n", dev->name, adapter->irx_pcb.command);
+ break;
+ }
+ } else {
+ printk("%s: failed to read PCB on interrupt\n", dev->name);
+ adapter_reset(dev);
+ }
+ }
+
+ /*
+ * indicate no longer in interrupt routine
+ */
+ dev->interrupt = 0;
+}
+
+
+/******************************************************
+ *
+ * open the board
+ *
+ ******************************************************/
+
+static int
+elp_open (struct device *dev)
+{
+ elp_device * adapter;
+
+ CHECK_NULL(dev);
+
+ adapter = dev->priv;
+
+ if (elp_debug >= 3)
+ printk("%s: request to open device\n", dev->name);
+
+ /*
+ * make sure we actually found the device
+ */
+ if (adapter == NULL) {
+ printk("%s: Opening a non-existent physical device\n", dev->name);
+ return -EAGAIN;
+ }
+
+ /*
+ * disable interrupts on the board
+ */
+ outb_control(0x00, dev->base_addr);
+
+ /*
+ * clear any pending interrupts
+ */
+ inb_command(dev->base_addr);
+ adapter_reset(dev);
+
+ /*
+ * interrupt routine not entered
+ */
+ dev->interrupt = 0;
+
+ /*
+ * transmitter not busy
+ */
+ dev->tbusy = 0;
+
+ /*
+ * make sure we can find the device header given the interrupt number
+ */
+ irq2dev_map[dev->irq] = dev;
+
+ /*
+ * install our interrupt service routine
+ */
+ if (request_irq(dev->irq, &elp_interrupt, 0, "3c505")) {
+ irq2dev_map[dev->irq] = NULL;
+ return -EAGAIN;
+ }
+
+ /*
+ * enable interrupts on the board
+ */
+ outb_control(CMDE, dev->base_addr);
+
+ /*
+ * device is now officially open!
+ */
+ dev->start = 1;
+
+ /*
+ * configure adapter memory: we need 10 multicast addresses, default==0
+ */
+ if (elp_debug >= 3)
+ printk("%s: sending 3c505 memory configuration command\n", dev->name);
+ adapter->tx_pcb.command = CMD_CONFIGURE_ADAPTER_MEMORY;
+ adapter->tx_pcb.data.memconf.cmd_q = 10;
+ adapter->tx_pcb.data.memconf.rcv_q = 20;
+ adapter->tx_pcb.data.memconf.mcast = 10;
+ adapter->tx_pcb.data.memconf.frame = 20;
+ adapter->tx_pcb.data.memconf.rcv_b = 20;
+ adapter->tx_pcb.data.memconf.progs = 0;
+ adapter->tx_pcb.length = sizeof(struct Memconf);
+ adapter->got[CMD_CONFIGURE_ADAPTER_MEMORY] = 0;
+ if (!send_pcb(dev, &adapter->tx_pcb))
+ printk("%s: couldn't send memory configuration command\n", dev->name);
+ else {
+ int timeout = jiffies + TIMEOUT;
+ while (adapter->got[CMD_CONFIGURE_ADAPTER_MEMORY] == 0 && jiffies < timeout)
+ ;
+ if (jiffies >= timeout)
+ TIMEOUT_MSG(__LINE__);
+ }
+
+
+ /*
+ * configure adapter to receive broadcast messages and wait for response
+ */
+ if (elp_debug >= 3)
+ printk("%s: sending 82586 configure command\n", dev->name);
+ adapter->tx_pcb.command = CMD_CONFIGURE_82586;
+ adapter->tx_pcb.data.configure = NO_LOOPBACK | RECV_BROAD;
+ adapter->tx_pcb.length = 2;
+ adapter->got[CMD_CONFIGURE_82586] = 0;
+ if (!send_pcb(dev, &adapter->tx_pcb))
+ printk("%s: couldn't send 82586 configure command\n", dev->name);
+ else {
+ int timeout = jiffies + TIMEOUT;
+ while (adapter->got[CMD_CONFIGURE_82586] == 0 && jiffies < timeout)
+ ;
+ if (jiffies >= timeout)
+ TIMEOUT_MSG(__LINE__);
+ }
+
+ /*
+ * queue receive commands to provide buffering
+ */
+ if (!start_receive(dev, &adapter->tx_pcb))
+ printk("%s: start receive command failed \n", dev->name);
+ if (elp_debug >= 3)
+ printk("%s: start receive command sent\n", dev->name);
+
+ MOD_INC_USE_COUNT;
+
+ return 0; /* Always succeed */
+}
+
+
+/******************************************************
+ *
+ * send a packet to the adapter
+ *
+ ******************************************************/
+
+static int
+send_packet (struct device * dev, unsigned char * ptr, int len)
+{
+ int i;
+ int timeout = 0;
+ elp_device * adapter;
+
+ /*
+ * make sure the length is even and no shorter than 60 bytes
+ */
+ unsigned int nlen = (((len < 60) ? 60 : len) + 1) & (~1);
+
+ CHECK_NULL(dev);
+ CHECK_NULL(ptr);
+
+ adapter = dev->priv;
+
+ if (nlen < len)
+ printk("Warning, bad length nlen=%d len=%d %s(%d)\n",nlen,len,filename,__LINE__);
+
+ /*
+ * send the adapter a transmit packet command. Ignore segment and offset
+ * and make sure the length is even
+ */
+ adapter->tx_pcb.command = CMD_TRANSMIT_PACKET;
+ adapter->tx_pcb.length = sizeof(struct Xmit_pkt);
+ adapter->tx_pcb.data.xmit_pkt.buf_ofs
+ = adapter->tx_pcb.data.xmit_pkt.buf_seg = 0; /* Unused */
+ adapter->tx_pcb.data.xmit_pkt.pkt_len = nlen;
+ if (!send_pcb(dev, &adapter->tx_pcb)) {
+ return FALSE;
+ }
+
+ /*
+ * write data to the adapter
+ */
+ cli();
+ for (i = 0; i < (nlen/2);i++) {
+ while (((inb_status(dev->base_addr)&HRDY) == 0)
+ && (timeout++ < 20000))
+ ;
+ if (timeout >= 20000) {
+ sti();
+ printk("%s: timeout at %s(%d) writing word %d of %d ***\n",
+ dev->name,filename,__LINE__, i, nlen/2);
+ return FALSE;
+ }
+
+ outw_data(*(short *)ptr, dev->base_addr);
+ ptr +=2;
+ }
+ sti();
+
+ return TRUE;
+}
+
+/******************************************************
+ *
+ * start the transmitter
+ * return 0 if sent OK, else return 1
+ *
+ ******************************************************/
+
+static int
+elp_start_xmit (struct sk_buff *skb, struct device *dev)
+{
+ CHECK_NULL(dev);
+
+ /*
+ * not sure what this does, but the 3c509 driver does it, so...
+ */
+ if (skb == NULL) {
+ dev_tint(dev);
+ return 0;
+ }
+
+ /*
+ * if we ended up with a munged length, don't send it
+ */
+ if (skb->len <= 0)
+ return 0;
+
+ if (elp_debug >= 3)
+ printk("%s: request to send packet of length %d\n", dev->name, (int)skb->len);
+
+ /*
+ * if the transmitter is still busy, we have a transmit timeout...
+ */
+ if (dev->tbusy) {
+ int tickssofar = jiffies - dev->trans_start;
+ int stat;
+ if (tickssofar < 50) /* was 500, AJT */
+ return 1;
+ printk("%s: transmit timed out, not resetting adapter\n", dev->name);
+ if (((stat=inb_status(dev->base_addr))&ACRF) != 0)
+ printk("%s: hmmm...seemed to have missed an interrupt!\n", dev->name);
+ printk("%s: status %#02x\n", dev->name, stat);
+ dev->trans_start = jiffies;
+ dev->tbusy = 0;
+ }
+
+ /*
+ * send the packet at skb->data for skb->len
+ */
+ if (!send_packet(dev, skb->data, skb->len)) {
+ printk("%s: send packet PCB failed\n", dev->name);
+ return 1;
+ }
+
+ if (elp_debug >= 3)
+ printk("%s: packet of length %d sent\n", dev->name, (int)skb->len);
+
+
+ /*
+ * start the transmit timeout
+ */
+ dev->trans_start = jiffies;
+
+ /*
+ * the transmitter is now busy
+ */
+ dev->tbusy = 1;
+
+ /*
+ * free the buffer
+ */
+ dev_kfree_skb(skb, FREE_WRITE);
+
+ return 0;
+}
+
+/******************************************************
+ *
+ * return statistics on the board
+ *
+ ******************************************************/
+
+static struct enet_statistics *
+elp_get_stats (struct device *dev)
+{
+ elp_device *adapter = (elp_device *) dev->priv;
+
+ if (elp_debug >= 3)
+ printk("%s: request for stats\n", dev->name);
+
+ /* If the device is closed, just return the latest stats we have,
+ - we cannot ask from the adapter without interrupts */
+ if (!dev->start)
+ return &adapter->stats;
+
+ /* send a get statistics command to the board */
+ adapter->tx_pcb.command = CMD_NETWORK_STATISTICS;
+ adapter->tx_pcb.length = 0;
+ adapter->got[CMD_NETWORK_STATISTICS] = 0;
+ if (!send_pcb(dev, &adapter->tx_pcb))
+ printk("%s: couldn't send get statistics command\n", dev->name);
+ else {
+ int timeout = jiffies + TIMEOUT;
+ while (adapter->got[CMD_NETWORK_STATISTICS] == 0 && jiffies < timeout)
+ ;
+ if (jiffies >= timeout) {
+ TIMEOUT_MSG(__LINE__);
+ return &adapter->stats;
+ }
+ }
+
+ /* statistics are now up to date */
+ return &adapter->stats;
+}
+
+/******************************************************
+ *
+ * close the board
+ *
+ ******************************************************/
+
+static int
+elp_close (struct device *dev)
+{
+ elp_device * adapter;
+
+ CHECK_NULL(dev);
+ adapter = dev->priv;
+ CHECK_NULL(adapter);
+
+ if (elp_debug >= 3)
+ printk("%s: request to close device\n", dev->name);
+
+ /* Someone may request the device statistic information even when
+ * the interface is closed. The following will update the statistics
+ * structure in the driver, so we'll be able to give current statistics.
+ */
+ (void) elp_get_stats(dev);
+
+ /*
+ * disable interrupts on the board
+ */
+ outb_control(0x00, dev->base_addr);
+
+ /*
+ * flag transmitter as busy (i.e. not available)
+ */
+ dev->tbusy = 1;
+
+ /*
+ * indicate device is closed
+ */
+ dev->start = 0;
+
+ /*
+ * release the IRQ
+ */
+ free_irq(dev->irq);
+
+ /*
+ * and we no longer have to map irq to dev either
+ */
+ irq2dev_map[dev->irq] = 0;
+
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+}
+
+
+/************************************************************
+ *
+ * Set multicast list
+ * num_addrs==0: clear mc_list
+ * num_addrs==-1: set promiscuous mode
+ * num_addrs>0: set mc_list
+ *
+ ************************************************************/
+
+static void
+elp_set_mc_list (struct device *dev)
+{
+ elp_device *adapter = (elp_device *) dev->priv;
+ struct dev_mc_list *dmi=dev->mc_list;
+ int i;
+
+ if (elp_debug >= 3)
+ printk("%s: request to set multicast list\n", dev->name);
+
+ if (!(dev->flags&(IFF_PROMISC|IFF_ALLMULTI)))
+ {
+ /* send a "load multicast list" command to the board, max 10 addrs/cmd */
+ /* if num_addrs==0 the list will be cleared */
+ adapter->tx_pcb.command = CMD_LOAD_MULTICAST_LIST;
+ adapter->tx_pcb.length = 6*dev->mc_count;
+ for (i=0;i<dev->mc_count;i++)
+ {
+ memcpy(adapter->tx_pcb.data.multicast[i], dmi->dmi_addr,6);
+ dmi=dmi->next;
+ }
+ adapter->got[CMD_LOAD_MULTICAST_LIST] = 0;
+ if (!send_pcb(dev, &adapter->tx_pcb))
+ printk("%s: couldn't send set_multicast command\n", dev->name);
+ else {
+ int timeout = jiffies + TIMEOUT;
+ while (adapter->got[CMD_LOAD_MULTICAST_LIST] == 0 && jiffies < timeout)
+ ;
+ if (jiffies >= timeout) {
+ TIMEOUT_MSG(__LINE__);
+ }
+ }
+ if (dev->mc_count)
+ adapter->tx_pcb.data.configure = NO_LOOPBACK | RECV_BROAD | RECV_MULTI;
+ else /* num_addrs == 0 */
+ adapter->tx_pcb.data.configure = NO_LOOPBACK | RECV_BROAD;
+ }
+ else
+ adapter->tx_pcb.data.configure = NO_LOOPBACK | RECV_PROMISC;
+ /*
+ * configure adapter to receive messages (as specified above)
+ * and wait for response
+ */
+ if (elp_debug >= 3)
+ printk("%s: sending 82586 configure command\n", dev->name);
+ adapter->tx_pcb.command = CMD_CONFIGURE_82586;
+ adapter->tx_pcb.length = 2;
+ adapter->got[CMD_CONFIGURE_82586] = 0;
+ if (!send_pcb(dev, &adapter->tx_pcb))
+ printk("%s: couldn't send 82586 configure command\n", dev->name);
+ else {
+ int timeout = jiffies + TIMEOUT;
+ while (adapter->got[CMD_CONFIGURE_82586] == 0 && jiffies < timeout)
+ ;
+ if (jiffies >= timeout)
+ TIMEOUT_MSG(__LINE__);
+ }
+}
+
+/******************************************************
+ *
+ * initialise Etherlink Plus board
+ *
+ ******************************************************/
+
+static void
+elp_init (struct device *dev)
+{
+ elp_device * adapter;
+
+ CHECK_NULL(dev);
+
+ /*
+ * set ptrs to various functions
+ */
+ dev->open = elp_open; /* local */
+ dev->stop = elp_close; /* local */
+ dev->get_stats = elp_get_stats; /* local */
+ dev->hard_start_xmit = elp_start_xmit; /* local */
+ dev->set_multicast_list = elp_set_mc_list; /* local */
+
+ /* Setup the generic properties */
+ ether_setup(dev);
+
+ /*
+ * setup ptr to adapter specific information
+ */
+ adapter = (elp_device *)(dev->priv = kmalloc(sizeof(elp_device), GFP_KERNEL));
+ CHECK_NULL(adapter);
+ if (adapter == NULL)
+ return;
+ memset(&(adapter->stats), 0, sizeof(struct enet_statistics));
+
+ /*
+ * memory information
+ */
+ dev->mem_start = dev->mem_end = dev->rmem_end = dev->rmem_start = 0;
+}
+
+/************************************************************
+ *
+ * A couple of tests to see if there's 3C505 or not
+ * Called only by elp_autodetect
+ ************************************************************/
+
+static int
+elp_sense (struct device * dev)
+{
+ int timeout;
+ int addr=dev->base_addr;
+ const char *name=dev->name;
+ long flags;
+ byte orig_HCR, orig_HSR;
+
+ if (check_region(addr, 0xf))
+ return -1;
+
+ orig_HCR=inb_control(addr);
+ orig_HSR=inb_status(addr);
+
+ if (elp_debug > 0)
+ printk(search_msg, name, addr);
+
+ if (((orig_HCR==0xff) && (orig_HSR==0xff)) ||
+ ((orig_HCR & DIR) != (orig_HSR & DIR))) {
+ if (elp_debug > 0)
+ printk(notfound_msg, 1);
+ return -1; /* It can't be 3c505 if HCR.DIR != HSR.DIR */
+ }
+
+ /* Enable interrupts - we need timers! */
+ save_flags(flags);
+ sti();
+
+ /* Wait for a while; the adapter may still be booting up */
+ if (elp_debug > 0)
+ printk(stilllooking_msg);
+ for (timeout = jiffies + (100 * 15); jiffies <= timeout; )
+ if (GET_ASF(addr) != ASF_PCB_END)
+ break;
+
+ if (orig_HCR & DIR) {
+ /* If HCR.DIR is up, we pull it down. HSR.DIR should follow. */
+ outb_control(orig_HCR & ~DIR,addr);
+ timeout = jiffies+30;
+ while (jiffies < timeout)
+ ;
+ restore_flags(flags);
+ if (inb_status(addr) & DIR) {
+ outb_control(orig_HCR,addr);
+ if (elp_debug > 0)
+ printk(notfound_msg, 2);
+ return -1;
+ }
+ } else {
+ /* If HCR.DIR is down, we pull it up. HSR.DIR should follow. */
+ outb_control(orig_HCR | DIR,addr);
+ timeout = jiffies+300;
+ while (jiffies < timeout)
+ ;
+ restore_flags(flags);
+ if (!(inb_status(addr) & DIR)) {
+ outb_control(orig_HCR,addr);
+ if (elp_debug > 0)
+ printk(notfound_msg, 3);
+ return -1;
+ }
+ }
+ /*
+ * It certainly looks like a 3c505. If it has DMA enabled, it needs
+ * a hard reset. Also, do a hard reset if selected at the compile time.
+ */
+ if (elp_debug > 0)
+ printk(found_msg);
+
+ if (((orig_HCR==0x35) && (orig_HSR==0x5b)) || ELP_NEED_HARD_RESET)
+ adapter_hard_reset(dev);
+ return 0;
+}
+
+/*************************************************************
+ *
+ * Search through addr_list[] and try to find a 3C505
+ * Called only by eplus_probe
+ *************************************************************/
+
+static int
+elp_autodetect (struct device * dev)
+{
+ int idx=0;
+
+ /* if base address set, then only check that address
+ otherwise, run through the table */
+ if (dev->base_addr != 0) { /* dev->base_addr == 0 ==> plain autodetect */
+ if (elp_sense(dev) == 0)
+ return dev->base_addr;
+ } else while ( (dev->base_addr=addr_list[idx++]) ) {
+ if (elp_sense(dev) == 0)
+ return dev->base_addr;
+ }
+
+ /* could not find an adapter */
+ if (elp_debug > 0)
+ printk(couldnot_msg, dev->name);
+
+ return 0; /* Because of this, the layer above will return -ENODEV */
+}
+
+/******************************************************
+ *
+ * probe for an Etherlink Plus board at the specified address
+ *
+ ******************************************************/
+
+int
+elplus_probe (struct device *dev)
+{
+ elp_device adapter;
+ int i;
+
+ CHECK_NULL(dev);
+
+ /*
+ * setup adapter structure
+ */
+
+ dev->base_addr = elp_autodetect(dev);
+ if ( !(dev->base_addr) )
+ return -ENODEV;
+
+ /*
+ * As we enter here from bootup, the adapter should have IRQs enabled,
+ * but we can as well enable them anyway.
+ */
+ outb_control(inb_control(dev->base_addr) | CMDE, dev->base_addr);
+ autoirq_setup(0);
+
+ /*
+ * use ethernet address command to probe for board in polled mode
+ * (this also makes us the IRQ that we need for automatic detection)
+ */
+ adapter.tx_pcb.command = CMD_STATION_ADDRESS;
+ adapter.tx_pcb.length = 0;
+ if (!send_pcb (dev, &adapter.tx_pcb) ||
+ !receive_pcb(dev, &adapter.rx_pcb) ||
+ (adapter.rx_pcb.command != CMD_ADDRESS_RESPONSE) ||
+ (adapter.rx_pcb.length != 6)) {
+ printk("%s: not responding to first PCB\n", dev->name);
+ return -ENODEV;
+ }
+
+ if (dev->irq) { /* Is there a preset IRQ? */
+ if (dev->irq != autoirq_report(0)) {
+ printk("%s: Detected IRQ doesn't match user-defined one.\n",dev->name);
+ return -ENODEV;
+ }
+ /* if dev->irq == autoirq_report(0), all is well */
+ } else /* No preset IRQ; just use what we can detect */
+ dev->irq=autoirq_report(0);
+ switch (dev->irq) { /* Legal, sane? */
+ case 0:
+ printk("%s: No IRQ reported by autoirq_report().\n",dev->name);
+ printk("%s: Check the jumpers of your 3c505 board.\n",dev->name);
+ return -ENODEV;
+ case 1:
+ case 6:
+ case 8:
+ case 13:
+ printk("%s: Impossible IRQ %d reported by autoirq_report().\n",
+ dev->name, dev->irq);
+ return -ENODEV;
+ }
+ /*
+ * Now we have the IRQ number so we can disable the interrupts from
+ * the board until the board is opened.
+ */
+ outb_control(inb_control(dev->base_addr) & ~CMDE, dev->base_addr);
+
+ /*
+ * copy ethernet address into structure
+ */
+ for (i = 0; i < 6; i++)
+ dev->dev_addr[i] = adapter.rx_pcb.data.eth_addr[i];
+
+ /*
+ * print remainder of startup message
+ */
+ printk("%s: 3c505 card found at I/O %#lx using IRQ%d"
+ " has address %02x:%02x:%02x:%02x:%02x:%02x\n",
+ dev->name, dev->base_addr, dev->irq,
+ dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
+ dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
+
+ /*
+ * and reserve the address region
+ */
+ request_region(dev->base_addr, ELP_IO_EXTENT, "3c505");
+
+ /*
+ * initialise the device
+ */
+ elp_init(dev);
+ return 0;
+}
+
+#ifdef MODULE
+static char devicename[9] = { 0, };
+static struct device dev_3c505 = {
+ devicename, /* device name is inserted by linux/drivers/net/net_init.c */
+ 0, 0, 0, 0,
+ 0, 0,
+ 0, 0, 0, NULL, elplus_probe };
+
+int io = 0x300;
+int irq = 0;
+
+int init_module(void)
+{
+ if (io == 0)
+ printk("3c505: You should not use auto-probing with insmod!\n");
+ dev_3c505.base_addr = io;
+ dev_3c505.irq = irq;
+ if (register_netdev(&dev_3c505) != 0) {
+ printk("3c505: register_netdev() returned non-zero.\n");
+ return -EIO;
+ }
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ unregister_netdev(&dev_3c505);
+ kfree(dev_3c505.priv);
+ dev_3c505.priv = NULL;
+
+ /* If we don't do this, we can't re-insmod it later. */
+ release_region(dev_3c505.base_addr, ELP_IO_EXTENT);
+}
+#endif /* MODULE */
diff --git a/i386/i386at/gpl/linux/net/3c505.h b/i386/i386at/gpl/linux/net/3c505.h
new file mode 100644
index 00000000..f7d28368
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/3c505.h
@@ -0,0 +1,245 @@
+/*****************************************************************
+ *
+ * defines for 3Com Etherlink Plus adapter
+ *
+ *****************************************************************/
+
+/*
+ * I/O register offsets
+ */
+#define PORT_COMMAND 0x00 /* read/write, 8-bit */
+#define PORT_STATUS 0x02 /* read only, 8-bit */
+#define PORT_AUXDMA 0x02 /* write only, 8-bit */
+#define PORT_DATA 0x04 /* read/write, 16-bit */
+#define PORT_CONTROL 0x06 /* read/write, 8-bit */
+
+#define ELP_IO_EXTENT 0x10 /* size of used IO registers */
+
+/*
+ * host control registers bits
+ */
+#define ATTN 0x80 /* attention */
+#define FLSH 0x40 /* flush data register */
+#define DMAE 0x20 /* DMA enable */
+#define DIR 0x10 /* direction */
+#define TCEN 0x08 /* terminal count interrupt enable */
+#define CMDE 0x04 /* command register interrupt enable */
+#define HSF2 0x02 /* host status flag 2 */
+#define HSF1 0x01 /* host status flag 1 */
+
+/*
+ * combinations of HSF flags used for PCB transmission
+ */
+#define HSF_PCB_ACK HSF1
+#define HSF_PCB_NAK HSF2
+#define HSF_PCB_END (HSF2|HSF1)
+#define HSF_PCB_MASK (HSF2|HSF1)
+
+/*
+ * host status register bits
+ */
+#define HRDY 0x80 /* data register ready */
+#define HCRE 0x40 /* command register empty */
+#define ACRF 0x20 /* adapter command register full */
+/* #define DIR 0x10 direction - same as in control register */
+#define DONE 0x08 /* DMA done */
+#define ASF3 0x04 /* adapter status flag 3 */
+#define ASF2 0x02 /* adapter status flag 2 */
+#define ASF1 0x01 /* adapter status flag 1 */
+
+/*
+ * combinations of ASF flags used for PCB reception
+ */
+#define ASF_PCB_ACK ASF1
+#define ASF_PCB_NAK ASF2
+#define ASF_PCB_END (ASF2|ASF1)
+#define ASF_PCB_MASK (ASF2|ASF1)
+
+/*
+ * host aux DMA register bits
+ */
+#define DMA_BRST 0x01 /* DMA burst */
+
+/*
+ * maximum amount of data data allowed in a PCB
+ */
+#define MAX_PCB_DATA 62
+
+/*****************************************************************
+ *
+ * timeout value
+ * this is a rough value used for loops to stop them from
+ * locking up the whole machine in the case of failure or
+ * error conditions
+ *
+ *****************************************************************/
+
+#define TIMEOUT 300
+
+/*****************************************************************
+ *
+ * PCB commands
+ *
+ *****************************************************************/
+
+enum {
+ /*
+ * host PCB commands
+ */
+ CMD_CONFIGURE_ADAPTER_MEMORY = 0x01,
+ CMD_CONFIGURE_82586 = 0x02,
+ CMD_STATION_ADDRESS = 0x03,
+ CMD_DMA_DOWNLOAD = 0x04,
+ CMD_DMA_UPLOAD = 0x05,
+ CMD_PIO_DOWNLOAD = 0x06,
+ CMD_PIO_UPLOAD = 0x07,
+ CMD_RECEIVE_PACKET = 0x08,
+ CMD_TRANSMIT_PACKET = 0x09,
+ CMD_NETWORK_STATISTICS = 0x0a,
+ CMD_LOAD_MULTICAST_LIST = 0x0b,
+ CMD_CLEAR_PROGRAM = 0x0c,
+ CMD_DOWNLOAD_PROGRAM = 0x0d,
+ CMD_EXECUTE_PROGRAM = 0x0e,
+ CMD_SELF_TEST = 0x0f,
+ CMD_SET_STATION_ADDRESS = 0x10,
+ CMD_ADAPTER_INFO = 0x11,
+ NUM_TRANSMIT_CMDS,
+
+ /*
+ * adapter PCB commands
+ */
+ CMD_CONFIGURE_ADAPTER_RESPONSE = 0x31,
+ CMD_CONFIGURE_82586_RESPONSE = 0x32,
+ CMD_ADDRESS_RESPONSE = 0x33,
+ CMD_DOWNLOAD_DATA_REQUEST = 0x34,
+ CMD_UPLOAD_DATA_REQUEST = 0x35,
+ CMD_RECEIVE_PACKET_COMPLETE = 0x38,
+ CMD_TRANSMIT_PACKET_COMPLETE = 0x39,
+ CMD_NETWORK_STATISTICS_RESPONSE = 0x3a,
+ CMD_LOAD_MULTICAST_RESPONSE = 0x3b,
+ CMD_CLEAR_PROGRAM_RESPONSE = 0x3c,
+ CMD_DOWNLOAD_PROGRAM_RESPONSE = 0x3d,
+ CMD_EXECUTE_RESPONSE = 0x3e,
+ CMD_SELF_TEST_RESPONSE = 0x3f,
+ CMD_SET_ADDRESS_RESPONSE = 0x40,
+ CMD_ADAPTER_INFO_RESPONSE = 0x41
+};
+
+/* Definitions for the PCB data structure */
+
+/* Data units */
+typedef unsigned char byte;
+typedef unsigned short int word;
+typedef unsigned long int dword;
+
+/* Data structures */
+struct Memconf {
+ word cmd_q,
+ rcv_q,
+ mcast,
+ frame,
+ rcv_b,
+ progs;
+};
+
+struct Rcv_pkt {
+ word buf_ofs,
+ buf_seg,
+ buf_len,
+ timeout;
+};
+
+struct Xmit_pkt {
+ word buf_ofs,
+ buf_seg,
+ pkt_len;
+};
+
+struct Rcv_resp {
+ word buf_ofs,
+ buf_seg,
+ buf_len,
+ pkt_len,
+ timeout,
+ status;
+ dword timetag;
+};
+
+struct Xmit_resp {
+ word buf_ofs,
+ buf_seg,
+ c_stat,
+ status;
+};
+
+
+struct Netstat {
+ dword tot_recv,
+ tot_xmit;
+ word err_CRC,
+ err_align,
+ err_res,
+ err_ovrrun;
+};
+
+
+struct Selftest {
+ word error;
+ union {
+ word ROM_cksum;
+ struct {
+ word ofs, seg;
+ } RAM;
+ word i82586;
+ } failure;
+};
+
+struct Info {
+ byte minor_vers,
+ major_vers;
+ word ROM_cksum,
+ RAM_sz,
+ free_ofs,
+ free_seg;
+};
+
+struct Memdump {
+ word size,
+ off,
+ seg;
+};
+
+/*
+Primary Command Block. The most important data structure. All communication
+between the host and the adapter is done with these. (Except for the actual
+ethernet data, which has different packaging.)
+*/
+typedef struct {
+ byte command;
+ byte length;
+ union {
+ struct Memconf memconf;
+ word configure;
+ struct Rcv_pkt rcv_pkt;
+ struct Xmit_pkt xmit_pkt;
+ byte multicast[10][6];
+ byte eth_addr[6];
+ byte failed;
+ struct Rcv_resp rcv_resp;
+ struct Xmit_resp xmit_resp;
+ struct Netstat netstat;
+ struct Selftest selftest;
+ struct Info info;
+ struct Memdump memdump;
+ byte raw[62];
+ } data;
+} pcb_struct;
+
+/* These defines for 'configure' */
+#define RECV_STATION 0x00
+#define RECV_BROAD 0x01
+#define RECV_MULTI 0x02
+#define RECV_PROMISC 0x04
+#define NO_LOOPBACK 0x00
+#define INT_LOOPBACK 0x08
+#define EXT_LOOPBACK 0x10
diff --git a/i386/i386at/gpl/linux/net/3c507.c b/i386/i386at/gpl/linux/net/3c507.c
new file mode 100644
index 00000000..f18bc0a3
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/3c507.c
@@ -0,0 +1,923 @@
+/* 3c507.c: An EtherLink16 device driver for Linux. */
+/*
+ Written 1993,1994 by Donald Becker.
+
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency.
+
+ This software may be used and distributed according to the terms
+ of the GNU Public License, incorporated herein by reference.
+
+ The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
+ Center of Excellence in Space Data and Information Sciences
+ Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
+
+ Thanks go to jennings@Montrouge.SMR.slb.com ( Patrick Jennings)
+ and jrs@world.std.com (Rick Sladkey) for testing and bugfixes.
+ Mark Salazar <leslie@access.digex.net> made the changes for cards with
+ only 16K packet buffers.
+
+ Things remaining to do:
+ Verify that the tx and rx buffers don't have fencepost errors.
+ Move the theory of operation and memory map documentation.
+ The statistics need to be updated correctly.
+*/
+
+static const char *version =
+ "3c507.c:v1.10 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+
+#include <linux/module.h>
+
+/*
+ Sources:
+ This driver wouldn't have been written with the availability of the
+ Crynwr driver source code. It provided a known-working implementation
+ that filled in the gaping holes of the Intel documentation. Three cheers
+ for Russ Nelson.
+
+ Intel Microcommunications Databook, Vol. 1, 1990. It provides just enough
+ info that the casual reader might think that it documents the i82586 :-<.
+*/
+
+#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/in.h>
+#include <linux/string.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>
+
+
+/* use 0 for production, 1 for verification, 2..7 for debug */
+#ifndef NET_DEBUG
+#define NET_DEBUG 1
+#endif
+static unsigned int net_debug = NET_DEBUG;
+
+/* A zero-terminated list of common I/O addresses to be probed. */
+static unsigned int netcard_portlist[] =
+ { 0x300, 0x320, 0x340, 0x280, 0};
+
+/*
+ Details of the i82586.
+
+ You'll really need the databook to understand the details of this part,
+ but the outline is that the i82586 has two separate processing units.
+ Both are started from a list of three configuration tables, of which only
+ the last, the System Control Block (SCB), is used after reset-time. The SCB
+ has the following fields:
+ Status word
+ Command word
+ Tx/Command block addr.
+ Rx block addr.
+ The command word accepts the following controls for the Tx and Rx units:
+ */
+
+#define CUC_START 0x0100
+#define CUC_RESUME 0x0200
+#define CUC_SUSPEND 0x0300
+#define RX_START 0x0010
+#define RX_RESUME 0x0020
+#define RX_SUSPEND 0x0030
+
+/* The Rx unit uses a list of frame descriptors and a list of data buffer
+ descriptors. We use full-sized (1518 byte) data buffers, so there is
+ a one-to-one pairing of frame descriptors to buffer descriptors.
+
+ The Tx ("command") unit executes a list of commands that look like:
+ Status word Written by the 82586 when the command is done.
+ Command word Command in lower 3 bits, post-command action in upper 3
+ Link word The address of the next command.
+ Parameters (as needed).
+
+ Some definitions related to the Command Word are:
+ */
+#define CMD_EOL 0x8000 /* The last command of the list, stop. */
+#define CMD_SUSP 0x4000 /* Suspend after doing cmd. */
+#define CMD_INTR 0x2000 /* Interrupt after doing cmd. */
+
+enum commands {
+ CmdNOp = 0, CmdSASetup = 1, CmdConfigure = 2, CmdMulticastList = 3,
+ CmdTx = 4, CmdTDR = 5, CmdDump = 6, CmdDiagnose = 7};
+
+/* Information that need to be kept for each board. */
+struct net_local {
+ struct enet_statistics stats;
+ int last_restart;
+ ushort rx_head;
+ ushort rx_tail;
+ ushort tx_head;
+ ushort tx_cmd_link;
+ ushort tx_reap;
+};
+
+/*
+ Details of the EtherLink16 Implementation
+ The 3c507 is a generic shared-memory i82586 implementation.
+ The host can map 16K, 32K, 48K, or 64K of the 64K memory into
+ 0x0[CD][08]0000, or all 64K into 0xF[02468]0000.
+ */
+
+/* Offsets from the base I/O address. */
+#define SA_DATA 0 /* Station address data, or 3Com signature. */
+#define MISC_CTRL 6 /* Switch the SA_DATA banks, and bus config bits. */
+#define RESET_IRQ 10 /* Reset the latched IRQ line. */
+#define SIGNAL_CA 11 /* Frob the 82586 Channel Attention line. */
+#define ROM_CONFIG 13
+#define MEM_CONFIG 14
+#define IRQ_CONFIG 15
+#define EL16_IO_EXTENT 16
+
+/* The ID port is used at boot-time to locate the ethercard. */
+#define ID_PORT 0x100
+
+/* Offsets to registers in the mailbox (SCB). */
+#define iSCB_STATUS 0x8
+#define iSCB_CMD 0xA
+#define iSCB_CBL 0xC /* Command BLock offset. */
+#define iSCB_RFA 0xE /* Rx Frame Area offset. */
+
+/* Since the 3c507 maps the shared memory window so that the last byte is
+ at 82586 address FFFF, the first byte is at 82586 address 0, 16K, 32K, or
+ 48K corresponding to window sizes of 64K, 48K, 32K and 16K respectively.
+ We can account for this be setting the 'SBC Base' entry in the ISCP table
+ below for all the 16 bit offset addresses, and also adding the 'SCB Base'
+ value to all 24 bit physical addresses (in the SCP table and the TX and RX
+ Buffer Descriptors).
+ -Mark
+ */
+#define SCB_BASE ((unsigned)64*1024 - (dev->mem_end - dev->mem_start))
+
+/*
+ What follows in 'init_words[]' is the "program" that is downloaded to the
+ 82586 memory. It's mostly tables and command blocks, and starts at the
+ reset address 0xfffff6. This is designed to be similar to the EtherExpress,
+ thus the unusual location of the SCB at 0x0008.
+
+ Even with the additional "don't care" values, doing it this way takes less
+ program space than initializing the individual tables, and I feel it's much
+ cleaner.
+
+ The databook is particularly useless for the first two structures, I had
+ to use the Crynwr driver as an example.
+
+ The memory setup is as follows:
+ */
+
+#define CONFIG_CMD 0x0018
+#define SET_SA_CMD 0x0024
+#define SA_OFFSET 0x002A
+#define IDLELOOP 0x30
+#define TDR_CMD 0x38
+#define TDR_TIME 0x3C
+#define DUMP_CMD 0x40
+#define DIAG_CMD 0x48
+#define SET_MC_CMD 0x4E
+#define DUMP_DATA 0x56 /* A 170 byte buffer for dump and Set-MC into. */
+
+#define TX_BUF_START 0x0100
+#define NUM_TX_BUFS 4
+#define TX_BUF_SIZE (1518+14+20+16) /* packet+header+TBD */
+
+#define RX_BUF_START 0x2000
+#define RX_BUF_SIZE (1518+14+18) /* packet+header+RBD */
+#define RX_BUF_END (dev->mem_end - dev->mem_start)
+
+/*
+ That's it: only 86 bytes to set up the beast, including every extra
+ command available. The 170 byte buffer at DUMP_DATA is shared between the
+ Dump command (called only by the diagnostic program) and the SetMulticastList
+ command.
+
+ To complete the memory setup you only have to write the station address at
+ SA_OFFSET and create the Tx & Rx buffer lists.
+
+ The Tx command chain and buffer list is setup as follows:
+ A Tx command table, with the data buffer pointing to...
+ A Tx data buffer descriptor. The packet is in a single buffer, rather than
+ chaining together several smaller buffers.
+ A NoOp command, which initially points to itself,
+ And the packet data.
+
+ A transmit is done by filling in the Tx command table and data buffer,
+ re-writing the NoOp command, and finally changing the offset of the last
+ command to point to the current Tx command. When the Tx command is finished,
+ it jumps to the NoOp, when it loops until the next Tx command changes the
+ "link offset" in the NoOp. This way the 82586 never has to go through the
+ slow restart sequence.
+
+ The Rx buffer list is set up in the obvious ring structure. We have enough
+ memory (and low enough interrupt latency) that we can avoid the complicated
+ Rx buffer linked lists by alway associating a full-size Rx data buffer with
+ each Rx data frame.
+
+ I current use four transmit buffers starting at TX_BUF_START (0x0100), and
+ use the rest of memory, from RX_BUF_START to RX_BUF_END, for Rx buffers.
+
+ */
+
+unsigned short init_words[] = {
+ /* System Configuration Pointer (SCP). */
+ 0x0000, /* Set bus size to 16 bits. */
+ 0,0, /* pad words. */
+ 0x0000,0x0000, /* ISCP phys addr, set in init_82586_mem(). */
+
+ /* Intermediate System Configuration Pointer (ISCP). */
+ 0x0001, /* Status word that's cleared when init is done. */
+ 0x0008,0,0, /* SCB offset, (skip, skip) */
+
+ /* System Control Block (SCB). */
+ 0,0xf000|RX_START|CUC_START, /* SCB status and cmd. */
+ CONFIG_CMD, /* Command list pointer, points to Configure. */
+ RX_BUF_START, /* Rx block list. */
+ 0,0,0,0, /* Error count: CRC, align, buffer, overrun. */
+
+ /* 0x0018: Configure command. Change to put MAC data with packet. */
+ 0, CmdConfigure, /* Status, command. */
+ SET_SA_CMD, /* Next command is Set Station Addr. */
+ 0x0804, /* "4" bytes of config data, 8 byte FIFO. */
+ 0x2e40, /* Magic values, including MAC data location. */
+ 0, /* Unused pad word. */
+
+ /* 0x0024: Setup station address command. */
+ 0, CmdSASetup,
+ SET_MC_CMD, /* Next command. */
+ 0xaa00,0xb000,0x0bad, /* Station address (to be filled in) */
+
+ /* 0x0030: NOP, looping back to itself. Point to first Tx buffer to Tx. */
+ 0, CmdNOp, IDLELOOP, 0 /* pad */,
+
+ /* 0x0038: A unused Time-Domain Reflectometer command. */
+ 0, CmdTDR, IDLELOOP, 0,
+
+ /* 0x0040: An unused Dump State command. */
+ 0, CmdDump, IDLELOOP, DUMP_DATA,
+
+ /* 0x0048: An unused Diagnose command. */
+ 0, CmdDiagnose, IDLELOOP,
+
+ /* 0x004E: An empty set-multicast-list command. */
+ 0, CmdMulticastList, IDLELOOP, 0,
+};
+
+/* Index to functions, as function prototypes. */
+
+extern int el16_probe(struct device *dev); /* Called from Space.c */
+
+static int el16_probe1(struct device *dev, int ioaddr);
+static int el16_open(struct device *dev);
+static int el16_send_packet(struct sk_buff *skb, struct device *dev);
+static void el16_interrupt(int irq, struct pt_regs *regs);
+static void el16_rx(struct device *dev);
+static int el16_close(struct device *dev);
+static struct enet_statistics *el16_get_stats(struct device *dev);
+
+static void hardware_send_packet(struct device *dev, void *buf, short length);
+void init_82586_mem(struct device *dev);
+
+
+#ifdef HAVE_DEVLIST
+struct netdev_entry netcard_drv =
+{"3c507", el16_probe1, EL16_IO_EXTENT, netcard_portlist};
+#endif
+
+/* Check for a network adaptor of this type, and return '0' iff one exists.
+ If dev->base_addr == 0, probe all likely locations.
+ If dev->base_addr == 1, always return failure.
+ If dev->base_addr == 2, (detachable devices only) allocate space for the
+ device and return success.
+ */
+int
+el16_probe(struct device *dev)
+{
+ int base_addr = dev ? dev->base_addr : 0;
+ int i;
+
+ if (base_addr > 0x1ff) /* Check a single specified location. */
+ return el16_probe1(dev, base_addr);
+ else if (base_addr != 0)
+ return ENXIO; /* Don't probe at all. */
+
+ for (i = 0; netcard_portlist[i]; i++) {
+ int ioaddr = netcard_portlist[i];
+ if (check_region(ioaddr, EL16_IO_EXTENT))
+ continue;
+ if (el16_probe1(dev, ioaddr) == 0)
+ return 0;
+ }
+
+ return ENODEV;
+}
+
+int el16_probe1(struct device *dev, int ioaddr)
+{
+ static unsigned char init_ID_done = 0, version_printed = 0;
+ int i, irq, irqval;
+
+ if (init_ID_done == 0) {
+ ushort lrs_state = 0xff;
+ /* Send the ID sequence to the ID_PORT to enable the board(s). */
+ outb(0x00, ID_PORT);
+ for(i = 0; i < 255; i++) {
+ outb(lrs_state, ID_PORT);
+ lrs_state <<= 1;
+ if (lrs_state & 0x100)
+ lrs_state ^= 0xe7;
+ }
+ outb(0x00, ID_PORT);
+ init_ID_done = 1;
+ }
+
+ if (inb(ioaddr) == '*' && inb(ioaddr+1) == '3'
+ && inb(ioaddr+2) == 'C' && inb(ioaddr+3) == 'O')
+ ;
+ else
+ return ENODEV;
+
+ /* Allocate a new 'dev' if needed. */
+ if (dev == NULL)
+ dev = init_etherdev(0, sizeof(struct net_local));
+
+ if (net_debug && version_printed++ == 0)
+ printk(version);
+
+ printk("%s: 3c507 at %#x,", dev->name, ioaddr);
+
+ /* We should make a few more checks here, like the first three octets of
+ the S.A. for the manufacturer's code. */
+
+ irq = inb(ioaddr + IRQ_CONFIG) & 0x0f;
+
+ irqval = request_irq(irq, &el16_interrupt, 0, "3c507");
+ if (irqval) {
+ printk ("unable to get IRQ %d (irqval=%d).\n", irq, irqval);
+ return EAGAIN;
+ }
+
+ /* We've committed to using the board, and can start filling in *dev. */
+ request_region(ioaddr, EL16_IO_EXTENT, "3c507");
+ dev->base_addr = ioaddr;
+
+ outb(0x01, ioaddr + MISC_CTRL);
+ for (i = 0; i < 6; i++) {
+ dev->dev_addr[i] = inb(ioaddr + i);
+ printk(" %02x", dev->dev_addr[i]);
+ }
+
+ if ((dev->mem_start & 0xf) > 0)
+ net_debug = dev->mem_start & 7;
+
+#ifdef MEM_BASE
+ dev->mem_start = MEM_BASE;
+ dev->mem_end = dev->mem_start + 0x10000;
+#else
+ {
+ int base;
+ int size;
+ char mem_config = inb(ioaddr + MEM_CONFIG);
+ if (mem_config & 0x20) {
+ size = 64*1024;
+ base = 0xf00000 + (mem_config & 0x08 ? 0x080000
+ : ((mem_config & 3) << 17));
+ } else {
+ size = ((mem_config & 3) + 1) << 14;
+ base = 0x0c0000 + ( (mem_config & 0x18) << 12);
+ }
+ dev->mem_start = base;
+ dev->mem_end = base + size;
+ }
+#endif
+
+ dev->if_port = (inb(ioaddr + ROM_CONFIG) & 0x80) ? 1 : 0;
+ dev->irq = inb(ioaddr + IRQ_CONFIG) & 0x0f;
+
+ printk(", IRQ %d, %sternal xcvr, memory %#lx-%#lx.\n", dev->irq,
+ dev->if_port ? "ex" : "in", dev->mem_start, dev->mem_end-1);
+
+ if (net_debug)
+ printk(version);
+
+ /* Initialize the device structure. */
+ dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
+ if (dev->priv == NULL)
+ return -ENOMEM;
+ memset(dev->priv, 0, sizeof(struct net_local));
+
+ dev->open = el16_open;
+ dev->stop = el16_close;
+ dev->hard_start_xmit = el16_send_packet;
+ dev->get_stats = el16_get_stats;
+
+ ether_setup(dev); /* Generic ethernet behaviour */
+
+ dev->flags&=~IFF_MULTICAST; /* Multicast doesn't work */
+
+ return 0;
+}
+
+
+
+static int
+el16_open(struct device *dev)
+{
+ irq2dev_map[dev->irq] = dev;
+
+ /* Initialize the 82586 memory and start it. */
+ init_82586_mem(dev);
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+
+ MOD_INC_USE_COUNT;
+
+ return 0;
+}
+
+static int
+el16_send_packet(struct sk_buff *skb, struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+ short *shmem = (short*)dev->mem_start;
+
+ if (dev->tbusy) {
+ /* If we get here, some higher level has decided we are broken.
+ There should really be a "kick me" function call instead. */
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < 5)
+ return 1;
+ if (net_debug > 1)
+ printk("%s: transmit timed out, %s? ", dev->name,
+ shmem[iSCB_STATUS>>1] & 0x8000 ? "IRQ conflict" :
+ "network cable problem");
+ /* Try to restart the adaptor. */
+ if (lp->last_restart == lp->stats.tx_packets) {
+ if (net_debug > 1) printk("Resetting board.\n");
+ /* Completely reset the adaptor. */
+ init_82586_mem(dev);
+ } else {
+ /* Issue the channel attention signal and hope it "gets better". */
+ if (net_debug > 1) printk("Kicking board.\n");
+ shmem[iSCB_CMD>>1] = 0xf000|CUC_START|RX_START;
+ outb(0, ioaddr + SIGNAL_CA); /* Issue channel-attn. */
+ lp->last_restart = lp->stats.tx_packets;
+ }
+ dev->tbusy=0;
+ dev->trans_start = jiffies;
+ }
+
+ /* If some higher layer thinks we've missed an tx-done interrupt
+ we are passed NULL. Caution: dev_tint() handles the cli()/sti()
+ itself. */
+ if (skb == NULL) {
+ dev_tint(dev);
+ return 0;
+ }
+
+ /* Block a timer-based transmit from overlapping. */
+ if (set_bit(0, (void*)&dev->tbusy) != 0)
+ printk("%s: Transmitter access conflict.\n", dev->name);
+ else {
+ short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+ unsigned char *buf = skb->data;
+
+ /* Disable the 82586's input to the interrupt line. */
+ outb(0x80, ioaddr + MISC_CTRL);
+ hardware_send_packet(dev, buf, length);
+ dev->trans_start = jiffies;
+ /* Enable the 82586 interrupt input. */
+ outb(0x84, ioaddr + MISC_CTRL);
+ }
+
+ dev_kfree_skb (skb, FREE_WRITE);
+
+ /* You might need to clean up and record Tx statistics here. */
+
+ return 0;
+}
+
+/* The typical workload of the driver:
+ Handle the network interface interrupts. */
+static void
+el16_interrupt(int irq, struct pt_regs *regs)
+{
+ struct device *dev = (struct device *)(irq2dev_map[irq]);
+ struct net_local *lp;
+ int ioaddr, status, boguscount = 0;
+ ushort ack_cmd = 0;
+ ushort *shmem;
+
+ if (dev == NULL) {
+ printk ("net_interrupt(): irq %d for unknown device.\n", irq);
+ return;
+ }
+ dev->interrupt = 1;
+
+ ioaddr = dev->base_addr;
+ lp = (struct net_local *)dev->priv;
+ shmem = ((ushort*)dev->mem_start);
+
+ status = shmem[iSCB_STATUS>>1];
+
+ if (net_debug > 4) {
+ printk("%s: 3c507 interrupt, status %4.4x.\n", dev->name, status);
+ }
+
+ /* Disable the 82586's input to the interrupt line. */
+ outb(0x80, ioaddr + MISC_CTRL);
+
+ /* Reap the Tx packet buffers. */
+ while (lp->tx_reap != lp->tx_head) {
+ unsigned short tx_status = shmem[lp->tx_reap>>1];
+
+ if (tx_status == 0) {
+ if (net_debug > 5) printk("Couldn't reap %#x.\n", lp->tx_reap);
+ break;
+ }
+ if (tx_status & 0x2000) {
+ lp->stats.tx_packets++;
+ lp->stats.collisions += tx_status & 0xf;
+ dev->tbusy = 0;
+ mark_bh(NET_BH); /* Inform upper layers. */
+ } else {
+ lp->stats.tx_errors++;
+ if (tx_status & 0x0600) lp->stats.tx_carrier_errors++;
+ if (tx_status & 0x0100) lp->stats.tx_fifo_errors++;
+ if (!(tx_status & 0x0040)) lp->stats.tx_heartbeat_errors++;
+ if (tx_status & 0x0020) lp->stats.tx_aborted_errors++;
+ }
+ if (net_debug > 5)
+ printk("Reaped %x, Tx status %04x.\n" , lp->tx_reap, tx_status);
+ lp->tx_reap += TX_BUF_SIZE;
+ if (lp->tx_reap > RX_BUF_START - TX_BUF_SIZE)
+ lp->tx_reap = TX_BUF_START;
+ if (++boguscount > 4)
+ break;
+ }
+
+ if (status & 0x4000) { /* Packet received. */
+ if (net_debug > 5)
+ printk("Received packet, rx_head %04x.\n", lp->rx_head);
+ el16_rx(dev);
+ }
+
+ /* Acknowledge the interrupt sources. */
+ ack_cmd = status & 0xf000;
+
+ if ((status & 0x0700) != 0x0200 && dev->start) {
+ if (net_debug)
+ printk("%s: Command unit stopped, status %04x, restarting.\n",
+ dev->name, status);
+ /* If this ever occurs we should really re-write the idle loop, reset
+ the Tx list, and do a complete restart of the command unit.
+ For now we rely on the Tx timeout if the resume doesn't work. */
+ ack_cmd |= CUC_RESUME;
+ }
+
+ if ((status & 0x0070) != 0x0040 && dev->start) {
+ static void init_rx_bufs(struct device *);
+ /* The Rx unit is not ready, it must be hung. Restart the receiver by
+ initializing the rx buffers, and issuing an Rx start command. */
+ if (net_debug)
+ printk("%s: Rx unit stopped, status %04x, restarting.\n",
+ dev->name, status);
+ init_rx_bufs(dev);
+ shmem[iSCB_RFA >> 1] = RX_BUF_START;
+ ack_cmd |= RX_START;
+ }
+
+ shmem[iSCB_CMD>>1] = ack_cmd;
+ outb(0, ioaddr + SIGNAL_CA); /* Issue channel-attn. */
+
+ /* Clear the latched interrupt. */
+ outb(0, ioaddr + RESET_IRQ);
+
+ /* Enable the 82586's interrupt input. */
+ outb(0x84, ioaddr + MISC_CTRL);
+
+ return;
+}
+
+static int
+el16_close(struct device *dev)
+{
+ int ioaddr = dev->base_addr;
+ ushort *shmem = (short*)dev->mem_start;
+
+ dev->tbusy = 1;
+ dev->start = 0;
+
+ /* Flush the Tx and disable Rx. */
+ shmem[iSCB_CMD >> 1] = RX_SUSPEND | CUC_SUSPEND;
+ outb(0, ioaddr + SIGNAL_CA);
+
+ /* Disable the 82586's input to the interrupt line. */
+ outb(0x80, ioaddr + MISC_CTRL);
+
+ /* We always physically use the IRQ line, so we don't do free_irq().
+ We do remove ourselves from the map. */
+
+ irq2dev_map[dev->irq] = 0;
+
+ /* Update the statistics here. */
+
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+}
+
+/* Get the current statistics. This may be called with the card open or
+ closed. */
+static struct enet_statistics *
+el16_get_stats(struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+
+ /* ToDo: decide if there are any useful statistics from the SCB. */
+
+ return &lp->stats;
+}
+
+/* Initialize the Rx-block list. */
+static void
+init_rx_bufs(struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ unsigned short *write_ptr;
+ unsigned short SCB_base = SCB_BASE;
+
+ int cur_rxbuf = lp->rx_head = RX_BUF_START;
+
+ /* Initialize each Rx frame + data buffer. */
+ do { /* While there is room for one more. */
+
+ write_ptr = (unsigned short *)(dev->mem_start + cur_rxbuf);
+
+ *write_ptr++ = 0x0000; /* Status */
+ *write_ptr++ = 0x0000; /* Command */
+ *write_ptr++ = cur_rxbuf + RX_BUF_SIZE; /* Link */
+ *write_ptr++ = cur_rxbuf + 22; /* Buffer offset */
+ *write_ptr++ = 0x0000; /* Pad for dest addr. */
+ *write_ptr++ = 0x0000;
+ *write_ptr++ = 0x0000;
+ *write_ptr++ = 0x0000; /* Pad for source addr. */
+ *write_ptr++ = 0x0000;
+ *write_ptr++ = 0x0000;
+ *write_ptr++ = 0x0000; /* Pad for protocol. */
+
+ *write_ptr++ = 0x0000; /* Buffer: Actual count */
+ *write_ptr++ = -1; /* Buffer: Next (none). */
+ *write_ptr++ = cur_rxbuf + 0x20 + SCB_base; /* Buffer: Address low */
+ *write_ptr++ = 0x0000;
+ /* Finally, the number of bytes in the buffer. */
+ *write_ptr++ = 0x8000 + RX_BUF_SIZE-0x20;
+
+ lp->rx_tail = cur_rxbuf;
+ cur_rxbuf += RX_BUF_SIZE;
+ } while (cur_rxbuf <= RX_BUF_END - RX_BUF_SIZE);
+
+ /* Terminate the list by setting the EOL bit, and wrap the pointer to make
+ the list a ring. */
+ write_ptr = (unsigned short *)
+ (dev->mem_start + lp->rx_tail + 2);
+ *write_ptr++ = 0xC000; /* Command, mark as last. */
+ *write_ptr++ = lp->rx_head; /* Link */
+
+}
+
+void
+init_82586_mem(struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ short ioaddr = dev->base_addr;
+ ushort *shmem = (short*)dev->mem_start;
+
+ /* Enable loopback to protect the wire while starting up,
+ and hold the 586 in reset during the memory initialization. */
+ outb(0x20, ioaddr + MISC_CTRL);
+
+ /* Fix the ISCP address and base. */
+ init_words[3] = SCB_BASE;
+ init_words[7] = SCB_BASE;
+
+ /* Write the words at 0xfff6 (address-aliased to 0xfffff6). */
+ memcpy((void*)dev->mem_end-10, init_words, 10);
+
+ /* Write the words at 0x0000. */
+ memcpy((char*)dev->mem_start, init_words + 5, sizeof(init_words) - 10);
+
+ /* Fill in the station address. */
+ memcpy((char*)dev->mem_start+SA_OFFSET, dev->dev_addr,
+ sizeof(dev->dev_addr));
+
+ /* The Tx-block list is written as needed. We just set up the values. */
+ lp->tx_cmd_link = IDLELOOP + 4;
+ lp->tx_head = lp->tx_reap = TX_BUF_START;
+
+ init_rx_bufs(dev);
+
+ /* Start the 586 by releasing the reset line, but leave loopback. */
+ outb(0xA0, ioaddr + MISC_CTRL);
+
+ /* This was time consuming to track down: you need to give two channel
+ attention signals to reliably start up the i82586. */
+ outb(0, ioaddr + SIGNAL_CA);
+
+ {
+ int boguscnt = 50;
+ while (shmem[iSCB_STATUS>>1] == 0)
+ if (--boguscnt == 0) {
+ printk("%s: i82586 initialization timed out with status %04x,"
+ "cmd %04x.\n", dev->name,
+ shmem[iSCB_STATUS>>1], shmem[iSCB_CMD>>1]);
+ break;
+ }
+ /* Issue channel-attn -- the 82586 won't start. */
+ outb(0, ioaddr + SIGNAL_CA);
+ }
+
+ /* Disable loopback and enable interrupts. */
+ outb(0x84, ioaddr + MISC_CTRL);
+ if (net_debug > 4)
+ printk("%s: Initialized 82586, status %04x.\n", dev->name,
+ shmem[iSCB_STATUS>>1]);
+ return;
+}
+
+static void
+hardware_send_packet(struct device *dev, void *buf, short length)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ short ioaddr = dev->base_addr;
+ ushort tx_block = lp->tx_head;
+ ushort *write_ptr = (ushort *)(dev->mem_start + tx_block);
+
+ /* Set the write pointer to the Tx block, and put out the header. */
+ *write_ptr++ = 0x0000; /* Tx status */
+ *write_ptr++ = CMD_INTR|CmdTx; /* Tx command */
+ *write_ptr++ = tx_block+16; /* Next command is a NoOp. */
+ *write_ptr++ = tx_block+8; /* Data Buffer offset. */
+
+ /* Output the data buffer descriptor. */
+ *write_ptr++ = length | 0x8000; /* Byte count parameter. */
+ *write_ptr++ = -1; /* No next data buffer. */
+ *write_ptr++ = tx_block+22+SCB_BASE;/* Buffer follows the NoOp command. */
+ *write_ptr++ = 0x0000; /* Buffer address high bits (always zero). */
+
+ /* Output the Loop-back NoOp command. */
+ *write_ptr++ = 0x0000; /* Tx status */
+ *write_ptr++ = CmdNOp; /* Tx command */
+ *write_ptr++ = tx_block+16; /* Next is myself. */
+
+ /* Output the packet at the write pointer. */
+ memcpy(write_ptr, buf, length);
+
+ /* Set the old command link pointing to this send packet. */
+ *(ushort*)(dev->mem_start + lp->tx_cmd_link) = tx_block;
+ lp->tx_cmd_link = tx_block + 20;
+
+ /* Set the next free tx region. */
+ lp->tx_head = tx_block + TX_BUF_SIZE;
+ if (lp->tx_head > RX_BUF_START - TX_BUF_SIZE)
+ lp->tx_head = TX_BUF_START;
+
+ if (net_debug > 4) {
+ printk("%s: 3c507 @%x send length = %d, tx_block %3x, next %3x.\n",
+ dev->name, ioaddr, length, tx_block, lp->tx_head);
+ }
+
+ if (lp->tx_head != lp->tx_reap)
+ dev->tbusy = 0;
+}
+
+static void
+el16_rx(struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ short *shmem = (short*)dev->mem_start;
+ ushort rx_head = lp->rx_head;
+ ushort rx_tail = lp->rx_tail;
+ ushort boguscount = 10;
+ short frame_status;
+
+ while ((frame_status = shmem[rx_head>>1]) < 0) { /* Command complete */
+ ushort *read_frame = (short *)(dev->mem_start + rx_head);
+ ushort rfd_cmd = read_frame[1];
+ ushort next_rx_frame = read_frame[2];
+ ushort data_buffer_addr = read_frame[3];
+ ushort *data_frame = (short *)(dev->mem_start + data_buffer_addr);
+ ushort pkt_len = data_frame[0];
+
+ if (rfd_cmd != 0 || data_buffer_addr != rx_head + 22
+ || (pkt_len & 0xC000) != 0xC000) {
+ printk("%s: Rx frame at %#x corrupted, status %04x cmd %04x"
+ "next %04x data-buf @%04x %04x.\n", dev->name, rx_head,
+ frame_status, rfd_cmd, next_rx_frame, data_buffer_addr,
+ pkt_len);
+ } else if ((frame_status & 0x2000) == 0) {
+ /* Frame Rxed, but with error. */
+ lp->stats.rx_errors++;
+ if (frame_status & 0x0800) lp->stats.rx_crc_errors++;
+ if (frame_status & 0x0400) lp->stats.rx_frame_errors++;
+ if (frame_status & 0x0200) lp->stats.rx_fifo_errors++;
+ if (frame_status & 0x0100) lp->stats.rx_over_errors++;
+ if (frame_status & 0x0080) lp->stats.rx_length_errors++;
+ } else {
+ /* Malloc up new buffer. */
+ struct sk_buff *skb;
+
+ pkt_len &= 0x3fff;
+ skb = dev_alloc_skb(pkt_len+2);
+ if (skb == NULL) {
+ printk("%s: Memory squeeze, dropping packet.\n", dev->name);
+ lp->stats.rx_dropped++;
+ break;
+ }
+
+ skb_reserve(skb,2);
+ skb->dev = dev;
+
+ /* 'skb->data' points to the start of sk_buff data area. */
+ memcpy(skb_put(skb,pkt_len), data_frame + 5, pkt_len);
+
+ skb->protocol=eth_type_trans(skb,dev);
+ netif_rx(skb);
+ lp->stats.rx_packets++;
+ }
+
+ /* Clear the status word and set End-of-List on the rx frame. */
+ read_frame[0] = 0;
+ read_frame[1] = 0xC000;
+ /* Clear the end-of-list on the prev. RFD. */
+ *(short*)(dev->mem_start + rx_tail + 2) = 0x0000;
+
+ rx_tail = rx_head;
+ rx_head = next_rx_frame;
+ if (--boguscount == 0)
+ break;
+ }
+
+ lp->rx_head = rx_head;
+ lp->rx_tail = rx_tail;
+}
+#ifdef MODULE
+static char devicename[9] = { 0, };
+static struct device dev_3c507 = {
+ devicename, /* device name is inserted by linux/drivers/net/net_init.c */
+ 0, 0, 0, 0,
+ 0, 0,
+ 0, 0, 0, NULL, el16_probe
+};
+
+static int io = 0x300;
+static int irq = 0;
+
+int init_module(void)
+{
+ if (io == 0)
+ printk("3c507: You should not use auto-probing with insmod!\n");
+ dev_3c507.base_addr = io;
+ dev_3c507.irq = irq;
+ if (register_netdev(&dev_3c507) != 0) {
+ printk("3c507: register_netdev() returned non-zero.\n");
+ return -EIO;
+ }
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ unregister_netdev(&dev_3c507);
+ kfree(dev_3c507.priv);
+ dev_3c507.priv = NULL;
+
+ /* If we don't do this, we can't re-insmod it later. */
+ free_irq(dev_3c507.irq);
+ release_region(dev_3c507.base_addr, EL16_IO_EXTENT);
+}
+#endif /* MODULE */
+
+/*
+ * Local variables:
+ * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -I/usr/src/linux/drivers/net -Wall -Wstrict-prototypes -O6 -m486 -c 3c507.c"
+ * version-control: t
+ * kept-new-versions: 5
+ * tab-width: 4
+ * c-indent-level: 4
+ * End:
+ */
diff --git a/i386/i386at/gpl/linux/net/3c509.c b/i386/i386at/gpl/linux/net/3c509.c
new file mode 100644
index 00000000..5e7dce4f
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/3c509.c
@@ -0,0 +1,739 @@
+/* 3c509.c: A 3c509 EtherLink3 ethernet driver for linux. */
+/*
+ Written 1993,1994 by Donald Becker.
+
+ Copyright 1994 by Donald Becker.
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency. This software may be used and
+ distributed according to the terms of the GNU Public License,
+ incorporated herein by reference.
+
+ This driver is for the 3Com EtherLinkIII series.
+
+ The author may be reached as becker@cesdis.gsfc.nasa.gov or
+ C/O Center of Excellence in Space Data and Information Sciences
+ Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
+
+ Known limitations:
+ Because of the way 3c509 ISA detection works it's difficult to predict
+ a priori which of several ISA-mode cards will be detected first.
+
+ This driver does not use predictive interrupt mode, resulting in higher
+ packet latency but lower overhead. If interrupts are disabled for an
+ unusually long time it could also result in missed packets, but in
+ practice this rarely happens.
+*/
+
+static const char *version = "3c509.c:1.03 10/8/94 becker@cesdis.gsfc.nasa.gov\n";
+
+#include <linux/module.h>
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/ioport.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/config.h> /* for CONFIG_MCA */
+
+#include <asm/bitops.h>
+#include <asm/io.h>
+
+
+#ifdef EL3_DEBUG
+int el3_debug = EL3_DEBUG;
+#else
+int el3_debug = 2;
+#endif
+
+/* To minimize the size of the driver source I only define operating
+ constants if they are used several times. You'll need the manual
+ if you want to understand driver details. */
+/* Offsets from base I/O address. */
+#define EL3_DATA 0x00
+#define EL3_CMD 0x0e
+#define EL3_STATUS 0x0e
+#define ID_PORT 0x100
+#define EEPROM_READ 0x80
+
+#define EL3_IO_EXTENT 16
+
+#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD)
+
+
+/* The top five bits written to EL3_CMD are a command, the lower
+ 11 bits are the parameter, if applicable. */
+enum c509cmd {
+ TotalReset = 0<<11, SelectWindow = 1<<11, StartCoax = 2<<11,
+ RxDisable = 3<<11, RxEnable = 4<<11, RxReset = 5<<11, RxDiscard = 8<<11,
+ TxEnable = 9<<11, TxDisable = 10<<11, TxReset = 11<<11,
+ FakeIntr = 12<<11, AckIntr = 13<<11, SetIntrMask = 14<<11,
+ SetReadZero = 15<<11, SetRxFilter = 16<<11, SetRxThreshold = 17<<11,
+ SetTxThreshold = 18<<11, SetTxStart = 19<<11, StatsEnable = 21<<11,
+ StatsDisable = 22<<11, StopCoax = 23<<11,};
+
+/* The SetRxFilter command accepts the following classes: */
+enum RxFilter {
+ RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8 };
+
+/* Register window 1 offsets, the window used in normal operation. */
+#define TX_FIFO 0x00
+#define RX_FIFO 0x00
+#define RX_STATUS 0x08
+#define TX_STATUS 0x0B
+#define TX_FREE 0x0C /* Remaining free bytes in Tx buffer. */
+
+#define WN0_IRQ 0x08 /* Window 0: Set IRQ line in bits 12-15. */
+#define WN4_MEDIA 0x0A /* Window 4: Various transcvr/media bits. */
+#define MEDIA_TP 0x00C0 /* Enable link beat and jabber for 10baseT. */
+
+struct el3_private {
+ struct enet_statistics stats;
+};
+
+static ushort id_read_eeprom(int index);
+static ushort read_eeprom(short ioaddr, int index);
+static int el3_open(struct device *dev);
+static int el3_start_xmit(struct sk_buff *skb, struct device *dev);
+static void el3_interrupt(int irq, struct pt_regs *regs);
+static void update_stats(int addr, struct device *dev);
+static struct enet_statistics *el3_get_stats(struct device *dev);
+static int el3_rx(struct device *dev);
+static int el3_close(struct device *dev);
+static void set_multicast_list(struct device *dev);
+
+
+
+int el3_probe(struct device *dev)
+{
+ short lrs_state = 0xff, i;
+ ushort ioaddr, irq, if_port;
+ short *phys_addr = (short *)dev->dev_addr;
+ static int current_tag = 0;
+
+ /* First check all slots of the EISA bus. The next slot address to
+ probe is kept in 'eisa_addr' to support multiple probe() calls. */
+ if (EISA_bus) {
+ static int eisa_addr = 0x1000;
+ while (eisa_addr < 0x9000) {
+ ioaddr = eisa_addr;
+ eisa_addr += 0x1000;
+
+ /* Check the standard EISA ID register for an encoded '3Com'. */
+ if (inw(ioaddr + 0xC80) != 0x6d50)
+ continue;
+
+ /* Change the register set to the configuration window 0. */
+ outw(SelectWindow | 0, ioaddr + 0xC80 + EL3_CMD);
+
+ irq = inw(ioaddr + WN0_IRQ) >> 12;
+ if_port = inw(ioaddr + 6)>>14;
+ for (i = 0; i < 3; i++)
+ phys_addr[i] = htons(read_eeprom(ioaddr, i));
+
+ /* Restore the "Product ID" to the EEPROM read register. */
+ read_eeprom(ioaddr, 3);
+
+ /* Was the EISA code an add-on hack? Nahhhhh... */
+ goto found;
+ }
+ }
+
+#ifdef CONFIG_MCA
+ if (MCA_bus) {
+ mca_adaptor_select_mode(1);
+ for (i = 0; i < 8; i++)
+ if ((mca_adaptor_id(i) | 1) == 0x627c) {
+ ioaddr = mca_pos_base_addr(i);
+ irq = inw(ioaddr + WN0_IRQ) >> 12;
+ if_port = inw(ioaddr + 6)>>14;
+ for (i = 0; i < 3; i++)
+ phys_addr[i] = htons(read_eeprom(ioaddr, i));
+
+ mca_adaptor_select_mode(0);
+ goto found;
+ }
+ mca_adaptor_select_mode(0);
+
+ }
+#endif
+
+ /* Next check for all ISA bus boards by sending the ID sequence to the
+ ID_PORT. We find cards past the first by setting the 'current_tag'
+ on cards as they are found. Cards with their tag set will not
+ respond to subsequent ID sequences. */
+
+ if (check_region(ID_PORT,1)) {
+ static int once = 1;
+ if (once) printk("3c509: Somebody has reserved 0x%x, can't do ID_PORT lookup, nor card auto-probing\n",ID_PORT);
+ once = 0;
+ return -ENODEV;
+ }
+
+ outb(0x00, ID_PORT);
+ outb(0x00, ID_PORT);
+ for(i = 0; i < 255; i++) {
+ outb(lrs_state, ID_PORT);
+ lrs_state <<= 1;
+ lrs_state = lrs_state & 0x100 ? lrs_state ^ 0xcf : lrs_state;
+ }
+
+ /* For the first probe, clear all board's tag registers. */
+ if (current_tag == 0)
+ outb(0xd0, ID_PORT);
+ else /* Otherwise kill off already-found boards. */
+ outb(0xd8, ID_PORT);
+
+ if (id_read_eeprom(7) != 0x6d50) {
+ return -ENODEV;
+ }
+
+ /* Read in EEPROM data, which does contention-select.
+ Only the lowest address board will stay "on-line".
+ 3Com got the byte order backwards. */
+ for (i = 0; i < 3; i++) {
+ phys_addr[i] = htons(id_read_eeprom(i));
+ }
+
+ {
+ unsigned short iobase = id_read_eeprom(8);
+ if_port = iobase >> 14;
+ ioaddr = 0x200 + ((iobase & 0x1f) << 4);
+ }
+ irq = id_read_eeprom(9) >> 12;
+
+ if (dev->base_addr != 0
+ && dev->base_addr != (unsigned short)ioaddr) {
+ return -ENODEV;
+ }
+
+ /* Set the adaptor tag so that the next card can be found. */
+ outb(0xd0 + ++current_tag, ID_PORT);
+
+ /* Activate the adaptor at the EEPROM location. */
+ outb(0xff, ID_PORT);
+
+ EL3WINDOW(0);
+ if (inw(ioaddr) != 0x6d50)
+ return -ENODEV;
+
+ /* Free the interrupt so that some other card can use it. */
+ outw(0x0f00, ioaddr + WN0_IRQ);
+ found:
+ dev->base_addr = ioaddr;
+ dev->irq = irq;
+ dev->if_port = if_port;
+ request_region(dev->base_addr, EL3_IO_EXTENT, "3c509");
+
+ {
+ const char *if_names[] = {"10baseT", "AUI", "undefined", "BNC"};
+ printk("%s: 3c509 at %#3.3lx tag %d, %s port, address ",
+ dev->name, dev->base_addr, current_tag, if_names[dev->if_port]);
+ }
+
+ /* Read in the station address. */
+ for (i = 0; i < 6; i++)
+ printk(" %2.2x", dev->dev_addr[i]);
+ printk(", IRQ %d.\n", dev->irq);
+
+ /* Make up a EL3-specific-data structure. */
+ dev->priv = kmalloc(sizeof(struct el3_private), GFP_KERNEL);
+ if (dev->priv == NULL)
+ return -ENOMEM;
+ memset(dev->priv, 0, sizeof(struct el3_private));
+
+ if (el3_debug > 0)
+ printk(version);
+
+ /* The EL3-specific entries in the device structure. */
+ dev->open = &el3_open;
+ dev->hard_start_xmit = &el3_start_xmit;
+ dev->stop = &el3_close;
+ dev->get_stats = &el3_get_stats;
+ dev->set_multicast_list = &set_multicast_list;
+
+ /* Fill in the generic fields of the device structure. */
+ ether_setup(dev);
+ return 0;
+}
+
+/* Read a word from the EEPROM using the regular EEPROM access register.
+ Assume that we are in register window zero.
+ */
+static ushort read_eeprom(short ioaddr, int index)
+{
+ int timer;
+
+ outw(EEPROM_READ + index, ioaddr + 10);
+ /* Pause for at least 162 us. for the read to take place. */
+ for (timer = 0; timer < 162*4 + 400; timer++)
+ SLOW_DOWN_IO;
+ return inw(ioaddr + 12);
+}
+
+/* Read a word from the EEPROM when in the ISA ID probe state. */
+static ushort id_read_eeprom(int index)
+{
+ int timer, bit, word = 0;
+
+ /* Issue read command, and pause for at least 162 us. for it to complete.
+ Assume extra-fast 16Mhz bus. */
+ outb(EEPROM_READ + index, ID_PORT);
+
+ /* This should really be done by looking at one of the timer channels. */
+ for (timer = 0; timer < 162*4 + 400; timer++)
+ SLOW_DOWN_IO;
+
+ for (bit = 15; bit >= 0; bit--)
+ word = (word << 1) + (inb(ID_PORT) & 0x01);
+
+ if (el3_debug > 3)
+ printk(" 3c509 EEPROM word %d %#4.4x.\n", index, word);
+
+ return word;
+}
+
+
+
+static int
+el3_open(struct device *dev)
+{
+ int ioaddr = dev->base_addr;
+ int i;
+
+ outw(TxReset, ioaddr + EL3_CMD);
+ outw(RxReset, ioaddr + EL3_CMD);
+ outw(SetReadZero | 0x00, ioaddr + EL3_CMD);
+
+ if (request_irq(dev->irq, &el3_interrupt, 0, "3c509")) {
+ return -EAGAIN;
+ }
+
+ EL3WINDOW(0);
+ if (el3_debug > 3)
+ printk("%s: Opening, IRQ %d status@%x %4.4x.\n", dev->name,
+ dev->irq, ioaddr + EL3_STATUS, inw(ioaddr + EL3_STATUS));
+
+ /* Activate board: this is probably unnecessary. */
+ outw(0x0001, ioaddr + 4);
+
+ irq2dev_map[dev->irq] = dev;
+
+ /* Set the IRQ line. */
+ outw((dev->irq << 12) | 0x0f00, ioaddr + WN0_IRQ);
+
+ /* Set the station address in window 2 each time opened. */
+ EL3WINDOW(2);
+
+ for (i = 0; i < 6; i++)
+ outb(dev->dev_addr[i], ioaddr + i);
+
+ if (dev->if_port == 3)
+ /* Start the thinnet transceiver. We should really wait 50ms...*/
+ outw(StartCoax, ioaddr + EL3_CMD);
+ else if (dev->if_port == 0) {
+ /* 10baseT interface, enabled link beat and jabber check. */
+ EL3WINDOW(4);
+ outw(inw(ioaddr + WN4_MEDIA) | MEDIA_TP, ioaddr + WN4_MEDIA);
+ }
+
+ /* Switch to the stats window, and clear all stats by reading. */
+ outw(StatsDisable, ioaddr + EL3_CMD);
+ EL3WINDOW(6);
+ for (i = 0; i < 9; i++)
+ inb(ioaddr + i);
+ inb(ioaddr + 10);
+ inb(ioaddr + 12);
+
+ /* Switch to register set 1 for normal use. */
+ EL3WINDOW(1);
+
+ /* Accept b-case and phys addr only. */
+ outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD);
+ outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */
+
+ dev->interrupt = 0;
+ dev->tbusy = 0;
+ dev->start = 1;
+
+ outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */
+ outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */
+ /* Allow status bits to be seen. */
+ outw(SetReadZero | 0xff, ioaddr + EL3_CMD);
+ outw(AckIntr | 0x69, ioaddr + EL3_CMD); /* Ack IRQ */
+ outw(SetIntrMask | 0x98, ioaddr + EL3_CMD); /* Set interrupt mask. */
+
+ if (el3_debug > 3)
+ printk("%s: Opened 3c509 IRQ %d status %4.4x.\n",
+ dev->name, dev->irq, inw(ioaddr + EL3_STATUS));
+
+ MOD_INC_USE_COUNT;
+ return 0; /* Always succeed */
+}
+
+static int
+el3_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+ struct el3_private *lp = (struct el3_private *)dev->priv;
+ int ioaddr = dev->base_addr;
+
+ /* Transmitter timeout, serious problems. */
+ if (dev->tbusy) {
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < 10)
+ return 1;
+ printk("%s: transmit timed out, tx_status %2.2x status %4.4x.\n",
+ dev->name, inb(ioaddr + TX_STATUS), inw(ioaddr + EL3_STATUS));
+ dev->trans_start = jiffies;
+ /* Issue TX_RESET and TX_START commands. */
+ outw(TxReset, ioaddr + EL3_CMD);
+ outw(TxEnable, ioaddr + EL3_CMD);
+ dev->tbusy = 0;
+ }
+
+ if (skb == NULL) {
+ dev_tint(dev);
+ return 0;
+ }
+
+ if (skb->len <= 0)
+ return 0;
+
+ if (el3_debug > 4) {
+ printk("%s: el3_start_xmit(length = %ld) called, status %4.4x.\n",
+ dev->name, skb->len, inw(ioaddr + EL3_STATUS));
+ }
+#ifndef final_version
+ { /* Error-checking code, delete for 1.30. */
+ ushort status = inw(ioaddr + EL3_STATUS);
+ if (status & 0x0001 /* IRQ line active, missed one. */
+ && inw(ioaddr + EL3_STATUS) & 1) { /* Make sure. */
+ printk("%s: Missed interrupt, status then %04x now %04x"
+ " Tx %2.2x Rx %4.4x.\n", dev->name, status,
+ inw(ioaddr + EL3_STATUS), inb(ioaddr + TX_STATUS),
+ inw(ioaddr + RX_STATUS));
+ /* Fake interrupt trigger by masking, acknowledge interrupts. */
+ outw(SetReadZero | 0x00, ioaddr + EL3_CMD);
+ outw(AckIntr | 0x69, ioaddr + EL3_CMD); /* Ack IRQ */
+ outw(SetReadZero | 0xff, ioaddr + EL3_CMD);
+ }
+ }
+#endif
+
+ /* Avoid timer-based retransmission conflicts. */
+ if (set_bit(0, (void*)&dev->tbusy) != 0)
+ printk("%s: Transmitter access conflict.\n", dev->name);
+ else {
+ /* Put out the doubleword header... */
+ outw(skb->len, ioaddr + TX_FIFO);
+ outw(0x00, ioaddr + TX_FIFO);
+ /* ... and the packet rounded to a doubleword. */
+ outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
+
+ dev->trans_start = jiffies;
+ if (inw(ioaddr + TX_FREE) > 1536) {
+ dev->tbusy = 0;
+ } else
+ /* Interrupt us when the FIFO has room for max-sized packet. */
+ outw(SetTxThreshold + 1536, ioaddr + EL3_CMD);
+ }
+
+ dev_kfree_skb (skb, FREE_WRITE);
+
+ /* Clear the Tx status stack. */
+ {
+ short tx_status;
+ int i = 4;
+
+ while (--i > 0 && (tx_status = inb(ioaddr + TX_STATUS)) > 0) {
+ if (tx_status & 0x38) lp->stats.tx_aborted_errors++;
+ if (tx_status & 0x30) outw(TxReset, ioaddr + EL3_CMD);
+ if (tx_status & 0x3C) outw(TxEnable, ioaddr + EL3_CMD);
+ outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */
+ }
+ }
+ return 0;
+}
+
+/* The EL3 interrupt handler. */
+static void
+el3_interrupt(int irq, struct pt_regs *regs)
+{
+ struct device *dev = (struct device *)(irq2dev_map[irq]);
+ int ioaddr, status;
+ int i = 0;
+
+ if (dev == NULL) {
+ printk ("el3_interrupt(): irq %d for unknown device.\n", irq);
+ return;
+ }
+
+ if (dev->interrupt)
+ printk("%s: Re-entering the interrupt handler.\n", dev->name);
+ dev->interrupt = 1;
+
+ ioaddr = dev->base_addr;
+ status = inw(ioaddr + EL3_STATUS);
+
+ if (el3_debug > 4)
+ printk("%s: interrupt, status %4.4x.\n", dev->name, status);
+
+ while ((status = inw(ioaddr + EL3_STATUS)) & 0x91) {
+
+ if (status & 0x10)
+ el3_rx(dev);
+
+ if (status & 0x08) {
+ if (el3_debug > 5)
+ printk(" TX room bit was handled.\n");
+ /* There's room in the FIFO for a full-sized packet. */
+ outw(AckIntr | 0x08, ioaddr + EL3_CMD);
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+ }
+ if (status & 0x80) /* Statistics full. */
+ update_stats(ioaddr, dev);
+
+ if (++i > 10) {
+ printk("%s: Infinite loop in interrupt, status %4.4x.\n",
+ dev->name, status);
+ /* Clear all interrupts. */
+ outw(AckIntr | 0xFF, ioaddr + EL3_CMD);
+ break;
+ }
+ /* Acknowledge the IRQ. */
+ outw(AckIntr | 0x41, ioaddr + EL3_CMD); /* Ack IRQ */
+
+ }
+
+ if (el3_debug > 4) {
+ printk("%s: exiting interrupt, status %4.4x.\n", dev->name,
+ inw(ioaddr + EL3_STATUS));
+ }
+
+ dev->interrupt = 0;
+ return;
+}
+
+
+static struct enet_statistics *
+el3_get_stats(struct device *dev)
+{
+ struct el3_private *lp = (struct el3_private *)dev->priv;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ update_stats(dev->base_addr, dev);
+ restore_flags(flags);
+ return &lp->stats;
+}
+
+/* Update statistics. We change to register window 6, so this should be run
+ single-threaded if the device is active. This is expected to be a rare
+ operation, and it's simpler for the rest of the driver to assume that
+ window 1 is always valid rather than use a special window-state variable.
+ */
+static void update_stats(int ioaddr, struct device *dev)
+{
+ struct el3_private *lp = (struct el3_private *)dev->priv;
+
+ if (el3_debug > 5)
+ printk(" Updating the statistics.\n");
+ /* Turn off statistics updates while reading. */
+ outw(StatsDisable, ioaddr + EL3_CMD);
+ /* Switch to the stats window, and read everything. */
+ EL3WINDOW(6);
+ lp->stats.tx_carrier_errors += inb(ioaddr + 0);
+ lp->stats.tx_heartbeat_errors += inb(ioaddr + 1);
+ /* Multiple collisions. */ inb(ioaddr + 2);
+ lp->stats.collisions += inb(ioaddr + 3);
+ lp->stats.tx_window_errors += inb(ioaddr + 4);
+ lp->stats.rx_fifo_errors += inb(ioaddr + 5);
+ lp->stats.tx_packets += inb(ioaddr + 6);
+ /* Rx packets */ inb(ioaddr + 7);
+ /* Tx deferrals */ inb(ioaddr + 8);
+ inw(ioaddr + 10); /* Total Rx and Tx octets. */
+ inw(ioaddr + 12);
+
+ /* Back to window 1, and turn statistics back on. */
+ EL3WINDOW(1);
+ outw(StatsEnable, ioaddr + EL3_CMD);
+ return;
+}
+
+static int
+el3_rx(struct device *dev)
+{
+ struct el3_private *lp = (struct el3_private *)dev->priv;
+ int ioaddr = dev->base_addr;
+ short rx_status;
+
+ if (el3_debug > 5)
+ printk(" In rx_packet(), status %4.4x, rx_status %4.4x.\n",
+ inw(ioaddr+EL3_STATUS), inw(ioaddr+RX_STATUS));
+ while ((rx_status = inw(ioaddr + RX_STATUS)) > 0) {
+ if (rx_status & 0x4000) { /* Error, update stats. */
+ short error = rx_status & 0x3800;
+ lp->stats.rx_errors++;
+ switch (error) {
+ case 0x0000: lp->stats.rx_over_errors++; break;
+ case 0x0800: lp->stats.rx_length_errors++; break;
+ case 0x1000: lp->stats.rx_frame_errors++; break;
+ case 0x1800: lp->stats.rx_length_errors++; break;
+ case 0x2000: lp->stats.rx_frame_errors++; break;
+ case 0x2800: lp->stats.rx_crc_errors++; break;
+ }
+ } else {
+ short pkt_len = rx_status & 0x7ff;
+ struct sk_buff *skb;
+
+ skb = dev_alloc_skb(pkt_len+5);
+ if (el3_debug > 4)
+ printk("Receiving packet size %d status %4.4x.\n",
+ pkt_len, rx_status);
+ if (skb != NULL) {
+ skb->dev = dev;
+ skb_reserve(skb,2); /* Align IP on 16 byte boundaries */
+
+ /* 'skb->data' points to the start of sk_buff data area. */
+ insl(ioaddr+RX_FIFO, skb_put(skb,pkt_len),
+ (pkt_len + 3) >> 2);
+
+ skb->protocol=eth_type_trans(skb,dev);
+ netif_rx(skb);
+ outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */
+ lp->stats.rx_packets++;
+ continue;
+ } else if (el3_debug)
+ printk("%s: Couldn't allocate a sk_buff of size %d.\n",
+ dev->name, pkt_len);
+ }
+ lp->stats.rx_dropped++;
+ outw(RxDiscard, ioaddr + EL3_CMD);
+ while (inw(ioaddr + EL3_STATUS) & 0x1000)
+ printk(" Waiting for 3c509 to discard packet, status %x.\n",
+ inw(ioaddr + EL3_STATUS) );
+ }
+
+ return 0;
+}
+
+/*
+ * Set or clear the multicast filter for this adaptor.
+ */
+
+static void set_multicast_list(struct device *dev)
+{
+ short ioaddr = dev->base_addr;
+ if (el3_debug > 1) {
+ static int old = 0;
+ if (old != dev->mc_count) {
+ old = dev->mc_count;
+ printk("%s: Setting Rx mode to %d addresses.\n", dev->name, dev->mc_count);
+ }
+ }
+ if (dev->flags&IFF_PROMISC)
+ {
+ outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast | RxProm,
+ ioaddr + EL3_CMD);
+ }
+ else if (dev->mc_count || (dev->flags&IFF_ALLMULTI))
+ {
+ outw(SetRxFilter|RxStation|RxMulticast|RxBroadcast, ioaddr + EL3_CMD);
+ }
+ else
+ outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD);
+}
+
+static int
+el3_close(struct device *dev)
+{
+ int ioaddr = dev->base_addr;
+
+ if (el3_debug > 2)
+ printk("%s: Shutting down ethercard.\n", dev->name);
+
+ dev->tbusy = 1;
+ dev->start = 0;
+
+ /* Turn off statistics ASAP. We update lp->stats below. */
+ outw(StatsDisable, ioaddr + EL3_CMD);
+
+ /* Disable the receiver and transmitter. */
+ outw(RxDisable, ioaddr + EL3_CMD);
+ outw(TxDisable, ioaddr + EL3_CMD);
+
+ if (dev->if_port == 3)
+ /* Turn off thinnet power. Green! */
+ outw(StopCoax, ioaddr + EL3_CMD);
+ else if (dev->if_port == 0) {
+ /* Disable link beat and jabber, if_port may change ere next open(). */
+ EL3WINDOW(4);
+ outw(inw(ioaddr + WN4_MEDIA) & ~MEDIA_TP, ioaddr + WN4_MEDIA);
+ }
+
+ free_irq(dev->irq);
+ /* Switching back to window 0 disables the IRQ. */
+ EL3WINDOW(0);
+ /* But we explicitly zero the IRQ line select anyway. */
+ outw(0x0f00, ioaddr + WN0_IRQ);
+
+
+ irq2dev_map[dev->irq] = 0;
+
+ update_stats(ioaddr, dev);
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+#ifdef MODULE
+static char devicename[9] = { 0, };
+static struct device dev_3c509 = {
+ devicename, /* device name is inserted by linux/drivers/net/net_init.c */
+ 0, 0, 0, 0,
+ 0, 0,
+ 0, 0, 0, NULL, el3_probe };
+
+static int io = 0;
+static int irq = 0;
+
+int
+init_module(void)
+{
+ dev_3c509.base_addr = io;
+ dev_3c509.irq = irq;
+ if (!EISA_bus) {
+ printk("3c509: WARNING! Module load-time probing works reliably only for EISA-bus!\n");
+ }
+ if (register_netdev(&dev_3c509) != 0)
+ return -EIO;
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ unregister_netdev(&dev_3c509);
+ kfree_s(dev_3c509.priv,sizeof(struct el3_private));
+ dev_3c509.priv=NULL;
+ /* If we don't do this, we can't re-insmod it later. */
+ release_region(dev_3c509.base_addr, EL3_IO_EXTENT);
+}
+#endif /* MODULE */
+
+/*
+ * Local variables:
+ * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c 3c509.c"
+ * version-control: t
+ * kept-new-versions: 5
+ * tab-width: 4
+ * End:
+ */
diff --git a/i386/i386at/gpl/linux/net/3c59x.c b/i386/i386at/gpl/linux/net/3c59x.c
new file mode 100644
index 00000000..b5c4d5b7
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/3c59x.c
@@ -0,0 +1,1066 @@
+/* 3c59x.c: A 3Com 3c590/3c595 "Vortex" ethernet driver for linux. */
+/*
+ Written 1995 by Donald Becker.
+
+ This software may be used and distributed according to the terms
+ of the GNU Public License, incorporated herein by reference.
+
+ This driver is for the 3Com "Vortex" series ethercards. Members of
+ the series include the 3c590 PCI EtherLink III and 3c595-Tx PCI Fast
+ EtherLink. It also works with the 10Mbs-only 3c590 PCI EtherLink III.
+
+ The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
+ Center of Excellence in Space Data and Information Sciences
+ Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
+*/
+
+static char *version = "3c59x.c:v0.13 2/13/96 becker@cesdis.gsfc.nasa.gov\n";
+
+/* "Knobs" that turn on special features. */
+/* Allow the use of bus master transfers instead of programmed-I/O for the
+ Tx process. Bus master transfers are always disabled by default, but
+ iff this is set they may be turned on using 'options'. */
+#define VORTEX_BUS_MASTER
+
+/* Put out somewhat more debugging messages. (0 - no msg, 1 minimal msgs). */
+#define VORTEX_DEBUG 1
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/in.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#include <linux/timer.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#ifdef HAVE_SHARED_IRQ
+#define USE_SHARED_IRQ
+#include <linux/shared_irq.h>
+#endif
+
+/* The total size is twice that of the original EtherLinkIII series: the
+ runtime register window, window 1, is now always mapped in. */
+#define VORTEX_TOTAL_SIZE 0x20
+
+#ifdef HAVE_DEVLIST
+struct netdev_entry tc59x_drv =
+{"Vortex", vortex_pci_probe, VORTEX_TOTAL_SIZE, NULL};
+#endif
+
+#ifdef VORTEX_DEBUG
+int vortex_debug = VORTEX_DEBUG;
+#else
+int vortex_debug = 1;
+#endif
+
+static int product_ids[] = {0x5900, 0x5950, 0x5951, 0x5952, 0, 0};
+static const char *product_names[] = {
+ "3c590 Vortex 10Mbps",
+ "3c595 Vortex 100baseTX",
+ "3c595 Vortex 100baseT4",
+ "3c595 Vortex 100base-MII",
+ "EISA Vortex 3c597",
+};
+#define DEMON_INDEX 5 /* Caution! Must be consistent with above! */
+
+/*
+ Theory of Operation
+
+I. Board Compatibility
+
+This device driver is designed for the 3Com FastEtherLink, 3Com's PCI to
+10/100baseT adapter. It also works with the 3c590, a similar product
+with only a 10Mbs interface.
+
+II. Board-specific settings
+
+PCI bus devices are configured by the system at boot time, so no jumpers
+need to be set on the board. The system BIOS should be set to assign the
+PCI INTA signal to an otherwise unused system IRQ line. While it's
+physically possible to shared PCI interrupt lines, the 1.2.0 kernel doesn't
+support it.
+
+III. Driver operation
+
+The 3c59x series use an interface that's very similar to the previous 3c5x9
+series. The primary interface is two programmed-I/O FIFOs, with an
+alternate single-contiguous-region bus-master transfer (see next).
+
+One extension that is advertised in a very large font is that the adapters
+are capable of being bus masters. Unfortunately this capability is only for
+a single contiguous region making it less useful than the list of transfer
+regions available with the DEC Tulip or AMD PCnet. Given the significant
+performance impact of taking an extra interrupt for each transfer, using
+DMA transfers is a win only with large blocks.
+
+IIIC. Synchronization
+The driver runs as two independent, single-threaded flows of control. One
+is the send-packet routine, which enforces single-threaded use by the
+dev->tbusy flag. The other thread is the interrupt handler, which is single
+threaded by the hardware and other software.
+
+IV. Notes
+
+Thanks to Cameron Spitzer and Terry Murphy of 3Com for providing both
+3c590 and 3c595 boards.
+The name "Vortex" is the internal 3Com project name for the PCI ASIC, and
+the not-yet-released (3/95) EISA version is called "Demon". According to
+Terry these names come from rides at the local amusement park.
+
+The new chips support both ethernet (1.5K) and FDDI (4.5K) packet sizes!
+This driver only supports ethernet packets because of the skbuff allocation
+limit of 4K.
+*/
+
+#define TCOM_VENDOR_ID 0x10B7 /* 3Com's manufacturer's ID. */
+
+/* Operational defintions.
+ These are not used by other compilation units and thus are not
+ exported in a ".h" file.
+
+ First the windows. There are eight register windows, with the command
+ and status registers available in each.
+ */
+#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD)
+#define EL3_CMD 0x0e
+#define EL3_STATUS 0x0e
+
+/* The top five bits written to EL3_CMD are a command, the lower
+ 11 bits are the parameter, if applicable.
+ Note that 11 parameters bits was fine for ethernet, but the new chip
+ can handle FDDI lenght frames (~4500 octets) and now parameters count
+ 32-bit 'Dwords' rather than octets. */
+
+enum vortex_cmd {
+ TotalReset = 0<<11, SelectWindow = 1<<11, StartCoax = 2<<11,
+ RxDisable = 3<<11, RxEnable = 4<<11, RxReset = 5<<11, RxDiscard = 8<<11,
+ TxEnable = 9<<11, TxDisable = 10<<11, TxReset = 11<<11,
+ FakeIntr = 12<<11, AckIntr = 13<<11, SetIntrEnb = 14<<11,
+ SetStatusEnb = 15<<11, SetRxFilter = 16<<11, SetRxThreshold = 17<<11,
+ SetTxThreshold = 18<<11, SetTxStart = 19<<11,
+ StartDMAUp = 20<<11, StartDMADown = (20<<11)+1, StatsEnable = 21<<11,
+ StatsDisable = 22<<11, StopCoax = 23<<11,};
+
+/* The SetRxFilter command accepts the following classes: */
+enum RxFilter {
+ RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8 };
+
+/* Bits in the general status register. */
+enum vortex_status {
+ IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004,
+ TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020,
+ IntReq = 0x0040, StatsFull = 0x0080, DMADone = 1<<8,
+ DMAInProgress = 1<<11, /* DMA controller is still busy.*/
+ CmdInProgress = 1<<12, /* EL3_CMD is still busy.*/
+};
+
+/* Register window 1 offsets, the window used in normal operation.
+ On the Vortex this window is always mapped at offsets 0x10-0x1f. */
+enum Window1 {
+ TX_FIFO = 0x10, RX_FIFO = 0x10, RxErrors = 0x14,
+ RxStatus = 0x18, Timer=0x1A, TxStatus = 0x1B,
+ TxFree = 0x1C, /* Remaining free bytes in Tx buffer. */
+};
+enum Window0 {
+ Wn0EepromCmd = 10, /* Window 0: EEPROM command register. */
+};
+enum Win0_EEPROM_bits {
+ EEPROM_Read = 0x80, EEPROM_WRITE = 0x40, EEPROM_ERASE = 0xC0,
+ EEPROM_EWENB = 0x30, /* Enable erasing/writing for 10 msec. */
+ EEPROM_EWDIS = 0x00, /* Disable EWENB before 10 msec timeout. */
+};
+/* EEPROM locations. */
+enum eeprom_offset {
+ PhysAddr01=0, PhysAddr23=1, PhysAddr45=2, ModelID=3,
+ EtherLink3ID=7, IFXcvrIO=8, IRQLine=9,
+ NodeAddr01=10, NodeAddr23=11, NodeAddr45=12,
+ DriverTune=13, Checksum=15};
+
+enum Window3 { /* Window 3: MAC/config bits. */
+ Wn3_Config=0, Wn3_MAC_Ctrl=6, Wn3_Options=8,
+};
+union wn3_config {
+ int i;
+ struct w3_config_fields {
+ unsigned int ram_size:3, ram_width:1, ram_speed:2, rom_size:2;
+ int pad8:8;
+ unsigned int ram_split:2, pad18:2, xcvr:3, pad21:1, autoselect:1;
+ int pad24:8;
+ } u;
+};
+
+enum Window4 {
+ Wn4_Media = 0x0A, /* Window 4: Various transcvr/media bits. */
+};
+enum Win4_Media_bits {
+ Media_TP = 0x00C0, /* Enable link beat and jabber for 10baseT. */
+};
+enum Window7 { /* Window 7: Bus Master control. */
+ Wn7_MasterAddr = 0, Wn7_MasterLen = 6, Wn7_MasterStatus = 12,
+};
+
+struct vortex_private {
+ char devname[8]; /* "ethN" string, also for kernel debug. */
+ const char *product_name;
+ struct device *next_module;
+ struct enet_statistics stats;
+#ifdef VORTEX_BUS_MASTER
+ struct sk_buff *tx_skb; /* Packet being eaten by bus master ctrl. */
+#endif
+ struct timer_list timer; /* Media selection timer. */
+ int options; /* User-settable driver options (none yet). */
+ unsigned int media_override:3, full_duplex:1, bus_master:1, autoselect:1;
+};
+
+static char *if_names[] = {
+ "10baseT", "10Mbs AUI", "undefined", "10base2",
+ "100baseTX", "100baseFX", "MII", "undefined"};
+
+static int vortex_scan(struct device *dev);
+static int vortex_found_device(struct device *dev, int ioaddr, int irq,
+ int product_index, int options);
+static int vortex_probe1(struct device *dev);
+static int vortex_open(struct device *dev);
+static void vortex_timer(unsigned long arg);
+static int vortex_start_xmit(struct sk_buff *skb, struct device *dev);
+static int vortex_rx(struct device *dev);
+static void vortex_interrupt(int irq, struct pt_regs *regs);
+static int vortex_close(struct device *dev);
+static void update_stats(int addr, struct device *dev);
+static struct enet_statistics *vortex_get_stats(struct device *dev);
+static void set_multicast_list(struct device *dev);
+
+
+/* Unlike the other PCI cards the 59x cards don't need a large contiguous
+ memory region, so making the driver a loadable module is feasible.
+
+ Unfortuneately maximizing the shared code between the integrated and
+ module version of the driver results in a complicated set of initialization
+ procedures.
+ init_module() -- modules / tc59x_init() -- built-in
+ The wrappers for vortex_scan()
+ vortex_scan() The common routine that scans for PCI and EISA cards
+ vortex_found_device() Allocate a device structure when we find a card.
+ Different versions exist for modules and built-in.
+ vortex_probe1() Fill in the device structure -- this is seperated
+ so that the modules code can put it in dev->init.
+*/
+/* This driver uses 'options' to pass the media type, full-duplex flag, etc. */
+/* Note: this is the only limit on the number of cards supported!! */
+int options[8] = { -1, -1, -1, -1, -1, -1, -1, -1,};
+
+#ifdef MODULE
+static int debug = -1;
+/* A list of all installed Vortex devices, for removing the driver module. */
+static struct device *root_vortex_dev = NULL;
+
+int
+init_module(void)
+{
+ int cards_found;
+
+ if (debug >= 0)
+ vortex_debug = debug;
+ if (vortex_debug)
+ printk(version);
+
+ root_vortex_dev = NULL;
+ cards_found = vortex_scan(0);
+ return cards_found < 0 ? cards_found : 0;
+}
+
+#else
+unsigned long tc59x_probe(struct device *dev)
+{
+ int cards_found = 0;
+
+ cards_found = vortex_scan(dev);
+
+ if (vortex_debug > 0 && cards_found)
+ printk(version);
+
+ return cards_found ? 0 : -ENODEV;
+}
+#endif /* not MODULE */
+
+static int vortex_scan(struct device *dev)
+{
+ int cards_found = 0;
+
+ if (pcibios_present()) {
+ static int pci_index = 0;
+ for (; pci_index < 8; pci_index++) {
+ unsigned char pci_bus, pci_device_fn, pci_irq_line, pci_latency;
+ unsigned int pci_ioaddr;
+ unsigned short pci_command;
+ int index;
+
+ for (index = 0; product_ids[index]; index++) {
+ if ( ! pcibios_find_device(TCOM_VENDOR_ID, product_ids[index],
+ pci_index, &pci_bus,
+ &pci_device_fn))
+ break;
+ }
+ if ( ! product_ids[index])
+ break;
+
+ pcibios_read_config_byte(pci_bus, pci_device_fn,
+ PCI_INTERRUPT_LINE, &pci_irq_line);
+ pcibios_read_config_dword(pci_bus, pci_device_fn,
+ PCI_BASE_ADDRESS_0, &pci_ioaddr);
+ /* Remove I/O space marker in bit 0. */
+ pci_ioaddr &= ~3;
+
+#ifdef VORTEX_BUS_MASTER
+ /* Get and check the bus-master and latency values.
+ Some PCI BIOSes fail to set the master-enable bit, and
+ the latency timer must be set to the maximum value to avoid
+ data corruption that occurs when the timer expires during
+ a transfer. Yes, it's a bug. */
+ pcibios_read_config_word(pci_bus, pci_device_fn,
+ PCI_COMMAND, &pci_command);
+ if ( ! (pci_command & PCI_COMMAND_MASTER)) {
+ printk(" PCI Master Bit has not been set! Setting...\n");
+ pci_command |= PCI_COMMAND_MASTER;
+ pcibios_write_config_word(pci_bus, pci_device_fn,
+ PCI_COMMAND, pci_command);
+ }
+ pcibios_read_config_byte(pci_bus, pci_device_fn,
+ PCI_LATENCY_TIMER, &pci_latency);
+ if (pci_latency != 255) {
+ printk(" Overriding PCI latency timer (CFLT) setting of %d, new value is 255.\n", pci_latency);
+ pcibios_write_config_byte(pci_bus, pci_device_fn,
+ PCI_LATENCY_TIMER, 255);
+ }
+#endif /* VORTEX_BUS_MASTER */
+ vortex_found_device(dev, pci_ioaddr, pci_irq_line, index,
+ dev && dev->mem_start ? dev->mem_start
+ : options[cards_found]);
+ dev = 0;
+ cards_found++;
+ }
+ }
+
+ /* Now check all slots of the EISA bus. */
+ if (EISA_bus) {
+ static int ioaddr = 0x1000;
+ for (ioaddr = 0x1000; ioaddr < 0x9000; ioaddr += 0x1000) {
+ /* Check the standard EISA ID register for an encoded '3Com'. */
+ if (inw(ioaddr + 0xC80) != 0x6d50)
+ continue;
+ /* Check for a product that we support. */
+ if ((inw(ioaddr + 0xC82) & 0xFFF0) != 0x5970
+ && (inw(ioaddr + 0xC82) & 0xFFF0) != 0x5920)
+ continue;
+ vortex_found_device(dev, ioaddr, inw(ioaddr + 0xC88) >> 12,
+ DEMON_INDEX, dev && dev->mem_start
+ ? dev->mem_start : options[cards_found]);
+ dev = 0;
+ cards_found++;
+ }
+ }
+
+ return cards_found;
+}
+
+static int vortex_found_device(struct device *dev, int ioaddr, int irq,
+ int product_index, int options)
+{
+ struct vortex_private *vp;
+
+#ifdef MODULE
+ /* Allocate and fill new device structure. */
+ int dev_size = sizeof(struct device) +
+ sizeof(struct vortex_private);
+
+ dev = (struct device *) kmalloc(dev_size, GFP_KERNEL);
+ memset(dev, 0, dev_size);
+ dev->priv = ((void *)dev) + sizeof(struct device);
+ vp = (struct vortex_private *)dev->priv;
+ dev->name = vp->devname; /* An empty string. */
+ dev->base_addr = ioaddr;
+ dev->irq = irq;
+ dev->init = vortex_probe1;
+ vp->product_name = product_names[product_index];
+ vp->options = options;
+ if (options >= 0) {
+ vp->media_override = ((options & 7) == 2) ? 0 : options & 7;
+ vp->full_duplex = (options & 8) ? 1 : 0;
+ vp->bus_master = (options & 16) ? 1 : 0;
+ } else {
+ vp->media_override = 7;
+ vp->full_duplex = 0;
+ vp->bus_master = 0;
+ }
+ ether_setup(dev);
+ vp->next_module = root_vortex_dev;
+ root_vortex_dev = dev;
+ if (register_netdev(dev) != 0)
+ return -EIO;
+#else /* not a MODULE */
+ if (dev) {
+ dev->priv = kmalloc(sizeof (struct vortex_private), GFP_KERNEL);
+ memset(dev->priv, 0, sizeof (struct vortex_private));
+ }
+ dev = init_etherdev(dev, sizeof(struct vortex_private));
+ dev->base_addr = ioaddr;
+ dev->irq = irq;
+ vp = (struct vortex_private *)dev->priv;
+ vp->product_name = product_names[product_index];
+ vp->options = options;
+ if (options >= 0) {
+ vp->media_override = ((options & 7) == 2) ? 0 : options & 7;
+ vp->full_duplex = (options & 8) ? 1 : 0;
+ vp->bus_master = (options & 16) ? 1 : 0;
+ } else {
+ vp->media_override = 7;
+ vp->full_duplex = 0;
+ vp->bus_master = 0;
+ }
+
+ vortex_probe1(dev);
+#endif /* MODULE */
+ return 0;
+}
+
+static int vortex_probe1(struct device *dev)
+{
+ int ioaddr = dev->base_addr;
+ struct vortex_private *vp = (struct vortex_private *)dev->priv;
+ int i;
+
+ printk("%s: 3Com %s at %#3x,", dev->name,
+ vp->product_name, ioaddr);
+
+ /* Read the station address from the EEPROM. */
+ EL3WINDOW(0);
+ for (i = 0; i < 3; i++) {
+ short *phys_addr = (short *)dev->dev_addr;
+ int timer;
+ outw(EEPROM_Read + PhysAddr01 + i, ioaddr + Wn0EepromCmd);
+ /* Pause for at least 162 us. for the read to take place. */
+ for (timer = 0; timer < 162*4 + 400; timer++) {
+ SLOW_DOWN_IO;
+ if ((inw(ioaddr + Wn0EepromCmd) & 0x8000) == 0)
+ break;
+ }
+ phys_addr[i] = htons(inw(ioaddr + 12));
+ }
+ for (i = 0; i < 6; i++)
+ printk("%c%2.2x", i ? ':' : ' ', dev->dev_addr[i]);
+ printk(", IRQ %d\n", dev->irq);
+ /* Tell them about an invalid IRQ. */
+ if (vortex_debug && (dev->irq <= 0 || dev->irq > 15))
+ printk(" *** Warning: this IRQ is unlikely to work!\n");
+
+ {
+ char *ram_split[] = {"5:3", "3:1", "1:1", "invalid"};
+ union wn3_config config;
+ EL3WINDOW(3);
+ config.i = inl(ioaddr + Wn3_Config);
+ if (vortex_debug > 1)
+ printk(" Internal config register is %4.4x, transceivers %#x.\n",
+ config.i, inw(ioaddr + Wn3_Options));
+ printk(" %dK %s-wide RAM %s Rx:Tx split, %s%s interface.\n",
+ 8 << config.u.ram_size,
+ config.u.ram_width ? "word" : "byte",
+ ram_split[config.u.ram_split],
+ config.u.autoselect ? "autoselect/" : "",
+ if_names[config.u.xcvr]);
+ dev->if_port = config.u.xcvr;
+ vp->autoselect = config.u.autoselect;
+ }
+
+ /* We do a request_region() only to register /proc/ioports info. */
+ request_region(ioaddr, VORTEX_TOTAL_SIZE, vp->product_name);
+
+ /* The 3c59x-specific entries in the device structure. */
+ dev->open = &vortex_open;
+ dev->hard_start_xmit = &vortex_start_xmit;
+ dev->stop = &vortex_close;
+ dev->get_stats = &vortex_get_stats;
+ dev->set_multicast_list = &set_multicast_list;
+#if defined (HAVE_SET_MAC_ADDR) && 0
+ dev->set_mac_address = &set_mac_address;
+#endif
+
+ return 0;
+}
+
+
+static int
+vortex_open(struct device *dev)
+{
+ int ioaddr = dev->base_addr;
+ struct vortex_private *vp = (struct vortex_private *)dev->priv;
+ union wn3_config config;
+ int i;
+
+ /* Before initializing select the active media port. */
+ EL3WINDOW(3);
+ if (vp->full_duplex)
+ outb(0x20, ioaddr + Wn3_MAC_Ctrl); /* Set the full-duplex bit. */
+ config.i = inl(ioaddr + Wn3_Config);
+
+ if (vp->media_override != 7) {
+ if (vortex_debug > 1)
+ printk("%s: Media override to transceiver %d (%s).\n",
+ dev->name, vp->media_override, if_names[vp->media_override]);
+ config.u.xcvr = vp->media_override;
+ dev->if_port = vp->media_override;
+ outl(config.i, ioaddr + Wn3_Config);
+ }
+
+ if (vortex_debug > 1) {
+ printk("%s: vortex_open() InternalConfig %8.8x.\n",
+ dev->name, config.i);
+ }
+
+ outw(TxReset, ioaddr + EL3_CMD);
+ for (i = 20; i >= 0 ; i--)
+ if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress)
+ break;
+
+ outw(RxReset, ioaddr + EL3_CMD);
+ /* Wait a few ticks for the RxReset command to complete. */
+ for (i = 20; i >= 0 ; i--)
+ if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress)
+ break;
+
+ outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD);
+
+#ifdef USE_SHARED_IRQ
+ i = request_shared_irq(dev->irq, &vortex_interrupt, dev, vp->product_name);
+ if (i) /* Error */
+ return i;
+#else
+ if (dev->irq == 0 || irq2dev_map[dev->irq] != NULL)
+ return -EAGAIN;
+ irq2dev_map[dev->irq] = dev;
+ if (request_irq(dev->irq, &vortex_interrupt, 0, vp->product_name)) {
+ irq2dev_map[dev->irq] = NULL;
+ return -EAGAIN;
+ }
+#endif
+
+ if (vortex_debug > 1) {
+ EL3WINDOW(4);
+ printk("%s: vortex_open() irq %d media status %4.4x.\n",
+ dev->name, dev->irq, inw(ioaddr + Wn4_Media));
+ }
+
+ /* Set the station address and mask in window 2 each time opened. */
+ EL3WINDOW(2);
+ for (i = 0; i < 6; i++)
+ outb(dev->dev_addr[i], ioaddr + i);
+ for (; i < 12; i+=2)
+ outw(0, ioaddr + i);
+
+ if (dev->if_port == 3)
+ /* Start the thinnet transceiver. We should really wait 50ms...*/
+ outw(StartCoax, ioaddr + EL3_CMD);
+ else if (dev->if_port == 0) {
+ /* 10baseT interface, enabled link beat and jabber check. */
+ EL3WINDOW(4);
+ outw(inw(ioaddr + Wn4_Media) | Media_TP, ioaddr + Wn4_Media);
+ }
+
+ /* Switch to the stats window, and clear all stats by reading. */
+ outw(StatsDisable, ioaddr + EL3_CMD);
+ EL3WINDOW(6);
+ for (i = 0; i < 10; i++)
+ inb(ioaddr + i);
+ inw(ioaddr + 10);
+ inw(ioaddr + 12);
+ /* New: On the Vortex we must also clear the BadSSD counter. */
+ EL3WINDOW(4);
+ inb(ioaddr + 12);
+
+ /* Switch to register set 7 for normal use. */
+ EL3WINDOW(7);
+
+ /* Accept b-case and phys addr only. */
+ outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD);
+ outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+
+ outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */
+ outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */
+ /* Allow status bits to be seen. */
+ outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD);
+ /* Ack all pending events, and set active indicator mask. */
+ outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq,
+ ioaddr + EL3_CMD);
+ outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull
+ | DMADone, ioaddr + EL3_CMD);
+
+#ifdef MODULE
+ MOD_INC_USE_COUNT;
+#endif
+
+ if (vp->autoselect) {
+ init_timer(&vp->timer);
+ vp->timer.expires = (14*HZ)/10; /* 1.4 sec. */
+ vp->timer.data = (unsigned long)dev;
+ vp->timer.function = &vortex_timer; /* timer handler */
+ add_timer(&vp->timer);
+ }
+ return 0;
+}
+
+static void vortex_timer(unsigned long data)
+{
+ struct device *dev = (struct device *)data;
+ if (vortex_debug > 2)
+ printk("%s: Media selection timer tick happened.\n", dev->name);
+ /* ToDo: active media selection here! */
+}
+
+static int
+vortex_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+ struct vortex_private *vp = (struct vortex_private *)dev->priv;
+ int ioaddr = dev->base_addr;
+
+ /* Transmitter timeout, serious problems. */
+ if (dev->tbusy) {
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < 40)
+ return 1;
+ printk("%s: transmit timed out, tx_status %2.2x status %4.4x.\n",
+ dev->name, inb(ioaddr + TxStatus), inw(ioaddr + EL3_STATUS));
+ vp->stats.tx_errors++;
+ /* Issue TX_RESET and TX_START commands. */
+ outw(TxReset, ioaddr + EL3_CMD);
+ {
+ int i;
+ for (i = 20; i >= 0 ; i--)
+ if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress)
+ break;
+ }
+ outw(TxEnable, ioaddr + EL3_CMD);
+ dev->trans_start = jiffies;
+ dev->tbusy = 0;
+ return 0;
+ }
+
+ if (skb == NULL || skb->len <= 0) {
+ printk("%s: Obsolete driver layer request made: skbuff==NULL.\n",
+ dev->name);
+ dev_tint(dev);
+ return 0;
+ }
+
+ /* Block a timer-based transmit from overlapping. This could better be
+ done with atomic_swap(1, dev->tbusy), but set_bit() works as well.
+ If this ever occurs the queue layer is doing something evil! */
+ if (set_bit(0, (void*)&dev->tbusy) != 0) {
+ printk("%s: Transmitter access conflict.\n", dev->name);
+ return 1;
+ }
+
+ /* Put out the doubleword header... */
+ outl(skb->len, ioaddr + TX_FIFO);
+#ifdef VORTEX_BUS_MASTER
+ if (vp->bus_master) {
+ /* Set the bus-master controller to transfer the packet. */
+ outl((int)(skb->data), ioaddr + Wn7_MasterAddr);
+ outw((skb->len + 3) & ~3, ioaddr + Wn7_MasterLen);
+ vp->tx_skb = skb;
+ outw(StartDMADown, ioaddr + EL3_CMD);
+ } else {
+ /* ... and the packet rounded to a doubleword. */
+ outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
+ dev_kfree_skb (skb, FREE_WRITE);
+ if (inw(ioaddr + TxFree) > 1536) {
+ dev->tbusy = 0;
+ } else
+ /* Interrupt us when the FIFO has room for max-sized packet. */
+ outw(SetTxThreshold + 1536, ioaddr + EL3_CMD);
+ }
+#else
+ /* ... and the packet rounded to a doubleword. */
+ outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
+ dev_kfree_skb (skb, FREE_WRITE);
+ if (inw(ioaddr + TxFree) > 1536) {
+ dev->tbusy = 0;
+ } else
+ /* Interrupt us when the FIFO has room for max-sized packet. */
+ outw(SetTxThreshold + 1536, ioaddr + EL3_CMD);
+#endif /* bus master */
+
+ dev->trans_start = jiffies;
+
+ /* Clear the Tx status stack. */
+ {
+ short tx_status;
+ int i = 4;
+
+ while (--i > 0 && (tx_status = inb(ioaddr + TxStatus)) > 0) {
+ if (tx_status & 0x3C) { /* A Tx-disabling error occured. */
+ if (vortex_debug > 2)
+ printk("%s: Tx error, status %2.2x.\n",
+ dev->name, tx_status);
+ if (tx_status & 0x04) vp->stats.tx_fifo_errors++;
+ if (tx_status & 0x38) vp->stats.tx_aborted_errors++;
+ if (tx_status & 0x30) {
+ int j;
+ outw(TxReset, ioaddr + EL3_CMD);
+ for (j = 20; j >= 0 ; j--)
+ if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress)
+ break;
+ }
+ outw(TxEnable, ioaddr + EL3_CMD);
+ }
+ outb(0x00, ioaddr + TxStatus); /* Pop the status stack. */
+ }
+ }
+ return 0;
+}
+
+/* The interrupt handler does all of the Rx thread work and cleans up
+ after the Tx thread. */
+static void vortex_interrupt(int irq, struct pt_regs *regs)
+{
+#ifdef USE_SHARED_IRQ
+ struct device *dev = (struct device *)(irq == 0 ? regs : irq2dev_map[irq]);
+#else
+ struct device *dev = (struct device *)(irq2dev_map[irq]);
+#endif
+ struct vortex_private *lp;
+ int ioaddr, status;
+ int latency;
+ int i = 0;
+
+ if (dev == NULL) {
+ printk ("vortex_interrupt(): irq %d for unknown device.\n", irq);
+ return;
+ }
+
+ if (dev->interrupt)
+ printk("%s: Re-entering the interrupt handler.\n", dev->name);
+ dev->interrupt = 1;
+
+ ioaddr = dev->base_addr;
+ latency = inb(ioaddr + Timer);
+ lp = (struct vortex_private *)dev->priv;
+
+ status = inw(ioaddr + EL3_STATUS);
+
+ if (vortex_debug > 4)
+ printk("%s: interrupt, status %4.4x, timer %d.\n", dev->name,
+ status, latency);
+ if ((status & 0xE000) != 0xE000) {
+ static int donedidthis=0;
+ /* Some interrupt controllers store a bogus interrupt from boot-time.
+ Ignore a single early interrupt, but don't hang the machine for
+ other interrupt problems. */
+ if (donedidthis++ > 1) {
+ printk("%s: Bogus interrupt, bailing. Status %4.4x, start=%d.\n",
+ dev->name, status, dev->start);
+ free_irq(dev->irq);
+ }
+ }
+
+ do {
+ if (vortex_debug > 5)
+ printk("%s: In interrupt loop, status %4.4x.\n",
+ dev->name, status);
+ if (status & RxComplete)
+ vortex_rx(dev);
+
+ if (status & TxAvailable) {
+ if (vortex_debug > 5)
+ printk(" TX room bit was handled.\n");
+ /* There's room in the FIFO for a full-sized packet. */
+ outw(AckIntr | TxAvailable, ioaddr + EL3_CMD);
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+ }
+#ifdef VORTEX_BUS_MASTER
+ if (status & DMADone) {
+ outw(0x1000, ioaddr + Wn7_MasterStatus); /* Ack the event. */
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+ }
+#endif
+ if (status & (AdapterFailure | RxEarly | StatsFull)) {
+ /* Handle all uncommon interrupts at once. */
+ if (status & RxEarly) { /* Rx early is unused. */
+ vortex_rx(dev);
+ outw(AckIntr | RxEarly, ioaddr + EL3_CMD);
+ }
+ if (status & StatsFull) { /* Empty statistics. */
+ static int DoneDidThat = 0;
+ if (vortex_debug > 4)
+ printk("%s: Updating stats.\n", dev->name);
+ update_stats(ioaddr, dev);
+ /* DEBUG HACK: Disable statistics as an interrupt source. */
+ /* This occurs when we have the wrong media type! */
+ if (DoneDidThat == 0 &&
+ inw(ioaddr + EL3_STATUS) & StatsFull) {
+ int win, reg;
+ printk("%s: Updating stats failed, disabling stats as an"
+ " interrupt source.\n", dev->name);
+ for (win = 0; win < 8; win++) {
+ EL3WINDOW(win);
+ printk("\n Vortex window %d:", win);
+ for (reg = 0; reg < 16; reg++)
+ printk(" %2.2x", inb(ioaddr+reg));
+ }
+ EL3WINDOW(7);
+ outw(SetIntrEnb | 0x18, ioaddr + EL3_CMD);
+ DoneDidThat++;
+ }
+ }
+ if (status & AdapterFailure) {
+ /* Adapter failure requires Rx reset and reinit. */
+ outw(RxReset, ioaddr + EL3_CMD);
+ /* Set the Rx filter to the current state. */
+ outw(SetRxFilter | RxStation | RxBroadcast
+ | (dev->flags & IFF_ALLMULTI ? RxMulticast : 0)
+ | (dev->flags & IFF_PROMISC ? RxProm : 0),
+ ioaddr + EL3_CMD);
+ outw(RxEnable, ioaddr + EL3_CMD); /* Re-enable the receiver. */
+ outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD);
+ }
+ }
+
+ if (++i > 10) {
+ printk("%s: Infinite loop in interrupt, status %4.4x. "
+ "Disabling functions (%4.4x).\n",
+ dev->name, status, SetStatusEnb | ((~status) & 0xFE));
+ /* Disable all pending interrupts. */
+ outw(SetStatusEnb | ((~status) & 0xFE), ioaddr + EL3_CMD);
+ outw(AckIntr | 0xFF, ioaddr + EL3_CMD);
+ break;
+ }
+ /* Acknowledge the IRQ. */
+ outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD);
+
+ } while ((status = inw(ioaddr + EL3_STATUS)) & (IntLatch | RxComplete));
+
+ if (vortex_debug > 4)
+ printk("%s: exiting interrupt, status %4.4x.\n", dev->name, status);
+
+ dev->interrupt = 0;
+ return;
+}
+
+static int
+vortex_rx(struct device *dev)
+{
+ struct vortex_private *vp = (struct vortex_private *)dev->priv;
+ int ioaddr = dev->base_addr;
+ int i;
+ short rx_status;
+
+ if (vortex_debug > 5)
+ printk(" In rx_packet(), status %4.4x, rx_status %4.4x.\n",
+ inw(ioaddr+EL3_STATUS), inw(ioaddr+RxStatus));
+ while ((rx_status = inw(ioaddr + RxStatus)) > 0) {
+ if (rx_status & 0x4000) { /* Error, update stats. */
+ unsigned char rx_error = inb(ioaddr + RxErrors);
+ if (vortex_debug > 4)
+ printk(" Rx error: status %2.2x.\n", rx_error);
+ vp->stats.rx_errors++;
+ if (rx_error & 0x01) vp->stats.rx_over_errors++;
+ if (rx_error & 0x02) vp->stats.rx_length_errors++;
+ if (rx_error & 0x04) vp->stats.rx_frame_errors++;
+ if (rx_error & 0x08) vp->stats.rx_crc_errors++;
+ if (rx_error & 0x10) vp->stats.rx_length_errors++;
+ } else {
+ /* The packet length: up to 4.5K!. */
+ short pkt_len = rx_status & 0x1fff;
+ struct sk_buff *skb;
+
+ skb = dev_alloc_skb(pkt_len + 5);
+ if (vortex_debug > 4)
+ printk("Receiving packet size %d status %4.4x.\n",
+ pkt_len, rx_status);
+ if (skb != NULL) {
+ skb->dev = dev;
+ skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */
+ /* 'skb_put()' points to the start of sk_buff data area. */
+ insl(ioaddr + RX_FIFO, skb_put(skb, pkt_len),
+ (pkt_len + 3) >> 2);
+ skb->protocol = eth_type_trans(skb, dev);
+ netif_rx(skb);
+ outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */
+ /* Wait a limited time to go to next packet. */
+ for (i = 200; i >= 0; i--)
+ if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress)
+ break;
+ vp->stats.rx_packets++;
+ continue;
+ } else if (vortex_debug)
+ printk("%s: Couldn't allocate a sk_buff of size %d.\n",
+ dev->name, pkt_len);
+ }
+ vp->stats.rx_dropped++;
+ outw(RxDiscard, ioaddr + EL3_CMD);
+ /* Wait a limited time to skip this packet. */
+ for (i = 200; i >= 0; i--)
+ if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress)
+ break;
+ }
+
+ return 0;
+}
+
+static int
+vortex_close(struct device *dev)
+{
+ int ioaddr = dev->base_addr;
+
+ dev->start = 0;
+ dev->tbusy = 1;
+
+ if (vortex_debug > 1)
+ printk("%s: vortex_close() status %4.4x, Tx status %2.2x.\n",
+ dev->name, inw(ioaddr + EL3_STATUS), inb(ioaddr + TxStatus));
+
+ /* Turn off statistics ASAP. We update lp->stats below. */
+ outw(StatsDisable, ioaddr + EL3_CMD);
+
+ /* Disable the receiver and transmitter. */
+ outw(RxDisable, ioaddr + EL3_CMD);
+ outw(TxDisable, ioaddr + EL3_CMD);
+
+ if (dev->if_port == 3)
+ /* Turn off thinnet power. Green! */
+ outw(StopCoax, ioaddr + EL3_CMD);
+ else if (dev->if_port == 0) {
+ /* Disable link beat and jabber, if_port may change ere next open(). */
+ EL3WINDOW(4);
+ outw(inw(ioaddr + Wn4_Media) & ~Media_TP, ioaddr + Wn4_Media);
+ }
+
+#ifdef USE_SHARED_IRQ
+ free_shared_irq(dev->irq, dev);
+#else
+ free_irq(dev->irq);
+ /* Mmmm, we should diable all interrupt sources here. */
+ irq2dev_map[dev->irq] = 0;
+#endif
+
+ update_stats(ioaddr, dev);
+#ifdef MODULE
+ MOD_DEC_USE_COUNT;
+#endif
+
+ return 0;
+}
+
+static struct enet_statistics *
+vortex_get_stats(struct device *dev)
+{
+ struct vortex_private *vp = (struct vortex_private *)dev->priv;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ update_stats(dev->base_addr, dev);
+ restore_flags(flags);
+ return &vp->stats;
+}
+
+/* Update statistics.
+ Unlike with the EL3 we need not worry about interrupts changing
+ the window setting from underneath us, but we must still guard
+ against a race condition with a StatsUpdate interrupt updating the
+ table. This is done by checking that the ASM (!) code generated uses
+ atomic updates with '+='.
+ */
+static void update_stats(int ioaddr, struct device *dev)
+{
+ struct vortex_private *vp = (struct vortex_private *)dev->priv;
+
+ /* Unlike the 3c5x9 we need not turn off stats updates while reading. */
+ /* Switch to the stats window, and read everything. */
+ EL3WINDOW(6);
+ vp->stats.tx_carrier_errors += inb(ioaddr + 0);
+ vp->stats.tx_heartbeat_errors += inb(ioaddr + 1);
+ /* Multiple collisions. */ inb(ioaddr + 2);
+ vp->stats.collisions += inb(ioaddr + 3);
+ vp->stats.tx_window_errors += inb(ioaddr + 4);
+ vp->stats.rx_fifo_errors += inb(ioaddr + 5);
+ vp->stats.tx_packets += inb(ioaddr + 6);
+ vp->stats.tx_packets += (inb(ioaddr + 9)&0x30) << 4;
+ /* Rx packets */ inb(ioaddr + 7); /* Must read to clear */
+ /* Tx deferrals */ inb(ioaddr + 8);
+ /* Don't bother with register 9, an extention of registers 6&7.
+ If we do use the 6&7 values the atomic update assumption above
+ is invalid. */
+ inw(ioaddr + 10); /* Total Rx and Tx octets. */
+ inw(ioaddr + 12);
+ /* New: On the Vortex we must also clear the BadSSD counter. */
+ EL3WINDOW(4);
+ inb(ioaddr + 12);
+
+ /* We change back to window 7 (not 1) with the Vortex. */
+ EL3WINDOW(7);
+ return;
+}
+
+/* There are two version of set_multicast_list() to support both v1.2 and
+ v1.4 kernels. */
+static void
+set_multicast_list(struct device *dev)
+{
+ short ioaddr = dev->base_addr;
+
+ if ((dev->mc_list) || (dev->flags & IFF_ALLMULTI)) {
+ outw(SetRxFilter|RxStation|RxMulticast|RxBroadcast, ioaddr + EL3_CMD);
+ if (vortex_debug > 3) {
+ printk("%s: Setting Rx multicast mode, %d addresses.\n",
+ dev->name, dev->mc_count);
+ }
+ } else if (dev->flags & IFF_PROMISC) {
+ outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast | RxProm,
+ ioaddr + EL3_CMD);
+ } else
+ outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD);
+}
+
+
+#ifdef MODULE
+void
+cleanup_module(void)
+{
+ struct device *next_dev;
+
+ /* No need to check MOD_IN_USE, as sys_delete_module() checks. */
+ while (root_vortex_dev) {
+ next_dev = ((struct vortex_private *)root_vortex_dev->priv)->next_module;
+ unregister_netdev(root_vortex_dev);
+ release_region(root_vortex_dev->base_addr, VORTEX_TOTAL_SIZE);
+ kfree(root_vortex_dev);
+ root_vortex_dev = next_dev;
+ }
+}
+#endif /* MODULE */
+
+/*
+ * Local variables:
+ * compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c 3c59x.c -o 3c59x.o"
+ * c-indent-level: 4
+ * tab-width: 4
+ * End:
+ */
diff --git a/i386/i386at/gpl/linux/net/8390.c b/i386/i386at/gpl/linux/net/8390.c
new file mode 100644
index 00000000..05ea32f6
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/8390.c
@@ -0,0 +1,727 @@
+/* 8390.c: A general NS8390 ethernet driver core for linux. */
+/*
+ Written 1992-94 by Donald Becker.
+
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency.
+
+ This software may be used and distributed according to the terms
+ of the GNU Public License, incorporated herein by reference.
+
+ The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
+ Center of Excellence in Space Data and Information Sciences
+ Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
+
+ This is the chip-specific code for many 8390-based ethernet adaptors.
+ This is not a complete driver, it must be combined with board-specific
+ code such as ne.c, wd.c, 3c503.c, etc.
+
+ Changelog:
+
+ Paul Gortmaker : remove set_bit lock, other cleanups.
+ Paul Gortmaker : add ei_get_8390_hdr() so we can pass skb's to
+ ei_block_input() for eth_io_copy_and_sum().
+
+ */
+
+static const char *version =
+ "8390.c:v1.10 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+/*
+ Braindamage remaining:
+ Much of this code should have been cleaned up, but every attempt
+ has broken some clone part.
+
+ Sources:
+ The National Semiconductor LAN Databook, and the 3Com 3c503 databook.
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/types.h>
+#include <linux/ptrace.h>
+#include <linux/string.h>
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <linux/errno.h>
+#include <linux/fcntl.h>
+#include <linux/in.h>
+#include <linux/interrupt.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+
+#include "8390.h"
+
+/* These are the operational function interfaces to board-specific
+ routines.
+ void reset_8390(struct device *dev)
+ Resets the board associated with DEV, including a hardware reset of
+ the 8390. This is only called when there is a transmit timeout, and
+ it is always followed by 8390_init().
+ void block_output(struct device *dev, int count, const unsigned char *buf,
+ int start_page)
+ Write the COUNT bytes of BUF to the packet buffer at START_PAGE. The
+ "page" value uses the 8390's 256-byte pages.
+ void get_8390_hdr(struct device *dev, struct e8390_hdr *hdr, int ring_page)
+ Read the 4 byte, page aligned 8390 header. *If* there is a
+ subsequent read, it will be of the rest of the packet.
+ void block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset)
+ Read COUNT bytes from the packet buffer into the skb data area. Start
+ reading from RING_OFFSET, the address as the 8390 sees it. This will always
+ follow the read of the 8390 header.
+*/
+#define ei_reset_8390 (ei_local->reset_8390)
+#define ei_block_output (ei_local->block_output)
+#define ei_block_input (ei_local->block_input)
+#define ei_get_8390_hdr (ei_local->get_8390_hdr)
+
+/* use 0 for production, 1 for verification, >2 for debug */
+#ifdef EI_DEBUG
+int ei_debug = EI_DEBUG;
+#else
+int ei_debug = 1;
+#endif
+#ifdef EI_PINGPONG
+static int ei_pingpong = 1;
+#else
+static int ei_pingpong = 0;
+#endif
+
+/* Max number of packets received at one Intr.
+ Currently this may only be examined by a kernel debugger. */
+static int high_water_mark = 0;
+
+/* Index to functions. */
+static void ei_tx_intr(struct device *dev);
+static void ei_receive(struct device *dev);
+static void ei_rx_overrun(struct device *dev);
+
+/* Routines generic to NS8390-based boards. */
+static void NS8390_trigger_send(struct device *dev, unsigned int length,
+ int start_page);
+static void set_multicast_list(struct device *dev);
+
+
+/* Open/initialize the board. This routine goes all-out, setting everything
+ up anew at each open, even though many of these registers should only
+ need to be set once at boot.
+ */
+int ei_open(struct device *dev)
+{
+ struct ei_device *ei_local = (struct ei_device *) dev->priv;
+
+ /* This can't happen unless somebody forgot to call ethdev_init(). */
+ if (ei_local == NULL) {
+ printk(KERN_EMERG "%s: ei_open passed a non-existent device!\n", dev->name);
+ return -ENXIO;
+ }
+
+ irq2dev_map[dev->irq] = dev;
+ NS8390_init(dev, 1);
+ dev->start = 1;
+ ei_local->irqlock = 0;
+ return 0;
+}
+
+/* Opposite of above. Only used when "ifconfig <devname> down" is done. */
+int ei_close(struct device *dev)
+{
+ NS8390_init(dev, 0);
+ dev->start = 0;
+ return 0;
+}
+
+static int ei_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+ int e8390_base = dev->base_addr;
+ struct ei_device *ei_local = (struct ei_device *) dev->priv;
+ int length, send_length;
+
+/*
+ * We normally shouldn't be called if dev->tbusy is set, but the
+ * existing code does anyway. If it has been too long since the
+ * last Tx, we assume the board has died and kick it.
+ */
+
+ if (dev->tbusy) { /* Do timeouts, just like the 8003 driver. */
+ int txsr = inb(e8390_base+EN0_TSR), isr;
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < TX_TIMEOUT || (tickssofar < (TX_TIMEOUT+5) && ! (txsr & ENTSR_PTX))) {
+ return 1;
+ }
+ isr = inb(e8390_base+EN0_ISR);
+ if (dev->start == 0) {
+ printk("%s: xmit on stopped card\n", dev->name);
+ return 1;
+ }
+
+ printk(KERN_DEBUG "%s: Tx timed out, %s TSR=%#2x, ISR=%#2x, t=%d.\n",
+ dev->name, (txsr & ENTSR_ABT) ? "excess collisions." :
+ (isr) ? "lost interrupt?" : "cable problem?", txsr, isr, tickssofar);
+
+ if (!isr && !ei_local->stat.tx_packets) {
+ /* The 8390 probably hasn't gotten on the cable yet. */
+ ei_local->interface_num ^= 1; /* Try a different xcvr. */
+ }
+
+ /* Try to restart the card. Perhaps the user has fixed something. */
+ ei_reset_8390(dev);
+ NS8390_init(dev, 1);
+ dev->trans_start = jiffies;
+ }
+
+ /* Sending a NULL skb means some higher layer thinks we've missed an
+ tx-done interrupt. Caution: dev_tint() handles the cli()/sti()
+ itself. */
+ if (skb == NULL) {
+ dev_tint(dev);
+ return 0;
+ }
+
+ length = skb->len;
+ if (skb->len <= 0)
+ return 0;
+
+ /* Mask interrupts from the ethercard. */
+ outb_p(0x00, e8390_base + EN0_IMR);
+ if (dev->interrupt) {
+ printk("%s: Tx request while isr active.\n",dev->name);
+ outb_p(ENISR_ALL, e8390_base + EN0_IMR);
+ return 1;
+ }
+ ei_local->irqlock = 1;
+
+ send_length = ETH_ZLEN < length ? length : ETH_ZLEN;
+
+ if (ei_local->pingpong) {
+ int output_page;
+ if (ei_local->tx1 == 0) {
+ output_page = ei_local->tx_start_page;
+ ei_local->tx1 = send_length;
+ if (ei_debug && ei_local->tx2 > 0)
+ printk("%s: idle transmitter tx2=%d, lasttx=%d, txing=%d.\n",
+ dev->name, ei_local->tx2, ei_local->lasttx,
+ ei_local->txing);
+ } else if (ei_local->tx2 == 0) {
+ output_page = ei_local->tx_start_page + 6;
+ ei_local->tx2 = send_length;
+ if (ei_debug && ei_local->tx1 > 0)
+ printk("%s: idle transmitter, tx1=%d, lasttx=%d, txing=%d.\n",
+ dev->name, ei_local->tx1, ei_local->lasttx,
+ ei_local->txing);
+ } else { /* We should never get here. */
+ if (ei_debug)
+ printk("%s: No Tx buffers free. irq=%d tx1=%d tx2=%d last=%d\n",
+ dev->name, dev->interrupt, ei_local->tx1,
+ ei_local->tx2, ei_local->lasttx);
+ ei_local->irqlock = 0;
+ dev->tbusy = 1;
+ outb_p(ENISR_ALL, e8390_base + EN0_IMR);
+ return 1;
+ }
+ ei_block_output(dev, length, skb->data, output_page);
+ if (! ei_local->txing) {
+ ei_local->txing = 1;
+ NS8390_trigger_send(dev, send_length, output_page);
+ dev->trans_start = jiffies;
+ if (output_page == ei_local->tx_start_page)
+ ei_local->tx1 = -1, ei_local->lasttx = -1;
+ else
+ ei_local->tx2 = -1, ei_local->lasttx = -2;
+ } else
+ ei_local->txqueue++;
+
+ dev->tbusy = (ei_local->tx1 && ei_local->tx2);
+ } else { /* No pingpong, just a single Tx buffer. */
+ ei_block_output(dev, length, skb->data, ei_local->tx_start_page);
+ ei_local->txing = 1;
+ NS8390_trigger_send(dev, send_length, ei_local->tx_start_page);
+ dev->trans_start = jiffies;
+ dev->tbusy = 1;
+ }
+
+ /* Turn 8390 interrupts back on. */
+ ei_local->irqlock = 0;
+ outb_p(ENISR_ALL, e8390_base + EN0_IMR);
+
+ dev_kfree_skb (skb, FREE_WRITE);
+
+ return 0;
+}
+
+/* The typical workload of the driver:
+ Handle the ether interface interrupts. */
+void ei_interrupt(int irq, struct pt_regs * regs)
+{
+ struct device *dev = (struct device *)(irq2dev_map[irq]);
+ int e8390_base;
+ int interrupts, nr_serviced = 0;
+ struct ei_device *ei_local;
+
+ if (dev == NULL) {
+ printk ("net_interrupt(): irq %d for unknown device.\n", irq);
+ return;
+ }
+ e8390_base = dev->base_addr;
+ ei_local = (struct ei_device *) dev->priv;
+ if (dev->interrupt || ei_local->irqlock) {
+ /* The "irqlock" check is only for testing. */
+ printk(ei_local->irqlock
+ ? "%s: Interrupted while interrupts are masked! isr=%#2x imr=%#2x.\n"
+ : "%s: Reentering the interrupt handler! isr=%#2x imr=%#2x.\n",
+ dev->name, inb_p(e8390_base + EN0_ISR),
+ inb_p(e8390_base + EN0_IMR));
+ return;
+ }
+
+ dev->interrupt = 1;
+
+ /* Change to page 0 and read the intr status reg. */
+ outb_p(E8390_NODMA+E8390_PAGE0, e8390_base + E8390_CMD);
+ if (ei_debug > 3)
+ printk("%s: interrupt(isr=%#2.2x).\n", dev->name,
+ inb_p(e8390_base + EN0_ISR));
+
+ /* !!Assumption!! -- we stay in page 0. Don't break this. */
+ while ((interrupts = inb_p(e8390_base + EN0_ISR)) != 0
+ && ++nr_serviced < MAX_SERVICE) {
+ if (dev->start == 0) {
+ printk("%s: interrupt from stopped card\n", dev->name);
+ interrupts = 0;
+ break;
+ }
+ if (interrupts & ENISR_OVER) {
+ ei_rx_overrun(dev);
+ } else if (interrupts & (ENISR_RX+ENISR_RX_ERR)) {
+ /* Got a good (?) packet. */
+ ei_receive(dev);
+ }
+ /* Push the next to-transmit packet through. */
+ if (interrupts & ENISR_TX) {
+ ei_tx_intr(dev);
+ } else if (interrupts & ENISR_COUNTERS) {
+ ei_local->stat.rx_frame_errors += inb_p(e8390_base + EN0_COUNTER0);
+ ei_local->stat.rx_crc_errors += inb_p(e8390_base + EN0_COUNTER1);
+ ei_local->stat.rx_missed_errors+= inb_p(e8390_base + EN0_COUNTER2);
+ outb_p(ENISR_COUNTERS, e8390_base + EN0_ISR); /* Ack intr. */
+ }
+
+ /* Ignore the transmit errs and reset intr for now. */
+ if (interrupts & ENISR_TX_ERR) {
+ outb_p(ENISR_TX_ERR, e8390_base + EN0_ISR); /* Ack intr. */
+ }
+
+ /* Ignore any RDC interrupts that make it back to here. */
+ if (interrupts & ENISR_RDC) {
+ outb_p(ENISR_RDC, e8390_base + EN0_ISR);
+ }
+
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base + E8390_CMD);
+ }
+
+ if (interrupts && ei_debug) {
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base + E8390_CMD);
+ if (nr_serviced >= MAX_SERVICE) {
+ printk("%s: Too much work at interrupt, status %#2.2x\n",
+ dev->name, interrupts);
+ outb_p(ENISR_ALL, e8390_base + EN0_ISR); /* Ack. most intrs. */
+ } else {
+ printk("%s: unknown interrupt %#2x\n", dev->name, interrupts);
+ outb_p(0xff, e8390_base + EN0_ISR); /* Ack. all intrs. */
+ }
+ }
+ dev->interrupt = 0;
+ return;
+}
+
+/* We have finished a transmit: check for errors and then trigger the next
+ packet to be sent. */
+static void ei_tx_intr(struct device *dev)
+{
+ int e8390_base = dev->base_addr;
+ int status = inb(e8390_base + EN0_TSR);
+ struct ei_device *ei_local = (struct ei_device *) dev->priv;
+
+ outb_p(ENISR_TX, e8390_base + EN0_ISR); /* Ack intr. */
+
+ if (ei_local->pingpong) {
+ ei_local->txqueue--;
+ if (ei_local->tx1 < 0) {
+ if (ei_local->lasttx != 1 && ei_local->lasttx != -1)
+ printk("%s: bogus last_tx_buffer %d, tx1=%d.\n",
+ ei_local->name, ei_local->lasttx, ei_local->tx1);
+ ei_local->tx1 = 0;
+ dev->tbusy = 0;
+ if (ei_local->tx2 > 0) {
+ ei_local->txing = 1;
+ NS8390_trigger_send(dev, ei_local->tx2, ei_local->tx_start_page + 6);
+ dev->trans_start = jiffies;
+ ei_local->tx2 = -1,
+ ei_local->lasttx = 2;
+ } else
+ ei_local->lasttx = 20, ei_local->txing = 0;
+ } else if (ei_local->tx2 < 0) {
+ if (ei_local->lasttx != 2 && ei_local->lasttx != -2)
+ printk("%s: bogus last_tx_buffer %d, tx2=%d.\n",
+ ei_local->name, ei_local->lasttx, ei_local->tx2);
+ ei_local->tx2 = 0;
+ dev->tbusy = 0;
+ if (ei_local->tx1 > 0) {
+ ei_local->txing = 1;
+ NS8390_trigger_send(dev, ei_local->tx1, ei_local->tx_start_page);
+ dev->trans_start = jiffies;
+ ei_local->tx1 = -1;
+ ei_local->lasttx = 1;
+ } else
+ ei_local->lasttx = 10, ei_local->txing = 0;
+ } else
+ printk("%s: unexpected TX-done interrupt, lasttx=%d.\n",
+ dev->name, ei_local->lasttx);
+ } else {
+ ei_local->txing = 0;
+ dev->tbusy = 0;
+ }
+
+ /* Minimize Tx latency: update the statistics after we restart TXing. */
+ if (status & ENTSR_COL) ei_local->stat.collisions++;
+ if (status & ENTSR_PTX)
+ ei_local->stat.tx_packets++;
+ else {
+ ei_local->stat.tx_errors++;
+ if (status & ENTSR_ABT) ei_local->stat.tx_aborted_errors++;
+ if (status & ENTSR_CRS) ei_local->stat.tx_carrier_errors++;
+ if (status & ENTSR_FU) ei_local->stat.tx_fifo_errors++;
+ if (status & ENTSR_CDH) ei_local->stat.tx_heartbeat_errors++;
+ if (status & ENTSR_OWC) ei_local->stat.tx_window_errors++;
+ }
+
+ mark_bh (NET_BH);
+}
+
+/* We have a good packet(s), get it/them out of the buffers. */
+
+static void ei_receive(struct device *dev)
+{
+ int e8390_base = dev->base_addr;
+ struct ei_device *ei_local = (struct ei_device *) dev->priv;
+ int rxing_page, this_frame, next_frame, current_offset;
+ int rx_pkt_count = 0;
+ struct e8390_pkt_hdr rx_frame;
+ int num_rx_pages = ei_local->stop_page-ei_local->rx_start_page;
+
+ while (++rx_pkt_count < 10) {
+ int pkt_len;
+
+ /* Get the rx page (incoming packet pointer). */
+ outb_p(E8390_NODMA+E8390_PAGE1, e8390_base + E8390_CMD);
+ rxing_page = inb_p(e8390_base + EN1_CURPAG);
+ outb_p(E8390_NODMA+E8390_PAGE0, e8390_base + E8390_CMD);
+
+ /* Remove one frame from the ring. Boundary is always a page behind. */
+ this_frame = inb_p(e8390_base + EN0_BOUNDARY) + 1;
+ if (this_frame >= ei_local->stop_page)
+ this_frame = ei_local->rx_start_page;
+
+ /* Someday we'll omit the previous, iff we never get this message.
+ (There is at least one clone claimed to have a problem.) */
+ if (ei_debug > 0 && this_frame != ei_local->current_page)
+ printk("%s: mismatched read page pointers %2x vs %2x.\n",
+ dev->name, this_frame, ei_local->current_page);
+
+ if (this_frame == rxing_page) /* Read all the frames? */
+ break; /* Done for now */
+
+ current_offset = this_frame << 8;
+ ei_get_8390_hdr(dev, &rx_frame, this_frame);
+
+ pkt_len = rx_frame.count - sizeof(struct e8390_pkt_hdr);
+
+ next_frame = this_frame + 1 + ((pkt_len+4)>>8);
+
+ /* Check for bogosity warned by 3c503 book: the status byte is never
+ written. This happened a lot during testing! This code should be
+ cleaned up someday. */
+ if (rx_frame.next != next_frame
+ && rx_frame.next != next_frame + 1
+ && rx_frame.next != next_frame - num_rx_pages
+ && rx_frame.next != next_frame + 1 - num_rx_pages) {
+ ei_local->current_page = rxing_page;
+ outb(ei_local->current_page-1, e8390_base+EN0_BOUNDARY);
+ ei_local->stat.rx_errors++;
+ continue;
+ }
+
+ if (pkt_len < 60 || pkt_len > 1518) {
+ if (ei_debug)
+ printk("%s: bogus packet size: %d, status=%#2x nxpg=%#2x.\n",
+ dev->name, rx_frame.count, rx_frame.status,
+ rx_frame.next);
+ ei_local->stat.rx_errors++;
+ } else if ((rx_frame.status & 0x0F) == ENRSR_RXOK) {
+ struct sk_buff *skb;
+
+ skb = dev_alloc_skb(pkt_len+2);
+ if (skb == NULL) {
+ if (ei_debug > 1)
+ printk("%s: Couldn't allocate a sk_buff of size %d.\n",
+ dev->name, pkt_len);
+ ei_local->stat.rx_dropped++;
+ break;
+ } else {
+ skb_reserve(skb,2); /* IP headers on 16 byte boundaries */
+ skb->dev = dev;
+ skb_put(skb, pkt_len); /* Make room */
+ ei_block_input(dev, pkt_len, skb, current_offset + sizeof(rx_frame));
+ skb->protocol=eth_type_trans(skb,dev);
+ netif_rx(skb);
+ ei_local->stat.rx_packets++;
+ }
+ } else {
+ int errs = rx_frame.status;
+ if (ei_debug)
+ printk("%s: bogus packet: status=%#2x nxpg=%#2x size=%d\n",
+ dev->name, rx_frame.status, rx_frame.next,
+ rx_frame.count);
+ if (errs & ENRSR_FO)
+ ei_local->stat.rx_fifo_errors++;
+ }
+ next_frame = rx_frame.next;
+
+ /* This _should_ never happen: it's here for avoiding bad clones. */
+ if (next_frame >= ei_local->stop_page) {
+ printk("%s: next frame inconsistency, %#2x\n", dev->name,
+ next_frame);
+ next_frame = ei_local->rx_start_page;
+ }
+ ei_local->current_page = next_frame;
+ outb_p(next_frame-1, e8390_base+EN0_BOUNDARY);
+ }
+ /* If any worth-while packets have been received, netif_rx()
+ has done a mark_bh(NET_BH) for us and will work on them
+ when we get to the bottom-half routine. */
+
+ /* Record the maximum Rx packet queue. */
+ if (rx_pkt_count > high_water_mark)
+ high_water_mark = rx_pkt_count;
+
+ /* We used to also ack ENISR_OVER here, but that would sometimes mask
+ a real overrun, leaving the 8390 in a stopped state with rec'vr off. */
+ outb_p(ENISR_RX+ENISR_RX_ERR, e8390_base+EN0_ISR);
+ return;
+}
+
+/* We have a receiver overrun: we have to kick the 8390 to get it started
+ again.*/
+static void ei_rx_overrun(struct device *dev)
+{
+ int e8390_base = dev->base_addr;
+ int reset_start_time = jiffies;
+ struct ei_device *ei_local = (struct ei_device *) dev->priv;
+
+ /* We should already be stopped and in page0. Remove after testing. */
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base+E8390_CMD);
+
+ if (ei_debug > 1)
+ printk("%s: Receiver overrun.\n", dev->name);
+ ei_local->stat.rx_over_errors++;
+
+ /* The old Biro driver does dummy = inb_p( RBCR[01] ); at this point.
+ It might mean something -- magic to speed up a reset? A 8390 bug?*/
+
+ /* Wait for the reset to complete. This should happen almost instantly,
+ but could take up to 1.5msec in certain rare instances. There is no
+ easy way of timing something in that range, so we use 'jiffies' as
+ a sanity check. */
+ while ((inb_p(e8390_base+EN0_ISR) & ENISR_RESET) == 0)
+ if (jiffies - reset_start_time > 2*HZ/100) {
+ printk("%s: reset did not complete at ei_rx_overrun.\n",
+ dev->name);
+ NS8390_init(dev, 1);
+ return;
+ }
+
+ /* Remove packets right away. */
+ ei_receive(dev);
+
+ outb_p(ENISR_OVER, e8390_base+EN0_ISR);
+ /* Generic 8390 insns to start up again, same as in open_8390(). */
+ outb_p(E8390_NODMA + E8390_PAGE0 + E8390_START, e8390_base + E8390_CMD);
+ outb_p(E8390_TXCONFIG, e8390_base + EN0_TXCR); /* xmit on. */
+}
+
+static struct enet_statistics *get_stats(struct device *dev)
+{
+ short ioaddr = dev->base_addr;
+ struct ei_device *ei_local = (struct ei_device *) dev->priv;
+
+ /* If the card is stopped, just return the present stats. */
+ if (dev->start == 0) return &ei_local->stat;
+
+ /* Read the counter registers, assuming we are in page 0. */
+ ei_local->stat.rx_frame_errors += inb_p(ioaddr + EN0_COUNTER0);
+ ei_local->stat.rx_crc_errors += inb_p(ioaddr + EN0_COUNTER1);
+ ei_local->stat.rx_missed_errors+= inb_p(ioaddr + EN0_COUNTER2);
+
+ return &ei_local->stat;
+}
+
+/*
+ * Set or clear the multicast filter for this adaptor.
+ */
+
+static void set_multicast_list(struct device *dev)
+{
+ short ioaddr = dev->base_addr;
+
+ if(dev->flags&IFF_PROMISC)
+ {
+ outb_p(E8390_RXCONFIG | 0x18, ioaddr + EN0_RXCR);
+ }
+ else if((dev->flags&IFF_ALLMULTI)||dev->mc_list)
+ {
+ /* The multicast-accept list is initialized to accept-all, and we
+ rely on higher-level filtering for now. */
+ outb_p(E8390_RXCONFIG | 0x08, ioaddr + EN0_RXCR);
+ }
+ else
+ outb_p(E8390_RXCONFIG, ioaddr + EN0_RXCR);
+}
+
+/* Initialize the rest of the 8390 device structure. */
+int ethdev_init(struct device *dev)
+{
+ if (ei_debug > 1)
+ printk(version);
+
+ if (dev->priv == NULL) {
+ struct ei_device *ei_local;
+
+ dev->priv = kmalloc(sizeof(struct ei_device), GFP_KERNEL);
+ if (dev->priv == NULL)
+ return -ENOMEM;
+ memset(dev->priv, 0, sizeof(struct ei_device));
+ ei_local = (struct ei_device *)dev->priv;
+ ei_local->pingpong = ei_pingpong;
+ }
+
+ dev->hard_start_xmit = &ei_start_xmit;
+ dev->get_stats = get_stats;
+ dev->set_multicast_list = &set_multicast_list;
+
+ ether_setup(dev);
+
+ return 0;
+}
+
+
+/* This page of functions should be 8390 generic */
+/* Follow National Semi's recommendations for initializing the "NIC". */
+void NS8390_init(struct device *dev, int startp)
+{
+ int e8390_base = dev->base_addr;
+ struct ei_device *ei_local = (struct ei_device *) dev->priv;
+ int i;
+ int endcfg = ei_local->word16 ? (0x48 | ENDCFG_WTS) : 0x48;
+ unsigned long flags;
+
+ /* Follow National Semi's recommendations for initing the DP83902. */
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base); /* 0x21 */
+ outb_p(endcfg, e8390_base + EN0_DCFG); /* 0x48 or 0x49 */
+ /* Clear the remote byte count registers. */
+ outb_p(0x00, e8390_base + EN0_RCNTLO);
+ outb_p(0x00, e8390_base + EN0_RCNTHI);
+ /* Set to monitor and loopback mode -- this is vital!. */
+ outb_p(E8390_RXOFF, e8390_base + EN0_RXCR); /* 0x20 */
+ outb_p(E8390_TXOFF, e8390_base + EN0_TXCR); /* 0x02 */
+ /* Set the transmit page and receive ring. */
+ outb_p(ei_local->tx_start_page, e8390_base + EN0_TPSR);
+ ei_local->tx1 = ei_local->tx2 = 0;
+ outb_p(ei_local->rx_start_page, e8390_base + EN0_STARTPG);
+ outb_p(ei_local->stop_page-1, e8390_base + EN0_BOUNDARY); /* 3c503 says 0x3f,NS0x26*/
+ ei_local->current_page = ei_local->rx_start_page; /* assert boundary+1 */
+ outb_p(ei_local->stop_page, e8390_base + EN0_STOPPG);
+ /* Clear the pending interrupts and mask. */
+ outb_p(0xFF, e8390_base + EN0_ISR);
+ outb_p(0x00, e8390_base + EN0_IMR);
+
+ /* Copy the station address into the DS8390 registers,
+ and set the multicast hash bitmap to receive all multicasts. */
+ save_flags(flags);
+ cli();
+ outb_p(E8390_NODMA + E8390_PAGE1 + E8390_STOP, e8390_base); /* 0x61 */
+ for(i = 0; i < 6; i++) {
+ outb_p(dev->dev_addr[i], e8390_base + EN1_PHYS + i);
+ }
+ /* Initialize the multicast list to accept-all. If we enable multicast
+ the higher levels can do the filtering. */
+ for(i = 0; i < 8; i++)
+ outb_p(0xff, e8390_base + EN1_MULT + i);
+
+ outb_p(ei_local->rx_start_page, e8390_base + EN1_CURPAG);
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base);
+ restore_flags(flags);
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ ei_local->tx1 = ei_local->tx2 = 0;
+ ei_local->txing = 0;
+ if (startp) {
+ outb_p(0xff, e8390_base + EN0_ISR);
+ outb_p(ENISR_ALL, e8390_base + EN0_IMR);
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base);
+ outb_p(E8390_TXCONFIG, e8390_base + EN0_TXCR); /* xmit on. */
+ /* 3c503 TechMan says rxconfig only after the NIC is started. */
+ outb_p(E8390_RXCONFIG, e8390_base + EN0_RXCR); /* rx on, */
+ dev->set_multicast_list(dev); /* Get the multicast status right if this
+ was a reset. */
+ }
+ return;
+}
+
+/* Trigger a transmit start, assuming the length is valid. */
+static void NS8390_trigger_send(struct device *dev, unsigned int length,
+ int start_page)
+{
+ int e8390_base = dev->base_addr;
+
+ outb_p(E8390_NODMA+E8390_PAGE0, e8390_base);
+
+ if (inb_p(e8390_base) & E8390_TRANS) {
+ printk("%s: trigger_send() called with the transmitter busy.\n",
+ dev->name);
+ return;
+ }
+ outb_p(length & 0xff, e8390_base + EN0_TCNTLO);
+ outb_p(length >> 8, e8390_base + EN0_TCNTHI);
+ outb_p(start_page, e8390_base + EN0_TPSR);
+ outb_p(E8390_NODMA+E8390_TRANS+E8390_START, e8390_base);
+ return;
+}
+
+#ifdef MODULE
+
+int init_module(void)
+{
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+}
+#endif /* MODULE */
+
+/*
+ * Local variables:
+ * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c 8390.c"
+ * version-control: t
+ * kept-new-versions: 5
+ * c-indent-level: 4
+ * tab-width: 4
+ * End:
+ */
diff --git a/i386/i386at/gpl/linux/net/8390.h b/i386/i386at/gpl/linux/net/8390.h
new file mode 100644
index 00000000..17b8cdb5
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/8390.h
@@ -0,0 +1,168 @@
+/* Generic NS8390 register definitions. */
+/* This file is part of Donald Becker's 8390 drivers, and is distributed
+ under the same license.
+ Some of these names and comments originated from the Crynwr
+ packet drivers, which are distributed under the GPL. */
+
+#ifndef _8390_h
+#define _8390_h
+
+#include <linux/if_ether.h>
+#include <linux/ioport.h>
+#include <linux/skbuff.h>
+
+#define TX_2X_PAGES 12
+#define TX_1X_PAGES 6
+#define TX_PAGES (ei_status.pingpong ? TX_2X_PAGES : TX_1X_PAGES)
+
+#define ETHER_ADDR_LEN 6
+
+/* The 8390 specific per-packet-header format. */
+struct e8390_pkt_hdr {
+ unsigned char status; /* status */
+ unsigned char next; /* pointer to next packet. */
+ unsigned short count; /* header + packet length in bytes */
+};
+
+/* From 8390.c */
+extern int ei_debug;
+extern struct sigaction ei_sigaction;
+
+extern int ethif_init(struct device *dev);
+extern int ethdev_init(struct device *dev);
+extern void NS8390_init(struct device *dev, int startp);
+extern int ei_open(struct device *dev);
+extern int ei_close(struct device *dev);
+extern void ei_interrupt(int irq, struct pt_regs *regs);
+
+#ifndef HAVE_AUTOIRQ
+/* From auto_irq.c */
+extern struct device *irq2dev_map[16];
+extern int autoirq_setup(int waittime);
+extern int autoirq_report(int waittime);
+#endif
+
+/* Most of these entries should be in 'struct device' (or most of the
+ things in there should be here!) */
+/* You have one of these per-board */
+struct ei_device {
+ const char *name;
+ void (*reset_8390)(struct device *);
+ void (*get_8390_hdr)(struct device *, struct e8390_pkt_hdr *, int);
+ void (*block_output)(struct device *, int, const unsigned char *, int);
+ void (*block_input)(struct device *, int, struct sk_buff *, int);
+ unsigned open:1;
+ unsigned word16:1; /* We have the 16-bit (vs 8-bit) version of the card. */
+ unsigned txing:1; /* Transmit Active */
+ unsigned irqlock:1; /* 8390's intrs disabled when '1'. */
+ unsigned dmaing:1; /* Remote DMA Active */
+ unsigned pingpong:1; /* Using the ping-pong driver */
+ unsigned char tx_start_page, rx_start_page, stop_page;
+ unsigned char current_page; /* Read pointer in buffer */
+ unsigned char interface_num; /* Net port (AUI, 10bT.) to use. */
+ unsigned char txqueue; /* Tx Packet buffer queue length. */
+ short tx1, tx2; /* Packet lengths for ping-pong tx. */
+ short lasttx; /* Alpha version consistency check. */
+ unsigned char reg0; /* Register '0' in a WD8013 */
+ unsigned char reg5; /* Register '5' in a WD8013 */
+ unsigned char saved_irq; /* Original dev->irq value. */
+ /* The new statistics table. */
+ struct enet_statistics stat;
+};
+
+/* The maximum number of 8390 interrupt service routines called per IRQ. */
+#define MAX_SERVICE 12
+
+/* The maximum time waited (in jiffies) before assuming a Tx failed. (20ms) */
+#define TX_TIMEOUT (20*HZ/100)
+
+#define ei_status (*(struct ei_device *)(dev->priv))
+
+/* Some generic ethernet register configurations. */
+#define E8390_TX_IRQ_MASK 0xa /* For register EN0_ISR */
+#define E8390_RX_IRQ_MASK 0x5
+#define E8390_RXCONFIG 0x4 /* EN0_RXCR: broadcasts, no multicast,errors */
+#define E8390_RXOFF 0x20 /* EN0_RXCR: Accept no packets */
+#define E8390_TXCONFIG 0x00 /* EN0_TXCR: Normal transmit mode */
+#define E8390_TXOFF 0x02 /* EN0_TXCR: Transmitter off */
+
+/* Register accessed at EN_CMD, the 8390 base addr. */
+#define E8390_STOP 0x01 /* Stop and reset the chip */
+#define E8390_START 0x02 /* Start the chip, clear reset */
+#define E8390_TRANS 0x04 /* Transmit a frame */
+#define E8390_RREAD 0x08 /* Remote read */
+#define E8390_RWRITE 0x10 /* Remote write */
+#define E8390_NODMA 0x20 /* Remote DMA */
+#define E8390_PAGE0 0x00 /* Select page chip registers */
+#define E8390_PAGE1 0x40 /* using the two high-order bits */
+#define E8390_PAGE2 0x80 /* Page 3 is invalid. */
+
+#define E8390_CMD 0x00 /* The command register (for all pages) */
+/* Page 0 register offsets. */
+#define EN0_CLDALO 0x01 /* Low byte of current local dma addr RD */
+#define EN0_STARTPG 0x01 /* Starting page of ring bfr WR */
+#define EN0_CLDAHI 0x02 /* High byte of current local dma addr RD */
+#define EN0_STOPPG 0x02 /* Ending page +1 of ring bfr WR */
+#define EN0_BOUNDARY 0x03 /* Boundary page of ring bfr RD WR */
+#define EN0_TSR 0x04 /* Transmit status reg RD */
+#define EN0_TPSR 0x04 /* Transmit starting page WR */
+#define EN0_NCR 0x05 /* Number of collision reg RD */
+#define EN0_TCNTLO 0x05 /* Low byte of tx byte count WR */
+#define EN0_FIFO 0x06 /* FIFO RD */
+#define EN0_TCNTHI 0x06 /* High byte of tx byte count WR */
+#define EN0_ISR 0x07 /* Interrupt status reg RD WR */
+#define EN0_CRDALO 0x08 /* low byte of current remote dma address RD */
+#define EN0_RSARLO 0x08 /* Remote start address reg 0 */
+#define EN0_CRDAHI 0x09 /* high byte, current remote dma address RD */
+#define EN0_RSARHI 0x09 /* Remote start address reg 1 */
+#define EN0_RCNTLO 0x0a /* Remote byte count reg WR */
+#define EN0_RCNTHI 0x0b /* Remote byte count reg WR */
+#define EN0_RSR 0x0c /* rx status reg RD */
+#define EN0_RXCR 0x0c /* RX configuration reg WR */
+#define EN0_TXCR 0x0d /* TX configuration reg WR */
+#define EN0_COUNTER0 0x0d /* Rcv alignment error counter RD */
+#define EN0_DCFG 0x0e /* Data configuration reg WR */
+#define EN0_COUNTER1 0x0e /* Rcv CRC error counter RD */
+#define EN0_IMR 0x0f /* Interrupt mask reg WR */
+#define EN0_COUNTER2 0x0f /* Rcv missed frame error counter RD */
+
+/* Bits in EN0_ISR - Interrupt status register */
+#define ENISR_RX 0x01 /* Receiver, no error */
+#define ENISR_TX 0x02 /* Transmitter, no error */
+#define ENISR_RX_ERR 0x04 /* Receiver, with error */
+#define ENISR_TX_ERR 0x08 /* Transmitter, with error */
+#define ENISR_OVER 0x10 /* Receiver overwrote the ring */
+#define ENISR_COUNTERS 0x20 /* Counters need emptying */
+#define ENISR_RDC 0x40 /* remote dma complete */
+#define ENISR_RESET 0x80 /* Reset completed */
+#define ENISR_ALL 0x3f /* Interrupts we will enable */
+
+/* Bits in EN0_DCFG - Data config register */
+#define ENDCFG_WTS 0x01 /* word transfer mode selection */
+
+/* Page 1 register offsets. */
+#define EN1_PHYS 0x01 /* This board's physical enet addr RD WR */
+#define EN1_CURPAG 0x07 /* Current memory page RD WR */
+#define EN1_MULT 0x08 /* Multicast filter mask array (8 bytes) RD WR */
+
+/* Bits in received packet status byte and EN0_RSR*/
+#define ENRSR_RXOK 0x01 /* Received a good packet */
+#define ENRSR_CRC 0x02 /* CRC error */
+#define ENRSR_FAE 0x04 /* frame alignment error */
+#define ENRSR_FO 0x08 /* FIFO overrun */
+#define ENRSR_MPA 0x10 /* missed pkt */
+#define ENRSR_PHY 0x20 /* physical/multicase address */
+#define ENRSR_DIS 0x40 /* receiver disable. set in monitor mode */
+#define ENRSR_DEF 0x80 /* deferring */
+
+/* Transmitted packet status, EN0_TSR. */
+#define ENTSR_PTX 0x01 /* Packet transmitted without error */
+#define ENTSR_ND 0x02 /* The transmit wasn't deferred. */
+#define ENTSR_COL 0x04 /* The transmit collided at least once. */
+#define ENTSR_ABT 0x08 /* The transmit collided 16 times, and was deferred. */
+#define ENTSR_CRS 0x10 /* The carrier sense was lost. */
+#define ENTSR_FU 0x20 /* A "FIFO underrun" occurred during transmit. */
+#define ENTSR_CDH 0x40 /* The collision detect "heartbeat" signal was lost. */
+#define ENTSR_OWC 0x80 /* There was an out-of-window collision. */
+
+#endif /* _8390_h */
diff --git a/i386/i386at/gpl/linux/net/Space.c b/i386/i386at/gpl/linux/net/Space.c
new file mode 100644
index 00000000..a05507d3
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/Space.c
@@ -0,0 +1,400 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Holds initial configuration information for devices.
+ *
+ * NOTE: This file is a nice idea, but its current format does not work
+ * well for drivers that support multiple units, like the SLIP
+ * driver. We should actually have only one pointer to a driver
+ * here, with the driver knowing how many units it supports.
+ * Currently, the SLIP driver abuses the "base_addr" integer
+ * field of the 'device' structure to store the unit number...
+ * -FvK
+ *
+ * Version: @(#)Space.c 1.0.7 08/12/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Donald J. Becker, <becker@super.org>
+ *
+ * FIXME:
+ * Sort the device chain fastest first.
+ *
+ * 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.
+ */
+#include <linux/config.h>
+#include <linux/netdevice.h>
+#include <linux/errno.h>
+
+#define NEXT_DEV NULL
+
+
+/* A unified ethernet device probe. This is the easiest way to have every
+ ethernet adaptor have the name "eth[0123...]".
+ */
+
+extern int hp100_probe(struct device *dev);
+extern int ultra_probe(struct device *dev);
+extern int wd_probe(struct device *dev);
+extern int el2_probe(struct device *dev);
+extern int ne_probe(struct device *dev);
+extern int hp_probe(struct device *dev);
+extern int hp_plus_probe(struct device *dev);
+extern int znet_probe(struct device *);
+extern int express_probe(struct device *);
+extern int eepro_probe(struct device *);
+extern int el3_probe(struct device *);
+extern int at1500_probe(struct device *);
+extern int at1700_probe(struct device *);
+extern int eth16i_probe(struct device *);
+extern int depca_probe(struct device *);
+extern int apricot_probe(struct device *);
+extern int ewrk3_probe(struct device *);
+extern int de4x5_probe(struct device *);
+extern int el1_probe(struct device *);
+#if defined(CONFIG_WAVELAN)
+extern int wavelan_probe(struct device *);
+#endif /* defined(CONFIG_WAVELAN) */
+extern int el16_probe(struct device *);
+extern int elplus_probe(struct device *);
+extern int ac3200_probe(struct device *);
+extern int e2100_probe(struct device *);
+extern int ni52_probe(struct device *);
+extern int ni65_probe(struct device *);
+extern int SK_init(struct device *);
+extern int seeq8005_probe(struct device *);
+extern int tc59x_probe(struct device *);
+
+/* Detachable devices ("pocket adaptors") */
+extern int atp_init(struct device *);
+extern int de600_probe(struct device *);
+extern int de620_probe(struct device *);
+
+static int
+ethif_probe(struct device *dev)
+{
+ u_long base_addr = dev->base_addr;
+
+ if ((base_addr == 0xffe0) || (base_addr == 1))
+ return 1; /* ENXIO */
+
+ if (1
+#if defined(CONFIG_VORTEX)
+ && tc59x_probe(dev)
+#endif
+#if defined(CONFIG_SEEQ8005)
+ && seeq8005_probe(dev)
+#endif
+#if defined(CONFIG_HP100)
+ && hp100_probe(dev)
+#endif
+#if defined(CONFIG_ULTRA)
+ && ultra_probe(dev)
+#endif
+#if defined(CONFIG_WD80x3) || defined(WD80x3)
+
+ && wd_probe(dev)
+#endif
+#if defined(CONFIG_EL2) || defined(EL2) /* 3c503 */
+ && el2_probe(dev)
+#endif
+#if defined(CONFIG_HPLAN) || defined(HPLAN)
+ && hp_probe(dev)
+#endif
+#if 0
+#if defined(CONFIG_HPLAN_PLUS)
+ && hp_plus_probe(dev)
+#endif
+#endif
+#ifdef CONFIG_AC3200 /* Ansel Communications EISA 3200. */
+ && ac3200_probe(dev)
+#endif
+#ifdef CONFIG_E2100 /* Cabletron E21xx series. */
+ && e2100_probe(dev)
+#endif
+#if defined(CONFIG_NE2000) || defined(NE2000)
+ && ne_probe(dev)
+#endif
+#ifdef CONFIG_AT1500
+ && at1500_probe(dev)
+#endif
+#ifdef CONFIG_AT1700
+ && at1700_probe(dev)
+#endif
+#ifdef CONFIG_ETH16I
+ && eth16i_probe(dev) /* ICL EtherTeam 16i/32 */
+#endif
+#ifdef CONFIG_EL3 /* 3c509 */
+ && el3_probe(dev)
+#endif
+#ifdef CONFIG_ZNET /* Zenith Z-Note and some IBM Thinkpads. */
+ && znet_probe(dev)
+#endif
+#ifdef CONFIG_EEXPRESS /* Intel EtherExpress */
+ && express_probe(dev)
+#endif
+#ifdef CONFIG_EEXPRESS_PRO /* Intel EtherExpress Pro/10 */
+ && eepro_probe(dev)
+#endif
+#ifdef CONFIG_DEPCA /* DEC DEPCA */
+ && depca_probe(dev)
+#endif
+#ifdef CONFIG_EWRK3 /* DEC EtherWORKS 3 */
+ && ewrk3_probe(dev)
+#endif
+#ifdef CONFIG_DE4X5 /* DEC DE425, DE434, DE435 adapters */
+ && de4x5_probe(dev)
+#endif
+#ifdef CONFIG_APRICOT /* Apricot I82596 */
+ && apricot_probe(dev)
+#endif
+#ifdef CONFIG_EL1 /* 3c501 */
+ && el1_probe(dev)
+#endif
+#if defined(CONFIG_WAVELAN) /* WaveLAN */
+ && wavelan_probe(dev)
+#endif /* defined(CONFIG_WAVELAN) */
+#ifdef CONFIG_EL16 /* 3c507 */
+ && el16_probe(dev)
+#endif
+#ifdef CONFIG_ELPLUS /* 3c505 */
+ && elplus_probe(dev)
+#endif
+#ifdef CONFIG_DE600 /* D-Link DE-600 adapter */
+ && de600_probe(dev)
+#endif
+#ifdef CONFIG_DE620 /* D-Link DE-620 adapter */
+ && de620_probe(dev)
+#endif
+#if defined(CONFIG_SK_G16)
+ && SK_init(dev)
+#endif
+#ifdef CONFIG_NI52
+ && ni52_probe(dev)
+#endif
+#ifdef CONFIG_NI65
+ && ni65_probe(dev)
+#endif
+ && 1 ) {
+ return 1; /* -ENODEV or -EAGAIN would be more accurate. */
+ }
+ return 0;
+}
+
+
+#ifdef CONFIG_NETROM
+ extern int nr_init(struct device *);
+
+ static struct device nr3_dev = { "nr3", 0, 0, 0, 0, 0, 0, 0, 0, 0, NEXT_DEV, nr_init, };
+ static struct device nr2_dev = { "nr2", 0, 0, 0, 0, 0, 0, 0, 0, 0, &nr3_dev, nr_init, };
+ static struct device nr1_dev = { "nr1", 0, 0, 0, 0, 0, 0, 0, 0, 0, &nr2_dev, nr_init, };
+ static struct device nr0_dev = { "nr0", 0, 0, 0, 0, 0, 0, 0, 0, 0, &nr1_dev, nr_init, };
+
+# undef NEXT_DEV
+# define NEXT_DEV (&nr0_dev)
+#endif
+
+/* Run-time ATtachable (Pocket) devices have a different (not "eth#") name. */
+#ifdef CONFIG_ATP /* AT-LAN-TEC (RealTek) pocket adaptor. */
+static struct device atp_dev = {
+ "atp0", 0, 0, 0, 0, 0, 0, 0, 0, 0, NEXT_DEV, atp_init, /* ... */ };
+# undef NEXT_DEV
+# define NEXT_DEV (&atp_dev)
+#endif
+
+#ifdef CONFIG_ARCNET
+ extern int arcnet_probe(struct device *dev);
+ static struct device arcnet_dev = {
+ "arc0", 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, NEXT_DEV, arcnet_probe, };
+# undef NEXT_DEV
+# define NEXT_DEV (&arcnet_dev)
+#endif
+
+/* In Mach, by default allow at least 2 interfaces. */
+#ifdef MACH
+#ifndef ETH1_ADDR
+# define ETH1_ADDR 0
+#endif
+#ifndef ETH1_IRQ
+# define ETH1_IRQ 0
+#endif
+#endif
+
+/* The first device defaults to I/O base '0', which means autoprobe. */
+#ifndef ETH0_ADDR
+# define ETH0_ADDR 0
+#endif
+#ifndef ETH0_IRQ
+# define ETH0_IRQ 0
+#endif
+/* "eth0" defaults to autoprobe (== 0), other use a base of 0xffe0 (== -0x20),
+ which means "don't probe". These entries exist to only to provide empty
+ slots which may be enabled at boot-time. */
+
+static struct device eth3_dev = {
+ "eth3", 0,0,0,0,0xffe0 /* I/O base*/, 0,0,0,0, NEXT_DEV, ethif_probe };
+static struct device eth2_dev = {
+ "eth2", 0,0,0,0,0xffe0 /* I/O base*/, 0,0,0,0, &eth3_dev, ethif_probe };
+#ifdef MACH
+static struct device eth1_dev = {
+ "eth1", 0,0,0,0,ETH1_ADDR, ETH1_IRQ,0,0,0, &eth2_dev, ethif_probe };
+#else
+static struct device eth1_dev = {
+ "eth1", 0,0,0,0,0xffe0 /* I/O base*/, 0,0,0,0, &eth2_dev, ethif_probe };
+#endif
+static struct device eth0_dev = {
+ "eth0", 0, 0, 0, 0, ETH0_ADDR, ETH0_IRQ, 0, 0, 0, &eth1_dev, ethif_probe };
+
+# undef NEXT_DEV
+# define NEXT_DEV (&eth0_dev)
+
+#if defined(PLIP) || defined(CONFIG_PLIP)
+ extern int plip_init(struct device *);
+ static struct device plip2_dev = {
+ "plip2", 0, 0, 0, 0, 0x278, 2, 0, 0, 0, NEXT_DEV, plip_init, };
+ static struct device plip1_dev = {
+ "plip1", 0, 0, 0, 0, 0x378, 7, 0, 0, 0, &plip2_dev, plip_init, };
+ static struct device plip0_dev = {
+ "plip0", 0, 0, 0, 0, 0x3BC, 5, 0, 0, 0, &plip1_dev, plip_init, };
+# undef NEXT_DEV
+# define NEXT_DEV (&plip0_dev)
+#endif /* PLIP */
+
+#if defined(SLIP) || defined(CONFIG_SLIP)
+ /* To be exact, this node just hooks the initialization
+ routines to the device structures. */
+extern int slip_init_ctrl_dev(struct device *);
+static struct device slip_bootstrap = {
+ "slip_proto", 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, NEXT_DEV, slip_init_ctrl_dev, };
+#undef NEXT_DEV
+#define NEXT_DEV (&slip_bootstrap)
+#endif /* SLIP */
+
+#if defined(CONFIG_PPP)
+extern int ppp_init(struct device *);
+static struct device ppp_bootstrap = {
+ "ppp_proto", 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, NEXT_DEV, ppp_init, };
+#undef NEXT_DEV
+#define NEXT_DEV (&ppp_bootstrap)
+#endif /* PPP */
+
+#ifdef CONFIG_DUMMY
+ extern int dummy_init(struct device *dev);
+ static struct device dummy_dev = {
+ "dummy", 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, NEXT_DEV, dummy_init, };
+# undef NEXT_DEV
+# define NEXT_DEV (&dummy_dev)
+#endif
+
+#ifdef CONFIG_EQUALIZER
+extern int eql_init(struct device *dev);
+struct device eql_dev = {
+ "eql", /* Master device for IP traffic load
+ balancing */
+ 0x0, 0x0, 0x0, 0x0, /* recv end/start; mem end/start */
+ 0, /* base I/O address */
+ 0, /* IRQ */
+ 0, 0, 0, /* flags */
+ NEXT_DEV, /* next device */
+ eql_init /* set up the rest */
+};
+# undef NEXT_DEV
+# define NEXT_DEV (&eql_dev)
+#endif
+
+#ifdef CONFIG_IBMTR
+
+ extern int tok_probe(struct device *dev);
+ static struct device ibmtr_dev1 = {
+ "tr1", /* IBM Token Ring (Non-DMA) Interface */
+ 0x0, /* recv memory end */
+ 0x0, /* recv memory start */
+ 0x0, /* memory end */
+ 0x0, /* memory start */
+ 0xa24, /* base I/O address */
+ 0, /* IRQ */
+ 0, 0, 0, /* flags */
+ NEXT_DEV, /* next device */
+ tok_probe /* ??? Token_init should set up the rest */
+ };
+# undef NEXT_DEV
+# define NEXT_DEV (&ibmtr_dev1)
+
+
+ static struct device ibmtr_dev0 = {
+ "tr0", /* IBM Token Ring (Non-DMA) Interface */
+ 0x0, /* recv memory end */
+ 0x0, /* recv memory start */
+ 0x0, /* memory end */
+ 0x0, /* memory start */
+ 0xa20, /* base I/O address */
+ 0, /* IRQ */
+ 0, 0, 0, /* flags */
+ NEXT_DEV, /* next device */
+ tok_probe /* ??? Token_init should set up the rest */
+ };
+# undef NEXT_DEV
+# define NEXT_DEV (&ibmtr_dev0)
+
+#endif
+#ifdef CONFIG_NET_IPIP
+#ifdef CONFIG_IP_FORWARD
+ extern int tunnel_init(struct device *);
+
+ static struct device tunnel_dev1 =
+ {
+ "tunl1", /* IPIP tunnel */
+ 0x0, /* recv memory end */
+ 0x0, /* recv memory start */
+ 0x0, /* memory end */
+ 0x0, /* memory start */
+ 0x0, /* base I/O address */
+ 0, /* IRQ */
+ 0, 0, 0, /* flags */
+ NEXT_DEV, /* next device */
+ tunnel_init /* Fill in the details */
+ };
+
+ static struct device tunnel_dev0 =
+ {
+ "tunl0", /* IPIP tunnel */
+ 0x0, /* recv memory end */
+ 0x0, /* recv memory start */
+ 0x0, /* memory end */
+ 0x0, /* memory start */
+ 0x0, /* base I/O address */
+ 0, /* IRQ */
+ 0, 0, 0, /* flags */
+ &tunnel_dev1, /* next device */
+ tunnel_init /* Fill in the details */
+ };
+# undef NEXT_DEV
+# define NEXT_DEV (&tunnel_dev0)
+
+#endif
+#endif
+
+#ifdef MACH
+struct device *dev_base = &eth0_dev;
+#else
+extern int loopback_init(struct device *dev);
+struct device loopback_dev = {
+ "lo", /* Software Loopback interface */
+ 0x0, /* recv memory end */
+ 0x0, /* recv memory start */
+ 0x0, /* memory end */
+ 0x0, /* memory start */
+ 0, /* base I/O address */
+ 0, /* IRQ */
+ 0, 0, 0, /* flags */
+ NEXT_DEV, /* next device */
+ loopback_init /* loopback_init should set up the rest */
+};
+
+struct device *dev_base = &loopback_dev;
+#endif
diff --git a/i386/i386at/gpl/linux/net/ac3200.c b/i386/i386at/gpl/linux/net/ac3200.c
new file mode 100644
index 00000000..054af13a
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/ac3200.c
@@ -0,0 +1,385 @@
+/* ac3200.c: A driver for the Ansel Communications EISA ethernet adaptor. */
+/*
+ Written 1993, 1994 by Donald Becker.
+ Copyright 1993 United States Government as represented by the Director,
+ National Security Agency. This software may only be used and distributed
+ according to the terms of the GNU Public License as modified by SRC,
+ incorporated herein by reference.
+
+ The author may be reached as becker@cesdis.gsfc.nasa.gov, or
+ C/O Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
+
+ This is driver for the Ansel Communications Model 3200 EISA Ethernet LAN
+ Adapter. The programming information is from the users manual, as related
+ by glee@ardnassak.math.clemson.edu.
+ */
+
+static const char *version =
+ "ac3200.c:v1.01 7/1/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+
+#include "8390.h"
+
+/* Offsets from the base address. */
+#define AC_NIC_BASE 0x00
+#define AC_SA_PROM 0x16 /* The station address PROM. */
+#define AC_ADDR0 0x00 /* Prefix station address values. */
+#define AC_ADDR1 0x40 /* !!!!These are just guesses!!!! */
+#define AC_ADDR2 0x90
+#define AC_ID_PORT 0xC80
+#define AC_EISA_ID 0x0110d305
+#define AC_RESET_PORT 0xC84
+#define AC_RESET 0x00
+#define AC_ENABLE 0x01
+#define AC_CONFIG 0xC90 /* The configuration port. */
+
+#define AC_IO_EXTENT 0x10 /* IS THIS REALLY TRUE ??? */
+ /* Actually accessed is:
+ * AC_NIC_BASE (0-15)
+ * AC_SA_PROM (0-5)
+ * AC_ID_PORT (0-3)
+ * AC_RESET_PORT
+ * AC_CONFIG
+ */
+
+/* Decoding of the configuration register. */
+static unsigned char config2irqmap[8] = {15, 12, 11, 10, 9, 7, 5, 3};
+static int addrmap[8] =
+{0xFF0000, 0xFE0000, 0xFD0000, 0xFFF0000, 0xFFE0000, 0xFFC0000, 0xD0000, 0 };
+static const char *port_name[4] = { "10baseT", "invalid", "AUI", "10base2"};
+
+#define config2irq(configval) config2irqmap[((configval) >> 3) & 7]
+#define config2mem(configval) addrmap[(configval) & 7]
+#define config2name(configval) port_name[((configval) >> 6) & 3]
+
+/* First and last 8390 pages. */
+#define AC_START_PG 0x00 /* First page of 8390 TX buffer */
+#define AC_STOP_PG 0x80 /* Last page +1 of the 8390 RX ring */
+
+int ac3200_probe(struct device *dev);
+static int ac_probe1(int ioaddr, struct device *dev);
+
+static int ac_open(struct device *dev);
+static void ac_reset_8390(struct device *dev);
+static void ac_block_input(struct device *dev, int count,
+ struct sk_buff *skb, int ring_offset);
+static void ac_block_output(struct device *dev, const int count,
+ const unsigned char *buf, const int start_page);
+static void ac_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
+ int ring_page);
+
+static int ac_close_card(struct device *dev);
+
+
+/* Probe for the AC3200.
+
+ The AC3200 can be identified by either the EISA configuration registers,
+ or the unique value in the station address PROM.
+ */
+
+int ac3200_probe(struct device *dev)
+{
+ unsigned short ioaddr = dev->base_addr;
+
+ if (ioaddr > 0x1ff) /* Check a single specified location. */
+ return ac_probe1(ioaddr, dev);
+ else if (ioaddr > 0) /* Don't probe at all. */
+ return ENXIO;
+
+ /* If you have a pre 0.99pl15 machine you should delete this line. */
+ if ( ! EISA_bus)
+ return ENXIO;
+
+ for (ioaddr = 0x1000; ioaddr < 0x9000; ioaddr += 0x1000) {
+ if (check_region(ioaddr, AC_IO_EXTENT))
+ continue;
+ if (ac_probe1(ioaddr, dev) == 0)
+ return 0;
+ }
+
+ return ENODEV;
+}
+
+static int ac_probe1(int ioaddr, struct device *dev)
+{
+ int i;
+
+#ifndef final_version
+ printk("AC3200 ethercard probe at %#3x:", ioaddr);
+
+ for(i = 0; i < 6; i++)
+ printk(" %02x", inb(ioaddr + AC_SA_PROM + i));
+#endif
+
+ /* !!!!The values of AC_ADDRn (see above) should be corrected when we
+ find out the correct station address prefix!!!! */
+ if (inb(ioaddr + AC_SA_PROM + 0) != AC_ADDR0
+ || inb(ioaddr + AC_SA_PROM + 1) != AC_ADDR1
+ || inb(ioaddr + AC_SA_PROM + 2) != AC_ADDR2 ) {
+#ifndef final_version
+ printk(" not found (invalid prefix).\n");
+#endif
+ return ENODEV;
+ }
+
+ /* The correct probe method is to check the EISA ID. */
+ for (i = 0; i < 4; i++)
+ if (inl(ioaddr + AC_ID_PORT) != AC_EISA_ID) {
+ printk("EISA ID mismatch, %8x vs %8x.\n",
+ inl(ioaddr + AC_EISA_ID), AC_EISA_ID);
+ return ENODEV;
+ }
+
+
+ /* We should have a "dev" from Space.c or the static module table. */
+ if (dev == NULL) {
+ printk("ac3200.c: Passed a NULL device.\n");
+ dev = init_etherdev(0, 0);
+ }
+
+ for(i = 0; i < ETHER_ADDR_LEN; i++)
+ dev->dev_addr[i] = inb(ioaddr + AC_SA_PROM + i);
+
+#ifndef final_version
+ printk("\nAC3200 ethercard configuration register is %#02x,"
+ " EISA ID %02x %02x %02x %02x.\n", inb(ioaddr + AC_CONFIG),
+ inb(ioaddr + AC_ID_PORT + 0), inb(ioaddr + AC_ID_PORT + 1),
+ inb(ioaddr + AC_ID_PORT + 2), inb(ioaddr + AC_ID_PORT + 3));
+#endif
+
+ /* Assign and allocate the interrupt now. */
+ if (dev->irq == 0)
+ dev->irq = config2irq(inb(ioaddr + AC_CONFIG));
+ else if (dev->irq == 2)
+ dev->irq = 9;
+
+ if (request_irq(dev->irq, ei_interrupt, 0, "ac3200")) {
+ printk (" unable to get IRQ %d.\n", dev->irq);
+ return EAGAIN;
+ }
+
+ /* Allocate dev->priv and fill in 8390 specific dev fields. */
+ if (ethdev_init(dev)) {
+ printk (" unable to allocate memory for dev->priv.\n");
+ free_irq(dev->irq);
+ return -ENOMEM;
+ }
+
+ request_region(ioaddr, AC_IO_EXTENT, "ac3200");
+
+ dev->base_addr = ioaddr;
+
+#ifdef notyet
+ if (dev->mem_start) { /* Override the value from the board. */
+ for (i = 0; i < 7; i++)
+ if (addrmap[i] == dev->mem_start)
+ break;
+ if (i >= 7)
+ i = 0;
+ outb((inb(ioaddr + AC_CONFIG) & ~7) | i, ioaddr + AC_CONFIG);
+ }
+#endif
+
+ dev->if_port = inb(ioaddr + AC_CONFIG) >> 6;
+ dev->mem_start = config2mem(inb(ioaddr + AC_CONFIG));
+ dev->rmem_start = dev->mem_start + TX_PAGES*256;
+ dev->mem_end = dev->rmem_end = dev->mem_start
+ + (AC_STOP_PG - AC_START_PG)*256;
+
+ ei_status.name = "AC3200";
+ ei_status.tx_start_page = AC_START_PG;
+ ei_status.rx_start_page = AC_START_PG + TX_PAGES;
+ ei_status.stop_page = AC_STOP_PG;
+ ei_status.word16 = 1;
+
+ printk("\n%s: AC3200 at %#x, IRQ %d, %s port, shared memory %#lx-%#lx.\n",
+ dev->name, ioaddr, dev->irq, port_name[dev->if_port],
+ dev->mem_start, dev->mem_end-1);
+
+ if (ei_debug > 0)
+ printk(version);
+
+ ei_status.reset_8390 = &ac_reset_8390;
+ ei_status.block_input = &ac_block_input;
+ ei_status.block_output = &ac_block_output;
+ ei_status.get_8390_hdr = &ac_get_8390_hdr;
+
+ dev->open = &ac_open;
+ dev->stop = &ac_close_card;
+ NS8390_init(dev, 0);
+ return 0;
+}
+
+static int ac_open(struct device *dev)
+{
+#ifdef notyet
+ /* Someday we may enable the IRQ and shared memory here. */
+ int ioaddr = dev->base_addr;
+
+ if (request_irq(dev->irq, ei_interrupt, 0, "ac3200"))
+ return -EAGAIN;
+#endif
+
+ ei_open(dev);
+
+ MOD_INC_USE_COUNT;
+
+ return 0;
+}
+
+static void ac_reset_8390(struct device *dev)
+{
+ ushort ioaddr = dev->base_addr;
+
+ outb(AC_RESET, ioaddr + AC_RESET_PORT);
+ if (ei_debug > 1) printk("resetting AC3200, t=%ld...", jiffies);
+
+ ei_status.txing = 0;
+ outb(AC_ENABLE, ioaddr + AC_RESET_PORT);
+ if (ei_debug > 1) printk("reset done\n");
+
+ return;
+}
+
+/* Grab the 8390 specific header. Similar to the block_input routine, but
+ we don't need to be concerned with ring wrap as the header will be at
+ the start of a page, so we optimize accordingly. */
+
+static void
+ac_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+ unsigned long hdr_start = dev->mem_start + ((ring_page - AC_START_PG)<<8);
+ memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr));
+}
+
+/* Block input and output are easy on shared memory ethercards, the only
+ complication is when the ring buffer wraps. */
+
+static void ac_block_input(struct device *dev, int count, struct sk_buff *skb,
+ int ring_offset)
+{
+ unsigned long xfer_start = dev->mem_start + ring_offset - (AC_START_PG<<8);
+
+ if (xfer_start + count > dev->rmem_end) {
+ /* We must wrap the input move. */
+ int semi_count = dev->rmem_end - xfer_start;
+ memcpy_fromio(skb->data, xfer_start, semi_count);
+ count -= semi_count;
+ memcpy_fromio(skb->data + semi_count, dev->rmem_start, count);
+ } else {
+ /* Packet is in one chunk -- we can copy + cksum. */
+ eth_io_copy_and_sum(skb, xfer_start, count, 0);
+ }
+}
+
+static void ac_block_output(struct device *dev, int count,
+ const unsigned char *buf, int start_page)
+{
+ unsigned long shmem = dev->mem_start + ((start_page - AC_START_PG)<<8);
+
+ memcpy_toio(shmem, buf, count);
+}
+
+static int ac_close_card(struct device *dev)
+{
+ dev->start = 0;
+ dev->tbusy = 1;
+
+ if (ei_debug > 1)
+ printk("%s: Shutting down ethercard.\n", dev->name);
+
+#ifdef notyet
+ /* We should someday disable shared memory and interrupts. */
+ outb(0x00, ioaddr + 6); /* Disable interrupts. */
+ free_irq(dev->irq);
+ irq2dev_map[dev->irq] = 0;
+#endif
+
+ ei_close(dev);
+
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+}
+
+#ifdef MODULE
+#define MAX_AC32_CARDS 4 /* Max number of AC32 cards per module */
+#define NAMELEN 8 /* # of chars for storing dev->name */
+static char namelist[NAMELEN * MAX_AC32_CARDS] = { 0, };
+static struct device dev_ac32[MAX_AC32_CARDS] = {
+ {
+ NULL, /* assign a chunk of namelist[] below */
+ 0, 0, 0, 0,
+ 0, 0,
+ 0, 0, 0, NULL, NULL
+ },
+};
+
+static int io[MAX_AC32_CARDS] = { 0, };
+static int irq[MAX_AC32_CARDS] = { 0, };
+static int mem[MAX_AC32_CARDS] = { 0, };
+
+int
+init_module(void)
+{
+ int this_dev, found = 0;
+
+ for (this_dev = 0; this_dev < MAX_AC32_CARDS; this_dev++) {
+ struct device *dev = &dev_ac32[this_dev];
+ dev->name = namelist+(NAMELEN*this_dev);
+ dev->irq = irq[this_dev];
+ dev->base_addr = io[this_dev];
+ dev->mem_start = mem[this_dev]; /* Currently ignored by driver */
+ dev->init = ac3200_probe;
+ /* Default is to only install one card. */
+ if (io[this_dev] == 0 && this_dev != 0) break;
+ if (register_netdev(dev) != 0) {
+ printk(KERN_WARNING "ac3200.c: No ac3200 card found (i/o = 0x%x).\n", io[this_dev]);
+ if (found != 0) return 0; /* Got at least one. */
+ return -ENXIO;
+ }
+ found++;
+ }
+
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ int this_dev;
+
+ for (this_dev = 0; this_dev < MAX_AC32_CARDS; this_dev++) {
+ struct device *dev = &dev_ac32[this_dev];
+ if (dev->priv != NULL) {
+ kfree(dev->priv);
+ dev->priv = NULL;
+ /* Someday free_irq + irq2dev may be in ac_close_card() */
+ free_irq(dev->irq);
+ irq2dev_map[dev->irq] = NULL;
+ release_region(dev->base_addr, AC_IO_EXTENT);
+ unregister_netdev(dev);
+ }
+ }
+}
+#endif /* MODULE */
+
+/*
+ * Local variables:
+ * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c ac3200.c"
+ * version-control: t
+ * kept-new-versions: 5
+ * tab-width: 4
+ * End:
+ */
diff --git a/i386/i386at/gpl/linux/net/apricot.c b/i386/i386at/gpl/linux/net/apricot.c
new file mode 100644
index 00000000..130d7759
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/apricot.c
@@ -0,0 +1,1046 @@
+/* apricot.c: An Apricot 82596 ethernet driver for linux. */
+/*
+ Apricot
+ Written 1994 by Mark Evans.
+ This driver is for the Apricot 82596 bus-master interface
+
+ Modularised 12/94 Mark Evans
+
+ Driver skeleton
+ Written 1993 by Donald Becker.
+ Copyright 1993 United States Government as represented by the Director,
+ National Security Agency. This software may only be used and distributed
+ according to the terms of the GNU Public License as modified by SRC,
+ incorporated herein by reference.
+
+ The author may be reached as becker@super.org or
+ C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
+
+
+*/
+
+static const char *version = "apricot.c:v0.2 05/12/94\n";
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#ifndef HAVE_PORTRESERVE
+#define check_region(addr, size) 0
+#define request_region(addr, size,name) do ; while(0)
+#endif
+
+#ifndef HAVE_ALLOC_SKB
+#define alloc_skb(size, priority) (struct sk_buff *) kmalloc(size,priority)
+#define kfree_skbmem(buff, size) kfree_s(buff,size)
+#endif
+
+#define APRICOT_DEBUG 1
+
+#ifdef APRICOT_DEBUG
+int i596_debug = APRICOT_DEBUG;
+#else
+int i596_debug = 1;
+#endif
+
+#define APRICOT_TOTAL_SIZE 17
+
+#define I596_NULL -1
+
+#define CMD_EOL 0x8000 /* The last command of the list, stop. */
+#define CMD_SUSP 0x4000 /* Suspend after doing cmd. */
+#define CMD_INTR 0x2000 /* Interrupt after doing cmd. */
+
+#define CMD_FLEX 0x0008 /* Enable flexible memory model */
+
+enum commands {
+ CmdNOp = 0, CmdSASetup = 1, CmdConfigure = 2, CmdMulticastList = 3,
+ CmdTx = 4, CmdTDR = 5, CmdDump = 6, CmdDiagnose = 7};
+
+#define STAT_C 0x8000 /* Set to 0 after execution */
+#define STAT_B 0x4000 /* Command being executed */
+#define STAT_OK 0x2000 /* Command executed ok */
+#define STAT_A 0x1000 /* Command aborted */
+
+#define CUC_START 0x0100
+#define CUC_RESUME 0x0200
+#define CUC_SUSPEND 0x0300
+#define CUC_ABORT 0x0400
+#define RX_START 0x0010
+#define RX_RESUME 0x0020
+#define RX_SUSPEND 0x0030
+#define RX_ABORT 0x0040
+
+struct i596_cmd {
+ unsigned short status;
+ unsigned short command;
+ struct i596_cmd *next;
+};
+
+#define EOF 0x8000
+#define SIZE_MASK 0x3fff
+
+struct i596_tbd {
+ unsigned short size;
+ unsigned short pad;
+ struct i596_tbd *next;
+ char *data;
+};
+
+struct tx_cmd {
+ struct i596_cmd cmd;
+ struct i596_tbd *tbd;
+ unsigned short size;
+ unsigned short pad;
+};
+
+struct i596_rfd {
+ unsigned short stat;
+ unsigned short cmd;
+ struct i596_rfd *next;
+ long rbd;
+ unsigned short count;
+ unsigned short size;
+ char data[1532];
+};
+
+#define RX_RING_SIZE 8
+
+struct i596_scb {
+ unsigned short status;
+ unsigned short command;
+ struct i596_cmd *cmd;
+ struct i596_rfd *rfd;
+ unsigned long crc_err;
+ unsigned long align_err;
+ unsigned long resource_err;
+ unsigned long over_err;
+ unsigned long rcvdt_err;
+ unsigned long short_err;
+ unsigned short t_on;
+ unsigned short t_off;
+};
+
+struct i596_iscp {
+ unsigned long stat;
+ struct i596_scb *scb;
+};
+
+struct i596_scp {
+ unsigned long sysbus;
+ unsigned long pad;
+ struct i596_iscp *iscp;
+};
+
+struct i596_private {
+ struct i596_scp scp;
+ struct i596_iscp iscp;
+ struct i596_scb scb;
+ struct i596_cmd set_add;
+ char eth_addr[8];
+ struct i596_cmd set_conf;
+ char i596_config[16];
+ struct i596_cmd tdr;
+ unsigned long stat;
+ int last_restart;
+ struct i596_rfd *rx_tail;
+ struct i596_cmd *cmd_tail;
+ struct i596_cmd *cmd_head;
+ int cmd_backlog;
+ unsigned long last_cmd;
+ struct enet_statistics stats;
+};
+
+char init_setup[] = {
+ 0x8E, /* length, prefetch on */
+ 0xC8, /* fifo to 8, monitor off */
+ 0x80, /* don't save bad frames */
+ 0x2E, /* No source address insertion, 8 byte preamble */
+ 0x00, /* priority and backoff defaults */
+ 0x60, /* interframe spacing */
+ 0x00, /* slot time LSB */
+ 0xf2, /* slot time and retries */
+ 0x00, /* promiscuous mode */
+ 0x00, /* collision detect */
+ 0x40, /* minimum frame length */
+ 0xff,
+ 0x00,
+ 0x7f /* *multi IA */ };
+
+static int i596_open(struct device *dev);
+static int i596_start_xmit(struct sk_buff *skb, struct device *dev);
+static void i596_interrupt(int irq, struct pt_regs *regs);
+static int i596_close(struct device *dev);
+static struct enet_statistics *i596_get_stats(struct device *dev);
+static void i596_add_cmd(struct device *dev, struct i596_cmd *cmd);
+static void print_eth(char *);
+static void set_multicast_list(struct device *dev);
+
+
+static inline int
+init_rx_bufs(struct device *dev, int num)
+{
+ struct i596_private *lp = (struct i596_private *)dev->priv;
+ int i;
+ struct i596_rfd *rfd;
+
+ lp->scb.rfd = (struct i596_rfd *)I596_NULL;
+
+ if (i596_debug > 1) printk ("%s: init_rx_bufs %d.\n", dev->name, num);
+
+ for (i = 0; i < num; i++)
+ {
+ if (!(rfd = (struct i596_rfd *)kmalloc(sizeof(struct i596_rfd), GFP_KERNEL)))
+ break;
+
+ rfd->stat = 0x0000;
+ rfd->rbd = I596_NULL;
+ rfd->count = 0;
+ rfd->size = 1532;
+ if (i == 0)
+ {
+ rfd->cmd = CMD_EOL;
+ lp->rx_tail = rfd;
+ }
+ else
+ rfd->cmd = 0x0000;
+
+ rfd->next = lp->scb.rfd;
+ lp->scb.rfd = rfd;
+ }
+
+ if (i != 0)
+ lp->rx_tail->next = lp->scb.rfd;
+
+ return (i);
+}
+
+static inline void
+remove_rx_bufs(struct device *dev)
+{
+ struct i596_private *lp = (struct i596_private *)dev->priv;
+ struct i596_rfd *rfd = lp->scb.rfd;
+
+ lp->rx_tail->next = (struct i596_rfd *)I596_NULL;
+
+ do
+ {
+ lp->scb.rfd = rfd->next;
+ kfree_s(rfd, sizeof(struct i596_rfd));
+ rfd = lp->scb.rfd;
+ }
+ while (rfd != lp->rx_tail);
+}
+
+static inline void
+init_i596_mem(struct device *dev)
+{
+ struct i596_private *lp = (struct i596_private *)dev->priv;
+ short ioaddr = dev->base_addr;
+ int boguscnt = 100;
+
+ /* change the scp address */
+ outw(0, ioaddr);
+ outw(0, ioaddr);
+ outb(4, ioaddr+0xf);
+ outw(((((int)&lp->scp) & 0xffff) | 2), ioaddr);
+ outw((((int)&lp->scp)>>16) & 0xffff, ioaddr);
+
+ lp->last_cmd = jiffies;
+
+ lp->scp.sysbus = 0x00440000;
+ lp->scp.iscp = &(lp->iscp);
+ lp->iscp.scb = &(lp->scb);
+ lp->iscp.stat = 0x0001;
+ lp->cmd_backlog = 0;
+
+ lp->cmd_head = lp->scb.cmd = (struct i596_cmd *) I596_NULL;
+
+ if (i596_debug > 2) printk("%s: starting i82596.\n", dev->name);
+
+ (void) inb (ioaddr+0x10);
+ outb(4, ioaddr+0xf);
+ outw(0, ioaddr+4);
+
+ while (lp->iscp.stat)
+ if (--boguscnt == 0)
+ {
+ printk("%s: i82596 initialization timed out with status %4.4x, cmd %4.4x.\n",
+ dev->name, lp->scb.status, lp->scb.command);
+ break;
+ }
+
+ lp->scb.command = 0;
+
+ memcpy (lp->i596_config, init_setup, 14);
+ lp->set_conf.command = CmdConfigure;
+ i596_add_cmd(dev, &lp->set_conf);
+
+ memcpy (lp->eth_addr, dev->dev_addr, 6);
+ lp->set_add.command = CmdSASetup;
+ i596_add_cmd(dev, &lp->set_add);
+
+ lp->tdr.command = CmdTDR;
+ i596_add_cmd(dev, &lp->tdr);
+
+ boguscnt = 200;
+ while (lp->scb.status, lp->scb.command)
+ if (--boguscnt == 0)
+ {
+ printk("%s: receive unit start timed out with status %4.4x, cmd %4.4x.\n",
+ dev->name, lp->scb.status, lp->scb.command);
+ break;
+ }
+
+ lp->scb.command = RX_START;
+ outw(0, ioaddr+4);
+
+ boguscnt = 200;
+ while (lp->scb.status, lp->scb.command)
+ if (--boguscnt == 0)
+ {
+ printk("i82596 init timed out with status %4.4x, cmd %4.4x.\n",
+ lp->scb.status, lp->scb.command);
+ break;
+ }
+
+ return;
+}
+
+static inline int
+i596_rx(struct device *dev)
+{
+ struct i596_private *lp = (struct i596_private *)dev->priv;
+ int frames = 0;
+
+ if (i596_debug > 3) printk ("i596_rx()\n");
+
+ while ((lp->scb.rfd->stat) & STAT_C)
+ {
+ if (i596_debug >2) print_eth(lp->scb.rfd->data);
+
+ if ((lp->scb.rfd->stat) & STAT_OK)
+ {
+ /* a good frame */
+ int pkt_len = lp->scb.rfd->count & 0x3fff;
+ struct sk_buff *skb = dev_alloc_skb(pkt_len);
+
+ frames++;
+
+ if (skb == NULL)
+ {
+ printk ("%s: i596_rx Memory squeeze, dropping packet.\n", dev->name);
+ lp->stats.rx_dropped++;
+ break;
+ }
+
+ skb->dev = dev;
+ memcpy(skb_put(skb,pkt_len), lp->scb.rfd->data, pkt_len);
+
+ skb->protocol=eth_type_trans(skb,dev);
+ netif_rx(skb);
+ lp->stats.rx_packets++;
+
+ if (i596_debug > 4) print_eth(skb->data);
+ }
+ else
+ {
+ lp->stats.rx_errors++;
+ if ((lp->scb.rfd->stat) & 0x0001) lp->stats.collisions++;
+ if ((lp->scb.rfd->stat) & 0x0080) lp->stats.rx_length_errors++;
+ if ((lp->scb.rfd->stat) & 0x0100) lp->stats.rx_over_errors++;
+ if ((lp->scb.rfd->stat) & 0x0200) lp->stats.rx_fifo_errors++;
+ if ((lp->scb.rfd->stat) & 0x0400) lp->stats.rx_frame_errors++;
+ if ((lp->scb.rfd->stat) & 0x0800) lp->stats.rx_crc_errors++;
+ if ((lp->scb.rfd->stat) & 0x1000) lp->stats.rx_length_errors++;
+ }
+
+ lp->scb.rfd->stat = 0;
+ lp->rx_tail->cmd = 0;
+ lp->rx_tail = lp->scb.rfd;
+ lp->scb.rfd = lp->scb.rfd->next;
+ lp->rx_tail->count = 0;
+ lp->rx_tail->cmd = CMD_EOL;
+
+ }
+
+ if (i596_debug > 3) printk ("frames %d\n", frames);
+
+ return 0;
+}
+
+static inline void
+i596_cleanup_cmd(struct i596_private *lp)
+{
+ struct i596_cmd *ptr;
+ int boguscnt = 100;
+
+ if (i596_debug > 4) printk ("i596_cleanup_cmd\n");
+
+ while (lp->cmd_head != (struct i596_cmd *) I596_NULL)
+ {
+ ptr = lp->cmd_head;
+
+ lp->cmd_head = lp->cmd_head->next;
+ lp->cmd_backlog--;
+
+ switch ((ptr->command) & 0x7)
+ {
+ case CmdTx:
+ {
+ struct tx_cmd *tx_cmd = (struct tx_cmd *) ptr;
+ struct sk_buff *skb = ((struct sk_buff *)(tx_cmd->tbd->data)) -1;
+
+ dev_kfree_skb(skb, FREE_WRITE);
+
+ lp->stats.tx_errors++;
+ lp->stats.tx_aborted_errors++;
+
+ ptr->next = (struct i596_cmd * ) I596_NULL;
+ kfree_s((unsigned char *)tx_cmd, (sizeof (struct tx_cmd) + sizeof (struct i596_tbd)));
+ break;
+ }
+ case CmdMulticastList:
+ {
+ unsigned short count = *((unsigned short *) (ptr + 1));
+
+ ptr->next = (struct i596_cmd * ) I596_NULL;
+ kfree_s((unsigned char *)ptr, (sizeof (struct i596_cmd) + count + 2));
+ break;
+ }
+ default:
+ ptr->next = (struct i596_cmd * ) I596_NULL;
+ }
+ }
+
+ while (lp->scb.status, lp->scb.command)
+ if (--boguscnt == 0)
+ {
+ printk("i596_cleanup_cmd timed out with status %4.4x, cmd %4.4x.\n",
+ lp->scb.status, lp->scb.command);
+ break;
+ }
+
+ lp->scb.cmd = lp->cmd_head;
+}
+
+static inline void
+i596_reset(struct device *dev, struct i596_private *lp, int ioaddr)
+{
+ int boguscnt = 100;
+
+ if (i596_debug > 4) printk ("i596_reset\n");
+
+ while (lp->scb.status, lp->scb.command)
+ if (--boguscnt == 0)
+ {
+ printk("i596_reset timed out with status %4.4x, cmd %4.4x.\n",
+ lp->scb.status, lp->scb.command);
+ break;
+ }
+
+ dev->start = 0;
+ dev->tbusy = 1;
+
+ lp->scb.command = CUC_ABORT|RX_ABORT;
+ outw(0, ioaddr+4);
+
+ /* wait for shutdown */
+ boguscnt = 400;
+
+ while ((lp->scb.status, lp->scb.command) || lp->scb.command)
+ if (--boguscnt == 0)
+ {
+ printk("i596_reset 2 timed out with status %4.4x, cmd %4.4x.\n",
+ lp->scb.status, lp->scb.command);
+ break;
+ }
+
+ i596_cleanup_cmd(lp);
+ i596_rx(dev);
+
+ dev->start = 1;
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ init_i596_mem(dev);
+}
+
+static void i596_add_cmd(struct device *dev, struct i596_cmd *cmd)
+{
+ struct i596_private *lp = (struct i596_private *)dev->priv;
+ int ioaddr = dev->base_addr;
+ unsigned long flags;
+ int boguscnt = 100;
+
+ if (i596_debug > 4) printk ("i596_add_cmd\n");
+
+ cmd->status = 0;
+ cmd->command |= (CMD_EOL|CMD_INTR);
+ cmd->next = (struct i596_cmd *) I596_NULL;
+
+ save_flags(flags);
+ cli();
+ if (lp->cmd_head != (struct i596_cmd *) I596_NULL)
+ lp->cmd_tail->next = cmd;
+ else
+ {
+ lp->cmd_head = cmd;
+ while (lp->scb.status, lp->scb.command)
+ if (--boguscnt == 0)
+ {
+ printk("i596_add_cmd timed out with status %4.4x, cmd %4.4x.\n",
+ lp->scb.status, lp->scb.command);
+ break;
+ }
+
+ lp->scb.cmd = cmd;
+ lp->scb.command = CUC_START;
+ outw (0, ioaddr+4);
+ }
+ lp->cmd_tail = cmd;
+ lp->cmd_backlog++;
+
+ lp->cmd_head = lp->scb.cmd;
+ restore_flags(flags);
+
+ if (lp->cmd_backlog > 16)
+ {
+ int tickssofar = jiffies - lp->last_cmd;
+
+ if (tickssofar < 25) return;
+
+ printk("%s: command unit timed out, status resetting.\n", dev->name);
+
+ i596_reset(dev, lp, ioaddr);
+ }
+}
+
+static int
+i596_open(struct device *dev)
+{
+ int i;
+
+ if (i596_debug > 1)
+ printk("%s: i596_open() irq %d.\n", dev->name, dev->irq);
+
+ if (request_irq(dev->irq, &i596_interrupt, 0, "apricot"))
+ return -EAGAIN;
+
+ irq2dev_map[dev->irq] = dev;
+
+ i = init_rx_bufs(dev, RX_RING_SIZE);
+
+ if ((i = init_rx_bufs(dev, RX_RING_SIZE)) < RX_RING_SIZE)
+ printk("%s: only able to allocate %d receive buffers\n", dev->name, i);
+
+ if (i < 4)
+ {
+ free_irq(dev->irq);
+ irq2dev_map[dev->irq] = 0;
+ return -EAGAIN;
+ }
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+ MOD_INC_USE_COUNT;
+
+ /* Initialize the 82596 memory */
+ init_i596_mem(dev);
+
+ return 0; /* Always succeed */
+}
+
+static int
+i596_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+ struct i596_private *lp = (struct i596_private *)dev->priv;
+ int ioaddr = dev->base_addr;
+ struct tx_cmd *tx_cmd;
+
+ if (i596_debug > 2) printk ("%s: Apricot start xmit\n", dev->name);
+
+ /* Transmitter timeout, serious problems. */
+ if (dev->tbusy) {
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < 5)
+ return 1;
+ printk("%s: transmit timed out, status resetting.\n",
+ dev->name);
+ lp->stats.tx_errors++;
+ /* Try to restart the adaptor */
+ if (lp->last_restart == lp->stats.tx_packets) {
+ if (i596_debug > 1) printk ("Resetting board.\n");
+
+ /* Shutdown and restart */
+ i596_reset(dev,lp, ioaddr);
+ } else {
+ /* Issue a channel attention signal */
+ if (i596_debug > 1) printk ("Kicking board.\n");
+
+ lp->scb.command = CUC_START|RX_START;
+ outw(0, ioaddr+4);
+
+ lp->last_restart = lp->stats.tx_packets;
+ }
+ dev->tbusy = 0;
+ dev->trans_start = jiffies;
+ }
+
+ /* If some higher level thinks we've misses a tx-done interrupt
+ we are passed NULL. n.b. dev_tint handles the cli()/sti()
+ itself. */
+ if (skb == NULL) {
+ dev_tint(dev);
+ return 0;
+ }
+
+ /* shouldn't happen */
+ if (skb->len <= 0) return 0;
+
+ if (i596_debug > 3) printk("%s: i596_start_xmit() called\n", dev->name);
+
+ /* Block a timer-based transmit from overlapping. This could better be
+ done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
+ if (set_bit(0, (void*)&dev->tbusy) != 0)
+ printk("%s: Transmitter access conflict.\n", dev->name);
+ else
+ {
+ short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+ dev->trans_start = jiffies;
+
+ tx_cmd = (struct tx_cmd *) kmalloc ((sizeof (struct tx_cmd) + sizeof (struct i596_tbd)), GFP_ATOMIC);
+ if (tx_cmd == NULL)
+ {
+ printk ("%s: i596_xmit Memory squeeze, dropping packet.\n", dev->name);
+ lp->stats.tx_dropped++;
+
+ dev_kfree_skb(skb, FREE_WRITE);
+ }
+ else
+ {
+ tx_cmd->tbd = (struct i596_tbd *) (tx_cmd + 1);
+ tx_cmd->tbd->next = (struct i596_tbd *) I596_NULL;
+
+ tx_cmd->cmd.command = CMD_FLEX|CmdTx;
+
+ tx_cmd->pad = 0;
+ tx_cmd->size = 0;
+ tx_cmd->tbd->pad = 0;
+ tx_cmd->tbd->size = EOF | length;
+
+ tx_cmd->tbd->data = skb->data;
+
+ if (i596_debug > 3) print_eth(skb->data);
+
+ i596_add_cmd(dev, (struct i596_cmd *)tx_cmd);
+
+ lp->stats.tx_packets++;
+ }
+ }
+
+ dev->tbusy = 0;
+
+ return 0;
+}
+
+
+static void print_eth(char *add)
+{
+ int i;
+
+ printk ("Dest ");
+ for (i = 0; i < 6; i++)
+ printk(" %2.2X", (unsigned char)add[i]);
+ printk ("\n");
+
+ printk ("Source");
+ for (i = 0; i < 6; i++)
+ printk(" %2.2X", (unsigned char)add[i+6]);
+ printk ("\n");
+ printk ("type %2.2X%2.2X\n", (unsigned char)add[12], (unsigned char)add[13]);
+}
+
+int apricot_probe(struct device *dev)
+{
+ int i;
+ struct i596_private *lp;
+ int checksum = 0;
+ int ioaddr = 0x300;
+ char eth_addr[6];
+
+ /* this is easy the ethernet interface can only be at 0x300 */
+ /* first check nothing is already registered here */
+
+ if (check_region(ioaddr, APRICOT_TOTAL_SIZE))
+ return ENODEV;
+
+ for (i = 0; i < 8; i++)
+ {
+ eth_addr[i] = inb(ioaddr+8+i);
+ checksum += eth_addr[i];
+ }
+
+ /* checksum is a multiple of 0x100, got this wrong first time
+ some machines have 0x100, some 0x200. The DOS driver doesn't
+ even bother with the checksum */
+
+ if (checksum % 0x100) return ENODEV;
+
+ /* Some other boards trip the checksum.. but then appear as ether
+ address 0. Trap these - AC */
+
+ if(memcmp(eth_addr,"\x00\x00\x49",3)!= 0)
+ return ENODEV;
+
+ request_region(ioaddr, APRICOT_TOTAL_SIZE, "apricot");
+
+ dev->base_addr = ioaddr;
+ ether_setup(dev);
+ printk("%s: Apricot 82596 at %#3x,", dev->name, ioaddr);
+
+ for (i = 0; i < 6; i++)
+ printk(" %2.2X", dev->dev_addr[i] = eth_addr[i]);
+
+ dev->base_addr = ioaddr;
+ dev->irq = 10;
+ printk(" IRQ %d.\n", dev->irq);
+
+ if (i596_debug > 0) printk(version);
+
+ /* The APRICOT-specific entries in the device structure. */
+ dev->open = &i596_open;
+ dev->stop = &i596_close;
+ dev->hard_start_xmit = &i596_start_xmit;
+ dev->get_stats = &i596_get_stats;
+ dev->set_multicast_list = &set_multicast_list;
+
+ dev->mem_start = (int)kmalloc(sizeof(struct i596_private)+ 0x0f, GFP_KERNEL);
+ /* align for scp */
+ dev->priv = (void *)((dev->mem_start + 0xf) & 0xfffffff0);
+
+ lp = (struct i596_private *)dev->priv;
+ memset((void *)lp, 0, sizeof(struct i596_private));
+ lp->scb.command = 0;
+ lp->scb.cmd = (struct i596_cmd *) I596_NULL;
+ lp->scb.rfd = (struct i596_rfd *)I596_NULL;
+
+ return 0;
+}
+
+static void
+i596_interrupt(int irq, struct pt_regs *regs)
+{
+ struct device *dev = (struct device *)(irq2dev_map[irq]);
+ struct i596_private *lp;
+ short ioaddr;
+ int boguscnt = 200;
+ unsigned short status, ack_cmd = 0;
+
+ if (dev == NULL) {
+ printk ("i596_interrupt(): irq %d for unknown device.\n", irq);
+ return;
+ }
+
+ if (i596_debug > 3) printk ("%s: i596_interrupt(): irq %d\n",dev->name, irq);
+
+ if (dev->interrupt)
+ printk("%s: Re-entering the interrupt handler.\n", dev->name);
+
+ dev->interrupt = 1;
+
+ ioaddr = dev->base_addr;
+
+ lp = (struct i596_private *)dev->priv;
+
+ while (lp->scb.status, lp->scb.command)
+ if (--boguscnt == 0)
+ {
+ printk("%s: i596 interrupt, timeout status %4.4x command %4.4x.\n", dev->name, lp->scb.status, lp->scb.command);
+ break;
+ }
+ status = lp->scb.status;
+
+ if (i596_debug > 4)
+ printk("%s: i596 interrupt, status %4.4x.\n", dev->name, status);
+
+ ack_cmd = status & 0xf000;
+
+ if ((status & 0x8000) || (status & 0x2000))
+ {
+ struct i596_cmd *ptr;
+
+ if ((i596_debug > 4) && (status & 0x8000))
+ printk("%s: i596 interrupt completed command.\n", dev->name);
+ if ((i596_debug > 4) && (status & 0x2000))
+ printk("%s: i596 interrupt command unit inactive %x.\n", dev->name, status & 0x0700);
+
+ while ((lp->cmd_head != (struct i596_cmd *) I596_NULL) && (lp->cmd_head->status & STAT_C))
+ {
+ ptr = lp->cmd_head;
+
+ lp->cmd_head = lp->cmd_head->next;
+ lp->cmd_backlog--;
+
+ switch ((ptr->command) & 0x7)
+ {
+ case CmdTx:
+ {
+ struct tx_cmd *tx_cmd = (struct tx_cmd *) ptr;
+ struct sk_buff *skb = ((struct sk_buff *)(tx_cmd->tbd->data)) -1;
+
+ dev_kfree_skb(skb, FREE_WRITE);
+
+ if ((ptr->status) & STAT_OK)
+ {
+ if (i596_debug >2) print_eth(skb->data);
+ }
+ else
+ {
+ lp->stats.tx_errors++;
+ if ((ptr->status) & 0x0020) lp->stats.collisions++;
+ if (!((ptr->status) & 0x0040)) lp->stats.tx_heartbeat_errors++;
+ if ((ptr->status) & 0x0400) lp->stats.tx_carrier_errors++;
+ if ((ptr->status) & 0x0800) lp->stats.collisions++;
+ if ((ptr->status) & 0x1000) lp->stats.tx_aborted_errors++;
+ }
+
+
+ ptr->next = (struct i596_cmd * ) I596_NULL;
+ kfree_s((unsigned char *)tx_cmd, (sizeof (struct tx_cmd) + sizeof (struct i596_tbd)));
+ break;
+ }
+ case CmdMulticastList:
+ {
+ unsigned short count = *((unsigned short *) (ptr + 1));
+
+ ptr->next = (struct i596_cmd * ) I596_NULL;
+ kfree_s((unsigned char *)ptr, (sizeof (struct i596_cmd) + count + 2));
+ break;
+ }
+ case CmdTDR:
+ {
+ unsigned long status = *((unsigned long *) (ptr + 1));
+
+ if (status & 0x8000)
+ {
+ if (i596_debug > 3)
+ printk("%s: link ok.\n", dev->name);
+ }
+ else
+ {
+ if (status & 0x4000)
+ printk("%s: Transceiver problem.\n", dev->name);
+ if (status & 0x2000)
+ printk("%s: Termination problem.\n", dev->name);
+ if (status & 0x1000)
+ printk("%s: Short circuit.\n", dev->name);
+
+ printk("%s: Time %ld.\n", dev->name, status & 0x07ff);
+ }
+ }
+ default:
+ ptr->next = (struct i596_cmd * ) I596_NULL;
+
+ lp->last_cmd = jiffies;
+ }
+ }
+
+ ptr = lp->cmd_head;
+ while ((ptr != (struct i596_cmd *) I596_NULL) && (ptr != lp->cmd_tail))
+ {
+ ptr->command &= 0x1fff;
+ ptr = ptr->next;
+ }
+
+ if ((lp->cmd_head != (struct i596_cmd *) I596_NULL) && (dev->start)) ack_cmd |= CUC_START;
+ lp->scb.cmd = lp->cmd_head;
+ }
+
+ if ((status & 0x1000) || (status & 0x4000))
+ {
+ if ((i596_debug > 4) && (status & 0x4000))
+ printk("%s: i596 interrupt received a frame.\n", dev->name);
+ if ((i596_debug > 4) && (status & 0x1000))
+ printk("%s: i596 interrupt receive unit inactive %x.\n", dev->name, status & 0x0070);
+
+ i596_rx(dev);
+
+ if (dev->start) ack_cmd |= RX_START;
+ }
+
+ /* acknowledge the interrupt */
+
+/*
+ if ((lp->scb.cmd != (struct i596_cmd *) I596_NULL) && (dev->start)) ack_cmd | = CUC_START;
+*/
+ boguscnt = 100;
+ while (lp->scb.status, lp->scb.command)
+ if (--boguscnt == 0)
+ {
+ printk("%s: i596 interrupt, timeout status %4.4x command %4.4x.\n", dev->name, lp->scb.status, lp->scb.command);
+ break;
+ }
+ lp->scb.command = ack_cmd;
+
+ (void) inb (ioaddr+0x10);
+ outb (4, ioaddr+0xf);
+ outw (0, ioaddr+4);
+
+ if (i596_debug > 4)
+ printk("%s: exiting interrupt.\n", dev->name);
+
+ dev->interrupt = 0;
+ return;
+}
+
+static int
+i596_close(struct device *dev)
+{
+ int ioaddr = dev->base_addr;
+ struct i596_private *lp = (struct i596_private *)dev->priv;
+ int boguscnt = 200;
+
+ dev->start = 0;
+ dev->tbusy = 1;
+
+ if (i596_debug > 1)
+ printk("%s: Shutting down ethercard, status was %4.4x.\n",
+ dev->name, lp->scb.status);
+
+ lp->scb.command = CUC_ABORT|RX_ABORT;
+ outw(0, ioaddr+4);
+
+ i596_cleanup_cmd(lp);
+
+ while (lp->scb.status, lp->scb.command)
+ if (--boguscnt == 0)
+ {
+ printk("%s: close timed timed out with status %4.4x, cmd %4.4x.\n",
+ dev->name, lp->scb.status, lp->scb.command);
+ break;
+ }
+ free_irq(dev->irq);
+ irq2dev_map[dev->irq] = 0;
+ remove_rx_bufs(dev);
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+}
+
+static struct enet_statistics *
+i596_get_stats(struct device *dev)
+{
+ struct i596_private *lp = (struct i596_private *)dev->priv;
+
+ return &lp->stats;
+}
+
+/*
+ * Set or clear the multicast filter for this adaptor.
+ */
+
+static void set_multicast_list(struct device *dev)
+{
+ struct i596_private *lp = (struct i596_private *)dev->priv;
+ struct i596_cmd *cmd;
+
+ if (i596_debug > 1)
+ printk ("%s: set multicast list %d\n", dev->name, dev->mc_count);
+
+ if (dev->mc_count > 0)
+ {
+ struct dev_mc_list *dmi;
+ char *cp;
+ cmd = (struct i596_cmd *) kmalloc(sizeof(struct i596_cmd)+2+dev->mc_count*6, GFP_ATOMIC);
+ if (cmd == NULL)
+ {
+ printk ("%s: set_multicast Memory squeeze.\n", dev->name);
+ return;
+ }
+ cmd->command = CmdMulticastList;
+ *((unsigned short *) (cmd + 1)) = dev->mc_count * 6;
+ cp=((char *)(cmd + 1))+2;
+ for(dmi=dev->mc_list;dmi!=NULL;dmi=dmi->next)
+ {
+ memcpy(cp, dmi,6);
+ cp+=6;
+ }
+ print_eth (((char *)(cmd + 1)) + 2);
+ i596_add_cmd(dev, cmd);
+ }
+ else
+ {
+ if (lp->set_conf.next != (struct i596_cmd * ) I596_NULL)
+ return;
+ if (dev->mc_count == 0 && !(dev->flags&(IFF_PROMISC|IFF_ALLMULTI)))
+ {
+ if(dev->flags&IFF_ALLMULTI)
+ dev->flags|=IFF_PROMISC;
+ lp->i596_config[8] &= ~0x01;
+ }
+ else
+ lp->i596_config[8] |= 0x01;
+
+ i596_add_cmd(dev, &lp->set_conf);
+ }
+}
+
+#ifdef HAVE_DEVLIST
+static unsigned int apricot_portlist[] = {0x300, 0};
+struct netdev_entry apricot_drv =
+{"apricot", apricot_probe, APRICOT_TOTAL_SIZE, apricot_portlist};
+#endif
+
+#ifdef MODULE
+static char devicename[9] = { 0, };
+static struct device dev_apricot = {
+ devicename, /* device name inserted by /linux/drivers/net/net_init.c */
+ 0, 0, 0, 0,
+ 0x300, 10,
+ 0, 0, 0, NULL, apricot_probe };
+
+static int io = 0x300;
+static int irq = 10;
+
+int
+init_module(void)
+{
+ dev_apricot.base_addr = io;
+ dev_apricot.irq = irq;
+ if (register_netdev(&dev_apricot) != 0)
+ return -EIO;
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ unregister_netdev(&dev_apricot);
+ kfree_s((void *)dev_apricot.mem_start, sizeof(struct i596_private) + 0xf);
+ dev_apricot.priv = NULL;
+
+ /* If we don't do this, we can't re-insmod it later. */
+ release_region(dev_apricot.base_addr, APRICOT_TOTAL_SIZE);
+}
+#endif /* MODULE */
+
+/*
+ * Local variables:
+ * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c apricot.c"
+ * End:
+ */
diff --git a/i386/i386at/gpl/linux/net/at1700.c b/i386/i386at/gpl/linux/net/at1700.c
new file mode 100644
index 00000000..3d684c0a
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/at1700.c
@@ -0,0 +1,677 @@
+/* at1700.c: A network device driver for the Allied Telesis AT1700.
+
+ Written 1993-94 by Donald Becker.
+
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency.
+
+ This software may be used and distributed according to the terms
+ of the GNU Public License, incorporated herein by reference.
+
+ The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
+ Center of Excellence in Space Data and Information Sciences
+ Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
+
+ This is a device driver for the Allied Telesis AT1700, which is a
+ straight-forward Fujitsu MB86965 implementation.
+
+ Sources:
+ The Fujitsu MB86965 datasheet.
+
+ After the initial version of this driver was written Gerry Sawkins of
+ ATI provided their EEPROM configuration code header file.
+ Thanks to NIIBE Yutaka <gniibe@mri.co.jp> for bug fixes.
+
+ Bugs:
+ The MB86965 has a design flaw that makes all probes unreliable. Not
+ only is it difficult to detect, it also moves around in I/O space in
+ response to inb()s from other device probes!
+*/
+
+static const char *version =
+ "at1700.c:v1.12 1/18/95 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+#include <linux/module.h>
+
+#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/in.h>
+#include <linux/malloc.h>
+#include <linux/string.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>
+
+/* This unusual address order is used to verify the CONFIG register. */
+static int at1700_probe_list[] =
+{0x260, 0x280, 0x2a0, 0x240, 0x340, 0x320, 0x380, 0x300, 0};
+
+/* use 0 for production, 1 for verification, >2 for debug */
+#ifndef NET_DEBUG
+#define NET_DEBUG 1
+#endif
+static unsigned int net_debug = NET_DEBUG;
+
+typedef unsigned char uchar;
+
+/* Information that need to be kept for each board. */
+struct net_local {
+ struct enet_statistics stats;
+ uint tx_started:1; /* Number of packet on the Tx queue. */
+ uchar tx_queue; /* Number of packet on the Tx queue. */
+ ushort tx_queue_len; /* Current length of the Tx queue. */
+};
+
+
+/* Offsets from the base address. */
+#define STATUS 0
+#define TX_STATUS 0
+#define RX_STATUS 1
+#define TX_INTR 2 /* Bit-mapped interrupt enable registers. */
+#define RX_INTR 3
+#define TX_MODE 4
+#define RX_MODE 5
+#define CONFIG_0 6 /* Misc. configuration settings. */
+#define CONFIG_1 7
+/* Run-time register bank 2 definitions. */
+#define DATAPORT 8 /* Word-wide DMA or programmed-I/O dataport. */
+#define TX_START 10
+#define MODE13 13
+#define EEPROM_Ctrl 16
+#define EEPROM_Data 17
+#define IOCONFIG 19
+#define RESET 31 /* Write to reset some parts of the chip. */
+#define AT1700_IO_EXTENT 32
+
+/* EEPROM_Ctrl bits. */
+#define EE_SHIFT_CLK 0x40 /* EEPROM shift clock, in reg. 16. */
+#define EE_CS 0x20 /* EEPROM chip select, in reg. 16. */
+#define EE_DATA_WRITE 0x80 /* EEPROM chip data in, in reg. 17. */
+#define EE_DATA_READ 0x80 /* EEPROM chip data out, in reg. 17. */
+
+/* Delay between EEPROM clock transitions. */
+#define eeprom_delay() do { int _i = 40; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0)
+
+/* The EEPROM commands include the alway-set leading bit. */
+#define EE_WRITE_CMD (5 << 6)
+#define EE_READ_CMD (6 << 6)
+#define EE_ERASE_CMD (7 << 6)
+
+
+/* Index to functions, as function prototypes. */
+
+extern int at1700_probe(struct device *dev);
+
+static int at1700_probe1(struct device *dev, short ioaddr);
+static int read_eeprom(int ioaddr, int location);
+static int net_open(struct device *dev);
+static int net_send_packet(struct sk_buff *skb, struct device *dev);
+static void net_interrupt(int irq, struct pt_regs *regs);
+static void net_rx(struct device *dev);
+static int net_close(struct device *dev);
+static struct enet_statistics *net_get_stats(struct device *dev);
+static void set_multicast_list(struct device *dev);
+
+
+/* Check for a network adaptor of this type, and return '0' iff one exists.
+ If dev->base_addr == 0, probe all likely locations.
+ If dev->base_addr == 1, always return failure.
+ If dev->base_addr == 2, allocate space for the device and return success
+ (detachable devices only).
+ */
+#ifdef HAVE_DEVLIST
+/* Support for a alternate probe manager, which will eliminate the
+ boilerplate below. */
+struct netdev_entry at1700_drv =
+{"at1700", at1700_probe1, AT1700_IO_EXTENT, at1700_probe_list};
+#else
+int
+at1700_probe(struct device *dev)
+{
+ int i;
+ int base_addr = dev ? dev->base_addr : 0;
+
+ if (base_addr > 0x1ff) /* Check a single specified location. */
+ return at1700_probe1(dev, base_addr);
+ else if (base_addr != 0) /* Don't probe at all. */
+ return ENXIO;
+
+ for (i = 0; at1700_probe_list[i]; i++) {
+ int ioaddr = at1700_probe_list[i];
+ if (check_region(ioaddr, AT1700_IO_EXTENT))
+ continue;
+ if (at1700_probe1(dev, ioaddr) == 0)
+ return 0;
+ }
+
+ return ENODEV;
+}
+#endif
+
+/* The Fujitsu datasheet suggests that the NIC be probed for by checking its
+ "signature", the default bit pattern after a reset. This *doesn't* work --
+ there is no way to reset the bus interface without a complete power-cycle!
+
+ It turns out that ATI came to the same conclusion I did: the only thing
+ that can be done is checking a few bits and then diving right into an
+ EEPROM read. */
+
+int at1700_probe1(struct device *dev, short ioaddr)
+{
+ char irqmap[8] = {3, 4, 5, 9, 10, 11, 14, 15};
+ unsigned int i, irq;
+
+ /* Resetting the chip doesn't reset the ISA interface, so don't bother.
+ That means we have to be careful with the register values we probe for.
+ */
+#ifdef notdef
+ printk("at1700 probe at %#x, eeprom is %4.4x %4.4x %4.4x ctrl %4.4x.\n",
+ ioaddr, read_eeprom(ioaddr, 4), read_eeprom(ioaddr, 5),
+ read_eeprom(ioaddr, 6), inw(ioaddr + EEPROM_Ctrl));
+#endif
+ if (at1700_probe_list[inb(ioaddr + IOCONFIG) & 0x07] != ioaddr
+ || read_eeprom(ioaddr, 4) != 0x0000
+ || (read_eeprom(ioaddr, 5) & 0xff00) != 0xF400)
+ return -ENODEV;
+
+ /* Reset the internal state machines. */
+ outb(0, ioaddr + RESET);
+
+ irq = irqmap[(read_eeprom(ioaddr, 12)&0x04)
+ | (read_eeprom(ioaddr, 0)>>14)];
+
+ /* Snarf the interrupt vector now. */
+ if (request_irq(irq, &net_interrupt, 0, "at1700")) {
+ printk ("AT1700 found at %#3x, but it's unusable due to a conflict on"
+ "IRQ %d.\n", ioaddr, irq);
+ return EAGAIN;
+ }
+
+ /* Allocate a new 'dev' if needed. */
+ if (dev == NULL)
+ dev = init_etherdev(0, sizeof(struct net_local));
+
+ /* Grab the region so that we can find another board if the IRQ request
+ fails. */
+ request_region(ioaddr, AT1700_IO_EXTENT, "at1700");
+
+ printk("%s: AT1700 found at %#3x, IRQ %d, address ", dev->name,
+ ioaddr, irq);
+
+ dev->base_addr = ioaddr;
+ dev->irq = irq;
+ irq2dev_map[irq] = dev;
+
+ for(i = 0; i < 3; i++) {
+ unsigned short eeprom_val = read_eeprom(ioaddr, 4+i);
+ printk("%04x", eeprom_val);
+ ((unsigned short *)dev->dev_addr)[i] = ntohs(eeprom_val);
+ }
+
+ /* The EEPROM word 12 bit 0x0400 means use regular 100 ohm 10baseT signals,
+ rather than 150 ohm shielded twisted pair compensation.
+ 0x0000 == auto-sense the interface
+ 0x0800 == use TP interface
+ 0x1800 == use coax interface
+ */
+ {
+ const char *porttype[] = {"auto-sense", "10baseT", "auto-sense", "10base2"};
+ ushort setup_value = read_eeprom(ioaddr, 12);
+
+ dev->if_port = setup_value >> 8;
+ printk(" %s interface.\n", porttype[(dev->if_port>>3) & 3]);
+ }
+
+ /* Set the station address in bank zero. */
+ outb(0xe0, ioaddr + 7);
+ for (i = 0; i < 6; i++)
+ outb(dev->dev_addr[i], ioaddr + 8 + i);
+
+ /* Switch to bank 1 and set the multicast table to accept none. */
+ outb(0xe4, ioaddr + 7);
+ for (i = 0; i < 8; i++)
+ outb(0x00, ioaddr + 8 + i);
+
+ /* Set the configuration register 0 to 32K 100ns. byte-wide memory, 16 bit
+ bus access, two 4K Tx queues, and disabled Tx and Rx. */
+ outb(0xda, ioaddr + CONFIG_0);
+
+ /* Switch to bank 2 and lock our I/O address. */
+ outb(0xe8, ioaddr + 7);
+ outb(dev->if_port, MODE13);
+
+ /* Power-down the chip. Aren't we green! */
+ outb(0x00, ioaddr + CONFIG_1);
+
+ if (net_debug)
+ printk(version);
+
+ /* Initialize the device structure. */
+ dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
+ if (dev->priv == NULL)
+ return -ENOMEM;
+ memset(dev->priv, 0, sizeof(struct net_local));
+
+ dev->open = net_open;
+ dev->stop = net_close;
+ dev->hard_start_xmit = net_send_packet;
+ dev->get_stats = net_get_stats;
+ dev->set_multicast_list = &set_multicast_list;
+
+ /* Fill in the fields of 'dev' with ethernet-generic values. */
+
+ ether_setup(dev);
+ return 0;
+}
+
+static int read_eeprom(int ioaddr, int location)
+{
+ int i;
+ unsigned short retval = 0;
+ short ee_addr = ioaddr + EEPROM_Ctrl;
+ short ee_daddr = ioaddr + EEPROM_Data;
+ int read_cmd = location | EE_READ_CMD;
+ short ctrl_val = EE_CS;
+
+ outb(ctrl_val, ee_addr);
+
+ /* Shift the read command bits out. */
+ for (i = 9; i >= 0; i--) {
+ short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
+ outb(dataval, ee_daddr);
+ outb(EE_CS | EE_SHIFT_CLK, ee_addr); /* EEPROM clock tick. */
+ eeprom_delay();
+ outb(EE_CS, ee_addr); /* Finish EEPROM a clock tick. */
+ eeprom_delay();
+ }
+ outb(EE_CS, ee_addr);
+
+ for (i = 16; i > 0; i--) {
+ outb(EE_CS | EE_SHIFT_CLK, ee_addr);
+ eeprom_delay();
+ retval = (retval << 1) | ((inb(ee_daddr) & EE_DATA_READ) ? 1 : 0);
+ outb(EE_CS, ee_addr);
+ eeprom_delay();
+ }
+
+ /* Terminate the EEPROM access. */
+ ctrl_val &= ~EE_CS;
+ outb(ctrl_val | EE_SHIFT_CLK, ee_addr);
+ eeprom_delay();
+ outb(ctrl_val, ee_addr);
+ eeprom_delay();
+ return retval;
+}
+
+
+
+static int net_open(struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+ int i;
+
+ /* Powerup the chip, initialize config register 1, and select bank 0. */
+ outb(0xe0, ioaddr + CONFIG_1);
+
+ /* Set the station address in bank zero. */
+ for (i = 0; i < 6; i++)
+ outb(dev->dev_addr[i], ioaddr + 8 + i);
+
+ /* Switch to bank 1 and set the multicast table to accept none. */
+ outb(0xe4, ioaddr + 7);
+ for (i = 0; i < 8; i++)
+ outb(0x00, ioaddr + 8 + i);
+
+ /* Set the configuration register 0 to 32K 100ns. byte-wide memory, 16 bit
+ bus access, and two 4K Tx queues. */
+ outb(0xda, ioaddr + CONFIG_0);
+
+ /* Same config 0, except enable the Rx and Tx. */
+ outb(0x5a, ioaddr + CONFIG_0);
+ /* Switch to register bank 2 for the run-time registers. */
+ outb(0xe8, ioaddr + CONFIG_1);
+
+ lp->tx_started = 0;
+ lp->tx_queue = 0;
+ lp->tx_queue_len = 0;
+
+ /* Turn on Rx interrupts, leave Tx interrupts off until packet Tx. */
+ outb(0x00, ioaddr + TX_INTR);
+ outb(0x81, ioaddr + RX_INTR);
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+
+ MOD_INC_USE_COUNT;
+
+ return 0;
+}
+
+static int
+net_send_packet(struct sk_buff *skb, struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+
+ if (dev->tbusy) {
+ /* If we get here, some higher level has decided we are broken.
+ There should really be a "kick me" function call instead. */
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < 10)
+ return 1;
+ printk("%s: transmit timed out with status %04x, %s?\n", dev->name,
+ inw(ioaddr + STATUS), inb(ioaddr + TX_STATUS) & 0x80
+ ? "IRQ conflict" : "network cable problem");
+ printk("%s: timeout registers: %04x %04x %04x %04x %04x %04x %04x %04x.\n",
+ dev->name, inw(ioaddr + 0), inw(ioaddr + 2), inw(ioaddr + 4),
+ inw(ioaddr + 6), inw(ioaddr + 8), inw(ioaddr + 10),
+ inw(ioaddr + 12), inw(ioaddr + 14));
+ lp->stats.tx_errors++;
+ /* ToDo: We should try to restart the adaptor... */
+ outw(0xffff, ioaddr + 24);
+ outw(0xffff, ioaddr + TX_STATUS);
+ outw(0xe85a, ioaddr + CONFIG_0);
+ outw(0x8100, ioaddr + TX_INTR);
+ dev->tbusy=0;
+ dev->trans_start = jiffies;
+ lp->tx_started = 0;
+ lp->tx_queue = 0;
+ lp->tx_queue_len = 0;
+ }
+
+ /* If some higher layer thinks we've missed an tx-done interrupt
+ we are passed NULL. Caution: dev_tint() handles the cli()/sti()
+ itself. */
+ if (skb == NULL) {
+ dev_tint(dev);
+ return 0;
+ }
+
+ /* Block a timer-based transmit from overlapping. This could better be
+ done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
+ if (set_bit(0, (void*)&dev->tbusy) != 0)
+ printk("%s: Transmitter access conflict.\n", dev->name);
+ else {
+ short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+ unsigned char *buf = skb->data;
+
+ /* Turn off the possible Tx interrupts. */
+ outb(0x00, ioaddr + TX_INTR);
+
+ outw(length, ioaddr + DATAPORT);
+ outsw(ioaddr + DATAPORT, buf, (length + 1) >> 1);
+
+ lp->tx_queue++;
+ lp->tx_queue_len += length + 2;
+
+ if (lp->tx_started == 0) {
+ /* If the Tx is idle, always trigger a transmit. */
+ outb(0x80 | lp->tx_queue, ioaddr + TX_START);
+ lp->tx_queue = 0;
+ lp->tx_queue_len = 0;
+ dev->trans_start = jiffies;
+ lp->tx_started = 1;
+ dev->tbusy = 0;
+ } else if (lp->tx_queue_len < 4096 - 1502)
+ /* Yes, there is room for one more packet. */
+ dev->tbusy = 0;
+
+ /* Turn on Tx interrupts back on. */
+ outb(0x82, ioaddr + TX_INTR);
+ }
+ dev_kfree_skb (skb, FREE_WRITE);
+
+ return 0;
+}
+
+/* The typical workload of the driver:
+ Handle the network interface interrupts. */
+static void
+net_interrupt(int irq, struct pt_regs *regs)
+{
+ struct device *dev = (struct device *)(irq2dev_map[irq]);
+ struct net_local *lp;
+ int ioaddr, status;
+
+ if (dev == NULL) {
+ printk ("at1700_interrupt(): irq %d for unknown device.\n", irq);
+ return;
+ }
+ dev->interrupt = 1;
+
+ ioaddr = dev->base_addr;
+ lp = (struct net_local *)dev->priv;
+ status = inw(ioaddr + TX_STATUS);
+ outw(status, ioaddr + TX_STATUS);
+
+ if (net_debug > 4)
+ printk("%s: Interrupt with status %04x.\n", dev->name, status);
+ if (status & 0xff00
+ || (inb(ioaddr + RX_MODE) & 0x40) == 0) { /* Got a packet(s). */
+ net_rx(dev);
+ }
+ if (status & 0x00ff) {
+ if (status & 0x80) {
+ lp->stats.tx_packets++;
+ if (lp->tx_queue) {
+ outb(0x80 | lp->tx_queue, ioaddr + TX_START);
+ lp->tx_queue = 0;
+ lp->tx_queue_len = 0;
+ dev->trans_start = jiffies;
+ dev->tbusy = 0;
+ mark_bh(NET_BH); /* Inform upper layers. */
+ } else {
+ lp->tx_started = 0;
+ /* Turn on Tx interrupts off. */
+ outb(0x00, ioaddr + TX_INTR);
+ dev->tbusy = 0;
+ mark_bh(NET_BH); /* Inform upper layers. */
+ }
+ }
+ }
+
+ dev->interrupt = 0;
+ return;
+}
+
+/* We have a good packet(s), get it/them out of the buffers. */
+static void
+net_rx(struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+ int boguscount = 5;
+
+ while ((inb(ioaddr + RX_MODE) & 0x40) == 0) {
+ ushort status = inw(ioaddr + DATAPORT);
+ ushort pkt_len = inw(ioaddr + DATAPORT);
+
+ if (net_debug > 4)
+ printk("%s: Rxing packet mode %02x status %04x.\n",
+ dev->name, inb(ioaddr + RX_MODE), status);
+#ifndef final_version
+ if (status == 0) {
+ outb(0x05, ioaddr + 14);
+ break;
+ }
+#endif
+
+ if ((status & 0xF0) != 0x20) { /* There was an error. */
+ lp->stats.rx_errors++;
+ if (status & 0x08) lp->stats.rx_length_errors++;
+ if (status & 0x04) lp->stats.rx_frame_errors++;
+ if (status & 0x02) lp->stats.rx_crc_errors++;
+ if (status & 0x01) lp->stats.rx_over_errors++;
+ } else {
+ /* Malloc up new buffer. */
+ struct sk_buff *skb;
+
+ if (pkt_len > 1550) {
+ printk("%s: The AT1700 claimed a very large packet, size %d.\n",
+ dev->name, pkt_len);
+ /* Prime the FIFO and then flush the packet. */
+ inw(ioaddr + DATAPORT); inw(ioaddr + DATAPORT);
+ outb(0x05, ioaddr + 14);
+ lp->stats.rx_errors++;
+ break;
+ }
+ skb = dev_alloc_skb(pkt_len+3);
+ if (skb == NULL) {
+ printk("%s: Memory squeeze, dropping packet (len %d).\n",
+ dev->name, pkt_len);
+ /* Prime the FIFO and then flush the packet. */
+ inw(ioaddr + DATAPORT); inw(ioaddr + DATAPORT);
+ outb(0x05, ioaddr + 14);
+ lp->stats.rx_dropped++;
+ break;
+ }
+ skb->dev = dev;
+ skb_reserve(skb,2);
+
+ insw(ioaddr + DATAPORT, skb_put(skb,pkt_len), (pkt_len + 1) >> 1);
+ skb->protocol=eth_type_trans(skb, dev);
+ netif_rx(skb);
+ lp->stats.rx_packets++;
+ }
+ if (--boguscount <= 0)
+ break;
+ }
+
+ /* If any worth-while packets have been received, dev_rint()
+ has done a mark_bh(NET_BH) for us and will work on them
+ when we get to the bottom-half routine. */
+ {
+ int i;
+ for (i = 0; i < 20; i++) {
+ if ((inb(ioaddr + RX_MODE) & 0x40) == 0x40)
+ break;
+ inw(ioaddr + DATAPORT); /* dummy status read */
+ outb(0x05, ioaddr + 14);
+ }
+
+ if (net_debug > 5)
+ printk("%s: Exint Rx packet with mode %02x after %d ticks.\n",
+ dev->name, inb(ioaddr + RX_MODE), i);
+ }
+ return;
+}
+
+/* The inverse routine to net_open(). */
+static int net_close(struct device *dev)
+{
+ int ioaddr = dev->base_addr;
+
+ dev->tbusy = 1;
+ dev->start = 0;
+
+ /* Set configuration register 0 to disable Tx and Rx. */
+ outb(0xda, ioaddr + CONFIG_0);
+
+ /* Update the statistics -- ToDo. */
+
+ /* Power-down the chip. Green, green, green! */
+ outb(0x00, ioaddr + CONFIG_1);
+
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+}
+
+/* Get the current statistics. This may be called with the card open or
+ closed. */
+static struct enet_statistics *
+net_get_stats(struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+
+ cli();
+ /* ToDo: Update the statistics from the device registers. */
+ sti();
+
+ return &lp->stats;
+}
+
+/* Set or clear the multicast filter for this adaptor.
+ num_addrs == -1 Promiscuous mode, receive all packets
+ num_addrs == 0 Normal mode, clear multicast list
+ num_addrs > 0 Multicast mode, receive normal and MC packets, and do
+ best-effort filtering.
+ */
+static void
+set_multicast_list(struct device *dev)
+{
+ short ioaddr = dev->base_addr;
+ if (dev->mc_count || dev->flags&(IFF_PROMISC|IFF_ALLMULTI))
+ {
+ /*
+ * We must make the kernel realise we had to move
+ * into promisc mode or we start all out war on
+ * the cable. - AC
+ */
+ dev->flags|=IFF_PROMISC;
+
+ outb(3, ioaddr + RX_MODE); /* Enable promiscuous mode */
+ }
+ else
+ outb(2, ioaddr + RX_MODE); /* Disable promiscuous, use normal mode */
+}
+#ifdef MODULE
+static char devicename[9] = { 0, };
+static struct device dev_at1700 = {
+ devicename, /* device name is inserted by linux/drivers/net/net_init.c */
+ 0, 0, 0, 0,
+ 0, 0,
+ 0, 0, 0, NULL, at1700_probe };
+
+static int io = 0x260;
+static int irq = 0;
+
+int init_module(void)
+{
+ if (io == 0)
+ printk("at1700: You should not use auto-probing with insmod!\n");
+ dev_at1700.base_addr = io;
+ dev_at1700.irq = irq;
+ if (register_netdev(&dev_at1700) != 0) {
+ printk("at1700: register_netdev() returned non-zero.\n");
+ return -EIO;
+ }
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ unregister_netdev(&dev_at1700);
+ kfree(dev_at1700.priv);
+ dev_at1700.priv = NULL;
+
+ /* If we don't do this, we can't re-insmod it later. */
+ free_irq(dev_at1700.irq);
+ irq2dev_map[dev_at1700.irq] = NULL;
+ release_region(dev_at1700.base_addr, AT1700_IO_EXTENT);
+}
+#endif /* MODULE */
+
+/*
+ * Local variables:
+ * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c at1700.c"
+ * version-control: t
+ * kept-new-versions: 5
+ * tab-width: 4
+ * c-indent-level: 4
+ * End:
+ */
diff --git a/i386/i386at/gpl/linux/net/atp.c b/i386/i386at/gpl/linux/net/atp.c
new file mode 100644
index 00000000..62aa04ef
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/atp.c
@@ -0,0 +1,787 @@
+/* atp.c: Attached (pocket) ethernet adapter driver for linux. */
+/*
+ This is a driver for a commonly OEMed pocket (parallel port)
+ ethernet adapter.
+
+ Written 1993,1994,1995 by Donald Becker.
+
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency.
+
+ This software may be used and distributed according to the terms
+ of the GNU Public License, incorporated herein by reference.
+
+ The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
+ Center of Excellence in Space Data and Information Sciences
+ Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
+
+ The timer-based reset code was written by Bill Carlson, wwc@super.org.
+*/
+
+static const char *version =
+ "atp.c:v1.01 1/18/95 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+/*
+ This file is a device driver for the RealTek (aka AT-Lan-Tec) pocket
+ ethernet adapter. This is a common low-cost OEM pocket ethernet
+ adapter, sold under many names.
+
+ Sources:
+ This driver was written from the packet driver assembly code provided by
+ Vincent Bono of AT-Lan-Tec. Ever try to figure out how a complicated
+ device works just from the assembly code? It ain't pretty. The following
+ description is written based on guesses and writing lots of special-purpose
+ code to test my theorized operation.
+
+ Theory of Operation
+
+ The RTL8002 adapter seems to be built around a custom spin of the SEEQ
+ controller core. It probably has a 16K or 64K internal packet buffer, of
+ which the first 4K is devoted to transmit and the rest to receive.
+ The controller maintains the queue of received packet and the packet buffer
+ access pointer internally, with only 'reset to beginning' and 'skip to next
+ packet' commands visible. The transmit packet queue holds two (or more?)
+ packets: both 'retransmit this packet' (due to collision) and 'transmit next
+ packet' commands must be started by hand.
+
+ The station address is stored in a standard bit-serial EEPROM which must be
+ read (ughh) by the device driver. (Provisions have been made for
+ substituting a 74S288 PROM, but I haven't gotten reports of any models
+ using it.) Unlike built-in devices, a pocket adapter can temporarily lose
+ power without indication to the device driver. The major effect is that
+ the station address, receive filter (promiscuous, etc.) and transceiver
+ must be reset.
+
+ The controller itself has 16 registers, some of which use only the lower
+ bits. The registers are read and written 4 bits at a time. The four bit
+ register address is presented on the data lines along with a few additional
+ timing and control bits. The data is then read from status port or written
+ to the data port.
+
+ Since the bulk data transfer of the actual packets through the slow
+ parallel port dominates the driver's running time, four distinct data
+ (non-register) transfer modes are provided by the adapter, two in each
+ direction. In the first mode timing for the nibble transfers is
+ provided through the data port. In the second mode the same timing is
+ provided through the control port. In either case the data is read from
+ the status port and written to the data port, just as it is accessing
+ registers.
+
+ In addition to the basic data transfer methods, several more are modes are
+ created by adding some delay by doing multiple reads of the data to allow
+ it to stabilize. This delay seems to be needed on most machines.
+
+ The data transfer mode is stored in the 'dev->if_port' field. Its default
+ value is '4'. It may be overridden at boot-time using the third parameter
+ to the "ether=..." initialization.
+
+ The header file <atp.h> provides inline functions that encapsulate the
+ register and data access methods. These functions are hand-tuned to
+ generate reasonable object code. This header file also documents my
+ interpretations of the device registers.
+*/
+
+#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/in.h>
+#include <linux/malloc.h>
+#include <linux/string.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 "atp.h"
+
+/* use 0 for production, 1 for verification, >2 for debug */
+#ifndef NET_DEBUG
+#define NET_DEBUG 1
+#endif
+static unsigned int net_debug = NET_DEBUG;
+
+/* The number of low I/O ports used by the ethercard. */
+#define ETHERCARD_TOTAL_SIZE 3
+
+/* This code, written by wwc@super.org, resets the adapter every
+ TIMED_CHECKER ticks. This recovers from an unknown error which
+ hangs the device. */
+#define TIMED_CHECKER (HZ/4)
+#ifdef TIMED_CHECKER
+#include <linux/timer.h>
+static void atp_timed_checker(unsigned long ignored);
+static struct device *atp_timed_dev;
+static struct timer_list atp_timer = {NULL, NULL, 0, 0, atp_timed_checker};
+#endif
+
+/* Index to functions, as function prototypes. */
+
+extern int atp_probe(struct device *dev);
+
+static int atp_probe1(struct device *dev, short ioaddr);
+static void get_node_ID(struct device *dev);
+static unsigned short eeprom_op(short ioaddr, unsigned int cmd);
+static int net_open(struct device *dev);
+static void hardware_init(struct device *dev);
+static void write_packet(short ioaddr, int length, unsigned char *packet, int mode);
+static void trigger_send(short ioaddr, int length);
+static int net_send_packet(struct sk_buff *skb, struct device *dev);
+static void net_interrupt(int irq, struct pt_regs *regs);
+static void net_rx(struct device *dev);
+static void read_block(short ioaddr, int length, unsigned char *buffer, int data_mode);
+static int net_close(struct device *dev);
+static struct enet_statistics *net_get_stats(struct device *dev);
+static void set_multicast_list(struct device *dev);
+
+
+/* Check for a network adapter of this type, and return '0' iff one exists.
+ If dev->base_addr == 0, probe all likely locations.
+ If dev->base_addr == 1, always return failure.
+ If dev->base_addr == 2, allocate space for the device and return success
+ (detachable devices only).
+ */
+int
+atp_init(struct device *dev)
+{
+ int *port, ports[] = {0x378, 0x278, 0x3bc, 0};
+ int base_addr = dev->base_addr;
+
+ if (base_addr > 0x1ff) /* Check a single specified location. */
+ return atp_probe1(dev, base_addr);
+ else if (base_addr == 1) /* Don't probe at all. */
+ return ENXIO;
+
+ for (port = ports; *port; port++) {
+ int ioaddr = *port;
+ outb(0x57, ioaddr + PAR_DATA);
+ if (inb(ioaddr + PAR_DATA) != 0x57)
+ continue;
+ if (atp_probe1(dev, ioaddr) == 0)
+ return 0;
+ }
+
+ return ENODEV;
+}
+
+static int atp_probe1(struct device *dev, short ioaddr)
+{
+ int saved_ctrl_reg, status;
+
+ outb(0xff, ioaddr + PAR_DATA);
+ /* Save the original value of the Control register, in case we guessed
+ wrong. */
+ saved_ctrl_reg = inb(ioaddr + PAR_CONTROL);
+ /* IRQEN=0, SLCTB=high INITB=high, AUTOFDB=high, STBB=high. */
+ outb(0x04, ioaddr + PAR_CONTROL);
+ write_reg_high(ioaddr, CMR1, CMR1h_RESET);
+ eeprom_delay(2048);
+ status = read_nibble(ioaddr, CMR1);
+
+ if ((status & 0x78) != 0x08) {
+ /* The pocket adapter probe failed, restore the control register. */
+ outb(saved_ctrl_reg, ioaddr + PAR_CONTROL);
+ return 1;
+ }
+ status = read_nibble(ioaddr, CMR2_h);
+ if ((status & 0x78) != 0x10) {
+ outb(saved_ctrl_reg, ioaddr + PAR_CONTROL);
+ return 1;
+ }
+ /* Find the IRQ used by triggering an interrupt. */
+ write_reg_byte(ioaddr, CMR2, 0x01); /* No accept mode, IRQ out. */
+ write_reg_high(ioaddr, CMR1, CMR1h_RxENABLE | CMR1h_TxENABLE); /* Enable Tx and Rx. */
+
+ /* Omit autoIRQ routine for now. Use "table lookup" instead. Uhgggh. */
+ if (ioaddr == 0x378)
+ dev->irq = 7;
+ else
+ dev->irq = 5;
+ write_reg_high(ioaddr, CMR1, CMR1h_TxRxOFF); /* Disable Tx and Rx units. */
+ write_reg(ioaddr, CMR2, CMR2_NULL);
+
+ dev->base_addr = ioaddr;
+
+ /* Read the station address PROM. */
+ get_node_ID(dev);
+
+ printk("%s: Pocket adapter found at %#3lx, IRQ %d, SAPROM "
+ "%02X:%02X:%02X:%02X:%02X:%02X.\n", dev->name, dev->base_addr,
+ dev->irq, dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
+ dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
+
+ /* Leave the hardware in a reset state. */
+ write_reg_high(ioaddr, CMR1, CMR1h_RESET);
+
+ if (net_debug)
+ printk(version);
+
+ /* Initialize the device structure. */
+ ether_setup(dev);
+ dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
+ if (dev->priv == NULL)
+ return -ENOMEM;
+ memset(dev->priv, 0, sizeof(struct net_local));
+
+
+ {
+ struct net_local *lp = (struct net_local *)dev->priv;
+ lp->addr_mode = CMR2h_Normal;
+ }
+
+ /* For the ATP adapter the "if_port" is really the data transfer mode. */
+ dev->if_port = (dev->mem_start & 0xf) ? dev->mem_start & 0x7 : 4;
+ if (dev->mem_end & 0xf)
+ net_debug = dev->mem_end & 7;
+
+ dev->open = net_open;
+ dev->stop = net_close;
+ dev->hard_start_xmit = net_send_packet;
+ dev->get_stats = net_get_stats;
+ dev->set_multicast_list = &set_multicast_list;
+
+#ifdef TIMED_CHECKER
+ del_timer(&atp_timer);
+ atp_timer.expires = jiffies + TIMED_CHECKER;
+ atp_timed_dev = dev;
+ add_timer(&atp_timer);
+#endif
+ return 0;
+}
+
+/* Read the station address PROM, usually a word-wide EEPROM. */
+static void get_node_ID(struct device *dev)
+{
+ short ioaddr = dev->base_addr;
+ int sa_offset = 0;
+ int i;
+
+ write_reg(ioaddr, CMR2, CMR2_EEPROM); /* Point to the EEPROM control registers. */
+
+ /* Some adapters have the station address at offset 15 instead of offset
+ zero. Check for it, and fix it if needed. */
+ if (eeprom_op(ioaddr, EE_READ(0)) == 0xffff)
+ sa_offset = 15;
+
+ for (i = 0; i < 3; i++)
+ ((unsigned short *)dev->dev_addr)[i] =
+ ntohs(eeprom_op(ioaddr, EE_READ(sa_offset + i)));
+
+ write_reg(ioaddr, CMR2, CMR2_NULL);
+}
+
+/*
+ An EEPROM read command starts by shifting out 0x60+address, and then
+ shifting in the serial data. See the NatSemi databook for details.
+ * ________________
+ * CS : __|
+ * ___ ___
+ * CLK: ______| |___| |
+ * __ _______ _______
+ * DI : __X_______X_______X
+ * DO : _________X_______X
+ */
+
+static unsigned short eeprom_op(short ioaddr, unsigned int cmd)
+{
+ unsigned eedata_out = 0;
+ int num_bits = EE_CMD_SIZE;
+
+ while (--num_bits >= 0) {
+ char outval = test_bit(num_bits, &cmd) ? EE_DATA_WRITE : 0;
+ write_reg_high(ioaddr, PROM_CMD, outval | EE_CLK_LOW);
+ eeprom_delay(5);
+ write_reg_high(ioaddr, PROM_CMD, outval | EE_CLK_HIGH);
+ eedata_out <<= 1;
+ if (read_nibble(ioaddr, PROM_DATA) & EE_DATA_READ)
+ eedata_out++;
+ eeprom_delay(5);
+ }
+ write_reg_high(ioaddr, PROM_CMD, EE_CLK_LOW & ~EE_CS);
+ return eedata_out;
+}
+
+
+/* Open/initialize the board. This is called (in the current kernel)
+ sometime after booting when the 'ifconfig' program is run.
+
+ This routine sets everything up anew at each open, even
+ registers that "should" only need to be set once at boot, so that
+ there is non-reboot way to recover if something goes wrong.
+
+ This is an attachable device: if there is no dev->priv entry then it wasn't
+ probed for at boot-time, and we need to probe for it again.
+ */
+static int net_open(struct device *dev)
+{
+
+ /* The interrupt line is turned off (tri-stated) when the device isn't in
+ use. That's especially important for "attached" interfaces where the
+ port or interrupt may be shared. */
+ if (irq2dev_map[dev->irq] != 0
+ || (irq2dev_map[dev->irq] = dev) == 0
+ || request_irq(dev->irq, &net_interrupt, 0, "ATP")) {
+ return -EAGAIN;
+ }
+
+ hardware_init(dev);
+ dev->start = 1;
+ return 0;
+}
+
+/* This routine resets the hardware. We initialize everything, assuming that
+ the hardware may have been temporarily detached. */
+static void hardware_init(struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+ int i;
+
+ write_reg_high(ioaddr, CMR1, CMR1h_RESET);
+
+ for (i = 0; i < 6; i++)
+ write_reg_byte(ioaddr, PAR0 + i, dev->dev_addr[i]);
+
+ write_reg_high(ioaddr, CMR2, lp->addr_mode);
+
+ if (net_debug > 2) {
+ printk("%s: Reset: current Rx mode %d.\n", dev->name,
+ (read_nibble(ioaddr, CMR2_h) >> 3) & 0x0f);
+ }
+
+ write_reg(ioaddr, CMR2, CMR2_IRQOUT);
+ write_reg_high(ioaddr, CMR1, CMR1h_RxENABLE | CMR1h_TxENABLE);
+
+ /* Enable the interrupt line from the serial port. */
+ outb(Ctrl_SelData + Ctrl_IRQEN, ioaddr + PAR_CONTROL);
+
+ /* Unmask the interesting interrupts. */
+ write_reg(ioaddr, IMR, ISR_RxOK | ISR_TxErr | ISR_TxOK);
+ write_reg_high(ioaddr, IMR, ISRh_RxErr);
+
+ lp->tx_unit_busy = 0;
+ lp->pac_cnt_in_tx_buf = 0;
+ lp->saved_tx_size = 0;
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+}
+
+static void trigger_send(short ioaddr, int length)
+{
+ write_reg_byte(ioaddr, TxCNT0, length & 0xff);
+ write_reg(ioaddr, TxCNT1, length >> 8);
+ write_reg(ioaddr, CMR1, CMR1_Xmit);
+}
+
+static void write_packet(short ioaddr, int length, unsigned char *packet, int data_mode)
+{
+ length = (length + 1) & ~1; /* Round up to word length. */
+ outb(EOC+MAR, ioaddr + PAR_DATA);
+ if ((data_mode & 1) == 0) {
+ /* Write the packet out, starting with the write addr. */
+ outb(WrAddr+MAR, ioaddr + PAR_DATA);
+ do {
+ write_byte_mode0(ioaddr, *packet++);
+ } while (--length > 0) ;
+ } else {
+ /* Write the packet out in slow mode. */
+ unsigned char outbyte = *packet++;
+
+ outb(Ctrl_LNibWrite + Ctrl_IRQEN, ioaddr + PAR_CONTROL);
+ outb(WrAddr+MAR, ioaddr + PAR_DATA);
+
+ outb((outbyte & 0x0f)|0x40, ioaddr + PAR_DATA);
+ outb(outbyte & 0x0f, ioaddr + PAR_DATA);
+ outbyte >>= 4;
+ outb(outbyte & 0x0f, ioaddr + PAR_DATA);
+ outb(Ctrl_HNibWrite + Ctrl_IRQEN, ioaddr + PAR_CONTROL);
+ while (--length > 0)
+ write_byte_mode1(ioaddr, *packet++);
+ }
+ /* Terminate the Tx frame. End of write: ECB. */
+ outb(0xff, ioaddr + PAR_DATA);
+ outb(Ctrl_HNibWrite | Ctrl_SelData | Ctrl_IRQEN, ioaddr + PAR_CONTROL);
+}
+
+static int
+net_send_packet(struct sk_buff *skb, struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+
+ if (dev->tbusy) {
+ /* If we get here, some higher level has decided we are broken.
+ There should really be a "kick me" function call instead. */
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < 5)
+ return 1;
+ printk("%s: transmit timed out, %s?\n", dev->name,
+ inb(ioaddr + PAR_CONTROL) & 0x10 ? "network cable problem"
+ : "IRQ conflict");
+ lp->stats.tx_errors++;
+ /* Try to restart the adapter. */
+ hardware_init(dev);
+ dev->tbusy=0;
+ dev->trans_start = jiffies;
+ }
+
+ /* If some higher layer thinks we've missed an tx-done interrupt
+ we are passed NULL. Caution: dev_tint() handles the cli()/sti()
+ itself. */
+ if (skb == NULL) {
+ dev_tint(dev);
+ return 0;
+ }
+
+ /* Block a timer-based transmit from overlapping. This could better be
+ done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
+ if (set_bit(0, (void*)&dev->tbusy) != 0)
+ printk("%s: Transmitter access conflict.\n", dev->name);
+ else {
+ short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+ unsigned char *buf = skb->data;
+ int flags;
+
+ /* Disable interrupts by writing 0x00 to the Interrupt Mask Register.
+ This sequence must not be interrupted by an incoming packet. */
+ save_flags(flags);
+ cli();
+ write_reg(ioaddr, IMR, 0);
+ write_reg_high(ioaddr, IMR, 0);
+ restore_flags(flags);
+
+ write_packet(ioaddr, length, buf, dev->if_port);
+
+ lp->pac_cnt_in_tx_buf++;
+ if (lp->tx_unit_busy == 0) {
+ trigger_send(ioaddr, length);
+ lp->saved_tx_size = 0; /* Redundant */
+ lp->re_tx = 0;
+ lp->tx_unit_busy = 1;
+ } else
+ lp->saved_tx_size = length;
+
+ dev->trans_start = jiffies;
+ /* Re-enable the LPT interrupts. */
+ write_reg(ioaddr, IMR, ISR_RxOK | ISR_TxErr | ISR_TxOK);
+ write_reg_high(ioaddr, IMR, ISRh_RxErr);
+ }
+
+ dev_kfree_skb (skb, FREE_WRITE);
+
+ return 0;
+}
+
+/* The typical workload of the driver:
+ Handle the network interface interrupts. */
+static void
+net_interrupt(int irq, struct pt_regs * regs)
+{
+ struct device *dev = (struct device *)(irq2dev_map[irq]);
+ struct net_local *lp;
+ int ioaddr, status, boguscount = 20;
+ static int num_tx_since_rx = 0;
+
+ if (dev == NULL) {
+ printk ("ATP_interrupt(): irq %d for unknown device.\n", irq);
+ return;
+ }
+ dev->interrupt = 1;
+
+ ioaddr = dev->base_addr;
+ lp = (struct net_local *)dev->priv;
+
+ /* Disable additional spurious interrupts. */
+ outb(Ctrl_SelData, ioaddr + PAR_CONTROL);
+
+ /* The adapter's output is currently the IRQ line, switch it to data. */
+ write_reg(ioaddr, CMR2, CMR2_NULL);
+ write_reg(ioaddr, IMR, 0);
+
+ if (net_debug > 5) printk("%s: In interrupt ", dev->name);
+ while (--boguscount > 0) {
+ status = read_nibble(ioaddr, ISR);
+ if (net_debug > 5) printk("loop status %02x..", status);
+
+ if (status & (ISR_RxOK<<3)) {
+ write_reg(ioaddr, ISR, ISR_RxOK); /* Clear the Rx interrupt. */
+ do {
+ int read_status = read_nibble(ioaddr, CMR1);
+ if (net_debug > 6)
+ printk("handling Rx packet %02x..", read_status);
+ /* We acknowledged the normal Rx interrupt, so if the interrupt
+ is still outstanding we must have a Rx error. */
+ if (read_status & (CMR1_IRQ << 3)) { /* Overrun. */
+ lp->stats.rx_over_errors++;
+ /* Set to no-accept mode long enough to remove a packet. */
+ write_reg_high(ioaddr, CMR2, CMR2h_OFF);
+ net_rx(dev);
+ /* Clear the interrupt and return to normal Rx mode. */
+ write_reg_high(ioaddr, ISR, ISRh_RxErr);
+ write_reg_high(ioaddr, CMR2, lp->addr_mode);
+ } else if ((read_status & (CMR1_BufEnb << 3)) == 0) {
+ net_rx(dev);
+ dev->last_rx = jiffies;
+ num_tx_since_rx = 0;
+ } else
+ break;
+ } while (--boguscount > 0);
+ } else if (status & ((ISR_TxErr + ISR_TxOK)<<3)) {
+ if (net_debug > 6) printk("handling Tx done..");
+ /* Clear the Tx interrupt. We should check for too many failures
+ and reinitialize the adapter. */
+ write_reg(ioaddr, ISR, ISR_TxErr + ISR_TxOK);
+ if (status & (ISR_TxErr<<3)) {
+ lp->stats.collisions++;
+ if (++lp->re_tx > 15) {
+ lp->stats.tx_aborted_errors++;
+ hardware_init(dev);
+ break;
+ }
+ /* Attempt to retransmit. */
+ if (net_debug > 6) printk("attempting to ReTx");
+ write_reg(ioaddr, CMR1, CMR1_ReXmit + CMR1_Xmit);
+ } else {
+ /* Finish up the transmit. */
+ lp->stats.tx_packets++;
+ lp->pac_cnt_in_tx_buf--;
+ if ( lp->saved_tx_size) {
+ trigger_send(ioaddr, lp->saved_tx_size);
+ lp->saved_tx_size = 0;
+ lp->re_tx = 0;
+ } else
+ lp->tx_unit_busy = 0;
+ dev->tbusy = 0;
+ mark_bh(NET_BH); /* Inform upper layers. */
+ }
+ num_tx_since_rx++;
+ } else if (num_tx_since_rx > 8
+ && jiffies > dev->last_rx + 100) {
+ if (net_debug > 2)
+ printk("%s: Missed packet? No Rx after %d Tx and %ld jiffies"
+ " status %02x CMR1 %02x.\n", dev->name,
+ num_tx_since_rx, jiffies - dev->last_rx, status,
+ (read_nibble(ioaddr, CMR1) >> 3) & 15);
+ lp->stats.rx_missed_errors++;
+ hardware_init(dev);
+ num_tx_since_rx = 0;
+ break;
+ } else
+ break;
+ }
+
+ /* This following code fixes a rare (and very difficult to track down)
+ problem where the adapter forgets its ethernet address. */
+ {
+ int i;
+ for (i = 0; i < 6; i++)
+ write_reg_byte(ioaddr, PAR0 + i, dev->dev_addr[i]);
+#ifdef TIMED_CHECKER
+ del_timer(&atp_timer);
+ atp_timer.expires = jiffies + TIMED_CHECKER;
+ add_timer(&atp_timer);
+#endif
+ }
+
+ /* Tell the adapter that it can go back to using the output line as IRQ. */
+ write_reg(ioaddr, CMR2, CMR2_IRQOUT);
+ /* Enable the physical interrupt line, which is sure to be low until.. */
+ outb(Ctrl_SelData + Ctrl_IRQEN, ioaddr + PAR_CONTROL);
+ /* .. we enable the interrupt sources. */
+ write_reg(ioaddr, IMR, ISR_RxOK | ISR_TxErr | ISR_TxOK);
+ write_reg_high(ioaddr, IMR, ISRh_RxErr); /* Hmmm, really needed? */
+
+ if (net_debug > 5) printk("exiting interrupt.\n");
+
+ dev->interrupt = 0;
+
+ return;
+}
+
+#ifdef TIMED_CHECKER
+/* This following code fixes a rare (and very difficult to track down)
+ problem where the adapter forgets its ethernet address. */
+static void atp_timed_checker(unsigned long ignored)
+{
+ int i;
+ int ioaddr = atp_timed_dev->base_addr;
+
+ if (!atp_timed_dev->interrupt)
+ {
+ for (i = 0; i < 6; i++)
+#if 0
+ if (read_cmd_byte(ioaddr, PAR0 + i) != atp_timed_dev->dev_addr[i])
+ {
+ struct net_local *lp = (struct net_local *)atp_timed_dev->priv;
+ write_reg_byte(ioaddr, PAR0 + i, atp_timed_dev->dev_addr[i]);
+ if (i == 2)
+ lp->stats.tx_errors++;
+ else if (i == 3)
+ lp->stats.tx_dropped++;
+ else if (i == 4)
+ lp->stats.collisions++;
+ else
+ lp->stats.rx_errors++;
+ }
+#else
+ write_reg_byte(ioaddr, PAR0 + i, atp_timed_dev->dev_addr[i]);
+#endif
+ }
+ del_timer(&atp_timer);
+ atp_timer.expires = jiffies + TIMED_CHECKER;
+ add_timer(&atp_timer);
+}
+#endif
+
+/* We have a good packet(s), get it/them out of the buffers. */
+static void net_rx(struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+#ifdef notdef
+ ushort header[4];
+#else
+ struct rx_header rx_head;
+#endif
+
+ /* Process the received packet. */
+ outb(EOC+MAR, ioaddr + PAR_DATA);
+ read_block(ioaddr, 8, (unsigned char*)&rx_head, dev->if_port);
+ if (net_debug > 5)
+ printk(" rx_count %04x %04x %04x %04x..", rx_head.pad,
+ rx_head.rx_count, rx_head.rx_status, rx_head.cur_addr);
+ if ((rx_head.rx_status & 0x77) != 0x01) {
+ lp->stats.rx_errors++;
+ /* Ackkk! I don't have any documentation on what the error bits mean!
+ The best I can do is slap the device around a bit. */
+ if (net_debug > 3) printk("%s: Unknown ATP Rx error %04x.\n",
+ dev->name, rx_head.rx_status);
+ hardware_init(dev);
+ return;
+ } else {
+ /* Malloc up new buffer. */
+ int pkt_len = (rx_head.rx_count & 0x7ff) - 4; /* The "-4" is omits the FCS (CRC). */
+ struct sk_buff *skb;
+
+ skb = dev_alloc_skb(pkt_len);
+ if (skb == NULL) {
+ printk("%s: Memory squeeze, dropping packet.\n", dev->name);
+ lp->stats.rx_dropped++;
+ goto done;
+ }
+ skb->dev = dev;
+
+ read_block(ioaddr, pkt_len, skb_put(skb,pkt_len), dev->if_port);
+
+ if (net_debug > 6) {
+ unsigned char *data = skb->data;
+ printk(" data %02x%02x%02x %02x%02x%02x %02x%02x%02x"
+ "%02x%02x%02x %02x%02x..",
+ data[0], data[1], data[2], data[3], data[4], data[5],
+ data[6], data[7], data[8], data[9], data[10], data[11],
+ data[12], data[13]);
+ }
+
+ skb->protocol=eth_type_trans(skb,dev);
+ netif_rx(skb);
+ lp->stats.rx_packets++;
+ }
+ done:
+ write_reg(ioaddr, CMR1, CMR1_NextPkt);
+ return;
+}
+
+static void read_block(short ioaddr, int length, unsigned char *p, int data_mode)
+{
+
+ if (data_mode <= 3) { /* Mode 0 or 1 */
+ outb(Ctrl_LNibRead, ioaddr + PAR_CONTROL);
+ outb(length == 8 ? RdAddr | HNib | MAR : RdAddr | MAR,
+ ioaddr + PAR_DATA);
+ if (data_mode <= 1) { /* Mode 0 or 1 */
+ do *p++ = read_byte_mode0(ioaddr); while (--length > 0);
+ } else /* Mode 2 or 3 */
+ do *p++ = read_byte_mode2(ioaddr); while (--length > 0);
+ } else if (data_mode <= 5)
+ do *p++ = read_byte_mode4(ioaddr); while (--length > 0);
+ else
+ do *p++ = read_byte_mode6(ioaddr); while (--length > 0);
+
+ outb(EOC+HNib+MAR, ioaddr + PAR_DATA);
+ outb(Ctrl_SelData, ioaddr + PAR_CONTROL);
+}
+
+/* The inverse routine to net_open(). */
+static int
+net_close(struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+
+ dev->tbusy = 1;
+ dev->start = 0;
+
+ /* Flush the Tx and disable Rx here. */
+ lp->addr_mode = CMR2h_OFF;
+ write_reg_high(ioaddr, CMR2, CMR2h_OFF);
+
+ /* Free the IRQ line. */
+ outb(0x00, ioaddr + PAR_CONTROL);
+ free_irq(dev->irq);
+ irq2dev_map[dev->irq] = 0;
+
+ /* Leave the hardware in a reset state. */
+ write_reg_high(ioaddr, CMR1, CMR1h_RESET);
+
+ return 0;
+}
+
+/* Get the current statistics. This may be called with the card open or
+ closed. */
+static struct enet_statistics *
+net_get_stats(struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ return &lp->stats;
+}
+
+/*
+ * Set or clear the multicast filter for this adapter.
+ */
+
+static void set_multicast_list(struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ short ioaddr = dev->base_addr;
+ int num_addrs=dev->mc_list;
+
+ if(dev->flags&(IFF_ALLMULTI|IFF_PROMISC))
+ num_addrs=1;
+ /*
+ * We must make the kernel realise we had to move
+ * into promisc mode or we start all out war on
+ * the cable. - AC
+ */
+ if(num_addrs)
+ dev->flags|=IFF_PROMISC;
+ lp->addr_mode = num_addrs ? CMR2h_PROMISC : CMR2h_Normal;
+ write_reg_high(ioaddr, CMR2, lp->addr_mode);
+}
+
+/*
+ * Local variables:
+ * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c atp.c"
+ * version-control: t
+ * kept-new-versions: 5
+ * tab-width: 4
+ * End:
+ */
diff --git a/i386/i386at/gpl/linux/net/atp.h b/i386/i386at/gpl/linux/net/atp.h
new file mode 100644
index 00000000..e58f8c10
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/atp.h
@@ -0,0 +1,264 @@
+#include <linux/if_ether.h>
+#include <linux/types.h>
+#include <asm/io.h>
+
+struct net_local {
+#ifdef __KERNEL__
+ struct enet_statistics stats;
+#endif
+ ushort saved_tx_size;
+ unsigned char
+ re_tx, /* Number of packet retransmissions. */
+ tx_unit_busy,
+ addr_mode, /* Current Rx filter e.g. promiscuous, etc. */
+ pac_cnt_in_tx_buf;
+};
+
+struct rx_header {
+ ushort pad; /* The first read is always corrupted. */
+ ushort rx_count;
+ ushort rx_status; /* Unknown bit assignments :-<. */
+ ushort cur_addr; /* Apparently the current buffer address(?) */
+};
+
+#define PAR_DATA 0
+#define PAR_STATUS 1
+#define PAR_CONTROL 2
+
+#define Ctrl_LNibRead 0x08 /* LP_PSELECP */
+#define Ctrl_HNibRead 0
+#define Ctrl_LNibWrite 0x08 /* LP_PSELECP */
+#define Ctrl_HNibWrite 0
+#define Ctrl_SelData 0x04 /* LP_PINITP */
+#define Ctrl_IRQEN 0x10 /* LP_PINTEN */
+
+#define EOW 0xE0
+#define EOC 0xE0
+#define WrAddr 0x40 /* Set address of EPLC read, write register. */
+#define RdAddr 0xC0
+#define HNib 0x10
+
+enum page0_regs
+{
+ /* The first six registers hold the ethernet physical station address. */
+ PAR0 = 0, PAR1 = 1, PAR2 = 2, PAR3 = 3, PAR4 = 4, PAR5 = 5,
+ TxCNT0 = 6, TxCNT1 = 7, /* The transmit byte count. */
+ TxSTAT = 8, RxSTAT = 9, /* Tx and Rx status. */
+ ISR = 10, IMR = 11, /* Interrupt status and mask. */
+ CMR1 = 12, /* Command register 1. */
+ CMR2 = 13, /* Command register 2. */
+ MAR = 14, /* Memory address register. */
+ CMR2_h = 0x1d, };
+
+enum eepage_regs
+{ PROM_CMD = 6, PROM_DATA = 7 }; /* Note that PROM_CMD is in the "high" bits. */
+
+
+#define ISR_TxOK 0x01
+#define ISR_RxOK 0x04
+#define ISR_TxErr 0x02
+#define ISRh_RxErr 0x11 /* ISR, high nibble */
+
+#define CMR1h_RESET 0x04 /* Reset. */
+#define CMR1h_RxENABLE 0x02 /* Rx unit enable. */
+#define CMR1h_TxENABLE 0x01 /* Tx unit enable. */
+#define CMR1h_TxRxOFF 0x00
+#define CMR1_ReXmit 0x08 /* Trigger a retransmit. */
+#define CMR1_Xmit 0x04 /* Trigger a transmit. */
+#define CMR1_IRQ 0x02 /* Interrupt active. */
+#define CMR1_BufEnb 0x01 /* Enable the buffer(?). */
+#define CMR1_NextPkt 0x01 /* Enable the buffer(?). */
+
+#define CMR2_NULL 8
+#define CMR2_IRQOUT 9
+#define CMR2_RAMTEST 10
+#define CMR2_EEPROM 12 /* Set to page 1, for reading the EEPROM. */
+
+#define CMR2h_OFF 0 /* No accept mode. */
+#define CMR2h_Physical 1 /* Accept a physical address match only. */
+#define CMR2h_Normal 2 /* Accept physical and broadcast address. */
+#define CMR2h_PROMISC 3 /* Promiscuous mode. */
+
+/* An inline function used below: it differs from inb() by explicitly return an unsigned
+ char, saving a truncation. */
+extern inline unsigned char inbyte(unsigned short port)
+{
+ unsigned char _v;
+ __asm__ __volatile__ ("inb %w1,%b0" :"=a" (_v):"d" (port));
+ return _v;
+}
+
+/* Read register OFFSET.
+ This command should always be terminated with read_end(). */
+extern inline unsigned char read_nibble(short port, unsigned char offset)
+{
+ unsigned char retval;
+ outb(EOC+offset, port + PAR_DATA);
+ outb(RdAddr+offset, port + PAR_DATA);
+ inbyte(port + PAR_STATUS); /* Settling time delay */
+ retval = inbyte(port + PAR_STATUS);
+ outb(EOC+offset, port + PAR_DATA);
+
+ return retval;
+}
+
+/* Functions for bulk data read. The interrupt line is always disabled. */
+/* Get a byte using read mode 0, reading data from the control lines. */
+extern inline unsigned char read_byte_mode0(short ioaddr)
+{
+ unsigned char low_nib;
+
+ outb(Ctrl_LNibRead, ioaddr + PAR_CONTROL);
+ inbyte(ioaddr + PAR_STATUS);
+ low_nib = (inbyte(ioaddr + PAR_STATUS) >> 3) & 0x0f;
+ outb(Ctrl_HNibRead, ioaddr + PAR_CONTROL);
+ inbyte(ioaddr + PAR_STATUS); /* Settling time delay -- needed! */
+ inbyte(ioaddr + PAR_STATUS); /* Settling time delay -- needed! */
+ return low_nib | ((inbyte(ioaddr + PAR_STATUS) << 1) & 0xf0);
+}
+
+/* The same as read_byte_mode0(), but does multiple inb()s for stability. */
+extern inline unsigned char read_byte_mode2(short ioaddr)
+{
+ unsigned char low_nib;
+
+ outb(Ctrl_LNibRead, ioaddr + PAR_CONTROL);
+ inbyte(ioaddr + PAR_STATUS);
+ low_nib = (inbyte(ioaddr + PAR_STATUS) >> 3) & 0x0f;
+ outb(Ctrl_HNibRead, ioaddr + PAR_CONTROL);
+ inbyte(ioaddr + PAR_STATUS); /* Settling time delay -- needed! */
+ return low_nib | ((inbyte(ioaddr + PAR_STATUS) << 1) & 0xf0);
+}
+
+/* Read a byte through the data register. */
+extern inline unsigned char read_byte_mode4(short ioaddr)
+{
+ unsigned char low_nib;
+
+ outb(RdAddr | MAR, ioaddr + PAR_DATA);
+ low_nib = (inbyte(ioaddr + PAR_STATUS) >> 3) & 0x0f;
+ outb(RdAddr | HNib | MAR, ioaddr + PAR_DATA);
+ return low_nib | ((inbyte(ioaddr + PAR_STATUS) << 1) & 0xf0);
+}
+
+/* Read a byte through the data register, double reading to allow settling. */
+extern inline unsigned char read_byte_mode6(short ioaddr)
+{
+ unsigned char low_nib;
+
+ outb(RdAddr | MAR, ioaddr + PAR_DATA);
+ inbyte(ioaddr + PAR_STATUS);
+ low_nib = (inbyte(ioaddr + PAR_STATUS) >> 3) & 0x0f;
+ outb(RdAddr | HNib | MAR, ioaddr + PAR_DATA);
+ inbyte(ioaddr + PAR_STATUS);
+ return low_nib | ((inbyte(ioaddr + PAR_STATUS) << 1) & 0xf0);
+}
+
+extern inline void
+write_reg(short port, unsigned char reg, unsigned char value)
+{
+ unsigned char outval;
+ outb(EOC | reg, port + PAR_DATA);
+ outval = WrAddr | reg;
+ outb(outval, port + PAR_DATA);
+ outb(outval, port + PAR_DATA); /* Double write for PS/2. */
+
+ outval &= 0xf0;
+ outval |= value;
+ outb(outval, port + PAR_DATA);
+ outval &= 0x1f;
+ outb(outval, port + PAR_DATA);
+ outb(outval, port + PAR_DATA);
+
+ outb(EOC | outval, port + PAR_DATA);
+}
+
+extern inline void
+write_reg_high(short port, unsigned char reg, unsigned char value)
+{
+ unsigned char outval = EOC | HNib | reg;
+
+ outb(outval, port + PAR_DATA);
+ outval &= WrAddr | HNib | 0x0f;
+ outb(outval, port + PAR_DATA);
+ outb(outval, port + PAR_DATA); /* Double write for PS/2. */
+
+ outval = WrAddr | HNib | value;
+ outb(outval, port + PAR_DATA);
+ outval &= HNib | 0x0f; /* HNib | value */
+ outb(outval, port + PAR_DATA);
+ outb(outval, port + PAR_DATA);
+
+ outb(EOC | HNib | outval, port + PAR_DATA);
+}
+
+/* Write a byte out using nibble mode. The low nibble is written first. */
+extern inline void
+write_reg_byte(short port, unsigned char reg, unsigned char value)
+{
+ unsigned char outval;
+ outb(EOC | reg, port + PAR_DATA); /* Reset the address register. */
+ outval = WrAddr | reg;
+ outb(outval, port + PAR_DATA);
+ outb(outval, port + PAR_DATA); /* Double write for PS/2. */
+
+ outb((outval & 0xf0) | (value & 0x0f), port + PAR_DATA);
+ outb(value & 0x0f, port + PAR_DATA);
+ value >>= 4;
+ outb(value, port + PAR_DATA);
+ outb(0x10 | value, port + PAR_DATA);
+ outb(0x10 | value, port + PAR_DATA);
+
+ outb(EOC | value, port + PAR_DATA); /* Reset the address register. */
+}
+
+/*
+ * Bulk data writes to the packet buffer. The interrupt line remains enabled.
+ * The first, faster method uses only the dataport (data modes 0, 2 & 4).
+ * The second (backup) method uses data and control regs (modes 1, 3 & 5).
+ * It should only be needed when there is skew between the individual data
+ * lines.
+ */
+extern inline void write_byte_mode0(short ioaddr, unsigned char value)
+{
+ outb(value & 0x0f, ioaddr + PAR_DATA);
+ outb((value>>4) | 0x10, ioaddr + PAR_DATA);
+}
+
+extern inline void write_byte_mode1(short ioaddr, unsigned char value)
+{
+ outb(value & 0x0f, ioaddr + PAR_DATA);
+ outb(Ctrl_IRQEN | Ctrl_LNibWrite, ioaddr + PAR_CONTROL);
+ outb((value>>4) | 0x10, ioaddr + PAR_DATA);
+ outb(Ctrl_IRQEN | Ctrl_HNibWrite, ioaddr + PAR_CONTROL);
+}
+
+/* Write 16bit VALUE to the packet buffer: the same as above just doubled. */
+extern inline void write_word_mode0(short ioaddr, unsigned short value)
+{
+ outb(value & 0x0f, ioaddr + PAR_DATA);
+ value >>= 4;
+ outb((value & 0x0f) | 0x10, ioaddr + PAR_DATA);
+ value >>= 4;
+ outb(value & 0x0f, ioaddr + PAR_DATA);
+ value >>= 4;
+ outb((value & 0x0f) | 0x10, ioaddr + PAR_DATA);
+}
+
+/* EEPROM_Ctrl bits. */
+#define EE_SHIFT_CLK 0x04 /* EEPROM shift clock. */
+#define EE_CS 0x02 /* EEPROM chip select. */
+#define EE_CLK_HIGH 0x12
+#define EE_CLK_LOW 0x16
+#define EE_DATA_WRITE 0x01 /* EEPROM chip data in. */
+#define EE_DATA_READ 0x08 /* EEPROM chip data out. */
+
+/* Delay between EEPROM clock transitions. */
+#define eeprom_delay(ticks) \
+do { int _i = 40; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0)
+
+/* The EEPROM commands include the alway-set leading bit. */
+#define EE_WRITE_CMD(offset) (((5 << 6) + (offset)) << 17)
+#define EE_READ(offset) (((6 << 6) + (offset)) << 17)
+#define EE_ERASE(offset) (((7 << 6) + (offset)) << 17)
+#define EE_CMD_SIZE 27 /* The command+address+data size. */
diff --git a/i386/i386at/gpl/linux/net/de4x5.c b/i386/i386at/gpl/linux/net/de4x5.c
new file mode 100644
index 00000000..249887a6
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/de4x5.c
@@ -0,0 +1,2788 @@
+/* de4x5.c: A DIGITAL DE425/DE434/DE435/DE500 ethernet driver for Linux.
+
+ 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.
+
+ This driver is written for the Digital Equipment Corporation series
+ of EtherWORKS ethernet cards:
+
+ DE425 TP/COAX EISA
+ DE434 TP PCI
+ DE435 TP/COAX/AUI PCI
+ DE500 10/100 PCI Fasternet
+
+ The driver has been tested on a relatively busy network using the DE425,
+ DE434, DE435 and DE500 cards and benchmarked with 'ttcp': it transferred
+ 16M of data to a DECstation 5000/200 as follows:
+
+ TCP UDP
+ TX RX TX RX
+ DE425 1030k 997k 1170k 1128k
+ DE434 1063k 995k 1170k 1125k
+ DE435 1063k 995k 1170k 1125k
+ DE500 1063k 998k 1170k 1125k in 10Mb/s mode
+
+ All values are typical (in kBytes/sec) from a sample of 4 for each
+ measurement. Their error is +/-20k on a quiet (private) network and also
+ depend on what load the CPU has.
+
+ The author may be reached as davies@wanton.lkg.dec.com or Digital
+ Equipment Corporation, 550 King Street, Littleton MA 01460.
+
+ =========================================================================
+ 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.
+
+ Upto 15 EISA cards can be supported under this driver, limited primarily
+ by the available IRQ lines. I have checked different configurations of
+ multiple depca, EtherWORKS 3 cards and de4x5 cards and have not found a
+ problem yet (provided you have at least depca.c v0.38) ...
+
+ PCI support has been added to allow the driver to work with the DE434
+ and DE435 cards. The I/O accesses are a bit of a kludge due to the
+ differences in the EISA and PCI CSR address offsets from the base
+ address.
+
+ The ability to load this driver as a loadable module has been included
+ and used extensively during the driver development (to save those long
+ reboot sequences). Loadable module support under PCI has been achieved
+ by letting any I/O address less than 0x1000 be assigned as:
+
+ 0xghh
+
+ where g is the bus number (usually 0 until the BIOS's get fixed)
+ hh is the device number (max is 32 per bus).
+
+ Essentially, the I/O address and IRQ information are ignored and filled
+ in later by the PCI BIOS during the PCI probe. Note that the board
+ should be in the system at boot time so that its I/O address and IRQ are
+ allocated by the PCI BIOS automatically. The special case of device 0 on
+ bus 0 is not allowed as the probe will think you're autoprobing a
+ module.
+
+ To utilise this ability, you have to do 8 things:
+
+ 0) have a copy of the loadable modules code installed on your system.
+ 1) copy de4x5.c from the /linux/drivers/net directory to your favourite
+ temporary directory.
+ 2) edit the source code near line 2762 to reflect the I/O address and
+ IRQ you're using, or assign these when loading by:
+
+ insmod de4x5.o irq=x io=y
+
+ 3) compile de4x5.c, but include -DMODULE in the command line to ensure
+ that the correct bits are compiled (see end of source code).
+ 4) if you are wanting to add a new card, goto 5. Otherwise, recompile a
+ kernel with the de4x5 configuration turned off and reboot.
+ 5) insmod de4x5.o
+ 6) run the net startup bits for your new eth?? interface manually
+ (usually /etc/rc.inet[12] at boot time).
+ 7) enjoy!
+
+ Note that autoprobing is not allowed in loadable modules - the system is
+ already up and running and you're messing with interrupts.
+
+ To unload a module, turn off the associated interface
+ 'ifconfig eth?? down' then 'rmmod de4x5'.
+
+ Automedia detection is included so that in principal you can disconnect
+ from, e.g. TP, reconnect to BNC and things will still work (after a
+ pause whilst the driver figures out where its media went). My tests
+ using ping showed that it appears to work....
+
+ A compile time switch to allow Znyx recognition has been added. This
+ "feature" is in no way supported nor tested in this driver and the user
+ may use it at his/her sole discretion. I have had 2 conflicting reports
+ that my driver will or won't work with Znyx. Try Donald Becker's
+ 'tulip.c' if this driver doesn't work for you. I will not be supporting
+ Znyx cards since I have no information on them and can't test them in a
+ system.
+
+ TO DO:
+ ------
+
+
+ Revision History
+ ----------------
+
+ Version Date Description
+
+ 0.1 17-Nov-94 Initial writing. ALPHA code release.
+ 0.2 13-Jan-95 Added PCI support for DE435's.
+ 0.21 19-Jan-95 Added auto media detection.
+ 0.22 10-Feb-95 Fix interrupt handler call <chris@cosy.sbg.ac.at>.
+ Fix recognition bug reported by <bkm@star.rl.ac.uk>.
+ Add request/release_region code.
+ Add loadable modules support for PCI.
+ Clean up loadable modules support.
+ 0.23 28-Feb-95 Added DC21041 and DC21140 support.
+ Fix missed frame counter value and initialisation.
+ Fixed EISA probe.
+ 0.24 11-Apr-95 Change delay routine to use <linux/udelay>.
+ Change TX_BUFFS_AVAIL macro.
+ Change media autodetection to allow manual setting.
+ Completed DE500 (DC21140) support.
+ 0.241 18-Apr-95 Interim release without DE500 Autosense Algorithm.
+ 0.242 10-May-95 Minor changes
+ 0.30 12-Jun-95 Timer fix for DC21140
+ Portability changes.
+ Add ALPHA changes from <jestabro@ant.tay1.dec.com>.
+ Add DE500 semi automatic autosense.
+ Add Link Fail interrupt TP failure detection.
+ Add timer based link change detection.
+ Plugged a memory leak in de4x5_queue_pkt().
+ 0.31 13-Jun-95 Fixed PCI stuff for 1.3.1
+ 0.32 26-Jun-95 Added verify_area() calls in de4x5_ioctl() from
+ suggestion by <heiko@colossus.escape.de>
+
+ =========================================================================
+*/
+
+static const char *version = "de4x5.c:v0.32 6/26/95 davies@wanton.lkg.dec.com\n";
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/segment.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include <linux/time.h>
+#include <linux/types.h>
+#include <linux/unistd.h>
+
+#include "de4x5.h"
+
+#ifdef DE4X5_DEBUG
+static int de4x5_debug = DE4X5_DEBUG;
+#else
+static int de4x5_debug = 1;
+#endif
+
+#ifdef DE4X5_AUTOSENSE /* Should be done on a per adapter basis */
+static int de4x5_autosense = DE4X5_AUTOSENSE;
+#else
+static int de4x5_autosense = AUTO; /* Do auto media/mode sensing */
+#endif
+
+#ifdef DE4X5_FULL_DUPLEX /* Should be done on a per adapter basis */
+static s32 de4x5_full_duplex = 1;
+#else
+static s32 de4x5_full_duplex = 0;
+#endif
+
+#define DE4X5_NDA 0xffe0 /* No Device (I/O) Address */
+
+/*
+** Ethernet PROM defines
+*/
+#define PROBE_LENGTH 32
+#define ETH_PROM_SIG 0xAA5500FFUL
+
+/*
+** Ethernet Info
+*/
+#define PKT_BUF_SZ 1536 /* Buffer size for each Tx/Rx buffer */
+#define MAX_PKT_SZ 1514 /* Maximum ethernet packet length */
+#define MAX_DAT_SZ 1500 /* Maximum ethernet data length */
+#define MIN_DAT_SZ 1 /* Minimum ethernet data length */
+#define PKT_HDR_LEN 14 /* Addresses and data length info */
+#define FAKE_FRAME_LEN (MAX_PKT_SZ + 1)
+#define QUEUE_PKT_TIMEOUT (3*HZ) /* 3 second timeout */
+
+
+#define CRC_POLYNOMIAL_BE 0x04c11db7UL /* Ethernet CRC, big endian */
+#define CRC_POLYNOMIAL_LE 0xedb88320UL /* Ethernet CRC, little endian */
+
+/*
+** EISA bus defines
+*/
+#define DE4X5_EISA_IO_PORTS 0x0c00 /* I/O port base address, slot 0 */
+#define DE4X5_EISA_TOTAL_SIZE 0xfff /* I/O address extent */
+
+#define MAX_EISA_SLOTS 16
+#define EISA_SLOT_INC 0x1000
+
+#define DE4X5_SIGNATURE {"DE425",""}
+#define DE4X5_NAME_LENGTH 8
+
+/*
+** PCI Bus defines
+*/
+#define PCI_MAX_BUS_NUM 8
+#define DE4X5_PCI_TOTAL_SIZE 0x80 /* I/O address extent */
+#define DE4X5_CLASS_CODE 0x00020000 /* Network controller, Ethernet */
+
+/*
+** Memory Alignment. Each descriptor is 4 longwords long. To force a
+** particular alignment on the TX descriptor, adjust DESC_SKIP_LEN and
+** DESC_ALIGN. ALIGN aligns the start address of the private memory area
+** and hence the RX descriptor ring's first entry.
+*/
+#define ALIGN4 ((u_long)4 - 1) /* 1 longword align */
+#define ALIGN8 ((u_long)8 - 1) /* 2 longword align */
+#define ALIGN16 ((u_long)16 - 1) /* 4 longword align */
+#define ALIGN32 ((u_long)32 - 1) /* 8 longword align */
+#define ALIGN64 ((u_long)64 - 1) /* 16 longword align */
+#define ALIGN128 ((u_long)128 - 1) /* 32 longword align */
+
+#define ALIGN ALIGN32 /* Keep the DC21040 happy... */
+#define CACHE_ALIGN CAL_16LONG
+#define DESC_SKIP_LEN DSL_0 /* Must agree with DESC_ALIGN */
+/*#define DESC_ALIGN u32 dummy[4]; / * Must agree with DESC_SKIP_LEN */
+#define DESC_ALIGN
+
+#ifdef MACH
+#define IS_NOT_DEC
+#endif
+
+#ifndef IS_NOT_DEC /* See README.de4x5 for using this */
+static int is_not_dec = 0;
+#else
+static int is_not_dec = 1;
+#endif
+
+/*
+** DE4X5 IRQ ENABLE/DISABLE
+*/
+#define ENABLE_IRQs { \
+ imr |= lp->irq_en;\
+ outl(imr, DE4X5_IMR); /* Enable the IRQs */\
+}
+
+#define DISABLE_IRQs {\
+ imr = inl(DE4X5_IMR);\
+ imr &= ~lp->irq_en;\
+ outl(imr, DE4X5_IMR); /* Disable the IRQs */\
+}
+
+#define UNMASK_IRQs {\
+ imr |= lp->irq_mask;\
+ outl(imr, DE4X5_IMR); /* Unmask the IRQs */\
+}
+
+#define MASK_IRQs {\
+ imr = inl(DE4X5_IMR);\
+ imr &= ~lp->irq_mask;\
+ outl(imr, DE4X5_IMR); /* Mask the IRQs */\
+}
+
+/*
+** DE4X5 START/STOP
+*/
+#define START_DE4X5 {\
+ omr = inl(DE4X5_OMR);\
+ omr |= OMR_ST | OMR_SR;\
+ outl(omr, DE4X5_OMR); /* Enable the TX and/or RX */\
+}
+
+#define STOP_DE4X5 {\
+ omr = inl(DE4X5_OMR);\
+ omr &= ~(OMR_ST|OMR_SR);\
+ outl(omr, DE4X5_OMR); /* Disable the TX and/or RX */ \
+}
+
+/*
+** DE4X5 SIA RESET
+*/
+#define RESET_SIA outl(0, DE4X5_SICR); /* Reset SIA connectivity regs */
+
+/*
+** DE500 AUTOSENSE TIMER INTERVAL (MILLISECS)
+*/
+#define DE4X5_AUTOSENSE_MS 250
+
+/*
+** SROM Structure
+*/
+struct de4x5_srom {
+ char reserved[18];
+ char version;
+ char num_adapters;
+ char ieee_addr[6];
+ char info[100];
+ short chksum;
+};
+
+/*
+** DE4X5 Descriptors. Make sure that all the RX buffers are contiguous
+** and have sizes of both a power of 2 and a multiple of 4.
+** A size of 256 bytes for each buffer could be chosen because over 90% of
+** all packets in our network are <256 bytes long and 64 longword alignment
+** is possible. 1536 showed better 'ttcp' performance. Take your pick. 32 TX
+** descriptors are needed for machines with an ALPHA CPU.
+*/
+#define NUM_RX_DESC 8 /* Number of RX descriptors */
+#define NUM_TX_DESC 32 /* Number of TX descriptors */
+#define BUFF_ALLOC_RETRIES 10 /* In case of memory shortage */
+#define RX_BUFF_SZ 1536 /* Power of 2 for kmalloc and */
+ /* Multiple of 4 for DC21040 */
+struct de4x5_desc {
+ volatile s32 status;
+ u32 des1;
+ u32 buf;
+ u32 next;
+ DESC_ALIGN
+};
+
+/*
+** The DE4X5 private structure
+*/
+#define DE4X5_PKT_STAT_SZ 16
+#define DE4X5_PKT_BIN_SZ 128 /* Should be >=100 unless you
+ increase DE4X5_PKT_STAT_SZ */
+
+struct de4x5_private {
+ char adapter_name[80]; /* Adapter name */
+ struct de4x5_desc rx_ring[NUM_RX_DESC]; /* RX descriptor ring */
+ struct de4x5_desc tx_ring[NUM_TX_DESC]; /* TX descriptor ring */
+ struct sk_buff *skb[NUM_TX_DESC]; /* TX skb for freeing when sent */
+ int rx_new, rx_old; /* RX descriptor ring pointers */
+ int tx_new, tx_old; /* TX descriptor ring pointers */
+ char setup_frame[SETUP_FRAME_LEN]; /* Holds MCA and PA info. */
+ struct enet_statistics stats; /* Public stats */
+ struct {
+ u_int bins[DE4X5_PKT_STAT_SZ]; /* Private stats counters */
+ u_int unicast;
+ u_int multicast;
+ u_int broadcast;
+ u_int excessive_collisions;
+ u_int tx_underruns;
+ u_int excessive_underruns;
+ } pktStats;
+ char rxRingSize;
+ char txRingSize;
+ int bus; /* EISA or PCI */
+ int bus_num; /* PCI Bus number */
+ int chipset; /* DC21040, DC21041 or DC21140 */
+ s32 irq_mask; /* Interrupt Mask (Enable) bits */
+ s32 irq_en; /* Summary interrupt bits */
+ int media; /* Media (eg TP), mode (eg 100B)*/
+ int linkProb; /* Possible Link Problem */
+ int autosense; /* Allow/disallow autosensing */
+ int tx_enable; /* Enable descriptor polling */
+ int lostMedia; /* Possibly lost media */
+ int setup_f; /* Setup frame filtering type */
+};
+
+
+/*
+** The transmit ring full condition is described by the tx_old and tx_new
+** pointers by:
+** tx_old = tx_new Empty ring
+** tx_old = tx_new+1 Full ring
+** tx_old+txRingSize = tx_new+1 Full ring (wrapped condition)
+*/
+#define TX_BUFFS_AVAIL ((lp->tx_old<=lp->tx_new)?\
+ lp->tx_old+lp->txRingSize-lp->tx_new-1:\
+ lp->tx_old -lp->tx_new-1)
+
+/*
+** Public Functions
+*/
+static int de4x5_open(struct device *dev);
+static int de4x5_queue_pkt(struct sk_buff *skb, struct device *dev);
+static void de4x5_interrupt(int irq, struct pt_regs *regs);
+static int de4x5_close(struct device *dev);
+static struct enet_statistics *de4x5_get_stats(struct device *dev);
+static void set_multicast_list(struct device *dev);
+static int de4x5_ioctl(struct device *dev, struct ifreq *rq, int cmd);
+
+/*
+** Private functions
+*/
+static int de4x5_hw_init(struct device *dev, u_long iobase);
+static int de4x5_init(struct device *dev);
+static int de4x5_rx(struct device *dev);
+static int de4x5_tx(struct device *dev);
+static int de4x5_ast(struct device *dev);
+
+static int autoconf_media(struct device *dev);
+static void create_packet(struct device *dev, char *frame, int len);
+static void dce_us_delay(u32 usec);
+static void dce_ms_delay(u32 msec);
+static void load_packet(struct device *dev, char *buf, u32 flags, struct sk_buff *skb);
+static void dc21040_autoconf(struct device *dev);
+static void dc21041_autoconf(struct device *dev);
+static void dc21140_autoconf(struct device *dev);
+static int test_media(struct device *dev, s32 irqs, s32 irq_mask, s32 csr13, s32 csr14, s32 csr15, s32 msec);
+/*static int test_sym_link(struct device *dev, u32 msec);*/
+static int ping_media(struct device *dev);
+static void reset_init_sia(struct device *dev, s32 sicr, s32 strr, s32 sigr);
+static int test_ans(struct device *dev, s32 irqs, s32 irq_mask, s32 msec);
+static void load_ms_timer(struct device *dev, u32 msec);
+static int EISA_signature(char *name, s32 eisa_id);
+static int DevicePresent(u_long iobase);
+static short srom_rd(u_long address, u_char offset);
+static void srom_latch(u_int command, u_long address);
+static void srom_command(u_int command, u_long address);
+static void srom_address(u_int command, u_long address, u_char offset);
+static short srom_data(u_int command, u_long address);
+/*static void srom_busy(u_int command, u_long address);*/
+static void sendto_srom(u_int command, u_long addr);
+static int getfrom_srom(u_long addr);
+static void SetMulticastFilter(struct device *dev);
+static int get_hw_addr(struct device *dev);
+
+static void eisa_probe(struct device *dev, u_long iobase);
+static void pci_probe(struct device *dev, u_long iobase);
+static struct device *alloc_device(struct device *dev, u_long iobase);
+static char *build_setup_frame(struct device *dev, int mode);
+static void disable_ast(struct device *dev);
+static void enable_ast(struct device *dev, u32 time_out);
+static void kick_tx(struct device *dev);
+
+#ifdef MODULE
+int init_module(void);
+void cleanup_module(void);
+static int autoprobed = 1, loading_module = 1;
+# else
+static unsigned char de4x5_irq[] = {5,9,10,11};
+static int autoprobed = 0, loading_module = 0;
+#endif /* MODULE */
+
+static char name[DE4X5_NAME_LENGTH + 1];
+static int num_de4x5s = 0, num_eth = 0;
+
+/*
+** Kludge to get around the fact that the CSR addresses have different
+** offsets in the PCI and EISA boards. Also note that the ethernet address
+** PROM is accessed differently.
+*/
+static struct bus_type {
+ int bus;
+ int bus_num;
+ int device;
+ int chipset;
+ struct de4x5_srom srom;
+ int autosense;
+} bus;
+
+/*
+** Miscellaneous defines...
+*/
+#define RESET_DE4X5 {\
+ int i;\
+ i=inl(DE4X5_BMR);\
+ dce_ms_delay(1);\
+ outl(i | BMR_SWR, DE4X5_BMR);\
+ dce_ms_delay(1);\
+ outl(i, DE4X5_BMR);\
+ dce_ms_delay(1);\
+ for (i=0;i<5;i++) {inl(DE4X5_BMR); dce_ms_delay(1);}\
+ dce_ms_delay(1);\
+}
+
+
+
+int de4x5_probe(struct device *dev)
+{
+ int tmp = num_de4x5s, status = -ENODEV;
+ u_long iobase = dev->base_addr;
+
+ if ((iobase == 0) && loading_module){
+ printk("Autoprobing is not supported when loading a module based driver.\n");
+ status = -EIO;
+ } else {
+ eisa_probe(dev, iobase);
+ pci_probe(dev, iobase);
+
+ if ((tmp == num_de4x5s) && (iobase != 0) && loading_module) {
+ printk("%s: de4x5_probe() cannot find device at 0x%04lx.\n", dev->name,
+ iobase);
+ }
+
+ /*
+ ** Walk the device list to check that at least one device
+ ** initialised OK
+ */
+ for (; (dev->priv == NULL) && (dev->next != NULL); dev = dev->next);
+
+ if (dev->priv) status = 0;
+ if (iobase == 0) autoprobed = 1;
+ }
+
+ return status;
+}
+
+static int
+de4x5_hw_init(struct device *dev, u_long iobase)
+{
+ struct bus_type *lp = &bus;
+ int tmpbus, tmpchs, i, j, status=0;
+ char *tmp;
+
+ /* Ensure we're not sleeping */
+ if (lp->chipset == DC21041) {
+ outl(0, PCI_CFDA);
+ dce_ms_delay(10);
+ }
+
+ RESET_DE4X5;
+
+ if ((inl(DE4X5_STS) & (STS_TS | STS_RS)) == 0) {
+ /*
+ ** Now find out what kind of DC21040/DC21041/DC21140 board we have.
+ */
+ if (lp->bus == PCI) {
+ if (!is_not_dec) {
+ if ((lp->chipset == DC21040) || (lp->chipset == DC21041)) {
+ strcpy(name, "DE435");
+ } else if (lp->chipset == DC21140) {
+ strcpy(name, "DE500"); /* Must read the SROM here! */
+ }
+ } else {
+ strcpy(name, "UNKNOWN");
+ }
+ } else {
+ EISA_signature(name, EISA_ID0);
+ }
+
+ if (*name != '\0') { /* found a board signature */
+ dev->base_addr = iobase;
+ if (lp->bus == EISA) {
+ printk("%s: %s at %04lx (EISA slot %ld)",
+ dev->name, name, iobase, ((iobase>>12)&0x0f));
+ } else { /* PCI port address */
+ printk("%s: %s at %04lx (PCI bus %d, device %d)", dev->name, name,
+ iobase, lp->bus_num, lp->device);
+ }
+
+ printk(", h/w address ");
+ status = get_hw_addr(dev);
+ for (i = 0; i < ETH_ALEN - 1; i++) { /* get the ethernet addr. */
+ printk("%2.2x:", dev->dev_addr[i]);
+ }
+ printk("%2.2x,\n", dev->dev_addr[i]);
+
+ tmpbus = lp->bus;
+ tmpchs = lp->chipset;
+
+ if (status == 0) {
+ struct de4x5_private *lp;
+
+ /*
+ ** Reserve a section of kernel memory for the adapter
+ ** private area and the TX/RX descriptor rings.
+ */
+ dev->priv = (void *) kmalloc(sizeof(struct de4x5_private) + ALIGN,
+ GFP_KERNEL);
+ if (dev->priv == NULL)
+ return -ENOMEM;
+ /*
+ ** Align to a longword boundary
+ */
+ dev->priv = (void *)(((u_long)dev->priv + ALIGN) & ~ALIGN);
+ lp = (struct de4x5_private *)dev->priv;
+ memset(dev->priv, 0, sizeof(struct de4x5_private));
+ lp->bus = tmpbus;
+ lp->chipset = tmpchs;
+
+ /*
+ ** Choose autosensing
+ */
+ if (de4x5_autosense & AUTO) {
+ lp->autosense = AUTO;
+ } else {
+ if (lp->chipset != DC21140) {
+ if ((lp->chipset == DC21040) && (de4x5_autosense & TP_NW)) {
+ de4x5_autosense = TP;
+ }
+ if ((lp->chipset == DC21041) && (de4x5_autosense & BNC_AUI)) {
+ de4x5_autosense = BNC;
+ }
+ lp->autosense = de4x5_autosense & 0x001f;
+ } else {
+ lp->autosense = de4x5_autosense & 0x00c0;
+ }
+ }
+
+ sprintf(lp->adapter_name,"%s (%s)", name, dev->name);
+ request_region(iobase, (lp->bus == PCI ? DE4X5_PCI_TOTAL_SIZE :
+ DE4X5_EISA_TOTAL_SIZE),
+ lp->adapter_name);
+
+ /*
+ ** Allocate contiguous receive buffers, long word aligned.
+ ** This could be a possible memory leak if the private area
+ ** is ever hosed.
+ */
+ for (tmp=NULL, j=0; (j<BUFF_ALLOC_RETRIES) && (tmp==NULL); j++) {
+ if ((tmp = (void *)kmalloc(RX_BUFF_SZ * NUM_RX_DESC + ALIGN,
+ GFP_KERNEL)) != NULL) {
+ tmp = (char *)(((u_long) tmp + ALIGN) & ~ALIGN);
+ for (i=0; i<NUM_RX_DESC; i++) {
+ lp->rx_ring[i].status = 0;
+ lp->rx_ring[i].des1 = RX_BUFF_SZ;
+ lp->rx_ring[i].buf = virt_to_bus(tmp + i * RX_BUFF_SZ);
+ lp->rx_ring[i].next = (u32)NULL;
+ }
+ barrier();
+ }
+ }
+
+ if (tmp != NULL) {
+ lp->rxRingSize = NUM_RX_DESC;
+ lp->txRingSize = NUM_TX_DESC;
+
+ /* Write the end of list marker to the descriptor lists */
+ lp->rx_ring[lp->rxRingSize - 1].des1 |= RD_RER;
+ lp->tx_ring[lp->txRingSize - 1].des1 |= TD_TER;
+
+ /* Tell the adapter where the TX/RX rings are located. */
+ outl(virt_to_bus(lp->rx_ring), DE4X5_RRBA);
+ outl(virt_to_bus(lp->tx_ring), DE4X5_TRBA);
+
+ /* Initialise the IRQ mask and Enable/Disable */
+ lp->irq_mask = IMR_RIM | IMR_TIM | IMR_TUM ;
+ lp->irq_en = IMR_NIM | IMR_AIM;
+
+ lp->tx_enable = TRUE;
+
+ if (dev->irq < 2) {
+#ifndef MODULE
+ unsigned char irqnum;
+ s32 omr;
+ autoirq_setup(0);
+
+ omr = inl(DE4X5_OMR);
+ outl(IMR_AIM|IMR_RUM, DE4X5_IMR); /* Unmask RUM interrupt */
+ outl(OMR_SR | omr, DE4X5_OMR); /* Start RX w/no descriptors */
+
+ irqnum = autoirq_report(1);
+ if (!irqnum) {
+ printk(" and failed to detect IRQ line.\n");
+ status = -ENXIO;
+ } else {
+ for (dev->irq=0,i=0; (i<sizeof(de4x5_irq)) && (!dev->irq); i++) {
+ if (irqnum == de4x5_irq[i]) {
+ dev->irq = irqnum;
+ printk(" and uses IRQ%d.\n", dev->irq);
+ }
+ }
+
+ if (!dev->irq) {
+ printk(" but incorrect IRQ line detected.\n");
+ status = -ENXIO;
+ }
+ }
+
+ outl(0, DE4X5_IMR); /* Re-mask RUM interrupt */
+
+#endif /* MODULE */
+ } else {
+ printk(" and requires IRQ%d (not probed).\n", dev->irq);
+ }
+ } else {
+ printk("%s: Kernel could not allocate RX buffer memory.\n",
+ dev->name);
+ status = -ENXIO;
+ }
+ if (status) release_region(iobase, (lp->bus == PCI ?
+ DE4X5_PCI_TOTAL_SIZE :
+ DE4X5_EISA_TOTAL_SIZE));
+ } else {
+ printk(" which has an Ethernet PROM CRC error.\n");
+ status = -ENXIO;
+ }
+ } else {
+ status = -ENXIO;
+ }
+ } else {
+ status = -ENXIO;
+ }
+
+ if (!status) {
+ if (de4x5_debug > 0) {
+ printk(version);
+ }
+
+ /* The DE4X5-specific entries in the device structure. */
+ dev->open = &de4x5_open;
+ dev->hard_start_xmit = &de4x5_queue_pkt;
+ dev->stop = &de4x5_close;
+ dev->get_stats = &de4x5_get_stats;
+ dev->set_multicast_list = &set_multicast_list;
+ dev->do_ioctl = &de4x5_ioctl;
+
+ dev->mem_start = 0;
+
+ /* Fill in the generic field of the device structure. */
+ ether_setup(dev);
+
+ /* Let the adapter sleep to save power */
+ if (lp->chipset == DC21041) {
+ outl(0, DE4X5_SICR);
+ outl(CFDA_PSM, PCI_CFDA);
+ }
+ } else { /* Incorrectly initialised hardware */
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ if (lp) {
+ kfree_s(bus_to_virt(lp->rx_ring[0].buf),
+ RX_BUFF_SZ * NUM_RX_DESC + ALIGN);
+ }
+ if (dev->priv) {
+ kfree_s(dev->priv, sizeof(struct de4x5_private) + ALIGN);
+ dev->priv = NULL;
+ }
+ }
+
+ return status;
+}
+
+
+static int
+de4x5_open(struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+ int i, status = 0;
+ s32 imr, omr, sts;
+
+ /*
+ ** Wake up the adapter
+ */
+ if (lp->chipset == DC21041) {
+ outl(0, PCI_CFDA);
+ dce_ms_delay(10);
+ }
+
+ if (request_irq(dev->irq, (void *)de4x5_interrupt, 0, lp->adapter_name)) {
+ printk("de4x5_open(): Requested IRQ%d is busy\n",dev->irq);
+ status = -EAGAIN;
+ } else {
+
+ irq2dev_map[dev->irq] = dev;
+ /*
+ ** Re-initialize the DE4X5...
+ */
+ status = de4x5_init(dev);
+
+ if (de4x5_debug > 1){
+ printk("%s: de4x5 open with irq %d\n",dev->name,dev->irq);
+ printk("\tphysical address: ");
+ for (i=0;i<6;i++){
+ printk("%2.2x:",(short)dev->dev_addr[i]);
+ }
+ printk("\n");
+ printk("Descriptor head addresses:\n");
+ printk("\t0x%8.8lx 0x%8.8lx\n",(u_long)lp->rx_ring,(u_long)lp->tx_ring);
+ printk("Descriptor addresses:\nRX: ");
+ for (i=0;i<lp->rxRingSize-1;i++){
+ if (i < 3) {
+ printk("0x%8.8lx ",(u_long)&lp->rx_ring[i].status);
+ }
+ }
+ printk("...0x%8.8lx\n",(u_long)&lp->rx_ring[i].status);
+ printk("TX: ");
+ for (i=0;i<lp->txRingSize-1;i++){
+ if (i < 3) {
+ printk("0x%8.8lx ", (u_long)&lp->tx_ring[i].status);
+ }
+ }
+ printk("...0x%8.8lx\n", (u_long)&lp->tx_ring[i].status);
+ printk("Descriptor buffers:\nRX: ");
+ for (i=0;i<lp->rxRingSize-1;i++){
+ if (i < 3) {
+ printk("0x%8.8x ",lp->rx_ring[i].buf);
+ }
+ }
+ printk("...0x%8.8x\n",lp->rx_ring[i].buf);
+ printk("TX: ");
+ for (i=0;i<lp->txRingSize-1;i++){
+ if (i < 3) {
+ printk("0x%8.8x ", lp->tx_ring[i].buf);
+ }
+ }
+ printk("...0x%8.8x\n", lp->tx_ring[i].buf);
+ printk("Ring size: \nRX: %d\nTX: %d\n",
+ (short)lp->rxRingSize,
+ (short)lp->txRingSize);
+ printk("\tstatus: %d\n", status);
+ }
+
+ if (!status) {
+ dev->tbusy = 0;
+ dev->start = 1;
+ dev->interrupt = UNMASK_INTERRUPTS;
+ dev->trans_start = jiffies;
+
+ START_DE4X5;
+
+ /* Unmask and enable DE4X5 board interrupts */
+ imr = 0;
+ UNMASK_IRQs;
+
+ /* Reset any pending (stale) interrupts */
+ sts = inl(DE4X5_STS);
+ outl(sts, DE4X5_STS);
+
+ ENABLE_IRQs;
+ }
+ if (de4x5_debug > 1) {
+ printk("\tsts: 0x%08x\n", inl(DE4X5_STS));
+ printk("\tbmr: 0x%08x\n", inl(DE4X5_BMR));
+ printk("\timr: 0x%08x\n", inl(DE4X5_IMR));
+ printk("\tomr: 0x%08x\n", inl(DE4X5_OMR));
+ printk("\tsisr: 0x%08x\n", inl(DE4X5_SISR));
+ printk("\tsicr: 0x%08x\n", inl(DE4X5_SICR));
+ printk("\tstrr: 0x%08x\n", inl(DE4X5_STRR));
+ printk("\tsigr: 0x%08x\n", inl(DE4X5_SIGR));
+ }
+ }
+
+ MOD_INC_USE_COUNT;
+
+ return status;
+}
+
+/*
+** Initialize the DE4X5 operating conditions. NB: a chip problem with the
+** DC21140 requires using perfect filtering mode for that chip. Since I can't
+** see why I'd want > 14 multicast addresses, I may change all chips to use
+** the perfect filtering mode. Keep the DMA burst length at 8: there seems
+** to be data corruption problems if it is larger (UDP errors seen from a
+** ttcp source).
+*/
+static int
+de4x5_init(struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+ int i, j, status = 0;
+ s32 bmr, omr;
+
+ /* Lock out other processes whilst setting up the hardware */
+ set_bit(0, (void *)&dev->tbusy);
+
+ RESET_DE4X5;
+
+ bmr = inl(DE4X5_BMR);
+ bmr |= PBL_8 | DESC_SKIP_LEN | CACHE_ALIGN;
+ outl(bmr, DE4X5_BMR);
+
+ if (lp->chipset != DC21140) {
+ omr = TR_96;
+ lp->setup_f = HASH_PERF;
+ } else {
+ omr = OMR_SDP | OMR_SF;
+ lp->setup_f = PERFECT;
+ }
+ 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 = R_OWN;
+ }
+
+ for (i = 0; i < lp->txRingSize; i++) {
+ lp->tx_ring[i].status = 0;
+ }
+
+ barrier();
+
+ /* Build the setup frame depending on filtering mode */
+ SetMulticastFilter(dev);
+
+ if (lp->chipset != DC21140) {
+ load_packet(dev, lp->setup_frame, HASH_F|TD_SET|SETUP_FRAME_LEN, NULL);
+ } else {
+ load_packet(dev, lp->setup_frame, PERFECT_F|TD_SET|SETUP_FRAME_LEN, NULL);
+ }
+ outl(omr|OMR_ST, DE4X5_OMR);
+
+ /* Poll for completion of setup frame (interrupts are disabled for now) */
+ for (j=0, i=jiffies;(i<=jiffies+HZ/100) && (j==0);) {
+ if (lp->tx_ring[lp->tx_new].status >= 0) j=1;
+ }
+ outl(omr, DE4X5_OMR); /* Stop everything! */
+
+ if (j == 0) {
+ printk("%s: Setup frame timed out, status %08x\n", dev->name,
+ inl(DE4X5_STS));
+ status = -EIO;
+ }
+
+ lp->tx_new = (++lp->tx_new) % lp->txRingSize;
+ lp->tx_old = lp->tx_new;
+
+ /* Autoconfigure the connected port */
+ if (autoconf_media(dev) == 0) {
+ status = -EIO;
+ }
+
+ return 0;
+}
+
+/*
+** Writes a socket buffer address to the next available transmit descriptor
+*/
+static int
+de4x5_queue_pkt(struct sk_buff *skb, struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+ int i, status = 0;
+ s32 imr, omr, sts;
+
+ /*
+ ** Clean out the TX ring asynchronously to interrupts - sometimes the
+ ** interrupts are lost by delayed descriptor status updates relative to
+ ** the irq assertion, especially with a busy PCI bus.
+ */
+ if (set_bit(0, (void*)&dev->tbusy) == 0) {
+ cli();
+ de4x5_tx(dev);
+ dev->tbusy = 0;
+ sti();
+ }
+
+ /*
+ ** Transmitter timeout, possibly serious problems.
+ ** The 'lostMedia' threshold accounts for transient errors that
+ ** were noticed when switching media.
+ */
+ if (dev->tbusy || (lp->lostMedia > LOST_MEDIA_THRESHOLD)) {
+ u_long tickssofar = jiffies - dev->trans_start;
+ if ((tickssofar < QUEUE_PKT_TIMEOUT) &&
+ (lp->lostMedia <= LOST_MEDIA_THRESHOLD)) {
+ status = -1;
+ } else {
+ if (de4x5_debug >= 1) {
+ printk("%s: transmit timed out, status %08x, tbusy:%ld, lostMedia:%d tickssofar:%ld, resetting.\n",dev->name, inl(DE4X5_STS), dev->tbusy, lp->lostMedia, tickssofar);
+ }
+
+ /* Stop and reset the TX and RX... */
+ STOP_DE4X5;
+
+ /* Re-queue any skb's. */
+ for (i=lp->tx_old; i!=lp->tx_new; i=(++i)%lp->txRingSize) {
+ if (lp->skb[i] != NULL) {
+ if (lp->skb[i]->len != FAKE_FRAME_LEN) {
+ if (lp->tx_ring[i].status == T_OWN) {
+ dev_queue_xmit(lp->skb[i], dev, SOPRI_NORMAL);
+ } else { /* already sent */
+ dev_kfree_skb(lp->skb[i], FREE_WRITE);
+ }
+ } else {
+ dev_kfree_skb(lp->skb[i], FREE_WRITE);
+ }
+ lp->skb[i] = NULL;
+ }
+ }
+ if (skb->len != FAKE_FRAME_LEN) {
+ dev_queue_xmit(skb, dev, SOPRI_NORMAL);
+ } else {
+ dev_kfree_skb(skb, FREE_WRITE);
+ }
+
+ /* Initialise the hardware */
+ status = de4x5_init(dev);
+
+ /* Unmask DE4X5 board interrupts */
+ if (!status) {
+ /* Start here to clean stale interrupts later */
+ dev->interrupt = UNMASK_INTERRUPTS;
+ dev->start = 1;
+ dev->tbusy = 0;
+ dev->trans_start = jiffies;
+
+ START_DE4X5;
+
+ /* Unmask DE4X5 board interrupts */
+ imr = 0;
+ UNMASK_IRQs;
+
+ /* Clear any pending (stale) interrupts */
+ sts = inl(DE4X5_STS);
+ outl(sts, DE4X5_STS);
+
+ ENABLE_IRQs;
+ } else {
+ printk("%s: hardware initialisation failure, status %08x.\n",
+ dev->name, inl(DE4X5_STS));
+ }
+ }
+ } else if (skb == NULL) {
+ dev_tint(dev);
+ } else if (skb->len == FAKE_FRAME_LEN) { /* Don't TX a fake frame! */
+ dev_kfree_skb(skb, FREE_WRITE);
+ } else if (skb->len > 0) {
+ /* Enforce 1 process per h/w access */
+ if (set_bit(0, (void*)&dev->tbusy) != 0) {
+ printk("%s: Transmitter access conflict.\n", dev->name);
+ status = -1; /* Re-queue packet */
+ } else {
+ cli();
+ if (TX_BUFFS_AVAIL) { /* Fill in a Tx ring entry */
+ load_packet(dev, skb->data, TD_IC | TD_LS | TD_FS | skb->len, skb);
+ if (lp->tx_enable) {
+ outl(POLL_DEMAND, DE4X5_TPD); /* Start the TX */
+ }
+
+ lp->tx_new = (++lp->tx_new) % lp->txRingSize; /* Ensure a wrap */
+ dev->trans_start = jiffies;
+
+ if (TX_BUFFS_AVAIL) {
+ dev->tbusy = 0; /* Another pkt may be queued */
+ }
+ } else { /* Ring full - re-queue */
+ status = -1;
+ }
+ sti();
+ }
+ }
+
+ return status;
+}
+
+/*
+** The DE4X5 interrupt handler.
+**
+** I/O Read/Writes through intermediate PCI bridges are never 'posted',
+** so that the asserted interrupt always has some real data to work with -
+** if these I/O accesses are ever changed to memory accesses, ensure the
+** STS write is read immediately to complete the transaction if the adapter
+** is not on bus 0. Lost interrupts can still occur when the PCI bus load
+** is high and descriptor status bits cannot be set before the associated
+** interrupt is asserted and this routine entered.
+*/
+static void
+de4x5_interrupt(int irq, struct pt_regs *regs)
+{
+ struct device *dev = (struct device *)(irq2dev_map[irq]);
+ struct de4x5_private *lp;
+ s32 imr, omr, sts;
+ u_long iobase;
+
+ if (dev == NULL) {
+ printk ("de4x5_interrupt(): irq %d for unknown device.\n", irq);
+ } else {
+ lp = (struct de4x5_private *)dev->priv;
+ iobase = dev->base_addr;
+
+ if (dev->interrupt)
+ printk("%s: Re-entering the interrupt handler.\n", dev->name);
+
+ DISABLE_IRQs; /* Ensure non re-entrancy */
+ dev->interrupt = MASK_INTERRUPTS;
+
+ while ((sts = inl(DE4X5_STS)) & lp->irq_mask) { /* Read IRQ status */
+ outl(sts, DE4X5_STS); /* Reset the board interrupts */
+
+ if (sts & (STS_RI | STS_RU)) /* Rx interrupt (packet[s] arrived) */
+ de4x5_rx(dev);
+
+ if (sts & (STS_TI | STS_TU)) /* Tx interrupt (packet sent) */
+ de4x5_tx(dev);
+
+ if (sts & STS_TM) /* Autosense tick */
+ de4x5_ast(dev);
+
+ if (sts & STS_LNF) { /* TP Link has failed */
+ lp->lostMedia = LOST_MEDIA_THRESHOLD + 1;
+ lp->irq_mask &= ~IMR_LFM;
+ kick_tx(dev);
+ }
+
+ if (sts & STS_SE) { /* Bus Error */
+ STOP_DE4X5;
+ printk("%s: Fatal bus error occured, sts=%#8x, device stopped.\n",
+ dev->name, sts);
+ }
+ }
+
+ if (TX_BUFFS_AVAIL && dev->tbusy) {/* Any resources available? */
+ dev->tbusy = 0; /* Clear TX busy flag */
+ mark_bh(NET_BH);
+ }
+
+ dev->interrupt = UNMASK_INTERRUPTS;
+ ENABLE_IRQs;
+ }
+
+ return;
+}
+
+static int
+de4x5_rx(struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ int i, entry;
+ s32 status;
+ char *buf;
+
+ for (entry = lp->rx_new; lp->rx_ring[entry].status >= 0;entry = lp->rx_new) {
+ status = lp->rx_ring[entry].status;
+
+ if (status & RD_FS) { /* Remember the start of frame */
+ lp->rx_old = entry;
+ }
+
+ if (status & RD_LS) { /* Valid frame status */
+ if (status & RD_ES) { /* There was an error. */
+ lp->stats.rx_errors++; /* Update the error stats. */
+ if (status & (RD_RF | RD_TL)) lp->stats.rx_frame_errors++;
+ if (status & RD_CE) lp->stats.rx_crc_errors++;
+ if (status & RD_OF) lp->stats.rx_fifo_errors++;
+ } else { /* A valid frame received */
+ struct sk_buff *skb;
+ short pkt_len = (short)(lp->rx_ring[entry].status >> 16) - 4;
+
+ if ((skb = dev_alloc_skb(pkt_len+2)) != NULL) {
+ skb->dev = dev;
+
+ skb_reserve(skb,2); /* Align */
+ if (entry < lp->rx_old) { /* Wrapped buffer */
+ short len = (lp->rxRingSize - lp->rx_old) * RX_BUFF_SZ;
+ memcpy(skb_put(skb,len), bus_to_virt(lp->rx_ring[lp->rx_old].buf), len);
+ memcpy(skb_put(skb,pkt_len-len), bus_to_virt(lp->rx_ring[0].buf), pkt_len - len);
+ } else { /* Linear buffer */
+ memcpy(skb_put(skb,pkt_len), bus_to_virt(lp->rx_ring[lp->rx_old].buf), pkt_len);
+ }
+
+ /* Push up the protocol stack */
+ skb->protocol=eth_type_trans(skb,dev);
+ netif_rx(skb);
+
+ /* Update stats */
+ lp->stats.rx_packets++;
+ for (i=1; i<DE4X5_PKT_STAT_SZ-1; i++) {
+ if (pkt_len < (i*DE4X5_PKT_BIN_SZ)) {
+ lp->pktStats.bins[i]++;
+ i = DE4X5_PKT_STAT_SZ;
+ }
+ }
+ buf = skb->data; /* Look at the dest addr */
+ if (buf[0] & 0x01) { /* Multicast/Broadcast */
+ if ((*(s32 *)&buf[0] == -1) && (*(s16 *)&buf[4] == -1)) {
+ lp->pktStats.broadcast++;
+ } else {
+ lp->pktStats.multicast++;
+ }
+ } else if ((*(s32 *)&buf[0] == *(s32 *)&dev->dev_addr[0]) &&
+ (*(s16 *)&buf[4] == *(s16 *)&dev->dev_addr[4])) {
+ lp->pktStats.unicast++;
+ }
+
+ lp->pktStats.bins[0]++; /* Duplicates stats.rx_packets */
+ if (lp->pktStats.bins[0] == 0) { /* Reset counters */
+ memset((char *)&lp->pktStats, 0, sizeof(lp->pktStats));
+ }
+ } else {
+ printk("%s: Insufficient memory; nuking packet.\n", dev->name);
+ lp->stats.rx_dropped++; /* Really, deferred. */
+ break;
+ }
+ }
+
+ /* Change buffer ownership for this last frame, back to the adapter */
+ for (; lp->rx_old!=entry; lp->rx_old=(++lp->rx_old)%lp->rxRingSize) {
+ lp->rx_ring[lp->rx_old].status = R_OWN;
+ barrier();
+ }
+ lp->rx_ring[entry].status = R_OWN;
+ barrier();
+ }
+
+ /*
+ ** Update entry information
+ */
+ lp->rx_new = (++lp->rx_new) % lp->rxRingSize;
+ }
+
+ return 0;
+}
+
+/*
+** Buffer sent - check for TX buffer errors.
+*/
+static int
+de4x5_tx(struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+ int entry;
+ s32 status;
+
+ for (entry = lp->tx_old; entry != lp->tx_new; entry = lp->tx_old) {
+ status = lp->tx_ring[entry].status;
+ if (status < 0) { /* Buffer not sent yet */
+ break;
+ } else if (status & TD_ES) { /* An error happened */
+ lp->stats.tx_errors++;
+ if (status & TD_NC) lp->stats.tx_carrier_errors++;
+ if (status & TD_LC) lp->stats.tx_window_errors++;
+ if (status & TD_UF) lp->stats.tx_fifo_errors++;
+ if (status & TD_LC) lp->stats.collisions++;
+ if (status & TD_EC) lp->pktStats.excessive_collisions++;
+ if (status & TD_DE) lp->stats.tx_aborted_errors++;
+
+ if ((status != 0x7fffffff) && /* Not setup frame */
+ (status & (TD_LO | TD_NC | TD_EC | TD_LF))) {
+ lp->lostMedia++;
+ if (lp->lostMedia > LOST_MEDIA_THRESHOLD) { /* Trip autosense */
+ kick_tx(dev);
+ }
+ } else {
+ outl(POLL_DEMAND, DE4X5_TPD); /* Restart a stalled TX */
+ }
+ } else { /* Packet sent */
+ lp->stats.tx_packets++;
+ lp->lostMedia = 0; /* Remove transient problem */
+ }
+ /* Free the buffer if it's not a setup frame. */
+ if (lp->skb[entry] != NULL) {
+ dev_kfree_skb(lp->skb[entry], FREE_WRITE);
+ lp->skb[entry] = NULL;
+ }
+
+ /* Update all the pointers */
+ lp->tx_old = (++lp->tx_old) % lp->txRingSize;
+ }
+
+ return 0;
+}
+
+static int
+de4x5_ast(struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+ s32 gep;
+
+ disable_ast(dev);
+
+ if (lp->chipset == DC21140) {
+ gep = inl(DE4X5_GEP);
+ if (((lp->media == _100Mb) && (gep & GEP_SLNK)) ||
+ ((lp->media == _10Mb) && (gep & GEP_LNP)) ||
+ ((lp->media == _10Mb) && !(gep & GEP_SLNK)) ||
+ (lp->media == NC)) {
+ if (lp->linkProb || ((lp->media == NC) && (!(gep & GEP_LNP)))) {
+ lp->lostMedia = LOST_MEDIA_THRESHOLD + 1;
+ lp->linkProb = 0;
+ kick_tx(dev);
+ } else {
+ switch(lp->media) {
+ case NC:
+ lp->linkProb = 0;
+ enable_ast(dev, DE4X5_AUTOSENSE_MS);
+ break;
+
+ case _10Mb:
+ lp->linkProb = 1; /* Flag a potential problem */
+ enable_ast(dev, 1500);
+ break;
+
+ case _100Mb:
+ lp->linkProb = 1; /* Flag a potential problem */
+ enable_ast(dev, 4000);
+ break;
+ }
+ }
+ } else {
+ lp->linkProb = 0; /* Link OK */
+ enable_ast(dev, DE4X5_AUTOSENSE_MS);
+ }
+ }
+
+ return 0;
+}
+
+static int
+de4x5_close(struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+ s32 imr, omr;
+
+ dev->start = 0;
+ dev->tbusy = 1;
+
+ if (de4x5_debug > 1) {
+ printk("%s: Shutting down ethercard, status was %8.8x.\n",
+ dev->name, inl(DE4X5_STS));
+ }
+
+ /*
+ ** We stop the DE4X5 here... mask interrupts and stop TX & RX
+ */
+ DISABLE_IRQs;
+
+ STOP_DE4X5;
+
+ /*
+ ** Free the associated irq
+ */
+ free_irq(dev->irq);
+ irq2dev_map[dev->irq] = 0;
+
+ MOD_DEC_USE_COUNT;
+
+ /* Put the adapter to sleep to save power */
+ if (lp->chipset == DC21041) {
+ outl(0, DE4X5_SICR);
+ outl(CFDA_PSM, PCI_CFDA);
+ }
+
+ return 0;
+}
+
+static struct enet_statistics *
+de4x5_get_stats(struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+
+ lp->stats.rx_missed_errors = (int) (inl(DE4X5_MFC) & (MFC_OVFL | MFC_CNTR));
+
+ return &lp->stats;
+}
+
+static void load_packet(struct device *dev, char *buf, u32 flags, struct sk_buff *skb)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+
+ lp->tx_ring[lp->tx_new].buf = virt_to_bus(buf);
+ lp->tx_ring[lp->tx_new].des1 &= TD_TER;
+ lp->tx_ring[lp->tx_new].des1 |= flags;
+ lp->skb[lp->tx_new] = skb;
+ barrier();
+ lp->tx_ring[lp->tx_new].status = T_OWN;
+ barrier();
+
+ return;
+}
+/*
+** Set or clear the multicast filter for this adaptor.
+** num_addrs == -1 Promiscuous mode, receive all packets - now supported.
+** Can also use the ioctls.
+** num_addrs == 0 Normal mode, clear multicast list
+** num_addrs > 0 Multicast mode, receive normal and MC packets, and do
+** best-effort filtering.
+** num_addrs == HASH_TABLE_LEN
+** Set all multicast bits (pass all multicasts).
+*/
+static void
+set_multicast_list(struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+
+ /* First, double check that the adapter is open */
+ if (irq2dev_map[dev->irq] != NULL) {
+ if (dev->flags & IFF_PROMISC) { /* set promiscuous mode */
+ u32 omr;
+ omr = inl(DE4X5_OMR);
+ omr |= OMR_PR;
+ outl(omr, DE4X5_OMR);
+ } else {
+ SetMulticastFilter(dev);
+ if (lp->setup_f == HASH_PERF) {
+ load_packet(dev, lp->setup_frame, TD_IC | HASH_F | TD_SET |
+ SETUP_FRAME_LEN, NULL);
+ } else {
+ load_packet(dev, lp->setup_frame, TD_IC | PERFECT_F | TD_SET |
+ SETUP_FRAME_LEN, NULL);
+ }
+
+ lp->tx_new = (++lp->tx_new) % lp->txRingSize;
+ outl(POLL_DEMAND, DE4X5_TPD); /* Start the TX */
+ dev->trans_start = jiffies;
+ }
+ }
+
+ return;
+}
+
+/*
+** Calculate the hash code and update the logical address filter
+** from a list of ethernet multicast addresses.
+** Little endian crc one liner from Matt Thomas, DEC.
+*/
+static void SetMulticastFilter(struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ struct dev_mc_list *dmi=dev->mc_list;
+ u_long iobase = dev->base_addr;
+ int i, j, bit, byte;
+ u16 hashcode;
+ u32 omr, crc, poly = CRC_POLYNOMIAL_LE;
+ char *pa;
+ unsigned char *addrs;
+
+ omr = inl(DE4X5_OMR);
+ omr &= ~OMR_PR;
+ pa = build_setup_frame(dev, ALL); /* Build the basic frame */
+
+ if ((dev->flags & IFF_ALLMULTI) || (dev->mc_count > 14)) {
+ omr |= OMR_PM; /* Pass all multicasts */
+ } else if (lp->setup_f == HASH_PERF) {
+ /* Now update the MCA table */
+ for (i=0;i<dev->mc_count;i++) { /* for each address in the list */
+ addrs=dmi->dmi_addr;
+ dmi=dmi->next;
+ if ((*addrs & 0x01) == 1) { /* multicast address? */
+ crc = 0xffffffff; /* init CRC for each address */
+ for (byte=0;byte<ETH_ALEN;byte++) { /* for each address byte */
+ /* process each address bit */
+ for (bit = *addrs++,j=0;j<8;j++, bit>>=1) {
+ crc = (crc >> 1) ^ (((crc ^ bit) & 0x01) ? poly : 0);
+ }
+ }
+ hashcode = crc & HASH_BITS; /* hashcode is 9 LSb of CRC */
+
+ byte = hashcode >> 3; /* bit[3-8] -> byte in filter */
+ bit = 1 << (hashcode & 0x07); /* bit[0-2] -> bit in byte */
+
+ byte <<= 1; /* calc offset into setup frame */
+ if (byte & 0x02) {
+ byte -= 1;
+ }
+ lp->setup_frame[byte] |= bit;
+ }
+ }
+ } else { /* Perfect filtering */
+ for (j=0; j<dev->mc_count; j++) {
+ addrs=dmi->dmi_addr;
+ dmi=dmi->next;
+ for (i=0; i<ETH_ALEN; i++) {
+ *(pa + (i&1)) = *addrs++;
+ if (i & 0x01) pa += 4;
+ }
+ }
+ }
+ outl(omr, DE4X5_OMR);
+
+ return;
+}
+
+/*
+** EISA bus I/O device probe. Probe from slot 1 since slot 0 is usually
+** the motherboard. Upto 15 EISA devices are supported.
+*/
+static void eisa_probe(struct device *dev, u_long ioaddr)
+{
+ int i, maxSlots, status;
+ u_short vendor, device;
+ s32 cfid;
+ u_long iobase;
+ struct bus_type *lp = &bus;
+ char name[DE4X5_STRLEN];
+
+ if (!ioaddr && autoprobed) return ; /* Been here before ! */
+ if ((ioaddr < 0x1000) && (ioaddr > 0)) return; /* PCI MODULE special */
+
+ lp->bus = EISA;
+
+ if (ioaddr == 0) { /* Autoprobing */
+ iobase = EISA_SLOT_INC; /* Get the first slot address */
+ i = 1;
+ maxSlots = MAX_EISA_SLOTS;
+ } else { /* Probe a specific location */
+ iobase = ioaddr;
+ i = (ioaddr >> 12);
+ maxSlots = i + 1;
+ }
+
+ for (status = -ENODEV; (i<maxSlots) && (dev!=NULL); i++, iobase+=EISA_SLOT_INC) {
+ if (EISA_signature(name, EISA_ID)) {
+ cfid = inl(PCI_CFID);
+ device = (u_short)(cfid >> 16);
+ vendor = (u_short) cfid;
+
+ lp->bus = EISA;
+ lp->chipset = device;
+ if (DevicePresent(EISA_APROM) == 0) {
+ /* Write the PCI Configuration Registers */
+ outl(PCI_COMMAND_IO | PCI_COMMAND_MASTER, PCI_CFCS);
+ outl(0x00004000, PCI_CFLT);
+ outl(iobase, PCI_CBIO);
+
+ if (check_region(iobase, DE4X5_EISA_TOTAL_SIZE) == 0) {
+ if ((dev = alloc_device(dev, iobase)) != NULL) {
+ if ((status = de4x5_hw_init(dev, iobase)) == 0) {
+ num_de4x5s++;
+ }
+ num_eth++;
+ }
+ } else if (autoprobed) {
+ printk("%s: region already allocated at 0x%04lx.\n", dev->name, iobase);
+ }
+ }
+ }
+ }
+
+ return;
+}
+
+/*
+** PCI bus I/O device probe
+** NB: PCI I/O accesses and Bus Mastering are enabled by the PCI BIOS, not
+** the driver. Some PCI BIOS's, pre V2.1, need the slot + features to be
+** enabled by the user first in the set up utility. Hence we just check for
+** enabled features and silently ignore the card if they're not.
+**
+** STOP PRESS: Some BIOS's __require__ the driver to enable the bus mastering
+** bit. Here, check for I/O accesses and then set BM. If you put the card in
+** a non BM slot, you're on your own (and complain to the PC vendor that your
+** PC doesn't conform to the PCI standard)!
+*/
+#define PCI_DEVICE (dev_num << 3)
+#define PCI_LAST_DEV 32
+
+static void pci_probe(struct device *dev, u_long ioaddr)
+{
+ u_char irq;
+ u_char pb, pbus, dev_num, dnum, dev_fn;
+ u_short vendor, device, index, status;
+ u_int class = DE4X5_CLASS_CODE;
+ u_int iobase;
+ struct bus_type *lp = &bus;
+
+ if (!ioaddr && autoprobed) return ; /* Been here before ! */
+
+ if (pcibios_present()) {
+ lp->bus = PCI;
+
+ if (ioaddr < 0x1000) {
+ pbus = (u_short)(ioaddr >> 8);
+ dnum = (u_short)(ioaddr & 0xff);
+ } else {
+ pbus = 0;
+ dnum = 0;
+ }
+
+ for (index=0;
+ (pcibios_find_class(class, index, &pb, &dev_fn)!= PCIBIOS_DEVICE_NOT_FOUND);
+ index++) {
+ dev_num = PCI_SLOT(dev_fn);
+
+ if ((!pbus && !dnum) || ((pbus == pb) && (dnum == dev_num))) {
+ pcibios_read_config_word(pb, PCI_DEVICE, PCI_VENDOR_ID, &vendor);
+ pcibios_read_config_word(pb, PCI_DEVICE, PCI_DEVICE_ID, &device);
+ if (is_DC21040 || is_DC21041 || is_DC21140) {
+ /* Set the device number information */
+ lp->device = dev_num;
+ lp->bus_num = pb;
+
+ /* Set the chipset information */
+ lp->chipset = device;
+
+ /* Get the board I/O address */
+ pcibios_read_config_dword(pb, PCI_DEVICE, PCI_BASE_ADDRESS_0, &iobase);
+ iobase &= CBIO_MASK;
+
+ /* Fetch the IRQ to be used */
+ pcibios_read_config_byte(pb, PCI_DEVICE, PCI_INTERRUPT_LINE, &irq);
+
+ /* Check if I/O accesses and Bus Mastering are enabled */
+ pcibios_read_config_word(pb, PCI_DEVICE, PCI_COMMAND, &status);
+ if (status & PCI_COMMAND_IO) {
+ if (!(status & PCI_COMMAND_MASTER)) {
+ status |= PCI_COMMAND_MASTER;
+ pcibios_write_config_word(pb, PCI_DEVICE, PCI_COMMAND, status);
+ pcibios_read_config_word(pb, PCI_DEVICE, PCI_COMMAND, &status);
+ }
+ if (status & PCI_COMMAND_MASTER) {
+ if ((DevicePresent(DE4X5_APROM) == 0) || is_not_dec) {
+ if (check_region(iobase, DE4X5_PCI_TOTAL_SIZE) == 0) {
+ if ((dev = alloc_device(dev, iobase)) != NULL) {
+ dev->irq = irq;
+ if ((status = de4x5_hw_init(dev, iobase)) == 0) {
+ num_de4x5s++;
+ }
+ num_eth++;
+ }
+ } else if (autoprobed) {
+ printk("%s: region already allocated at 0x%04x.\n", dev->name, (u_short)iobase);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return;
+}
+
+/*
+** Allocate the device by pointing to the next available space in the
+** device structure. Should one not be available, it is created.
+*/
+static struct device *alloc_device(struct device *dev, u_long iobase)
+{
+ int addAutoProbe = 0;
+ struct device *tmp = NULL, *ret;
+ int (*init)(struct device *) = NULL;
+
+ /*
+ ** Check the device structures for an end of list or unused device
+ */
+ if (!loading_module) {
+ while (dev->next != NULL) {
+ if ((dev->base_addr == DE4X5_NDA) || (dev->base_addr == 0)) break;
+ dev = dev->next; /* walk through eth device list */
+ num_eth++; /* increment eth device number */
+ }
+
+ /*
+ ** If an autoprobe is requested for another device, we must re-insert
+ ** the request later in the list. Remember the current position first.
+ */
+ if ((dev->base_addr == 0) && (num_de4x5s > 0)) {
+ addAutoProbe++;
+ tmp = dev->next; /* point to the next device */
+ init = dev->init; /* remember the probe function */
+ }
+
+ /*
+ ** If at end of list and can't use current entry, malloc one up.
+ ** If memory could not be allocated, print an error message.
+ */
+ if ((dev->next == NULL) &&
+ !((dev->base_addr == DE4X5_NDA) || (dev->base_addr == 0))){
+ dev->next = (struct device *)kmalloc(sizeof(struct device) + 8,
+ GFP_KERNEL);
+
+ dev = dev->next; /* point to the new device */
+ if (dev == NULL) {
+ printk("eth%d: Device not initialised, insufficient memory\n",
+ num_eth);
+ } else {
+ /*
+ ** If the memory was allocated, point to the new memory area
+ ** and initialize it (name, I/O address, next device (NULL) and
+ ** initialisation probe routine).
+ */
+ dev->name = (char *)(dev + sizeof(struct device));
+ if (num_eth > 9999) {
+ sprintf(dev->name,"eth????"); /* New device name */
+ } else {
+ sprintf(dev->name,"eth%d", num_eth);/* New device name */
+ }
+ dev->base_addr = iobase; /* assign the io address */
+ dev->next = NULL; /* mark the end of list */
+ dev->init = &de4x5_probe; /* initialisation routine */
+ num_de4x5s++;
+ }
+ }
+ ret = dev; /* return current struct, or NULL */
+
+ /*
+ ** Now figure out what to do with the autoprobe that has to be inserted.
+ ** Firstly, search the (possibly altered) list for an empty space.
+ */
+ if (ret != NULL) {
+ if (addAutoProbe) {
+ for (; (tmp->next!=NULL) && (tmp->base_addr!=DE4X5_NDA); tmp=tmp->next);
+
+ /*
+ ** If no more device structures and can't use the current one, malloc
+ ** one up. If memory could not be allocated, print an error message.
+ */
+ if ((tmp->next == NULL) && !(tmp->base_addr == DE4X5_NDA)) {
+ tmp->next = (struct device *)kmalloc(sizeof(struct device) + 8,
+ GFP_KERNEL);
+ tmp = tmp->next; /* point to the new device */
+ if (tmp == NULL) {
+ printk("%s: Insufficient memory to extend the device list.\n",
+ dev->name);
+ } else {
+ /*
+ ** If the memory was allocated, point to the new memory area
+ ** and initialize it (name, I/O address, next device (NULL) and
+ ** initialisation probe routine).
+ */
+ tmp->name = (char *)(tmp + sizeof(struct device));
+ if (num_eth > 9999) {
+ sprintf(tmp->name,"eth????"); /* New device name */
+ } else {
+ sprintf(tmp->name,"eth%d", num_eth);/* New device name */
+ }
+ tmp->base_addr = 0; /* re-insert the io address */
+ tmp->next = NULL; /* mark the end of list */
+ tmp->init = init; /* initialisation routine */
+ }
+ } else { /* structure already exists */
+ tmp->base_addr = 0; /* re-insert the io address */
+ }
+ }
+ }
+ } else {
+ ret = dev;
+ }
+
+ return ret;
+}
+
+/*
+** Auto configure the media here rather than setting the port at compile
+** time. This routine is called by de4x5_init() when a loss of media is
+** detected (excessive collisions, loss of carrier, no carrier or link fail
+** [TP]) to check whether the user has been sneaky and changed the port on us.
+*/
+static int autoconf_media(struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+
+ lp->tx_enable = YES;
+ if (de4x5_debug > 0 ) {
+ if (lp->chipset != DC21140) {
+ printk("%s: Searching for media... ",dev->name);
+ } else {
+ printk("%s: Searching for mode... ",dev->name);
+ }
+ }
+
+ if (lp->chipset == DC21040) {
+ lp->media = (lp->autosense == AUTO ? TP : lp->autosense);
+ dc21040_autoconf(dev);
+ } else if (lp->chipset == DC21041) {
+ lp->media = (lp->autosense == AUTO ? TP_NW : lp->autosense);
+ dc21041_autoconf(dev);
+ } else if (lp->chipset == DC21140) {
+ disable_ast(dev);
+ lp->media = (lp->autosense == AUTO ? _10Mb : lp->autosense);
+ dc21140_autoconf(dev);
+ }
+
+ if (de4x5_debug > 0 ) {
+ if (lp->chipset != DC21140) {
+ printk("media is %s\n", (lp->media == NC ? "unconnected!" :
+ (lp->media == TP ? "TP." :
+ (lp->media == ANS ? "TP/Nway." :
+ (lp->media == BNC ? "BNC." :
+ (lp->media == AUI ? "AUI." :
+ "BNC/AUI."
+ ))))));
+ } else {
+ printk("mode is %s\n",(lp->media == NC ? "link down.":
+ (lp->media == _100Mb ? "100Mb/s." :
+ (lp->media == _10Mb ? "10Mb/s." :
+ "\?\?\?"
+ ))));
+ }
+ }
+
+ if (lp->media) {
+ lp->lostMedia = 0;
+ inl(DE4X5_MFC); /* Zero the lost frames counter */
+ if ((lp->media == TP) || (lp->media == ANS)) {
+ lp->irq_mask |= IMR_LFM;
+ }
+ }
+ dce_ms_delay(10);
+
+ return (lp->media);
+}
+
+static void dc21040_autoconf(struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+ int i, linkBad;
+ s32 sisr = 0, t_3s = 3000;
+
+ switch (lp->media) {
+ case TP:
+ reset_init_sia(dev, 0x8f01, 0xffff, 0x0000);
+ for (linkBad=1,i=0;(i<t_3s) && linkBad && !(sisr & SISR_NCR);i++) {
+ if (((sisr = inl(DE4X5_SISR)) & SISR_LKF) == 0) linkBad = 0;
+ dce_ms_delay(1);
+ }
+ if (linkBad && (lp->autosense == AUTO)) {
+ lp->media = BNC_AUI;
+ dc21040_autoconf(dev);
+ }
+ break;
+
+ case BNC:
+ case AUI:
+ case BNC_AUI:
+ reset_init_sia(dev, 0x8f09, 0x0705, 0x0006);
+ dce_ms_delay(500);
+ linkBad = ping_media(dev);
+ if (linkBad && (lp->autosense == AUTO)) {
+ lp->media = EXT_SIA;
+ dc21040_autoconf(dev);
+ }
+ break;
+
+ case EXT_SIA:
+ reset_init_sia(dev, 0x3041, 0x0000, 0x0006);
+ dce_ms_delay(500);
+ linkBad = ping_media(dev);
+ if (linkBad && (lp->autosense == AUTO)) {
+ lp->media = NC;
+ dc21040_autoconf(dev);
+ }
+ break;
+
+ case NC:
+#ifndef __alpha__
+ reset_init_sia(dev, 0x8f01, 0xffff, 0x0000);
+ break;
+#else
+ /* JAE: for Alpha, default to BNC/AUI, *not* TP */
+ reset_init_sia(dev, 0x8f09, 0x0705, 0x0006);
+#endif /* i386 */
+ }
+
+ return;
+}
+
+/*
+** Autoconfigure the media when using the DC21041. AUI needs to be tested
+** before BNC, because the BNC port will indicate activity if it's not
+** terminated correctly. The only way to test for that is to place a loopback
+** packet onto the network and watch for errors.
+*/
+static void dc21041_autoconf(struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+ s32 sts, irqs, irq_mask, omr;
+
+ switch (lp->media) {
+ case TP_NW:
+ omr = inl(DE4X5_OMR); /* Set up full duplex for the autonegotiate */
+ outl(omr | OMR_FD, DE4X5_OMR);
+ irqs = STS_LNF | STS_LNP;
+ irq_mask = IMR_LFM | IMR_LPM;
+ sts = test_media(dev, irqs, irq_mask, 0xef01, 0xffff, 0x0008, 2400);
+ if (sts & STS_LNP) {
+ lp->media = ANS;
+ } else {
+ lp->media = AUI;
+ }
+ dc21041_autoconf(dev);
+ break;
+
+ case ANS:
+ irqs = STS_LNP;
+ irq_mask = IMR_LPM;
+ sts = test_ans(dev, irqs, irq_mask, 3000);
+ if (!(sts & STS_LNP) && (lp->autosense == AUTO)) {
+ lp->media = TP;
+ dc21041_autoconf(dev);
+ }
+ break;
+
+ case TP:
+ omr = inl(DE4X5_OMR); /* Set up half duplex for TP */
+ outl(omr & ~OMR_FD, DE4X5_OMR);
+ irqs = STS_LNF | STS_LNP;
+ irq_mask = IMR_LFM | IMR_LPM;
+ sts = test_media(dev, irqs, irq_mask, 0xef01, 0xff3f, 0x0008, 2400);
+ if (!(sts & STS_LNP) && (lp->autosense == AUTO)) {
+ if (inl(DE4X5_SISR) & SISR_NRA) { /* Non selected port activity */
+ lp->media = AUI;
+ } else {
+ lp->media = BNC;
+ }
+ dc21041_autoconf(dev);
+ }
+ break;
+
+ case AUI:
+ omr = inl(DE4X5_OMR); /* Set up half duplex for AUI */
+ outl(omr & ~OMR_FD, DE4X5_OMR);
+ irqs = 0;
+ irq_mask = 0;
+ sts = test_media(dev, irqs, irq_mask, 0xef09, 0xf7fd, 0x000e, 1000);
+ if (!(inl(DE4X5_SISR) & SISR_SRA) && (lp->autosense == AUTO)) {
+ lp->media = BNC;
+ dc21041_autoconf(dev);
+ }
+ break;
+
+ case BNC:
+ omr = inl(DE4X5_OMR); /* Set up half duplex for BNC */
+ outl(omr & ~OMR_FD, DE4X5_OMR);
+ irqs = 0;
+ irq_mask = 0;
+ sts = test_media(dev, irqs, irq_mask, 0xef09, 0xf7fd, 0x0006, 1000);
+ if (!(inl(DE4X5_SISR) & SISR_SRA) && (lp->autosense == AUTO)) {
+ lp->media = NC;
+ } else { /* Ensure media connected */
+ if (ping_media(dev)) lp->media = NC;
+ }
+ break;
+
+ case NC:
+ omr = inl(DE4X5_OMR); /* Set up full duplex for the autonegotiate */
+ outl(omr | OMR_FD, DE4X5_OMR);
+ reset_init_sia(dev, 0xef01, 0xffff, 0x0008);/* Initialise the SIA */
+ break;
+ }
+
+ return;
+}
+
+/*
+** Reduced feature version (temporary I hope)
+*/
+static void dc21140_autoconf(struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+ s32 omr;
+
+ switch(lp->media) {
+ case _100Mb: /* Set 100Mb/s, MII Port with PCS Function and Scrambler */
+ omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR));
+ omr |= (de4x5_full_duplex ? OMR_FD : 0); /* Set up Full Duplex */
+ outl(omr | OMR_PS | OMR_HBD | OMR_PCS | OMR_SCR, DE4X5_OMR);
+ outl(GEP_FDXD | GEP_MODE, DE4X5_GEP);
+ break;
+
+ case _10Mb: /* Set conventional 10Mb/s ENDEC interface */
+ omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR));
+ omr |= (de4x5_full_duplex ? OMR_FD : 0); /* Set up Full Duplex */
+ outl(omr | OMR_TTM, DE4X5_OMR);
+ outl(GEP_FDXD, DE4X5_GEP);
+ break;
+ }
+
+ return;
+}
+
+static int
+test_media(struct device *dev, s32 irqs, s32 irq_mask, s32 csr13, s32 csr14, s32 csr15, s32 msec)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+ s32 sts, time, csr12;
+
+ reset_init_sia(dev, csr13, csr14, csr15);
+
+ /* Set link_fail_inhibit_timer */
+ load_ms_timer(dev, msec);
+
+ /* clear all pending interrupts */
+ sts = inl(DE4X5_STS);
+ outl(sts, DE4X5_STS);
+
+ /* clear csr12 NRA and SRA bits */
+ csr12 = inl(DE4X5_SISR);
+ outl(csr12, DE4X5_SISR);
+
+ /* Poll for timeout - timer interrupt doesn't work correctly */
+ do {
+ time = inl(DE4X5_GPT) & GPT_VAL;
+ sts = inl(DE4X5_STS);
+ } while ((time != 0) && !(sts & irqs));
+
+ sts = inl(DE4X5_STS);
+
+ return sts;
+}
+/*
+static int test_sym_link(struct device *dev, u32 msec)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+ u32 gep, time;
+
+ / * Set link_fail_inhibit_timer * /
+ load_ms_timer(dev, msec);
+
+ / * Poll for timeout or SYM_LINK=0 * /
+ do {
+ time = inl(DE4X5_GPT) & GPT_VAL;
+ gep = inl(DE4X5_GEP) & (GEP_SLNK | GEP_LNP);
+ } while ((time > 0) && (gep & GEP_SLNK));
+
+ return gep;
+}
+*/
+/*
+** Send a packet onto the media and watch for send errors that indicate the
+** media is bad or unconnected.
+*/
+static int ping_media(struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+ int i, entry, linkBad;
+ s32 omr, t_3s = 4000;
+ char frame[64];
+
+ create_packet(dev, frame, sizeof(frame));
+
+ entry = lp->tx_new; /* Remember the ring position */
+ load_packet(dev, frame, TD_LS | TD_FS | sizeof(frame),NULL);
+
+ omr = inl(DE4X5_OMR);
+ outl(omr|OMR_ST, DE4X5_OMR);
+
+ lp->tx_new = (++lp->tx_new) % lp->txRingSize;
+ lp->tx_old = lp->tx_new;
+
+ /* Poll for completion of frame (interrupts are disabled for now)... */
+ for (linkBad=1,i=0;(i<t_3s) && linkBad;i++) {
+ if ((inl(DE4X5_SISR) & SISR_NCR) == 1) break;
+ if (lp->tx_ring[entry].status >= 0) linkBad=0;
+ dce_ms_delay(1);
+ }
+ outl(omr, DE4X5_OMR);
+
+ return ((linkBad || (lp->tx_ring[entry].status & TD_ES)) ? 1 : 0);
+}
+
+/*
+** Check the Auto Negotiation State. Return OK when a link pass interrupt
+** is received and the auto-negotiation status is NWAY OK.
+*/
+static int test_ans(struct device *dev, s32 irqs, s32 irq_mask, s32 msec)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+ s32 sts, ans;
+
+ outl(irq_mask, DE4X5_IMR);
+
+ /* Set timeout limit */
+ load_ms_timer(dev, msec);
+
+ /* clear all pending interrupts */
+ sts = inl(DE4X5_STS);
+ outl(sts, DE4X5_STS);
+
+ /* Poll for interrupts */
+ do {
+ ans = inl(DE4X5_SISR) & SISR_ANS;
+ sts = inl(DE4X5_STS);
+ } while (!(sts & irqs) && (ans ^ ANS_NWOK) != 0);
+
+ return ((sts & STS_LNP) && ((ans ^ ANS_NWOK) == 0) ? STS_LNP : 0);
+}
+
+/*
+**
+*/
+static void reset_init_sia(struct device *dev, s32 sicr, s32 strr, s32 sigr)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+
+ RESET_SIA;
+ outl(sigr, DE4X5_SIGR);
+ outl(strr, DE4X5_STRR);
+ outl(sicr, DE4X5_SICR);
+
+ return;
+}
+
+/*
+** Load the timer on the DC21041 and 21140. Max time is 13.42 secs.
+*/
+static void load_ms_timer(struct device *dev, u32 msec)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+ s32 i = 2048, j;
+
+ if (lp->chipset == DC21140) {
+ j = inl(DE4X5_OMR);
+ if ((j & OMR_TTM) && (j & OMR_PS)) { /* 10Mb/s MII */
+ i = 8192;
+ } else if ((~j & OMR_TTM) && (j & OMR_PS)) { /* 100Mb/s MII */
+ i = 819;
+ }
+ }
+
+ outl((s32)(msec * 10000)/i, DE4X5_GPT);
+
+ return;
+}
+
+/*
+** Create an Ethernet packet with an invalid CRC
+*/
+static void create_packet(struct device *dev, char *frame, int len)
+{
+ int i;
+ char *buf = frame;
+
+ for (i=0; i<ETH_ALEN; i++) { /* Use this source address */
+ *buf++ = dev->dev_addr[i];
+ }
+ for (i=0; i<ETH_ALEN; i++) { /* Use this destination address */
+ *buf++ = dev->dev_addr[i];
+ }
+
+ *buf++ = 0; /* Packet length (2 bytes) */
+ *buf++ = 1;
+
+ return;
+}
+
+/*
+** Known delay in microseconds
+*/
+static void dce_us_delay(u32 usec)
+{
+ udelay(usec);
+
+ return;
+}
+
+/*
+** Known delay in milliseconds, in millisecond steps.
+*/
+static void dce_ms_delay(u32 msec)
+{
+ u_int i;
+
+ for (i=0; i<msec; i++) {
+ dce_us_delay(1000);
+ }
+
+ return;
+}
+
+
+/*
+** Look for a particular board name in the EISA configuration space
+*/
+static int EISA_signature(char *name, s32 eisa_id)
+{
+ u_int i;
+ const char *signatures[] = DE4X5_SIGNATURE;
+ char ManCode[DE4X5_STRLEN];
+ union {
+ s32 ID;
+ char Id[4];
+ } Eisa;
+ int status = 0;
+
+ *name = '\0';
+ Eisa.ID = inl(eisa_id);
+
+ ManCode[0]=(((Eisa.Id[0]>>2)&0x1f)+0x40);
+ ManCode[1]=(((Eisa.Id[1]&0xe0)>>5)+((Eisa.Id[0]&0x03)<<3)+0x40);
+ ManCode[2]=(((Eisa.Id[2]>>4)&0x0f)+0x30);
+ ManCode[3]=((Eisa.Id[2]&0x0f)+0x30);
+ ManCode[4]=(((Eisa.Id[3]>>4)&0x0f)+0x30);
+ ManCode[5]='\0';
+
+ for (i=0;(*signatures[i] != '\0') && (*name == '\0');i++) {
+ if (strstr(ManCode, signatures[i]) != NULL) {
+ strcpy(name,ManCode);
+ status = 1;
+ }
+ }
+
+ return status; /* return the device name string */
+}
+
+/*
+** Look for a special sequence in the Ethernet station address PROM that
+** is common across all DIGITAL network adapter products.
+**
+** Search the Ethernet address ROM for the signature. Since the ROM address
+** counter can start at an arbitrary point, the search must include the entire
+** probe sequence length plus the (length_of_the_signature - 1).
+** Stop the search IMMEDIATELY after the signature is found so that the
+** PROM address counter is correctly positioned at the start of the
+** ethernet address for later read out.
+*/
+
+static int DevicePresent(u_long aprom_addr)
+{
+ union {
+ struct {
+ u32 a;
+ u32 b;
+ } llsig;
+ char Sig[sizeof(u32) << 1];
+ } dev;
+ char data;
+ int i, j, tmp, status = 0;
+ short sigLength;
+ struct bus_type *lp = &bus;
+
+ dev.llsig.a = ETH_PROM_SIG;
+ dev.llsig.b = ETH_PROM_SIG;
+ sigLength = sizeof(u32) << 1;
+
+ if (lp->chipset == DC21040) {
+ for (i=0,j=0;(j<sigLength) && (i<PROBE_LENGTH+sigLength-1);i++) {
+ if (lp->bus == PCI) {
+ while ((tmp = inl(aprom_addr)) < 0);
+ data = (char)tmp;
+ } else {
+ data = inb(aprom_addr);
+ }
+ if (dev.Sig[j] == data) { /* track signature */
+ j++;
+ } else { /* lost signature; begin search again */
+ if (data == dev.Sig[0]) {
+ j=1;
+ } else {
+ j=0;
+ }
+ }
+ }
+
+ if (j!=sigLength) {
+ status = -ENODEV; /* search failed */
+ }
+
+ } else { /* use new srom */
+ short *p = (short *)&lp->srom;
+ for (i=0; i<(sizeof(struct de4x5_srom)>>1); i++) {
+ *p++ = srom_rd(aprom_addr, i);
+ }
+ }
+
+ return status;
+}
+
+static int get_hw_addr(struct device *dev)
+{
+ u_long iobase = dev->base_addr;
+ int i, k, tmp, status = 0;
+ u_short j,chksum;
+ struct bus_type *lp = &bus;
+
+ for (i=0,k=0,j=0;j<3;j++) {
+ k <<= 1 ;
+ if (k > 0xffff) k-=0xffff;
+
+ if (lp->bus == PCI) {
+ if (lp->chipset == DC21040) {
+ while ((tmp = inl(DE4X5_APROM)) < 0);
+ k += (u_char) tmp;
+ dev->dev_addr[i++] = (u_char) tmp;
+ while ((tmp = inl(DE4X5_APROM)) < 0);
+ k += (u_short) (tmp << 8);
+ dev->dev_addr[i++] = (u_char) tmp;
+ } else {
+ dev->dev_addr[i] = (u_char) lp->srom.ieee_addr[i]; i++;
+ dev->dev_addr[i] = (u_char) lp->srom.ieee_addr[i]; i++;
+ }
+ } else {
+ k += (u_char) (tmp = inb(EISA_APROM));
+ dev->dev_addr[i++] = (u_char) tmp;
+ k += (u_short) ((tmp = inb(EISA_APROM)) << 8);
+ dev->dev_addr[i++] = (u_char) tmp;
+ }
+
+ if (k > 0xffff) k-=0xffff;
+ }
+ if (k == 0xffff) k=0;
+
+ if (lp->bus == PCI) {
+ if (lp->chipset == DC21040) {
+ while ((tmp = inl(DE4X5_APROM)) < 0);
+ chksum = (u_char) tmp;
+ while ((tmp = inl(DE4X5_APROM)) < 0);
+ chksum |= (u_short) (tmp << 8);
+ if (k != chksum) status = -1;
+ }
+ } else {
+ chksum = (u_char) inb(EISA_APROM);
+ chksum |= (u_short) (inb(EISA_APROM) << 8);
+ if (k != chksum) status = -1;
+ }
+
+
+ return status;
+}
+
+/*
+** SROM Read
+*/
+static short srom_rd(u_long addr, u_char offset)
+{
+ sendto_srom(SROM_RD | SROM_SR, addr);
+
+ srom_latch(SROM_RD | SROM_SR | DT_CS, addr);
+ srom_command(SROM_RD | SROM_SR | DT_IN | DT_CS, addr);
+ srom_address(SROM_RD | SROM_SR | DT_CS, addr, offset);
+
+ return srom_data(SROM_RD | SROM_SR | DT_CS, addr);
+}
+
+static void srom_latch(u_int command, u_long addr)
+{
+ sendto_srom(command, addr);
+ sendto_srom(command | DT_CLK, addr);
+ sendto_srom(command, addr);
+
+ return;
+}
+
+static void srom_command(u_int command, u_long addr)
+{
+ srom_latch(command, addr);
+ srom_latch(command, addr);
+ srom_latch((command & 0x0000ff00) | DT_CS, addr);
+
+ return;
+}
+
+static void srom_address(u_int command, u_long addr, u_char offset)
+{
+ int i;
+ char a;
+
+ a = (char)(offset << 2);
+ for (i=0; i<6; i++, a <<= 1) {
+ srom_latch(command | ((a < 0) ? DT_IN : 0), addr);
+ }
+ dce_us_delay(1);
+
+ i = (getfrom_srom(addr) >> 3) & 0x01;
+ if (i != 0) {
+ printk("Bad SROM address phase.....\n");
+/* printk(".");*/
+ }
+
+ return;
+}
+
+static short srom_data(u_int command, u_long addr)
+{
+ int i;
+ short word = 0;
+ s32 tmp;
+
+ for (i=0; i<16; i++) {
+ sendto_srom(command | DT_CLK, addr);
+ tmp = getfrom_srom(addr);
+ sendto_srom(command, addr);
+
+ word = (word << 1) | ((tmp >> 3) & 0x01);
+ }
+
+ sendto_srom(command & 0x0000ff00, addr);
+
+ return word;
+}
+
+/*
+static void srom_busy(u_int command, u_long addr)
+{
+ sendto_srom((command & 0x0000ff00) | DT_CS, addr);
+
+ while (!((getfrom_srom(addr) >> 3) & 0x01)) {
+ dce_ms_delay(1);
+ }
+
+ sendto_srom(command & 0x0000ff00, addr);
+
+ return;
+}
+*/
+
+static void sendto_srom(u_int command, u_long addr)
+{
+ outl(command, addr);
+ dce_us_delay(1);
+
+ return;
+}
+
+static int getfrom_srom(u_long addr)
+{
+ s32 tmp;
+
+ tmp = inl(addr);
+ dce_us_delay(1);
+
+ return tmp;
+}
+
+static char *build_setup_frame(struct device *dev, int mode)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ int i;
+ char *pa = lp->setup_frame;
+
+ /* Initialise the setup frame */
+ if (mode == ALL) {
+ memset(lp->setup_frame, 0, SETUP_FRAME_LEN);
+ }
+
+ if (lp->setup_f == HASH_PERF) {
+ for (pa=lp->setup_frame+IMPERF_PA_OFFSET, i=0; i<ETH_ALEN; i++) {
+ *(pa + i) = dev->dev_addr[i]; /* Host address */
+ if (i & 0x01) pa += 2;
+ }
+ *(lp->setup_frame + (HASH_TABLE_LEN >> 3) - 3) = 0x80; /* B'cast address */
+ } else {
+ for (i=0; i<ETH_ALEN; i++) { /* Host address */
+ *(pa + (i&1)) = dev->dev_addr[i];
+ if (i & 0x01) pa += 4;
+ }
+ for (i=0; i<ETH_ALEN; i++) { /* Broadcast address */
+ *(pa + (i&1)) = (char) 0xff;
+ if (i & 0x01) pa += 4;
+ }
+ }
+
+ return pa; /* Points to the next entry */
+}
+
+static void enable_ast(struct device *dev, u32 time_out)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+
+ lp->irq_mask |= IMR_TMM;
+ outl(lp->irq_mask, DE4X5_IMR);
+ load_ms_timer(dev, time_out);
+
+ return;
+}
+
+static void disable_ast(struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+
+ lp->irq_mask &= ~IMR_TMM;
+ outl(lp->irq_mask, DE4X5_IMR);
+ load_ms_timer(dev, 0);
+
+ return;
+}
+
+static void kick_tx(struct device *dev)
+{
+ struct sk_buff *skb;
+
+ if ((skb = alloc_skb(0, GFP_ATOMIC)) != NULL) {
+ skb->len= FAKE_FRAME_LEN;
+ skb->arp=1;
+ skb->dev=dev;
+ dev_queue_xmit(skb, dev, SOPRI_NORMAL);
+ }
+
+ return;
+}
+
+/*
+** Perform IOCTL call functions here. Some are privileged operations and the
+** effective uid is checked in those cases.
+*/
+static int de4x5_ioctl(struct device *dev, struct ifreq *rq, int cmd)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ struct de4x5_ioctl *ioc = (struct de4x5_ioctl *) &rq->ifr_data;
+ u_long iobase = dev->base_addr;
+ int i, j, status = 0;
+ s32 omr;
+ union {
+ u8 addr[(HASH_TABLE_LEN * ETH_ALEN)];
+ u16 sval[(HASH_TABLE_LEN * ETH_ALEN) >> 1];
+ u32 lval[(HASH_TABLE_LEN * ETH_ALEN) >> 2];
+ } tmp;
+
+ switch(ioc->cmd) {
+ case DE4X5_GET_HWADDR: /* Get the hardware address */
+ ioc->len = ETH_ALEN;
+ status = verify_area(VERIFY_WRITE, (void *)ioc->data, ioc->len);
+ if (status)
+ break;
+ for (i=0; i<ETH_ALEN; i++) {
+ tmp.addr[i] = dev->dev_addr[i];
+ }
+ memcpy_tofs(ioc->data, tmp.addr, ioc->len);
+
+ break;
+ case DE4X5_SET_HWADDR: /* Set the hardware address */
+ status = verify_area(VERIFY_READ, (void *)ioc->data, ETH_ALEN);
+ if (status)
+ break;
+ status = -EPERM;
+ if (!suser())
+ break;
+ status = 0;
+ memcpy_fromfs(tmp.addr, ioc->data, ETH_ALEN);
+ for (i=0; i<ETH_ALEN; i++) {
+ dev->dev_addr[i] = tmp.addr[i];
+ }
+ 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*/
+ if (lp->setup_f == HASH_PERF) {
+ load_packet(dev, lp->setup_frame, TD_IC | HASH_F | TD_SET |
+ SETUP_FRAME_LEN, NULL);
+ } else {
+ load_packet(dev, lp->setup_frame, TD_IC | PERFECT_F | TD_SET |
+ SETUP_FRAME_LEN, NULL);
+ }
+ lp->tx_new = (++lp->tx_new) % lp->txRingSize;
+ outl(POLL_DEMAND, DE4X5_TPD); /* Start the TX */
+ dev->tbusy = 0; /* Unlock the TX ring */
+
+ break;
+ case DE4X5_SET_PROM: /* Set Promiscuous Mode */
+ if (suser()) {
+ omr = inl(DE4X5_OMR);
+ omr |= OMR_PR;
+ outl(omr, DE4X5_OMR);
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case DE4X5_CLR_PROM: /* Clear Promiscuous Mode */
+ if (suser()) {
+ omr = inl(DE4X5_OMR);
+ omr &= ~OMR_PR;
+ outb(omr, DE4X5_OMR);
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case DE4X5_SAY_BOO: /* Say "Boo!" to the kernel log file */
+ printk("%s: Boo!\n", dev->name);
+
+ 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)
+ break;
+ memcpy_tofs(ioc->data, lp->setup_frame, ioc->len);
+
+ break;
+ case DE4X5_SET_MCA: /* Set a multicast address */
+ if (suser()) {
+ if (ioc->len != HASH_TABLE_LEN) { /* MCA changes */
+ if (!(status = verify_area(VERIFY_READ, (void *)ioc->data, ETH_ALEN * ioc->len))) {
+ memcpy_fromfs(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()) {
+ set_multicast_list(dev);
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case DE4X5_MCA_EN: /* Enable pass all multicast addressing */
+ if (suser()) {
+ omr = inl(DE4X5_OMR);
+ omr |= OMR_PM;
+ outl(omr, DE4X5_OMR);
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case DE4X5_GET_STATS: /* Get the driver statistics */
+ ioc->len = sizeof(lp->pktStats);
+ status = verify_area(VERIFY_WRITE, (void *)ioc->data, ioc->len);
+ if (status)
+ break;
+
+ cli();
+ memcpy_tofs(ioc->data, &lp->pktStats, ioc->len);
+ sti();
+
+ break;
+ case DE4X5_CLR_STATS: /* Zero out the driver statistics */
+ if (suser()) {
+ cli();
+ memset(&lp->pktStats, 0, sizeof(lp->pktStats));
+ sti();
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case DE4X5_GET_OMR: /* Get the OMR Register contents */
+ tmp.addr[0] = inl(DE4X5_OMR);
+ if (!(status = verify_area(VERIFY_WRITE, (void *)ioc->data, 1))) {
+ memcpy_tofs(ioc->data, tmp.addr, 1);
+ }
+
+ break;
+ case DE4X5_SET_OMR: /* Set the OMR Register contents */
+ if (suser()) {
+ if (!(status = verify_area(VERIFY_READ, (void *)ioc->data, 1))) {
+ memcpy_fromfs(tmp.addr, ioc->data, 1);
+ outl(tmp.addr[0], DE4X5_OMR);
+ }
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case DE4X5_GET_REG: /* Get the DE4X5 Registers */
+ j = 0;
+ tmp.lval[0] = inl(DE4X5_STS); j+=4;
+ tmp.lval[1] = inl(DE4X5_BMR); j+=4;
+ tmp.lval[2] = inl(DE4X5_IMR); j+=4;
+ tmp.lval[3] = inl(DE4X5_OMR); j+=4;
+ tmp.lval[4] = inl(DE4X5_SISR); j+=4;
+ tmp.lval[5] = inl(DE4X5_SICR); j+=4;
+ tmp.lval[6] = inl(DE4X5_STRR); j+=4;
+ tmp.lval[7] = inl(DE4X5_SIGR); j+=4;
+ ioc->len = j;
+ if (!(status = verify_area(VERIFY_WRITE, (void *)ioc->data, ioc->len))) {
+ memcpy_tofs(ioc->data, tmp.addr, ioc->len);
+ }
+ break;
+
+#define DE4X5_DUMP 0x0f /* Dump the DE4X5 Status */
+
+ case DE4X5_DUMP:
+ j = 0;
+ tmp.addr[j++] = dev->irq;
+ for (i=0; i<ETH_ALEN; i++) {
+ tmp.addr[j++] = dev->dev_addr[i];
+ }
+ tmp.addr[j++] = lp->rxRingSize;
+ tmp.lval[j>>2] = (long)lp->rx_ring; j+=4;
+ tmp.lval[j>>2] = (long)lp->tx_ring; j+=4;
+
+ for (i=0;i<lp->rxRingSize-1;i++){
+ if (i < 3) {
+ tmp.lval[j>>2] = (long)&lp->rx_ring[i].status; j+=4;
+ }
+ }
+ tmp.lval[j>>2] = (long)&lp->rx_ring[i].status; j+=4;
+ for (i=0;i<lp->txRingSize-1;i++){
+ if (i < 3) {
+ tmp.lval[j>>2] = (long)&lp->tx_ring[i].status; j+=4;
+ }
+ }
+ tmp.lval[j>>2] = (long)&lp->tx_ring[i].status; j+=4;
+
+ for (i=0;i<lp->rxRingSize-1;i++){
+ if (i < 3) {
+ tmp.lval[j>>2] = (s32)lp->rx_ring[i].buf; j+=4;
+ }
+ }
+ tmp.lval[j>>2] = (s32)lp->rx_ring[i].buf; j+=4;
+ for (i=0;i<lp->txRingSize-1;i++){
+ if (i < 3) {
+ tmp.lval[j>>2] = (s32)lp->tx_ring[i].buf; j+=4;
+ }
+ }
+ tmp.lval[j>>2] = (s32)lp->tx_ring[i].buf; j+=4;
+
+ for (i=0;i<lp->rxRingSize;i++){
+ tmp.lval[j>>2] = lp->rx_ring[i].status; j+=4;
+ }
+ for (i=0;i<lp->txRingSize;i++){
+ tmp.lval[j>>2] = lp->tx_ring[i].status; j+=4;
+ }
+
+ tmp.lval[j>>2] = inl(DE4X5_STS); j+=4;
+ tmp.lval[j>>2] = inl(DE4X5_BMR); j+=4;
+ tmp.lval[j>>2] = inl(DE4X5_IMR); j+=4;
+ tmp.lval[j>>2] = inl(DE4X5_OMR); j+=4;
+ tmp.lval[j>>2] = inl(DE4X5_SISR); j+=4;
+ tmp.lval[j>>2] = inl(DE4X5_SICR); j+=4;
+ tmp.lval[j>>2] = inl(DE4X5_STRR); j+=4;
+ tmp.lval[j>>2] = inl(DE4X5_SIGR); j+=4;
+
+ tmp.addr[j++] = lp->txRingSize;
+ tmp.addr[j++] = dev->tbusy;
+
+ ioc->len = j;
+ if (!(status = verify_area(VERIFY_WRITE, (void *)ioc->data, ioc->len))) {
+ memcpy_tofs(ioc->data, tmp.addr, ioc->len);
+ }
+
+ break;
+ default:
+ status = -EOPNOTSUPP;
+ }
+
+ return status;
+}
+
+#ifdef MODULE
+static char devicename[9] = { 0, };
+static struct device thisDE4X5 = {
+ devicename, /* device name is inserted by linux/drivers/net/net_init.c */
+ 0, 0, 0, 0,
+ 0x2000, 10, /* I/O address, IRQ */
+ 0, 0, 0, NULL, de4x5_probe };
+
+static int io=0x000b; /* EDIT THESE LINES FOR YOUR CONFIGURATION */
+static int irq=10; /* or use the insmod io= irq= options */
+
+int
+init_module(void)
+{
+ thisDE4X5.base_addr=io;
+ thisDE4X5.irq=irq;
+ if (register_netdev(&thisDE4X5) != 0)
+ return -EIO;
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ struct de4x5_private *lp = (struct de4x5_private *) thisDE4X5.priv;
+
+ if (lp) {
+ kfree_s(bus_to_virt(lp->rx_ring[0].buf), RX_BUFF_SZ * NUM_RX_DESC + ALIGN);
+ }
+ kfree_s(thisDE4X5.priv, sizeof(struct de4x5_private) + ALIGN);
+ thisDE4X5.priv = NULL;
+
+ release_region(thisDE4X5.base_addr, (lp->bus == PCI ?
+ DE4X5_PCI_TOTAL_SIZE :
+ DE4X5_EISA_TOTAL_SIZE));
+ unregister_netdev(&thisDE4X5);
+}
+#endif /* MODULE */
+
+
+/*
+ * Local variables:
+ * kernel-compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O2 -m486 -c de4x5.c"
+ *
+ * module-compile-command: "gcc -D__KERNEL__ -DMODULE -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O2 -m486 -c de4x5.c"
+ * End:
+ */
+
+
diff --git a/i386/i386at/gpl/linux/net/de4x5.h b/i386/i386at/gpl/linux/net/de4x5.h
new file mode 100644
index 00000000..b0ee43ea
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/de4x5.h
@@ -0,0 +1,645 @@
+/*
+ Copyright 1994 Digital Equipment Corporation.
+
+ This software may be used and distributed according to the terms of the
+ GNU Public License, incorporated herein by reference.
+
+ The author may be reached as davies@wanton.lkg.dec.com or Digital
+ Equipment Corporation, 550 King Street, Littleton MA 01460.
+
+ =========================================================================
+*/
+
+/*
+** DC21040 CSR<1..15> Register Address Map
+*/
+#define DE4X5_BMR iobase+(0x000 << lp->bus) /* Bus Mode Register */
+#define DE4X5_TPD iobase+(0x008 << lp->bus) /* Transmit Poll Demand Reg */
+#define DE4X5_RPD iobase+(0x010 << lp->bus) /* Receive Poll Demand Reg */
+#define DE4X5_RRBA iobase+(0x018 << lp->bus) /* RX Ring Base Address Reg */
+#define DE4X5_TRBA iobase+(0x020 << lp->bus) /* TX Ring Base Address Reg */
+#define DE4X5_STS iobase+(0x028 << lp->bus) /* Status Register */
+#define DE4X5_OMR iobase+(0x030 << lp->bus) /* Operation Mode Register */
+#define DE4X5_IMR iobase+(0x038 << lp->bus) /* Interrupt Mask Register */
+#define DE4X5_MFC iobase+(0x040 << lp->bus) /* Missed Frame Counter */
+#define DE4X5_APROM iobase+(0x048 << lp->bus) /* Ethernet Address PROM */
+#define DE4X5_BROM iobase+(0x048 << lp->bus) /* Boot ROM Register */
+#define DE4X5_SROM iobase+(0x048 << lp->bus) /* Serial ROM Register */
+#define DE4X5_DDR iobase+(0x050 << lp->bus) /* Data Diagnostic Register */
+#define DE4X5_FDR iobase+(0x058 << lp->bus) /* Full Duplex Register */
+#define DE4X5_GPT iobase+(0x058 << lp->bus) /* General Purpose Timer Reg.*/
+#define DE4X5_GEP iobase+(0x060 << lp->bus) /* General Purpose Register */
+#define DE4X5_SISR iobase+(0x060 << lp->bus) /* SIA Status Register */
+#define DE4X5_SICR iobase+(0x068 << lp->bus) /* SIA Connectivity Register */
+#define DE4X5_STRR iobase+(0x070 << lp->bus) /* SIA TX/RX Register */
+#define DE4X5_SIGR iobase+(0x078 << lp->bus) /* SIA General Register */
+
+/*
+** EISA Register Address Map
+*/
+#define EISA_ID iobase+0x0c80 /* EISA ID Registers */
+#define EISA_ID0 iobase+0x0c80 /* EISA ID Register 0 */
+#define EISA_ID1 iobase+0x0c81 /* EISA ID Register 1 */
+#define EISA_ID2 iobase+0x0c82 /* EISA ID Register 2 */
+#define EISA_ID3 iobase+0x0c83 /* EISA ID Register 3 */
+#define EISA_CR iobase+0x0c84 /* EISA Control Register */
+#define EISA_REG0 iobase+0x0c88 /* EISA Configuration Register 0 */
+#define EISA_REG1 iobase+0x0c89 /* EISA Configuration Register 1 */
+#define EISA_REG2 iobase+0x0c8a /* EISA Configuration Register 2 */
+#define EISA_REG3 iobase+0x0c8f /* EISA Configuration Register 3 */
+#define EISA_APROM iobase+0x0c90 /* Ethernet Address PROM */
+
+/*
+** PCI/EISA Configuration Registers Address Map
+*/
+#define PCI_CFID iobase+0x0008 /* PCI Configuration ID Register */
+#define PCI_CFCS iobase+0x000c /* PCI Command/Status Register */
+#define PCI_CFRV iobase+0x0018 /* PCI Revision Register */
+#define PCI_CFLT iobase+0x001c /* PCI Latency Timer Register */
+#define PCI_CBIO iobase+0x0028 /* PCI Base I/O Register */
+#define PCI_CBMA iobase+0x002c /* PCI Base Memory Address Register */
+#define PCI_CBER iobase+0x0030 /* PCI Expansion ROM Base Address Reg. */
+#define PCI_CFIT iobase+0x003c /* PCI Configuration Interrupt Register */
+#define PCI_CFDA iobase+0x0040 /* PCI Driver Area Register */
+
+/*
+** EISA Configuration Register 0 bit definitions
+*/
+#define ER0_BSW 0x80 /* EISA Bus Slave Width, 1: 32 bits */
+#define ER0_BMW 0x40 /* EISA Bus Master Width, 1: 32 bits */
+#define ER0_EPT 0x20 /* EISA PREEMPT Time, 0: 23 BCLKs */
+#define ER0_ISTS 0x10 /* Interrupt Status (X) */
+#define ER0_LI 0x08 /* Latch Interrupts */
+#define ER0_INTL 0x06 /* INTerrupt Level */
+#define ER0_INTT 0x01 /* INTerrupt Type, 0: Level, 1: Edge */
+
+/*
+** EISA Configuration Register 1 bit definitions
+*/
+#define ER1_IAM 0xe0 /* ISA Address Mode */
+#define ER1_IAE 0x10 /* ISA Addressing Enable */
+#define ER1_UPIN 0x0f /* User Pins */
+
+/*
+** EISA Configuration Register 2 bit definitions
+*/
+#define ER2_BRS 0xc0 /* Boot ROM Size */
+#define ER2_BRA 0x3c /* Boot ROM Address <16:13> */
+
+/*
+** EISA Configuration Register 3 bit definitions
+*/
+#define ER3_BWE 0x40 /* Burst Write Enable */
+#define ER3_BRE 0x04 /* Burst Read Enable */
+#define ER3_LSR 0x02 /* Local Software Reset */
+
+/*
+** PCI Configuration ID Register (PCI_CFID)
+*/
+#define CFID_DID 0xff00 /* Device ID */
+#define CFID_VID 0x00ff /* Vendor ID */
+#define DC21040_DID 0x0002 /* Unique Device ID # */
+#define DC21040_VID 0x1011 /* DC21040 Manufacturer */
+#define DC21041_DID 0x0014 /* Unique Device ID # */
+#define DC21041_VID 0x1011 /* DC21041 Manufacturer */
+#define DC21140_DID 0x0009 /* Unique Device ID # */
+#define DC21140_VID 0x1011 /* DC21140 Manufacturer */
+
+/*
+** Chipset defines
+*/
+#define DC21040 DC21040_DID
+#define DC21041 DC21041_DID
+#define DC21140 DC21140_DID
+
+#define is_DC21040 ((vendor == DC21040_VID) && (device == DC21040_DID))
+#define is_DC21041 ((vendor == DC21041_VID) && (device == DC21041_DID))
+#define is_DC21140 ((vendor == DC21140_VID) && (device == DC21140_DID))
+
+/*
+** PCI Configuration Command/Status Register (PCI_CFCS)
+*/
+#define CFCS_DPE 0x80000000 /* Detected Parity Error (S) */
+#define CFCS_SSE 0x40000000 /* Signal System Error (S) */
+#define CFCS_RMA 0x20000000 /* Receive Master Abort (S) */
+#define CFCS_RTA 0x10000000 /* Receive Target Abort (S) */
+#define CFCS_DST 0x06000000 /* DEVSEL Timing (S) */
+#define CFCS_DPR 0x01000000 /* Data Parity Report (S) */
+#define CFCS_FBB 0x00800000 /* Fast Back-To-Back (S) */
+#define CFCS_SLE 0x00000100 /* System Error Enable (C) */
+#define CFCS_PER 0x00000040 /* Parity Error Response (C) */
+#define CFCS_MO 0x00000004 /* Master Operation (C) */
+#define CFCS_MSA 0x00000002 /* Memory Space Access (C) */
+#define CFCS_IOSA 0x00000001 /* I/O Space Access (C) */
+
+/*
+** PCI Configuration Revision Register (PCI_CFRV)
+*/
+#define CFRV_BC 0xff000000 /* Base Class */
+#define CFRV_SC 0x00ff0000 /* Subclass */
+#define CFRV_SN 0x000000f0 /* Step Number */
+#define CFRV_RN 0x0000000f /* Revision Number */
+#define BASE_CLASS 0x02000000 /* Indicates Network Controller */
+#define SUB_CLASS 0x00000000 /* Indicates Ethernet Controller */
+#define STEP_NUMBER 0x00000020 /* Increments for future chips */
+#define REV_NUMBER 0x00000003 /* 0x00, 0x01, 0x02, 0x03: Rev in Step */
+#define CFRV_MASK 0xffff0000 /* Register mask */
+
+/*
+** PCI Configuration Latency Timer Register (PCI_CFLT)
+*/
+#define CFLT_BC 0x0000ff00 /* Latency Timer bits */
+
+/*
+** PCI Configuration Base I/O Address Register (PCI_CBIO)
+*/
+#define CBIO_MASK 0xffffff80 /* Base I/O Address Mask */
+#define CBIO_IOSI 0x00000001 /* I/O Space Indicator (RO, value is 1) */
+
+/*
+** PCI Configuration Expansion ROM Base Address Register (PCI_CBER)
+*/
+#define CBER_MASK 0xfffffc00 /* Expansion ROM Base Address Mask */
+#define CBER_ROME 0x00000001 /* ROM Enable */
+
+/*
+** PCI Configuration Driver Area Register (PCI_CFDA)
+*/
+#define CFDA_PSM 0x80000000 /* Power Saving Mode */
+
+/*
+** DC21040 Bus Mode Register (DE4X5_BMR)
+*/
+#define BMR_DBO 0x00100000 /* Descriptor Byte Ordering (Endian) */
+#define BMR_TAP 0x000e0000 /* Transmit Automatic Polling */
+#define BMR_DAS 0x00010000 /* Diagnostic Address Space */
+#define BMR_CAL 0x0000c000 /* Cache Alignment */
+#define BMR_PBL 0x00003f00 /* Programmable Burst Length */
+#define BMR_BLE 0x00000080 /* Big/Little Endian */
+#define BMR_DSL 0x0000007c /* Descriptor Skip Length */
+#define BMR_BAR 0x00000002 /* Bus ARbitration */
+#define BMR_SWR 0x00000001 /* Software Reset */
+
+#define TAP_NOPOLL 0x00000000 /* No automatic polling */
+#define TAP_200US 0x00020000 /* TX automatic polling every 200us */
+#define TAP_800US 0x00040000 /* TX automatic polling every 800us */
+#define TAP_1_6MS 0x00060000 /* TX automatic polling every 1.6ms */
+#define TAP_12_8US 0x00080000 /* TX automatic polling every 12.8us */
+#define TAP_25_6US 0x000a0000 /* TX automatic polling every 25.6us */
+#define TAP_51_2US 0x000c0000 /* TX automatic polling every 51.2us */
+#define TAP_102_4US 0x000e0000 /* TX automatic polling every 102.4us */
+
+#define CAL_NOUSE 0x00000000 /* Not used */
+#define CAL_8LONG 0x00004000 /* 8-longword alignment */
+#define CAL_16LONG 0x00008000 /* 16-longword alignment */
+#define CAL_32LONG 0x0000c000 /* 32-longword alignment */
+
+#define PBL_0 0x00000000 /* DMA burst length = amount in RX FIFO */
+#define PBL_1 0x00000100 /* 1 longword DMA burst length */
+#define PBL_2 0x00000200 /* 2 longwords DMA burst length */
+#define PBL_4 0x00000400 /* 4 longwords DMA burst length */
+#define PBL_8 0x00000800 /* 8 longwords DMA burst length */
+#define PBL_16 0x00001000 /* 16 longwords DMA burst length */
+#define PBL_32 0x00002000 /* 32 longwords DMA burst length */
+
+#define DSL_0 0x00000000 /* 0 longword / descriptor */
+#define DSL_1 0x00000004 /* 1 longword / descriptor */
+#define DSL_2 0x00000008 /* 2 longwords / descriptor */
+#define DSL_4 0x00000010 /* 4 longwords / descriptor */
+#define DSL_8 0x00000020 /* 8 longwords / descriptor */
+#define DSL_16 0x00000040 /* 16 longwords / descriptor */
+#define DSL_32 0x00000080 /* 32 longwords / descriptor */
+
+/*
+** DC21040 Transmit Poll Demand Register (DE4X5_TPD)
+*/
+#define TPD 0x00000001 /* Transmit Poll Demand */
+
+/*
+** DC21040 Receive Poll Demand Register (DE4X5_RPD)
+*/
+#define RPD 0x00000001 /* Receive Poll Demand */
+
+/*
+** DC21040 Receive Ring Base Address Register (DE4X5_RRBA)
+*/
+#define RRBA 0xfffffffc /* RX Descriptor List Start Address */
+
+/*
+** DC21040 Transmit Ring Base Address Register (DE4X5_TRBA)
+*/
+#define TRBA 0xfffffffc /* TX Descriptor List Start Address */
+
+/*
+** DC21040 Status Register (DE4X5_STS)
+*/
+#define STS_BE 0x03800000 /* Bus Error Bits */
+#define STS_TS 0x00700000 /* Transmit Process State */
+#define STS_RS 0x000e0000 /* Receive Process State */
+#define STS_NIS 0x00010000 /* Normal Interrupt Summary */
+#define STS_AIS 0x00008000 /* Abnormal Interrupt Summary */
+#define STS_ER 0x00004000 /* Early Receive */
+#define STS_SE 0x00002000 /* System Error */
+#define STS_LNF 0x00001000 /* Link Fail */
+#define STS_FD 0x00000800 /* Full-Duplex Short Frame Received */
+#define STS_TM 0x00000800 /* Timer Expired (DC21041) */
+#define STS_AT 0x00000400 /* AUI/TP Pin */
+#define STS_RWT 0x00000200 /* Receive Watchdog Time-Out */
+#define STS_RPS 0x00000100 /* Receive Process Stopped */
+#define STS_RU 0x00000080 /* Receive Buffer Unavailable */
+#define STS_RI 0x00000040 /* Receive Interrupt */
+#define STS_UNF 0x00000020 /* Transmit Underflow */
+#define STS_LNP 0x00000010 /* Link Pass */
+#define STS_TJT 0x00000008 /* Transmit Jabber Time-Out */
+#define STS_TU 0x00000004 /* Transmit Buffer Unavailable */
+#define STS_TPS 0x00000002 /* Transmit Process Stopped */
+#define STS_TI 0x00000001 /* Transmit Interrupt */
+
+#define EB_PAR 0x00000000 /* Parity Error */
+#define EB_MA 0x00800000 /* Master Abort */
+#define EB_TA 0x01000000 /* Target Abort */
+#define EB_RES0 0x01800000 /* Reserved */
+#define EB_RES1 0x02000000 /* Reserved */
+
+#define TS_STOP 0x00000000 /* Stopped */
+#define TS_FTD 0x00100000 /* Fetch Transmit Descriptor */
+#define TS_WEOT 0x00200000 /* Wait for End Of Transmission */
+#define TS_QDAT 0x00300000 /* Queue skb data into TX FIFO */
+#define TS_RES 0x00400000 /* Reserved */
+#define TS_SPKT 0x00500000 /* Setup Packet */
+#define TS_SUSP 0x00600000 /* Suspended */
+#define TS_CLTD 0x00700000 /* Close Transmit Descriptor */
+
+#define RS_STOP 0x00000000 /* Stopped */
+#define RS_FRD 0x00020000 /* Fetch Receive Descriptor */
+#define RS_CEOR 0x00040000 /* Check for End of Receive Packet */
+#define RS_WFRP 0x00060000 /* Wait for Receive Packet */
+#define RS_SUSP 0x00080000 /* Suspended */
+#define RS_CLRD 0x000a0000 /* Close Receive Descriptor */
+#define RS_FLUSH 0x000c0000 /* Flush RX FIFO */
+#define RS_QRFS 0x000e0000 /* Queue RX FIFO into RX Skb */
+
+#define INT_CANCEL 0x0001ffff /* For zeroing all interrupt sources */
+
+/*
+** DC21040 Operation Mode Register (DE4X5_OMR)
+*/
+#define OMR_SDP 0x02000000 /* SD Polarity - MUST BE ASSERTED */
+#define OMR_SCR 0x01000000 /* Scrambler Mode */
+#define OMR_PCS 0x00800000 /* PCS Function */
+#define OMR_TTM 0x00400000 /* Transmit Threshold Mode */
+#define OMR_SF 0x00200000 /* Store and Forward */
+#define OMR_HBD 0x00080000 /* HeartBeat Disable */
+#define OMR_PS 0x00040000 /* Port Select */
+#define OMR_CA 0x00020000 /* Capture Effect Enable */
+#define OMR_BP 0x00010000 /* Back Pressure */
+#define OMR_TR 0x0000c000 /* Threshold Control Bits */
+#define OMR_ST 0x00002000 /* Start/Stop Transmission Command */
+#define OMR_FC 0x00001000 /* Force Collision Mode */
+#define OMR_OM 0x00000c00 /* Operating Mode */
+#define OMR_FD 0x00000200 /* Full Duplex Mode */
+#define OMR_FKD 0x00000100 /* Flaky Oscillator Disable */
+#define OMR_PM 0x00000080 /* Pass All Multicast */
+#define OMR_PR 0x00000040 /* Promiscuous Mode */
+#define OMR_SB 0x00000020 /* Start/Stop Backoff Counter */
+#define OMR_IF 0x00000010 /* Inverse Filtering */
+#define OMR_PB 0x00000008 /* Pass Bad Frames */
+#define OMR_HO 0x00000004 /* Hash Only Filtering Mode */
+#define OMR_SR 0x00000002 /* Start/Stop Receive */
+#define OMR_HP 0x00000001 /* Hash/Perfect Receive Filtering Mode */
+
+#define TR_72 0x00000000 /* Threshold set to 72 bytes */
+#define TR_96 0x00004000 /* Threshold set to 96 bytes */
+#define TR_128 0x00008000 /* Threshold set to 128 bytes */
+#define TR_160 0x0000c000 /* Threshold set to 160 bytes */
+
+/*
+** DC21040 Interrupt Mask Register (DE4X5_IMR)
+*/
+#define IMR_NIM 0x00010000 /* Normal Interrupt Summary Mask */
+#define IMR_AIM 0x00008000 /* Abnormal Interrupt Summary Mask */
+#define IMR_ERM 0x00004000 /* Early Receive Mask */
+#define IMR_SEM 0x00002000 /* System Error Mask */
+#define IMR_LFM 0x00001000 /* Link Fail Mask */
+#define IMR_FDM 0x00000800 /* Full-Duplex (Short Frame) Mask */
+#define IMR_TMM 0x00000800 /* Timer Expired Mask (DC21041) */
+#define IMR_ATM 0x00000400 /* AUI/TP Switch Mask */
+#define IMR_RWM 0x00000200 /* Receive Watchdog Time-Out Mask */
+#define IMR_RSM 0x00000100 /* Receive Stopped Mask */
+#define IMR_RUM 0x00000080 /* Receive Buffer Unavailable Mask */
+#define IMR_RIM 0x00000040 /* Receive Interrupt Mask */
+#define IMR_UNM 0x00000020 /* Underflow Interrupt Mask */
+#define IMR_LPM 0x00000010 /* Link Pass */
+#define IMR_TJM 0x00000008 /* Transmit Time-Out Jabber Mask */
+#define IMR_TUM 0x00000004 /* Transmit Buffer Unavailable Mask */
+#define IMR_TSM 0x00000002 /* Transmission Stopped Mask */
+#define IMR_TIM 0x00000001 /* Transmit Interrupt Mask */
+
+/*
+** DC21040 Missed Frame Counter (DE4X5_MFC)
+*/
+#define MFC_OVFL 0x00010000 /* Counter Overflow Bit */
+#define MFC_CNTR 0x0000ffff /* Counter Bits */
+
+/*
+** DC21040 Ethernet Address PROM (DE4X5_APROM)
+*/
+#define APROM_DN 0x80000000 /* Data Not Valid */
+#define APROM_DT 0x000000ff /* Address Byte */
+
+/*
+** DC21041 Boot/Ethernet Address ROM (DE4X5_BROM)
+*/
+#define BROM_MODE 0x00008000 /* MODE_1: 0, MODE_0: 1 (read only) */
+#define BROM_RD 0x00004000 /* Read from Boot ROM */
+#define BROM_WR 0x00002000 /* Write to Boot ROM */
+#define BROM_BR 0x00001000 /* Select Boot ROM when set */
+#define BROM_SR 0x00000800 /* Select Serial ROM when set */
+#define BROM_REG 0x00000400 /* External Register Select */
+#define BROM_DT 0x000000ff /* Data Byte */
+
+/*
+** DC21041 Serial/Ethernet Address ROM (DE4X5_SROM)
+*/
+#define SROM_MODE 0x00008000 /* MODE_1: 0, MODE_0: 1 (read only) */
+#define SROM_RD 0x00004000 /* Read from Boot ROM */
+#define SROM_WR 0x00002000 /* Write to Boot ROM */
+#define SROM_BR 0x00001000 /* Select Boot ROM when set */
+#define SROM_SR 0x00000800 /* Select Serial ROM when set */
+#define SROM_REG 0x00000400 /* External Register Select */
+#define SROM_DT 0x000000ff /* Data Byte */
+
+#define DT_OUT 0x00000008 /* Serial Data Out */
+#define DT_IN 0x00000004 /* Serial Data In */
+#define DT_CLK 0x00000002 /* Serial ROM Clock */
+#define DT_CS 0x00000001 /* Serial ROM Chip Select */
+
+/*
+** DC21040 Full Duplex Register (DE4X5_FDR)
+*/
+#define FDR_FDACV 0x0000ffff /* Full Duplex Auto Configuration Value */
+
+/*
+** DC21041 General Purpose Timer Register (DE4X5_GPT)
+*/
+#define GPT_CON 0x00010000 /* One shot: 0, Continuous: 1 */
+#define GPT_VAL 0x0000ffff /* Timer Value */
+
+/*
+** DC21140 General Purpose Register (DE4X5_GEP) (hardware dependent bits)
+*/
+/* Valid ONLY for DE500 hardware */
+#define GEP_LNP 0x00000080 /* Link Pass (input) */
+#define GEP_SLNK 0x00000040 /* SYM LINK (input) */
+#define GEP_SDET 0x00000020 /* Signal Detect (input) */
+#define GEP_FDXD 0x00000008 /* Full Duplex Disable (output) */
+#define GEP_PHYL 0x00000004 /* PHY Loopback (output) */
+#define GEP_FLED 0x00000002 /* Force Activity LED on (output) */
+#define GEP_MODE 0x00000001 /* 0: 10Mb/s, 1: 100Mb/s */
+#define GEP_INIT 0x0000010f /* Setup inputs (0) and outputs (1) */
+
+
+/*
+** DC21040 SIA Status Register (DE4X5_SISR)
+*/
+#define SISR_LPC 0xffff0000 /* Link Partner's Code Word */
+#define SISR_LPN 0x00008000 /* Link Partner Negotiable */
+#define SISR_ANS 0x00007000 /* Auto Negotiation Arbitration State */
+#define SISR_NSN 0x00000800 /* Non Stable NLPs Detected */
+#define SISR_ANR_FDS 0x00000400 /* Auto Negotiate Restart/Full Duplex Sel.*/
+#define SISR_NRA 0x00000200 /* Non Selected Port Receive Activity */
+#define SISR_SRA 0x00000100 /* Selected Port Receive Activity */
+#define SISR_DAO 0x00000080 /* PLL All One */
+#define SISR_DAZ 0x00000040 /* PLL All Zero */
+#define SISR_DSP 0x00000020 /* PLL Self-Test Pass */
+#define SISR_DSD 0x00000010 /* PLL Self-Test Done */
+#define SISR_APS 0x00000008 /* Auto Polarity State */
+#define SISR_LKF 0x00000004 /* Link Fail Status */
+#define SISR_NCR 0x00000002 /* Network Connection Error */
+#define SISR_PAUI 0x00000001 /* AUI_TP Indication */
+#define SIA_RESET 0x00000000 /* SIA Reset */
+
+#define ANS_NDIS 0x00000000 /* Nway disable */
+#define ANS_TDIS 0x00001000 /* Transmit Disable */
+#define ANS_ADET 0x00002000 /* Ability Detect */
+#define ANS_ACK 0x00003000 /* Acknowledge */
+#define ANS_CACK 0x00004000 /* Complete Acknowledge */
+#define ANS_NWOK 0x00005000 /* Nway OK - FLP Link Good */
+#define ANS_LCHK 0x00006000 /* Link Check */
+
+/*
+** DC21040 SIA Connectivity Register (DE4X5_SICR)
+*/
+#define SICR_SDM 0xffff0000 /* SIA Diagnostics Mode */
+#define SICR_OE57 0x00008000 /* Output Enable 5 6 7 */
+#define SICR_OE24 0x00004000 /* Output Enable 2 4 */
+#define SICR_OE13 0x00002000 /* Output Enable 1 3 */
+#define SICR_IE 0x00001000 /* Input Enable */
+#define SICR_EXT 0x00000000 /* SIA MUX Select External SIA Mode */
+#define SICR_D_SIA 0x00000400 /* SIA MUX Select Diagnostics - SIA Sigs */
+#define SICR_DPLL 0x00000800 /* SIA MUX Select Diagnostics - DPLL Sigs*/
+#define SICR_APLL 0x00000a00 /* SIA MUX Select Diagnostics - DPLL Sigs*/
+#define SICR_D_RxM 0x00000c00 /* SIA MUX Select Diagnostics - RxM Sigs */
+#define SICR_M_RxM 0x00000d00 /* SIA MUX Select Diagnostics - RxM Sigs */
+#define SICR_LNKT 0x00000e00 /* SIA MUX Select Diagnostics - Link Test*/
+#define SICR_SEL 0x00000f00 /* SIA MUX Select AUI or TP with LEDs */
+#define SICR_ASE 0x00000080 /* APLL Start Enable*/
+#define SICR_SIM 0x00000040 /* Serial Interface Input Multiplexer */
+#define SICR_ENI 0x00000020 /* Encoder Input Multiplexer */
+#define SICR_EDP 0x00000010 /* SIA PLL External Input Enable */
+#define SICR_AUI 0x00000008 /* 10Base-T or AUI */
+#define SICR_CAC 0x00000004 /* CSR Auto Configuration */
+#define SICR_PS 0x00000002 /* Pin AUI/TP Selection */
+#define SICR_SRL 0x00000001 /* SIA Reset */
+#define SICR_RESET 0xffff0000 /* Reset value for SICR */
+
+/*
+** DC21040 SIA Transmit and Receive Register (DE4X5_STRR)
+*/
+#define STRR_TAS 0x00008000 /* 10Base-T/AUI Autosensing Enable */
+#define STRR_SPP 0x00004000 /* Set Polarity Plus */
+#define STRR_APE 0x00002000 /* Auto Polarity Enable */
+#define STRR_LTE 0x00001000 /* Link Test Enable */
+#define STRR_SQE 0x00000800 /* Signal Quality Enable */
+#define STRR_CLD 0x00000400 /* Collision Detect Enable */
+#define STRR_CSQ 0x00000200 /* Collision Squelch Enable */
+#define STRR_RSQ 0x00000100 /* Receive Squelch Enable */
+#define STRR_ANE 0x00000080 /* Auto Negotiate Enable */
+#define STRR_HDE 0x00000040 /* Half Duplex Enable */
+#define STRR_CPEN 0x00000030 /* Compensation Enable */
+#define STRR_LSE 0x00000008 /* Link Pulse Send Enable */
+#define STRR_DREN 0x00000004 /* Driver Enable */
+#define STRR_LBK 0x00000002 /* Loopback Enable */
+#define STRR_ECEN 0x00000001 /* Encoder Enable */
+#define STRR_RESET 0xffffffff /* Reset value for STRR */
+
+/*
+** DC21040 SIA General Register (DE4X5_SIGR)
+*/
+#define SIGR_LV2 0x00008000 /* General Purpose LED2 value */
+#define SIGR_LE2 0x00004000 /* General Purpose LED2 enable */
+#define SIGR_FRL 0x00002000 /* Force Receiver Low */
+#define SIGR_DPST 0x00001000 /* PLL Self Test Start */
+#define SIGR_LSD 0x00000800 /* LED Stretch Disable */
+#define SIGR_FLF 0x00000400 /* Force Link Fail */
+#define SIGR_FUSQ 0x00000200 /* Force Unsquelch */
+#define SIGR_TSCK 0x00000100 /* Test Clock */
+#define SIGR_LV1 0x00000080 /* General Purpose LED1 value */
+#define SIGR_LE1 0x00000040 /* General Purpose LED1 enable */
+#define SIGR_RWR 0x00000020 /* Receive Watchdog Release */
+#define SIGR_RWD 0x00000010 /* Receive Watchdog Disable */
+#define SIGR_ABM 0x00000008 /* BNC: 0, AUI:1 */
+#define SIGR_JCK 0x00000004 /* Jabber Clock */
+#define SIGR_HUJ 0x00000002 /* Host Unjab */
+#define SIGR_JBD 0x00000001 /* Jabber Disable */
+#define SIGR_RESET 0xffff0000 /* Reset value for SIGR */
+
+/*
+** Receive Descriptor Bit Summary
+*/
+#define R_OWN 0x80000000 /* Own Bit */
+#define RD_FL 0x7fff0000 /* Frame Length */
+#define RD_ES 0x00008000 /* Error Summary */
+#define RD_LE 0x00004000 /* Length Error */
+#define RD_DT 0x00003000 /* Data Type */
+#define RD_RF 0x00000800 /* Runt Frame */
+#define RD_MF 0x00000400 /* Multicast Frame */
+#define RD_FS 0x00000200 /* First Descriptor */
+#define RD_LS 0x00000100 /* Last Descriptor */
+#define RD_TL 0x00000080 /* Frame Too Long */
+#define RD_CS 0x00000040 /* Collision Seen */
+#define RD_FT 0x00000020 /* Frame Type */
+#define RD_RJ 0x00000010 /* Receive Watchdog */
+#define RD_DB 0x00000004 /* Dribbling Bit */
+#define RD_CE 0x00000002 /* CRC Error */
+#define RD_OF 0x00000001 /* Overflow */
+
+#define RD_RER 0x02000000 /* Receive End Of Ring */
+#define RD_RCH 0x01000000 /* Second Address Chained */
+#define RD_RBS2 0x003ff800 /* Buffer 2 Size */
+#define RD_RBS1 0x000007ff /* Buffer 1 Size */
+
+/*
+** Transmit Descriptor Bit Summary
+*/
+#define T_OWN 0x80000000 /* Own Bit */
+#define TD_ES 0x00008000 /* Error Summary */
+#define TD_TO 0x00004000 /* Transmit Jabber Time-Out */
+#define TD_LO 0x00000800 /* Loss Of Carrier */
+#define TD_NC 0x00000400 /* No Carrier */
+#define TD_LC 0x00000200 /* Late Collision */
+#define TD_EC 0x00000100 /* Excessive Collisions */
+#define TD_HF 0x00000080 /* Heartbeat Fail */
+#define TD_CC 0x00000078 /* Collision Counter */
+#define TD_LF 0x00000004 /* Link Fail */
+#define TD_UF 0x00000002 /* Underflow Error */
+#define TD_DE 0x00000001 /* Deferred */
+
+#define TD_IC 0x80000000 /* Interrupt On Completion */
+#define TD_LS 0x40000000 /* Last Segment */
+#define TD_FS 0x20000000 /* First Segment */
+#define TD_FT1 0x10000000 /* Filtering Type */
+#define TD_SET 0x08000000 /* Setup Packet */
+#define TD_AC 0x04000000 /* Add CRC Disable */
+#define TD_TER 0x02000000 /* Transmit End Of Ring */
+#define TD_TCH 0x01000000 /* Second Address Chained */
+#define TD_DPD 0x00800000 /* Disabled Padding */
+#define TD_FT0 0x00400000 /* Filtering Type */
+#define TD_RBS2 0x003ff800 /* Buffer 2 Size */
+#define TD_RBS1 0x000007ff /* Buffer 1 Size */
+
+#define PERFECT_F 0x00000000
+#define HASH_F TD_FT0
+#define INVERSE_F TD_FT1
+#define HASH_O_F TD_FT1| TD_F0
+
+/*
+** Media / mode state machine definitions
+*/
+#define NC 0x0000 /* No Connection */
+#define TP 0x0001 /* 10Base-T */
+#define TP_NW 0x0002 /* 10Base-T with Nway */
+#define BNC 0x0004 /* Thinwire */
+#define AUI 0x0008 /* Thickwire */
+#define BNC_AUI 0x0010 /* BNC/AUI on DC21040 indistinguishable */
+#define ANS 0x0020 /* Intermediate AutoNegotiation State */
+#define EXT_SIA 0x0400 /* external SIA (as on DEC MULTIA) */
+
+#define _10Mb 0x0040 /* 10Mb/s Ethernet */
+#define _100Mb 0x0080 /* 100Mb/s Ethernet */
+#define SYM_WAIT 0x0100 /* Wait for SYM_LINK */
+#define INIT 0x0200 /* Initial state */
+
+#define AUTO 0x4000 /* Auto sense the media or speed */
+
+/*
+** Miscellaneous
+*/
+#define PCI 0
+#define EISA 1
+
+#define HASH_TABLE_LEN 512 /* Bits */
+#define HASH_BITS 0x01ff /* 9 LS bits */
+
+#define SETUP_FRAME_LEN 192 /* Bytes */
+#define IMPERF_PA_OFFSET 156 /* Bytes */
+
+#define POLL_DEMAND 1
+
+#define LOST_MEDIA_THRESHOLD 3
+
+#define MASK_INTERRUPTS 1
+#define UNMASK_INTERRUPTS 0
+
+#define DE4X5_STRLEN 8
+
+/*
+** Address Filtering Modes
+*/
+#define PERFECT 0 /* 16 perfect physical addresses */
+#define HASH_PERF 1 /* 1 perfect, 512 multicast addresses */
+#define PERFECT_REJ 2 /* Reject 16 perfect physical addresses */
+#define ALL_HASH 3 /* Hashes all physical & multicast addrs */
+
+#define ALL 0 /* Clear out all the setup frame */
+#define PHYS_ADDR_ONLY 1 /* Update the physical address only */
+
+/*
+** Booleans
+*/
+#define NO 0
+#define FALSE 0
+
+#define YES !0
+#define TRUE !0
+
+/*
+** Include the IOCTL stuff
+*/
+#include <linux/sockios.h>
+
+#define DE4X5IOCTL SIOCDEVPRIVATE
+
+struct de4x5_ioctl {
+ unsigned short cmd; /* Command to run */
+ unsigned short len; /* Length of the data buffer */
+ unsigned char *data; /* Pointer to the data buffer */
+};
+
+/*
+** Recognised commands for the driver
+*/
+#define DE4X5_GET_HWADDR 0x01 /* Get the hardware address */
+#define DE4X5_SET_HWADDR 0x02 /* Get the hardware address */
+#define DE4X5_SET_PROM 0x03 /* Set Promiscuous Mode */
+#define DE4X5_CLR_PROM 0x04 /* Clear Promiscuous Mode */
+#define DE4X5_SAY_BOO 0x05 /* Say "Boo!" to the kernel log file */
+#define DE4X5_GET_MCA 0x06 /* Get a multicast address */
+#define DE4X5_SET_MCA 0x07 /* Set a multicast address */
+#define DE4X5_CLR_MCA 0x08 /* Clear a multicast address */
+#define DE4X5_MCA_EN 0x09 /* Enable a multicast address group */
+#define DE4X5_GET_STATS 0x0a /* Get the driver statistics */
+#define DE4X5_CLR_STATS 0x0b /* Zero out the driver statistics */
+#define DE4X5_GET_OMR 0x0c /* Get the OMR Register contents */
+#define DE4X5_SET_OMR 0x0d /* Set the OMR Register contents */
+#define DE4X5_GET_REG 0x0e /* Get the DE4X5 Registers */
diff --git a/i386/i386at/gpl/linux/net/de600.c b/i386/i386at/gpl/linux/net/de600.c
new file mode 100644
index 00000000..256759df
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/de600.c
@@ -0,0 +1,853 @@
+static const char *version =
+ "de600.c: $Revision: 1.1.1.1 $, Bjorn Ekwall (bj0rn@blox.se)\n";
+/*
+ * de600.c
+ *
+ * Linux driver for the D-Link DE-600 Ethernet pocket adapter.
+ *
+ * Portions (C) Copyright 1993, 1994 by Bjorn Ekwall
+ * The Author may be reached as bj0rn@blox.se
+ *
+ * Based on adapter information gathered from DE600.ASM by D-Link Inc.,
+ * as included on disk C in the v.2.11 of PC/TCP from FTP Software.
+ * For DE600.asm:
+ * Portions (C) Copyright 1990 D-Link, Inc.
+ * Copyright, 1988-1992, Russell Nelson, Crynwr Software
+ *
+ * Adapted to the sample network driver core for linux,
+ * written by: Donald Becker <becker@super.org>
+ * C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
+ *
+ * compile-command:
+ * "gcc -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer \
+ * -m486 -c de600.c
+ *
+ **************************************************************/
+/*
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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.
+ *
+ **************************************************************/
+/* Add another "; SLOW_DOWN_IO" here if your adapter won't work OK: */
+#define DE600_SLOW_DOWN SLOW_DOWN_IO; SLOW_DOWN_IO; SLOW_DOWN_IO
+
+ /*
+ * If you still have trouble reading/writing to the adapter,
+ * modify the following "#define": (see <asm/io.h> for more info)
+#define REALLY_SLOW_IO
+ */
+#define SLOW_IO_BY_JUMPING /* Looks "better" than dummy write to port 0x80 :-) */
+
+/*
+ * If you want to enable automatic continuous checking for the DE600,
+ * keep this #define enabled.
+ * It doesn't cost much per packet, so I think it is worth it!
+ * If you disagree, comment away the #define, and live with it...
+ *
+ */
+#define CHECK_LOST_DE600
+
+/*
+ * Enable this #define if you want the adapter to do a "ifconfig down" on
+ * itself when we have detected that something is possibly wrong with it.
+ * The default behaviour is to retry with "adapter_init()" until success.
+ * This should be used for debugging purposes only.
+ * (Depends on the CHECK_LOST_DE600 above)
+ *
+ */
+#define SHUTDOWN_WHEN_LOST
+
+/*
+ * See comment at "de600_rspace()"!
+ * This is an *ugly* hack, but for now it achieves its goal of
+ * faking a TCP flow-control that will not flood the poor DE600.
+ *
+ * Tricks TCP to announce a small max window (max 2 fast packets please :-)
+ *
+ * Comment away at your own risk!
+ *
+ * Update: Use the more general per-device maxwindow parameter instead.
+ */
+#undef FAKE_SMALL_MAX
+
+/* use 0 for production, 1 for verification, >2 for debug */
+#ifdef DE600_DEBUG
+#define PRINTK(x) if (de600_debug >= 2) printk x
+#else
+#define DE600_DEBUG 0
+#define PRINTK(x) /**/
+#endif
+unsigned int de600_debug = DE600_DEBUG;
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include <linux/in.h>
+#include <linux/ptrace.h>
+#include <asm/system.h>
+#include <linux/errno.h>
+
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#ifdef FAKE_SMALL_MAX
+static unsigned long de600_rspace(struct sock *sk);
+#include <net/sock.h>
+#endif
+
+#define netstats enet_statistics
+typedef unsigned char byte;
+
+/**************************************************
+ * *
+ * Definition of D-Link Ethernet Pocket adapter *
+ * *
+ **************************************************/
+/*
+ * D-Link Ethernet pocket adapter ports
+ */
+/*
+ * OK, so I'm cheating, but there are an awful lot of
+ * reads and writes in order to get anything in and out
+ * of the DE-600 with 4 bits at a time in the parallel port,
+ * so every saved instruction really helps :-)
+ *
+ * That is, I don't care what the device struct says
+ * but hope that Space.c will keep the rest of the drivers happy.
+ */
+#ifndef DE600_IO
+#define DE600_IO 0x378
+#endif
+
+#define DATA_PORT (DE600_IO)
+#define STATUS_PORT (DE600_IO + 1)
+#define COMMAND_PORT (DE600_IO + 2)
+
+#ifndef DE600_IRQ
+#define DE600_IRQ 7
+#endif
+/*
+ * It really should look like this, and autoprobing as well...
+ *
+#define DATA_PORT (dev->base_addr + 0)
+#define STATUS_PORT (dev->base_addr + 1)
+#define COMMAND_PORT (dev->base_addr + 2)
+#define DE600_IRQ dev->irq
+ */
+
+/*
+ * D-Link COMMAND_PORT commands
+ */
+#define SELECT_NIC 0x04 /* select Network Interface Card */
+#define SELECT_PRN 0x1c /* select Printer */
+#define NML_PRN 0xec /* normal Printer situation */
+#define IRQEN 0x10 /* enable IRQ line */
+
+/*
+ * D-Link STATUS_PORT
+ */
+#define RX_BUSY 0x80
+#define RX_GOOD 0x40
+#define TX_FAILED16 0x10
+#define TX_BUSY 0x08
+
+/*
+ * D-Link DATA_PORT commands
+ * command in low 4 bits
+ * data in high 4 bits
+ * select current data nibble with HI_NIBBLE bit
+ */
+#define WRITE_DATA 0x00 /* write memory */
+#define READ_DATA 0x01 /* read memory */
+#define STATUS 0x02 /* read status register */
+#define COMMAND 0x03 /* write command register (see COMMAND below) */
+#define NULL_COMMAND 0x04 /* null command */
+#define RX_LEN 0x05 /* read received packet length */
+#define TX_ADDR 0x06 /* set adapter transmit memory address */
+#define RW_ADDR 0x07 /* set adapter read/write memory address */
+#define HI_NIBBLE 0x08 /* read/write the high nibble of data,
+ or-ed with rest of command */
+
+/*
+ * command register, accessed through DATA_PORT with low bits = COMMAND
+ */
+#define RX_ALL 0x01 /* PROMISCUOUS */
+#define RX_BP 0x02 /* default: BROADCAST & PHYSICAL ADDRESS */
+#define RX_MBP 0x03 /* MULTICAST, BROADCAST & PHYSICAL ADDRESS */
+
+#define TX_ENABLE 0x04 /* bit 2 */
+#define RX_ENABLE 0x08 /* bit 3 */
+
+#define RESET 0x80 /* set bit 7 high */
+#define STOP_RESET 0x00 /* set bit 7 low */
+
+/*
+ * data to command register
+ * (high 4 bits in write to DATA_PORT)
+ */
+#define RX_PAGE2_SELECT 0x10 /* bit 4, only 2 pages to select */
+#define RX_BASE_PAGE 0x20 /* bit 5, always set when specifying RX_ADDR */
+#define FLIP_IRQ 0x40 /* bit 6 */
+
+/*
+ * D-Link adapter internal memory:
+ *
+ * 0-2K 1:st transmit page (send from pointer up to 2K)
+ * 2-4K 2:nd transmit page (send from pointer up to 4K)
+ *
+ * 4-6K 1:st receive page (data from 4K upwards)
+ * 6-8K 2:nd receive page (data from 6K upwards)
+ *
+ * 8K+ Adapter ROM (contains magic code and last 3 bytes of Ethernet address)
+ */
+#define MEM_2K 0x0800 /* 2048 */
+#define MEM_4K 0x1000 /* 4096 */
+#define MEM_6K 0x1800 /* 6144 */
+#define NODE_ADDRESS 0x2000 /* 8192 */
+
+#define RUNT 60 /* Too small Ethernet packet */
+
+/**************************************************
+ * *
+ * End of definition *
+ * *
+ **************************************************/
+
+/*
+ * Index to functions, as function prototypes.
+ */
+/* Routines used internally. (See "convenience macros") */
+static byte de600_read_status(struct device *dev);
+static byte de600_read_byte(unsigned char type, struct device *dev);
+
+/* Put in the device structure. */
+static int de600_open(struct device *dev);
+static int de600_close(struct device *dev);
+static struct netstats *get_stats(struct device *dev);
+static int de600_start_xmit(struct sk_buff *skb, struct device *dev);
+
+/* Dispatch from interrupts. */
+static void de600_interrupt(int irq, struct pt_regs *regs);
+static int de600_tx_intr(struct device *dev, int irq_status);
+static void de600_rx_intr(struct device *dev);
+
+/* Initialization */
+static void trigger_interrupt(struct device *dev);
+int de600_probe(struct device *dev);
+static int adapter_init(struct device *dev);
+
+/*
+ * D-Link driver variables:
+ */
+static volatile int rx_page = 0;
+
+#define TX_PAGES 2
+static volatile int tx_fifo[TX_PAGES];
+static volatile int tx_fifo_in = 0;
+static volatile int tx_fifo_out = 0;
+static volatile int free_tx_pages = TX_PAGES;
+static int was_down = 0;
+
+/*
+ * Convenience macros/functions for D-Link adapter
+ */
+
+#define select_prn() outb_p(SELECT_PRN, COMMAND_PORT); DE600_SLOW_DOWN
+#define select_nic() outb_p(SELECT_NIC, COMMAND_PORT); DE600_SLOW_DOWN
+
+/* Thanks for hints from Mark Burton <markb@ordern.demon.co.uk> */
+#define de600_put_byte(data) ( \
+ outb_p(((data) << 4) | WRITE_DATA , DATA_PORT), \
+ outb_p(((data) & 0xf0) | WRITE_DATA | HI_NIBBLE, DATA_PORT))
+
+/*
+ * The first two outb_p()'s below could perhaps be deleted if there
+ * would be more delay in the last two. Not certain about it yet...
+ */
+#define de600_put_command(cmd) ( \
+ outb_p(( rx_page << 4) | COMMAND , DATA_PORT), \
+ outb_p(( rx_page & 0xf0) | COMMAND | HI_NIBBLE, DATA_PORT), \
+ outb_p(((rx_page | cmd) << 4) | COMMAND , DATA_PORT), \
+ outb_p(((rx_page | cmd) & 0xf0) | COMMAND | HI_NIBBLE, DATA_PORT))
+
+#define de600_setup_address(addr,type) ( \
+ outb_p((((addr) << 4) & 0xf0) | type , DATA_PORT), \
+ outb_p(( (addr) & 0xf0) | type | HI_NIBBLE, DATA_PORT), \
+ outb_p((((addr) >> 4) & 0xf0) | type , DATA_PORT), \
+ outb_p((((addr) >> 8) & 0xf0) | type | HI_NIBBLE, DATA_PORT))
+
+#define rx_page_adr() ((rx_page & RX_PAGE2_SELECT)?(MEM_6K):(MEM_4K))
+
+/* Flip bit, only 2 pages */
+#define next_rx_page() (rx_page ^= RX_PAGE2_SELECT)
+
+#define tx_page_adr(a) (((a) + 1) * MEM_2K)
+
+static inline byte
+de600_read_status(struct device *dev)
+{
+ byte status;
+
+ outb_p(STATUS, DATA_PORT);
+ status = inb(STATUS_PORT);
+ outb_p(NULL_COMMAND | HI_NIBBLE, DATA_PORT);
+
+ return status;
+}
+
+static inline byte
+de600_read_byte(unsigned char type, struct device *dev) { /* dev used by macros */
+ byte lo;
+
+ (void)outb_p((type), DATA_PORT);
+ lo = ((unsigned char)inb(STATUS_PORT)) >> 4;
+ (void)outb_p((type) | HI_NIBBLE, DATA_PORT);
+ return ((unsigned char)inb(STATUS_PORT) & (unsigned char)0xf0) | lo;
+}
+
+/*
+ * Open/initialize the board. This is called (in the current kernel)
+ * after booting when 'ifconfig <dev->name> $IP_ADDR' is run (in rc.inet1).
+ *
+ * This routine should set everything up anew at each open, even
+ * registers that "should" only need to be set once at boot, so that
+ * there is a non-reboot way to recover if something goes wrong.
+ */
+static int
+de600_open(struct device *dev)
+{
+ if (request_irq(DE600_IRQ, de600_interrupt, 0, "de600")) {
+ printk ("%s: unable to get IRQ %d\n", dev->name, DE600_IRQ);
+ return 1;
+ }
+ irq2dev_map[DE600_IRQ] = dev;
+
+ MOD_INC_USE_COUNT;
+ dev->start = 1;
+ if (adapter_init(dev)) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * The inverse routine to de600_open().
+ */
+static int
+de600_close(struct device *dev)
+{
+ select_nic();
+ rx_page = 0;
+ de600_put_command(RESET);
+ de600_put_command(STOP_RESET);
+ de600_put_command(0);
+ select_prn();
+
+ if (dev->start) {
+ free_irq(DE600_IRQ);
+ irq2dev_map[DE600_IRQ] = NULL;
+ dev->start = 0;
+ MOD_DEC_USE_COUNT;
+ }
+ return 0;
+}
+
+static struct netstats *
+get_stats(struct device *dev)
+{
+ return (struct netstats *)(dev->priv);
+}
+
+static inline void
+trigger_interrupt(struct device *dev)
+{
+ de600_put_command(FLIP_IRQ);
+ select_prn();
+ DE600_SLOW_DOWN;
+ select_nic();
+ de600_put_command(0);
+}
+
+/*
+ * Copy a buffer to the adapter transmit page memory.
+ * Start sending.
+ */
+static int
+de600_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+ int transmit_from;
+ int len;
+ int tickssofar;
+ byte *buffer = skb->data;
+
+ /*
+ * If some higher layer thinks we've missed a
+ * tx-done interrupt we are passed NULL.
+ * Caution: dev_tint() handles the cli()/sti() itself.
+ */
+
+ if (skb == NULL) {
+ dev_tint(dev);
+ return 0;
+ }
+
+ if (free_tx_pages <= 0) { /* Do timeouts, to avoid hangs. */
+ tickssofar = jiffies - dev->trans_start;
+
+ if (tickssofar < 5)
+ return 1;
+
+ /* else */
+ printk("%s: transmit timed out (%d), %s?\n",
+ dev->name,
+ tickssofar,
+ "network cable problem"
+ );
+ /* Restart the adapter. */
+ if (adapter_init(dev)) {
+ return 1;
+ }
+ }
+
+ /* Start real output */
+ PRINTK(("de600_start_xmit:len=%d, page %d/%d\n", skb->len, tx_fifo_in, free_tx_pages));
+
+ if ((len = skb->len) < RUNT)
+ len = RUNT;
+
+ cli();
+ select_nic();
+ tx_fifo[tx_fifo_in] = transmit_from = tx_page_adr(tx_fifo_in) - len;
+ tx_fifo_in = (tx_fifo_in + 1) % TX_PAGES; /* Next free tx page */
+
+#ifdef CHECK_LOST_DE600
+ /* This costs about 40 instructions per packet... */
+ de600_setup_address(NODE_ADDRESS, RW_ADDR);
+ de600_read_byte(READ_DATA, dev);
+ if (was_down || (de600_read_byte(READ_DATA, dev) != 0xde)) {
+ if (adapter_init(dev)) {
+ sti();
+ return 1;
+ }
+ }
+#endif
+
+ de600_setup_address(transmit_from, RW_ADDR);
+ for ( ; len > 0; --len, ++buffer)
+ de600_put_byte(*buffer);
+
+ if (free_tx_pages-- == TX_PAGES) { /* No transmission going on */
+ dev->trans_start = jiffies;
+ dev->tbusy = 0; /* allow more packets into adapter */
+ /* Send page and generate a faked interrupt */
+ de600_setup_address(transmit_from, TX_ADDR);
+ de600_put_command(TX_ENABLE);
+ }
+ else {
+ dev->tbusy = !free_tx_pages;
+ select_prn();
+ }
+
+ sti(); /* interrupts back on */
+
+#ifdef FAKE_SMALL_MAX
+ /* This will "patch" the socket TCP proto at an early moment */
+ if (skb->sk && (skb->sk->protocol == IPPROTO_TCP) &&
+ (skb->sk->prot->rspace != &de600_rspace))
+ skb->sk->prot->rspace = de600_rspace; /* Ugh! */
+#endif
+
+ dev_kfree_skb (skb, FREE_WRITE);
+
+ return 0;
+}
+
+/*
+ * The typical workload of the driver:
+ * Handle the network interface interrupts.
+ */
+static void
+de600_interrupt(int irq, struct pt_regs * regs)
+{
+ struct device *dev = irq2dev_map[irq];
+ byte irq_status;
+ int retrig = 0;
+ int boguscount = 0;
+
+ /* This might just as well be deleted now, no crummy drivers present :-) */
+ if ((dev == NULL) || (dev->start == 0) || (DE600_IRQ != irq)) {
+ printk("%s: bogus interrupt %d\n", dev?dev->name:"DE-600", irq);
+ return;
+ }
+
+ dev->interrupt = 1;
+ select_nic();
+ irq_status = de600_read_status(dev);
+
+ do {
+ PRINTK(("de600_interrupt (%02X)\n", irq_status));
+
+ if (irq_status & RX_GOOD)
+ de600_rx_intr(dev);
+ else if (!(irq_status & RX_BUSY))
+ de600_put_command(RX_ENABLE);
+
+ /* Any transmission in progress? */
+ if (free_tx_pages < TX_PAGES)
+ retrig = de600_tx_intr(dev, irq_status);
+ else
+ retrig = 0;
+
+ irq_status = de600_read_status(dev);
+ } while ( (irq_status & RX_GOOD) || ((++boguscount < 100) && retrig) );
+ /*
+ * Yeah, it _looks_ like busy waiting, smells like busy waiting
+ * and I know it's not PC, but please, it will only occur once
+ * in a while and then only for a loop or so (< 1ms for sure!)
+ */
+
+ /* Enable adapter interrupts */
+ dev->interrupt = 0;
+ select_prn();
+
+ if (retrig)
+ trigger_interrupt(dev);
+
+ sti();
+ return;
+}
+
+static int
+de600_tx_intr(struct device *dev, int irq_status)
+{
+ /*
+ * Returns 1 if tx still not done
+ */
+
+ mark_bh(NET_BH);
+ /* Check if current transmission is done yet */
+ if (irq_status & TX_BUSY)
+ return 1; /* tx not done, try again */
+
+ /* else */
+ /* If last transmission OK then bump fifo index */
+ if (!(irq_status & TX_FAILED16)) {
+ tx_fifo_out = (tx_fifo_out + 1) % TX_PAGES;
+ ++free_tx_pages;
+ ((struct netstats *)(dev->priv))->tx_packets++;
+ dev->tbusy = 0;
+ }
+
+ /* More to send, or resend last packet? */
+ if ((free_tx_pages < TX_PAGES) || (irq_status & TX_FAILED16)) {
+ dev->trans_start = jiffies;
+ de600_setup_address(tx_fifo[tx_fifo_out], TX_ADDR);
+ de600_put_command(TX_ENABLE);
+ return 1;
+ }
+ /* else */
+
+ return 0;
+}
+
+/*
+ * We have a good packet, get it out of the adapter.
+ */
+static void
+de600_rx_intr(struct device *dev)
+{
+ struct sk_buff *skb;
+ int i;
+ int read_from;
+ int size;
+ register unsigned char *buffer;
+
+ cli();
+ /* Get size of received packet */
+ size = de600_read_byte(RX_LEN, dev); /* low byte */
+ size += (de600_read_byte(RX_LEN, dev) << 8); /* high byte */
+ size -= 4; /* Ignore trailing 4 CRC-bytes */
+
+ /* Tell adapter where to store next incoming packet, enable receiver */
+ read_from = rx_page_adr();
+ next_rx_page();
+ de600_put_command(RX_ENABLE);
+ sti();
+
+ if ((size < 32) || (size > 1535)) {
+ printk("%s: Bogus packet size %d.\n", dev->name, size);
+ if (size > 10000)
+ adapter_init(dev);
+ return;
+ }
+
+ skb = dev_alloc_skb(size+2);
+ sti();
+ if (skb == NULL) {
+ printk("%s: Couldn't allocate a sk_buff of size %d.\n",
+ dev->name, size);
+ return;
+ }
+ /* else */
+
+ skb->dev = dev;
+ skb_reserve(skb,2); /* Align */
+
+ /* 'skb->data' points to the start of sk_buff data area. */
+ buffer = skb_put(skb,size);
+
+ /* copy the packet into the buffer */
+ de600_setup_address(read_from, RW_ADDR);
+ for (i = size; i > 0; --i, ++buffer)
+ *buffer = de600_read_byte(READ_DATA, dev);
+
+ ((struct netstats *)(dev->priv))->rx_packets++; /* count all receives */
+
+ skb->protocol=eth_type_trans(skb,dev);
+
+ netif_rx(skb);
+ /*
+ * If any worth-while packets have been received, netif_rx()
+ * has done a mark_bh(INET_BH) for us and will work on them
+ * when we get to the bottom-half routine.
+ */
+}
+
+int
+de600_probe(struct device *dev)
+{
+ int i;
+ static struct netstats de600_netstats;
+ /*dev->priv = kmalloc(sizeof(struct netstats), GFP_KERNEL);*/
+
+ printk("%s: D-Link DE-600 pocket adapter", dev->name);
+ /* Alpha testers must have the version number to report bugs. */
+ if (de600_debug > 1)
+ printk(version);
+
+ /* probe for adapter */
+ rx_page = 0;
+ select_nic();
+ (void)de600_read_status(dev);
+ de600_put_command(RESET);
+ de600_put_command(STOP_RESET);
+ if (de600_read_status(dev) & 0xf0) {
+ printk(": not at I/O %#3x.\n", DATA_PORT);
+ return ENODEV;
+ }
+
+ /*
+ * Maybe we found one,
+ * have to check if it is a D-Link DE-600 adapter...
+ */
+
+ /* Get the adapter ethernet address from the ROM */
+ de600_setup_address(NODE_ADDRESS, RW_ADDR);
+ for (i = 0; i < ETH_ALEN; i++) {
+ dev->dev_addr[i] = de600_read_byte(READ_DATA, dev);
+ dev->broadcast[i] = 0xff;
+ }
+
+ /* Check magic code */
+ if ((dev->dev_addr[1] == 0xde) && (dev->dev_addr[2] == 0x15)) {
+ /* OK, install real address */
+ dev->dev_addr[0] = 0x00;
+ dev->dev_addr[1] = 0x80;
+ dev->dev_addr[2] = 0xc8;
+ dev->dev_addr[3] &= 0x0f;
+ dev->dev_addr[3] |= 0x70;
+ } else {
+ printk(" not identified in the printer port\n");
+ return ENODEV;
+ }
+
+#if 0 /* Not yet */
+ if (check_region(DE600_IO, 3)) {
+ printk(", port 0x%x busy\n", DE600_IO);
+ return EBUSY;
+ }
+#endif
+ request_region(DE600_IO, 3, "de600");
+
+ printk(", Ethernet Address: %02X", dev->dev_addr[0]);
+ for (i = 1; i < ETH_ALEN; i++)
+ printk(":%02X",dev->dev_addr[i]);
+ printk("\n");
+
+ /* Initialize the device structure. */
+ /*dev->priv = kmalloc(sizeof(struct netstats), GFP_KERNEL);*/
+ dev->priv = &de600_netstats;
+
+ memset(dev->priv, 0, sizeof(struct netstats));
+ dev->get_stats = get_stats;
+
+ dev->open = de600_open;
+ dev->stop = de600_close;
+ dev->hard_start_xmit = &de600_start_xmit;
+
+ ether_setup(dev);
+
+ dev->flags&=~IFF_MULTICAST;
+
+ select_prn();
+ return 0;
+}
+
+static int
+adapter_init(struct device *dev)
+{
+ int i;
+ long flags;
+
+ save_flags(flags);
+ cli();
+
+ select_nic();
+ rx_page = 0; /* used by RESET */
+ de600_put_command(RESET);
+ de600_put_command(STOP_RESET);
+#ifdef CHECK_LOST_DE600
+ /* Check if it is still there... */
+ /* Get the some bytes of the adapter ethernet address from the ROM */
+ de600_setup_address(NODE_ADDRESS, RW_ADDR);
+ de600_read_byte(READ_DATA, dev);
+ if ((de600_read_byte(READ_DATA, dev) != 0xde) ||
+ (de600_read_byte(READ_DATA, dev) != 0x15)) {
+ /* was: if (de600_read_status(dev) & 0xf0) { */
+ printk("Something has happened to the DE-600! Please check it"
+#ifdef SHUTDOWN_WHEN_LOST
+ " and do a new ifconfig"
+#endif /* SHUTDOWN_WHEN_LOST */
+ "!\n");
+#ifdef SHUTDOWN_WHEN_LOST
+ /* Goodbye, cruel world... */
+ dev->flags &= ~IFF_UP;
+ de600_close(dev);
+#endif /* SHUTDOWN_WHEN_LOST */
+ was_down = 1;
+ dev->tbusy = 1; /* Transmit busy... */
+ restore_flags(flags);
+ return 1; /* failed */
+ }
+#endif /* CHECK_LOST_DE600 */
+ if (was_down) {
+ printk("Thanks, I feel much better now!\n");
+ was_down = 0;
+ }
+
+ dev->tbusy = 0; /* Transmit busy... */
+ dev->interrupt = 0;
+ tx_fifo_in = 0;
+ tx_fifo_out = 0;
+ free_tx_pages = TX_PAGES;
+
+ /* set the ether address. */
+ de600_setup_address(NODE_ADDRESS, RW_ADDR);
+ for (i = 0; i < ETH_ALEN; i++)
+ de600_put_byte(dev->dev_addr[i]);
+
+ /* where to start saving incoming packets */
+ rx_page = RX_BP | RX_BASE_PAGE;
+ de600_setup_address(MEM_4K, RW_ADDR);
+ /* Enable receiver */
+ de600_put_command(RX_ENABLE);
+ select_prn();
+ restore_flags(flags);
+
+ return 0; /* OK */
+}
+
+#ifdef FAKE_SMALL_MAX
+/*
+ * The new router code (coming soon 8-) ) will fix this properly.
+ */
+#define DE600_MIN_WINDOW 1024
+#define DE600_MAX_WINDOW 2048
+#define DE600_TCP_WINDOW_DIFF 1024
+/*
+ * Copied from "net/inet/sock.c"
+ *
+ * Sets a lower max receive window in order to achieve <= 2
+ * packets arriving at the adapter in fast succession.
+ * (No way that a DE-600 can keep up with a net saturated
+ * with packets homing in on it :-( )
+ *
+ * Since there are only 2 receive buffers in the DE-600
+ * and it takes some time to copy from the adapter,
+ * this is absolutely necessary for any TCP performance whatsoever!
+ *
+ * Note that the returned window info will never be smaller than
+ * DE600_MIN_WINDOW, i.e. 1024
+ * This differs from the standard function, that can return an
+ * arbitrarily small window!
+ */
+#define min(a,b) ((a)<(b)?(a):(b))
+static unsigned long
+de600_rspace(struct sock *sk)
+{
+ int amt;
+
+ if (sk != NULL) {
+/*
+ * Hack! You might want to play with commenting away the following line,
+ * if you know what you do!
+ sk->max_unacked = DE600_MAX_WINDOW - DE600_TCP_WINDOW_DIFF;
+ */
+
+ if (sk->rmem_alloc >= sk->rcvbuf-2*DE600_MIN_WINDOW) return(0);
+ amt = min((sk->rcvbuf-sk->rmem_alloc)/2/*-DE600_MIN_WINDOW*/, DE600_MAX_WINDOW);
+ if (amt < 0) return(0);
+ return(amt);
+ }
+ return(0);
+}
+#endif
+
+#ifdef MODULE
+static char nullname[8];
+static struct device de600_dev = {
+ nullname, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, de600_probe };
+
+int
+init_module(void)
+{
+ if (register_netdev(&de600_dev) != 0)
+ return -EIO;
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ unregister_netdev(&de600_dev);
+ release_region(DE600_IO, 3);
+}
+#endif /* MODULE */
+/*
+ * Local variables:
+ * kernel-compile-command: "gcc -D__KERNEL__ -Ilinux/include -I../../net/inet -Wall -Wstrict-prototypes -O2 -m486 -c de600.c"
+ * module-compile-command: "gcc -D__KERNEL__ -DMODULE -Ilinux/include -I../../net/inet -Wall -Wstrict-prototypes -O2 -m486 -c de600.c"
+ * compile-command: "gcc -D__KERNEL__ -DMODULE -Ilinux/include -I../../net/inet -Wall -Wstrict-prototypes -O2 -m486 -c de600.c"
+ * End:
+ */
diff --git a/i386/i386at/gpl/linux/net/de620.c b/i386/i386at/gpl/linux/net/de620.c
new file mode 100644
index 00000000..2b17c390
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/de620.c
@@ -0,0 +1,1045 @@
+/*
+ * de620.c $Revision: 1.1.1.1 $ BETA
+ *
+ *
+ * Linux driver for the D-Link DE-620 Ethernet pocket adapter.
+ *
+ * Portions (C) Copyright 1993, 1994 by Bjorn Ekwall <bj0rn@blox.se>
+ *
+ * Based on adapter information gathered from DOS packetdriver
+ * sources from D-Link Inc: (Special thanks to Henry Ngai of D-Link.)
+ * Portions (C) Copyright D-Link SYSTEM Inc. 1991, 1992
+ * Copyright, 1988, Russell Nelson, Crynwr Software
+ *
+ * Adapted to the sample network driver core for linux,
+ * written by: Donald Becker <becker@super.org>
+ * (Now at <becker@cesdis.gsfc.nasa.gov>
+ *
+ * Valuable assistance from:
+ * J. Joshua Kopper <kopper@rtsg.mot.com>
+ * Olav Kvittem <Olav.Kvittem@uninett.no>
+ * Germano Caronni <caronni@nessie.cs.id.ethz.ch>
+ * Jeremy Fitzhardinge <jeremy@suite.sw.oz.au>
+ *
+ *****************************************************************************/
+/*
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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.
+ *
+ *****************************************************************************/
+static const char *version =
+ "de620.c: $Revision: 1.1.1.1 $, Bjorn Ekwall <bj0rn@blox.se>\n";
+
+/***********************************************************************
+ *
+ * "Tuning" section.
+ *
+ * Compile-time options: (see below for descriptions)
+ * -DDE620_IO=0x378 (lpt1)
+ * -DDE620_IRQ=7 (lpt1)
+ * -DDE602_DEBUG=...
+ * -DSHUTDOWN_WHEN_LOST
+ * -DCOUNT_LOOPS
+ * -DLOWSPEED
+ * -DREAD_DELAY
+ * -DWRITE_DELAY
+ */
+
+/*
+ * This driver assumes that the printer port is a "normal",
+ * dumb, uni-directional port!
+ * If your port is "fancy" in any way, please try to set it to "normal"
+ * with your BIOS setup. I have no access to machines with bi-directional
+ * ports, so I can't test such a driver :-(
+ * (Yes, I _know_ it is possible to use DE620 with bidirectional ports...)
+ *
+ * There are some clones of DE620 out there, with different names.
+ * If the current driver does not recognize a clone, try to change
+ * the following #define to:
+ *
+ * #define DE620_CLONE 1
+ */
+#define DE620_CLONE 0
+
+/*
+ * If the adapter has problems with high speeds, enable this #define
+ * otherwise full printerport speed will be attempted.
+ *
+ * You can tune the READ_DELAY/WRITE_DELAY below if you enable LOWSPEED
+ *
+#define LOWSPEED
+ */
+
+#ifndef READ_DELAY
+#define READ_DELAY 100 /* adapter internal read delay in 100ns units */
+#endif
+
+#ifndef WRITE_DELAY
+#define WRITE_DELAY 100 /* adapter internal write delay in 100ns units */
+#endif
+
+/*
+ * Enable this #define if you want the adapter to do a "ifconfig down" on
+ * itself when we have detected that something is possibly wrong with it.
+ * The default behaviour is to retry with "adapter_init()" until success.
+ * This should be used for debugging purposes only.
+ *
+#define SHUTDOWN_WHEN_LOST
+ */
+
+/*
+ * Enable debugging by "-DDE620_DEBUG=3" when compiling,
+ * OR in "./CONFIG"
+ * OR by enabling the following #define
+ *
+ * use 0 for production, 1 for verification, >2 for debug
+ *
+#define DE620_DEBUG 3
+ */
+
+#ifdef LOWSPEED
+/*
+ * Enable this #define if you want to see debugging output that show how long
+ * we have to wait before the DE-620 is ready for the next read/write/command.
+ *
+#define COUNT_LOOPS
+ */
+#endif
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include <linux/in.h>
+#include <linux/ptrace.h>
+#include <asm/system.h>
+#include <linux/errno.h>
+
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+/* Constant definitions for the DE-620 registers, commands and bits */
+#include "de620.h"
+
+#define netstats enet_statistics
+typedef unsigned char byte;
+
+/*******************************************************
+ * *
+ * Definition of D-Link DE-620 Ethernet Pocket adapter *
+ * See also "de620.h" *
+ * *
+ *******************************************************/
+#ifndef DE620_IO /* Compile-time configurable */
+#define DE620_IO 0x378
+#endif
+
+#ifndef DE620_IRQ /* Compile-time configurable */
+#define DE620_IRQ 7
+#endif
+
+#define DATA_PORT (dev->base_addr)
+#define STATUS_PORT (dev->base_addr + 1)
+#define COMMAND_PORT (dev->base_addr + 2)
+
+#define RUNT 60 /* Too small Ethernet packet */
+#define GIANT 1514 /* largest legal size packet, no fcs */
+
+#ifdef DE620_DEBUG /* Compile-time configurable */
+#define PRINTK(x) if (de620_debug >= 2) printk x
+#else
+#define DE620_DEBUG 0
+#define PRINTK(x) /**/
+#endif
+
+
+/*
+ * Force media with insmod:
+ * insmod de620.o bnc=1
+ * or
+ * insmod de620.o utp=1
+ *
+ * Force io and/or irq with insmod:
+ * insmod de620.o io=0x378 irq=7
+ *
+ * Make a clone skip the Ethernet-address range check:
+ * insmod de620.o clone=1
+ */
+static int bnc = 0;
+static int utp = 0;
+static int io = DE620_IO;
+static int irq = DE620_IRQ;
+static int clone = DE620_CLONE;
+
+static unsigned int de620_debug = DE620_DEBUG;
+
+/***********************************************
+ * *
+ * Index to functions, as function prototypes. *
+ * *
+ ***********************************************/
+
+/*
+ * Routines used internally. (See also "convenience macros.. below")
+ */
+
+/* Put in the device structure. */
+static int de620_open(struct device *);
+static int de620_close(struct device *);
+static struct netstats *get_stats(struct device *);
+static void de620_set_multicast_list(struct device *);
+static int de620_start_xmit(struct sk_buff *, struct device *);
+
+/* Dispatch from interrupts. */
+static void de620_interrupt(int, struct pt_regs *);
+static int de620_rx_intr(struct device *);
+
+/* Initialization */
+static int adapter_init(struct device *);
+int de620_probe(struct device *);
+static int read_eeprom(struct device *);
+
+
+/*
+ * D-Link driver variables:
+ */
+#define SCR_DEF NIBBLEMODE |INTON | SLEEP | AUTOTX
+#define TCR_DEF RXPB /* not used: | TXSUCINT | T16INT */
+#define DE620_RX_START_PAGE 12 /* 12 pages (=3k) reserved for tx */
+#define DEF_NIC_CMD IRQEN | ICEN | DS1
+
+static volatile byte NIC_Cmd;
+static volatile byte next_rx_page;
+static byte first_rx_page;
+static byte last_rx_page;
+static byte EIPRegister;
+
+static struct nic {
+ byte NodeID[6];
+ byte RAM_Size;
+ byte Model;
+ byte Media;
+ byte SCR;
+} nic_data;
+
+/**********************************************************
+ * *
+ * Convenience macros/functions for D-Link DE-620 adapter *
+ * *
+ **********************************************************/
+#define de620_tx_buffs(dd) (inb(STATUS_PORT) & (TXBF0 | TXBF1))
+#define de620_flip_ds(dd) NIC_Cmd ^= DS0 | DS1; outb(NIC_Cmd, COMMAND_PORT);
+
+/* Check for ready-status, and return a nibble (high 4 bits) for data input */
+#ifdef COUNT_LOOPS
+static int tot_cnt;
+#endif
+static inline byte
+de620_ready(struct device *dev)
+{
+ byte value;
+ register short int cnt = 0;
+
+ while ((((value = inb(STATUS_PORT)) & READY) == 0) && (cnt <= 1000))
+ ++cnt;
+
+#ifdef COUNT_LOOPS
+ tot_cnt += cnt;
+#endif
+ return value & 0xf0; /* nibble */
+}
+
+static inline void
+de620_send_command(struct device *dev, byte cmd)
+{
+ de620_ready(dev);
+ if (cmd == W_DUMMY)
+ outb(NIC_Cmd, COMMAND_PORT);
+
+ outb(cmd, DATA_PORT);
+
+ outb(NIC_Cmd ^ CS0, COMMAND_PORT);
+ de620_ready(dev);
+ outb(NIC_Cmd, COMMAND_PORT);
+}
+
+static inline void
+de620_put_byte(struct device *dev, byte value)
+{
+ /* The de620_ready() makes 7 loops, on the average, on a DX2/66 */
+ de620_ready(dev);
+ outb(value, DATA_PORT);
+ de620_flip_ds(dev);
+}
+
+static inline byte
+de620_read_byte(struct device *dev)
+{
+ byte value;
+
+ /* The de620_ready() makes 7 loops, on the average, on a DX2/66 */
+ value = de620_ready(dev); /* High nibble */
+ de620_flip_ds(dev);
+ value |= de620_ready(dev) >> 4; /* Low nibble */
+ return value;
+}
+
+static inline void
+de620_write_block(struct device *dev, byte *buffer, int count)
+{
+#ifndef LOWSPEED
+ byte uflip = NIC_Cmd ^ (DS0 | DS1);
+ byte dflip = NIC_Cmd;
+#else /* LOWSPEED */
+#ifdef COUNT_LOOPS
+ int bytes = count;
+#endif /* COUNT_LOOPS */
+#endif /* LOWSPEED */
+
+#ifdef LOWSPEED
+#ifdef COUNT_LOOPS
+ tot_cnt = 0;
+#endif /* COUNT_LOOPS */
+ /* No further optimization useful, the limit is in the adapter. */
+ for ( ; count > 0; --count, ++buffer) {
+ de620_put_byte(dev,*buffer);
+ }
+ de620_send_command(dev,W_DUMMY);
+#ifdef COUNT_LOOPS
+ /* trial debug output: loops per byte in de620_ready() */
+ printk("WRITE(%d)\n", tot_cnt/((bytes?bytes:1)));
+#endif /* COUNT_LOOPS */
+#else /* not LOWSPEED */
+ for ( ; count > 0; count -=2) {
+ outb(*buffer++, DATA_PORT);
+ outb(uflip, COMMAND_PORT);
+ outb(*buffer++, DATA_PORT);
+ outb(dflip, COMMAND_PORT);
+ }
+ de620_send_command(dev,W_DUMMY);
+#endif /* LOWSPEED */
+}
+
+static inline void
+de620_read_block(struct device *dev, byte *data, int count)
+{
+#ifndef LOWSPEED
+ byte value;
+ byte uflip = NIC_Cmd ^ (DS0 | DS1);
+ byte dflip = NIC_Cmd;
+#else /* LOWSPEED */
+#ifdef COUNT_LOOPS
+ int bytes = count;
+
+ tot_cnt = 0;
+#endif /* COUNT_LOOPS */
+#endif /* LOWSPEED */
+
+#ifdef LOWSPEED
+ /* No further optimization useful, the limit is in the adapter. */
+ while (count-- > 0) {
+ *data++ = de620_read_byte(dev);
+ de620_flip_ds(dev);
+ }
+#ifdef COUNT_LOOPS
+ /* trial debug output: loops per byte in de620_ready() */
+ printk("READ(%d)\n", tot_cnt/(2*(bytes?bytes:1)));
+#endif /* COUNT_LOOPS */
+#else /* not LOWSPEED */
+ while (count-- > 0) {
+ value = inb(STATUS_PORT) & 0xf0; /* High nibble */
+ outb(uflip, COMMAND_PORT);
+ *data++ = value | inb(STATUS_PORT) >> 4; /* Low nibble */
+ outb(dflip , COMMAND_PORT);
+ }
+#endif /* LOWSPEED */
+}
+
+static inline void
+de620_set_delay(struct device *dev)
+{
+ de620_ready(dev);
+ outb(W_DFR, DATA_PORT);
+ outb(NIC_Cmd ^ CS0, COMMAND_PORT);
+
+ de620_ready(dev);
+#ifdef LOWSPEED
+ outb(WRITE_DELAY, DATA_PORT);
+#else
+ outb(0, DATA_PORT);
+#endif
+ de620_flip_ds(dev);
+
+ de620_ready(dev);
+#ifdef LOWSPEED
+ outb(READ_DELAY, DATA_PORT);
+#else
+ outb(0, DATA_PORT);
+#endif
+ de620_flip_ds(dev);
+}
+
+static inline void
+de620_set_register(struct device *dev, byte reg, byte value)
+{
+ de620_ready(dev);
+ outb(reg, DATA_PORT);
+ outb(NIC_Cmd ^ CS0, COMMAND_PORT);
+
+ de620_put_byte(dev, value);
+}
+
+static inline byte
+de620_get_register(struct device *dev, byte reg)
+{
+ byte value;
+
+ de620_send_command(dev,reg);
+ value = de620_read_byte(dev);
+ de620_send_command(dev,W_DUMMY);
+
+ return value;
+}
+
+/*********************************************************************
+ *
+ * Open/initialize the board.
+ *
+ * This routine should set everything up anew at each open, even
+ * registers that "should" only need to be set once at boot, so that
+ * there is a non-reboot way to recover if something goes wrong.
+ *
+ */
+static int
+de620_open(struct device *dev)
+{
+ if (request_irq(dev->irq, de620_interrupt, 0, "de620")) {
+ printk ("%s: unable to get IRQ %d\n", dev->name, dev->irq);
+ return 1;
+ }
+ irq2dev_map[dev->irq] = dev;
+
+ MOD_INC_USE_COUNT;
+ if (adapter_init(dev)) {
+ return 1;
+ }
+ dev->start = 1;
+ return 0;
+}
+
+/************************************************
+ *
+ * The inverse routine to de620_open().
+ *
+ */
+static int
+de620_close(struct device *dev)
+{
+ /* disable recv */
+ de620_set_register(dev, W_TCR, RXOFF);
+
+ free_irq(dev->irq);
+ irq2dev_map[dev->irq] = NULL;
+
+ dev->start = 0;
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+/*********************************************
+ *
+ * Return current statistics
+ *
+ */
+static struct netstats *
+get_stats(struct device *dev)
+{
+ return (struct netstats *)(dev->priv);
+}
+
+/*********************************************
+ *
+ * Set or clear the multicast filter for this adaptor.
+ * (no real multicast implemented for the DE-620, but she can be promiscuous...)
+ *
+ */
+
+static void de620_set_multicast_list(struct device *dev)
+{
+ if (dev->mc_count || dev->flags&(IFF_ALLMULTI|IFF_PROMISC))
+ { /* Enable promiscuous mode */
+ /*
+ * We must make the kernel realise we had to move
+ * into promisc mode or we start all out war on
+ * the cable. - AC
+ */
+ dev->flags|=IFF_PROMISC;
+
+ de620_set_register(dev, W_TCR, (TCR_DEF & ~RXPBM) | RXALL);
+ }
+ else
+ { /* Disable promiscuous mode, use normal mode */
+ de620_set_register(dev, W_TCR, TCR_DEF);
+ }
+}
+
+/*******************************************************
+ *
+ * Copy a buffer to the adapter transmit page memory.
+ * Start sending.
+ */
+static int
+de620_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+ unsigned long flags;
+ int len;
+ int tickssofar;
+ byte *buffer = skb->data;
+ byte using_txbuf;
+
+ /*
+ * If some higher layer thinks we've missed a
+ * tx-done interrupt we are passed NULL.
+ * Caution: dev_tint() handles the cli()/sti() itself.
+ */
+
+ if (skb == NULL) {
+ dev_tint(dev);
+ return 0;
+ }
+
+ using_txbuf = de620_tx_buffs(dev); /* Peek at the adapter */
+ dev->tbusy = (using_txbuf == (TXBF0 | TXBF1)); /* Boolean! */
+
+ if (dev->tbusy) { /* Do timeouts, to avoid hangs. */
+ tickssofar = jiffies - dev->trans_start;
+
+ if (tickssofar < 5)
+ return 1;
+
+ /* else */
+ printk("%s: transmit timed out (%d), %s?\n",
+ dev->name,
+ tickssofar,
+ "network cable problem"
+ );
+ /* Restart the adapter. */
+ if (adapter_init(dev)) /* maybe close it */
+ return 1;
+ }
+
+ if ((len = skb->len) < RUNT)
+ len = RUNT;
+ if (len & 1) /* send an even number of bytes */
+ ++len;
+
+ /* Start real output */
+ save_flags(flags);
+ cli();
+
+ PRINTK(("de620_start_xmit: len=%d, bufs 0x%02x\n",
+ (int)skb->len, using_txbuf));
+
+ /* select a free tx buffer. if there is one... */
+ switch (using_txbuf) {
+ default: /* both are free: use TXBF0 */
+ case TXBF1: /* use TXBF0 */
+ de620_send_command(dev,W_CR | RW0);
+ using_txbuf |= TXBF0;
+ break;
+
+ case TXBF0: /* use TXBF1 */
+ de620_send_command(dev,W_CR | RW1);
+ using_txbuf |= TXBF1;
+ break;
+
+ case (TXBF0 | TXBF1): /* NONE!!! */
+ printk("de620: Ouch! No tx-buffer available!\n");
+ restore_flags(flags);
+ return 1;
+ break;
+ }
+ de620_write_block(dev, buffer, len);
+
+ dev->trans_start = jiffies;
+ dev->tbusy = (using_txbuf == (TXBF0 | TXBF1)); /* Boolean! */
+
+ ((struct netstats *)(dev->priv))->tx_packets++;
+
+ restore_flags(flags); /* interrupts maybe back on */
+
+ dev_kfree_skb (skb, FREE_WRITE);
+
+ return 0;
+}
+
+/*****************************************************
+ *
+ * Handle the network interface interrupts.
+ *
+ */
+static void
+de620_interrupt(int irq_in, struct pt_regs *regs)
+{
+ struct device *dev = irq2dev_map[irq_in];
+ byte irq_status;
+ int bogus_count = 0;
+ int again = 0;
+
+ /* This might be deleted now, no crummy drivers present :-) Or..? */
+ if ((dev == NULL) || (irq != irq_in)) {
+ printk("%s: bogus interrupt %d\n", dev?dev->name:"de620", irq_in);
+ return;
+ }
+
+ cli();
+ dev->interrupt = 1;
+
+ /* Read the status register (_not_ the status port) */
+ irq_status = de620_get_register(dev, R_STS);
+
+ PRINTK(("de620_interrupt (%2.2X)\n", irq_status));
+
+ if (irq_status & RXGOOD) {
+ do {
+ again = de620_rx_intr(dev);
+ PRINTK(("again=%d\n", again));
+ }
+ while (again && (++bogus_count < 100));
+ }
+
+ dev->tbusy = (de620_tx_buffs(dev) == (TXBF0 | TXBF1)); /* Boolean! */
+
+ dev->interrupt = 0;
+ sti();
+ return;
+}
+
+/**************************************
+ *
+ * Get a packet from the adapter
+ *
+ * Send it "upstairs"
+ *
+ */
+static int
+de620_rx_intr(struct device *dev)
+{
+ struct header_buf {
+ byte status;
+ byte Rx_NextPage;
+ unsigned short Rx_ByteCount;
+ } header_buf;
+ struct sk_buff *skb;
+ int size;
+ byte *buffer;
+ byte pagelink;
+ byte curr_page;
+
+ PRINTK(("de620_rx_intr: next_rx_page = %d\n", next_rx_page));
+
+ /* Tell the adapter that we are going to read data, and from where */
+ de620_send_command(dev, W_CR | RRN);
+ de620_set_register(dev, W_RSA1, next_rx_page);
+ de620_set_register(dev, W_RSA0, 0);
+
+ /* Deep breath, and away we goooooo */
+ de620_read_block(dev, (byte *)&header_buf, sizeof(struct header_buf));
+ PRINTK(("page status=0x%02x, nextpage=%d, packetsize=%d\n",
+ header_buf.status, header_buf.Rx_NextPage, header_buf.Rx_ByteCount));
+
+ /* Plausible page header? */
+ pagelink = header_buf.Rx_NextPage;
+ if ((pagelink < first_rx_page) || (last_rx_page < pagelink)) {
+ /* Ouch... Forget it! Skip all and start afresh... */
+ printk("%s: Ring overrun? Restoring...\n", dev->name);
+ /* You win some, you loose some. And sometimes plenty... */
+ adapter_init(dev);
+ ((struct netstats *)(dev->priv))->rx_over_errors++;
+ return 0;
+ }
+
+ /* OK, this look good, so far. Let's see if it's consistent... */
+ /* Let's compute the start of the next packet, based on where we are */
+ pagelink = next_rx_page +
+ ((header_buf.Rx_ByteCount + (4 - 1 + 0x100)) >> 8);
+
+ /* Are we going to wrap around the page counter? */
+ if (pagelink > last_rx_page)
+ pagelink -= (last_rx_page - first_rx_page + 1);
+
+ /* Is the _computed_ next page number equal to what the adapter says? */
+ if (pagelink != header_buf.Rx_NextPage) {
+ /* Naah, we'll skip this packet. Probably bogus data as well */
+ printk("%s: Page link out of sync! Restoring...\n", dev->name);
+ next_rx_page = header_buf.Rx_NextPage; /* at least a try... */
+ de620_send_command(dev, W_DUMMY);
+ de620_set_register(dev, W_NPRF, next_rx_page);
+ ((struct netstats *)(dev->priv))->rx_over_errors++;
+ return 0;
+ }
+ next_rx_page = pagelink;
+
+ size = header_buf.Rx_ByteCount - 4;
+ if ((size < RUNT) || (GIANT < size)) {
+ printk("%s: Illegal packet size: %d!\n", dev->name, size);
+ }
+ else { /* Good packet? */
+ skb = dev_alloc_skb(size+2);
+ if (skb == NULL) { /* Yeah, but no place to put it... */
+ printk("%s: Couldn't allocate a sk_buff of size %d.\n",
+ dev->name, size);
+ ((struct netstats *)(dev->priv))->rx_dropped++;
+ }
+ else { /* Yep! Go get it! */
+ skb_reserve(skb,2); /* Align */
+ skb->dev = dev;
+ skb->free = 1;
+ /* skb->data points to the start of sk_buff data area */
+ buffer = skb_put(skb,size);
+ /* copy the packet into the buffer */
+ de620_read_block(dev, buffer, size);
+ PRINTK(("Read %d bytes\n", size));
+ skb->protocol=eth_type_trans(skb,dev);
+ netif_rx(skb); /* deliver it "upstairs" */
+ /* count all receives */
+ ((struct netstats *)(dev->priv))->rx_packets++;
+ }
+ }
+
+ /* Let's peek ahead to see if we have read the last current packet */
+ /* NOTE! We're _not_ checking the 'EMPTY'-flag! This seems better... */
+ curr_page = de620_get_register(dev, R_CPR);
+ de620_set_register(dev, W_NPRF, next_rx_page);
+ PRINTK(("next_rx_page=%d CPR=%d\n", next_rx_page, curr_page));
+
+ return (next_rx_page != curr_page); /* That was slightly tricky... */
+}
+
+/*********************************************
+ *
+ * Reset the adapter to a known state
+ *
+ */
+static int
+adapter_init(struct device *dev)
+{
+ int i;
+ static int was_down = 0;
+
+ if ((nic_data.Model == 3) || (nic_data.Model == 0)) { /* CT */
+ EIPRegister = NCTL0;
+ if (nic_data.Media != 1)
+ EIPRegister |= NIS0; /* not BNC */
+ }
+ else if (nic_data.Model == 2) { /* UTP */
+ EIPRegister = NCTL0 | NIS0;
+ }
+
+ if (utp)
+ EIPRegister = NCTL0 | NIS0;
+ if (bnc)
+ EIPRegister = NCTL0;
+
+ de620_send_command(dev, W_CR | RNOP | CLEAR);
+ de620_send_command(dev, W_CR | RNOP);
+
+ de620_set_register(dev, W_SCR, SCR_DEF);
+ /* disable recv to wait init */
+ de620_set_register(dev, W_TCR, RXOFF);
+
+ /* Set the node ID in the adapter */
+ for (i = 0; i < 6; ++i) { /* W_PARn = 0xaa + n */
+ de620_set_register(dev, W_PAR0 + i, dev->dev_addr[i]);
+ }
+
+ de620_set_register(dev, W_EIP, EIPRegister);
+
+ next_rx_page = first_rx_page = DE620_RX_START_PAGE;
+ if (nic_data.RAM_Size)
+ last_rx_page = nic_data.RAM_Size - 1;
+ else /* 64k RAM */
+ last_rx_page = 255;
+
+ de620_set_register(dev, W_SPR, first_rx_page); /* Start Page Register*/
+ de620_set_register(dev, W_EPR, last_rx_page); /* End Page Register */
+ de620_set_register(dev, W_CPR, first_rx_page);/*Current Page Register*/
+ de620_send_command(dev, W_NPR | first_rx_page); /* Next Page Register*/
+ de620_send_command(dev, W_DUMMY);
+ de620_set_delay(dev);
+
+ /* Final sanity check: Anybody out there? */
+ /* Let's hope some bits from the statusregister make a good check */
+#define CHECK_MASK ( 0 | TXSUC | T16 | 0 | RXCRC | RXSHORT | 0 | 0 )
+#define CHECK_OK ( 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 )
+ /* success: X 0 0 X 0 0 X X */
+ /* ignore: EEDI RXGOOD COLS LNKS*/
+
+ if (((i = de620_get_register(dev, R_STS)) & CHECK_MASK) != CHECK_OK) {
+ printk("Something has happened to the DE-620! Please check it"
+#ifdef SHUTDOWN_WHEN_LOST
+ " and do a new ifconfig"
+#endif
+ "! (%02x)\n", i);
+#ifdef SHUTDOWN_WHEN_LOST
+ /* Goodbye, cruel world... */
+ dev->flags &= ~IFF_UP;
+ de620_close(dev);
+#endif
+ was_down = 1;
+ return 1; /* failed */
+ }
+ if (was_down) {
+ printk("Thanks, I feel much better now!\n");
+ was_down = 0;
+ }
+
+ /* All OK, go ahead... */
+ de620_set_register(dev, W_TCR, TCR_DEF);
+
+ return 0; /* all ok */
+}
+
+/******************************************************************************
+ *
+ * Only start-up code below
+ *
+ */
+/****************************************
+ *
+ * Check if there is a DE-620 connected
+ */
+int
+de620_probe(struct device *dev)
+{
+ static struct netstats de620_netstats;
+ int i;
+ byte checkbyte = 0xa5;
+
+ /*
+ * This is where the base_addr and irq gets set.
+ * Tunable at compile-time and insmod-time
+ */
+ dev->base_addr = io;
+ dev->irq = irq;
+
+ if (de620_debug)
+ printk(version);
+
+ printk("D-Link DE-620 pocket adapter");
+
+ /* Initially, configure basic nibble mode, so we can read the EEPROM */
+ NIC_Cmd = DEF_NIC_CMD;
+ de620_set_register(dev, W_EIP, EIPRegister);
+
+ /* Anybody out there? */
+ de620_set_register(dev, W_CPR, checkbyte);
+ checkbyte = de620_get_register(dev, R_CPR);
+
+ if ((checkbyte != 0xa5) || (read_eeprom(dev) != 0)) {
+ printk(" not identified in the printer port\n");
+ return ENODEV;
+ }
+
+#if 0 /* Not yet */
+ if (check_region(dev->base_addr, 3)) {
+ printk(", port 0x%x busy\n", dev->base_addr);
+ return EBUSY;
+ }
+#endif
+ request_region(dev->base_addr, 3, "de620");
+
+ /* else, got it! */
+ printk(", Ethernet Address: %2.2X",
+ dev->dev_addr[0] = nic_data.NodeID[0]);
+ for (i = 1; i < ETH_ALEN; i++) {
+ printk(":%2.2X", dev->dev_addr[i] = nic_data.NodeID[i]);
+ dev->broadcast[i] = 0xff;
+ }
+
+ printk(" (%dk RAM,",
+ (nic_data.RAM_Size) ? (nic_data.RAM_Size >> 2) : 64);
+
+ if (nic_data.Media == 1)
+ printk(" BNC)\n");
+ else
+ printk(" UTP)\n");
+
+ /* Initialize the device structure. */
+ /*dev->priv = kmalloc(sizeof(struct netstats), GFP_KERNEL);*/
+ dev->priv = &de620_netstats;
+
+ memset(dev->priv, 0, sizeof(struct netstats));
+ dev->get_stats = get_stats;
+ dev->open = de620_open;
+ dev->stop = de620_close;
+ dev->hard_start_xmit = &de620_start_xmit;
+ dev->set_multicast_list = &de620_set_multicast_list;
+ /* base_addr and irq are already set, see above! */
+
+ ether_setup(dev);
+
+ /* dump eeprom */
+ if (de620_debug) {
+ printk("\nEEPROM contents:\n");
+ printk("RAM_Size = 0x%02X\n", nic_data.RAM_Size);
+ printk("NodeID = %02X:%02X:%02X:%02X:%02X:%02X\n",
+ nic_data.NodeID[0], nic_data.NodeID[1],
+ nic_data.NodeID[2], nic_data.NodeID[3],
+ nic_data.NodeID[4], nic_data.NodeID[5]);
+ printk("Model = %d\n", nic_data.Model);
+ printk("Media = %d\n", nic_data.Media);
+ printk("SCR = 0x%02x\n", nic_data.SCR);
+ }
+
+ return 0;
+}
+
+/**********************************
+ *
+ * Read info from on-board EEPROM
+ *
+ * Note: Bitwise serial I/O to/from the EEPROM vi the status _register_!
+ */
+#define sendit(dev,data) de620_set_register(dev, W_EIP, data | EIPRegister);
+
+static unsigned short
+ReadAWord(struct device *dev, int from)
+{
+ unsigned short data;
+ int nbits;
+
+ /* cs [__~~] SET SEND STATE */
+ /* di [____] */
+ /* sck [_~~_] */
+ sendit(dev, 0); sendit(dev, 1); sendit(dev, 5); sendit(dev, 4);
+
+ /* Send the 9-bit address from where we want to read the 16-bit word */
+ for (nbits = 9; nbits > 0; --nbits, from <<= 1) {
+ if (from & 0x0100) { /* bit set? */
+ /* cs [~~~~] SEND 1 */
+ /* di [~~~~] */
+ /* sck [_~~_] */
+ sendit(dev, 6); sendit(dev, 7); sendit(dev, 7); sendit(dev, 6);
+ }
+ else {
+ /* cs [~~~~] SEND 0 */
+ /* di [____] */
+ /* sck [_~~_] */
+ sendit(dev, 4); sendit(dev, 5); sendit(dev, 5); sendit(dev, 4);
+ }
+ }
+
+ /* Shift in the 16-bit word. The bits appear serially in EEDI (=0x80) */
+ for (data = 0, nbits = 16; nbits > 0; --nbits) {
+ /* cs [~~~~] SEND 0 */
+ /* di [____] */
+ /* sck [_~~_] */
+ sendit(dev, 4); sendit(dev, 5); sendit(dev, 5); sendit(dev, 4);
+ data = (data << 1) | ((de620_get_register(dev, R_STS) & EEDI) >> 7);
+ }
+ /* cs [____] RESET SEND STATE */
+ /* di [____] */
+ /* sck [_~~_] */
+ sendit(dev, 0); sendit(dev, 1); sendit(dev, 1); sendit(dev, 0);
+
+ return data;
+}
+
+static int
+read_eeprom(struct device *dev)
+{
+ unsigned short wrd;
+
+ /* D-Link Ethernet addresses are in the series 00:80:c8:7X:XX:XX:XX */
+ wrd = ReadAWord(dev, 0x1aa); /* bytes 0 + 1 of NodeID */
+ if (!clone && (wrd != htons(0x0080))) /* Valid D-Link ether sequence? */
+ return -1; /* Nope, not a DE-620 */
+ nic_data.NodeID[0] = wrd & 0xff;
+ nic_data.NodeID[1] = wrd >> 8;
+
+ wrd = ReadAWord(dev, 0x1ab); /* bytes 2 + 3 of NodeID */
+ if (!clone && ((wrd & 0xff) != 0xc8)) /* Valid D-Link ether sequence? */
+ return -1; /* Nope, not a DE-620 */
+ nic_data.NodeID[2] = wrd & 0xff;
+ nic_data.NodeID[3] = wrd >> 8;
+
+ wrd = ReadAWord(dev, 0x1ac); /* bytes 4 + 5 of NodeID */
+ nic_data.NodeID[4] = wrd & 0xff;
+ nic_data.NodeID[5] = wrd >> 8;
+
+ wrd = ReadAWord(dev, 0x1ad); /* RAM size in pages (256 bytes). 0 = 64k */
+ nic_data.RAM_Size = (wrd >> 8);
+
+ wrd = ReadAWord(dev, 0x1ae); /* hardware model (CT = 3) */
+ nic_data.Model = (wrd & 0xff);
+
+ wrd = ReadAWord(dev, 0x1af); /* media (indicates BNC/UTP) */
+ nic_data.Media = (wrd & 0xff);
+
+ wrd = ReadAWord(dev, 0x1a8); /* System Configuration Register */
+ nic_data.SCR = (wrd >> 8);
+
+ return 0; /* no errors */
+}
+
+/******************************************************************************
+ *
+ * Loadable module skeleton
+ *
+ */
+#ifdef MODULE
+static char nullname[8] = "";
+static struct device de620_dev = {
+ nullname, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, de620_probe };
+
+int
+init_module(void)
+{
+ if (register_netdev(&de620_dev) != 0)
+ return -EIO;
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ unregister_netdev(&de620_dev);
+ release_region(de620_dev.base_addr, 3);
+}
+#endif /* MODULE */
+
+/*
+ * (add '-DMODULE' when compiling as loadable module)
+ *
+ * compile-command:
+ * gcc -D__KERNEL__ -Wall -Wstrict-prototypes -O2 \
+ * -fomit-frame-pointer -m486 \
+ * -I/usr/src/linux/include -I../../net/inet -c de620.c
+*/
+/*
+ * Local variables:
+ * kernel-compile-command: "gcc -D__KERNEL__ -Ilinux/include -I../../net/inet -Wall -Wstrict-prototypes -O2 -m486 -c de620.c"
+ * module-compile-command: "gcc -D__KERNEL__ -DMODULE -Ilinux/include -I../../net/inet -Wall -Wstrict-prototypes -O2 -m486 -c de620.c"
+ * compile-command: "gcc -D__KERNEL__ -DMODULE -Ilinux/include -I../../net/inet -Wall -Wstrict-prototypes -O2 -m486 -c de620.c"
+ * End:
+ */
diff --git a/i386/i386at/gpl/linux/net/de620.h b/i386/i386at/gpl/linux/net/de620.h
new file mode 100644
index 00000000..e8d9a88f
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/de620.h
@@ -0,0 +1,117 @@
+/*********************************************************
+ * *
+ * Definition of D-Link DE-620 Ethernet Pocket adapter *
+ * *
+ *********************************************************/
+
+/* DE-620's CMD port Command */
+#define CS0 0x08 /* 1->0 command strobe */
+#define ICEN 0x04 /* 0=enable DL3520 host interface */
+#define DS0 0x02 /* 1->0 data strobe 0 */
+#define DS1 0x01 /* 1->0 data strobe 1 */
+
+#define WDIR 0x20 /* general 0=read 1=write */
+#define RDIR 0x00 /* (not 100% confirm ) */
+#define PS2WDIR 0x00 /* ps/2 mode 1=read, 0=write */
+#define PS2RDIR 0x20
+
+#define IRQEN 0x10 /* 1 = enable printer IRQ line */
+#define SELECTIN 0x08 /* 1 = select printer */
+#define INITP 0x04 /* 0 = initial printer */
+#define AUTOFEED 0x02 /* 1 = printer auto form feed */
+#define STROBE 0x01 /* 0->1 data strobe */
+
+#define RESET 0x08
+#define NIS0 0x20 /* 0 = BNC, 1 = UTP */
+#define NCTL0 0x10
+
+/* DE-620 DIC Command */
+#define W_DUMMY 0x00 /* DIC reserved command */
+#define W_CR 0x20 /* DIC write command register */
+#define W_NPR 0x40 /* DIC write Next Page Register */
+#define W_TBR 0x60 /* DIC write Tx Byte Count 1 reg */
+#define W_RSA 0x80 /* DIC write Remote Start Addr 1 */
+
+/* DE-620's STAT port bits 7-4 */
+#define EMPTY 0x80 /* 1 = receive buffer empty */
+#define INTLEVEL 0x40 /* 1 = interrupt level is high */
+#define TXBF1 0x20 /* 1 = transmit buffer 1 is in use */
+#define TXBF0 0x10 /* 1 = transmit buffer 0 is in use */
+#define READY 0x08 /* 1 = h/w ready to accept cmd/data */
+
+/* IDC 1 Command */
+#define W_RSA1 0xa0 /* write remote start address 1 */
+#define W_RSA0 0xa1 /* write remote start address 0 */
+#define W_NPRF 0xa2 /* write next page register NPR15-NPR8 */
+#define W_DFR 0xa3 /* write delay factor register */
+#define W_CPR 0xa4 /* write current page register */
+#define W_SPR 0xa5 /* write start page register */
+#define W_EPR 0xa6 /* write end page register */
+#define W_SCR 0xa7 /* write system configuration register */
+#define W_TCR 0xa8 /* write Transceiver Configuration reg */
+#define W_EIP 0xa9 /* write EEPM Interface port */
+#define W_PAR0 0xaa /* write physical address register 0 */
+#define W_PAR1 0xab /* write physical address register 1 */
+#define W_PAR2 0xac /* write physical address register 2 */
+#define W_PAR3 0xad /* write physical address register 3 */
+#define W_PAR4 0xae /* write physical address register 4 */
+#define W_PAR5 0xaf /* write physical address register 5 */
+
+/* IDC 2 Command */
+#define R_STS 0xc0 /* read status register */
+#define R_CPR 0xc1 /* read current page register */
+#define R_BPR 0xc2 /* read boundary page register */
+#define R_TDR 0xc3 /* read time domain reflectometry reg */
+
+/* STATUS Register */
+#define EEDI 0x80 /* EEPM DO pin */
+#define TXSUC 0x40 /* tx success */
+#define T16 0x20 /* tx fail 16 times */
+#define TS1 0x40 /* 0=Tx success, 1=T16 */
+#define TS0 0x20 /* 0=Tx success, 1=T16 */
+#define RXGOOD 0x10 /* rx a good packet */
+#define RXCRC 0x08 /* rx a CRC error packet */
+#define RXSHORT 0x04 /* rx a short packet */
+#define COLS 0x02 /* coaxial collision status */
+#define LNKS 0x01 /* UTP link status */
+
+/* Command Register */
+#define CLEAR 0x10 /* reset part of hardware */
+#define NOPER 0x08 /* No Operation */
+#define RNOP 0x08
+#define RRA 0x06 /* After RR then auto-advance NPR & BPR(=NPR-1) */
+#define RRN 0x04 /* Normal Remote Read mode */
+#define RW1 0x02 /* Remote Write tx buffer 1 ( page 6 - 11 ) */
+#define RW0 0x00 /* Remote Write tx buffer 0 ( page 0 - 5 ) */
+#define TXEN 0x01 /* 0->1 tx enable */
+
+/* System Configuration Register */
+#define TESTON 0x80 /* test host data transfer reliability */
+#define SLEEP 0x40 /* sleep mode */
+#if 0
+#define FASTMODE 0x04 /* fast mode for intel 82360SL fast mode */
+#define BYTEMODE 0x02 /* byte mode */
+#else
+#define FASTMODE 0x20 /* fast mode for intel 82360SL fast mode */
+#define BYTEMODE 0x10 /* byte mode */
+#endif
+#define NIBBLEMODE 0x00 /* nibble mode */
+#define IRQINV 0x08 /* turn off IRQ line inverter */
+#define IRQNML 0x00 /* turn on IRQ line inverter */
+#define INTON 0x04
+#define AUTOFFSET 0x02 /* auto shift address to TPR+12 */
+#define AUTOTX 0x01 /* auto tx when leave RW mode */
+
+/* Transceiver Configuration Register */
+#define JABBER 0x80 /* generate jabber condition */
+#define TXSUCINT 0x40 /* enable tx success interrupt */
+#define T16INT 0x20 /* enable T16 interrupt */
+#define RXERRPKT 0x10 /* accept CRC error or short packet */
+#define EXTERNALB2 0x0C /* external loopback 2 */
+#define EXTERNALB1 0x08 /* external loopback 1 */
+#define INTERNALB 0x04 /* internal loopback */
+#define NMLOPERATE 0x00 /* normal operation */
+#define RXPBM 0x03 /* rx physical, broadcast, multicast */
+#define RXPB 0x02 /* rx physical, broadcast */
+#define RXALL 0x01 /* rx all packet */
+#define RXOFF 0x00 /* rx disable */
diff --git a/i386/i386at/gpl/linux/net/depca.c b/i386/i386at/gpl/linux/net/depca.c
new file mode 100644
index 00000000..ae9a8ca3
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/depca.c
@@ -0,0 +1,1901 @@
+/* depca.c: A DIGITAL DEPCA & EtherWORKS ethernet driver for linux.
+
+ Written 1994, 1995 by David C. Davies.
+
+
+ Copyright 1994 David C. Davies
+ and
+ United States Government
+ (as represented by the Director, National Security Agency).
+
+ Copyright 1995 Digital Equipment Corporation.
+
+
+ This software may be used and distributed according to the terms of
+ the GNU Public License, incorporated herein by reference.
+
+ This driver is written for the Digital Equipment Corporation series
+ of DEPCA and EtherWORKS ethernet cards:
+
+ DEPCA (the original)
+ DE100
+ DE101
+ DE200 Turbo
+ DE201 Turbo
+ DE202 Turbo (TP BNC)
+ DE210
+ DE422 (EISA)
+
+ The driver has been tested on DE100, DE200 and DE202 cards in a
+ relatively busy network. The DE422 has been tested a little.
+
+ This driver will NOT work for the DE203, DE204 and DE205 series of
+ cards, since they have a new custom ASIC in place of the AMD LANCE
+ chip. See the 'ewrk3.c' driver in the Linux source tree for running
+ those cards.
+
+ I have benchmarked the driver with a DE100 at 595kB/s to (542kB/s from)
+ a DECstation 5000/200.
+
+ The author may be reached at davies@wanton.lkg.dec.com or
+ davies@maniac.ultranet.com or Digital Equipment Corporation, 550 King
+ Street, Littleton MA 01460.
+
+ =========================================================================
+
+ The driver was originally based on the 'lance.c' driver from Donald
+ Becker which is included with the standard driver distribution for
+ linux. V0.4 is a complete re-write with only the kernel interface
+ remaining from the original code.
+
+ 1) Lance.c code in /linux/drivers/net/
+ 2) "Ethernet/IEEE 802.3 Family. 1992 World Network Data Book/Handbook",
+ AMD, 1992 [(800) 222-9323].
+ 3) "Am79C90 CMOS Local Area Network Controller for Ethernet (C-LANCE)",
+ AMD, Pub. #17881, May 1993.
+ 4) "Am79C960 PCnet-ISA(tm), Single-Chip Ethernet Controller for ISA",
+ AMD, Pub. #16907, May 1992
+ 5) "DEC EtherWORKS LC Ethernet Controller Owners Manual",
+ Digital Equipment corporation, 1990, Pub. #EK-DE100-OM.003
+ 6) "DEC EtherWORKS Turbo Ethernet Controller Owners Manual",
+ Digital Equipment corporation, 1990, Pub. #EK-DE200-OM.003
+ 7) "DEPCA Hardware Reference Manual", Pub. #EK-DEPCA-PR
+ Digital Equipment Corporation, 1989
+ 8) "DEC EtherWORKS Turbo_(TP BNC) Ethernet Controller Owners Manual",
+ Digital Equipment corporation, 1991, Pub. #EK-DE202-OM.001
+
+
+ Peter Bauer's depca.c (V0.5) was referred to when debugging V0.1 of this
+ driver.
+
+ The original DEPCA card requires that the ethernet ROM address counter
+ be enabled to count and has an 8 bit NICSR. The ROM counter enabling is
+ only done when a 0x08 is read as the first address octet (to minimise
+ the chances of writing over some other hardware's I/O register). The
+ NICSR accesses have been changed to byte accesses for all the cards
+ supported by this driver, since there is only one useful bit in the MSB
+ (remote boot timeout) and it is not used. Also, there is a maximum of
+ only 48kB network RAM for this card. My thanks to Torbjorn Lindh for
+ help debugging all this (and holding my feet to the fire until I got it
+ right).
+
+ The DE200 series boards have on-board 64kB RAM for use as a shared
+ memory network buffer. Only the DE100 cards make use of a 2kB buffer
+ mode which has not been implemented in this driver (only the 32kB and
+ 64kB modes are supported [16kB/48kB for the original DEPCA]).
+
+ At the most only 2 DEPCA cards can be supported on the ISA bus because
+ there is only provision for two I/O base addresses on each card (0x300
+ and 0x200). The I/O address is detected by searching for a byte sequence
+ in the Ethernet station address PROM at the expected I/O address for the
+ Ethernet PROM. The shared memory base address is 'autoprobed' by
+ looking for the self test PROM and detecting the card name. When a
+ second DEPCA is detected, information is placed in the base_addr
+ variable of the next device structure (which is created if necessary),
+ thus enabling ethif_probe initialization for the device. More than 2
+ EISA cards can be supported, but care will be needed assigning the
+ shared memory to ensure that each slot has the correct IRQ, I/O address
+ and shared memory address assigned.
+
+ ************************************************************************
+
+ NOTE: If you are using two ISA DEPCAs, it is important that you assign
+ the base memory addresses correctly. The driver autoprobes I/O 0x300
+ then 0x200. The base memory address for the first device must be less
+ than that of the second so that the auto probe will correctly assign the
+ I/O and memory addresses on the same card. I can't think of a way to do
+ this unambiguously at the moment, since there is nothing on the cards to
+ tie I/O and memory information together.
+
+ I am unable to test 2 cards together for now, so this code is
+ unchecked. All reports, good or bad, are welcome.
+
+ ************************************************************************
+
+ The board IRQ setting must be at an unused IRQ which is auto-probed
+ using Donald Becker's autoprobe routines. DEPCA and DE100 board IRQs are
+ {2,3,4,5,7}, whereas the DE200 is at {5,9,10,11,15}. Note that IRQ2 is
+ really IRQ9 in machines with 16 IRQ lines.
+
+ No 16MB memory limitation should exist with this driver as DMA is not
+ used and the common memory area is in low memory on the network card (my
+ current system has 20MB and I've not had problems yet).
+
+ The ability to load this driver as a loadable module has been added. To
+ utilise this ability, you have to do <8 things:
+
+ 0) have a copy of the loadable modules code installed on your system.
+ 1) copy depca.c from the /linux/drivers/net directory to your favourite
+ temporary directory.
+ 2) if you wish, edit the source code near line 1530 to reflect the I/O
+ address and IRQ you're using (see also 5).
+ 3) compile depca.c, but include -DMODULE in the command line to ensure
+ that the correct bits are compiled (see end of source code).
+ 4) if you are wanting to add a new card, goto 5. Otherwise, recompile a
+ kernel with the depca configuration turned off and reboot.
+ 5) insmod depca.o [irq=7] [io=0x200] [mem=0xd0000] [adapter_name=DE100]
+ [Alan Cox: Changed the code to allow command line irq/io assignments]
+ [Dave Davies: Changed the code to allow command line mem/name
+ assignments]
+ 6) run the net startup bits for your eth?? interface manually
+ (usually /etc/rc.inet[12] at boot time).
+ 7) enjoy!
+
+ Note that autoprobing is not allowed in loadable modules - the system is
+ already up and running and you're messing with interrupts.
+
+ To unload a module, turn off the associated interface
+ 'ifconfig eth?? down' then 'rmmod depca'.
+
+ To assign a base memory address for the shared memory when running as a
+ loadable module, see 5 above. To include the adapter name (if you have
+ no PROM but know the card name) also see 5 above. Note that this last
+ option will not work with kernel built-in depca's.
+
+ The shared memory assignment for a loadable module makes sense to avoid
+ the 'memory autoprobe' picking the wrong shared memory (for the case of
+ 2 depca's in a PC).
+
+
+ TO DO:
+ ------
+
+
+ Revision History
+ ----------------
+
+ Version Date Description
+
+ 0.1 25-jan-94 Initial writing.
+ 0.2 27-jan-94 Added LANCE TX hardware buffer chaining.
+ 0.3 1-feb-94 Added multiple DEPCA support.
+ 0.31 4-feb-94 Added DE202 recognition.
+ 0.32 19-feb-94 Tidy up. Improve multi-DEPCA support.
+ 0.33 25-feb-94 Fix DEPCA ethernet ROM counter enable.
+ Add jabber packet fix from murf@perftech.com
+ and becker@super.org
+ 0.34 7-mar-94 Fix DEPCA max network memory RAM & NICSR access.
+ 0.35 8-mar-94 Added DE201 recognition. Tidied up.
+ 0.351 30-apr-94 Added EISA support. Added DE422 recognition.
+ 0.36 16-may-94 DE422 fix released.
+ 0.37 22-jul-94 Added MODULE support
+ 0.38 15-aug-94 Added DBR ROM switch in depca_close().
+ Multi DEPCA bug fix.
+ 0.38axp 15-sep-94 Special version for Alpha AXP Linux V1.0.
+ 0.381 12-dec-94 Added DE101 recognition, fix multicast bug.
+ 0.382 9-feb-95 Fix recognition bug reported by <bkm@star.rl.ac.uk>.
+ 0.383 22-feb-95 Fix for conflict with VESA SCSI reported by
+ <stromain@alf.dec.com>
+ 0.384 17-mar-95 Fix a ring full bug reported by <bkm@star.rl.ac.uk>
+ 0.385 3-apr-95 Fix a recognition bug reported by
+ <ryan.niemi@lastfrontier.com>
+ 0.386 21-apr-95 Fix the last fix...sorry, must be galloping senility
+ 0.40 25-May-95 Rewrite for portability & updated.
+ ALPHA support from <jestabro@amt.tay1.dec.com>
+ 0.41 26-Jun-95 Added verify_area() calls in depca_ioctl() from
+ suggestion by <heiko@colossus.escape.de>
+ 0.42 27-Dec-95 Add 'mem' shared memory assigment for loadable
+ modules.
+ Add 'adapter_name' for loadable modules when no PROM.
+ Both above from a suggestion by
+ <pchen@woodruffs121.residence.gatech.edu>.
+ Add new multicasting code.
+
+ =========================================================================
+*/
+
+static const char *version = "depca.c:v0.42 95/12/27 davies@wanton.lkg.dec.com\n";
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <asm/segment.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include <linux/time.h>
+#include <linux/types.h>
+#include <linux/unistd.h>
+
+#include "depca.h"
+
+#ifdef DEPCA_DEBUG
+static int depca_debug = DEPCA_DEBUG;
+#else
+static int depca_debug = 1;
+#endif
+
+#define DEPCA_NDA 0xffe0 /* No Device Address */
+
+/*
+** Ethernet PROM defines
+*/
+#define PROBE_LENGTH 32
+#define ETH_PROM_SIG 0xAA5500FFUL
+
+/*
+** Set the number of Tx and Rx buffers. Ensure that the memory requested
+** here is <= to the amount of shared memory set up by the board switches.
+** The number of descriptors MUST BE A POWER OF 2.
+**
+** total_memory = NUM_RX_DESC*(8+RX_BUFF_SZ) + NUM_TX_DESC*(8+TX_BUFF_SZ)
+*/
+#define NUM_RX_DESC 8 /* Number of RX descriptors */
+#define NUM_TX_DESC 8 /* Number of TX descriptors */
+#define RX_BUFF_SZ 1536 /* Buffer size for each Rx buffer */
+#define TX_BUFF_SZ 1536 /* Buffer size for each Tx buffer */
+
+#define CRC_POLYNOMIAL_BE 0x04c11db7UL /* Ethernet CRC, big endian */
+#define CRC_POLYNOMIAL_LE 0xedb88320UL /* Ethernet CRC, little endian */
+
+/*
+** EISA bus defines
+*/
+#define DEPCA_EISA_IO_PORTS 0x0c00 /* I/O port base address, slot 0 */
+#define MAX_EISA_SLOTS 16
+#define EISA_SLOT_INC 0x1000
+
+/*
+** ISA Bus defines
+*/
+#define DEPCA_RAM_BASE_ADDRESSES {0xc0000,0xd0000,0xe0000,0x00000}
+#define DEPCA_IO_PORTS {0x300, 0x200, 0}
+#define DEPCA_TOTAL_SIZE 0x10
+static short mem_chkd = 0;
+
+/*
+** Name <-> Adapter mapping
+*/
+#define DEPCA_SIGNATURE {"DEPCA",\
+ "DE100","DE101",\
+ "DE200","DE201","DE202",\
+ "DE210",\
+ "DE422",\
+ ""}
+static enum {DEPCA, de100, de101, de200, de201, de202, de210, de422, unknown} adapter;
+
+/*
+** Miscellaneous info...
+*/
+#define DEPCA_STRLEN 16
+#define MAX_NUM_DEPCAS 2
+
+/*
+** Memory Alignment. Each descriptor is 4 longwords long. To force a
+** particular alignment on the TX descriptor, adjust DESC_SKIP_LEN and
+** DESC_ALIGN. ALIGN aligns the start address of the private memory area
+** and hence the RX descriptor ring's first entry.
+*/
+#define ALIGN4 ((u_long)4 - 1) /* 1 longword align */
+#define ALIGN8 ((u_long)8 - 1) /* 2 longword (quadword) align */
+#define ALIGN ALIGN8 /* Keep the LANCE happy... */
+
+/*
+** The DEPCA Rx and Tx ring descriptors.
+*/
+struct depca_rx_desc {
+ volatile s32 base;
+ s16 buf_length; /* This length is negative 2's complement! */
+ s16 msg_length; /* This length is "normal". */
+};
+
+struct depca_tx_desc {
+ volatile s32 base;
+ s16 length; /* This length is negative 2's complement! */
+ s16 misc; /* Errors and TDR info */
+};
+
+#define LA_MASK 0x0000ffff /* LANCE address mask for mapping network RAM
+ to LANCE memory address space */
+
+/*
+** The Lance initialization block, described in databook, in common memory.
+*/
+struct depca_init {
+ u16 mode; /* Mode register */
+ u8 phys_addr[ETH_ALEN]; /* Physical ethernet address */
+ u8 mcast_table[8]; /* Multicast Hash Table. */
+ u32 rx_ring; /* Rx ring base pointer & ring length */
+ u32 tx_ring; /* Tx ring base pointer & ring length */
+};
+
+#define DEPCA_PKT_STAT_SZ 16
+#define DEPCA_PKT_BIN_SZ 128 /* Should be >=100 unless you
+ increase DEPCA_PKT_STAT_SZ */
+struct depca_private {
+ char devname[DEPCA_STRLEN]; /* Device Product String */
+ char adapter_name[DEPCA_STRLEN];/* /proc/ioports string */
+ char adapter; /* Adapter type */
+ struct depca_rx_desc *rx_ring; /* Pointer to start of RX descriptor ring */
+ struct depca_tx_desc *tx_ring; /* Pointer to start of TX descriptor ring */
+ struct depca_init init_block;/* Shadow Initialization block */
+ char *rx_memcpy[NUM_RX_DESC]; /* CPU virt address of sh'd memory buffs */
+ char *tx_memcpy[NUM_TX_DESC]; /* CPU virt address of sh'd memory buffs */
+ u_long bus_offset; /* (E)ISA bus address offset vs LANCE */
+ u_long sh_mem; /* Physical start addr of shared mem area */
+ u_long dma_buffs; /* LANCE Rx and Tx buffers start address. */
+ int rx_new, tx_new; /* The next free ring entry */
+ int rx_old, tx_old; /* The ring entries to be free()ed. */
+ struct enet_statistics stats;
+ struct { /* Private stats counters */
+ u32 bins[DEPCA_PKT_STAT_SZ];
+ u32 unicast;
+ u32 multicast;
+ u32 broadcast;
+ u32 excessive_collisions;
+ u32 tx_underruns;
+ u32 excessive_underruns;
+ } pktStats;
+ int txRingMask; /* TX ring mask */
+ int rxRingMask; /* RX ring mask */
+ s32 rx_rlen; /* log2(rxRingMask+1) for the descriptors */
+ s32 tx_rlen; /* log2(txRingMask+1) for the descriptors */
+};
+
+/*
+** The transmit ring full condition is described by the tx_old and tx_new
+** pointers by:
+** tx_old = tx_new Empty ring
+** tx_old = tx_new+1 Full ring
+** tx_old+txRingMask = tx_new Full ring (wrapped condition)
+*/
+#define TX_BUFFS_AVAIL ((lp->tx_old<=lp->tx_new)?\
+ lp->tx_old+lp->txRingMask-lp->tx_new:\
+ lp->tx_old -lp->tx_new-1)
+
+/*
+** Public Functions
+*/
+static int depca_open(struct device *dev);
+static int depca_start_xmit(struct sk_buff *skb, struct device *dev);
+static void depca_interrupt(int irq, struct pt_regs * regs);
+static int depca_close(struct device *dev);
+static int depca_ioctl(struct device *dev, struct ifreq *rq, int cmd);
+static struct enet_statistics *depca_get_stats(struct device *dev);
+static void set_multicast_list(struct device *dev);
+
+/*
+** Private functions
+*/
+static int depca_hw_init(struct device *dev, u_long ioaddr);
+static void depca_init_ring(struct device *dev);
+static int depca_rx(struct device *dev);
+static int depca_tx(struct device *dev);
+
+static void LoadCSRs(struct device *dev);
+static int InitRestartDepca(struct device *dev);
+static void DepcaSignature(char *name, u_long paddr);
+static int DevicePresent(u_long ioaddr);
+static int get_hw_addr(struct device *dev);
+static int EISA_signature(char *name, s32 eisa_id);
+static void SetMulticastFilter(struct device *dev);
+static void isa_probe(struct device *dev, u_long iobase);
+static void eisa_probe(struct device *dev, u_long iobase);
+static struct device *alloc_device(struct device *dev, u_long iobase);
+static int load_packet(struct device *dev, struct sk_buff *skb);
+static void depca_dbg_open(struct device *dev);
+
+#ifdef MODULE
+int init_module(void);
+void cleanup_module(void);
+static int autoprobed = 1, loading_module = 1;
+# else
+static u_char de1xx_irq[] = {2,3,4,5,7,0};
+static u_char de2xx_irq[] = {5,9,10,11,15,0};
+static u_char de422_irq[] = {5,9,10,11,0};
+static u_char *depca_irq;
+static int autoprobed = 0, loading_module = 0;
+#endif /* MODULE */
+
+static char name[DEPCA_STRLEN];
+static int num_depcas = 0, num_eth = 0;
+static int mem=0; /* For loadable module assignment
+ use insmod mem=0x????? .... */
+static char *adapter_name = '\0'; /* If no PROM when loadable module
+ use insmod adapter_name=DE??? ...
+ */
+/*
+** Miscellaneous defines...
+*/
+#define STOP_DEPCA \
+ outw(CSR0, DEPCA_ADDR);\
+ outw(STOP, DEPCA_DATA)
+
+
+
+int depca_probe(struct device *dev)
+{
+ int tmp = num_depcas, status = -ENODEV;
+ u_long iobase = dev->base_addr;
+
+ if ((iobase == 0) && loading_module){
+ printk("Autoprobing is not supported when loading a module based driver.\n");
+ status = -EIO;
+ } else {
+ isa_probe(dev, iobase);
+ eisa_probe(dev, iobase);
+
+ if ((tmp == num_depcas) && (iobase != 0) && loading_module) {
+ printk("%s: depca_probe() cannot find device at 0x%04lx.\n", dev->name,
+ iobase);
+ }
+
+ /*
+ ** Walk the device list to check that at least one device
+ ** initialised OK
+ */
+ for (; (dev->priv == NULL) && (dev->next != NULL); dev = dev->next);
+
+ if (dev->priv) status = 0;
+ if (iobase == 0) autoprobed = 1;
+ }
+
+ return status;
+}
+
+static int
+depca_hw_init(struct device *dev, u_long ioaddr)
+{
+ struct depca_private *lp;
+ int i, j, offset, netRAM, mem_len, status=0;
+ s16 nicsr;
+ u_long mem_start=0, mem_base[] = DEPCA_RAM_BASE_ADDRESSES;
+
+ STOP_DEPCA;
+
+ nicsr = inb(DEPCA_NICSR);
+ nicsr = ((nicsr & ~SHE & ~RBE & ~IEN) | IM);
+ outb(nicsr, DEPCA_NICSR);
+
+ if (inw(DEPCA_DATA) == STOP) {
+ if (mem == 0) {
+ for (; mem_base[mem_chkd]; mem_chkd++) {
+ mem_start = mem_base[mem_chkd];
+ DepcaSignature(name, mem_start);
+ if (*name != '\0') break;
+ }
+ } else {
+ mem_start = mem;
+ if (adapter_name) {
+ strcpy(name, adapter_name);
+ } else{
+ DepcaSignature(name, mem_start);
+ }
+ }
+
+ if ((*name != '\0') && mem_start) { /* found a DEPCA device */
+ dev->base_addr = ioaddr;
+
+ if ((ioaddr&0x0fff)==DEPCA_EISA_IO_PORTS) {/* EISA slot address */
+ printk("%s: %s at 0x%04lx (EISA slot %d)",
+ dev->name, name, ioaddr, (int)((ioaddr>>12)&0x0f));
+ } else { /* ISA port address */
+ printk("%s: %s at 0x%04lx", dev->name, name, ioaddr);
+ }
+
+ printk(", h/w address ");
+ status = get_hw_addr(dev);
+ for (i=0; i<ETH_ALEN - 1; i++) { /* get the ethernet address */
+ printk("%2.2x:", dev->dev_addr[i]);
+ }
+ printk("%2.2x", dev->dev_addr[i]);
+
+ if (status == 0) {
+ /* Set up the maximum amount of network RAM(kB) */
+ netRAM = ((adapter != DEPCA) ? 64 : 48);
+ if ((nicsr & _128KB) && (adapter == de422)) netRAM = 128;
+ offset = 0x0000;
+
+ /* Shared Memory Base Address */
+ if (nicsr & BUF) {
+ offset = 0x8000; /* 32kbyte RAM offset*/
+ nicsr &= ~BS; /* DEPCA RAM in top 32k */
+ netRAM -= 32;
+ }
+ mem_start += offset; /* (E)ISA start address */
+ if ((mem_len = (NUM_RX_DESC*(sizeof(struct depca_rx_desc)+RX_BUFF_SZ) +
+ NUM_TX_DESC*(sizeof(struct depca_tx_desc)+TX_BUFF_SZ) +
+ sizeof(struct depca_init))) <=
+ (netRAM<<10)) {
+ printk(",\n has %dkB RAM at 0x%.5lx", netRAM, mem_start);
+
+ /* Enable the shadow RAM. */
+ if (adapter != DEPCA) {
+ nicsr |= SHE;
+ outb(nicsr, DEPCA_NICSR);
+ }
+
+ /* Define the device private memory */
+ dev->priv = (void *) kmalloc(sizeof(struct depca_private), GFP_KERNEL);
+ if (dev->priv == NULL)
+ return -ENOMEM;
+ lp = (struct depca_private *)dev->priv;
+ memset((char *)dev->priv, 0, sizeof(struct depca_private));
+ lp->adapter = adapter;
+ sprintf(lp->adapter_name,"%s (%s)", name, dev->name);
+ request_region(ioaddr, DEPCA_TOTAL_SIZE, lp->adapter_name);
+
+ /* Initialisation Block */
+ lp->sh_mem = mem_start;
+ mem_start += sizeof(struct depca_init);
+
+ /* Tx & Rx descriptors (aligned to a quadword boundary) */
+ mem_start = (mem_start + ALIGN) & ~ALIGN;
+ lp->rx_ring = (struct depca_rx_desc *)mem_start;
+
+ mem_start += (sizeof(struct depca_rx_desc) * NUM_RX_DESC);
+ lp->tx_ring = (struct depca_tx_desc *)mem_start;
+
+ mem_start += (sizeof(struct depca_tx_desc) * NUM_TX_DESC);
+ lp->bus_offset = mem_start & 0x00ff0000;
+ mem_start &= LA_MASK; /* LANCE re-mapped start address */
+
+ lp->dma_buffs = mem_start;
+
+ /* Finish initialising the ring information. */
+ lp->rxRingMask = NUM_RX_DESC - 1;
+ lp->txRingMask = NUM_TX_DESC - 1;
+
+ /* Calculate Tx/Rx RLEN size for the descriptors. */
+ for (i=0, j = lp->rxRingMask; j>0; i++) {
+ j >>= 1;
+ }
+ lp->rx_rlen = (s32)(i << 29);
+ for (i=0, j = lp->txRingMask; j>0; i++) {
+ j >>= 1;
+ }
+ lp->tx_rlen = (s32)(i << 29);
+
+ /* Load the initialisation block */
+ depca_init_ring(dev);
+
+ /* Initialise the control and status registers */
+ LoadCSRs(dev);
+
+ /* Enable DEPCA board interrupts for autoprobing */
+ nicsr = ((nicsr & ~IM)|IEN);
+ outb(nicsr, DEPCA_NICSR);
+
+ /* To auto-IRQ we enable the initialization-done and DMA err,
+ interrupts. For now we will always get a DMA error. */
+ if (dev->irq < 2) {
+#ifndef MODULE
+ unsigned char irqnum;
+ autoirq_setup(0);
+
+ /* Assign the correct irq list */
+ switch (lp->adapter) {
+ case DEPCA:
+ case de100:
+ case de101:
+ depca_irq = de1xx_irq;
+ break;
+ case de200:
+ case de201:
+ case de202:
+ case de210:
+ depca_irq = de2xx_irq;
+ break;
+ case de422:
+ depca_irq = de422_irq;
+ break;
+ }
+
+ /* Trigger an initialization just for the interrupt. */
+ outw(INEA | INIT, DEPCA_DATA);
+
+ irqnum = autoirq_report(1);
+ if (!irqnum) {
+ printk(" and failed to detect IRQ line.\n");
+ status = -ENXIO;
+ } else {
+ for (dev->irq=0,i=0; (depca_irq[i]) && (!dev->irq); i++) {
+ if (irqnum == depca_irq[i]) {
+ dev->irq = irqnum;
+ printk(" and uses IRQ%d.\n", dev->irq);
+ }
+ }
+
+ if (!dev->irq) {
+ printk(" but incorrect IRQ line detected.\n");
+ status = -ENXIO;
+ }
+ }
+#endif /* MODULE */
+ } else {
+ printk(" and assigned IRQ%d.\n", dev->irq);
+ }
+ if (status) release_region(ioaddr, DEPCA_TOTAL_SIZE);
+ } else {
+ printk(",\n requests %dkB RAM: only %dkB is available!\n",
+ (mem_len>>10), netRAM);
+ status = -ENXIO;
+ }
+ } else {
+ printk(" which has an Ethernet PROM CRC error.\n");
+ status = -ENXIO;
+ }
+ }
+ if (!status) {
+ if (depca_debug > 0) {
+ printk(version);
+ }
+
+ /* The DEPCA-specific entries in the device structure. */
+ dev->open = &depca_open;
+ dev->hard_start_xmit = &depca_start_xmit;
+ dev->stop = &depca_close;
+ dev->get_stats = &depca_get_stats;
+ dev->set_multicast_list = &set_multicast_list;
+ dev->do_ioctl = &depca_ioctl;
+
+ dev->mem_start = 0;
+
+ /* Fill in the generic field of the device structure. */
+ ether_setup(dev);
+ } else { /* Incorrectly initialised hardware */
+ if (dev->priv) {
+ kfree_s(dev->priv, sizeof(struct depca_private));
+ dev->priv = NULL;
+ }
+ }
+ } else {
+ status = -ENXIO;
+ }
+
+ return status;
+}
+
+
+static int
+depca_open(struct device *dev)
+{
+ struct depca_private *lp = (struct depca_private *)dev->priv;
+ u_long ioaddr = dev->base_addr;
+ s16 nicsr;
+ int status = 0;
+
+ irq2dev_map[dev->irq] = dev;
+ STOP_DEPCA;
+ nicsr = inb(DEPCA_NICSR);
+
+ /* Make sure the shadow RAM is enabled */
+ if (adapter != DEPCA) {
+ nicsr |= SHE;
+ outb(nicsr, DEPCA_NICSR);
+ }
+
+ /* Re-initialize the DEPCA... */
+ depca_init_ring(dev);
+ LoadCSRs(dev);
+
+ depca_dbg_open(dev);
+
+ if (request_irq(dev->irq, &depca_interrupt, 0, lp->adapter_name)) {
+ printk("depca_open(): Requested IRQ%d is busy\n",dev->irq);
+ status = -EAGAIN;
+ } else {
+
+ /* Enable DEPCA board interrupts and turn off LED */
+ nicsr = ((nicsr & ~IM & ~LED)|IEN);
+ outb(nicsr, DEPCA_NICSR);
+ outw(CSR0,DEPCA_ADDR);
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+
+ status = InitRestartDepca(dev);
+
+ if (depca_debug > 1){
+ printk("CSR0: 0x%4.4x\n",inw(DEPCA_DATA));
+ printk("nicsr: 0x%02x\n",inb(DEPCA_NICSR));
+ }
+ }
+
+ MOD_INC_USE_COUNT;
+
+ return status;
+}
+
+/* Initialize the lance Rx and Tx descriptor rings. */
+static void
+depca_init_ring(struct device *dev)
+{
+ struct depca_private *lp = (struct depca_private *)dev->priv;
+ u_int i;
+ u_long p;
+
+ /* Lock out other processes whilst setting up the hardware */
+ set_bit(0, (void *)&dev->tbusy);
+
+ lp->rx_new = lp->tx_new = 0;
+ lp->rx_old = lp->tx_old = 0;
+
+ /* Initialize the base addresses and length of each buffer in the ring */
+ for (i = 0; i <= lp->rxRingMask; i++) {
+ writel((p=lp->dma_buffs+i*RX_BUFF_SZ) | R_OWN, &lp->rx_ring[i].base);
+ writew(-RX_BUFF_SZ, &lp->rx_ring[i].buf_length);
+ lp->rx_memcpy[i]=(char *)(p+lp->bus_offset);
+ }
+ for (i = 0; i <= lp->txRingMask; i++) {
+ writel((p=lp->dma_buffs+(i+lp->txRingMask+1)*TX_BUFF_SZ) & 0x00ffffff,
+ &lp->tx_ring[i].base);
+ lp->tx_memcpy[i]=(char *)(p+lp->bus_offset);
+ }
+
+ /* Set up the initialization block */
+ lp->init_block.rx_ring = ((u32)((u_long)lp->rx_ring)&LA_MASK) | lp->rx_rlen;
+ lp->init_block.tx_ring = ((u32)((u_long)lp->tx_ring)&LA_MASK) | lp->tx_rlen;
+
+ SetMulticastFilter(dev);
+
+ for (i = 0; i < ETH_ALEN; i++) {
+ lp->init_block.phys_addr[i] = dev->dev_addr[i];
+ }
+
+ lp->init_block.mode = 0x0000; /* Enable the Tx and Rx */
+
+ return;
+}
+
+/*
+** Writes a socket buffer to TX descriptor ring and starts transmission
+*/
+static int
+depca_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+ struct depca_private *lp = (struct depca_private *)dev->priv;
+ u_long ioaddr = dev->base_addr;
+ int status = 0;
+
+ /* Transmitter timeout, serious problems. */
+ if (dev->tbusy) {
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < 1*HZ) {
+ status = -1;
+ } else {
+ printk("%s: transmit timed out, status %04x, resetting.\n",
+ dev->name, inw(DEPCA_DATA));
+
+ STOP_DEPCA;
+ depca_init_ring(dev);
+ LoadCSRs(dev);
+ dev->interrupt = UNMASK_INTERRUPTS;
+ dev->start = 1;
+ dev->tbusy=0;
+ dev->trans_start = jiffies;
+ InitRestartDepca(dev);
+ }
+ return status;
+ } else if (skb == NULL) {
+ dev_tint(dev);
+ } else if (skb->len > 0) {
+ /* Enforce 1 process per h/w access */
+ if (set_bit(0, (void*)&dev->tbusy) != 0) {
+ printk("%s: Transmitter access conflict.\n", dev->name);
+ status = -1;
+ } else {
+ if (TX_BUFFS_AVAIL) { /* Fill in a Tx ring entry */
+ status = load_packet(dev, skb);
+
+ if (!status) {
+ /* Trigger an immediate send demand. */
+ outw(CSR0, DEPCA_ADDR);
+ outw(INEA | TDMD, DEPCA_DATA);
+
+ dev->trans_start = jiffies;
+ dev_kfree_skb(skb, FREE_WRITE);
+ }
+ if (TX_BUFFS_AVAIL) {
+ dev->tbusy=0;
+ }
+ } else {
+ status = -1;
+ }
+ }
+ }
+
+ return status;
+}
+
+/*
+** The DEPCA interrupt handler.
+*/
+static void
+depca_interrupt(int irq, struct pt_regs * regs)
+{
+ struct device *dev = (struct device *)(irq2dev_map[irq]);
+ struct depca_private *lp;
+ s16 csr0, nicsr;
+ u_long ioaddr;
+
+ if (dev == NULL) {
+ printk ("depca_interrupt(): irq %d for unknown device.\n", irq);
+ } else {
+ lp = (struct depca_private *)dev->priv;
+ ioaddr = dev->base_addr;
+
+ if (dev->interrupt)
+ printk("%s: Re-entering the interrupt handler.\n", dev->name);
+
+ dev->interrupt = MASK_INTERRUPTS;
+
+ /* mask the DEPCA board interrupts and turn on the LED */
+ nicsr = inb(DEPCA_NICSR);
+ nicsr |= (IM|LED);
+ outb(nicsr, DEPCA_NICSR);
+
+ outw(CSR0, DEPCA_ADDR);
+ csr0 = inw(DEPCA_DATA);
+
+ /* Acknowledge all of the current interrupt sources ASAP. */
+ outw(csr0 & INTE, DEPCA_DATA);
+
+ if (csr0 & RINT) /* Rx interrupt (packet arrived) */
+ depca_rx(dev);
+
+ if (csr0 & TINT) /* Tx interrupt (packet sent) */
+ depca_tx(dev);
+
+ if ((TX_BUFFS_AVAIL >= 0) && dev->tbusy) { /* any resources available? */
+ dev->tbusy = 0; /* clear TX busy flag */
+ mark_bh(NET_BH);
+ }
+
+ /* Unmask the DEPCA board interrupts and turn off the LED */
+ nicsr = (nicsr & ~IM & ~LED);
+ outb(nicsr, DEPCA_NICSR);
+
+ dev->interrupt = UNMASK_INTERRUPTS;
+ }
+
+ return;
+}
+
+static int
+depca_rx(struct device *dev)
+{
+ struct depca_private *lp = (struct depca_private *)dev->priv;
+ int i, entry;
+ s32 status;
+
+ for (entry=lp->rx_new;
+ !(readl(&lp->rx_ring[entry].base) & R_OWN);
+ entry=lp->rx_new){
+ status = readl(&lp->rx_ring[entry].base) >> 16 ;
+ if (status & R_STP) { /* Remember start of frame */
+ lp->rx_old = entry;
+ }
+ if (status & R_ENP) { /* Valid frame status */
+ if (status & R_ERR) { /* There was an error. */
+ lp->stats.rx_errors++; /* Update the error stats. */
+ if (status & R_FRAM) lp->stats.rx_frame_errors++;
+ if (status & R_OFLO) lp->stats.rx_over_errors++;
+ if (status & R_CRC) lp->stats.rx_crc_errors++;
+ if (status & R_BUFF) lp->stats.rx_fifo_errors++;
+ } else {
+ short len, pkt_len = readw(&lp->rx_ring[entry].msg_length);
+ struct sk_buff *skb;
+
+ skb = dev_alloc_skb(pkt_len+2);
+ if (skb != NULL) {
+ unsigned char *buf;
+ skb_reserve(skb,2); /* 16 byte align the IP header */
+ buf = skb_put(skb,pkt_len);
+ skb->dev = dev;
+ if (entry < lp->rx_old) { /* Wrapped buffer */
+ len = (lp->rxRingMask - lp->rx_old + 1) * RX_BUFF_SZ;
+ memcpy_fromio(buf, lp->rx_memcpy[lp->rx_old], len);
+ memcpy_fromio(buf + len, lp->rx_memcpy[0], pkt_len-len);
+ } else { /* Linear buffer */
+ memcpy_fromio(buf, lp->rx_memcpy[lp->rx_old], pkt_len);
+ }
+
+ /*
+ ** Notify the upper protocol layers that there is another
+ ** packet to handle
+ */
+ skb->protocol=eth_type_trans(skb,dev);
+ netif_rx(skb);
+
+ /*
+ ** Update stats
+ */
+ lp->stats.rx_packets++;
+ for (i=1; i<DEPCA_PKT_STAT_SZ-1; i++) {
+ if (pkt_len < (i*DEPCA_PKT_BIN_SZ)) {
+ lp->pktStats.bins[i]++;
+ i = DEPCA_PKT_STAT_SZ;
+ }
+ }
+ if (buf[0] & 0x01) { /* Multicast/Broadcast */
+ if ((*(s16 *)&buf[0] == -1) &&
+ (*(s16 *)&buf[2] == -1) &&
+ (*(s16 *)&buf[4] == -1)) {
+ lp->pktStats.broadcast++;
+ } else {
+ lp->pktStats.multicast++;
+ }
+ } else if ((*(s16 *)&buf[0] == *(s16 *)&dev->dev_addr[0]) &&
+ (*(s16 *)&buf[2] == *(s16 *)&dev->dev_addr[2]) &&
+ (*(s16 *)&buf[4] == *(s16 *)&dev->dev_addr[4])) {
+ lp->pktStats.unicast++;
+ }
+
+ lp->pktStats.bins[0]++; /* Duplicates stats.rx_packets */
+ if (lp->pktStats.bins[0] == 0) { /* Reset counters */
+ memset((char *)&lp->pktStats, 0, sizeof(lp->pktStats));
+ }
+ } else {
+ printk("%s: Memory squeeze, deferring packet.\n", dev->name);
+ lp->stats.rx_dropped++; /* Really, deferred. */
+ break;
+ }
+ }
+ /* Change buffer ownership for this last frame, back to the adapter */
+ for (; lp->rx_old!=entry; lp->rx_old=(++lp->rx_old)&lp->rxRingMask) {
+ writel(readl(&lp->rx_ring[lp->rx_old].base) | R_OWN,
+ &lp->rx_ring[lp->rx_old].base);
+ }
+ writel(readl(&lp->rx_ring[entry].base) | R_OWN, &lp->rx_ring[entry].base);
+ }
+
+ /*
+ ** Update entry information
+ */
+ lp->rx_new = (++lp->rx_new) & lp->rxRingMask;
+ }
+
+ return 0;
+}
+
+/*
+** Buffer sent - check for buffer errors.
+*/
+static int
+depca_tx(struct device *dev)
+{
+ struct depca_private *lp = (struct depca_private *)dev->priv;
+ int entry;
+ s32 status;
+ u_long ioaddr = dev->base_addr;
+
+ for (entry = lp->tx_old; entry != lp->tx_new; entry = lp->tx_old) {
+ status = readl(&lp->tx_ring[entry].base) >> 16 ;
+
+ if (status < 0) { /* Packet not yet sent! */
+ break;
+ } else if (status & T_ERR) { /* An error occured. */
+ status = readl(&lp->tx_ring[entry].misc);
+ lp->stats.tx_errors++;
+ if (status & TMD3_RTRY) lp->stats.tx_aborted_errors++;
+ if (status & TMD3_LCAR) lp->stats.tx_carrier_errors++;
+ if (status & TMD3_LCOL) lp->stats.tx_window_errors++;
+ if (status & TMD3_UFLO) lp->stats.tx_fifo_errors++;
+ if (status & (TMD3_BUFF | TMD3_UFLO)) {
+ /* Trigger an immediate send demand. */
+ outw(CSR0, DEPCA_ADDR);
+ outw(INEA | TDMD, DEPCA_DATA);
+ }
+ } else if (status & (T_MORE | T_ONE)) {
+ lp->stats.collisions++;
+ } else {
+ lp->stats.tx_packets++;
+ }
+
+ /* Update all the pointers */
+ lp->tx_old = (++lp->tx_old) & lp->txRingMask;
+ }
+
+ return 0;
+}
+
+static int
+depca_close(struct device *dev)
+{
+ struct depca_private *lp = (struct depca_private *)dev->priv;
+ s16 nicsr;
+ u_long ioaddr = dev->base_addr;
+
+ dev->start = 0;
+ dev->tbusy = 1;
+
+ outw(CSR0, DEPCA_ADDR);
+
+ if (depca_debug > 1) {
+ printk("%s: Shutting down ethercard, status was %2.2x.\n",
+ dev->name, inw(DEPCA_DATA));
+ }
+
+ /*
+ ** We stop the DEPCA here -- it occasionally polls
+ ** memory if we don't.
+ */
+ outw(STOP, DEPCA_DATA);
+
+ /*
+ ** Give back the ROM in case the user wants to go to DOS
+ */
+ if (lp->adapter != DEPCA) {
+ nicsr = inb(DEPCA_NICSR);
+ nicsr &= ~SHE;
+ outb(nicsr, DEPCA_NICSR);
+ }
+
+ /*
+ ** Free the associated irq
+ */
+ free_irq(dev->irq);
+ irq2dev_map[dev->irq] = NULL;
+
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+}
+
+static void LoadCSRs(struct device *dev)
+{
+ struct depca_private *lp = (struct depca_private *)dev->priv;
+ u_long ioaddr = dev->base_addr;
+
+ outw(CSR1, DEPCA_ADDR); /* initialisation block address LSW */
+ outw((u16)(lp->sh_mem & LA_MASK), DEPCA_DATA);
+ outw(CSR2, DEPCA_ADDR); /* initialisation block address MSW */
+ outw((u16)((lp->sh_mem & LA_MASK) >> 16), DEPCA_DATA);
+ outw(CSR3, DEPCA_ADDR); /* ALE control */
+ outw(ACON, DEPCA_DATA);
+
+ outw(CSR0, DEPCA_ADDR); /* Point back to CSR0 */
+
+ return;
+}
+
+static int InitRestartDepca(struct device *dev)
+{
+ struct depca_private *lp = (struct depca_private *)dev->priv;
+ u_long ioaddr = dev->base_addr;
+ int i, status=0;
+
+ /* Copy the shadow init_block to shared memory */
+ memcpy_toio((char *)lp->sh_mem, &lp->init_block, sizeof(struct depca_init));
+
+ outw(CSR0, DEPCA_ADDR); /* point back to CSR0 */
+ outw(INIT, DEPCA_DATA); /* initialize DEPCA */
+
+ /* wait for lance to complete initialisation */
+ for (i=0;(i<100) && !(inw(DEPCA_DATA) & IDON); i++);
+
+ if (i!=100) {
+ /* clear IDON by writing a "1", enable interrupts and start lance */
+ outw(IDON | INEA | STRT, DEPCA_DATA);
+ if (depca_debug > 2) {
+ printk("%s: DEPCA open after %d ticks, init block 0x%08lx csr0 %4.4x.\n",
+ dev->name, i, lp->sh_mem, inw(DEPCA_DATA));
+ }
+ } else {
+ printk("%s: DEPCA unopen after %d ticks, init block 0x%08lx csr0 %4.4x.\n",
+ dev->name, i, lp->sh_mem, inw(DEPCA_DATA));
+ status = -1;
+ }
+
+ return status;
+}
+
+static struct enet_statistics *
+depca_get_stats(struct device *dev)
+{
+ struct depca_private *lp = (struct depca_private *)dev->priv;
+
+ /* Null body since there is no framing error counter */
+
+ return &lp->stats;
+}
+
+/*
+** Set or clear the multicast filter for this adaptor.
+*/
+static void
+set_multicast_list(struct device *dev)
+{
+ struct depca_private *lp = (struct depca_private *)dev->priv;
+ u_long ioaddr = dev->base_addr;
+
+ if (irq2dev_map[dev->irq] != NULL) {
+ while(dev->tbusy); /* Stop ring access */
+ set_bit(0, (void*)&dev->tbusy);
+ while(lp->tx_old != lp->tx_new); /* Wait for the ring to empty */
+
+ STOP_DEPCA; /* Temporarily stop the depca. */
+ depca_init_ring(dev); /* Initialize the descriptor rings */
+
+ if (dev->flags & IFF_PROMISC) { /* Set promiscuous mode */
+ lp->init_block.mode |= PROM;
+ } else {
+ SetMulticastFilter(dev);
+ lp->init_block.mode &= ~PROM; /* Unset promiscuous mode */
+ }
+
+ LoadCSRs(dev); /* Reload CSR3 */
+ InitRestartDepca(dev); /* Resume normal operation. */
+ dev->tbusy = 0; /* Unlock the TX ring */
+ }
+}
+
+/*
+** Calculate the hash code and update the logical address filter
+** from a list of ethernet multicast addresses.
+** Big endian crc one liner is mine, all mine, ha ha ha ha!
+** LANCE calculates its hash codes big endian.
+*/
+static void SetMulticastFilter(struct device *dev)
+{
+ struct depca_private *lp = (struct depca_private *)dev->priv;
+ struct dev_mc_list *dmi=dev->mc_list;
+ char *addrs;
+ int i, j, bit, byte;
+ u16 hashcode;
+ s32 crc, poly = CRC_POLYNOMIAL_BE;
+
+ if (dev->flags & IFF_ALLMULTI) { /* Set all multicast bits */
+ for (i=0; i<(HASH_TABLE_LEN>>3); i++) {
+ lp->init_block.mcast_table[i] = (char)0xff;
+ }
+ } else {
+ for (i=0; i<(HASH_TABLE_LEN>>3); i++){ /* Clear the multicast table */
+ lp->init_block.mcast_table[i]=0;
+ }
+ /* Add multicast addresses */
+ for (i=0;i<dev->mc_count;i++) { /* for each address in the list */
+ addrs=dmi->dmi_addr;
+ dmi=dmi->next;
+ if ((*addrs & 0x01) == 1) { /* multicast address? */
+ crc = 0xffffffff; /* init CRC for each address */
+ for (byte=0;byte<ETH_ALEN;byte++) {/* for each address byte */
+ /* process each address bit */
+ for (bit = *addrs++,j=0;j<8;j++, bit>>=1) {
+ crc = (crc << 1) ^ ((((crc<0?1:0) ^ bit) & 0x01) ? poly : 0);
+ }
+ }
+ hashcode = (crc & 1); /* hashcode is 6 LSb of CRC ... */
+ for (j=0;j<5;j++) { /* ... in reverse order. */
+ hashcode = (hashcode << 1) | ((crc>>=1) & 1);
+ }
+
+
+ byte = hashcode >> 3; /* bit[3-5] -> byte in filter */
+ bit = 1 << (hashcode & 0x07); /* bit[0-2] -> bit in byte */
+ lp->init_block.mcast_table[byte] |= bit;
+ }
+ }
+ }
+
+ return;
+}
+
+/*
+** ISA bus I/O device probe
+*/
+static void isa_probe(struct device *dev, u_long ioaddr)
+{
+ int i = num_depcas, maxSlots;
+ s32 ports[] = DEPCA_IO_PORTS;
+
+ if (!ioaddr && autoprobed) return ; /* Been here before ! */
+ if (ioaddr > 0x400) return; /* EISA Address */
+ if (i >= MAX_NUM_DEPCAS) return; /* Too many ISA adapters */
+
+ if (ioaddr == 0) { /* Autoprobing */
+ maxSlots = MAX_NUM_DEPCAS;
+ } else { /* Probe a specific location */
+ ports[i] = ioaddr;
+ maxSlots = i + 1;
+ }
+
+ for (; (i<maxSlots) && (dev!=NULL) && ports[i]; i++) {
+ if (DevicePresent(ports[i]) == 0) {
+ if (check_region(ports[i], DEPCA_TOTAL_SIZE) == 0) {
+ if ((dev = alloc_device(dev, ports[i])) != NULL) {
+ if (depca_hw_init(dev, ports[i]) == 0) {
+ num_depcas++;
+ }
+ num_eth++;
+ }
+ } else if (autoprobed) {
+ printk("%s: region already allocated at 0x%04x.\n", dev->name,ports[i]);
+ }
+ }
+ }
+
+ return;
+}
+
+/*
+** EISA bus I/O device probe. Probe from slot 1 since slot 0 is usually
+** the motherboard. Upto 15 EISA devices are supported.
+*/
+static void eisa_probe(struct device *dev, u_long ioaddr)
+{
+ int i, maxSlots;
+ u_long iobase;
+ char name[DEPCA_STRLEN];
+
+ if (!ioaddr && autoprobed) return ; /* Been here before ! */
+ if ((ioaddr < 0x400) && (ioaddr > 0)) return; /* ISA Address */
+
+ if (ioaddr == 0) { /* Autoprobing */
+ iobase = EISA_SLOT_INC; /* Get the first slot address */
+ i = 1;
+ maxSlots = MAX_EISA_SLOTS;
+ } else { /* Probe a specific location */
+ iobase = ioaddr;
+ i = (ioaddr >> 12);
+ maxSlots = i + 1;
+ }
+ if ((iobase & 0x0fff) == 0) iobase += DEPCA_EISA_IO_PORTS;
+
+ for (; (i<maxSlots) && (dev!=NULL); i++, iobase+=EISA_SLOT_INC) {
+ if (EISA_signature(name, EISA_ID)) {
+ if (DevicePresent(iobase) == 0) {
+ if (check_region(iobase, DEPCA_TOTAL_SIZE) == 0) {
+ if ((dev = alloc_device(dev, iobase)) != NULL) {
+ if (depca_hw_init(dev, iobase) == 0) {
+ num_depcas++;
+ }
+ num_eth++;
+ }
+ } else if (autoprobed) {
+ printk("%s: region already allocated at 0x%04lx.\n",dev->name,iobase);
+ }
+ }
+ }
+ }
+
+ return;
+}
+
+/*
+** Allocate the device by pointing to the next available space in the
+** device structure. Should one not be available, it is created.
+*/
+static struct device *alloc_device(struct device *dev, u_long iobase)
+{
+ int addAutoProbe = 0;
+ struct device *tmp = NULL, *ret;
+ int (*init)(struct device *) = NULL;
+
+ /*
+ ** Check the device structures for an end of list or unused device
+ */
+ if (!loading_module) {
+ while (dev->next != NULL) {
+ if ((dev->base_addr == DEPCA_NDA) || (dev->base_addr == 0)) break;
+ dev = dev->next; /* walk through eth device list */
+ num_eth++; /* increment eth device number */
+ }
+
+ /*
+ ** If an autoprobe is requested for another device, we must re-insert
+ ** the request later in the list. Remember the current information.
+ */
+ if ((dev->base_addr == 0) && (num_depcas > 0)) {
+ addAutoProbe++;
+ tmp = dev->next; /* point to the next device */
+ init = dev->init; /* remember the probe function */
+ }
+
+ /*
+ ** If at end of list and can't use current entry, malloc one up.
+ ** If memory could not be allocated, print an error message.
+ */
+ if ((dev->next == NULL) &&
+ !((dev->base_addr == DEPCA_NDA) || (dev->base_addr == 0))){
+ dev->next = (struct device *)kmalloc(sizeof(struct device) + 8,
+ GFP_KERNEL);
+
+ dev = dev->next; /* point to the new device */
+ if (dev == NULL) {
+ printk("eth%d: Device not initialised, insufficient memory\n",
+ num_eth);
+ } else {
+ /*
+ ** If the memory was allocated, point to the new memory area
+ ** and initialize it (name, I/O address, next device (NULL) and
+ ** initialisation probe routine).
+ */
+ dev->name = (char *)(dev + sizeof(struct device));
+ if (num_eth > 9999) {
+ sprintf(dev->name,"eth????"); /* New device name */
+ } else {
+ sprintf(dev->name,"eth%d", num_eth);/* New device name */
+ }
+ dev->base_addr = iobase; /* assign the io address */
+ dev->next = NULL; /* mark the end of list */
+ dev->init = &depca_probe; /* initialisation routine */
+ num_depcas++;
+ }
+ }
+ ret = dev; /* return current struct, or NULL */
+
+ /*
+ ** Now figure out what to do with the autoprobe that has to be inserted.
+ ** Firstly, search the (possibly altered) list for an empty space.
+ */
+ if (ret != NULL) {
+ if (addAutoProbe) {
+ for (;(tmp->next!=NULL) && (tmp->base_addr!=DEPCA_NDA); tmp=tmp->next);
+
+ /*
+ ** If no more device structures and can't use the current one, malloc
+ ** one up. If memory could not be allocated, print an error message.
+ */
+ if ((tmp->next == NULL) && !(tmp->base_addr == DEPCA_NDA)) {
+ tmp->next = (struct device *)kmalloc(sizeof(struct device) + 8,
+ GFP_KERNEL);
+ tmp = tmp->next; /* point to the new device */
+ if (tmp == NULL) {
+ printk("%s: Insufficient memory to extend the device list.\n",
+ dev->name);
+ } else {
+ /*
+ ** If the memory was allocated, point to the new memory area
+ ** and initialize it (name, I/O address, next device (NULL) and
+ ** initialisation probe routine).
+ */
+ tmp->name = (char *)(tmp + sizeof(struct device));
+ if (num_eth > 9999) {
+ sprintf(tmp->name,"eth????"); /* New device name */
+ } else {
+ sprintf(tmp->name,"eth%d", num_eth);/* New device name */
+ }
+ tmp->base_addr = 0; /* re-insert the io address */
+ tmp->next = NULL; /* mark the end of list */
+ tmp->init = init; /* initialisation routine */
+ }
+ } else { /* structure already exists */
+ tmp->base_addr = 0; /* re-insert the io address */
+ }
+ }
+ }
+ } else {
+ ret = dev;
+ }
+
+ return ret;
+}
+
+/*
+** Look for a particular board name in the on-board Remote Diagnostics
+** and Boot (readb) ROM. This will also give us a clue to the network RAM
+** base address.
+*/
+static void DepcaSignature(char *name, u_long paddr)
+{
+ u_int i,j,k;
+ const char *signatures[] = DEPCA_SIGNATURE;
+ char tmpstr[16];
+
+ for (i=0;i<16;i++) { /* copy the first 16 bytes of ROM to */
+ tmpstr[i] = readb(paddr+0xc000+i); /* a temporary string */
+ }
+
+ strcpy(name,"");
+ for (i=0;*signatures[i]!='\0' && *name=='\0';i++) {
+ for (j=0,k=0;j<16 && k<strlen(signatures[i]);j++) {
+ if (signatures[i][k] == tmpstr[j]) { /* track signature */
+ k++;
+ } else { /* lost signature; begin search again */
+ k=0;
+ }
+ }
+ if (k == strlen(signatures[i])) {
+ strcpy(name,signatures[i]);
+ }
+ }
+
+ adapter = i - 1;
+
+ return;
+}
+
+/*
+** Look for a special sequence in the Ethernet station address PROM that
+** is common across all DEPCA products. Note that the original DEPCA needs
+** its ROM address counter to be initialized and enabled. Only enable
+** if the first address octet is a 0x08 - this minimises the chances of
+** messing around with some other hardware, but it assumes that this DEPCA
+** card initialized itself correctly.
+**
+** Search the Ethernet address ROM for the signature. Since the ROM address
+** counter can start at an arbitrary point, the search must include the entire
+** probe sequence length plus the (length_of_the_signature - 1).
+** Stop the search IMMEDIATELY after the signature is found so that the
+** PROM address counter is correctly positioned at the start of the
+** ethernet address for later read out.
+*/
+static int DevicePresent(u_long ioaddr)
+{
+ union {
+ struct {
+ u32 a;
+ u32 b;
+ } llsig;
+ char Sig[sizeof(u32) << 1];
+ } dev;
+ short sigLength=0;
+ s8 data;
+ s16 nicsr;
+ int i, j, status = 0;
+
+ data = inb(DEPCA_PROM); /* clear counter on DEPCA */
+ data = inb(DEPCA_PROM); /* read data */
+
+ if (data == 0x08) { /* Enable counter on DEPCA */
+ nicsr = inb(DEPCA_NICSR);
+ nicsr |= AAC;
+ outb(nicsr, DEPCA_NICSR);
+ }
+
+ dev.llsig.a = ETH_PROM_SIG;
+ dev.llsig.b = ETH_PROM_SIG;
+ sigLength = sizeof(u32) << 1;
+
+ for (i=0,j=0;j<sigLength && i<PROBE_LENGTH+sigLength-1;i++) {
+ data = inb(DEPCA_PROM);
+ if (dev.Sig[j] == data) { /* track signature */
+ j++;
+ } else { /* lost signature; begin search again */
+ if (data == dev.Sig[0]) { /* rare case.... */
+ j=1;
+ } else {
+ j=0;
+ }
+ }
+ }
+
+ if (j!=sigLength) {
+ status = -ENODEV; /* search failed */
+ }
+
+ return status;
+}
+
+/*
+** The DE100 and DE101 PROM accesses were made non-standard for some bizarre
+** reason: access the upper half of the PROM with x=0; access the lower half
+** with x=1.
+*/
+static int get_hw_addr(struct device *dev)
+{
+ u_long ioaddr = dev->base_addr;
+ int i, k, tmp, status = 0;
+ u_short j, x, chksum;
+
+ x = (((adapter == de100) || (adapter == de101)) ? 1 : 0);
+
+ for (i=0,k=0,j=0;j<3;j++) {
+ k <<= 1 ;
+ if (k > 0xffff) k-=0xffff;
+
+ k += (u_char) (tmp = inb(DEPCA_PROM + x));
+ dev->dev_addr[i++] = (u_char) tmp;
+ k += (u_short) ((tmp = inb(DEPCA_PROM + x)) << 8);
+ dev->dev_addr[i++] = (u_char) tmp;
+
+ if (k > 0xffff) k-=0xffff;
+ }
+ if (k == 0xffff) k=0;
+
+ chksum = (u_char) inb(DEPCA_PROM + x);
+ chksum |= (u_short) (inb(DEPCA_PROM + x) << 8);
+ if (k != chksum) status = -1;
+
+ return status;
+}
+
+/*
+** Load a packet into the shared memory
+*/
+static int load_packet(struct device *dev, struct sk_buff *skb)
+{
+ struct depca_private *lp = (struct depca_private *)dev->priv;
+ int i, entry, end, len, status = 0;
+
+ entry = lp->tx_new; /* Ring around buffer number. */
+ end = (entry + (skb->len - 1) / TX_BUFF_SZ) & lp->txRingMask;
+ if (!(readl(&lp->tx_ring[end].base) & T_OWN)) {/* Enough room? */
+ /*
+ ** Caution: the write order is important here... don't set up the
+ ** ownership rights until all the other information is in place.
+ */
+ if (end < entry) { /* wrapped buffer */
+ len = (lp->txRingMask - entry + 1) * TX_BUFF_SZ;
+ memcpy_toio(lp->tx_memcpy[entry], skb->data, len);
+ memcpy_toio(lp->tx_memcpy[0], skb->data + len, skb->len - len);
+ } else { /* linear buffer */
+ memcpy_toio(lp->tx_memcpy[entry], skb->data, skb->len);
+ }
+
+ /* set up the buffer descriptors */
+ len = (skb->len < ETH_ZLEN) ? ETH_ZLEN : skb->len;
+ for (i = entry; i != end; i = (++i) & lp->txRingMask) {
+ /* clean out flags */
+ writel(readl(&lp->tx_ring[i].base) & ~T_FLAGS, &lp->tx_ring[i].base);
+ writew(0x0000, &lp->tx_ring[i].misc); /* clears other error flags */
+ writew(-TX_BUFF_SZ, &lp->tx_ring[i].length);/* packet length in buffer */
+ len -= TX_BUFF_SZ;
+ }
+ /* clean out flags */
+ writel(readl(&lp->tx_ring[end].base) & ~T_FLAGS, &lp->tx_ring[end].base);
+ writew(0x0000, &lp->tx_ring[end].misc); /* clears other error flags */
+ writew(-len, &lp->tx_ring[end].length); /* packet length in last buff */
+
+ /* start of packet */
+ writel(readl(&lp->tx_ring[entry].base) | T_STP, &lp->tx_ring[entry].base);
+ /* end of packet */
+ writel(readl(&lp->tx_ring[end].base) | T_ENP, &lp->tx_ring[end].base);
+
+ for (i=end; i!=entry; --i) {
+ /* ownership of packet */
+ writel(readl(&lp->tx_ring[i].base) | T_OWN, &lp->tx_ring[i].base);
+ if (i == 0) i=lp->txRingMask+1;
+ }
+ writel(readl(&lp->tx_ring[entry].base) | T_OWN, &lp->tx_ring[entry].base);
+
+ lp->tx_new = (++end) & lp->txRingMask; /* update current pointers */
+ } else {
+ status = -1;
+ }
+
+ return status;
+}
+
+/*
+** Look for a particular board name in the EISA configuration space
+*/
+static int EISA_signature(char *name, s32 eisa_id)
+{
+ u_int i;
+ const char *signatures[] = DEPCA_SIGNATURE;
+ char ManCode[DEPCA_STRLEN];
+ union {
+ s32 ID;
+ char Id[4];
+ } Eisa;
+ int status = 0;
+
+ *name = '\0';
+ Eisa.ID = inl(eisa_id);
+
+ ManCode[0]=(((Eisa.Id[0]>>2)&0x1f)+0x40);
+ ManCode[1]=(((Eisa.Id[1]&0xe0)>>5)+((Eisa.Id[0]&0x03)<<3)+0x40);
+ ManCode[2]=(((Eisa.Id[2]>>4)&0x0f)+0x30);
+ ManCode[3]=(( Eisa.Id[2]&0x0f)+0x30);
+ ManCode[4]=(((Eisa.Id[3]>>4)&0x0f)+0x30);
+ ManCode[5]='\0';
+
+ for (i=0;(*signatures[i] != '\0') && (*name == '\0');i++) {
+ if (strstr(ManCode, signatures[i]) != NULL) {
+ strcpy(name,ManCode);
+ status = 1;
+ }
+ }
+
+ return status;
+}
+
+static void depca_dbg_open(struct device *dev)
+{
+ struct depca_private *lp = (struct depca_private *)dev->priv;
+ u_long ioaddr = dev->base_addr;
+ struct depca_init *p = (struct depca_init *)lp->sh_mem;
+ int i;
+
+ if (depca_debug > 1){
+ /* Copy the shadow init_block to shared memory */
+ memcpy_toio((char *)lp->sh_mem,&lp->init_block,sizeof(struct depca_init));
+
+ printk("%s: depca open with irq %d\n",dev->name,dev->irq);
+ printk("Descriptor head addresses:\n");
+ printk("\t0x%lx 0x%lx\n",(u_long)lp->rx_ring, (u_long)lp->tx_ring);
+ printk("Descriptor addresses:\nRX: ");
+ for (i=0;i<lp->rxRingMask;i++){
+ if (i < 3) {
+ printk("0x%8.8lx ", (long) &lp->rx_ring[i].base);
+ }
+ }
+ printk("...0x%8.8lx\n", (long) &lp->rx_ring[i].base);
+ printk("TX: ");
+ for (i=0;i<lp->txRingMask;i++){
+ if (i < 3) {
+ printk("0x%8.8lx ", (long) &lp->tx_ring[i].base);
+ }
+ }
+ printk("...0x%8.8lx\n", (long) &lp->tx_ring[i].base);
+ printk("\nDescriptor buffers:\nRX: ");
+ for (i=0;i<lp->rxRingMask;i++){
+ if (i < 3) {
+ printk("0x%8.8x ", (u32) readl(&lp->rx_ring[i].base));
+ }
+ }
+ printk("...0x%8.8x\n", (u32) readl(&lp->rx_ring[i].base));
+ printk("TX: ");
+ for (i=0;i<lp->txRingMask;i++){
+ if (i < 3) {
+ printk("0x%8.8x ", (u32) readl(&lp->tx_ring[i].base));
+ }
+ }
+ printk("...0x%8.8x\n", (u32) readl(&lp->tx_ring[i].base));
+ printk("Initialisation block at 0x%8.8lx\n",lp->sh_mem);
+ printk("\tmode: 0x%4.4x\n", (u16) readw(&p->mode));
+ printk("\tphysical address: ");
+ for (i=0;i<ETH_ALEN-1;i++){
+ printk("%2.2x:",(u_char)readb(&p->phys_addr[i]));
+ }
+ printk("%2.2x\n",(u_char)readb(&p->phys_addr[i]));
+ printk("\tmulticast hash table: ");
+ for (i=0;i<(HASH_TABLE_LEN >> 3)-1;i++){
+ printk("%2.2x:",(u_char)readb(&p->mcast_table[i]));
+ }
+ printk("%2.2x\n",(u_char)readb(&p->mcast_table[i]));
+ printk("\trx_ring at: 0x%8.8x\n", (u32) readl(&p->rx_ring));
+ printk("\ttx_ring at: 0x%8.8x\n", (u32) readl(&p->tx_ring));
+ printk("dma_buffs: 0x%8.8lx\n",lp->dma_buffs);
+ printk("Ring size:\nRX: %d Log2(rxRingMask): 0x%8.8x\n",
+ (int)lp->rxRingMask + 1,
+ lp->rx_rlen);
+ printk("TX: %d Log2(txRingMask): 0x%8.8x\n",
+ (int)lp->txRingMask + 1,
+ lp->tx_rlen);
+ outw(CSR2,DEPCA_ADDR);
+ printk("CSR2&1: 0x%4.4x",inw(DEPCA_DATA));
+ outw(CSR1,DEPCA_ADDR);
+ printk("%4.4x\n",inw(DEPCA_DATA));
+ outw(CSR3,DEPCA_ADDR);
+ printk("CSR3: 0x%4.4x\n",inw(DEPCA_DATA));
+ }
+
+ return;
+}
+
+/*
+** Perform IOCTL call functions here. Some are privileged operations and the
+** effective uid is checked in those cases.
+** All MCA IOCTLs will not work here and are for testing purposes only.
+*/
+static int depca_ioctl(struct device *dev, struct ifreq *rq, int cmd)
+{
+ struct depca_private *lp = (struct depca_private *)dev->priv;
+ struct depca_ioctl *ioc = (struct depca_ioctl *) &rq->ifr_data;
+ int i, status = 0;
+ u_long ioaddr = dev->base_addr;
+ union {
+ u8 addr[(HASH_TABLE_LEN * ETH_ALEN)];
+ u16 sval[(HASH_TABLE_LEN * ETH_ALEN) >> 1];
+ u32 lval[(HASH_TABLE_LEN * ETH_ALEN) >> 2];
+ } tmp;
+
+ switch(ioc->cmd) {
+ case DEPCA_GET_HWADDR: /* Get the hardware address */
+ for (i=0; i<ETH_ALEN; i++) {
+ tmp.addr[i] = dev->dev_addr[i];
+ }
+ ioc->len = ETH_ALEN;
+ if (!(status = verify_area(VERIFY_WRITE, (void *)ioc->data, ioc->len))) {
+ memcpy_tofs(ioc->data, tmp.addr, ioc->len);
+ }
+
+ break;
+ case DEPCA_SET_HWADDR: /* Set the hardware address */
+ if (suser()) {
+ if (!(status = verify_area(VERIFY_READ, (void *)ioc->data, ETH_ALEN))) {
+ memcpy_fromfs(tmp.addr,ioc->data,ETH_ALEN);
+ for (i=0; i<ETH_ALEN; i++) {
+ dev->dev_addr[i] = tmp.addr[i];
+ }
+ while(dev->tbusy); /* Stop ring access */
+ set_bit(0, (void*)&dev->tbusy);
+ while(lp->tx_old != lp->tx_new);/* Wait for the ring to empty */
+
+ STOP_DEPCA; /* Temporarily stop the depca. */
+ depca_init_ring(dev); /* Initialize the descriptor rings */
+ LoadCSRs(dev); /* Reload CSR3 */
+ InitRestartDepca(dev); /* Resume normal operation. */
+ dev->tbusy = 0; /* Unlock the TX ring */
+ }
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case DEPCA_SET_PROM: /* Set Promiscuous Mode */
+ if (suser()) {
+ while(dev->tbusy); /* Stop ring access */
+ set_bit(0, (void*)&dev->tbusy);
+ while(lp->tx_old != lp->tx_new); /* Wait for the ring to empty */
+
+ STOP_DEPCA; /* Temporarily stop the depca. */
+ depca_init_ring(dev); /* Initialize the descriptor rings */
+ lp->init_block.mode |= PROM; /* Set promiscuous mode */
+
+ LoadCSRs(dev); /* Reload CSR3 */
+ InitRestartDepca(dev); /* Resume normal operation. */
+ dev->tbusy = 0; /* Unlock the TX ring */
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case DEPCA_CLR_PROM: /* Clear Promiscuous Mode */
+ if (suser()) {
+ while(dev->tbusy); /* Stop ring access */
+ set_bit(0, (void*)&dev->tbusy);
+ while(lp->tx_old != lp->tx_new); /* Wait for the ring to empty */
+
+ STOP_DEPCA; /* Temporarily stop the depca. */
+ depca_init_ring(dev); /* Initialize the descriptor rings */
+ lp->init_block.mode &= ~PROM; /* Clear promiscuous mode */
+
+ LoadCSRs(dev); /* Reload CSR3 */
+ InitRestartDepca(dev); /* Resume normal operation. */
+ dev->tbusy = 0; /* Unlock the TX ring */
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case DEPCA_SAY_BOO: /* Say "Boo!" to the kernel log file */
+ printk("%s: Boo!\n", dev->name);
+
+ break;
+ case DEPCA_GET_MCA: /* Get the multicast address table */
+ ioc->len = (HASH_TABLE_LEN >> 3);
+ if (!(status = verify_area(VERIFY_WRITE, ioc->data, ioc->len))) {
+ memcpy_tofs(ioc->data, lp->init_block.mcast_table, ioc->len);
+ }
+
+ break;
+ case DEPCA_SET_MCA: /* Set a multicast address */
+ if (suser()) {
+ if (!(status=verify_area(VERIFY_READ, ioc->data, ETH_ALEN*ioc->len))) {
+ memcpy_fromfs(tmp.addr, ioc->data, ETH_ALEN * ioc->len);
+ set_multicast_list(dev);
+ }
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case DEPCA_CLR_MCA: /* Clear all multicast addresses */
+ if (suser()) {
+ set_multicast_list(dev);
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case DEPCA_MCA_EN: /* Enable pass all multicast addressing */
+ if (suser()) {
+ set_multicast_list(dev);
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case DEPCA_GET_STATS: /* Get the driver statistics */
+ cli();
+ ioc->len = sizeof(lp->pktStats);
+ if (!(status=verify_area(VERIFY_WRITE, ioc->data, ioc->len))) {
+ memcpy_tofs(ioc->data, &lp->pktStats, ioc->len);
+ }
+ sti();
+
+ break;
+ case DEPCA_CLR_STATS: /* Zero out the driver statistics */
+ if (suser()) {
+ cli();
+ memset(&lp->pktStats, 0, sizeof(lp->pktStats));
+ sti();
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case DEPCA_GET_REG: /* Get the DEPCA Registers */
+ i=0;
+ tmp.sval[i++] = inw(DEPCA_NICSR);
+ outw(CSR0, DEPCA_ADDR); /* status register */
+ tmp.sval[i++] = inw(DEPCA_DATA);
+ memcpy(&tmp.sval[i], &lp->init_block, sizeof(struct depca_init));
+ ioc->len = i+sizeof(struct depca_init);
+ if (!(status=verify_area(VERIFY_WRITE, ioc->data, ioc->len))) {
+ memcpy_tofs(ioc->data, tmp.addr, ioc->len);
+ }
+
+ break;
+ default:
+ status = -EOPNOTSUPP;
+ }
+
+ return status;
+}
+
+#ifdef MODULE
+static char devicename[9] = { 0, };
+static struct device thisDepca = {
+ devicename, /* device name is inserted by /linux/drivers/net/net_init.c */
+ 0, 0, 0, 0,
+ 0x200, 7, /* I/O address, IRQ */
+ 0, 0, 0, NULL, depca_probe };
+
+static int irq=7; /* EDIT THESE LINE FOR YOUR CONFIGURATION */
+static int io=0x200; /* Or use the irq= io= options to insmod */
+
+/* See depca_probe() for autoprobe messages when a module */
+int
+init_module(void)
+{
+ thisDepca.irq=irq;
+ thisDepca.base_addr=io;
+
+ if (register_netdev(&thisDepca) != 0)
+ return -EIO;
+
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ release_region(thisDepca.base_addr, DEPCA_TOTAL_SIZE);
+ if (thisDepca.priv) {
+ kfree(thisDepca.priv);
+ thisDepca.priv = NULL;
+ }
+ thisDepca.irq=0;
+
+ unregister_netdev(&thisDepca);
+}
+#endif /* MODULE */
+
+
+/*
+ * Local variables:
+ * kernel-compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O2 -m486 -c depca.c"
+ *
+ * module-compile-command: "gcc -D__KERNEL__ -DMODULE -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O2 -m486 -c depca.c"
+ * End:
+ */
diff --git a/i386/i386at/gpl/linux/net/depca.h b/i386/i386at/gpl/linux/net/depca.h
new file mode 100644
index 00000000..012f7399
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/depca.h
@@ -0,0 +1,185 @@
+/*
+ Written 1994 by David C. Davies.
+
+ Copyright 1994 David C. Davies. This software may be used and distributed
+ according to the terms of the GNU Public License, incorporated herein by
+ reference.
+*/
+
+/*
+** I/O addresses. Note that the 2k buffer option is not supported in
+** this driver.
+*/
+#define DEPCA_NICSR ioaddr+0x00 /* Network interface CSR */
+#define DEPCA_RBI ioaddr+0x02 /* RAM buffer index (2k buffer mode) */
+#define DEPCA_DATA ioaddr+0x04 /* LANCE registers' data port */
+#define DEPCA_ADDR ioaddr+0x06 /* LANCE registers' address port */
+#define DEPCA_HBASE ioaddr+0x08 /* EISA high memory base address reg. */
+#define DEPCA_PROM ioaddr+0x0c /* Ethernet address ROM data port */
+#define DEPCA_CNFG ioaddr+0x0c /* EISA Configuration port */
+#define DEPCA_RBSA ioaddr+0x0e /* RAM buffer starting address (2k buff.) */
+
+/*
+** These are LANCE registers addressable through DEPCA_ADDR
+*/
+#define CSR0 0
+#define CSR1 1
+#define CSR2 2
+#define CSR3 3
+
+/*
+** NETWORK INTERFACE CSR (NI_CSR) bit definitions
+*/
+
+#define TO 0x0100 /* Time Out for remote boot */
+#define SHE 0x0080 /* SHadow memory Enable */
+#define BS 0x0040 /* Bank Select */
+#define BUF 0x0020 /* BUFfer size (1->32k, 0->64k) */
+#define RBE 0x0010 /* Remote Boot Enable (1->net boot) */
+#define AAC 0x0008 /* Address ROM Address Counter (1->enable) */
+#define _128KB 0x0008 /* 128kB Network RAM (1->enable) */
+#define IM 0x0004 /* Interrupt Mask (1->mask) */
+#define IEN 0x0002 /* Interrupt tristate ENable (1->enable) */
+#define LED 0x0001 /* LED control */
+
+/*
+** Control and Status Register 0 (CSR0) bit definitions
+*/
+
+#define ERR 0x8000 /* Error summary */
+#define BABL 0x4000 /* Babble transmitter timeout error */
+#define CERR 0x2000 /* Collision Error */
+#define MISS 0x1000 /* Missed packet */
+#define MERR 0x0800 /* Memory Error */
+#define RINT 0x0400 /* Receiver Interrupt */
+#define TINT 0x0200 /* Transmit Interrupt */
+#define IDON 0x0100 /* Initialization Done */
+#define INTR 0x0080 /* Interrupt Flag */
+#define INEA 0x0040 /* Interrupt Enable */
+#define RXON 0x0020 /* Receiver on */
+#define TXON 0x0010 /* Transmitter on */
+#define TDMD 0x0008 /* Transmit Demand */
+#define STOP 0x0004 /* Stop */
+#define STRT 0x0002 /* Start */
+#define INIT 0x0001 /* Initialize */
+#define INTM 0xff00 /* Interrupt Mask */
+#define INTE 0xfff0 /* Interrupt Enable */
+
+/*
+** CONTROL AND STATUS REGISTER 3 (CSR3)
+*/
+
+#define BSWP 0x0004 /* Byte SWaP */
+#define ACON 0x0002 /* ALE control */
+#define BCON 0x0001 /* Byte CONtrol */
+
+/*
+** Initialization Block Mode Register
+*/
+
+#define PROM 0x8000 /* Promiscuous Mode */
+#define EMBA 0x0080 /* Enable Modified Back-off Algorithm */
+#define INTL 0x0040 /* Internal Loopback */
+#define DRTY 0x0020 /* Disable Retry */
+#define COLL 0x0010 /* Force Collision */
+#define DTCR 0x0008 /* Disable Transmit CRC */
+#define LOOP 0x0004 /* Loopback */
+#define DTX 0x0002 /* Disable the Transmitter */
+#define DRX 0x0001 /* Disable the Receiver */
+
+/*
+** Receive Message Descriptor 1 (RMD1) bit definitions.
+*/
+
+#define R_OWN 0x80000000 /* Owner bit 0 = host, 1 = lance */
+#define R_ERR 0x4000 /* Error Summary */
+#define R_FRAM 0x2000 /* Framing Error */
+#define R_OFLO 0x1000 /* Overflow Error */
+#define R_CRC 0x0800 /* CRC Error */
+#define R_BUFF 0x0400 /* Buffer Error */
+#define R_STP 0x0200 /* Start of Packet */
+#define R_ENP 0x0100 /* End of Packet */
+
+/*
+** Transmit Message Descriptor 1 (TMD1) bit definitions.
+*/
+
+#define T_OWN 0x80000000 /* Owner bit 0 = host, 1 = lance */
+#define T_ERR 0x4000 /* Error Summary */
+#define T_ADD_FCS 0x2000 /* More the 1 retry needed to Xmit */
+#define T_MORE 0x1000 /* >1 retry to transmit packet */
+#define T_ONE 0x0800 /* 1 try needed to transmit the packet */
+#define T_DEF 0x0400 /* Deferred */
+#define T_STP 0x02000000 /* Start of Packet */
+#define T_ENP 0x01000000 /* End of Packet */
+#define T_FLAGS 0xff000000 /* TX Flags Field */
+
+/*
+** Transmit Message Descriptor 3 (TMD3) bit definitions.
+*/
+
+#define TMD3_BUFF 0x8000 /* BUFFer error */
+#define TMD3_UFLO 0x4000 /* UnderFLOw error */
+#define TMD3_RES 0x2000 /* REServed */
+#define TMD3_LCOL 0x1000 /* Late COLlision */
+#define TMD3_LCAR 0x0800 /* Loss of CARrier */
+#define TMD3_RTRY 0x0400 /* ReTRY error */
+
+/*
+** EISA configuration Register (CNFG) bit definitions
+*/
+
+#define TIMEOUT 0x0100 /* 0:2.5 mins, 1: 30 secs */
+#define REMOTE 0x0080 /* Remote Boot Enable -> 1 */
+#define IRQ11 0x0040 /* Enable -> 1 */
+#define IRQ10 0x0020 /* Enable -> 1 */
+#define IRQ9 0x0010 /* Enable -> 1 */
+#define IRQ5 0x0008 /* Enable -> 1 */
+#define BUFF 0x0004 /* 0: 64kB or 128kB, 1: 32kB */
+#define PADR16 0x0002 /* RAM on 64kB boundary */
+#define PADR17 0x0001 /* RAM on 128kB boundary */
+
+/*
+** Miscellaneous
+*/
+#define HASH_TABLE_LEN 64 /* Bits */
+#define HASH_BITS 0x003f /* 6 LS bits */
+
+#define MASK_INTERRUPTS 1
+#define UNMASK_INTERRUPTS 0
+
+#define EISA_EN 0x0001 /* Enable EISA bus buffers */
+#define EISA_ID iobase+0x0080 /* ID long word for EISA card */
+#define EISA_CTRL iobase+0x0084 /* Control word for EISA card */
+
+/*
+** Include the IOCTL stuff
+*/
+#include <linux/sockios.h>
+
+#define DEPCAIOCTL SIOCDEVPRIVATE
+
+struct depca_ioctl {
+ unsigned short cmd; /* Command to run */
+ unsigned short len; /* Length of the data buffer */
+ unsigned char *data; /* Pointer to the data buffer */
+};
+
+/*
+** Recognised commands for the driver
+*/
+#define DEPCA_GET_HWADDR 0x01 /* Get the hardware address */
+#define DEPCA_SET_HWADDR 0x02 /* Get the hardware address */
+#define DEPCA_SET_PROM 0x03 /* Set Promiscuous Mode */
+#define DEPCA_CLR_PROM 0x04 /* Clear Promiscuous Mode */
+#define DEPCA_SAY_BOO 0x05 /* Say "Boo!" to the kernel log file */
+#define DEPCA_GET_MCA 0x06 /* Get a multicast address */
+#define DEPCA_SET_MCA 0x07 /* Set a multicast address */
+#define DEPCA_CLR_MCA 0x08 /* Clear a multicast address */
+#define DEPCA_MCA_EN 0x09 /* Enable a multicast address group */
+#define DEPCA_GET_STATS 0x0a /* Get the driver statistics */
+#define DEPCA_CLR_STATS 0x0b /* Zero out the driver statistics */
+#define DEPCA_GET_REG 0x0c /* Get the Register contents */
+#define DEPCA_SET_REG 0x0d /* Set the Register contents */
+#define DEPCA_DUMP 0x0f /* Dump the DEPCA Status */
+
diff --git a/i386/i386at/gpl/linux/net/dev.c b/i386/i386at/gpl/linux/net/dev.c
new file mode 100644
index 00000000..69d576ff
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/dev.c
@@ -0,0 +1,1413 @@
+/*
+ * NET3 Protocol independent device support routines.
+ *
+ * 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.
+ *
+ * Derived from the non IP parts of dev.c 1.0.19
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Mark Evans, <evansmp@uhura.aston.ac.uk>
+ *
+ * Additional Authors:
+ * Florian la Roche <rzsfl@rz.uni-sb.de>
+ * Alan Cox <gw4pts@gw4pts.ampr.org>
+ * David Hinds <dhinds@allegro.stanford.edu>
+ *
+ * Changes:
+ * Alan Cox : device private ioctl copies fields back.
+ * Alan Cox : Transmit queue code does relevant stunts to
+ * keep the queue safe.
+ * Alan Cox : Fixed double lock.
+ * Alan Cox : Fixed promisc NULL pointer trap
+ * ???????? : Support the full private ioctl range
+ * Alan Cox : Moved ioctl permission check into drivers
+ * Tim Kordas : SIOCADDMULTI/SIOCDELMULTI
+ * Alan Cox : 100 backlog just doesn't cut it when
+ * you start doing multicast video 8)
+ * Alan Cox : Rewrote net_bh and list manager.
+ * Alan Cox : Fix ETH_P_ALL echoback lengths.
+ * Alan Cox : Took out transmit every packet pass
+ * Saved a few bytes in the ioctl handler
+ * Alan Cox : Network driver sets packet type before calling netif_rx. Saves
+ * a function call a packet.
+ * Alan Cox : Hashed net_bh()
+ * Richard Kooijman: Timestamp fixes.
+ * Alan Cox : Wrong field in SIOCGIFDSTADDR
+ * Alan Cox : Device lock protection.
+ * Alan Cox : Fixed nasty side effect of device close changes.
+ * Rudi Cilibrasi : Pass the right thing to set_mac_address()
+ * Dave Miller : 32bit quantity for the device lock to make it work out
+ * on a Sparc.
+ * Bjorn Ekwall : Added KERNELD hack.
+ * Alan Cox : Cleaned up the backlog initialise.
+ * Craig Metz : SIOCGIFCONF fix if space for under
+ * 1 device.
+ *
+ */
+
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/in.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/if_ether.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/notifier.h>
+#include <net/ip.h>
+#include <net/route.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <net/arp.h>
+#include <net/slhc.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#ifdef CONFIG_NET_ALIAS
+#include <linux/net_alias.h>
+#endif
+#ifdef CONFIG_KERNELD
+#include <linux/kerneld.h>
+#endif
+
+#ifndef MACH
+/*
+ * The list of packet types we will receive (as opposed to discard)
+ * and the routines to invoke.
+ */
+
+struct packet_type *ptype_base[16];
+struct packet_type *ptype_all = NULL; /* Taps */
+
+/*
+ * Device list lock
+ */
+
+int dev_lockct=0;
+
+/*
+ * Our notifier list
+ */
+
+struct notifier_block *netdev_chain=NULL;
+
+/*
+ * Device drivers call our routines to queue packets here. We empty the
+ * queue in the bottom half handler.
+ */
+
+static struct sk_buff_head backlog;
+
+/*
+ * We don't overdo the queue or we will thrash memory badly.
+ */
+
+static int backlog_size = 0;
+
+/*
+ * Return the lesser of the two values.
+ */
+
+static __inline__ unsigned long min(unsigned long a, unsigned long b)
+{
+ return (a < b)? a : b;
+}
+
+
+/******************************************************************************************
+
+ Protocol management and registration routines
+
+*******************************************************************************************/
+
+/*
+ * For efficiency
+ */
+
+static int dev_nit=0;
+
+/*
+ * Add a protocol ID to the list. Now that the input handler is
+ * smarter we can dispense with all the messy stuff that used to be
+ * here.
+ */
+
+void dev_add_pack(struct packet_type *pt)
+{
+ int hash;
+ if(pt->type==htons(ETH_P_ALL))
+ {
+ dev_nit++;
+ pt->next=ptype_all;
+ ptype_all=pt;
+ }
+ else
+ {
+ hash=ntohs(pt->type)&15;
+ pt->next = ptype_base[hash];
+ ptype_base[hash] = pt;
+ }
+}
+
+
+/*
+ * Remove a protocol ID from the list.
+ */
+
+void dev_remove_pack(struct packet_type *pt)
+{
+ struct packet_type **pt1;
+ if(pt->type==htons(ETH_P_ALL))
+ {
+ dev_nit--;
+ pt1=&ptype_all;
+ }
+ else
+ pt1=&ptype_base[ntohs(pt->type)&15];
+ for(; (*pt1)!=NULL; pt1=&((*pt1)->next))
+ {
+ if(pt==(*pt1))
+ {
+ *pt1=pt->next;
+ return;
+ }
+ }
+ printk("dev_remove_pack: %p not found.\n", pt);
+}
+
+/*****************************************************************************************
+
+ Device Interface Subroutines
+
+******************************************************************************************/
+
+/*
+ * Find an interface by name.
+ */
+
+struct device *dev_get(const char *name)
+{
+ struct device *dev;
+
+ for (dev = dev_base; dev != NULL; dev = dev->next)
+ {
+ if (strcmp(dev->name, name) == 0)
+ return(dev);
+ }
+ return NULL;
+}
+
+/*
+ * Find and possibly load an interface.
+ */
+
+#ifdef CONFIG_KERNELD
+
+extern __inline__ void dev_load(const char *name)
+{
+ char *sptr;
+
+ if(!dev_get(name)) {
+#ifdef CONFIG_NET_ALIAS
+ for (sptr=name ; *sptr ; sptr++) if(*sptr==':') break;
+ if (!(*sptr && *(sptr+1)))
+#endif
+ request_module(name);
+ }
+}
+
+#endif
+
+/*
+ * Prepare an interface for use.
+ */
+
+int dev_open(struct device *dev)
+{
+ int ret = 0;
+
+ /*
+ * Call device private open method
+ */
+ if (dev->open)
+ ret = dev->open(dev);
+
+ /*
+ * If it went open OK then set the flags
+ */
+
+ if (ret == 0)
+ {
+ dev->flags |= (IFF_UP | IFF_RUNNING);
+ /*
+ * Initialise multicasting status
+ */
+ dev_mc_upload(dev);
+ notifier_call_chain(&netdev_chain, NETDEV_UP, dev);
+ }
+ return(ret);
+}
+
+
+/*
+ * Completely shutdown an interface.
+ */
+
+int dev_close(struct device *dev)
+{
+ int ct=0;
+
+ /*
+ * Call the device specific close. This cannot fail.
+ * Only if device is UP
+ */
+
+ if ((dev->flags & IFF_UP) && dev->stop)
+ dev->stop(dev);
+
+ /*
+ * Device is now down.
+ */
+
+ dev->flags&=~(IFF_UP|IFF_RUNNING);
+
+ /*
+ * Tell people we are going down
+ */
+ notifier_call_chain(&netdev_chain, NETDEV_DOWN, dev);
+ /*
+ * Flush the multicast chain
+ */
+ dev_mc_discard(dev);
+ /*
+ * Blank the IP addresses
+ */
+ dev->pa_addr = 0;
+ dev->pa_dstaddr = 0;
+ dev->pa_brdaddr = 0;
+ dev->pa_mask = 0;
+ /*
+ * Purge any queued packets when we down the link
+ */
+ while(ct<DEV_NUMBUFFS)
+ {
+ struct sk_buff *skb;
+ while((skb=skb_dequeue(&dev->buffs[ct]))!=NULL)
+ if(skb->free)
+ kfree_skb(skb,FREE_WRITE);
+ ct++;
+ }
+ return(0);
+}
+
+
+/*
+ * Device change register/unregister. These are not inline or static
+ * as we export them to the world.
+ */
+
+int register_netdevice_notifier(struct notifier_block *nb)
+{
+ return notifier_chain_register(&netdev_chain, nb);
+}
+
+int unregister_netdevice_notifier(struct notifier_block *nb)
+{
+ return notifier_chain_unregister(&netdev_chain,nb);
+}
+
+/*
+ * Send (or queue for sending) a packet.
+ *
+ * IMPORTANT: When this is called to resend frames. The caller MUST
+ * already have locked the sk_buff. Apart from that we do the
+ * rest of the magic.
+ */
+
+void dev_queue_xmit(struct sk_buff *skb, struct device *dev, int pri)
+{
+ unsigned long flags;
+ struct sk_buff_head *list;
+ int retransmission = 0; /* used to say if the packet should go */
+ /* at the front or the back of the */
+ /* queue - front is a retransmit try */
+
+ if(pri>=0 && !skb_device_locked(skb))
+ skb_device_lock(skb); /* Shove a lock on the frame */
+#if CONFIG_SKB_CHECK
+ IS_SKB(skb);
+#endif
+ skb->dev = dev;
+
+ /*
+ * Negative priority is used to flag a frame that is being pulled from the
+ * queue front as a retransmit attempt. It therefore goes back on the queue
+ * start on a failure.
+ */
+
+ if (pri < 0)
+ {
+ pri = -pri-1;
+ retransmission = 1;
+ }
+
+#ifdef CONFIG_NET_DEBUG
+ if (pri >= DEV_NUMBUFFS)
+ {
+ printk("bad priority in dev_queue_xmit.\n");
+ pri = 1;
+ }
+#endif
+
+ /*
+ * If the address has not been resolved. Call the device header rebuilder.
+ * This can cover all protocols and technically not just ARP either.
+ */
+
+ if (!skb->arp && dev->rebuild_header(skb->data, dev, skb->raddr, skb)) {
+ return;
+ }
+
+ /*
+ *
+ * If dev is an alias, switch to its main device.
+ * "arp" resolution has been made with alias device, so
+ * arp entries refer to alias, not main.
+ *
+ */
+
+#ifdef CONFIG_NET_ALIAS
+ if (net_alias_is(dev))
+ skb->dev = dev = net_alias_main_dev(dev);
+#endif
+ list = dev->buffs + pri;
+
+ save_flags(flags);
+ /* if this isn't a retransmission, use the first packet instead... */
+ if (!retransmission) {
+ if (skb_queue_len(list)) {
+ /* avoid overrunning the device queue.. */
+ if (skb_queue_len(list) > dev->tx_queue_len) {
+ dev_kfree_skb(skb, FREE_WRITE);
+ return;
+ }
+ cli();
+ skb_device_unlock(skb); /* Buffer is on the device queue and can be freed safely */
+ __skb_queue_tail(list, skb);
+ skb = __skb_dequeue(list);
+ skb_device_lock(skb); /* New buffer needs locking down */
+ restore_flags(flags);
+ }
+
+ /* copy outgoing packets to any sniffer packet handlers */
+ if (dev_nit) {
+ struct packet_type *ptype;
+ skb->stamp=xtime;
+ for (ptype = ptype_all; ptype!=NULL; ptype = ptype->next)
+ {
+ /* Never send packets back to the socket
+ * they originated from - MvS (miquels@drinkel.ow.org)
+ */
+ if ((ptype->dev == dev || !ptype->dev) &&
+ ((struct sock *)ptype->data != skb->sk))
+ {
+ struct sk_buff *skb2;
+ if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL)
+ break;
+ skb2->h.raw = skb2->data + dev->hard_header_len;
+ skb2->mac.raw = skb2->data;
+ ptype->func(skb2, skb->dev, ptype);
+ }
+ }
+ }
+ }
+ start_bh_atomic();
+ if (dev->hard_start_xmit(skb, dev) == 0) {
+ /*
+ * Packet is now solely the responsibility of the driver
+ */
+ end_bh_atomic();
+ return;
+ }
+ end_bh_atomic();
+
+ /*
+ * Transmission failed, put skb back into a list. Once on the list it's safe and
+ * no longer device locked (it can be freed safely from the device queue)
+ */
+ cli();
+ skb_device_unlock(skb);
+ __skb_queue_head(list,skb);
+ restore_flags(flags);
+}
+
+/*
+ * Receive a packet from a device driver and queue it for the upper
+ * (protocol) levels. It always succeeds. This is the recommended
+ * interface to use.
+ */
+
+void netif_rx(struct sk_buff *skb)
+{
+ static int dropping = 0;
+
+ /*
+ * Any received buffers are un-owned and should be discarded
+ * when freed. These will be updated later as the frames get
+ * owners.
+ */
+
+ skb->sk = NULL;
+ skb->free = 1;
+ if(skb->stamp.tv_sec==0)
+ skb->stamp = xtime;
+
+ /*
+ * Check that we aren't overdoing things.
+ */
+
+ if (!backlog_size)
+ dropping = 0;
+ else if (backlog_size > 300)
+ dropping = 1;
+
+ if (dropping)
+ {
+ kfree_skb(skb, FREE_READ);
+ return;
+ }
+
+ /*
+ * Add it to the "backlog" queue.
+ */
+#if CONFIG_SKB_CHECK
+ IS_SKB(skb);
+#endif
+ skb_queue_tail(&backlog,skb);
+ backlog_size++;
+
+ /*
+ * If any packet arrived, mark it for processing after the
+ * hardware interrupt returns.
+ */
+
+#ifdef CONFIG_NET_RUNONIRQ /* Dont enable yet, needs some driver mods */
+ net_bh();
+#else
+ mark_bh(NET_BH);
+#endif
+ return;
+}
+
+/*
+ * This routine causes all interfaces to try to send some data.
+ */
+
+void dev_transmit(void)
+{
+ struct device *dev;
+
+ for (dev = dev_base; dev != NULL; dev = dev->next)
+ {
+ if (dev->flags != 0 && !dev->tbusy) {
+ /*
+ * Kick the device
+ */
+ dev_tint(dev);
+ }
+ }
+}
+
+
+/**********************************************************************************
+
+ Receive Queue Processor
+
+***********************************************************************************/
+
+/*
+ * This is a single non-reentrant routine which takes the received packet
+ * queue and throws it at the networking layers in the hope that something
+ * useful will emerge.
+ */
+
+volatile unsigned long in_bh = 0; /* Non-reentrant remember */
+
+int in_net_bh() /* Used by timer.c */
+{
+ return(in_bh==0?0:1);
+}
+
+/*
+ * When we are called the queue is ready to grab, the interrupts are
+ * on and hardware can interrupt and queue to the receive queue a we
+ * run with no problems.
+ * This is run as a bottom half after an interrupt handler that does
+ * mark_bh(NET_BH);
+ */
+
+void net_bh(void *tmp)
+{
+ struct sk_buff *skb;
+ struct packet_type *ptype;
+ struct packet_type *pt_prev;
+ unsigned short type;
+
+ /*
+ * Atomically check and mark our BUSY state.
+ */
+
+ if (set_bit(1, (void*)&in_bh))
+ return;
+
+ /*
+ * Can we send anything now? We want to clear the
+ * decks for any more sends that get done as we
+ * process the input. This also minimises the
+ * latency on a transmit interrupt bh.
+ */
+
+ dev_transmit();
+
+ /*
+ * Any data left to process. This may occur because a
+ * mark_bh() is done after we empty the queue including
+ * that from the device which does a mark_bh() just after
+ */
+
+ cli();
+
+ /*
+ * While the queue is not empty
+ */
+
+ while((skb=__skb_dequeue(&backlog))!=NULL)
+ {
+ /*
+ * We have a packet. Therefore the queue has shrunk
+ */
+ backlog_size--;
+
+ sti();
+
+ /*
+ * Bump the pointer to the next structure.
+ *
+ * On entry to the protocol layer. skb->data and
+ * skb->h.raw point to the MAC and encapsulated data
+ */
+
+ skb->h.raw = skb->data;
+
+ /*
+ * Fetch the packet protocol ID.
+ */
+
+ type = skb->protocol;
+
+ /*
+ * We got a packet ID. Now loop over the "known protocols"
+ * list. There are two lists. The ptype_all list of taps (normally empty)
+ * and the main protocol list which is hashed perfectly for normal protocols.
+ */
+ pt_prev = NULL;
+ for (ptype = ptype_all; ptype!=NULL; ptype=ptype->next)
+ {
+ if(pt_prev)
+ {
+ struct sk_buff *skb2=skb_clone(skb, GFP_ATOMIC);
+ if(skb2)
+ pt_prev->func(skb2,skb->dev, pt_prev);
+ }
+ pt_prev=ptype;
+ }
+
+ for (ptype = ptype_base[ntohs(type)&15]; ptype != NULL; ptype = ptype->next)
+ {
+ if (ptype->type == type && (!ptype->dev || ptype->dev==skb->dev))
+ {
+ /*
+ * We already have a match queued. Deliver
+ * to it and then remember the new match
+ */
+ if(pt_prev)
+ {
+ struct sk_buff *skb2;
+
+ skb2=skb_clone(skb, GFP_ATOMIC);
+
+ /*
+ * Kick the protocol handler. This should be fast
+ * and efficient code.
+ */
+
+ if(skb2)
+ pt_prev->func(skb2, skb->dev, pt_prev);
+ }
+ /* Remember the current last to do */
+ pt_prev=ptype;
+ }
+ } /* End of protocol list loop */
+
+ /*
+ * Is there a last item to send to ?
+ */
+
+ if(pt_prev)
+ pt_prev->func(skb, skb->dev, pt_prev);
+ /*
+ * Has an unknown packet has been received ?
+ */
+
+ else
+ kfree_skb(skb, FREE_WRITE);
+
+ /*
+ * Again, see if we can transmit anything now.
+ * [Ought to take this out judging by tests it slows
+ * us down not speeds us up]
+ */
+#ifdef XMIT_EVERY
+ dev_transmit();
+#endif
+ cli();
+ } /* End of queue loop */
+
+ /*
+ * We have emptied the queue
+ */
+
+ in_bh = 0;
+ sti();
+
+ /*
+ * One last output flush.
+ */
+
+#ifdef XMIT_AFTER
+ dev_transmit();
+#endif
+}
+
+
+/*
+ * This routine is called when an device driver (i.e. an
+ * interface) is ready to transmit a packet.
+ */
+
+void dev_tint(struct device *dev)
+{
+ int i;
+ unsigned long flags;
+ struct sk_buff_head * head;
+
+ /*
+ * aliases do not transmit (for now :) )
+ */
+
+#ifdef CONFIG_NET_ALIAS
+ if (net_alias_is(dev)) return;
+#endif
+ head = dev->buffs;
+ save_flags(flags);
+ cli();
+
+ /*
+ * Work the queues in priority order
+ */
+ for(i = 0;i < DEV_NUMBUFFS; i++,head++)
+ {
+ struct sk_buff *skb = skb_peek(head);
+
+ if (skb) {
+ __skb_unlink(skb, head);
+ /*
+ * Stop anyone freeing the buffer while we retransmit it
+ */
+ skb_device_lock(skb);
+ restore_flags(flags);
+ /*
+ * Feed them to the output stage and if it fails
+ * indicate they re-queue at the front.
+ */
+ dev_queue_xmit(skb,dev,-i - 1);
+ /*
+ * If we can take no more then stop here.
+ */
+ if (dev->tbusy)
+ return;
+ cli();
+ }
+ }
+ restore_flags(flags);
+}
+
+
+/*
+ * Perform a SIOCGIFCONF call. This structure will change
+ * size shortly, and there is nothing I can do about it.
+ * Thus we will need a 'compatibility mode'.
+ */
+
+static int dev_ifconf(char *arg)
+{
+ struct ifconf ifc;
+ struct ifreq ifr;
+ struct device *dev;
+ char *pos;
+ int len;
+ int err;
+
+ /*
+ * Fetch the caller's info block.
+ */
+
+ err=verify_area(VERIFY_WRITE, arg, sizeof(struct ifconf));
+ if(err)
+ return err;
+ memcpy_fromfs(&ifc, arg, sizeof(struct ifconf));
+ len = ifc.ifc_len;
+ pos = ifc.ifc_buf;
+
+ /*
+ * We now walk the device list filling each active device
+ * into the array.
+ */
+
+ err=verify_area(VERIFY_WRITE,pos,len);
+ if(err)
+ return err;
+
+ /*
+ * Loop over the interfaces, and write an info block for each.
+ */
+
+ for (dev = dev_base; dev != NULL; dev = dev->next)
+ {
+ if(!(dev->flags & IFF_UP)) /* Downed devices don't count */
+ continue;
+ /*
+ * Have we run out of space here ?
+ */
+
+ if (len < sizeof(struct ifreq))
+ break;
+
+ memset(&ifr, 0, sizeof(struct ifreq));
+ strcpy(ifr.ifr_name, dev->name);
+ (*(struct sockaddr_in *) &ifr.ifr_addr).sin_family = dev->family;
+ (*(struct sockaddr_in *) &ifr.ifr_addr).sin_addr.s_addr = dev->pa_addr;
+
+
+ /*
+ * Write this block to the caller's space.
+ */
+
+ memcpy_tofs(pos, &ifr, sizeof(struct ifreq));
+ pos += sizeof(struct ifreq);
+ len -= sizeof(struct ifreq);
+ }
+
+ /*
+ * All done. Write the updated control block back to the caller.
+ */
+
+ ifc.ifc_len = (pos - ifc.ifc_buf);
+ ifc.ifc_req = (struct ifreq *) ifc.ifc_buf;
+ memcpy_tofs(arg, &ifc, sizeof(struct ifconf));
+
+ /*
+ * Report how much was filled in
+ */
+
+ return(pos - arg);
+}
+
+
+/*
+ * This is invoked by the /proc filesystem handler to display a device
+ * in detail.
+ */
+
+#ifdef CONFIG_PROC_FS
+static int sprintf_stats(char *buffer, struct device *dev)
+{
+ struct enet_statistics *stats = (dev->get_stats ? dev->get_stats(dev): NULL);
+ int size;
+
+ if (stats)
+ size = sprintf(buffer, "%6s:%7d %4d %4d %4d %4d %8d %4d %4d %4d %5d %4d\n",
+ dev->name,
+ stats->rx_packets, stats->rx_errors,
+ stats->rx_dropped + stats->rx_missed_errors,
+ stats->rx_fifo_errors,
+ stats->rx_length_errors + stats->rx_over_errors
+ + stats->rx_crc_errors + stats->rx_frame_errors,
+ stats->tx_packets, stats->tx_errors, stats->tx_dropped,
+ stats->tx_fifo_errors, stats->collisions,
+ stats->tx_carrier_errors + stats->tx_aborted_errors
+ + stats->tx_window_errors + stats->tx_heartbeat_errors);
+ else
+ size = sprintf(buffer, "%6s: No statistics available.\n", dev->name);
+
+ return size;
+}
+
+/*
+ * Called from the PROCfs module. This now uses the new arbitrary sized /proc/net interface
+ * to create /proc/net/dev
+ */
+
+int dev_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+ int len=0;
+ off_t begin=0;
+ off_t pos=0;
+ int size;
+
+ struct device *dev;
+
+
+ size = sprintf(buffer, "Inter-| Receive | Transmit\n"
+ " face |packets errs drop fifo frame|packets errs drop fifo colls carrier\n");
+
+ pos+=size;
+ len+=size;
+
+
+ for (dev = dev_base; dev != NULL; dev = dev->next)
+ {
+ size = sprintf_stats(buffer+len, dev);
+ len+=size;
+ pos=begin+len;
+
+ if(pos<offset)
+ {
+ len=0;
+ begin=pos;
+ }
+ if(pos>offset+length)
+ break;
+ }
+
+ *start=buffer+(offset-begin); /* Start of wanted data */
+ len-=(offset-begin); /* Start slop */
+ if(len>length)
+ len=length; /* Ending slop */
+ return len;
+}
+#endif /* CONFIG_PROC_FS */
+
+
+/*
+ * This checks bitmasks for the ioctl calls for devices.
+ */
+
+static inline int bad_mask(unsigned long mask, unsigned long addr)
+{
+ if (addr & (mask = ~mask))
+ return 1;
+ mask = ntohl(mask);
+ if (mask & (mask+1))
+ return 1;
+ return 0;
+}
+
+/*
+ * Perform the SIOCxIFxxx calls.
+ *
+ * The socket layer has seen an ioctl the address family thinks is
+ * for the device. At this point we get invoked to make a decision
+ */
+
+static int dev_ifsioc(void *arg, unsigned int getset)
+{
+ struct ifreq ifr;
+ struct device *dev;
+ int ret;
+
+ /*
+ * Fetch the caller's info block into kernel space
+ */
+
+ int err=verify_area(VERIFY_WRITE, arg, sizeof(struct ifreq));
+ if(err)
+ return err;
+
+ memcpy_fromfs(&ifr, arg, sizeof(struct ifreq));
+
+ /*
+ * See which interface the caller is talking about.
+ */
+
+ /*
+ *
+ * net_alias_dev_get(): dev_get() with added alias naming magic.
+ * only allow alias creation/deletion if (getset==SIOCSIFADDR)
+ *
+ */
+
+#ifdef CONFIG_KERNELD
+ dev_load(ifr.ifr_name);
+#endif
+
+#ifdef CONFIG_NET_ALIAS
+ if ((dev = net_alias_dev_get(ifr.ifr_name, getset == SIOCSIFADDR, &err, NULL, NULL)) == NULL)
+ return(err);
+#else
+ if ((dev = dev_get(ifr.ifr_name)) == NULL)
+ return(-ENODEV);
+#endif
+ switch(getset)
+ {
+ case SIOCGIFFLAGS: /* Get interface flags */
+ ifr.ifr_flags = dev->flags;
+ goto rarok;
+
+ case SIOCSIFFLAGS: /* Set interface flags */
+ {
+ int old_flags = dev->flags;
+
+ /*
+ * We are not allowed to potentially close/unload
+ * a device until we get this lock.
+ */
+
+ dev_lock_wait();
+
+ /*
+ * Set the flags on our device.
+ */
+
+ dev->flags = (ifr.ifr_flags & (
+ IFF_BROADCAST | IFF_DEBUG | IFF_LOOPBACK |
+ IFF_POINTOPOINT | IFF_NOTRAILERS | IFF_RUNNING |
+ IFF_NOARP | IFF_PROMISC | IFF_ALLMULTI | IFF_SLAVE | IFF_MASTER
+ | IFF_MULTICAST)) | (dev->flags & IFF_UP);
+ /*
+ * Load in the correct multicast list now the flags have changed.
+ */
+
+ dev_mc_upload(dev);
+
+ /*
+ * Have we downed the interface. We handle IFF_UP ourselves
+ * according to user attempts to set it, rather than blindly
+ * setting it.
+ */
+
+ if ((old_flags^ifr.ifr_flags)&IFF_UP) /* Bit is different ? */
+ {
+ if(old_flags&IFF_UP) /* Gone down */
+ ret=dev_close(dev);
+ else /* Come up */
+ {
+ ret=dev_open(dev);
+ if(ret<0)
+ dev->flags&=~IFF_UP; /* Open failed */
+ }
+ }
+ else
+ ret=0;
+ /*
+ * Load in the correct multicast list now the flags have changed.
+ */
+
+ dev_mc_upload(dev);
+ }
+ break;
+
+ case SIOCGIFADDR: /* Get interface address (and family) */
+ if(ifr.ifr_addr.sa_family==AF_UNSPEC)
+ {
+ memcpy(ifr.ifr_hwaddr.sa_data,dev->dev_addr, MAX_ADDR_LEN);
+ ifr.ifr_hwaddr.sa_family=dev->type;
+ goto rarok;
+ }
+ else
+ {
+ (*(struct sockaddr_in *)
+ &ifr.ifr_addr).sin_addr.s_addr = dev->pa_addr;
+ (*(struct sockaddr_in *)
+ &ifr.ifr_addr).sin_family = dev->family;
+ (*(struct sockaddr_in *)
+ &ifr.ifr_addr).sin_port = 0;
+ }
+ goto rarok;
+
+ case SIOCSIFADDR: /* Set interface address (and family) */
+
+ /*
+ * BSDism. SIOCSIFADDR family=AF_UNSPEC sets the
+ * physical address. We can cope with this now.
+ */
+
+ if(ifr.ifr_addr.sa_family==AF_UNSPEC)
+ {
+ if(dev->set_mac_address==NULL)
+ return -EOPNOTSUPP;
+ ret=dev->set_mac_address(dev,&ifr.ifr_addr);
+ }
+ else
+ {
+
+ /*
+ * if dev is an alias, must rehash to update
+ * address change
+ */
+
+#ifdef CONFIG_NET_ALIAS
+ if (net_alias_is(dev))
+ net_alias_dev_rehash(dev ,&ifr.ifr_addr);
+#endif
+ dev->pa_addr = (*(struct sockaddr_in *)
+ &ifr.ifr_addr).sin_addr.s_addr;
+ dev->family = ifr.ifr_addr.sa_family;
+
+#ifdef CONFIG_INET
+ /* This is naughty. When net-032e comes out It wants moving into the net032
+ code not the kernel. Till then it can sit here (SIGH) */
+ dev->pa_mask = ip_get_mask(dev->pa_addr);
+#endif
+ dev->pa_brdaddr = dev->pa_addr | ~dev->pa_mask;
+ ret = 0;
+ }
+ break;
+
+ case SIOCGIFBRDADDR: /* Get the broadcast address */
+ (*(struct sockaddr_in *)
+ &ifr.ifr_broadaddr).sin_addr.s_addr = dev->pa_brdaddr;
+ (*(struct sockaddr_in *)
+ &ifr.ifr_broadaddr).sin_family = dev->family;
+ (*(struct sockaddr_in *)
+ &ifr.ifr_broadaddr).sin_port = 0;
+ goto rarok;
+
+ case SIOCSIFBRDADDR: /* Set the broadcast address */
+ dev->pa_brdaddr = (*(struct sockaddr_in *)
+ &ifr.ifr_broadaddr).sin_addr.s_addr;
+ ret = 0;
+ break;
+
+ case SIOCGIFDSTADDR: /* Get the destination address (for point-to-point links) */
+ (*(struct sockaddr_in *)
+ &ifr.ifr_dstaddr).sin_addr.s_addr = dev->pa_dstaddr;
+ (*(struct sockaddr_in *)
+ &ifr.ifr_dstaddr).sin_family = dev->family;
+ (*(struct sockaddr_in *)
+ &ifr.ifr_dstaddr).sin_port = 0;
+ goto rarok;
+
+ case SIOCSIFDSTADDR: /* Set the destination address (for point-to-point links) */
+ dev->pa_dstaddr = (*(struct sockaddr_in *)
+ &ifr.ifr_dstaddr).sin_addr.s_addr;
+ ret = 0;
+ break;
+
+ case SIOCGIFNETMASK: /* Get the netmask for the interface */
+ (*(struct sockaddr_in *)
+ &ifr.ifr_netmask).sin_addr.s_addr = dev->pa_mask;
+ (*(struct sockaddr_in *)
+ &ifr.ifr_netmask).sin_family = dev->family;
+ (*(struct sockaddr_in *)
+ &ifr.ifr_netmask).sin_port = 0;
+ goto rarok;
+
+ case SIOCSIFNETMASK: /* Set the netmask for the interface */
+ {
+ unsigned long mask = (*(struct sockaddr_in *)
+ &ifr.ifr_netmask).sin_addr.s_addr;
+ ret = -EINVAL;
+ /*
+ * The mask we set must be legal.
+ */
+ if (bad_mask(mask,0))
+ break;
+ dev->pa_mask = mask;
+ ret = 0;
+ }
+ break;
+
+ case SIOCGIFMETRIC: /* Get the metric on the interface (currently unused) */
+
+ ifr.ifr_metric = dev->metric;
+ goto rarok;
+
+ case SIOCSIFMETRIC: /* Set the metric on the interface (currently unused) */
+ dev->metric = ifr.ifr_metric;
+ ret=0;
+ break;
+
+ case SIOCGIFMTU: /* Get the MTU of a device */
+ ifr.ifr_mtu = dev->mtu;
+ goto rarok;
+
+ case SIOCSIFMTU: /* Set the MTU of a device */
+
+ /*
+ * MTU must be positive.
+ */
+
+ if(ifr.ifr_mtu<68)
+ return -EINVAL;
+ dev->mtu = ifr.ifr_mtu;
+ ret = 0;
+ break;
+
+ case SIOCGIFMEM: /* Get the per device memory space. We can add this but currently
+ do not support it */
+ ret = -EINVAL;
+ break;
+
+ case SIOCSIFMEM: /* Set the per device memory buffer space. Not applicable in our case */
+ ret = -EINVAL;
+ break;
+
+ case SIOCGIFHWADDR:
+ memcpy(ifr.ifr_hwaddr.sa_data,dev->dev_addr, MAX_ADDR_LEN);
+ ifr.ifr_hwaddr.sa_family=dev->type;
+ goto rarok;
+
+ case SIOCSIFHWADDR:
+ if(dev->set_mac_address==NULL)
+ return -EOPNOTSUPP;
+ if(ifr.ifr_hwaddr.sa_family!=dev->type)
+ return -EINVAL;
+ ret=dev->set_mac_address(dev,&ifr.ifr_hwaddr);
+ break;
+
+ case SIOCGIFMAP:
+ ifr.ifr_map.mem_start=dev->mem_start;
+ ifr.ifr_map.mem_end=dev->mem_end;
+ ifr.ifr_map.base_addr=dev->base_addr;
+ ifr.ifr_map.irq=dev->irq;
+ ifr.ifr_map.dma=dev->dma;
+ ifr.ifr_map.port=dev->if_port;
+ goto rarok;
+
+ case SIOCSIFMAP:
+ if(dev->set_config==NULL)
+ return -EOPNOTSUPP;
+ return dev->set_config(dev,&ifr.ifr_map);
+
+ case SIOCADDMULTI:
+ if(dev->set_multicast_list==NULL)
+ return -EINVAL;
+ if(ifr.ifr_hwaddr.sa_family!=AF_UNSPEC)
+ return -EINVAL;
+ dev_mc_add(dev,ifr.ifr_hwaddr.sa_data, dev->addr_len, 1);
+ return 0;
+
+ case SIOCDELMULTI:
+ if(dev->set_multicast_list==NULL)
+ return -EINVAL;
+ if(ifr.ifr_hwaddr.sa_family!=AF_UNSPEC)
+ return -EINVAL;
+ dev_mc_delete(dev,ifr.ifr_hwaddr.sa_data,dev->addr_len, 1);
+ return 0;
+ /*
+ * Unknown or private ioctl
+ */
+
+ default:
+ if((getset >= SIOCDEVPRIVATE) &&
+ (getset <= (SIOCDEVPRIVATE + 15))) {
+ if(dev->do_ioctl==NULL)
+ return -EOPNOTSUPP;
+ ret=dev->do_ioctl(dev, &ifr, getset);
+ memcpy_tofs(arg,&ifr,sizeof(struct ifreq));
+ break;
+ }
+
+ ret = -EINVAL;
+ }
+ return(ret);
+/*
+ * The load of calls that return an ifreq and ok (saves memory).
+ */
+rarok:
+ memcpy_tofs(arg, &ifr, sizeof(struct ifreq));
+ return 0;
+}
+
+
+/*
+ * This function handles all "interface"-type I/O control requests. The actual
+ * 'doing' part of this is dev_ifsioc above.
+ */
+
+int dev_ioctl(unsigned int cmd, void *arg)
+{
+ switch(cmd)
+ {
+ case SIOCGIFCONF:
+ (void) dev_ifconf((char *) arg);
+ return 0;
+
+ /*
+ * Ioctl calls that can be done by all.
+ */
+
+ case SIOCGIFFLAGS:
+ case SIOCGIFADDR:
+ case SIOCGIFDSTADDR:
+ case SIOCGIFBRDADDR:
+ case SIOCGIFNETMASK:
+ case SIOCGIFMETRIC:
+ case SIOCGIFMTU:
+ case SIOCGIFMEM:
+ case SIOCGIFHWADDR:
+ case SIOCSIFHWADDR:
+ case SIOCGIFSLAVE:
+ case SIOCGIFMAP:
+ return dev_ifsioc(arg, cmd);
+
+ /*
+ * Ioctl calls requiring the power of a superuser
+ */
+
+ case SIOCSIFFLAGS:
+ case SIOCSIFADDR:
+ case SIOCSIFDSTADDR:
+ case SIOCSIFBRDADDR:
+ case SIOCSIFNETMASK:
+ case SIOCSIFMETRIC:
+ case SIOCSIFMTU:
+ case SIOCSIFMEM:
+ case SIOCSIFMAP:
+ case SIOCSIFSLAVE:
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ if (!suser())
+ return -EPERM;
+ return dev_ifsioc(arg, cmd);
+
+ case SIOCSIFLINK:
+ return -EINVAL;
+
+ /*
+ * Unknown or private ioctl.
+ */
+
+ default:
+ if((cmd >= SIOCDEVPRIVATE) &&
+ (cmd <= (SIOCDEVPRIVATE + 15))) {
+ return dev_ifsioc(arg, cmd);
+ }
+ return -EINVAL;
+ }
+}
+#endif /* ! MACH */
+
+/*
+ * Initialize the DEV module. At boot time this walks the device list and
+ * unhooks any devices that fail to initialise (normally hardware not
+ * present) and leaves us with a valid list of present and active devices.
+ *
+ */
+extern int lance_init(void);
+extern int pi_init(void);
+extern int dec21040_init(void);
+
+int net_dev_init(void)
+{
+ struct device *dev, **dp;
+
+#ifndef MACH
+ /*
+ * Initialise the packet receive queue.
+ */
+
+ skb_queue_head_init(&backlog);
+#endif
+
+ /*
+ * This is VeryUgly(tm).
+ *
+ * Some devices want to be initialized eary..
+ */
+#if defined(CONFIG_LANCE)
+ lance_init();
+#endif
+#if defined(CONFIG_PI)
+ pi_init();
+#endif
+#if defined(CONFIG_PT)
+ pt_init();
+#endif
+#if defined(CONFIG_DEC_ELCP)
+ dec21040_init();
+#endif
+ /*
+ * SLHC if present needs attaching so other people see it
+ * even if not opened.
+ */
+#if (defined(CONFIG_SLIP_COMPRESSED) || defined(CONFIG_PPP)) && defined(CONFIG_SLHC_BUILTIN)
+ slhc_install();
+#endif
+
+ /*
+ * Add the devices.
+ * If the call to dev->init fails, the dev is removed
+ * from the chain disconnecting the device until the
+ * next reboot.
+ */
+
+ dp = &dev_base;
+ while ((dev = *dp) != NULL)
+ {
+ int i;
+ for (i = 0; i < DEV_NUMBUFFS; i++) {
+ skb_queue_head_init(dev->buffs + i);
+ }
+
+ if (dev->init && dev->init(dev))
+ {
+ /*
+ * It failed to come up. Unhook it.
+ */
+ *dp = dev->next;
+ }
+ else
+ {
+ dp = &dev->next;
+ }
+ }
+
+#ifdef CONFIG_PROC_FS
+ proc_net_register(&(struct proc_dir_entry) {
+ PROC_NET_DEV, 3, "dev",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ dev_get_info
+ });
+#endif
+
+ /*
+ * Initialise net_alias engine
+ *
+ * - register net_alias device notifier
+ * - register proc entries: /proc/net/alias_types
+ * /proc/net/aliases
+ */
+
+#ifdef CONFIG_NET_ALIAS
+ net_alias_init();
+#endif
+
+ bh_base[NET_BH].routine = net_bh;
+ enable_bh(NET_BH);
+ return 0;
+}
diff --git a/i386/i386at/gpl/linux/net/e2100.c b/i386/i386at/gpl/linux/net/e2100.c
new file mode 100644
index 00000000..fb0f1de6
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/e2100.c
@@ -0,0 +1,456 @@
+/* e2100.c: A Cabletron E2100 series ethernet driver for linux. */
+/*
+ Written 1993-1994 by Donald Becker.
+
+ Copyright 1994 by Donald Becker.
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency. This software may be used and
+ distributed according to the terms of the GNU Public License,
+ incorporated herein by reference.
+
+ This is a driver for the Cabletron E2100 series ethercards.
+
+ The Author may be reached as becker@cesdis.gsfc.nasa.gov, or
+ C/O Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
+
+ The E2100 series ethercard is a fairly generic shared memory 8390
+ implementation. The only unusual aspect is the way the shared memory
+ registers are set: first you do an inb() in what is normally the
+ station address region, and the low three bits of next outb() *address*
+ is used as the write value for that register. Either someone wasn't
+ too used to dem bit en bites, or they were trying to obfuscate the
+ programming interface.
+
+ There is an additional complication when setting the window on the packet
+ buffer. You must first do a read into the packet buffer region with the
+ low 8 address bits the address setting the page for the start of the packet
+ buffer window, and then do the above operation. See mem_on() for details.
+
+ One bug on the chip is that even a hard reset won't disable the memory
+ window, usually resulting in a hung machine if mem_off() isn't called.
+ If this happens, you must power down the machine for about 30 seconds.
+*/
+
+static const char *version =
+ "e2100.c:v1.01 7/21/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include "8390.h"
+
+static int e21_probe_list[] = {0x300, 0x280, 0x380, 0x220, 0};
+
+/* Offsets from the base_addr.
+ Read from the ASIC register, and the low three bits of the next outb()
+ address is used to set the corresponding register. */
+#define E21_NIC_OFFSET 0 /* Offset to the 8390 NIC. */
+#define E21_ASIC 0x10
+#define E21_MEM_ENABLE 0x10
+#define E21_MEM_ON 0x05 /* Enable memory in 16 bit mode. */
+#define E21_MEM_ON_8 0x07 /* Enable memory in 8 bit mode. */
+#define E21_MEM_BASE 0x11
+#define E21_IRQ_LOW 0x12 /* The low three bits of the IRQ number. */
+#define E21_IRQ_HIGH 0x14 /* The high IRQ bit and media select ... */
+#define E21_MEDIA 0x14 /* (alias). */
+#define E21_ALT_IFPORT 0x02 /* Set to use the other (BNC,AUI) port. */
+#define E21_BIG_MEM 0x04 /* Use a bigger (64K) buffer (we don't) */
+#define E21_SAPROM 0x10 /* Offset to station address data. */
+#define E21_IO_EXTENT 0x20
+
+extern inline void mem_on(short port, volatile char *mem_base,
+ unsigned char start_page )
+{
+ /* This is a little weird: set the shared memory window by doing a
+ read. The low address bits specify the starting page. */
+ mem_base[start_page];
+ inb(port + E21_MEM_ENABLE);
+ outb(E21_MEM_ON, port + E21_MEM_ENABLE + E21_MEM_ON);
+}
+
+extern inline void mem_off(short port)
+{
+ inb(port + E21_MEM_ENABLE);
+ outb(0x00, port + E21_MEM_ENABLE);
+}
+
+/* In other drivers I put the TX pages first, but the E2100 window circuitry
+ is designed to have a 4K Tx region last. The windowing circuitry wraps the
+ window at 0x2fff->0x0000 so that the packets at e.g. 0x2f00 in the RX ring
+ appear contiguously in the window. */
+#define E21_RX_START_PG 0x00 /* First page of RX buffer */
+#define E21_RX_STOP_PG 0x30 /* Last page +1 of RX ring */
+#define E21_BIG_RX_STOP_PG 0xF0 /* Last page +1 of RX ring */
+#define E21_TX_START_PG E21_RX_STOP_PG /* First page of TX buffer */
+
+int e2100_probe(struct device *dev);
+int e21_probe1(struct device *dev, int ioaddr);
+
+static int e21_open(struct device *dev);
+static void e21_reset_8390(struct device *dev);
+static void e21_block_input(struct device *dev, int count,
+ struct sk_buff *skb, int ring_offset);
+static void e21_block_output(struct device *dev, int count,
+ const unsigned char *buf, const start_page);
+static void e21_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
+ int ring_page);
+
+static int e21_close(struct device *dev);
+
+
+/* Probe for the E2100 series ethercards. These cards have an 8390 at the
+ base address and the station address at both offset 0x10 and 0x18. I read
+ the station address from offset 0x18 to avoid the dataport of NE2000
+ ethercards, and look for Ctron's unique ID (first three octets of the
+ station address).
+ */
+
+int e2100_probe(struct device *dev)
+{
+ int *port;
+ int base_addr = dev->base_addr;
+
+ if (base_addr > 0x1ff) /* Check a single specified location. */
+ return e21_probe1(dev, base_addr);
+ else if (base_addr != 0) /* Don't probe at all. */
+ return ENXIO;
+
+ for (port = e21_probe_list; *port; port++) {
+ if (check_region(*port, E21_IO_EXTENT))
+ continue;
+ if (e21_probe1(dev, *port) == 0)
+ return 0;
+ }
+
+ return ENODEV;
+}
+
+int e21_probe1(struct device *dev, int ioaddr)
+{
+ int i, status;
+ unsigned char *station_addr = dev->dev_addr;
+ static unsigned version_printed = 0;
+
+ /* First check the station address for the Ctron prefix. */
+ if (inb(ioaddr + E21_SAPROM + 0) != 0x00
+ || inb(ioaddr + E21_SAPROM + 1) != 0x00
+ || inb(ioaddr + E21_SAPROM + 2) != 0x1d)
+ return ENODEV;
+
+ /* Verify by making certain that there is a 8390 at there. */
+ outb(E8390_NODMA + E8390_STOP, ioaddr);
+ SLOW_DOWN_IO;
+ status = inb(ioaddr);
+ if (status != 0x21 && status != 0x23)
+ return ENODEV;
+
+ /* Read the station address PROM. */
+ for (i = 0; i < 6; i++)
+ station_addr[i] = inb(ioaddr + E21_SAPROM + i);
+
+ inb(ioaddr + E21_MEDIA); /* Point to media selection. */
+ outb(0, ioaddr + E21_ASIC); /* and disable the secondary interface. */
+
+ if (ei_debug && version_printed++ == 0)
+ printk(version);
+
+ /* We should have a "dev" from Space.c or the static module table. */
+ if (dev == NULL) {
+ printk("e2100.c: Passed a NULL device.\n");
+ dev = init_etherdev(0, 0);
+ }
+
+ printk("%s: E21** at %#3x,", dev->name, ioaddr);
+ for (i = 0; i < 6; i++)
+ printk(" %02X", station_addr[i]);
+
+ if (dev->irq < 2) {
+ int irqlist[] = {15,11,10,12,5,9,3,4}, i;
+ for (i = 0; i < 8; i++)
+ if (request_irq (irqlist[i], NULL, 0, "bogus") != -EBUSY) {
+ dev->irq = irqlist[i];
+ break;
+ }
+ if (i >= 8) {
+ printk(" unable to get IRQ %d.\n", dev->irq);
+ return EAGAIN;
+ }
+ } else if (dev->irq == 2) /* Fixup luser bogosity: IRQ2 is really IRQ9 */
+ dev->irq = 9;
+
+ /* Allocate dev->priv and fill in 8390 specific dev fields. */
+ if (ethdev_init(dev)) {
+ printk (" unable to get memory for dev->priv.\n");
+ return -ENOMEM;
+ }
+
+ /* Grab the region so we can find a different board if IRQ select fails. */
+ request_region(ioaddr, E21_IO_EXTENT, "e2100");
+
+ /* The 8390 is at the base address. */
+ dev->base_addr = ioaddr;
+
+ ei_status.name = "E2100";
+ ei_status.word16 = 1;
+ ei_status.tx_start_page = E21_TX_START_PG;
+ ei_status.rx_start_page = E21_RX_START_PG;
+ ei_status.stop_page = E21_RX_STOP_PG;
+ ei_status.saved_irq = dev->irq;
+
+ /* Check the media port used. The port can be passed in on the
+ low mem_end bits. */
+ if (dev->mem_end & 15)
+ dev->if_port = dev->mem_end & 7;
+ else {
+ dev->if_port = 0;
+ inb(ioaddr + E21_MEDIA); /* Turn automatic media detection on. */
+ for(i = 0; i < 6; i++)
+ if (station_addr[i] != inb(ioaddr + E21_SAPROM + 8 + i)) {
+ dev->if_port = 1;
+ break;
+ }
+ }
+
+ /* Never map in the E21 shared memory unless you are actively using it.
+ Also, the shared memory has effective only one setting -- spread all
+ over the 128K region! */
+ if (dev->mem_start == 0)
+ dev->mem_start = 0xd0000;
+
+#ifdef notdef
+ /* These values are unused. The E2100 has a 2K window into the packet
+ buffer. The window can be set to start on any page boundary. */
+ dev->rmem_start = dev->mem_start + TX_PAGES*256;
+ dev->mem_end = dev->rmem_end = dev->mem_start + 2*1024;
+#endif
+
+ printk(", IRQ %d, %s media, memory @ %#lx.\n", dev->irq,
+ dev->if_port ? "secondary" : "primary", dev->mem_start);
+
+ ei_status.reset_8390 = &e21_reset_8390;
+ ei_status.block_input = &e21_block_input;
+ ei_status.block_output = &e21_block_output;
+ ei_status.get_8390_hdr = &e21_get_8390_hdr;
+ dev->open = &e21_open;
+ dev->stop = &e21_close;
+ NS8390_init(dev, 0);
+
+ return 0;
+}
+
+static int
+e21_open(struct device *dev)
+{
+ short ioaddr = dev->base_addr;
+
+ if (request_irq(dev->irq, ei_interrupt, 0, "e2100")) {
+ return EBUSY;
+ }
+ irq2dev_map[dev->irq] = dev;
+
+ /* Set the interrupt line and memory base on the hardware. */
+ inb(ioaddr + E21_IRQ_LOW);
+ outb(0, ioaddr + E21_ASIC + (dev->irq & 7));
+ inb(ioaddr + E21_IRQ_HIGH); /* High IRQ bit, and if_port. */
+ outb(0, ioaddr + E21_ASIC + (dev->irq > 7 ? 1:0)
+ + (dev->if_port ? E21_ALT_IFPORT : 0));
+ inb(ioaddr + E21_MEM_BASE);
+ outb(0, ioaddr + E21_ASIC + ((dev->mem_start >> 17) & 7));
+
+ ei_open(dev);
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static void
+e21_reset_8390(struct device *dev)
+{
+ short ioaddr = dev->base_addr;
+
+ outb(0x01, ioaddr);
+ if (ei_debug > 1) printk("resetting the E2180x3 t=%ld...", jiffies);
+ ei_status.txing = 0;
+
+ /* Set up the ASIC registers, just in case something changed them. */
+
+ if (ei_debug > 1) printk("reset done\n");
+ return;
+}
+
+/* Grab the 8390 specific header. We put the 2k window so the header page
+ appears at the start of the shared memory. */
+
+static void
+e21_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+
+ short ioaddr = dev->base_addr;
+ char *shared_mem = (char *)dev->mem_start;
+
+ mem_on(ioaddr, shared_mem, ring_page);
+
+#ifdef notdef
+ /* Officially this is what we are doing, but the readl() is faster */
+ memcpy_fromio(hdr, shared_mem, sizeof(struct e8390_pkt_hdr));
+#else
+ ((unsigned int*)hdr)[0] = readl(shared_mem);
+#endif
+
+ /* Turn off memory access: we would need to reprogram the window anyway. */
+ mem_off(ioaddr);
+
+}
+
+/* Block input and output are easy on shared memory ethercards.
+ The E21xx makes block_input() especially easy by wrapping the top
+ ring buffer to the bottom automatically. */
+static void
+e21_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset)
+{
+ short ioaddr = dev->base_addr;
+ char *shared_mem = (char *)dev->mem_start;
+
+ mem_on(ioaddr, shared_mem, (ring_offset>>8));
+
+ /* Packet is always in one chunk -- we can copy + cksum. */
+ eth_io_copy_and_sum(skb, dev->mem_start + (ring_offset & 0xff), count, 0);
+
+ mem_off(ioaddr);
+}
+
+static void
+e21_block_output(struct device *dev, int count, const unsigned char *buf,
+ int start_page)
+{
+ short ioaddr = dev->base_addr;
+ volatile char *shared_mem = (char *)dev->mem_start;
+
+ /* Set the shared memory window start by doing a read, with the low address
+ bits specifying the starting page. */
+ readb(shared_mem + start_page);
+ mem_on(ioaddr, shared_mem, start_page);
+
+ memcpy_toio(shared_mem, buf, count);
+ mem_off(ioaddr);
+}
+
+static int
+e21_close(struct device *dev)
+{
+ short ioaddr = dev->base_addr;
+
+ if (ei_debug > 1)
+ printk("%s: Shutting down ethercard.\n", dev->name);
+
+ free_irq(dev->irq);
+ dev->irq = ei_status.saved_irq;
+
+ /* Shut off the interrupt line and secondary interface. */
+ inb(ioaddr + E21_IRQ_LOW);
+ outb(0, ioaddr + E21_ASIC);
+ inb(ioaddr + E21_IRQ_HIGH); /* High IRQ bit, and if_port. */
+ outb(0, ioaddr + E21_ASIC);
+
+ irq2dev_map[dev->irq] = NULL;
+
+ ei_close(dev);
+
+ /* Double-check that the memory has been turned off, because really
+ really bad things happen if it isn't. */
+ mem_off(ioaddr);
+
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+}
+
+#ifdef HAVE_DEVLIST
+struct netdev_entry e21_drv =
+{"e21", e21_probe1, E21_IO_EXTENT, e21_probe_list};
+#endif
+
+
+#ifdef MODULE
+#define MAX_E21_CARDS 4 /* Max number of E21 cards per module */
+#define NAMELEN 8 /* # of chars for storing dev->name */
+static char namelist[NAMELEN * MAX_E21_CARDS] = { 0, };
+static struct device dev_e21[MAX_E21_CARDS] = {
+ {
+ NULL, /* assign a chunk of namelist[] below */
+ 0, 0, 0, 0,
+ 0, 0,
+ 0, 0, 0, NULL, NULL
+ },
+};
+
+static int io[MAX_E21_CARDS] = { 0, };
+static int irq[MAX_E21_CARDS] = { 0, };
+static int mem[MAX_E21_CARDS] = { 0, };
+static int xcvr[MAX_E21_CARDS] = { 0, }; /* choose int. or ext. xcvr */
+
+/* This is set up so that only a single autoprobe takes place per call.
+ISA device autoprobes on a running machine are not recommended. */
+int
+init_module(void)
+{
+ int this_dev, found = 0;
+
+ for (this_dev = 0; this_dev < MAX_E21_CARDS; this_dev++) {
+ struct device *dev = &dev_e21[this_dev];
+ dev->name = namelist+(NAMELEN*this_dev);
+ dev->irq = irq[this_dev];
+ dev->base_addr = io[this_dev];
+ dev->mem_start = mem[this_dev];
+ dev->mem_end = xcvr[this_dev]; /* low 4bits = xcvr sel. */
+ dev->init = e2100_probe;
+ if (io[this_dev] == 0) {
+ if (this_dev != 0) break; /* only autoprobe 1st one */
+ printk(KERN_NOTICE "e2100.c: Presently autoprobing (not recommended) for a single card.\n");
+ }
+ if (register_netdev(dev) != 0) {
+ printk(KERN_WARNING "e2100.c: No E2100 card found (i/o = 0x%x).\n", io[this_dev]);
+ if (found != 0) return 0; /* Got at least one. */
+ return -ENXIO;
+ }
+ found++;
+ }
+
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ int this_dev;
+
+ for (this_dev = 0; this_dev < MAX_E21_CARDS; this_dev++) {
+ struct device *dev = &dev_e21[this_dev];
+ if (dev->priv != NULL) {
+ /* NB: e21_close() handles free_irq + irq2dev map */
+ kfree(dev->priv);
+ dev->priv = NULL;
+ release_region(dev->base_addr, E21_IO_EXTENT);
+ unregister_netdev(dev);
+ }
+ }
+}
+#endif /* MODULE */
+
+/*
+ * Local variables:
+ * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c e2100.c"
+ * version-control: t
+ * tab-width: 4
+ * kept-new-versions: 5
+ * End:
+ */
diff --git a/i386/i386at/gpl/linux/net/eepro.c b/i386/i386at/gpl/linux/net/eepro.c
new file mode 100644
index 00000000..2aa2bd14
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/eepro.c
@@ -0,0 +1,1169 @@
+/* eepro.c: Intel EtherExpress Pro/10 device driver for Linux. */
+/*
+ Written 1994, 1995 by Bao C. Ha.
+
+ Copyright (C) 1994, 1995 by Bao C. Ha.
+
+ This software may be used and distributed
+ according to the terms of the GNU Public License,
+ incorporated herein by reference.
+
+ The author may be reached at bao@saigon.async.com
+ or 418 Hastings Place, Martinez, GA 30907.
+
+ Things remaining to do:
+ Better record keeping of errors.
+ Eliminate transmit interrupt to reduce overhead.
+ Implement "concurrent processing". I won't be doing it!
+ Allow changes to the partition of the transmit and receive
+ buffers, currently the ratio is 3:1 of receive to transmit
+ buffer ratio.
+
+ Bugs:
+
+ If you have a problem of not detecting the 82595 during a
+ reboot (warm reset), disable the FLASH memory should fix it.
+ This is a compatibility hardware problem.
+
+ Versions:
+
+ 0.07a Fix a stat report which counts every packet as a
+ heart-beat failure. (BCH, 6/3/95)
+
+ 0.07 Modified to support all other 82595-based lan cards.
+ The IRQ vector of the EtherExpress Pro will be set
+ according to the value saved in the EEPROM. For other
+ cards, I will do autoirq_request() to grab the next
+ available interrupt vector. (BCH, 3/17/95)
+
+ 0.06a,b Interim released. Minor changes in the comments and
+ print out format. (BCH, 3/9/95 and 3/14/95)
+
+ 0.06 First stable release that I am comfortable with. (BCH,
+ 3/2/95)
+
+ 0.05 Complete testing of multicast. (BCH, 2/23/95)
+
+ 0.04 Adding multicast support. (BCH, 2/14/95)
+
+ 0.03 First widely alpha release for public testing.
+ (BCH, 2/14/95)
+
+*/
+
+static const char *version =
+ "eepro.c: v0.07a 6/5/95 Bao C. Ha (bao@saigon.async.com)\n";
+
+#include <linux/module.h>
+
+/*
+ Sources:
+
+ This driver wouldn't have been written without the availability
+ of the Crynwr's Lan595 driver source code. It helps me to
+ familiarize with the 82595 chipset while waiting for the Intel
+ documentation. I also learned how to detect the 82595 using
+ the packet driver's technique.
+
+ This driver is written by cutting and pasting the skeleton.c driver
+ provided by Donald Becker. I also borrowed the EEPROM routine from
+ Donald Becker's 82586 driver.
+
+ Datasheet for the Intel 82595. It provides just enough info that
+ the casual reader might think that it documents the i82595.
+
+ The User Manual for the 82595. It provides a lot of the missing
+ information.
+
+*/
+
+#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/in.h>
+#include <linux/malloc.h>
+#include <linux/string.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>
+
+
+/* First, a few definitions that the brave might change. */
+/* A zero-terminated list of I/O addresses to be probed. */
+static unsigned int eepro_portlist[] =
+ { 0x200, 0x240, 0x280, 0x2C0, 0x300, 0x320, 0x340, 0x360, 0};
+
+/* use 0 for production, 1 for verification, >2 for debug */
+#ifndef NET_DEBUG
+#define NET_DEBUG 2
+#endif
+static unsigned int net_debug = NET_DEBUG;
+
+/* The number of low I/O ports used by the ethercard. */
+#define EEPRO_IO_EXTENT 16
+
+/* Information that need to be kept for each board. */
+struct eepro_local {
+ struct enet_statistics stats;
+ unsigned rx_start;
+ unsigned tx_start; /* start of the transmit chain */
+ int tx_last; /* pointer to last packet in the transmit chain */
+ unsigned tx_end; /* end of the transmit chain (plus 1) */
+ int eepro; /* a flag, TRUE=1 for the EtherExpress Pro/10,
+ FALSE = 0 for other 82595-based lan cards. */
+};
+
+/* The station (ethernet) address prefix, used for IDing the board. */
+#define SA_ADDR0 0x00
+#define SA_ADDR1 0xaa
+#define SA_ADDR2 0x00
+
+/* Index to functions, as function prototypes. */
+
+extern int eepro_probe(struct device *dev);
+
+static int eepro_probe1(struct device *dev, short ioaddr);
+static int eepro_open(struct device *dev);
+static int eepro_send_packet(struct sk_buff *skb, struct device *dev);
+static void eepro_interrupt(int irq, struct pt_regs *regs);
+static void eepro_rx(struct device *dev);
+static void eepro_transmit_interrupt(struct device *dev);
+static int eepro_close(struct device *dev);
+static struct enet_statistics *eepro_get_stats(struct device *dev);
+static void set_multicast_list(struct device *dev);
+
+static int read_eeprom(int ioaddr, int location);
+static void hardware_send_packet(struct device *dev, void *buf, short length);
+static int eepro_grab_irq(struct device *dev);
+
+/*
+ Details of the i82595.
+
+You will need either the datasheet or the user manual to understand what
+is going on here. The 82595 is very different from the 82586, 82593.
+
+The receive algorithm in eepro_rx() is just an implementation of the
+RCV ring structure that the Intel 82595 imposes at the hardware level.
+The receive buffer is set at 24K, and the transmit buffer is 8K. I
+am assuming that the total buffer memory is 32K, which is true for the
+Intel EtherExpress Pro/10. If it is less than that on a generic card,
+the driver will be broken.
+
+The transmit algorithm in the hardware_send_packet() is similar to the
+one in the eepro_rx(). The transmit buffer is a ring linked list.
+I just queue the next available packet to the end of the list. In my
+system, the 82595 is so fast that the list seems to always contain a
+single packet. In other systems with faster computers and more congested
+network traffics, the ring linked list should improve performance by
+allowing up to 8K worth of packets to be queued.
+
+*/
+#define RAM_SIZE 0x8000
+#define RCV_HEADER 8
+#define RCV_RAM 0x6000 /* 24KB for RCV buffer */
+#define RCV_LOWER_LIMIT 0x00 /* 0x0000 */
+#define RCV_UPPER_LIMIT ((RCV_RAM - 2) >> 8) /* 0x5ffe */
+#define XMT_RAM (RAM_SIZE - RCV_RAM) /* 8KB for XMT buffer */
+#define XMT_LOWER_LIMIT (RCV_RAM >> 8) /* 0x6000 */
+#define XMT_UPPER_LIMIT ((RAM_SIZE - 2) >> 8) /* 0x7ffe */
+#define XMT_HEADER 8
+
+#define RCV_DONE 0x0008
+#define RX_OK 0x2000
+#define RX_ERROR 0x0d81
+
+#define TX_DONE_BIT 0x0080
+#define CHAIN_BIT 0x8000
+#define XMT_STATUS 0x02
+#define XMT_CHAIN 0x04
+#define XMT_COUNT 0x06
+
+#define BANK0_SELECT 0x00
+#define BANK1_SELECT 0x40
+#define BANK2_SELECT 0x80
+
+/* Bank 0 registers */
+#define COMMAND_REG 0x00 /* Register 0 */
+#define MC_SETUP 0x03
+#define XMT_CMD 0x04
+#define DIAGNOSE_CMD 0x07
+#define RCV_ENABLE_CMD 0x08
+#define RCV_DISABLE_CMD 0x0a
+#define STOP_RCV_CMD 0x0b
+#define RESET_CMD 0x0e
+#define POWER_DOWN_CMD 0x18
+#define RESUME_XMT_CMD 0x1c
+#define SEL_RESET_CMD 0x1e
+#define STATUS_REG 0x01 /* Register 1 */
+#define RX_INT 0x02
+#define TX_INT 0x04
+#define EXEC_STATUS 0x30
+#define ID_REG 0x02 /* Register 2 */
+#define R_ROBIN_BITS 0xc0 /* round robin counter */
+#define ID_REG_MASK 0x2c
+#define ID_REG_SIG 0x24
+#define AUTO_ENABLE 0x10
+#define INT_MASK_REG 0x03 /* Register 3 */
+#define RX_STOP_MASK 0x01
+#define RX_MASK 0x02
+#define TX_MASK 0x04
+#define EXEC_MASK 0x08
+#define ALL_MASK 0x0f
+#define RCV_BAR 0x04 /* The following are word (16-bit) registers */
+#define RCV_STOP 0x06
+#define XMT_BAR 0x0a
+#define HOST_ADDRESS_REG 0x0c
+#define IO_PORT 0x0e
+
+/* Bank 1 registers */
+#define REG1 0x01
+#define WORD_WIDTH 0x02
+#define INT_ENABLE 0x80
+#define INT_NO_REG 0x02
+#define RCV_LOWER_LIMIT_REG 0x08
+#define RCV_UPPER_LIMIT_REG 0x09
+#define XMT_LOWER_LIMIT_REG 0x0a
+#define XMT_UPPER_LIMIT_REG 0x0b
+
+/* Bank 2 registers */
+#define XMT_Chain_Int 0x20 /* Interrupt at the end of the transmit chain */
+#define XMT_Chain_ErrStop 0x40 /* Interrupt at the end of the chain even if there are errors */
+#define RCV_Discard_BadFrame 0x80 /* Throw bad frames away, and continue to receive others */
+#define REG2 0x02
+#define PRMSC_Mode 0x01
+#define Multi_IA 0x20
+#define REG3 0x03
+#define TPE_BIT 0x04
+#define BNC_BIT 0x20
+
+#define I_ADD_REG0 0x04
+#define I_ADD_REG1 0x05
+#define I_ADD_REG2 0x06
+#define I_ADD_REG3 0x07
+#define I_ADD_REG4 0x08
+#define I_ADD_REG5 0x09
+
+#define EEPROM_REG 0x0a
+#define EESK 0x01
+#define EECS 0x02
+#define EEDI 0x04
+#define EEDO 0x08
+
+
+/* Check for a network adaptor of this type, and return '0' iff one exists.
+ If dev->base_addr == 0, probe all likely locations.
+ If dev->base_addr == 1, always return failure.
+ If dev->base_addr == 2, allocate space for the device and return success
+ (detachable devices only).
+ */
+#ifdef HAVE_DEVLIST
+/* Support for a alternate probe manager, which will eliminate the
+ boilerplate below. */
+struct netdev_entry netcard_drv =
+{"eepro", eepro_probe1, EEPRO_IO_EXTENT, eepro_portlist};
+#else
+int
+eepro_probe(struct device *dev)
+{
+ int i;
+ int base_addr = dev ? dev->base_addr : 0;
+
+ if (base_addr > 0x1ff) /* Check a single specified location. */
+ return eepro_probe1(dev, base_addr);
+ else if (base_addr != 0) /* Don't probe at all. */
+ return ENXIO;
+
+ for (i = 0; eepro_portlist[i]; i++) {
+ int ioaddr = eepro_portlist[i];
+ if (check_region(ioaddr, EEPRO_IO_EXTENT))
+ continue;
+ if (eepro_probe1(dev, ioaddr) == 0)
+ return 0;
+ }
+
+ return ENODEV;
+}
+#endif
+
+/* This is the real probe routine. Linux has a history of friendly device
+ probes on the ISA bus. A good device probes avoids doing writes, and
+ verifies that the correct device exists and functions. */
+
+int eepro_probe1(struct device *dev, short ioaddr)
+{
+ unsigned short station_addr[6], id, counter;
+ int i;
+ int eepro; /* a flag, TRUE=1 for the EtherExpress Pro/10,
+ FALSE = 0 for other 82595-based lan cards. */
+ const char *ifmap[] = {"AUI", "10Base2", "10BaseT"};
+ enum iftype { AUI=0, BNC=1, TPE=2 };
+
+ /* Now, we are going to check for the signature of the
+ ID_REG (register 2 of bank 0) */
+
+ if (((id=inb(ioaddr + ID_REG)) & ID_REG_MASK) == ID_REG_SIG) {
+
+ /* We seem to have the 82595 signature, let's
+ play with its counter (last 2 bits of
+ register 2 of bank 0) to be sure. */
+
+ counter = (id & R_ROBIN_BITS);
+ if (((id=inb(ioaddr+ID_REG)) & R_ROBIN_BITS) ==
+ (counter + 0x40)) {
+
+ /* Yes, the 82595 has been found */
+
+ /* Now, get the ethernet hardware address from
+ the EEPROM */
+
+ station_addr[0] = read_eeprom(ioaddr, 2);
+ station_addr[1] = read_eeprom(ioaddr, 3);
+ station_addr[2] = read_eeprom(ioaddr, 4);
+
+ /* Check the station address for the manufacturer's code */
+
+ if (station_addr[2] != 0x00aa || (station_addr[1] & 0xff00) != 0x0000) {
+ eepro = 0;
+ printk("%s: Intel 82595-based lan card at %#x,",
+ dev->name, ioaddr);
+ }
+ else {
+ eepro = 1;
+ printk("%s: Intel EtherExpress Pro/10 at %#x,",
+ dev->name, ioaddr);
+ }
+
+ /* Fill in the 'dev' fields. */
+ dev->base_addr = ioaddr;
+
+ for (i=0; i < 6; i++) {
+ dev->dev_addr[i] = ((unsigned char *) station_addr)[5-i];
+ printk("%c%02x", i ? ':' : ' ', dev->dev_addr[i]);
+ }
+
+ outb(BANK2_SELECT, ioaddr); /* be CAREFUL, BANK 2 now */
+ id = inb(ioaddr + REG3);
+ if (id & TPE_BIT)
+ dev->if_port = TPE;
+ else dev->if_port = BNC;
+
+ if (dev->irq < 2 && eepro) {
+ i = read_eeprom(ioaddr, 1);
+ switch (i & 0x07) {
+ case 0: dev->irq = 9; break;
+ case 1: dev->irq = 3; break;
+ case 2: dev->irq = 5; break;
+ case 3: dev->irq = 10; break;
+ case 4: dev->irq = 11; break;
+ default: /* should never get here !!!!! */
+ printk(" illegal interrupt vector stored in EEPROM.\n");
+ return ENODEV;
+ }
+ }
+ else if (dev->irq == 2)
+ dev->irq = 9;
+
+ if (dev->irq > 2) {
+ printk(", IRQ %d, %s.\n", dev->irq,
+ ifmap[dev->if_port]);
+ if (request_irq(dev->irq, &eepro_interrupt, 0, "eepro")) {
+ printk("%s: unable to get IRQ %d.\n", dev->name, dev->irq);
+ return -EAGAIN;
+ }
+ }
+ else printk(", %s.\n", ifmap[dev->if_port]);
+
+ if ((dev->mem_start & 0xf) > 0)
+ net_debug = dev->mem_start & 7;
+
+ if (net_debug > 3) {
+ i = read_eeprom(ioaddr, 5);
+ if (i & 0x2000) /* bit 13 of EEPROM word 5 */
+ printk("%s: Concurrent Processing is enabled but not used!\n",
+ dev->name);
+ }
+
+ if (net_debug)
+ printk(version);
+
+ /* Grab the region so we can find another board if autoIRQ fails. */
+ request_region(ioaddr, EEPRO_IO_EXTENT, "eepro");
+
+ /* Initialize the device structure */
+ dev->priv = kmalloc(sizeof(struct eepro_local), GFP_KERNEL);
+ if (dev->priv == NULL)
+ return -ENOMEM;
+ memset(dev->priv, 0, sizeof(struct eepro_local));
+
+ dev->open = eepro_open;
+ dev->stop = eepro_close;
+ dev->hard_start_xmit = eepro_send_packet;
+ dev->get_stats = eepro_get_stats;
+ dev->set_multicast_list = &set_multicast_list;
+
+ /* Fill in the fields of the device structure with
+ ethernet generic values */
+
+ ether_setup(dev);
+
+ outb(RESET_CMD, ioaddr); /* RESET the 82595 */
+
+ return 0;
+ }
+ else return ENODEV;
+ }
+ else if (net_debug > 3)
+ printk ("EtherExpress Pro probed failed!\n");
+ return ENODEV;
+}
+
+/* Open/initialize the board. This is called (in the current kernel)
+ sometime after booting when the 'ifconfig' program is run.
+
+ This routine should set everything up anew at each open, even
+ registers that "should" only need to be set once at boot, so that
+ there is non-reboot way to recover if something goes wrong.
+ */
+
+static char irqrmap[] = {-1,-1,0,1,-1,2,-1,-1,-1,0,3,4,-1,-1,-1,-1};
+static int eepro_grab_irq(struct device *dev)
+{
+ int irqlist[] = { 5, 9, 10, 11, 4, 3, 0};
+ int *irqp = irqlist, temp_reg, ioaddr = dev->base_addr;
+
+ outb(BANK1_SELECT, ioaddr); /* be CAREFUL, BANK 1 now */
+
+ /* Enable the interrupt line. */
+ temp_reg = inb(ioaddr + REG1);
+ outb(temp_reg | INT_ENABLE, ioaddr + REG1);
+
+ outb(BANK0_SELECT, ioaddr); /* be CAREFUL, BANK 0 now */
+
+ /* clear all interrupts */
+ outb(ALL_MASK, ioaddr + STATUS_REG);
+ /* Let EXEC event to interrupt */
+ outb(ALL_MASK & ~(EXEC_MASK), ioaddr + INT_MASK_REG);
+
+ do {
+ outb(BANK1_SELECT, ioaddr); /* be CAREFUL, BANK 1 now */
+
+ temp_reg = inb(ioaddr + INT_NO_REG);
+ outb((temp_reg & 0xf8) | irqrmap[*irqp], ioaddr + INT_NO_REG);
+
+ outb(BANK0_SELECT, ioaddr); /* Switch back to Bank 0 */
+
+ if (request_irq (*irqp, NULL, 0, "bogus") != EBUSY) {
+ /* Twinkle the interrupt, and check if it's seen */
+ autoirq_setup(0);
+
+ outb(DIAGNOSE_CMD, ioaddr); /* RESET the 82595 */
+
+ if (*irqp == autoirq_report(2) && /* It's a good IRQ line */
+ (request_irq(dev->irq = *irqp, &eepro_interrupt, 0, "eepro") == 0))
+ break;
+
+ /* clear all interrupts */
+ outb(ALL_MASK, ioaddr + STATUS_REG);
+ }
+ } while (*++irqp);
+
+ outb(BANK1_SELECT, ioaddr); /* Switch back to Bank 1 */
+
+ /* Disable the physical interrupt line. */
+ temp_reg = inb(ioaddr + REG1);
+ outb(temp_reg & 0x7f, ioaddr + REG1);
+
+ outb(BANK0_SELECT, ioaddr); /* Switch back to Bank 0 */
+
+ /* Mask all the interrupts. */
+ outb(ALL_MASK, ioaddr + INT_MASK_REG);
+
+ /* clear all interrupts */
+ outb(ALL_MASK, ioaddr + STATUS_REG);
+
+ return dev->irq;
+}
+
+static int
+eepro_open(struct device *dev)
+{
+ unsigned short temp_reg;
+ int i, ioaddr = dev->base_addr;
+ struct eepro_local *lp = (struct eepro_local *)dev->priv;
+
+ if (net_debug > 3)
+ printk("eepro: entering eepro_open routine.\n");
+
+ if (dev->dev_addr[0] == SA_ADDR0 &&
+ dev->dev_addr[1] == SA_ADDR1 &&
+ dev->dev_addr[2] == SA_ADDR2)
+ lp->eepro = 1; /* Yes, an Intel EtherExpress Pro/10 */
+ else lp->eepro = 0; /* No, it is a generic 82585 lan card */
+
+ /* Get the interrupt vector for the 82595 */
+ if (dev->irq < 2 && eepro_grab_irq(dev) == 0) {
+ printk("%s: unable to get IRQ %d.\n", dev->name, dev->irq);
+ return -EAGAIN;
+ }
+
+ if (irq2dev_map[dev->irq] != 0
+ || (irq2dev_map[dev->irq] = dev) == 0)
+ return -EAGAIN;
+
+ /* Initialize the 82595. */
+
+ outb(BANK2_SELECT, ioaddr); /* be CAREFUL, BANK 2 now */
+ temp_reg = inb(ioaddr + EEPROM_REG);
+ if (temp_reg & 0x10) /* Check the TurnOff Enable bit */
+ outb(temp_reg & 0xef, ioaddr + EEPROM_REG);
+ for (i=0; i < 6; i++)
+ outb(dev->dev_addr[i] , ioaddr + I_ADD_REG0 + i);
+
+ temp_reg = inb(ioaddr + REG1); /* Setup Transmit Chaining */
+ outb(temp_reg | XMT_Chain_Int | XMT_Chain_ErrStop /* and discard bad RCV frames */
+ | RCV_Discard_BadFrame, ioaddr + REG1);
+
+ temp_reg = inb(ioaddr + REG2); /* Match broadcast */
+ outb(temp_reg | 0x14, ioaddr + REG2);
+
+ temp_reg = inb(ioaddr + REG3);
+ outb(temp_reg & 0x3f, ioaddr + REG3); /* clear test mode */
+
+ /* Set the receiving mode */
+ outb(BANK1_SELECT, ioaddr); /* be CAREFUL, BANK 1 now */
+
+ temp_reg = inb(ioaddr + INT_NO_REG);
+ outb((temp_reg & 0xf8) | irqrmap[dev->irq], ioaddr + INT_NO_REG);
+
+ /* Initialize the RCV and XMT upper and lower limits */
+ outb(RCV_LOWER_LIMIT, ioaddr + RCV_LOWER_LIMIT_REG);
+ outb(RCV_UPPER_LIMIT, ioaddr + RCV_UPPER_LIMIT_REG);
+ outb(XMT_LOWER_LIMIT, ioaddr + XMT_LOWER_LIMIT_REG);
+ outb(XMT_UPPER_LIMIT, ioaddr + XMT_UPPER_LIMIT_REG);
+
+ /* Enable the interrupt line. */
+ temp_reg = inb(ioaddr + REG1);
+ outb(temp_reg | INT_ENABLE, ioaddr + REG1);
+
+ outb(BANK0_SELECT, ioaddr); /* Switch back to Bank 0 */
+
+ /* Let RX and TX events to interrupt */
+ outb(ALL_MASK & ~(RX_MASK | TX_MASK), ioaddr + INT_MASK_REG);
+ /* clear all interrupts */
+ outb(ALL_MASK, ioaddr + STATUS_REG);
+
+ /* Initialize RCV */
+ outw(RCV_LOWER_LIMIT << 8, ioaddr + RCV_BAR);
+ lp->rx_start = (RCV_LOWER_LIMIT << 8) ;
+ outw((RCV_UPPER_LIMIT << 8) | 0xfe, ioaddr + RCV_STOP);
+
+ /* Initialize XMT */
+ outw(XMT_LOWER_LIMIT << 8, ioaddr + XMT_BAR);
+
+ outb(SEL_RESET_CMD, ioaddr);
+ /* We are supposed to wait for 2 us after a SEL_RESET */
+ SLOW_DOWN_IO;
+ SLOW_DOWN_IO;
+
+ lp->tx_start = lp->tx_end = XMT_LOWER_LIMIT << 8; /* or = RCV_RAM */
+ lp->tx_last = 0;
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+
+ if (net_debug > 3)
+ printk("eepro: exiting eepro_open routine.\n");
+
+ outb(RCV_ENABLE_CMD, ioaddr);
+
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int
+eepro_send_packet(struct sk_buff *skb, struct device *dev)
+{
+ struct eepro_local *lp = (struct eepro_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+
+ if (net_debug > 5)
+ printk("eepro: entering eepro_send_packet routine.\n");
+
+ if (dev->tbusy) {
+ /* If we get here, some higher level has decided we are broken.
+ There should really be a "kick me" function call instead. */
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < 5)
+ return 1;
+ if (net_debug > 1)
+ printk("%s: transmit timed out, %s?\n", dev->name,
+ "network cable problem");
+ lp->stats.tx_errors++;
+ /* Try to restart the adaptor. */
+ outb(SEL_RESET_CMD, ioaddr);
+ /* We are supposed to wait for 2 us after a SEL_RESET */
+ SLOW_DOWN_IO;
+ SLOW_DOWN_IO;
+
+ /* Do I also need to flush the transmit buffers here? YES? */
+ lp->tx_start = lp->tx_end = RCV_RAM;
+ lp->tx_last = 0;
+
+ dev->tbusy=0;
+ dev->trans_start = jiffies;
+
+ outb(RCV_ENABLE_CMD, ioaddr);
+
+ }
+
+ /* If some higher layer thinks we've missed an tx-done interrupt
+ we are passed NULL. Caution: dev_tint() handles the cli()/sti()
+ itself. */
+ if (skb == NULL) {
+ dev_tint(dev);
+ return 0;
+ }
+
+ /* Block a timer-based transmit from overlapping. */
+ if (set_bit(0, (void*)&dev->tbusy) != 0)
+ printk("%s: Transmitter access conflict.\n", dev->name);
+ else {
+ short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+ unsigned char *buf = skb->data;
+
+ hardware_send_packet(dev, buf, length);
+ dev->trans_start = jiffies;
+ }
+
+ dev_kfree_skb (skb, FREE_WRITE);
+
+ /* You might need to clean up and record Tx statistics here. */
+ /* lp->stats.tx_aborted_errors++; */
+
+ if (net_debug > 5)
+ printk("eepro: exiting eepro_send_packet routine.\n");
+
+ return 0;
+}
+
+
+/* The typical workload of the driver:
+ Handle the network interface interrupts. */
+static void
+eepro_interrupt(int irq, struct pt_regs * regs)
+{
+ struct device *dev = (struct device *)(irq2dev_map[irq]);
+ int ioaddr, status, boguscount = 0;
+
+ if (net_debug > 5)
+ printk("eepro: entering eepro_interrupt routine.\n");
+
+ if (dev == NULL) {
+ printk ("eepro_interrupt(): irq %d for unknown device.\n", irq);
+ return;
+ }
+ dev->interrupt = 1;
+
+ ioaddr = dev->base_addr;
+
+ do {
+ status = inb(ioaddr + STATUS_REG);
+
+ if (status & RX_INT) {
+ if (net_debug > 4)
+ printk("eepro: packet received interrupt.\n");
+
+ /* Acknowledge the RX_INT */
+ outb(RX_INT, ioaddr + STATUS_REG);
+
+ /* Get the received packets */
+ eepro_rx(dev);
+ }
+ else if (status & TX_INT) {
+ if (net_debug > 4)
+ printk("eepro: packet transmit interrupt.\n");
+
+ /* Acknowledge the TX_INT */
+ outb(TX_INT, ioaddr + STATUS_REG);
+
+ /* Process the status of transmitted packets */
+ eepro_transmit_interrupt(dev);
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+ }
+ } while ((++boguscount < 10) && (status & 0x06));
+
+ dev->interrupt = 0;
+ if (net_debug > 5)
+ printk("eepro: exiting eepro_interrupt routine.\n");
+
+ return;
+}
+
+static int
+eepro_close(struct device *dev)
+{
+ struct eepro_local *lp = (struct eepro_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+ short temp_reg;
+
+ dev->tbusy = 1;
+ dev->start = 0;
+
+ outb(BANK1_SELECT, ioaddr); /* Switch back to Bank 1 */
+
+ /* Disable the physical interrupt line. */
+ temp_reg = inb(ioaddr + REG1);
+ outb(temp_reg & 0x7f, ioaddr + REG1);
+
+ outb(BANK0_SELECT, ioaddr); /* Switch back to Bank 0 */
+
+ /* Flush the Tx and disable Rx. */
+ outb(STOP_RCV_CMD, ioaddr);
+ lp->tx_start = lp->tx_end = RCV_RAM ;
+ lp->tx_last = 0;
+
+ /* Mask all the interrupts. */
+ outb(ALL_MASK, ioaddr + INT_MASK_REG);
+
+ /* clear all interrupts */
+ outb(ALL_MASK, ioaddr + STATUS_REG);
+
+ /* Reset the 82595 */
+ outb(RESET_CMD, ioaddr);
+
+ /* release the interrupt */
+ free_irq(dev->irq);
+
+ irq2dev_map[dev->irq] = 0;
+
+ /* Update the statistics here. What statistics? */
+
+ /* We are supposed to wait for 200 us after a RESET */
+ SLOW_DOWN_IO;
+ SLOW_DOWN_IO; /* May not be enough? */
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+/* Get the current statistics. This may be called with the card open or
+ closed. */
+static struct enet_statistics *
+eepro_get_stats(struct device *dev)
+{
+ struct eepro_local *lp = (struct eepro_local *)dev->priv;
+
+ return &lp->stats;
+}
+
+/* Set or clear the multicast filter for this adaptor.
+ */
+static void
+set_multicast_list(struct device *dev)
+{
+ struct eepro_local *lp = (struct eepro_local *)dev->priv;
+ short ioaddr = dev->base_addr;
+ unsigned short mode;
+ struct dev_mc_list *dmi=dev->mc_list;
+
+ if (dev->flags&(IFF_ALLMULTI|IFF_PROMISC) || dev->mc_count > 63)
+ {
+ /*
+ * We must make the kernel realise we had to move
+ * into promisc mode or we start all out war on
+ * the cable. If it was a promisc rewquest the
+ * flag is already set. If not we assert it.
+ */
+ dev->flags|=IFF_PROMISC;
+
+ outb(BANK2_SELECT, ioaddr); /* be CAREFUL, BANK 2 now */
+ mode = inb(ioaddr + REG2);
+ outb(mode | PRMSC_Mode, ioaddr + REG2);
+ mode = inb(ioaddr + REG3);
+ outb(mode, ioaddr + REG3); /* writing reg. 3 to complete the update */
+ outb(BANK0_SELECT, ioaddr); /* Return to BANK 0 now */
+ printk("%s: promiscuous mode enabled.\n", dev->name);
+ }
+ else if (dev->mc_count==0 )
+ {
+ outb(BANK2_SELECT, ioaddr); /* be CAREFUL, BANK 2 now */
+ mode = inb(ioaddr + REG2);
+ outb(mode & 0xd6, ioaddr + REG2); /* Turn off Multi-IA and PRMSC_Mode bits */
+ mode = inb(ioaddr + REG3);
+ outb(mode, ioaddr + REG3); /* writing reg. 3 to complete the update */
+ outb(BANK0_SELECT, ioaddr); /* Return to BANK 0 now */
+ }
+ else
+ {
+ unsigned short status, *eaddrs;
+ int i, boguscount = 0;
+
+ /* Disable RX and TX interrupts. Neccessary to avoid
+ corruption of the HOST_ADDRESS_REG by interrupt
+ service routines. */
+ outb(ALL_MASK, ioaddr + INT_MASK_REG);
+
+ outb(BANK2_SELECT, ioaddr); /* be CAREFUL, BANK 2 now */
+ mode = inb(ioaddr + REG2);
+ outb(mode | Multi_IA, ioaddr + REG2);
+ mode = inb(ioaddr + REG3);
+ outb(mode, ioaddr + REG3); /* writing reg. 3 to complete the update */
+ outb(BANK0_SELECT, ioaddr); /* Return to BANK 0 now */
+ outw(lp->tx_end, ioaddr + HOST_ADDRESS_REG);
+ outw(MC_SETUP, ioaddr + IO_PORT);
+ outw(0, ioaddr + IO_PORT);
+ outw(0, ioaddr + IO_PORT);
+ outw(6*(dev->mc_count + 1), ioaddr + IO_PORT);
+ for (i = 0; i < dev->mc_count; i++)
+ {
+ eaddrs=(unsigned short *)dmi->dmi_addr;
+ dmi=dmi->next;
+ outw(*eaddrs++, ioaddr + IO_PORT);
+ outw(*eaddrs++, ioaddr + IO_PORT);
+ outw(*eaddrs++, ioaddr + IO_PORT);
+ }
+ eaddrs = (unsigned short *) dev->dev_addr;
+ outw(eaddrs[0], ioaddr + IO_PORT);
+ outw(eaddrs[1], ioaddr + IO_PORT);
+ outw(eaddrs[2], ioaddr + IO_PORT);
+ outw(lp->tx_end, ioaddr + XMT_BAR);
+ outb(MC_SETUP, ioaddr);
+
+ /* Update the transmit queue */
+ i = lp->tx_end + XMT_HEADER + 6*(dev->mc_count + 1);
+ if (lp->tx_start != lp->tx_end)
+ {
+ /* update the next address and the chain bit in the
+ last packet */
+ outw(lp->tx_last + XMT_CHAIN, ioaddr + HOST_ADDRESS_REG);
+ outw(i, ioaddr + IO_PORT);
+ outw(lp->tx_last + XMT_COUNT, ioaddr + HOST_ADDRESS_REG);
+ status = inw(ioaddr + IO_PORT);
+ outw(status | CHAIN_BIT, ioaddr + IO_PORT);
+ lp->tx_end = i ;
+ }
+ else lp->tx_start = lp->tx_end = i ;
+
+ /* Acknowledge that the MC setup is done */
+ do { /* We should be doing this in the eepro_interrupt()! */
+ SLOW_DOWN_IO;
+ SLOW_DOWN_IO;
+ if (inb(ioaddr + STATUS_REG) & 0x08)
+ {
+ i = inb(ioaddr);
+ outb(0x08, ioaddr + STATUS_REG);
+ if (i & 0x20) { /* command ABORTed */
+ printk("%s: multicast setup failed.\n",
+ dev->name);
+ break;
+ } else if ((i & 0x0f) == 0x03) { /* MC-Done */
+ printk("%s: set Rx mode to %d addresses.\n",
+ dev->name, dev->mc_count);
+ break;
+ }
+ }
+ } while (++boguscount < 100);
+
+ /* Re-enable RX and TX interrupts */
+ outb(ALL_MASK & ~(RX_MASK | TX_MASK), ioaddr + INT_MASK_REG);
+
+ }
+ outb(RCV_ENABLE_CMD, ioaddr);
+}
+
+/* The horrible routine to read a word from the serial EEPROM. */
+/* IMPORTANT - the 82595 will be set to Bank 0 after the eeprom is read */
+
+/* The delay between EEPROM clock transitions. */
+#define eeprom_delay() { int _i = 40; while (--_i > 0) { __SLOW_DOWN_IO; }}
+#define EE_READ_CMD (6 << 6)
+
+int
+read_eeprom(int ioaddr, int location)
+{
+ int i;
+ unsigned short retval = 0;
+ short ee_addr = ioaddr + EEPROM_REG;
+ int read_cmd = location | EE_READ_CMD;
+ short ctrl_val = EECS ;
+
+ outb(BANK2_SELECT, ioaddr);
+ outb(ctrl_val, ee_addr);
+
+ /* Shift the read command bits out. */
+ for (i = 8; i >= 0; i--) {
+ short outval = (read_cmd & (1 << i)) ? ctrl_val | EEDI
+ : ctrl_val;
+ outb(outval, ee_addr);
+ outb(outval | EESK, ee_addr); /* EEPROM clock tick. */
+ eeprom_delay();
+ outb(outval, ee_addr); /* Finish EEPROM a clock tick. */
+ eeprom_delay();
+ }
+ outb(ctrl_val, ee_addr);
+
+ for (i = 16; i > 0; i--) {
+ outb(ctrl_val | EESK, ee_addr); eeprom_delay();
+ retval = (retval << 1) | ((inb(ee_addr) & EEDO) ? 1 : 0);
+ outb(ctrl_val, ee_addr); eeprom_delay();
+ }
+
+ /* Terminate the EEPROM access. */
+ ctrl_val &= ~EECS;
+ outb(ctrl_val | EESK, ee_addr);
+ eeprom_delay();
+ outb(ctrl_val, ee_addr);
+ eeprom_delay();
+ outb(BANK0_SELECT, ioaddr);
+ return retval;
+}
+
+static void
+hardware_send_packet(struct device *dev, void *buf, short length)
+{
+ struct eepro_local *lp = (struct eepro_local *)dev->priv;
+ short ioaddr = dev->base_addr;
+ unsigned status, tx_available, last, end, boguscount = 10;
+
+ if (net_debug > 5)
+ printk("eepro: entering hardware_send_packet routine.\n");
+
+ while (boguscount-- > 0) {
+
+ /* determine how much of the transmit buffer space is available */
+ if (lp->tx_end > lp->tx_start)
+ tx_available = XMT_RAM - (lp->tx_end - lp->tx_start);
+ else if (lp->tx_end < lp->tx_start)
+ tx_available = lp->tx_start - lp->tx_end;
+ else tx_available = XMT_RAM;
+
+ /* Disable RX and TX interrupts. Neccessary to avoid
+ corruption of the HOST_ADDRESS_REG by interrupt
+ service routines. */
+ outb(ALL_MASK, ioaddr + INT_MASK_REG);
+
+ if (((((length + 1) >> 1) << 1) + 2*XMT_HEADER)
+ >= tx_available) /* No space available ??? */
+ continue;
+
+ last = lp->tx_end;
+ end = last + (((length + 1) >> 1) << 1) + XMT_HEADER;
+
+ if (end >= RAM_SIZE) { /* the transmit buffer is wrapped around */
+ if ((RAM_SIZE - last) <= XMT_HEADER) {
+ /* Arrrr!!!, must keep the xmt header together,
+ several days were lost to chase this one down. */
+ last = RCV_RAM;
+ end = last + (((length + 1) >> 1) << 1) + XMT_HEADER;
+ }
+ else end = RCV_RAM + (end - RAM_SIZE);
+ }
+
+ outw(last, ioaddr + HOST_ADDRESS_REG);
+ outw(XMT_CMD, ioaddr + IO_PORT);
+ outw(0, ioaddr + IO_PORT);
+ outw(end, ioaddr + IO_PORT);
+ outw(length, ioaddr + IO_PORT);
+ outsw(ioaddr + IO_PORT, buf, (length + 1) >> 1);
+
+ if (lp->tx_start != lp->tx_end) {
+ /* update the next address and the chain bit in the
+ last packet */
+ if (lp->tx_end != last) {
+ outw(lp->tx_last + XMT_CHAIN, ioaddr + HOST_ADDRESS_REG);
+ outw(last, ioaddr + IO_PORT);
+ }
+ outw(lp->tx_last + XMT_COUNT, ioaddr + HOST_ADDRESS_REG);
+ status = inw(ioaddr + IO_PORT);
+ outw(status | CHAIN_BIT, ioaddr + IO_PORT);
+ }
+
+ /* A dummy read to flush the DRAM write pipeline */
+ status = inw(ioaddr + IO_PORT);
+
+ /* Enable RX and TX interrupts */
+ outb(ALL_MASK & ~(RX_MASK | TX_MASK), ioaddr + INT_MASK_REG);
+
+ if (lp->tx_start == lp->tx_end) {
+ outw(last, ioaddr + XMT_BAR);
+ outb(XMT_CMD, ioaddr);
+ lp->tx_start = last; /* I don't like to change tx_start here */
+ }
+ else outb(RESUME_XMT_CMD, ioaddr);
+
+ lp->tx_last = last;
+ lp->tx_end = end;
+
+ if (dev->tbusy) {
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+ }
+
+ if (net_debug > 5)
+ printk("eepro: exiting hardware_send_packet routine.\n");
+ return;
+ }
+ dev->tbusy = 1;
+ if (net_debug > 5)
+ printk("eepro: exiting hardware_send_packet routine.\n");
+}
+
+static void
+eepro_rx(struct device *dev)
+{
+ struct eepro_local *lp = (struct eepro_local *)dev->priv;
+ short ioaddr = dev->base_addr;
+ short boguscount = 20;
+ short rcv_car = lp->rx_start;
+ unsigned rcv_event, rcv_status, rcv_next_frame, rcv_size;
+
+ if (net_debug > 5)
+ printk("eepro: entering eepro_rx routine.\n");
+
+ /* Set the read pointer to the start of the RCV */
+ outw(rcv_car, ioaddr + HOST_ADDRESS_REG);
+ rcv_event = inw(ioaddr + IO_PORT);
+
+ while (rcv_event == RCV_DONE) {
+ rcv_status = inw(ioaddr + IO_PORT);
+ rcv_next_frame = inw(ioaddr + IO_PORT);
+ rcv_size = inw(ioaddr + IO_PORT);
+
+ if ((rcv_status & (RX_OK | RX_ERROR)) == RX_OK) {
+ /* Malloc up new buffer. */
+ struct sk_buff *skb;
+
+ rcv_size &= 0x3fff;
+ skb = dev_alloc_skb(rcv_size+2);
+ if (skb == NULL) {
+ printk("%s: Memory squeeze, dropping packet.\n", dev->name);
+ lp->stats.rx_dropped++;
+ break;
+ }
+ skb->dev = dev;
+ skb_reserve(skb,2);
+
+ insw(ioaddr+IO_PORT, skb_put(skb,rcv_size), (rcv_size + 1) >> 1);
+
+ skb->protocol = eth_type_trans(skb,dev);
+ netif_rx(skb);
+ lp->stats.rx_packets++;
+ }
+ else { /* Not sure will ever reach here,
+ I set the 595 to discard bad received frames */
+ lp->stats.rx_errors++;
+ if (rcv_status & 0x0100)
+ lp->stats.rx_over_errors++;
+ else if (rcv_status & 0x0400)
+ lp->stats.rx_frame_errors++;
+ else if (rcv_status & 0x0800)
+ lp->stats.rx_crc_errors++;
+ printk("%s: event = %#x, status = %#x, next = %#x, size = %#x\n",
+ dev->name, rcv_event, rcv_status, rcv_next_frame, rcv_size);
+ }
+ if (rcv_status & 0x1000)
+ lp->stats.rx_length_errors++;
+ if (--boguscount == 0)
+ break;
+
+ rcv_car = lp->rx_start + RCV_HEADER + rcv_size;
+ lp->rx_start = rcv_next_frame;
+ outw(rcv_next_frame, ioaddr + HOST_ADDRESS_REG);
+ rcv_event = inw(ioaddr + IO_PORT);
+
+ }
+ if (rcv_car == 0)
+ rcv_car = (RCV_UPPER_LIMIT << 8) | 0xff;
+ outw(rcv_car - 1, ioaddr + RCV_STOP);
+
+ if (net_debug > 5)
+ printk("eepro: exiting eepro_rx routine.\n");
+}
+
+static void
+eepro_transmit_interrupt(struct device *dev)
+{
+ struct eepro_local *lp = (struct eepro_local *)dev->priv;
+ short ioaddr = dev->base_addr;
+ short boguscount = 10;
+ short xmt_status;
+
+ while (lp->tx_start != lp->tx_end) {
+
+ outw(lp->tx_start, ioaddr + HOST_ADDRESS_REG);
+ xmt_status = inw(ioaddr+IO_PORT);
+ if ((xmt_status & TX_DONE_BIT) == 0) break;
+ xmt_status = inw(ioaddr+IO_PORT);
+ lp->tx_start = inw(ioaddr+IO_PORT);
+
+ if (dev->tbusy) {
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+ }
+
+ if (xmt_status & 0x2000)
+ lp->stats.tx_packets++;
+ else {
+ lp->stats.tx_errors++;
+ if (xmt_status & 0x0400)
+ lp->stats.tx_carrier_errors++;
+ printk("%s: XMT status = %#x\n",
+ dev->name, xmt_status);
+ }
+ if (xmt_status & 0x000f)
+ lp->stats.collisions += (xmt_status & 0x000f);
+ if ((xmt_status & 0x0040) == 0x0)
+ lp->stats.tx_heartbeat_errors++;
+
+ if (--boguscount == 0)
+ break;
+ }
+}
+
+#ifdef MODULE
+static char devicename[9] = { 0, };
+static struct device dev_eepro = {
+ devicename, /* device name is inserted by linux/drivers/net/net_init.c */
+ 0, 0, 0, 0,
+ 0, 0,
+ 0, 0, 0, NULL, eepro_probe };
+
+static int io = 0x200;
+static int irq = 0;
+
+int
+init_module(void)
+{
+ if (io == 0)
+ printk("eepro: You should not use auto-probing with insmod!\n");
+ dev_eepro.base_addr = io;
+ dev_eepro.irq = irq;
+
+ if (register_netdev(&dev_eepro) != 0)
+ return -EIO;
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ unregister_netdev(&dev_eepro);
+ kfree_s(dev_eepro.priv,sizeof(struct eepro_local));
+ dev_eepro.priv=NULL;
+
+ /* If we don't do this, we can't re-insmod it later. */
+ release_region(dev_eepro.base_addr, EEPRO_IO_EXTENT);
+}
+#endif /* MODULE */
diff --git a/i386/i386at/gpl/linux/net/eexpress.c b/i386/i386at/gpl/linux/net/eexpress.c
new file mode 100644
index 00000000..2f641d68
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/eexpress.c
@@ -0,0 +1,1034 @@
+/* eexpress.c: Intel EtherExpress device driver for Linux. */
+/*
+ Written 1993 by Donald Becker.
+ Copyright 1993 United States Government as represented by the Director,
+ National Security Agency. This software may only be used and distributed
+ according to the terms of the GNU Public License as modified by SRC,
+ incorporated herein by reference.
+
+ The author may be reached as becker@super.org or
+ C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
+
+ Things remaining to do:
+ Check that the 586 and ASIC are reset/unreset at the right times.
+ Check tx and rx buffer setup.
+ The current Tx is single-buffer-only.
+ Move the theory of operation and memory map documentation.
+ Rework the board error reset
+ The statistics need to be updated correctly.
+
+ Modularized by Pauline Middelink <middelin@polyware.iaf.nl>
+ Changed to support io= irq= by Alan Cox <Alan.Cox@linux.org>
+*/
+
+static const char *version =
+ "eexpress.c:v0.07 1/19/94 Donald Becker (becker@super.org)\n";
+
+/*
+ Sources:
+ This driver wouldn't have been written with the availability of the
+ Crynwr driver source code. It provided a known-working implementation
+ that filled in the gaping holes of the Intel documentation. Three cheers
+ for Russ Nelson.
+
+ Intel Microcommunications Databook, Vol. 1, 1990. It provides just enough
+ info that the casual reader might think that it documents the i82586.
+*/
+
+#include <linux/module.h>
+
+#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>
+
+/* use 0 for production, 1 for verification, 2..7 for debug */
+#ifndef NET_DEBUG
+#define NET_DEBUG 2
+#endif
+static unsigned int net_debug = NET_DEBUG;
+
+/*
+ Details of the i82586.
+
+ You'll really need the databook to understand the details of this part,
+ but the outline is that the i82586 has two separate processing units.
+
+ The Rx unit uses a list of frame descriptors and a list of data buffer
+ descriptors. We use full-sized (1518 byte) data buffers, so there is
+ a one-to-one pairing of frame descriptors to buffer descriptors.
+
+ The Tx ("command") unit executes a list of commands that look like:
+ Status word Written by the 82586 when the command is done.
+ Command word Command in lower 3 bits, post-command action in upper 3
+ Link word The address of the next command.
+ Parameters (as needed).
+
+ Some definitions related to the Command Word are:
+ */
+#define CMD_EOL 0x8000 /* The last command of the list, stop. */
+#define CMD_SUSP 0x4000 /* Suspend after doing cmd. */
+#define CMD_INTR 0x2000 /* Interrupt after doing cmd. */
+
+enum commands {
+ CmdNOp = 0, CmdSASetup = 1, CmdConfigure = 2, CmdMulticastList = 3,
+ CmdTx = 4, CmdTDR = 5, CmdDump = 6, CmdDiagnose = 7};
+
+/* Information that need to be kept for each board. */
+struct net_local {
+ struct enet_statistics stats;
+ int last_restart;
+ short rx_head;
+ short rx_tail;
+ short tx_head;
+ short tx_cmd_link;
+ short tx_reap;
+};
+
+/*
+ Details of the EtherExpress Implementation
+ The EtherExpress takes an unusual approach to host access to packet buffer
+ memory. The host can use either the Dataport, with independent
+ autoincrementing read and write pointers, or it can I/O map 32 bytes of the
+ memory using the "Shadow Memory Pointer" (SMB) as follows:
+ ioaddr Normal EtherExpress registers
+ ioaddr+0x4000...0x400f Buffer Memory at SMB...SMB+15
+ ioaddr+0x8000...0x800f Buffer Memory at SMB+16...SMB+31
+ ioaddr+0xC000...0xC007 "" SMB+16...SMB+23 (hardware flaw?)
+ ioaddr+0xC008...0xC00f Buffer Memory at 0x0008...0x000f
+ The last I/O map set is useful if you put the i82586 System Command Block
+ (the command mailbox) exactly at 0x0008. (There seems to be some
+ undocumented init structure at 0x0000-7, so I had to use the Crywnr memory
+ setup verbatim for those four words anyway.)
+
+ A problem with using either one of these mechanisms is that you must run
+ single-threaded, or the interrupt handler must restore a changed value of
+ the read, write, or SMB pointers.
+
+ Unlike the Crynwr driver, my driver mostly ignores the I/O mapped "feature"
+ and relies heavily on the dataport for buffer memory access. To minimize
+ switching, the read_pointer is dedicated to the Rx interrupt handler, and
+ the write_pointer is used by the send_packet() routine (it's carefully saved
+ and restored when it's needed by the interrupt handler).
+ */
+
+/* Offsets from the base I/O address. */
+#define DATAPORT 0 /* Data Transfer Register. */
+#define WRITE_PTR 2 /* Write Address Pointer. */
+#define READ_PTR 4 /* Read Address Pointer. */
+#define SIGNAL_CA 6 /* Frob the 82586 Channel Attention line. */
+#define SET_IRQ 7 /* IRQ Select. */
+#define SHADOW_PTR 8 /* Shadow Memory Bank Pointer. */
+#define MEM_Ctrl 11
+#define MEM_Page_Ctrl 12
+#define Config 13
+#define EEPROM_Ctrl 14
+#define ID_PORT 15
+
+#define EEXPRESS_IO_EXTENT 16
+
+/* EEPROM_Ctrl bits. */
+
+#define EE_SHIFT_CLK 0x01 /* EEPROM shift clock. */
+#define EE_CS 0x02 /* EEPROM chip select. */
+#define EE_DATA_WRITE 0x04 /* EEPROM chip data in. */
+#define EE_DATA_READ 0x08 /* EEPROM chip data out. */
+#define EE_CTRL_BITS (EE_SHIFT_CLK | EE_CS | EE_DATA_WRITE | EE_DATA_READ)
+#define ASIC_RESET 0x40
+#define _586_RESET 0x80
+
+/* Offsets to elements of the System Control Block structure. */
+#define SCB_STATUS 0xc008
+#define SCB_CMD 0xc00A
+#define CUC_START 0x0100
+#define CUC_RESUME 0x0200
+#define CUC_SUSPEND 0x0300
+#define RX_START 0x0010
+#define RX_RESUME 0x0020
+#define RX_SUSPEND 0x0030
+#define SCB_CBL 0xc00C /* Command BLock offset. */
+#define SCB_RFA 0xc00E /* Rx Frame Area offset. */
+
+/*
+ What follows in 'init_words[]' is the "program" that is downloaded to the
+ 82586 memory. It's mostly tables and command blocks, and starts at the
+ reset address 0xfffff6.
+
+ Even with the additional "don't care" values, doing it this way takes less
+ program space than initializing the individual tables, and I feel it's much
+ cleaner.
+
+ The databook is particularly useless for the first two structures; they are
+ completely undocumented. I had to use the Crynwr driver as an example.
+
+ The memory setup is as follows:
+ */
+
+#define CONFIG_CMD 0x0018
+#define SET_SA_CMD 0x0024
+#define SA_OFFSET 0x002A
+#define IDLELOOP 0x30
+#define TDR_CMD 0x38
+#define TDR_TIME 0x3C
+#define DUMP_CMD 0x40
+#define DIAG_CMD 0x48
+#define SET_MC_CMD 0x4E
+#define DUMP_DATA 0x56 /* A 170 byte buffer for dump and Set-MC into. */
+
+#define TX_BUF_START 0x0100
+#define NUM_TX_BUFS 4
+#define TX_BUF_SIZE 0x0680 /* packet+header+TBD+extra (1518+14+20+16) */
+#define TX_BUF_END 0x2000
+
+#define RX_BUF_START 0x2000
+#define RX_BUF_SIZE (0x640) /* packet+header+RBD+extra */
+#define RX_BUF_END 0x4000
+
+/*
+ That's it: only 86 bytes to set up the beast, including every extra
+ command available. The 170 byte buffer at DUMP_DATA is shared between the
+ Dump command (called only by the diagnostic program) and the SetMulticastList
+ command.
+
+ To complete the memory setup you only have to write the station address at
+ SA_OFFSET and create the Tx & Rx buffer lists.
+
+ The Tx command chain and buffer list is setup as follows:
+ A Tx command table, with the data buffer pointing to...
+ A Tx data buffer descriptor. The packet is in a single buffer, rather than
+ chaining together several smaller buffers.
+ A NoOp command, which initially points to itself,
+ And the packet data.
+
+ A transmit is done by filling in the Tx command table and data buffer,
+ re-writing the NoOp command, and finally changing the offset of the last
+ command to point to the current Tx command. When the Tx command is finished,
+ it jumps to the NoOp, when it loops until the next Tx command changes the
+ "link offset" in the NoOp. This way the 82586 never has to go through the
+ slow restart sequence.
+
+ The Rx buffer list is set up in the obvious ring structure. We have enough
+ memory (and low enough interrupt latency) that we can avoid the complicated
+ Rx buffer linked lists by alway associating a full-size Rx data buffer with
+ each Rx data frame.
+
+ I current use four transmit buffers starting at TX_BUF_START (0x0100), and
+ use the rest of memory, from RX_BUF_START to RX_BUF_END, for Rx buffers.
+
+ */
+
+static short init_words[] = {
+ 0x0000, /* Set bus size to 16 bits. */
+ 0x0000,0x0000, /* Set control mailbox (SCB) addr. */
+ 0,0, /* pad to 0x000000. */
+ 0x0001, /* Status word that's cleared when init is done. */
+ 0x0008,0,0, /* SCB offset, (skip, skip) */
+
+ 0,0xf000|RX_START|CUC_START, /* SCB status and cmd. */
+ CONFIG_CMD, /* Command list pointer, points to Configure. */
+ RX_BUF_START, /* Rx block list. */
+ 0,0,0,0, /* Error count: CRC, align, buffer, overrun. */
+
+ /* 0x0018: Configure command. Change to put MAC data with packet. */
+ 0, CmdConfigure, /* Status, command. */
+ SET_SA_CMD, /* Next command is Set Station Addr. */
+ 0x0804, /* "4" bytes of config data, 8 byte FIFO. */
+ 0x2e40, /* Magic values, including MAC data location. */
+ 0, /* Unused pad word. */
+
+ /* 0x0024: Setup station address command. */
+ 0, CmdSASetup,
+ SET_MC_CMD, /* Next command. */
+ 0xaa00,0xb000,0x0bad, /* Station address (to be filled in) */
+
+ /* 0x0030: NOP, looping back to itself. Point to first Tx buffer to Tx. */
+ 0, CmdNOp, IDLELOOP, 0 /* pad */,
+
+ /* 0x0038: A unused Time-Domain Reflectometer command. */
+ 0, CmdTDR, IDLELOOP, 0,
+
+ /* 0x0040: An unused Dump State command. */
+ 0, CmdDump, IDLELOOP, DUMP_DATA,
+
+ /* 0x0048: An unused Diagnose command. */
+ 0, CmdDiagnose, IDLELOOP,
+
+ /* 0x004E: An empty set-multicast-list command. */
+#ifdef initial_text_tx
+ 0, CmdMulticastList, DUMP_DATA, 0,
+#else
+ 0, CmdMulticastList, IDLELOOP, 0,
+#endif
+
+ /* 0x0056: A continuous transmit command, only here for testing. */
+ 0, CmdTx, DUMP_DATA, DUMP_DATA+8, 0x83ff, -1, DUMP_DATA, 0,
+};
+
+/* Index to functions, as function prototypes. */
+
+extern int express_probe(struct device *dev); /* Called from Space.c */
+
+static int eexp_probe1(struct device *dev, short ioaddr);
+static int eexp_open(struct device *dev);
+static int eexp_send_packet(struct sk_buff *skb, struct device *dev);
+static void eexp_interrupt(int irq, struct pt_regs *regs);
+static void eexp_rx(struct device *dev);
+static int eexp_close(struct device *dev);
+static struct enet_statistics *eexp_get_stats(struct device *dev);
+static void set_multicast_list(struct device *dev);
+
+static int read_eeprom(int ioaddr, int location);
+static void hardware_send_packet(struct device *dev, void *buf, short length);
+static void init_82586_mem(struct device *dev);
+static void init_rx_bufs(struct device *dev);
+
+
+/* Check for a network adaptor of this type, and return '0' iff one exists.
+ If dev->base_addr == 0, probe all likely locations.
+ If dev->base_addr == 1, always return failure.
+ If dev->base_addr == 2, (detachable devices only) allocate space for the
+ device and return success.
+ */
+int
+express_probe(struct device *dev)
+{
+ /* Don't probe all settable addresses, 0x[23][0-7]0, just common ones. */
+ int *port, ports[] = {0x300, 0x270, 0x320, 0x340, 0};
+ int base_addr = dev->base_addr;
+
+ if (base_addr > 0x1ff) /* Check a single specified location. */
+ return eexp_probe1(dev, base_addr);
+ else if (base_addr > 0)
+ return ENXIO; /* Don't probe at all. */
+
+ for (port = &ports[0]; *port; port++) {
+ short id_addr = *port + ID_PORT;
+ unsigned short sum = 0;
+ int i;
+#ifdef notdef
+ for (i = 16; i > 0; i--)
+ sum += inb(id_addr);
+ printk("EtherExpress ID checksum is %04x.\n", sum);
+#else
+ for (i = 4; i > 0; i--) {
+ short id_val = inb(id_addr);
+ sum |= (id_val >> 4) << ((id_val & 3) << 2);
+ }
+#endif
+ if (sum == 0xbaba
+ && eexp_probe1(dev, *port) == 0)
+ return 0;
+ }
+
+ return ENODEV;
+}
+
+int eexp_probe1(struct device *dev, short ioaddr)
+{
+ unsigned short station_addr[3];
+ int i;
+
+ printk("%s: EtherExpress at %#x,", dev->name, ioaddr);
+
+ /* The station address is stored !backwards! in the EEPROM, reverse
+ after reading. (Hmmm, a little brain-damage there at Intel, eh?) */
+ station_addr[0] = read_eeprom(ioaddr, 2);
+ station_addr[1] = read_eeprom(ioaddr, 3);
+ station_addr[2] = read_eeprom(ioaddr, 4);
+
+ /* Check the first three octets of the S.A. for the manufacturer's code. */
+ if (station_addr[2] != 0x00aa || (station_addr[1] & 0xff00) != 0x0000) {
+ printk(" rejected (invalid address %04x%04x%04x).\n",
+ station_addr[2], station_addr[1], station_addr[0]);
+ return ENODEV;
+ }
+
+ /* We've committed to using the board, and can start filling in *dev. */
+ request_region(ioaddr, EEXPRESS_IO_EXTENT, "eexpress");
+ dev->base_addr = ioaddr;
+
+ for (i = 0; i < 6; i++) {
+ dev->dev_addr[i] = ((unsigned char*)station_addr)[5-i];
+ printk(" %02x", dev->dev_addr[i]);
+ }
+
+ /* There is no reason for the driver to care, but I print out the
+ interface to minimize bogus bug reports. */
+ {
+ char irqmap[] = {0, 9, 3, 4, 5, 10, 11, 0};
+ const char *ifmap[] = {"AUI", "BNC", "10baseT"};
+ enum iftype {AUI=0, BNC=1, TP=2};
+ unsigned short setupval = read_eeprom(ioaddr, 0);
+
+ dev->irq = irqmap[setupval >> 13];
+ dev->if_port = (setupval & 0x1000) == 0 ? AUI :
+ read_eeprom(ioaddr, 5) & 0x1 ? TP : BNC;
+ printk(", IRQ %d, Interface %s.\n", dev->irq, ifmap[dev->if_port]);
+ /* Release the IRQ line so that it can be shared if we don't use the
+ ethercard. */
+ outb(0x00, ioaddr + SET_IRQ);
+ }
+
+ /* It's now OK to leave the board in reset, pending the open(). */
+ outb(ASIC_RESET, ioaddr + EEPROM_Ctrl);
+
+ if ((dev->mem_start & 0xf) > 0)
+ net_debug = dev->mem_start & 7;
+
+ if (net_debug)
+ printk(version);
+
+ /* Initialize the device structure. */
+ dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
+ if (dev->priv == NULL)
+ return -ENOMEM;
+ memset(dev->priv, 0, sizeof(struct net_local));
+
+ dev->open = eexp_open;
+ dev->stop = eexp_close;
+ dev->hard_start_xmit = eexp_send_packet;
+ dev->get_stats = eexp_get_stats;
+ dev->set_multicast_list = &set_multicast_list;
+
+ /* Fill in the fields of the device structure with ethernet-generic values. */
+
+ ether_setup(dev);
+
+ dev->flags&=~IFF_MULTICAST;
+
+ return 0;
+}
+
+
+/* Reverse IRQ map: the value to put in the SET_IRQ reg. for IRQ<index>. */
+static char irqrmap[]={0,0,1,2,3,4,0,0,0,1,5,6,0,0,0,0};
+
+static int
+eexp_open(struct device *dev)
+{
+ int ioaddr = dev->base_addr;
+
+ if (dev->irq == 0 || irqrmap[dev->irq] == 0)
+ return -ENXIO;
+
+ if (irq2dev_map[dev->irq] != 0
+ /* This is always true, but avoid the false IRQ. */
+ || (irq2dev_map[dev->irq] = dev) == 0
+ || request_irq(dev->irq, &eexp_interrupt, 0, "EExpress")) {
+ return -EAGAIN;
+ }
+
+ /* Initialize the 82586 memory and start it. */
+ init_82586_mem(dev);
+
+ /* Enable the interrupt line. */
+ outb(irqrmap[dev->irq] | 0x08, ioaddr + SET_IRQ);
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int
+eexp_send_packet(struct sk_buff *skb, struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+
+ if (dev->tbusy) {
+ /* If we get here, some higher level has decided we are broken.
+ There should really be a "kick me" function call instead. */
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < 5)
+ return 1;
+ if (net_debug > 1)
+ printk("%s: transmit timed out, %s? ", dev->name,
+ inw(ioaddr+SCB_STATUS) & 0x8000 ? "IRQ conflict" :
+ "network cable problem");
+ lp->stats.tx_errors++;
+ /* Try to restart the adaptor. */
+ if (lp->last_restart == lp->stats.tx_packets) {
+ if (net_debug > 1) printk("Resetting board.\n");
+ /* Completely reset the adaptor. */
+ init_82586_mem(dev);
+ } else {
+ /* Issue the channel attention signal and hope it "gets better". */
+ if (net_debug > 1) printk("Kicking board.\n");
+ outw(0xf000|CUC_START|RX_START, ioaddr + SCB_CMD);
+ outb(0, ioaddr + SIGNAL_CA);
+ lp->last_restart = lp->stats.tx_packets;
+ }
+ dev->tbusy=0;
+ dev->trans_start = jiffies;
+ }
+
+ /* If some higher layer thinks we've missed an tx-done interrupt
+ we are passed NULL. Caution: dev_tint() handles the cli()/sti()
+ itself. */
+ if (skb == NULL) {
+ dev_tint(dev);
+ return 0;
+ }
+
+ /* Block a timer-based transmit from overlapping. */
+ if (set_bit(0, (void*)&dev->tbusy) != 0)
+ printk("%s: Transmitter access conflict.\n", dev->name);
+ else {
+ short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+ unsigned char *buf = skb->data;
+
+ /* Disable the 82586's input to the interrupt line. */
+ outb(irqrmap[dev->irq], ioaddr + SET_IRQ);
+ hardware_send_packet(dev, buf, length);
+ dev->trans_start = jiffies;
+ /* Enable the 82586 interrupt input. */
+ outb(0x08 | irqrmap[dev->irq], ioaddr + SET_IRQ);
+ }
+
+ dev_kfree_skb (skb, FREE_WRITE);
+
+ /* You might need to clean up and record Tx statistics here. */
+ lp->stats.tx_aborted_errors++;
+
+ return 0;
+}
+
+/* The typical workload of the driver:
+ Handle the network interface interrupts. */
+static void
+eexp_interrupt(int irq, struct pt_regs *regs)
+{
+ struct device *dev = (struct device *)(irq2dev_map[irq]);
+ struct net_local *lp;
+ int ioaddr, status, boguscount = 0;
+ short ack_cmd;
+
+ if (dev == NULL) {
+ printk ("net_interrupt(): irq %d for unknown device.\n", irq);
+ return;
+ }
+ dev->interrupt = 1;
+
+ ioaddr = dev->base_addr;
+ lp = (struct net_local *)dev->priv;
+
+ status = inw(ioaddr + SCB_STATUS);
+
+ if (net_debug > 4) {
+ printk("%s: EExp interrupt, status %4.4x.\n", dev->name, status);
+ }
+
+ /* Disable the 82586's input to the interrupt line. */
+ outb(irqrmap[dev->irq], ioaddr + SET_IRQ);
+
+ /* Reap the Tx packet buffers. */
+ while (lp->tx_reap != lp->tx_head) { /* if (status & 0x8000) */
+ unsigned short tx_status;
+ outw(lp->tx_reap, ioaddr + READ_PTR);
+ tx_status = inw(ioaddr);
+ if (tx_status == 0) {
+ if (net_debug > 5) printk("Couldn't reap %#x.\n", lp->tx_reap);
+ break;
+ }
+ if (tx_status & 0x2000) {
+ lp->stats.tx_packets++;
+ lp->stats.collisions += tx_status & 0xf;
+ dev->tbusy = 0;
+ mark_bh(NET_BH); /* Inform upper layers. */
+ } else {
+ lp->stats.tx_errors++;
+ if (tx_status & 0x0600) lp->stats.tx_carrier_errors++;
+ if (tx_status & 0x0100) lp->stats.tx_fifo_errors++;
+ if (!(tx_status & 0x0040)) lp->stats.tx_heartbeat_errors++;
+ if (tx_status & 0x0020) lp->stats.tx_aborted_errors++;
+ }
+ if (net_debug > 5)
+ printk("Reaped %x, Tx status %04x.\n" , lp->tx_reap, tx_status);
+ lp->tx_reap += TX_BUF_SIZE;
+ if (lp->tx_reap > TX_BUF_END - TX_BUF_SIZE)
+ lp->tx_reap = TX_BUF_START;
+ if (++boguscount > 4)
+ break;
+ }
+
+ if (status & 0x4000) { /* Packet received. */
+ if (net_debug > 5)
+ printk("Received packet, rx_head %04x.\n", lp->rx_head);
+ eexp_rx(dev);
+ }
+
+ /* Acknowledge the interrupt sources. */
+ ack_cmd = status & 0xf000;
+
+ if ((status & 0x0700) != 0x0200 && dev->start) {
+ short saved_write_ptr = inw(ioaddr + WRITE_PTR);
+ if (net_debug > 1)
+ printk("%s: Command unit stopped, status %04x, restarting.\n",
+ dev->name, status);
+ /* If this ever occurs we must re-write the idle loop, reset
+ the Tx list, and do a complete restart of the command unit. */
+ outw(IDLELOOP, ioaddr + WRITE_PTR);
+ outw(0, ioaddr);
+ outw(CmdNOp, ioaddr);
+ outw(IDLELOOP, ioaddr);
+ outw(IDLELOOP, SCB_CBL);
+ lp->tx_cmd_link = IDLELOOP + 4;
+ lp->tx_head = lp->tx_reap = TX_BUF_START;
+ /* Restore the saved write pointer. */
+ outw(saved_write_ptr, ioaddr + WRITE_PTR);
+ ack_cmd |= CUC_START;
+ }
+
+ if ((status & 0x0070) != 0x0040 && dev->start) {
+ short saved_write_ptr = inw(ioaddr + WRITE_PTR);
+ /* The Rx unit is not ready, it must be hung. Restart the receiver by
+ initializing the rx buffers, and issuing an Rx start command. */
+ lp->stats.rx_errors++;
+ if (net_debug > 1) {
+ int cur_rxbuf = RX_BUF_START;
+ printk("%s: Rx unit stopped status %04x rx head %04x tail %04x.\n",
+ dev->name, status, lp->rx_head, lp->rx_tail);
+ while (cur_rxbuf <= RX_BUF_END - RX_BUF_SIZE) {
+ int i;
+ printk(" Rx buf at %04x:", cur_rxbuf);
+ outw(cur_rxbuf, ioaddr + READ_PTR);
+ for (i = 0; i < 0x20; i += 2)
+ printk(" %04x", inw(ioaddr));
+ printk(".\n");
+ cur_rxbuf += RX_BUF_SIZE;
+ }
+ }
+ init_rx_bufs(dev);
+ outw(RX_BUF_START, SCB_RFA);
+ outw(saved_write_ptr, ioaddr + WRITE_PTR);
+ ack_cmd |= RX_START;
+ }
+
+ outw(ack_cmd, ioaddr + SCB_CMD);
+ outb(0, ioaddr + SIGNAL_CA);
+
+ if (net_debug > 5) {
+ printk("%s: EExp exiting interrupt, status %4.4x.\n", dev->name,
+ inw(ioaddr + SCB_CMD));
+ }
+ /* Enable the 82586's input to the interrupt line. */
+ outb(irqrmap[dev->irq] | 0x08, ioaddr + SET_IRQ);
+ return;
+}
+
+static int
+eexp_close(struct device *dev)
+{
+ int ioaddr = dev->base_addr;
+
+ dev->tbusy = 1;
+ dev->start = 0;
+
+ /* Flush the Tx and disable Rx. */
+ outw(RX_SUSPEND | CUC_SUSPEND, ioaddr + SCB_CMD);
+ outb(0, ioaddr + SIGNAL_CA);
+
+ /* Disable the physical interrupt line. */
+ outb(0, ioaddr + SET_IRQ);
+
+ free_irq(dev->irq);
+
+ irq2dev_map[dev->irq] = 0;
+
+ /* Update the statistics here. */
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+/* Get the current statistics. This may be called with the card open or
+ closed. */
+static struct enet_statistics *
+eexp_get_stats(struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+
+ /* ToDo: decide if there are any useful statistics from the SCB. */
+
+ return &lp->stats;
+}
+
+/* Set or clear the multicast filter for this adaptor.
+ */
+static void
+set_multicast_list(struct device *dev)
+{
+/* This doesn't work yet */
+#if 0
+ short ioaddr = dev->base_addr;
+ if (num_addrs < 0) {
+ /* Not written yet, this requires expanding the init_words config
+ cmd. */
+ } else if (num_addrs > 0) {
+ /* Fill in the SET_MC_CMD with the number of address bytes, followed
+ by the list of multicast addresses to be accepted. */
+ outw(SET_MC_CMD + 6, ioaddr + WRITE_PTR);
+ outw(num_addrs * 6, ioaddr);
+ outsw(ioaddr, addrs, num_addrs*3); /* 3 = addr len in words */
+ /* We must trigger a whole 586 reset due to a bug. */
+ } else {
+ /* Not written yet, this requires expanding the init_words config
+ cmd. */
+ outw(99, ioaddr); /* Disable promiscuous mode, use normal mode */
+ }
+#endif
+}
+
+/* The horrible routine to read a word from the serial EEPROM. */
+
+/* The delay between EEPROM clock transitions. */
+#define eeprom_delay() { int _i = 40; while (--_i > 0) { __SLOW_DOWN_IO; }}
+#define EE_READ_CMD (6 << 6)
+
+int
+read_eeprom(int ioaddr, int location)
+{
+ int i;
+ unsigned short retval = 0;
+ short ee_addr = ioaddr + EEPROM_Ctrl;
+ int read_cmd = location | EE_READ_CMD;
+ short ctrl_val = EE_CS | _586_RESET;
+
+ outb(ctrl_val, ee_addr);
+
+ /* Shift the read command bits out. */
+ for (i = 8; i >= 0; i--) {
+ short outval = (read_cmd & (1 << i)) ? ctrl_val | EE_DATA_WRITE
+ : ctrl_val;
+ outb(outval, ee_addr);
+ outb(outval | EE_SHIFT_CLK, ee_addr); /* EEPROM clock tick. */
+ eeprom_delay();
+ outb(outval, ee_addr); /* Finish EEPROM a clock tick. */
+ eeprom_delay();
+ }
+ outb(ctrl_val, ee_addr);
+
+ for (i = 16; i > 0; i--) {
+ outb(ctrl_val | EE_SHIFT_CLK, ee_addr); eeprom_delay();
+ retval = (retval << 1) | ((inb(ee_addr) & EE_DATA_READ) ? 1 : 0);
+ outb(ctrl_val, ee_addr); eeprom_delay();
+ }
+
+ /* Terminate the EEPROM access. */
+ ctrl_val &= ~EE_CS;
+ outb(ctrl_val | EE_SHIFT_CLK, ee_addr);
+ eeprom_delay();
+ outb(ctrl_val, ee_addr);
+ eeprom_delay();
+ return retval;
+}
+
+static void
+init_82586_mem(struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ short ioaddr = dev->base_addr;
+
+ /* Enable loopback to protect the wire while starting up.
+ This is Superstition From Crynwr. */
+ outb(inb(ioaddr + Config) | 0x02, ioaddr + Config);
+
+ /* Hold the 586 in reset during the memory initialization. */
+ outb(_586_RESET, ioaddr + EEPROM_Ctrl);
+
+ /* Place the write pointer at 0xfff6 (address-aliased to 0xfffff6). */
+ outw(0xfff6, ioaddr + WRITE_PTR);
+ outsw(ioaddr, init_words, sizeof(init_words)>>1);
+
+ /* Fill in the station address. */
+ outw(SA_OFFSET, ioaddr + WRITE_PTR);
+ outsw(ioaddr, dev->dev_addr, 3);
+
+ /* The Tx-block list is written as needed. We just set up the values. */
+#ifdef initial_text_tx
+ lp->tx_cmd_link = DUMP_DATA + 4;
+#else
+ lp->tx_cmd_link = IDLELOOP + 4;
+#endif
+ lp->tx_head = lp->tx_reap = TX_BUF_START;
+
+ init_rx_bufs(dev);
+
+ /* Start the 586 by releasing the reset line. */
+ outb(0x00, ioaddr + EEPROM_Ctrl);
+
+ /* This was time consuming to track down: you need to give two channel
+ attention signals to reliably start up the i82586. */
+ outb(0, ioaddr + SIGNAL_CA);
+
+ {
+ int boguscnt = 50;
+ while (inw(ioaddr + SCB_STATUS) == 0)
+ if (--boguscnt == 0) {
+ printk("%s: i82586 initialization timed out with status %04x, cmd %04x.\n",
+ dev->name, inw(ioaddr + SCB_STATUS), inw(ioaddr + SCB_CMD));
+ break;
+ }
+ /* Issue channel-attn -- the 82586 won't start without it. */
+ outb(0, ioaddr + SIGNAL_CA);
+ }
+
+ /* Disable loopback. */
+ outb(inb(ioaddr + Config) & ~0x02, ioaddr + Config);
+ if (net_debug > 4)
+ printk("%s: Initialized 82586, status %04x.\n", dev->name,
+ inw(ioaddr + SCB_STATUS));
+ return;
+}
+
+/* Initialize the Rx-block list. */
+static void init_rx_bufs(struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ short ioaddr = dev->base_addr;
+
+ int cur_rxbuf = lp->rx_head = RX_BUF_START;
+
+ /* Initialize each Rx frame + data buffer. */
+ do { /* While there is room for one more. */
+ outw(cur_rxbuf, ioaddr + WRITE_PTR);
+ outw(0x0000, ioaddr); /* Status */
+ outw(0x0000, ioaddr); /* Command */
+ outw(cur_rxbuf + RX_BUF_SIZE, ioaddr); /* Link */
+ outw(cur_rxbuf + 22, ioaddr); /* Buffer offset */
+ outw(0xFeed, ioaddr); /* Pad for dest addr. */
+ outw(0xF00d, ioaddr);
+ outw(0xF001, ioaddr);
+ outw(0x0505, ioaddr); /* Pad for source addr. */
+ outw(0x2424, ioaddr);
+ outw(0x6565, ioaddr);
+ outw(0xdeaf, ioaddr); /* Pad for protocol. */
+
+ outw(0x0000, ioaddr); /* Buffer: Actual count */
+ outw(-1, ioaddr); /* Buffer: Next (none). */
+ outw(cur_rxbuf + 0x20, ioaddr); /* Buffer: Address low */
+ outw(0x0000, ioaddr);
+ /* Finally, the number of bytes in the buffer. */
+ outw(0x8000 + RX_BUF_SIZE-0x20, ioaddr);
+
+ lp->rx_tail = cur_rxbuf;
+ cur_rxbuf += RX_BUF_SIZE;
+ } while (cur_rxbuf <= RX_BUF_END - RX_BUF_SIZE);
+
+ /* Terminate the list by setting the EOL bit, and wrap the pointer to make
+ the list a ring. */
+ outw(lp->rx_tail + 2, ioaddr + WRITE_PTR);
+ outw(0xC000, ioaddr); /* Command, mark as last. */
+ outw(lp->rx_head, ioaddr); /* Link */
+}
+
+static void
+hardware_send_packet(struct device *dev, void *buf, short length)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ short ioaddr = dev->base_addr;
+ short tx_block = lp->tx_head;
+
+ /* Set the write pointer to the Tx block, and put out the header. */
+ outw(tx_block, ioaddr + WRITE_PTR);
+ outw(0x0000, ioaddr); /* Tx status */
+ outw(CMD_INTR|CmdTx, ioaddr); /* Tx command */
+ outw(tx_block+16, ioaddr); /* Next command is a NoOp. */
+ outw(tx_block+8, ioaddr); /* Data Buffer offset. */
+
+ /* Output the data buffer descriptor. */
+ outw(length | 0x8000, ioaddr); /* Byte count parameter. */
+ outw(-1, ioaddr); /* No next data buffer. */
+ outw(tx_block+22, ioaddr); /* Buffer follows the NoOp command. */
+ outw(0x0000, ioaddr); /* Buffer address high bits (always zero). */
+
+ /* Output the Loop-back NoOp command. */
+ outw(0x0000, ioaddr); /* Tx status */
+ outw(CmdNOp, ioaddr); /* Tx command */
+ outw(tx_block+16, ioaddr); /* Next is myself. */
+
+ /* Output the packet using the write pointer.
+ Hmmm, it feels a little like a 3c501! */
+ outsw(ioaddr + DATAPORT, buf, (length + 1) >> 1);
+
+ /* Set the old command link pointing to this send packet. */
+ outw(lp->tx_cmd_link, ioaddr + WRITE_PTR);
+ outw(tx_block, ioaddr);
+ lp->tx_cmd_link = tx_block + 20;
+
+ /* Set the next free tx region. */
+ lp->tx_head = tx_block + TX_BUF_SIZE;
+ if (lp->tx_head > TX_BUF_END - TX_BUF_SIZE)
+ lp->tx_head = TX_BUF_START;
+
+ if (net_debug > 4) {
+ printk("%s: EExp @%x send length = %d, tx_block %3x, next %3x, "
+ "reap %4x status %4.4x.\n", dev->name, ioaddr, length,
+ tx_block, lp->tx_head, lp->tx_reap, inw(ioaddr + SCB_STATUS));
+ }
+
+ if (lp->tx_head != lp->tx_reap)
+ dev->tbusy = 0;
+}
+
+static void
+eexp_rx(struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ short ioaddr = dev->base_addr;
+ short saved_write_ptr = inw(ioaddr + WRITE_PTR);
+ short rx_head = lp->rx_head;
+ short rx_tail = lp->rx_tail;
+ short boguscount = 10;
+ short frame_status;
+
+ /* Set the read pointer to the Rx frame. */
+ outw(rx_head, ioaddr + READ_PTR);
+ while ((frame_status = inw(ioaddr)) < 0) { /* Command complete */
+ short rfd_cmd = inw(ioaddr);
+ short next_rx_frame = inw(ioaddr);
+ short data_buffer_addr = inw(ioaddr);
+ short pkt_len;
+
+ /* Set the read pointer the data buffer. */
+ outw(data_buffer_addr, ioaddr + READ_PTR);
+ pkt_len = inw(ioaddr);
+
+ if (rfd_cmd != 0 || data_buffer_addr != rx_head + 22
+ || (pkt_len & 0xC000) != 0xC000) {
+ printk("%s: Rx frame at %#x corrupted, status %04x cmd %04x"
+ "next %04x data-buf @%04x %04x.\n", dev->name, rx_head,
+ frame_status, rfd_cmd, next_rx_frame, data_buffer_addr,
+ pkt_len);
+ } else if ((frame_status & 0x2000) == 0) {
+ /* Frame Rxed, but with error. */
+ lp->stats.rx_errors++;
+ if (frame_status & 0x0800) lp->stats.rx_crc_errors++;
+ if (frame_status & 0x0400) lp->stats.rx_frame_errors++;
+ if (frame_status & 0x0200) lp->stats.rx_fifo_errors++;
+ if (frame_status & 0x0100) lp->stats.rx_over_errors++;
+ if (frame_status & 0x0080) lp->stats.rx_length_errors++;
+ } else {
+ /* Malloc up new buffer. */
+ struct sk_buff *skb;
+
+ pkt_len &= 0x3fff;
+ skb = dev_alloc_skb(pkt_len+2);
+ 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(data_buffer_addr + 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++;
+ }
+
+ /* Clear the status word and set End-of-List on the rx frame. */
+ outw(rx_head, ioaddr + WRITE_PTR);
+ outw(0x0000, ioaddr);
+ outw(0xC000, ioaddr);
+#ifndef final_version
+ if (next_rx_frame != rx_head + RX_BUF_SIZE
+ && next_rx_frame != RX_BUF_START) {
+ printk("%s: Rx next frame at %#x is %#x instead of %#x.\n", dev->name,
+ rx_head, next_rx_frame, rx_head + RX_BUF_SIZE);
+ next_rx_frame = rx_head + RX_BUF_SIZE;
+ if (next_rx_frame >= RX_BUF_END - RX_BUF_SIZE)
+ next_rx_frame = RX_BUF_START;
+ }
+#endif
+ outw(rx_tail+2, ioaddr + WRITE_PTR);
+ outw(0x0000, ioaddr); /* Clear the end-of-list on the prev. RFD. */
+
+#ifndef final_version
+ outw(rx_tail+4, ioaddr + READ_PTR);
+ if (inw(ioaddr) != rx_head) {
+ printk("%s: Rx buf link mismatch, at %04x link %04x instead of %04x.\n",
+ dev->name, rx_tail, (outw(rx_tail+4, ioaddr + READ_PTR),inw(ioaddr)),
+ rx_head);
+ outw(rx_head, ioaddr);
+ }
+#endif
+
+ rx_tail = rx_head;
+ rx_head = next_rx_frame;
+ if (--boguscount == 0)
+ break;
+ outw(rx_head, ioaddr + READ_PTR);
+ }
+
+ lp->rx_head = rx_head;
+ lp->rx_tail = rx_tail;
+
+ /* Restore the original write pointer. */
+ outw(saved_write_ptr, ioaddr + WRITE_PTR);
+}
+
+#ifdef MODULE
+static char devicename[9] = { 0, };
+static struct device dev_eexpress = {
+ devicename, /* device name is inserted by linux/drivers/net/net_init.c */
+ 0, 0, 0, 0,
+ 0, 0,
+ 0, 0, 0, NULL, express_probe };
+
+
+static int irq=0x300;
+static int io=0;
+
+int
+init_module(void)
+{
+ if (io == 0)
+ printk("eexpress: You should not use auto-probing with insmod!\n");
+ dev_eexpress.base_addr=io;
+ dev_eexpress.irq=irq;
+ if (register_netdev(&dev_eexpress) != 0)
+ return -EIO;
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ unregister_netdev(&dev_eexpress);
+ kfree_s(dev_eexpress.priv,sizeof(struct net_local));
+ dev_eexpress.priv=NULL;
+
+ /* If we don't do this, we can't re-insmod it later. */
+ release_region(dev_eexpress.base_addr, EEXPRESS_IO_EXTENT);
+}
+#endif /* MODULE */
+/*
+ * Local variables:
+ * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -I/usr/src/linux/drivers/net -Wall -Wstrict-prototypes -O6 -m486 -c eexpress.c"
+ * version-control: t
+ * kept-new-versions: 5
+ * tab-width: 4
+ * End:
+ */
diff --git a/i386/i386at/gpl/linux/net/eth16i.c b/i386/i386at/gpl/linux/net/eth16i.c
new file mode 100644
index 00000000..b21e4f33
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/eth16i.c
@@ -0,0 +1,1214 @@
+/* eth16i.c An ICL EtherTeam 16i and 32 EISA ethernet driver for Linux
+
+ Written 1994-95 by Mika Kuoppala
+
+ Copyright (C) 1994, 1995 by Mika Kuoppala
+ Based on skeleton.c and at1700.c by Donald Becker
+
+ This software may be used and distributed according to the terms
+ of the GNU Public Licence, incorporated herein by reference.
+
+ The author may be reached as miku@elt.icl.fi
+
+ This driver supports following cards :
+ - ICL EtherTeam 16i
+ - ICL EtherTeam 32 EISA
+
+ Sources:
+ - skeleton.c a sample network driver core for linux,
+ written by Donald Becker <becker@CESDIS.gsfc.nasa.gov>
+ - at1700.c a driver for Allied Telesis AT1700, written
+ by Donald Becker.
+ - e16iSRV.asm a Netware 3.X Server Driver for ICL EtherTeam16i
+ written by Markku Viima
+ - The Fujitsu MB86965 databook.
+
+ Valuable assistance from:
+ Markku Viima (ICL)
+ Ari Valve (ICL)
+
+ Revision history:
+
+ Version Date Description
+
+ 0.01 15.12-94 Initial version (card detection)
+ 0.02 23.01-95 Interrupt is now hooked correctly
+ 0.03 01.02-95 Rewrote initialization part
+ 0.04 07.02-95 Base skeleton done...
+ Made a few changes to signature checking
+ to make it a bit reliable.
+ - fixed bug in tx_buf mapping
+ - fixed bug in initialization (DLC_EN
+ wasn't enabled when initialization
+ was done.)
+ 0.05 08.02-95 If there were more than one packet to send,
+ transmit was jammed due to invalid
+ register write...now fixed
+ 0.06 19.02-95 Rewrote interrupt handling
+ 0.07 13.04-95 Wrote EEPROM read routines
+ Card configuration now set according to
+ data read from EEPROM
+ 0.08 23.06-95 Wrote part that tries to probe used interface
+ port if AUTO is selected
+
+ 0.09 01.09-95 Added module support
+
+ 0.10 04.09-95 Fixed receive packet allocation to work
+ with kernels > 1.3.x
+
+ 0.20 20.09-95 Added support for EtherTeam32 EISA
+
+ 0.21 17.10-95 Removed the unnecessary extern
+ init_etherdev() declaration. Some
+ other cleanups.
+ Bugs:
+ In some cases the interface autoprobing code doesn't find
+ the correct interface type. In this case you can
+ manually choose the interface type in DOS with E16IC.EXE which is
+ configuration software for EtherTeam16i and EtherTeam32 cards.
+
+ To do:
+ - Real multicast support
+*/
+
+static char *version =
+ "eth16i.c: v0.21 17-10-95 Mika Kuoppala (miku@elt.icl.fi)\n";
+
+#include <linux/module.h>
+
+#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/in.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+/* Few macros */
+#define BIT(a) ( (1 << (a)) )
+#define BITSET(ioaddr, bnum) ((outb(((inb(ioaddr)) | (bnum)), ioaddr)))
+#define BITCLR(ioaddr, bnum) ((outb(((inb(ioaddr)) & (~(bnum))), ioaddr)))
+
+/* This is the I/O address space for Etherteam 16i adapter. */
+#define ETH16I_IO_EXTENT 32
+
+/* Ticks before deciding that transmit has timed out */
+#define TIMEOUT_TICKS 30
+
+/* Maximum loop count when receiving packets */
+#define MAX_RX_LOOP 40
+
+/* Some interrupt masks */
+#define ETH16I_INTR_ON 0x8f82
+#define ETH16I_INTR_OFF 0x0000
+
+/* Buffers header status byte meanings */
+#define PKT_GOOD BIT(5)
+#define PKT_GOOD_RMT BIT(4)
+#define PKT_SHORT BIT(3)
+#define PKT_ALIGN_ERR BIT(2)
+#define PKT_CRC_ERR BIT(1)
+#define PKT_RX_BUF_OVERFLOW BIT(0)
+
+/* Transmit status register (DLCR0) */
+#define TX_STATUS_REG 0
+#define TX_DONE BIT(7)
+#define NET_BUSY BIT(6)
+#define TX_PKT_RCD BIT(5)
+#define CR_LOST BIT(4)
+#define COLLISION BIT(2)
+#define COLLISIONS_16 BIT(1)
+
+/* Receive status register (DLCR1) */
+#define RX_STATUS_REG 1
+#define RX_PKT BIT(7) /* Packet received */
+#define BUS_RD_ERR BIT(6)
+#define SHORT_PKT_ERR BIT(3)
+#define ALIGN_ERR BIT(2)
+#define CRC_ERR BIT(1)
+#define RX_BUF_OVERFLOW BIT(0)
+
+/* Transmit Interrupt Enable Register (DLCR2) */
+#define TX_INTR_REG 2
+#define TX_INTR_DONE BIT(7)
+#define TX_INTR_COL BIT(2)
+#define TX_INTR_16_COL BIT(1)
+
+/* Receive Interrupt Enable Register (DLCR3) */
+#define RX_INTR_REG 3
+#define RX_INTR_RECEIVE BIT(7)
+#define RX_INTR_SHORT_PKT BIT(3)
+#define RX_INTR_CRC_ERR BIT(1)
+#define RX_INTR_BUF_OVERFLOW BIT(0)
+
+/* Transmit Mode Register (DLCR4) */
+#define TRANSMIT_MODE_REG 4
+#define LOOPBACK_CONTROL BIT(1)
+#define CONTROL_OUTPUT BIT(2)
+
+/* Receive Mode Register (DLCR5) */
+#define RECEIVE_MODE_REG 5
+#define RX_BUFFER_EMPTY BIT(6)
+#define ACCEPT_BAD_PACKETS BIT(5)
+#define RECEIVE_SHORT_ADDR BIT(4)
+#define ACCEPT_SHORT_PACKETS BIT(3)
+#define REMOTE_RESET BIT(2)
+
+#define ADDRESS_FILTER_MODE BIT(1) | BIT(0)
+#define REJECT_ALL 0
+#define ACCEPT_ALL 3
+#define MODE_1 1 /* NODE ID, BC, MC, 2-24th bit */
+#define MODE_2 2 /* NODE ID, BC, MC, Hash Table */
+
+/* Configuration Register 0 (DLCR6) */
+#define CONFIG_REG_0 6
+#define DLC_EN BIT(7)
+#define SRAM_CYCLE_TIME_100NS BIT(6)
+#define SYSTEM_BUS_WIDTH_8 BIT(5) /* 1 = 8bit, 0 = 16bit */
+#define BUFFER_WIDTH_8 BIT(4) /* 1 = 8bit, 0 = 16bit */
+#define TBS1 BIT(3)
+#define TBS0 BIT(2)
+#define BS1 BIT(1) /* 00=8kb, 01=16kb */
+#define BS0 BIT(0) /* 10=32kb, 11=64kb */
+
+#ifndef ETH16I_TX_BUF_SIZE /* 0 = 2kb, 1 = 4kb */
+#define ETH16I_TX_BUF_SIZE 2 /* 2 = 8kb, 3 = 16kb */
+#endif
+#define TX_BUF_1x2048 0
+#define TX_BUF_2x2048 1
+#define TX_BUF_2x4098 2
+#define TX_BUF_2x8192 3
+
+/* Configuration Register 1 (DLCR7) */
+#define CONFIG_REG_1 7
+#define POWERUP BIT(5)
+
+/* Transmit start register */
+#define TRANSMIT_START_REG 10
+#define TRANSMIT_START_RB 2
+#define TX_START BIT(7) /* Rest of register bit indicate*/
+ /* number of packets in tx buffer*/
+/* Node ID registers (DLCR8-13) */
+#define NODE_ID_0 8
+#define NODE_ID_RB 0
+
+/* Hash Table registers (HT8-15) */
+#define HASH_TABLE_0 8
+#define HASH_TABLE_RB 1
+
+/* Buffer memory ports */
+#define BUFFER_MEM_PORT_LB 8
+#define DATAPORT BUFFER_MEM_PORT_LB
+#define BUFFER_MEM_PORT_HB 9
+
+/* 16 Collision control register (BMPR11) */
+#define COL_16_REG 11
+#define HALT_ON_16 0x00
+#define RETRANS_AND_HALT_ON_16 0x02
+
+/* DMA Burst and Transceiver Mode Register (BMPR13) */
+#define TRANSCEIVER_MODE_REG 13
+#define TRANSCEIVER_MODE_RB 2
+#define IO_BASE_UNLOCK BIT(7)
+#define LOWER_SQUELCH_TRESH BIT(6)
+#define LINK_TEST_DISABLE BIT(5)
+#define AUI_SELECT BIT(4)
+#define DIS_AUTO_PORT_SEL BIT(3)
+
+/* Filter Self Receive Register (BMPR14) */
+#define FILTER_SELF_RX_REG 14
+#define SKIP_RECEIVE_PACKET BIT(2)
+#define FILTER_SELF_RECEIVE BIT(0)
+#define RX_BUF_SKIP_PACKET SKIP_RECEIVE_PACKET | FILTER_SELF_RECEIVE
+
+/* EEPROM Control Register (BMPR 16) */
+#define EEPROM_CTRL_REG 16
+
+/* EEPROM Data Register (BMPR 17) */
+#define EEPROM_DATA_REG 17
+
+/* NMC93CSx6 EEPROM Control Bits */
+#define CS_0 0x00
+#define CS_1 0x20
+#define SK_0 0x00
+#define SK_1 0x40
+#define DI_0 0x00
+#define DI_1 0x80
+
+/* NMC93CSx6 EEPROM Instructions */
+#define EEPROM_READ 0x80
+
+/* NMC93CSx6 EEPROM Addresses */
+#define E_NODEID_0 0x02
+#define E_NODEID_1 0x03
+#define E_NODEID_2 0x04
+#define E_PORT_SELECT 0x14
+ #define E_PORT_BNC 0
+ #define E_PORT_DIX 1
+ #define E_PORT_TP 2
+ #define E_PORT_AUTO 3
+#define E_PRODUCT_CFG 0x30
+
+
+/* Macro to slow down io between EEPROM clock transitions */
+#define eeprom_slow_io() do { int _i = 40; while(--_i > 0) { __SLOW_DOWN_IO; }}while(0)
+
+/* Jumperless Configuration Register (BMPR19) */
+#define JUMPERLESS_CONFIG 19
+
+/* ID ROM registers, writing to them also resets some parts of chip */
+#define ID_ROM_0 24
+#define ID_ROM_7 31
+#define RESET ID_ROM_0
+
+/* This is the I/O address list to be probed when seeking the card */
+static unsigned int eth16i_portlist[] =
+ { 0x260, 0x280, 0x2A0, 0x240, 0x340, 0x320, 0x380, 0x300, 0 };
+
+static unsigned int eth32i_portlist[] =
+ { 0x1000, 0x2000, 0x3000, 0x4000, 0x5000, 0x6000, 0x7000, 0x8000,
+ 0x9000, 0xA000, 0xB000, 0xC000, 0xD000, 0xE000, 0xF000, 0 };
+
+/* This is the Interrupt lookup table for Eth16i card */
+static unsigned int eth16i_irqmap[] = { 9, 10, 5, 15 };
+
+/* This is the Interrupt lookup table for Eth32i card */
+static unsigned int eth32i_irqmap[] = { 3, 5, 7, 9, 10, 11, 12, 15 };
+#define EISA_IRQ_REG 0xc89
+
+static unsigned int eth16i_tx_buf_map[] = { 2048, 2048, 4096, 8192 };
+unsigned int boot = 1;
+
+/* Use 0 for production, 1 for verification, >2 for debug */
+#ifndef ETH16I_DEBUG
+#define ETH16I_DEBUG 0
+#endif
+static unsigned int eth16i_debug = ETH16I_DEBUG;
+
+/* Information for each board */
+struct eth16i_local {
+ struct enet_statistics stats;
+ unsigned int tx_started:1;
+ unsigned char tx_queue; /* Number of packets in transmit buffer */
+ unsigned short tx_queue_len;
+ unsigned int tx_buf_size;
+ unsigned long open_time;
+};
+
+/* Function prototypes */
+
+extern int eth16i_probe(struct device *dev);
+
+static int eth16i_probe1(struct device *dev, short ioaddr);
+static int eth16i_check_signature(short ioaddr);
+static int eth16i_probe_port(short ioaddr);
+static void eth16i_set_port(short ioaddr, int porttype);
+static int eth16i_send_probe_packet(short ioaddr, unsigned char *b, int l);
+static int eth16i_receive_probe_packet(short ioaddr);
+static int eth16i_get_irq(short ioaddr);
+static int eth16i_read_eeprom(int ioaddr, int offset);
+static int eth16i_read_eeprom_word(int ioaddr);
+static void eth16i_eeprom_cmd(int ioaddr, unsigned char command);
+static int eth16i_open(struct device *dev);
+static int eth16i_close(struct device *dev);
+static int eth16i_tx(struct sk_buff *skb, struct device *dev);
+static void eth16i_rx(struct device *dev);
+static void eth16i_interrupt(int irq, struct pt_regs *regs);
+static void eth16i_multicast(struct device *dev, int num_addrs, void *addrs);
+static void eth16i_select_regbank(unsigned char regbank, short ioaddr);
+static void eth16i_initialize(struct device *dev);
+static struct enet_statistics *eth16i_get_stats(struct device *dev);
+
+static char *cardname = "ICL EtherTeam 16i/32";
+
+#ifdef HAVE_DEVLIST
+/* Support for alternate probe manager */
+/struct netdev_entry eth16i_drv =
+ {"eth16i", eth16i_probe1, ETH16I_IO_EXTENT, eth16i_probe_list};
+
+#else /* Not HAVE_DEVLIST */
+int eth16i_probe(struct device *dev)
+{
+ int i;
+ int ioaddr;
+ int base_addr = dev ? dev->base_addr : 0;
+
+ if(eth16i_debug > 4)
+ printk("Probing started for %s\n", cardname);
+
+ if(base_addr > 0x1ff) /* Check only single location */
+ return eth16i_probe1(dev, base_addr);
+ else if(base_addr != 0) /* Don't probe at all */
+ return ENXIO;
+
+ /* Seek card from the ISA io address space */
+ for(i = 0; (ioaddr = eth16i_portlist[i]) ; i++) {
+ if(check_region(ioaddr, ETH16I_IO_EXTENT))
+ continue;
+ if(eth16i_probe1(dev, ioaddr) == 0)
+ return 0;
+ }
+
+ /* Seek card from the EISA io address space */
+ for(i = 0; (ioaddr = eth32i_portlist[i]) ; i++) {
+ if(check_region(ioaddr, ETH16I_IO_EXTENT))
+ continue;
+ if(eth16i_probe1(dev, ioaddr) == 0)
+ return 0;
+ }
+
+ return ENODEV;
+}
+#endif /* Not HAVE_DEVLIST */
+
+static int eth16i_probe1(struct device *dev, short ioaddr)
+{
+ static unsigned version_printed = 0;
+ unsigned int irq = 0;
+ boot = 1; /* To inform initilization that we are in boot probe */
+
+ /*
+ The MB86985 chip has on register which holds information in which
+ io address the chip lies. First read this register and compare
+ it to our current io address and if match then this could
+ be our chip.
+ */
+
+ if(ioaddr < 0x1000) {
+ if(eth16i_portlist[(inb(ioaddr + JUMPERLESS_CONFIG) & 0x07)] != ioaddr)
+ return -ENODEV;
+ }
+
+ /* Now we will go a bit deeper and try to find the chip's signature */
+
+ if(eth16i_check_signature(ioaddr) != 0) /* Can we find the signature here */
+ return -ENODEV;
+
+ /*
+ Now it seems that we have found a ethernet chip in this particular
+ ioaddr. The MB86985 chip has this feature, that when you read a
+ certain register it will increase it's io base address to next
+ configurable slot. Now when we have found the chip, first thing is
+ to make sure that the chip's ioaddr will hold still here.
+ */
+
+ eth16i_select_regbank(TRANSCEIVER_MODE_RB, ioaddr);
+ outb(0x00, ioaddr + TRANSCEIVER_MODE_REG);
+
+ outb(0x00, ioaddr + RESET); /* Will reset some parts of chip */
+ BITSET(ioaddr + CONFIG_REG_0, BIT(7)); /* This will disable the data link */
+
+ if(dev == NULL)
+ dev = init_etherdev(0, sizeof(struct eth16i_local));
+
+ if( (eth16i_debug & version_printed++) == 0)
+ printk(version);
+
+ dev->base_addr = ioaddr;
+
+ irq = eth16i_get_irq(ioaddr);
+ dev->irq = irq;
+
+ /* Try to obtain interrupt vector */
+ if(request_irq(dev->irq, &eth16i_interrupt, 0, "eth16i")) {
+ printk("%s: %s at %#3x, but is unusable due
+ conflict on IRQ %d.\n", dev->name, cardname, ioaddr, irq);
+ return EAGAIN;
+ }
+
+ printk("%s: %s at %#3x, IRQ %d, ",
+ dev->name, cardname, ioaddr, dev->irq);
+
+ /* Let's grab the region */
+ request_region(ioaddr, ETH16I_IO_EXTENT, "eth16i");
+
+ /* Now we will have to lock the chip's io address */
+ eth16i_select_regbank(TRANSCEIVER_MODE_RB, ioaddr);
+ outb(0x38, ioaddr + TRANSCEIVER_MODE_REG);
+
+ eth16i_initialize(dev); /* Initialize rest of the chip's registers */
+
+ /* Now let's same some energy by shutting down the chip ;) */
+ BITCLR(ioaddr + CONFIG_REG_1, POWERUP);
+
+ /* Initialize the device structure */
+ if(dev->priv == NULL)
+ dev->priv = kmalloc(sizeof(struct eth16i_local), GFP_KERNEL);
+ memset(dev->priv, 0, sizeof(struct eth16i_local));
+
+ dev->open = eth16i_open;
+ dev->stop = eth16i_close;
+ dev->hard_start_xmit = eth16i_tx;
+ dev->get_stats = eth16i_get_stats;
+ dev->set_multicast_list = &eth16i_multicast;
+
+ /* Fill in the fields of the device structure with ethernet values. */
+ ether_setup(dev);
+
+ boot = 0;
+
+ return 0;
+}
+
+
+static void eth16i_initialize(struct device *dev)
+{
+ short ioaddr = dev->base_addr;
+ int i, node_w = 0;
+ unsigned char node_byte = 0;
+
+ /* Setup station address */
+ eth16i_select_regbank(NODE_ID_RB, ioaddr);
+ for(i = 0 ; i < 3 ; i++) {
+ unsigned short node_val = eth16i_read_eeprom(ioaddr, E_NODEID_0 + i);
+ ((unsigned short *)dev->dev_addr)[i] = ntohs(node_val);
+ }
+
+ for(i = 0; i < 6; i++) {
+ outb( ((unsigned char *)dev->dev_addr)[i], ioaddr + NODE_ID_0 + i);
+ if(boot) {
+ printk("%02x", inb(ioaddr + NODE_ID_0 + i));
+ if(i != 5)
+ printk(":");
+ }
+ }
+
+ /* Now we will set multicast addresses to accept none */
+ eth16i_select_regbank(HASH_TABLE_RB, ioaddr);
+ for(i = 0; i < 8; i++)
+ outb(0x00, ioaddr + HASH_TABLE_0 + i);
+
+ /*
+ Now let's disable the transmitter and receiver, set the buffer ram
+ cycle time, bus width and buffer data path width. Also we shall
+ set transmit buffer size and total buffer size.
+ */
+
+ eth16i_select_regbank(2, ioaddr);
+
+ node_byte = 0;
+ node_w = eth16i_read_eeprom(ioaddr, E_PRODUCT_CFG);
+
+ if( (node_w & 0xFF00) == 0x0800)
+ node_byte |= BUFFER_WIDTH_8;
+
+ node_byte |= BS1;
+
+ if( (node_w & 0x00FF) == 64)
+ node_byte |= BS0;
+
+ node_byte |= DLC_EN | SRAM_CYCLE_TIME_100NS | (ETH16I_TX_BUF_SIZE << 2);
+
+ outb(node_byte, ioaddr + CONFIG_REG_0);
+
+ /* We shall halt the transmitting, if 16 collisions are detected */
+ outb(RETRANS_AND_HALT_ON_16, ioaddr + COL_16_REG);
+
+ if(boot) /* Now set port type */
+ {
+ char *porttype[] = {"BNC", "DIX", "TP", "AUTO"};
+
+ ushort ptype = eth16i_read_eeprom(ioaddr, E_PORT_SELECT);
+ dev->if_port = (ptype & 0x00FF);
+
+ printk(" %s interface.\n", porttype[dev->if_port]);
+
+ if(ptype == E_PORT_AUTO)
+ ptype = eth16i_probe_port(ioaddr);
+
+ eth16i_set_port(ioaddr, ptype);
+ }
+
+ /* Set Receive Mode to normal operation */
+ outb(MODE_2, ioaddr + RECEIVE_MODE_REG);
+}
+
+static int eth16i_probe_port(short ioaddr)
+{
+ int i;
+ int retcode;
+ unsigned char dummy_packet[64] = { 0 };
+
+ /* Powerup the chip */
+ outb(0xc0 | POWERUP, ioaddr + CONFIG_REG_1);
+
+ BITSET(ioaddr + CONFIG_REG_0, DLC_EN);
+
+ eth16i_select_regbank(NODE_ID_RB, ioaddr);
+
+ for(i = 0; i < 6; i++) {
+ dummy_packet[i] = inb(ioaddr + NODE_ID_0 + i);
+ dummy_packet[i+6] = inb(ioaddr + NODE_ID_0 + i);
+ }
+
+ dummy_packet[12] = 0x00;
+ dummy_packet[13] = 0x04;
+
+ eth16i_select_regbank(2, ioaddr);
+
+ for(i = 0; i < 3; i++) {
+ BITSET(ioaddr + CONFIG_REG_0, DLC_EN);
+ BITCLR(ioaddr + CONFIG_REG_0, DLC_EN);
+ eth16i_set_port(ioaddr, i);
+
+ if(eth16i_debug > 1)
+ printk("Set port number %d\n", i);
+
+ retcode = eth16i_send_probe_packet(ioaddr, dummy_packet, 64);
+ if(retcode == 0) {
+ retcode = eth16i_receive_probe_packet(ioaddr);
+ if(retcode != -1) {
+ if(eth16i_debug > 1)
+ printk("Eth16i interface port found at %d\n", i);
+ return i;
+ }
+ }
+ else {
+ if(eth16i_debug > 1)
+ printk("TRANSMIT_DONE timeout\n");
+ }
+ }
+
+ if( eth16i_debug > 1)
+ printk("Using default port\n");
+
+ return E_PORT_BNC;
+}
+
+static void eth16i_set_port(short ioaddr, int porttype)
+{
+ unsigned short temp = 0;
+
+ eth16i_select_regbank(TRANSCEIVER_MODE_RB, ioaddr);
+ outb(LOOPBACK_CONTROL, ioaddr + TRANSMIT_MODE_REG);
+
+ temp |= DIS_AUTO_PORT_SEL;
+
+ switch(porttype) {
+
+ case E_PORT_BNC :
+ temp |= AUI_SELECT;
+ break;
+
+ case E_PORT_TP :
+ break;
+
+ case E_PORT_DIX :
+ temp |= AUI_SELECT;
+ BITSET(ioaddr + TRANSMIT_MODE_REG, CONTROL_OUTPUT);
+ break;
+ }
+ outb(temp, ioaddr + TRANSCEIVER_MODE_REG);
+
+ if(eth16i_debug > 1) {
+ printk("TRANSMIT_MODE_REG = %x\n", inb(ioaddr + TRANSMIT_MODE_REG));
+ printk("TRANSCEIVER_MODE_REG = %x\n", inb(ioaddr+TRANSCEIVER_MODE_REG));
+ }
+}
+
+static int eth16i_send_probe_packet(short ioaddr, unsigned char *b, int l)
+{
+ int starttime;
+
+ outb(0xff, ioaddr + TX_STATUS_REG);
+
+ outw(l, ioaddr + DATAPORT);
+ outsw(ioaddr + DATAPORT, (unsigned short *)b, (l + 1) >> 1);
+
+ starttime = jiffies;
+ outb(TX_START | 1, ioaddr + TRANSMIT_START_REG);
+
+ while( (inb(ioaddr + TX_STATUS_REG) & 0x80) == 0) {
+ if( (jiffies - starttime) > TIMEOUT_TICKS) {
+ break;
+ }
+ }
+
+ return(0);
+}
+
+static int eth16i_receive_probe_packet(short ioaddr)
+{
+ int starttime;
+
+ starttime = jiffies;
+
+ while((inb(ioaddr + TX_STATUS_REG) & 0x20) == 0) {
+ if( (jiffies - starttime) > TIMEOUT_TICKS) {
+
+ if(eth16i_debug > 1)
+ printk("Timeout occured waiting transmit packet received\n");
+ starttime = jiffies;
+ while((inb(ioaddr + RX_STATUS_REG) & 0x80) == 0) {
+ if( (jiffies - starttime) > TIMEOUT_TICKS) {
+ if(eth16i_debug > 1)
+ printk("Timeout occured waiting receive packet\n");
+ return -1;
+ }
+ }
+
+ if(eth16i_debug > 1)
+ printk("RECEIVE_PACKET\n");
+ return(0); /* Found receive packet */
+ }
+ }
+
+ if(eth16i_debug > 1) {
+ printk("TRANSMIT_PACKET_RECEIVED %x\n", inb(ioaddr + TX_STATUS_REG));
+ printk("RX_STATUS_REG = %x\n", inb(ioaddr + RX_STATUS_REG));
+ }
+
+ return(0); /* Return success */
+}
+
+static int eth16i_get_irq(short ioaddr)
+{
+ unsigned char cbyte;
+
+ if( ioaddr < 0x1000) {
+ cbyte = inb(ioaddr + JUMPERLESS_CONFIG);
+ return( eth16i_irqmap[ ((cbyte & 0xC0) >> 6) ] );
+ } else { /* Oh..the card is EISA so method getting IRQ different */
+ unsigned short index = 0;
+ cbyte = inb(ioaddr + EISA_IRQ_REG);
+ while( (cbyte & 0x01) == 0) {
+ cbyte = cbyte >> 1;
+ index++;
+ }
+ return( eth32i_irqmap[ index ] );
+ }
+}
+
+static int eth16i_check_signature(short ioaddr)
+{
+ int i;
+ unsigned char creg[4] = { 0 };
+
+ for(i = 0; i < 4 ; i++) {
+
+ creg[i] = inb(ioaddr + TRANSMIT_MODE_REG + i);
+
+ if(eth16i_debug > 1)
+ printk("eth16i: read signature byte %x at %x\n", creg[i],
+ ioaddr + TRANSMIT_MODE_REG + i);
+ }
+
+ creg[0] &= 0x0F; /* Mask collision cnr */
+ creg[2] &= 0x7F; /* Mask DCLEN bit */
+
+#ifdef 0
+/*
+ This was removed because the card was sometimes left to state
+ from which it couldn't be find anymore. If there is need
+ to more strict chech still this have to be fixed.
+*/
+ if( !( (creg[0] == 0x06) && (creg[1] == 0x41)) ) {
+ if(creg[1] != 0x42)
+ return -1;
+ }
+#endif
+
+ if( !( (creg[2] == 0x36) && (creg[3] == 0xE0)) ) {
+ creg[2] &= 0x42;
+ creg[3] &= 0x03;
+
+ if( !( (creg[2] == 0x42) && (creg[3] == 0x00)) )
+ return -1;
+ }
+
+ if(eth16i_read_eeprom(ioaddr, E_NODEID_0) != 0)
+ return -1;
+ if((eth16i_read_eeprom(ioaddr, E_NODEID_1) & 0xFF00) != 0x4B00)
+ return -1;
+
+ return 0;
+}
+
+static int eth16i_read_eeprom(int ioaddr, int offset)
+{
+ int data = 0;
+
+ eth16i_eeprom_cmd(ioaddr, EEPROM_READ | offset);
+ outb(CS_1, ioaddr + EEPROM_CTRL_REG);
+ data = eth16i_read_eeprom_word(ioaddr);
+ outb(CS_0 | SK_0, ioaddr + EEPROM_CTRL_REG);
+
+ return(data);
+}
+
+static int eth16i_read_eeprom_word(int ioaddr)
+{
+ int i;
+ int data = 0;
+
+ for(i = 16; i > 0; i--) {
+ outb(CS_1 | SK_0, ioaddr + EEPROM_CTRL_REG);
+ eeprom_slow_io();
+ outb(CS_1 | SK_1, ioaddr + EEPROM_CTRL_REG);
+ eeprom_slow_io();
+ data = (data << 1) | ((inb(ioaddr + EEPROM_DATA_REG) & DI_1) ? 1 : 0);
+ eeprom_slow_io();
+ }
+
+ return(data);
+}
+
+static void eth16i_eeprom_cmd(int ioaddr, unsigned char command)
+{
+ int i;
+
+ outb(CS_0 | SK_0, ioaddr + EEPROM_CTRL_REG);
+ outb(DI_0, ioaddr + EEPROM_DATA_REG);
+ outb(CS_1 | SK_0, ioaddr + EEPROM_CTRL_REG);
+ outb(DI_1, ioaddr + EEPROM_DATA_REG);
+ outb(CS_1 | SK_1, ioaddr + EEPROM_CTRL_REG);
+
+ for(i = 7; i >= 0; i--) {
+ short cmd = ( (command & (1 << i)) ? DI_1 : DI_0 );
+ outb(cmd, ioaddr + EEPROM_DATA_REG);
+ outb(CS_1 | SK_0, ioaddr + EEPROM_CTRL_REG);
+ eeprom_slow_io();
+ outb(CS_1 | SK_1, ioaddr + EEPROM_CTRL_REG);
+ eeprom_slow_io();
+ }
+}
+
+static int eth16i_open(struct device *dev)
+{
+ struct eth16i_local *lp = (struct eth16i_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+
+ irq2dev_map[dev->irq] = dev;
+
+ /* Powerup the chip */
+ outb(0xc0 | POWERUP, ioaddr + CONFIG_REG_1);
+
+ /* Initialize the chip */
+ eth16i_initialize(dev);
+
+ /* Set the transmit buffer size */
+ lp->tx_buf_size = eth16i_tx_buf_map[ETH16I_TX_BUF_SIZE & 0x03];
+
+ if(eth16i_debug > 3)
+ printk("%s: transmit buffer size %d\n", dev->name, lp->tx_buf_size);
+
+ /* Now enable Transmitter and Receiver sections */
+ BITCLR(ioaddr + CONFIG_REG_0, DLC_EN);
+
+ /* Now switch to register bank 2, for run time operation */
+ eth16i_select_regbank(2, ioaddr);
+
+ lp->open_time = jiffies;
+ lp->tx_started = 0;
+ lp->tx_queue = 0;
+ lp->tx_queue_len = 0;
+
+ /* Turn on interrupts*/
+ outw(ETH16I_INTR_ON, ioaddr + TX_INTR_REG);
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+
+#ifdef MODULE
+ MOD_INC_USE_COUNT;
+#endif
+
+ return 0;
+}
+
+static int eth16i_close(struct device *dev)
+{
+ struct eth16i_local *lp = (struct eth16i_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+
+ lp->open_time = 0;
+
+ dev->tbusy = 1;
+ dev->start = 0;
+
+ /* Disable transmit and receive */
+ BITSET(ioaddr + CONFIG_REG_0, DLC_EN);
+
+ /* Reset the chip */
+ outb(0xff, ioaddr + RESET);
+
+ /* Save some energy by switching off power */
+ BITCLR(ioaddr + CONFIG_REG_1, POWERUP);
+
+#ifdef MODULE
+ MOD_DEC_USE_COUNT;
+#endif
+
+ return 0;
+}
+
+static int eth16i_tx(struct sk_buff *skb, struct device *dev)
+{
+ struct eth16i_local *lp = (struct eth16i_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+
+ if(dev->tbusy) {
+ /*
+ If we get here, some higher level has decided that we are broken.
+ There should really be a "kick me" function call instead.
+ */
+
+ int tickssofar = jiffies - dev->trans_start;
+ if(tickssofar < TIMEOUT_TICKS) /* Let's not rush with our timeout, */
+ return 1; /* wait a couple of ticks first */
+
+ printk("%s: transmit timed out with status %04x, %s ?\n", dev->name,
+ inw(ioaddr + TX_STATUS_REG),
+ (inb(ioaddr + TX_STATUS_REG) & TX_DONE) ?
+ "IRQ conflict" : "network cable problem");
+
+ /* Let's dump all registers */
+ if(eth16i_debug > 0) {
+ printk("%s: timeout regs: %02x %02x %02x %02x %02x %02x %02x %02x.\n",
+ dev->name, inb(ioaddr + 0), inb(ioaddr + 1), inb(ioaddr + 2),
+ inb(ioaddr + 3), inb(ioaddr + 4), inb(ioaddr + 5),
+ inb(ioaddr + 6), inb(ioaddr + 7));
+
+
+ printk("lp->tx_queue = %d\n", lp->tx_queue);
+ printk("lp->tx_queue_len = %d\n", lp->tx_queue_len);
+ printk("lp->tx_started = %d\n", lp->tx_started);
+
+ }
+
+ lp->stats.tx_errors++;
+
+ /* Now let's try to restart the adaptor */
+
+ BITSET(ioaddr + CONFIG_REG_0, DLC_EN);
+ outw(0xffff, ioaddr + RESET);
+ eth16i_initialize(dev);
+ outw(0xffff, ioaddr + TX_STATUS_REG);
+ BITCLR(ioaddr + CONFIG_REG_0, DLC_EN);
+
+ lp->tx_started = 0;
+ lp->tx_queue = 0;
+ lp->tx_queue_len = 0;
+
+ outw(ETH16I_INTR_ON, ioaddr + TX_INTR_REG);
+
+ dev->tbusy = 0;
+ dev->trans_start = jiffies;
+ }
+
+ /*
+ If some higher layer thinks we've missed an tx-done interrupt
+ we are passed NULL. Caution: dev_tint() handles the cli()/sti()
+ itself
+ */
+ if(skb == NULL) {
+ dev_tint(dev);
+ return 0;
+ }
+
+ /* Block a timer based transmitter from overlapping. This could better be
+ done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
+
+ /* Turn off TX interrupts */
+ outw(ETH16I_INTR_OFF, ioaddr + TX_INTR_REG);
+
+ if(set_bit(0, (void *)&dev->tbusy) != 0)
+ printk("%s: Transmitter access conflict.\n", dev->name);
+ else {
+ short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+ unsigned char *buf = skb->data;
+
+ outw(length, ioaddr + DATAPORT);
+
+ if( ioaddr < 0x1000 )
+ outsw(ioaddr + DATAPORT, buf, (length + 1) >> 1);
+ else {
+ unsigned char frag = length % 4;
+
+ outsl(ioaddr + DATAPORT, buf, length >> 2);
+
+ if( frag != 0 ) {
+ outsw(ioaddr + DATAPORT, (buf + (length & 0xFFFC)), 1);
+ if( frag == 3 )
+ outsw(ioaddr + DATAPORT, (buf + (length & 0xFFFC) + 2), 1);
+ }
+ }
+
+ lp->tx_queue++;
+ lp->tx_queue_len += length + 2;
+
+ if(lp->tx_started == 0) {
+ /* If the transmitter is idle..always trigger a transmit */
+ outb(TX_START | lp->tx_queue, ioaddr + TRANSMIT_START_REG);
+ lp->tx_queue = 0;
+ lp->tx_queue_len = 0;
+ dev->trans_start = jiffies;
+ lp->tx_started = 1;
+ dev->tbusy = 0;
+ }
+ else if(lp->tx_queue_len < lp->tx_buf_size - (ETH_FRAME_LEN + 2)) {
+ /* There is still more room for one more packet in tx buffer */
+ dev->tbusy = 0;
+ }
+
+ outw(ETH16I_INTR_ON, ioaddr + TX_INTR_REG);
+
+ /* Turn TX interrupts back on */
+ /* outb(TX_INTR_DONE | TX_INTR_16_COL, ioaddr + TX_INTR_REG); */
+ }
+ dev_kfree_skb(skb, FREE_WRITE);
+
+ return 0;
+}
+
+static void eth16i_rx(struct device *dev)
+{
+ struct eth16i_local *lp = (struct eth16i_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+ int boguscount = MAX_RX_LOOP;
+
+ /* Loop until all packets have been read */
+ while( (inb(ioaddr + RECEIVE_MODE_REG) & RX_BUFFER_EMPTY) == 0) {
+
+ /* Read status byte from receive buffer */
+ ushort status = inw(ioaddr + DATAPORT);
+
+ if(eth16i_debug > 4)
+ printk("%s: Receiving packet mode %02x status %04x.\n",
+ dev->name, inb(ioaddr + RECEIVE_MODE_REG), status);
+
+ if( !(status & PKT_GOOD) ) {
+ /* Hmm..something went wrong. Let's check what error occured */
+ lp->stats.rx_errors++;
+ if( status & PKT_SHORT ) lp->stats.rx_length_errors++;
+ if( status & PKT_ALIGN_ERR ) lp->stats.rx_frame_errors++;
+ if( status & PKT_CRC_ERR ) lp->stats.rx_crc_errors++;
+ if( status & PKT_RX_BUF_OVERFLOW) lp->stats.rx_over_errors++;
+ }
+ else { /* Ok so now we should have a good packet */
+ struct sk_buff *skb;
+
+ /* Get the size of the packet from receive buffer */
+ ushort pkt_len = inw(ioaddr + DATAPORT);
+
+ if(pkt_len > ETH_FRAME_LEN) {
+ printk("%s: %s claimed a very large packet, size of %d bytes.\n",
+ dev->name, cardname, pkt_len);
+ outb(RX_BUF_SKIP_PACKET, ioaddr + FILTER_SELF_RX_REG);
+ lp->stats.rx_dropped++;
+ break;
+ }
+
+ skb = dev_alloc_skb(pkt_len + 3);
+ if( skb == NULL ) {
+ printk("%s: Could'n allocate memory for packet (len %d)\n",
+ dev->name, pkt_len);
+ outb(RX_BUF_SKIP_PACKET, ioaddr + FILTER_SELF_RX_REG);
+ lp->stats.rx_dropped++;
+ break;
+ }
+
+ skb->dev = dev;
+ skb_reserve(skb,2);
+ /*
+ Now let's get the packet out of buffer.
+ size is (pkt_len + 1) >> 1, cause we are now reading words
+ and it have to be even aligned.
+ */
+
+ if( ioaddr < 0x1000)
+ insw(ioaddr + DATAPORT, skb_put(skb, pkt_len), (pkt_len + 1) >> 1);
+ else {
+ unsigned char *buf = skb_put(skb, pkt_len);
+ unsigned char frag = pkt_len % 4;
+
+ insl(ioaddr + DATAPORT, buf, pkt_len >> 2);
+
+ if(frag != 0) {
+ unsigned short rest[2];
+ rest[0] = inw( ioaddr + DATAPORT );
+ if(frag == 3)
+ rest[1] = inw( ioaddr + DATAPORT );
+
+ memcpy(buf + (pkt_len & 0xfffc), (char *)rest, frag);
+ }
+ }
+
+ skb->protocol=eth_type_trans(skb, dev);
+ netif_rx(skb);
+ lp->stats.rx_packets++;
+
+ if( eth16i_debug > 5 ) {
+ int i;
+ printk("%s: Received packet of length %d.\n", dev->name, pkt_len);
+ for(i = 0; i < 14; i++)
+ printk(" %02x", skb->data[i]);
+ printk(".\n");
+ }
+
+ } /* else */
+
+ if(--boguscount <= 0)
+ break;
+
+ } /* while */
+
+#if 0
+ {
+ int i;
+
+ for(i = 0; i < 20; i++) {
+ if( (inb(ioaddr+RECEIVE_MODE_REG) & RX_BUFFER_EMPTY) == RX_BUFFER_EMPTY)
+ break;
+ inw(ioaddr + DATAPORT);
+ outb(RX_BUF_SKIP_PACKET, ioaddr + FILTER_SELF_RX_REG);
+ }
+
+ if(eth16i_debug > 1)
+ printk("%s: Flushed receive buffer.\n", dev->name);
+ }
+#endif
+
+ return;
+}
+
+static void eth16i_interrupt(int irq, struct pt_regs *regs)
+{
+ struct device *dev = (struct device *)(irq2dev_map[irq]);
+ struct eth16i_local *lp;
+ int ioaddr = 0,
+ status;
+
+ if(dev == NULL) {
+ printk("eth16i_interrupt(): irq %d for unknown device. \n", irq);
+ return;
+ }
+
+ /* Turn off all interrupts from adapter */
+ outw(ETH16I_INTR_OFF, ioaddr + TX_INTR_REG);
+
+ dev->interrupt = 1;
+
+ ioaddr = dev->base_addr;
+ lp = (struct eth16i_local *)dev->priv;
+ status = inw(ioaddr + TX_STATUS_REG); /* Get the status */
+ outw(status, ioaddr + TX_STATUS_REG); /* Clear status bits */
+
+ if(eth16i_debug > 3)
+ printk("%s: Interrupt with status %04x.\n", dev->name, status);
+
+ if( status & 0x00ff ) { /* Let's check the transmit status reg */
+
+ if(status & TX_DONE) { /* The transmit has been done */
+ lp->stats.tx_packets++;
+
+ if(lp->tx_queue) { /* Is there still packets ? */
+ /* There was packet(s) so start transmitting and write also
+ how many packets there is to be sended */
+ outb(TX_START | lp->tx_queue, ioaddr + TRANSMIT_START_REG);
+ lp->tx_queue = 0;
+ lp->tx_queue_len = 0;
+ dev->trans_start = jiffies;
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+ }
+ else {
+ lp->tx_started = 0;
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+ }
+ }
+ }
+
+ if( ( status & 0xff00 ) ||
+ ( (inb(ioaddr + RECEIVE_MODE_REG) & RX_BUFFER_EMPTY) == 0) ) {
+ eth16i_rx(dev); /* We have packet in receive buffer */
+ }
+
+ dev->interrupt = 0;
+
+ /* Turn interrupts back on */
+ outw(ETH16I_INTR_ON, ioaddr + TX_INTR_REG);
+
+ return;
+}
+
+static void eth16i_multicast(struct device *dev, int num_addrs, void *addrs)
+{
+ short ioaddr = dev->base_addr;
+
+ if(dev->mc_count || dev->flags&(IFF_ALLMULTI|IFF_PROMISC))
+ {
+ dev->flags|=IFF_PROMISC; /* Must do this */
+ outb(3, ioaddr + RECEIVE_MODE_REG);
+ } else {
+ outb(2, ioaddr + RECEIVE_MODE_REG);
+ }
+}
+
+static struct enet_statistics *eth16i_get_stats(struct device *dev)
+{
+ struct eth16i_local *lp = (struct eth16i_local *)dev->priv;
+
+ return &lp->stats;
+}
+
+static void eth16i_select_regbank(unsigned char banknbr, short ioaddr)
+{
+ unsigned char data;
+
+ data = inb(ioaddr + CONFIG_REG_1);
+ outb( ((data & 0xF3) | ( (banknbr & 0x03) << 2)), ioaddr + CONFIG_REG_1);
+}
+
+#ifdef MODULE
+static char devicename[9] = { 0, };
+static struct device dev_eth16i = {
+ devicename,
+ 0, 0, 0, 0,
+ 0, 0,
+ 0, 0, 0, NULL, eth16i_probe };
+
+int io = 0x2a0;
+int irq = 0;
+
+int init_module(void)
+{
+ if(io == 0)
+ printk("eth16i: You should not use auto-probing with insmod!\n");
+
+ dev_eth16i.base_addr = io;
+ dev_eth16i.irq = irq;
+ if( register_netdev( &dev_eth16i ) != 0 ) {
+ printk("eth16i: register_netdev() returned non-zero.\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ unregister_netdev( &dev_eth16i );
+ free_irq( dev_eth16i.irq );
+ irq2dev_map[ dev_eth16i.irq ] = NULL;
+ release_region( dev_eth16i.base_addr, ETH16I_IO_EXTENT );
+}
+
+#endif /* MODULE */
+
+
diff --git a/i386/i386at/gpl/linux/net/ewrk3.c b/i386/i386at/gpl/linux/net/ewrk3.c
new file mode 100644
index 00000000..90e3b932
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/ewrk3.c
@@ -0,0 +1,1933 @@
+/* ewrk3.c: A DIGITAL EtherWORKS 3 ethernet driver for Linux.
+
+ Written 1994 by David C. Davies.
+
+ Copyright 1994 Digital Equipment Corporation.
+
+ This software may be used and distributed according to the terms of
+ the GNU Public License, incorporated herein by reference.
+
+ This driver is written for the Digital Equipment Corporation series
+ of EtherWORKS ethernet cards:
+
+ DE203 Turbo (BNC)
+ DE204 Turbo (TP)
+ DE205 Turbo (TP BNC)
+
+ The driver has been tested on a relatively busy network using the DE205
+ card and benchmarked with 'ttcp': it transferred 16M of data at 975kB/s
+ (7.8Mb/s) to a DECstation 5000/200.
+
+ The author may be reached at davies@wanton.lkg.dec.com or
+ davies@maniac.ultranet.com or Digital Equipment Corporation, 550 King
+ Street, Littleton MA 01460.
+
+ =========================================================================
+ This driver has been written substantially from scratch, although its
+ inheritance of style and stack interface from 'depca.c' and in turn from
+ Donald Becker's 'lance.c' should be obvious.
+
+ The DE203/4/5 boards all use a new proprietary chip in place of the
+ LANCE chip used in prior cards (DEPCA, DE100, DE200/1/2, DE210, DE422).
+ Use the depca.c driver in the standard distribution for the LANCE based
+ cards from DIGITAL; this driver will not work with them.
+
+ The DE203/4/5 cards have 2 main modes: shared memory and I/O only. I/O
+ only makes all the card accesses through I/O transactions and no high
+ (shared) memory is used. This mode provides a >48% performance penalty
+ and is deprecated in this driver, although allowed to provide initial
+ setup when hardstrapped.
+
+ The shared memory mode comes in 3 flavours: 2kB, 32kB and 64kB. There is
+ no point in using any mode other than the 2kB mode - their performances
+ are virtually identical, although the driver has been tested in the 2kB
+ and 32kB modes. I would suggest you uncomment the line:
+
+ FORCE_2K_MODE;
+
+ to allow the driver to configure the card as a 2kB card at your current
+ base address, thus leaving more room to clutter your system box with
+ other memory hungry boards.
+
+ As many ISA and EISA cards can be supported under this driver as you
+ wish, limited primarily by the available IRQ lines, rather than by the
+ available I/O addresses (24 ISA, 16 EISA). I have checked different
+ configurations of multiple depca cards and ewrk3 cards and have not
+ found a problem yet (provided you have at least depca.c v0.38) ...
+
+ The board IRQ setting must be at an unused IRQ which is auto-probed
+ using Donald Becker's autoprobe routines. All these cards are at
+ {5,10,11,15}.
+
+ No 16MB memory limitation should exist with this driver as DMA is not
+ used and the common memory area is in low memory on the network card (my
+ current system has 20MB and I've not had problems yet).
+
+ The ability to load this driver as a loadable module has been included
+ and used extensively during the driver development (to save those long
+ reboot sequences). To utilise this ability, you have to do 8 things:
+
+ 0) have a copy of the loadable modules code installed on your system.
+ 1) copy ewrk3.c from the /linux/drivers/net directory to your favourite
+ temporary directory.
+ 2) edit the source code near line 1880 to reflect the I/O address and
+ IRQ you're using.
+ 3) compile ewrk3.c, but include -DMODULE in the command line to ensure
+ that the correct bits are compiled (see end of source code).
+ 4) if you are wanting to add a new card, goto 5. Otherwise, recompile a
+ kernel with the ewrk3 configuration turned off and reboot.
+ 5) insmod ewrk3.o
+ [Alan Cox: Changed this so you can insmod ewrk3.o irq=x io=y]
+ 6) run the net startup bits for your new eth?? interface manually
+ (usually /etc/rc.inet[12] at boot time).
+ 7) enjoy!
+
+ Note that autoprobing is not allowed in loadable modules - the system is
+ already up and running and you're messing with interrupts.
+
+ To unload a module, turn off the associated interface
+ 'ifconfig eth?? down' then 'rmmod ewrk3'.
+
+ Promiscuous mode has been turned off in this driver, but all the
+ multicast address bits have been turned on. This improved the send
+ performance on a busy network by about 13%.
+
+ Ioctl's have now been provided (primarily because I wanted to grab some
+ packet size statistics). They are patterned after 'plipconfig.c' from a
+ suggestion by Alan Cox. Using these ioctls, you can enable promiscuous
+ mode, add/delete multicast addresses, change the hardware address, get
+ packet size distribution statistics and muck around with the control and
+ status register. I'll add others if and when the need arises.
+
+ TO DO:
+ ------
+
+
+ Revision History
+ ----------------
+
+ Version Date Description
+
+ 0.1 26-aug-94 Initial writing. ALPHA code release.
+ 0.11 31-aug-94 Fixed: 2k mode memory base calc.,
+ LeMAC version calc.,
+ IRQ vector assignments during autoprobe.
+ 0.12 31-aug-94 Tested working on LeMAC2 (DE20[345]-AC) card.
+ Fixed up MCA hash table algorithm.
+ 0.20 4-sep-94 Added IOCTL functionality.
+ 0.21 14-sep-94 Added I/O mode.
+ 0.21axp 15-sep-94 Special version for ALPHA AXP Linux V1.0.
+ 0.22 16-sep-94 Added more IOCTLs & tidied up.
+ 0.23 21-sep-94 Added transmit cut through.
+ 0.24 31-oct-94 Added uid checks in some ioctls.
+ 0.30 1-nov-94 BETA code release.
+ 0.31 5-dec-94 Added check/allocate region code.
+ 0.32 16-jan-95 Broadcast packet fix.
+ 0.33 10-Feb-95 Fix recognition bug reported by <bkm@star.rl.ac.uk>.
+ 0.40 27-Dec-95 Rationalise MODULE and autoprobe code.
+ Rewrite for portability & updated.
+ ALPHA support from <jestabro@amt.tay1.dec.com>
+ Added verify_area() calls in depca_ioctl() from
+ suggestion by <heiko@colossus.escape.de>.
+ Add new multicasting code.
+
+ =========================================================================
+*/
+
+static const char *version = "ewrk3.c:v0.40 95/12/27 davies@wanton.lkg.dec.com\n";
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/segment.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include <linux/time.h>
+#include <linux/types.h>
+#include <linux/unistd.h>
+
+#include "ewrk3.h"
+
+#ifdef EWRK3_DEBUG
+static int ewrk3_debug = EWRK3_DEBUG;
+#else
+static int ewrk3_debug = 1;
+#endif
+
+#define EWRK3_NDA 0xffe0 /* No Device Address */
+
+#define PROBE_LENGTH 32
+#define ETH_PROM_SIG 0xAA5500FFUL
+
+#ifndef EWRK3_SIGNATURE
+#define EWRK3_SIGNATURE {"DE203","DE204","DE205",""}
+#define EWRK3_STRLEN 8
+#endif
+
+#ifndef EWRK3_RAM_BASE_ADDRESSES
+#define EWRK3_RAM_BASE_ADDRESSES {0xc0000,0xd0000,0x00000}
+#endif
+
+/*
+** Sets up the I/O area for the autoprobe.
+*/
+#define EWRK3_IO_BASE 0x100 /* Start address for probe search */
+#define EWRK3_IOP_INC 0x20 /* I/O address increment */
+#define EWRK3_TOTAL_SIZE 0x20 /* required I/O address length */
+
+#ifndef MAX_NUM_EWRK3S
+#define MAX_NUM_EWRK3S 21
+#endif
+
+#ifndef EWRK3_EISA_IO_PORTS
+#define EWRK3_EISA_IO_PORTS 0x0c00 /* I/O port base address, slot 0 */
+#endif
+
+#ifndef MAX_EISA_SLOTS
+#define MAX_EISA_SLOTS 16
+#define EISA_SLOT_INC 0x1000
+#endif
+
+#define CRC_POLYNOMIAL_BE 0x04c11db7UL /* Ethernet CRC, big endian */
+#define CRC_POLYNOMIAL_LE 0xedb88320UL /* Ethernet CRC, little endian */
+
+#define QUEUE_PKT_TIMEOUT (100) /* Jiffies */
+
+/*
+** EtherWORKS 3 shared memory window sizes
+*/
+#define IO_ONLY 0x00
+#define SHMEM_2K 0x800
+#define SHMEM_32K 0x8000
+#define SHMEM_64K 0x10000
+
+/*
+** EtherWORKS 3 IRQ ENABLE/DISABLE
+*/
+#define ENABLE_IRQs { \
+ icr |= lp->irq_mask;\
+ outb(icr, EWRK3_ICR); /* Enable the IRQs */\
+}
+
+#define DISABLE_IRQs { \
+ icr = inb(EWRK3_ICR);\
+ icr &= ~lp->irq_mask;\
+ outb(icr, EWRK3_ICR); /* Disable the IRQs */\
+}
+
+/*
+** EtherWORKS 3 START/STOP
+*/
+#define START_EWRK3 { \
+ csr = inb(EWRK3_CSR);\
+ csr &= ~(CSR_TXD|CSR_RXD);\
+ outb(csr, EWRK3_CSR); /* Enable the TX and/or RX */\
+}
+
+#define STOP_EWRK3 { \
+ csr = (CSR_TXD|CSR_RXD);\
+ outb(csr, EWRK3_CSR); /* Disable the TX and/or RX */\
+}
+
+/*
+** The EtherWORKS 3 private structure
+*/
+#define EWRK3_PKT_STAT_SZ 16
+#define EWRK3_PKT_BIN_SZ 128 /* Should be >=100 unless you
+ increase EWRK3_PKT_STAT_SZ */
+
+struct ewrk3_private {
+ char adapter_name[80]; /* Name exported to /proc/ioports */
+ u_long shmem_base; /* Shared memory start address */
+ u_long shmem_length; /* Shared memory window length */
+ struct enet_statistics stats; /* Public stats */
+ struct {
+ u32 bins[EWRK3_PKT_STAT_SZ]; /* Private stats counters */
+ u32 unicast;
+ u32 multicast;
+ u32 broadcast;
+ u32 excessive_collisions;
+ u32 tx_underruns;
+ u32 excessive_underruns;
+ } pktStats;
+ u_char irq_mask; /* Adapter IRQ mask bits */
+ u_char mPage; /* Maximum 2kB Page number */
+ u_char lemac; /* Chip rev. level */
+ u_char hard_strapped; /* Don't allow a full open */
+ u_char lock; /* Lock the page register */
+ u_char txc; /* Transmit cut through */
+ u_char *mctbl; /* Pointer to the multicast table */
+};
+
+/*
+** Force the EtherWORKS 3 card to be in 2kB MODE
+*/
+#define FORCE_2K_MODE { \
+ shmem_length = SHMEM_2K;\
+ outb(((mem_start - 0x80000) >> 11), EWRK3_MBR);\
+}
+
+/*
+** Public Functions
+*/
+static int ewrk3_open(struct device *dev);
+static int ewrk3_queue_pkt(struct sk_buff *skb, struct device *dev);
+static void ewrk3_interrupt(int irq, struct pt_regs *regs);
+static int ewrk3_close(struct device *dev);
+static struct enet_statistics *ewrk3_get_stats(struct device *dev);
+static void set_multicast_list(struct device *dev);
+static int ewrk3_ioctl(struct device *dev, struct ifreq *rq, int cmd);
+
+/*
+** Private functions
+*/
+static int ewrk3_hw_init(struct device *dev, u_long iobase);
+static void ewrk3_init(struct device *dev);
+static int ewrk3_rx(struct device *dev);
+static int ewrk3_tx(struct device *dev);
+
+static void EthwrkSignature(char * name, char *eeprom_image);
+static int DevicePresent(u_long iobase);
+static void SetMulticastFilter(struct device *dev);
+static int EISA_signature(char *name, s32 eisa_id);
+
+static int Read_EEPROM(u_long iobase, u_char eaddr);
+static int Write_EEPROM(short data, u_long iobase, u_char eaddr);
+static u_char get_hw_addr (struct device *dev, u_char *eeprom_image, char chipType);
+
+static void isa_probe(struct device *dev, u_long iobase);
+static void eisa_probe(struct device *dev, u_long iobase);
+static struct device *alloc_device(struct device *dev, u_long iobase);
+
+
+#ifdef MODULE
+int init_module(void);
+void cleanup_module(void);
+static int autoprobed = 1, loading_module = 1;
+
+# else
+static u_char irq[] = {5,0,10,3,11,9,15,12};
+static int autoprobed = 0, loading_module = 0;
+
+#endif /* MODULE */
+
+static char name[EWRK3_STRLEN + 1];
+static int num_ewrk3s = 0, num_eth = 0;
+
+/*
+** Miscellaneous defines...
+*/
+#define INIT_EWRK3 {\
+ outb(EEPROM_INIT, EWRK3_IOPR);\
+ udelay(1000);\
+}
+
+
+
+
+int ewrk3_probe(struct device *dev)
+{
+ int tmp = num_ewrk3s, status = -ENODEV;
+ u_long iobase = dev->base_addr;
+
+ if ((iobase == 0) && loading_module){
+ printk("Autoprobing is not supported when loading a module based driver.\n");
+ status = -EIO;
+ } else { /* First probe for the Ethernet */
+ /* Address PROM pattern */
+ isa_probe(dev, iobase);
+ eisa_probe(dev, iobase);
+
+ if ((tmp == num_ewrk3s) && (iobase != 0) && loading_module) {
+ printk("%s: ewrk3_probe() cannot find device at 0x%04lx.\n", dev->name,
+ iobase);
+ }
+
+ /*
+ ** Walk the device list to check that at least one device
+ ** initialised OK
+ */
+ for (; (dev->priv == NULL) && (dev->next != NULL); dev = dev->next);
+
+ if (dev->priv) status = 0;
+ if (iobase == 0) autoprobed = 1;
+ }
+
+ return status;
+}
+
+static int
+ewrk3_hw_init(struct device *dev, u_long iobase)
+{
+ struct ewrk3_private *lp;
+ int i, status=0;
+ u_long mem_start, shmem_length;
+ u_char cr, cmr, icr, nicsr, lemac, hard_strapped = 0;
+ u_char eeprom_image[EEPROM_MAX], chksum, eisa_cr = 0;
+
+ /*
+ ** Stop the EWRK3. Enable the DBR ROM. Disable interrupts and remote boot.
+ ** This also disables the EISA_ENABLE bit in the EISA Control Register.
+ */
+ if (iobase > 0x400) eisa_cr = inb(EISA_CR);
+ INIT_EWRK3;
+
+ nicsr = inb(EWRK3_CSR);
+
+ icr = inb(EWRK3_ICR);
+ icr |= 0xf0;
+ outb(icr, EWRK3_ICR); /* Disable all the IRQs */
+
+ if (nicsr == CSR_TXD|CSR_RXD) {
+
+ /* Check that the EEPROM is alive and well and not living on Pluto... */
+ for (chksum=0, i=0; i<EEPROM_MAX; i+=2) {
+ union {
+ short val;
+ char c[2];
+ } tmp;
+
+ tmp.val = (short)Read_EEPROM(iobase, (i>>1));
+ eeprom_image[i] = tmp.c[0];
+ eeprom_image[i+1] = tmp.c[1];
+ chksum += eeprom_image[i] + eeprom_image[i+1];
+ }
+
+ if (chksum != 0) { /* Bad EEPROM Data! */
+ printk("%s: Device has a bad on-board EEPROM.\n", dev->name);
+ status = -ENXIO;
+ } else {
+ EthwrkSignature(name, eeprom_image);
+ if (*name != '\0') { /* found a EWRK3 device */
+ dev->base_addr = iobase;
+
+ if (iobase > 0x400) {
+ outb(eisa_cr, EISA_CR); /* Rewrite the EISA CR */
+ }
+
+ lemac = eeprom_image[EEPROM_CHIPVER];
+ cmr = inb(EWRK3_CMR);
+
+ if (((lemac == LeMAC) && ((cmr & CMR_NO_EEPROM) != CMR_NO_EEPROM)) ||
+ ((lemac == LeMAC2) && !(cmr & CMR_HS))) {
+ printk("%s: %s at %#4lx", dev->name, name, iobase);
+ hard_strapped = 1;
+ } else if ((iobase&0x0fff)==EWRK3_EISA_IO_PORTS) {
+ /* EISA slot address */
+ printk("%s: %s at %#4lx (EISA slot %ld)",
+ dev->name, name, iobase, ((iobase>>12)&0x0f));
+ } else { /* ISA port address */
+ printk("%s: %s at %#4lx", dev->name, name, iobase);
+ }
+
+ if (!status) {
+ printk(", h/w address ");
+ if (lemac!=LeMAC2) DevicePresent(iobase);/* need after EWRK3_INIT */
+ status = get_hw_addr(dev, eeprom_image, lemac);
+ for (i = 0; i < ETH_ALEN - 1; i++) { /* get the ethernet addr. */
+ printk("%2.2x:", dev->dev_addr[i]);
+ }
+ printk("%2.2x,\n", dev->dev_addr[i]);
+
+ if (status) {
+ printk(" which has an EEPROM CRC error.\n");
+ status = -ENXIO;
+ } else {
+ if (lemac == LeMAC2) { /* Special LeMAC2 CMR things */
+ cmr &= ~(CMR_RA | CMR_WB | CMR_LINK | CMR_POLARITY | CMR_0WS);
+ if (eeprom_image[EEPROM_MISC0] & READ_AHEAD) cmr |= CMR_RA;
+ if (eeprom_image[EEPROM_MISC0] & WRITE_BEHIND) cmr |= CMR_WB;
+ if (eeprom_image[EEPROM_NETMAN0] & NETMAN_POL) cmr |= CMR_POLARITY;
+ if (eeprom_image[EEPROM_NETMAN0] & NETMAN_LINK) cmr |= CMR_LINK;
+ if (eeprom_image[EEPROM_MISC0] & _0WS_ENA) cmr |= CMR_0WS;
+ }
+ if (eeprom_image[EEPROM_SETUP] & SETUP_DRAM) cmr |= CMR_DRAM;
+ outb(cmr, EWRK3_CMR);
+
+ cr = inb(EWRK3_CR); /* Set up the Control Register */
+ cr |= eeprom_image[EEPROM_SETUP] & SETUP_APD;
+ if (cr & SETUP_APD) cr |= eeprom_image[EEPROM_SETUP] & SETUP_PS;
+ cr |= eeprom_image[EEPROM_MISC0] & FAST_BUS;
+ cr |= eeprom_image[EEPROM_MISC0] & ENA_16;
+ outb(cr, EWRK3_CR);
+
+ /*
+ ** Determine the base address and window length for the EWRK3
+ ** RAM from the memory base register.
+ */
+ mem_start = inb(EWRK3_MBR);
+ shmem_length = 0;
+ if (mem_start != 0) {
+ if ((mem_start >= 0x0a) && (mem_start <= 0x0f)) {
+ mem_start *= SHMEM_64K;
+ shmem_length = SHMEM_64K;
+ } else if ((mem_start >= 0x14) && (mem_start <= 0x1f)) {
+ mem_start *= SHMEM_32K;
+ shmem_length = SHMEM_32K;
+ } else if ((mem_start >= 0x40) && (mem_start <= 0xff)) {
+ mem_start = mem_start * SHMEM_2K + 0x80000;
+ shmem_length = SHMEM_2K;
+ } else {
+ status = -ENXIO;
+ }
+ }
+
+ /*
+ ** See the top of this source code for comments about
+ ** uncommenting this line.
+ */
+/* FORCE_2K_MODE;*/
+
+ if (!status) {
+ if (hard_strapped) {
+ printk(" is hard strapped.\n");
+ } else if (mem_start) {
+ printk(" has a %dk RAM window", (int)(shmem_length >> 10));
+ printk(" at 0x%.5lx", mem_start);
+ } else {
+ printk(" is in I/O only mode");
+ }
+
+ /* private area & initialise */
+ dev->priv = (void *) kmalloc(sizeof(struct ewrk3_private),
+ GFP_KERNEL);
+ if (dev->priv == NULL) {
+ return -ENOMEM;
+ }
+ lp = (struct ewrk3_private *)dev->priv;
+ memset(dev->priv, 0, sizeof(struct ewrk3_private));
+ lp->shmem_base = mem_start;
+ lp->shmem_length = shmem_length;
+ lp->lemac = lemac;
+ lp->hard_strapped = hard_strapped;
+
+ lp->mPage = 64;
+ if (cmr & CMR_DRAM) lp->mPage <<= 1 ;/* 2 DRAMS on module */
+
+ sprintf(lp->adapter_name,"%s (%s)", name, dev->name);
+ request_region(iobase, EWRK3_TOTAL_SIZE, lp->adapter_name);
+
+ lp->irq_mask = ICR_TNEM|ICR_TXDM|ICR_RNEM|ICR_RXDM;
+
+ if (!hard_strapped) {
+ /*
+ ** Enable EWRK3 board interrupts for autoprobing
+ */
+ icr |= ICR_IE; /* Enable interrupts */
+ outb(icr, EWRK3_ICR);
+
+ /* The DMA channel may be passed in on this parameter. */
+ dev->dma = 0;
+
+ /* To auto-IRQ we enable the initialization-done and DMA err,
+ interrupts. For now we will always get a DMA error. */
+ if (dev->irq < 2) {
+#ifndef MODULE
+ u_char irqnum;
+
+ autoirq_setup(0);
+
+ /*
+ ** Trigger a TNE interrupt.
+ */
+ icr |=ICR_TNEM;
+ outb(1,EWRK3_TDQ); /* Write to the TX done queue */
+ outb(icr, EWRK3_ICR); /* Unmask the TXD interrupt */
+
+ irqnum = irq[((icr & IRQ_SEL) >> 4)];
+
+ dev->irq = autoirq_report(1);
+ if ((dev->irq) && (irqnum == dev->irq)) {
+ printk(" and uses IRQ%d.\n", dev->irq);
+ } else {
+ if (!dev->irq) {
+ printk(" and failed to detect IRQ line.\n");
+ } else if ((irqnum == 1) && (lemac == LeMAC2)) {
+ printk(" and an illegal IRQ line detected.\n");
+ } else {
+ printk(", but incorrect IRQ line detected.\n");
+ }
+ status = -ENXIO;
+ }
+
+ DISABLE_IRQs; /* Mask all interrupts */
+
+#endif /* MODULE */
+ } else {
+ printk(" and requires IRQ%d.\n", dev->irq);
+ }
+ }
+ if (status) release_region(iobase, EWRK3_TOTAL_SIZE);
+ } else {
+ status = -ENXIO;
+ }
+ }
+ }
+ } else {
+ status = -ENXIO;
+ }
+ }
+
+ if (!status) {
+ if (ewrk3_debug > 0) {
+ printk(version);
+ }
+
+ /* The EWRK3-specific entries in the device structure. */
+ dev->open = &ewrk3_open;
+ dev->hard_start_xmit = &ewrk3_queue_pkt;
+ dev->stop = &ewrk3_close;
+ dev->get_stats = &ewrk3_get_stats;
+ dev->set_multicast_list = &set_multicast_list;
+ dev->do_ioctl = &ewrk3_ioctl;
+
+ dev->mem_start = 0;
+
+ /* Fill in the generic field of the device structure. */
+ ether_setup(dev);
+ }
+ } else {
+ status = -ENXIO;
+ }
+
+ return status;
+}
+
+
+static int
+ewrk3_open(struct device *dev)
+{
+ struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+ int i, status = 0;
+ u_char icr, csr;
+
+ /*
+ ** Stop the TX and RX...
+ */
+ STOP_EWRK3;
+
+ if (!lp->hard_strapped) {
+ irq2dev_map[dev->irq] = dev; /* For latched interrupts */
+
+ if (request_irq(dev->irq, (void *)ewrk3_interrupt, 0, "ewrk3")) {
+ printk("ewrk3_open(): Requested IRQ%d is busy\n",dev->irq);
+ status = -EAGAIN;
+ } else {
+
+ /*
+ ** Re-initialize the EWRK3...
+ */
+ ewrk3_init(dev);
+
+ if (ewrk3_debug > 1){
+ printk("%s: ewrk3 open with irq %d\n",dev->name,dev->irq);
+ printk(" physical address: ");
+ for (i=0;i<5;i++){
+ printk("%2.2x:",(u_char)dev->dev_addr[i]);
+ }
+ printk("%2.2x\n",(u_char)dev->dev_addr[i]);
+ if (lp->shmem_length == 0) {
+ printk(" no shared memory, I/O only mode\n");
+ } else {
+ printk(" start of shared memory: 0x%08lx\n",lp->shmem_base);
+ printk(" window length: 0x%04lx\n",lp->shmem_length);
+ }
+ printk(" # of DRAMS: %d\n",((inb(EWRK3_CMR) & 0x02) ? 2 : 1));
+ printk(" csr: 0x%02x\n", inb(EWRK3_CSR));
+ printk(" cr: 0x%02x\n", inb(EWRK3_CR));
+ printk(" icr: 0x%02x\n", inb(EWRK3_ICR));
+ printk(" cmr: 0x%02x\n", inb(EWRK3_CMR));
+ printk(" fmqc: 0x%02x\n", inb(EWRK3_FMQC));
+ }
+
+ dev->tbusy = 0;
+ dev->start = 1;
+ dev->interrupt = UNMASK_INTERRUPTS;
+
+ /*
+ ** Unmask EWRK3 board interrupts
+ */
+ icr = inb(EWRK3_ICR);
+ ENABLE_IRQs;
+
+ }
+ } else {
+ dev->start = 0;
+ dev->tbusy = 1;
+ printk("%s: ewrk3 available for hard strapped set up only.\n", dev->name);
+ printk(" Run the 'ewrk3setup' utility or remove the hard straps.\n");
+ }
+
+ MOD_INC_USE_COUNT;
+
+ return status;
+}
+
+/*
+** Initialize the EtherWORKS 3 operating conditions
+*/
+static void
+ewrk3_init(struct device *dev)
+{
+ struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv;
+ u_char csr, page;
+ u_long iobase = dev->base_addr;
+
+ /*
+ ** Enable any multicasts
+ */
+ set_multicast_list(dev);
+
+ /*
+ ** Clean out any remaining entries in all the queues here
+ */
+ while (inb(EWRK3_TQ));
+ while (inb(EWRK3_TDQ));
+ while (inb(EWRK3_RQ));
+ while (inb(EWRK3_FMQ));
+
+ /*
+ ** Write a clean free memory queue
+ */
+ for (page=1;page<lp->mPage;page++) { /* Write the free page numbers */
+ outb(page, EWRK3_FMQ); /* to the Free Memory Queue */
+ }
+
+ lp->lock = 0; /* Ensure there are no locks */
+
+ START_EWRK3; /* Enable the TX and/or RX */
+}
+
+/*
+** Writes a socket buffer to the free page queue
+*/
+static int
+ewrk3_queue_pkt(struct sk_buff *skb, struct device *dev)
+{
+ struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+ int status = 0;
+ u_char icr, csr;
+
+ /* Transmitter timeout, serious problems. */
+ if (dev->tbusy || lp->lock) {
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < QUEUE_PKT_TIMEOUT) {
+ status = -1;
+ } else if (!lp->hard_strapped) {
+ printk("%s: transmit timed/locked out, status %04x, resetting.\n",
+ dev->name, inb(EWRK3_CSR));
+
+ /*
+ ** Mask all board interrupts
+ */
+ DISABLE_IRQs;
+
+ /*
+ ** Stop the TX and RX...
+ */
+ STOP_EWRK3;
+
+ ewrk3_init(dev);
+
+ /*
+ ** Unmask EWRK3 board interrupts
+ */
+ ENABLE_IRQs;
+
+ dev->tbusy=0;
+ dev->trans_start = jiffies;
+ }
+ } else if (skb == NULL) {
+ dev_tint(dev);
+ } else if (skb->len > 0) {
+
+ /*
+ ** Block a timer-based transmit from overlapping. This could better be
+ ** done with atomic_swap(1, dev->tbusy), but set_bit() works as well.
+ */
+ if (set_bit(0, (void*)&dev->tbusy) != 0)
+ printk("%s: Transmitter access conflict.\n", dev->name);
+
+ DISABLE_IRQs; /* So that the page # remains correct */
+
+ /*
+ ** Get a free page from the FMQ when resources are available
+ */
+ if (inb(EWRK3_FMQC) > 0) {
+ u_long buf = 0;
+ u_char page;
+
+ if ((page = inb(EWRK3_FMQ)) < lp->mPage) {
+ /*
+ ** Set up shared memory window and pointer into the window
+ */
+ while (set_bit(0, (void *)&lp->lock) != 0); /* Wait for lock to free */
+ if (lp->shmem_length == IO_ONLY) {
+ outb(page, EWRK3_IOPR);
+ } else if (lp->shmem_length == SHMEM_2K) {
+ buf = lp->shmem_base;
+ outb(page, EWRK3_MPR);
+ } else if (lp->shmem_length == SHMEM_32K) {
+ buf = ((((short)page << 11) & 0x7800) + lp->shmem_base);
+ outb((page >> 4), EWRK3_MPR);
+ } else if (lp->shmem_length == SHMEM_64K) {
+ buf = ((((short)page << 11) & 0xf800) + lp->shmem_base);
+ outb((page >> 5), EWRK3_MPR);
+ } else {
+ status = -1;
+ printk("%s: Oops - your private data area is hosed!\n",dev->name);
+ }
+
+ if (!status) {
+
+ /*
+ ** Set up the buffer control structures and copy the data from
+ ** the socket buffer to the shared memory .
+ */
+
+ if (lp->shmem_length == IO_ONLY) {
+ int i;
+ u_char *p = skb->data;
+
+ outb((char)(TCR_QMODE | TCR_PAD | TCR_IFC), EWRK3_DATA);
+ outb((char)(skb->len & 0xff), EWRK3_DATA);
+ outb((char)((skb->len >> 8) & 0xff), EWRK3_DATA);
+ outb((char)0x04, EWRK3_DATA);
+ for (i=0; i<skb->len; i++) {
+ outb(*p++, EWRK3_DATA);
+ }
+ outb(page, EWRK3_TQ); /* Start sending pkt */
+ } else {
+ writeb((char)(TCR_QMODE|TCR_PAD|TCR_IFC), (char *)buf);/* ctrl byte*/
+ buf+=1;
+ writeb((char)(skb->len & 0xff), (char *)buf);/* length (16 bit xfer)*/
+ buf+=1;
+ if (lp->txc) {
+ writeb((char)(((skb->len >> 8) & 0xff) | XCT), (char *)buf);
+ buf+=1;
+ writeb(0x04, (char *)buf); /* index byte */
+ buf+=1;
+ writeb(0x00, (char *)(buf + skb->len)); /* Write the XCT flag */
+ memcpy_toio(buf, skb->data, PRELOAD);/* Write PRELOAD bytes*/
+ outb(page, EWRK3_TQ); /* Start sending pkt */
+ memcpy_toio(buf+PRELOAD, skb->data+PRELOAD, skb->len-PRELOAD);
+ writeb(0xff, (char *)(buf + skb->len)); /* Write the XCT flag */
+ } else {
+ writeb((char)((skb->len >> 8) & 0xff), (char *)buf);
+ buf+=1;
+ writeb(0x04, (char *)buf); /* index byte */
+ buf+=1;
+ memcpy_toio((char *)buf, skb->data, skb->len);/* Write data bytes */
+ outb(page, EWRK3_TQ); /* Start sending pkt */
+ }
+ }
+
+ dev->trans_start = jiffies;
+ dev_kfree_skb (skb, FREE_WRITE);
+
+ } else { /* return unused page to the free memory queue */
+ outb(page, EWRK3_FMQ);
+ }
+ lp->lock = 0; /* unlock the page register */
+ } else {
+ printk("ewrk3_queue_pkt(): Invalid free memory page (%d).\n",
+ (u_char) page);
+ }
+ } else {
+ printk("ewrk3_queue_pkt(): No free resources...\n");
+ printk("ewrk3_queue_pkt(): CSR: %02x ICR: %02x FMQC: %02x\n",inb(EWRK3_CSR),inb(EWRK3_ICR),inb(EWRK3_FMQC));
+ }
+
+ /* Check for free resources: clear 'tbusy' if there are some */
+ if (inb(EWRK3_FMQC) > 0) {
+ dev->tbusy = 0;
+ }
+
+ ENABLE_IRQs;
+ }
+
+ return status;
+}
+
+/*
+** The EWRK3 interrupt handler.
+*/
+static void
+ewrk3_interrupt(int irq, struct pt_regs * regs)
+{
+ struct device *dev = (struct device *)(irq2dev_map[irq]);
+ struct ewrk3_private *lp;
+ u_long iobase;
+ u_char icr, cr, csr;
+
+ if (dev == NULL) {
+ printk ("ewrk3_interrupt(): irq %d for unknown device.\n", irq);
+ } else {
+ lp = (struct ewrk3_private *)dev->priv;
+ iobase = dev->base_addr;
+
+ if (dev->interrupt)
+ printk("%s: Re-entering the interrupt handler.\n", dev->name);
+
+ dev->interrupt = MASK_INTERRUPTS;
+
+ /* get the interrupt information */
+ csr = inb(EWRK3_CSR);
+
+ /*
+ ** Mask the EWRK3 board interrupts and turn on the LED
+ */
+ DISABLE_IRQs;
+
+ cr = inb(EWRK3_CR);
+ cr |= CR_LED;
+ outb(cr, EWRK3_CR);
+
+ if (csr & CSR_RNE) /* Rx interrupt (packet[s] arrived) */
+ ewrk3_rx(dev);
+
+ if (csr & CSR_TNE) /* Tx interrupt (packet sent) */
+ ewrk3_tx(dev);
+
+ /*
+ ** Now deal with the TX/RX disable flags. These are set when there
+ ** are no more resources. If resources free up then enable these
+ ** interrupts, otherwise mask them - failure to do this will result
+ ** in the system hanging in an interrupt loop.
+ */
+ if (inb(EWRK3_FMQC)) { /* any resources available? */
+ lp->irq_mask |= ICR_TXDM|ICR_RXDM;/* enable the interrupt source */
+ csr &= ~(CSR_TXD|CSR_RXD);/* ensure restart of a stalled TX or RX */
+ outb(csr, EWRK3_CSR);
+ dev->tbusy = 0; /* clear TX busy flag */
+ mark_bh(NET_BH);
+ } else {
+ lp->irq_mask &= ~(ICR_TXDM|ICR_RXDM);/* disable the interrupt source */
+ }
+
+ /* Unmask the EWRK3 board interrupts and turn off the LED */
+ cr &= ~CR_LED;
+ outb(cr, EWRK3_CR);
+
+ dev->interrupt = UNMASK_INTERRUPTS;
+ ENABLE_IRQs;
+ }
+
+ return;
+}
+
+static int
+ewrk3_rx(struct device *dev)
+{
+ struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+ int i, status = 0;
+ u_char page, tmpPage = 0, tmpLock = 0;
+ u_long buf = 0;
+
+ while (inb(EWRK3_RQC) && !status) { /* Whilst there's incoming data */
+ if ((page = inb(EWRK3_RQ)) < lp->mPage) {/* Get next entry's buffer page */
+ /*
+ ** Preempt any process using the current page register. Check for
+ ** an existing lock to reduce time taken in I/O transactions.
+ */
+ if ((tmpLock = set_bit(0, (void *)&lp->lock)) == 1) { /* Assert lock */
+ if (lp->shmem_length == IO_ONLY) { /* Get existing page */
+ tmpPage = inb(EWRK3_IOPR);
+ } else {
+ tmpPage = inb(EWRK3_MPR);
+ }
+ }
+
+ /*
+ ** Set up shared memory window and pointer into the window
+ */
+ if (lp->shmem_length == IO_ONLY) {
+ outb(page, EWRK3_IOPR);
+ } else if (lp->shmem_length == SHMEM_2K) {
+ buf = lp->shmem_base;
+ outb(page, EWRK3_MPR);
+ } else if (lp->shmem_length == SHMEM_32K) {
+ buf = ((((short)page << 11) & 0x7800) + lp->shmem_base);
+ outb((page >> 4), EWRK3_MPR);
+ } else if (lp->shmem_length == SHMEM_64K) {
+ buf = ((((short)page << 11) & 0xf800) + lp->shmem_base);
+ outb((page >> 5), EWRK3_MPR);
+ } else {
+ status = -1;
+ printk("%s: Oops - your private data area is hosed!\n",dev->name);
+ }
+
+ if (!status) {
+ char rx_status;
+ int pkt_len;
+
+ if (lp->shmem_length == IO_ONLY) {
+ rx_status = inb(EWRK3_DATA);
+ pkt_len = inb(EWRK3_DATA);
+ pkt_len |= ((u_short)inb(EWRK3_DATA) << 8);
+ } else {
+ rx_status = readb(buf);
+ buf+=1;
+ pkt_len = readw(buf);
+ buf+=3;
+ }
+
+ if (!(rx_status & R_ROK)) { /* There was an error. */
+ lp->stats.rx_errors++; /* Update the error stats. */
+ if (rx_status & R_DBE) lp->stats.rx_frame_errors++;
+ if (rx_status & R_CRC) lp->stats.rx_crc_errors++;
+ if (rx_status & R_PLL) lp->stats.rx_fifo_errors++;
+ } else {
+ struct sk_buff *skb;
+
+ if ((skb = dev_alloc_skb(pkt_len+2)) != NULL) {
+ unsigned char *p;
+ skb->dev = dev;
+ skb_reserve(skb,2); /* Align to 16 bytes */
+ p = skb_put(skb,pkt_len);
+
+ if (lp->shmem_length == IO_ONLY) {
+ *p = inb(EWRK3_DATA); /* dummy read */
+ for (i=0; i<pkt_len; i++) {
+ *p++ = inb(EWRK3_DATA);
+ }
+ } else {
+ memcpy_fromio(p, buf, pkt_len);
+ }
+
+ /*
+ ** Notify the upper protocol layers that there is another
+ ** packet to handle
+ */
+ skb->protocol=eth_type_trans(skb,dev);
+ netif_rx(skb);
+
+ /*
+ ** Update stats
+ */
+ lp->stats.rx_packets++;
+ for (i=1; i<EWRK3_PKT_STAT_SZ-1; i++) {
+ if (pkt_len < i*EWRK3_PKT_BIN_SZ) {
+ lp->pktStats.bins[i]++;
+ i = EWRK3_PKT_STAT_SZ;
+ }
+ }
+ p = skb->data; /* Look at the dest addr */
+ if (p[0] & 0x01) { /* Multicast/Broadcast */
+ if ((*(s32 *)&p[0] == -1) && (*(s16 *)&p[4] == -1)) {
+ lp->pktStats.broadcast++;
+ } else {
+ lp->pktStats.multicast++;
+ }
+ } else if ((*(s32 *)&p[0] == *(s32 *)&dev->dev_addr[0]) &&
+ (*(s16 *)&p[4] == *(s16 *)&dev->dev_addr[4])) {
+ lp->pktStats.unicast++;
+ }
+
+ lp->pktStats.bins[0]++; /* Duplicates stats.rx_packets */
+ if (lp->pktStats.bins[0] == 0) { /* Reset counters */
+ memset(&lp->pktStats, 0, sizeof(lp->pktStats));
+ }
+ } else {
+ printk("%s: Insufficient memory; nuking packet.\n", dev->name);
+ lp->stats.rx_dropped++; /* Really, deferred. */
+ break;
+ }
+ }
+ }
+ /*
+ ** Return the received buffer to the free memory queue
+ */
+ outb(page, EWRK3_FMQ);
+
+ if (tmpLock) { /* If a lock was preempted */
+ if (lp->shmem_length == IO_ONLY) { /* Replace old page */
+ outb(tmpPage, EWRK3_IOPR);
+ } else {
+ outb(tmpPage, EWRK3_MPR);
+ }
+ }
+ lp->lock = 0; /* Unlock the page register */
+ } else {
+ printk("ewrk3_rx(): Illegal page number, page %d\n",page);
+ printk("ewrk3_rx(): CSR: %02x ICR: %02x FMQC: %02x\n",inb(EWRK3_CSR),inb(EWRK3_ICR),inb(EWRK3_FMQC));
+ }
+ }
+ return status;
+}
+
+/*
+** Buffer sent - check for TX buffer errors.
+*/
+static int
+ewrk3_tx(struct device *dev)
+{
+ struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+ u_char tx_status;
+
+ while ((tx_status = inb(EWRK3_TDQ)) > 0) { /* Whilst there's old buffers */
+ if (tx_status & T_VSTS) { /* The status is valid */
+ if (tx_status & T_TXE) {
+ lp->stats.tx_errors++;
+ if (tx_status & T_NCL) lp->stats.tx_carrier_errors++;
+ if (tx_status & T_LCL) lp->stats.tx_window_errors++;
+ if (tx_status & T_CTU) {
+ if ((tx_status & T_COLL) ^ T_XUR) {
+ lp->pktStats.tx_underruns++;
+ } else {
+ lp->pktStats.excessive_underruns++;
+ }
+ } else if (tx_status & T_COLL) {
+ if ((tx_status & T_COLL) ^ T_XCOLL) {
+ lp->stats.collisions++;
+ } else {
+ lp->pktStats.excessive_collisions++;
+ }
+ }
+ } else {
+ lp->stats.tx_packets++;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int
+ewrk3_close(struct device *dev)
+{
+ struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+ u_char icr, csr;
+
+ dev->start = 0;
+ dev->tbusy = 1;
+
+ if (ewrk3_debug > 1) {
+ printk("%s: Shutting down ethercard, status was %2.2x.\n",
+ dev->name, inb(EWRK3_CSR));
+ }
+
+ /*
+ ** We stop the EWRK3 here... mask interrupts and stop TX & RX
+ */
+ DISABLE_IRQs;
+
+ STOP_EWRK3;
+
+ /*
+ ** Clean out the TX and RX queues here (note that one entry
+ ** may get added to either the TXD or RX queues if the the TX or RX
+ ** just starts processing a packet before the STOP_EWRK3 command
+ ** is received. This will be flushed in the ewrk3_open() call).
+ */
+ while (inb(EWRK3_TQ));
+ while (inb(EWRK3_TDQ));
+ while (inb(EWRK3_RQ));
+
+ if (!lp->hard_strapped) {
+ free_irq(dev->irq);
+
+ irq2dev_map[dev->irq] = 0;
+ }
+
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+}
+
+static struct enet_statistics *
+ewrk3_get_stats(struct device *dev)
+{
+ struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv;
+
+ /* Null body since there is no framing error counter */
+
+ return &lp->stats;
+}
+
+/*
+** Set or clear the multicast filter for this adaptor.
+*/
+static void
+set_multicast_list(struct device *dev)
+{
+ struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+ u_char csr;
+
+ if (irq2dev_map[dev->irq] != NULL) {
+ csr = inb(EWRK3_CSR);
+
+ if (lp->shmem_length == IO_ONLY) {
+ lp->mctbl = (char *) PAGE0_HTE;
+ } else {
+ lp->mctbl = (char *)(lp->shmem_base + PAGE0_HTE);
+ }
+
+ csr &= ~(CSR_PME | CSR_MCE);
+ if (dev->flags & IFF_PROMISC) { /* set promiscuous mode */
+ csr |= CSR_PME;
+ outb(csr, EWRK3_CSR);
+ } else {
+ SetMulticastFilter(dev);
+ csr |= CSR_MCE;
+ outb(csr, EWRK3_CSR);
+ }
+ }
+}
+
+/*
+** Calculate the hash code and update the logical address filter
+** from a list of ethernet multicast addresses.
+** Little endian crc one liner from Matt Thomas, DEC.
+**
+** Note that when clearing the table, the broadcast bit must remain asserted
+** to receive broadcast messages.
+*/
+static void SetMulticastFilter(struct device *dev)
+{
+ struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv;
+ struct dev_mc_list *dmi=dev->mc_list;
+ u_long iobase = dev->base_addr;
+ int i;
+ char *addrs, j, bit, byte;
+ short *p = (short *) lp->mctbl;
+ u16 hashcode;
+ s32 crc, poly = CRC_POLYNOMIAL_LE;
+
+ while (set_bit(0, (void *)&lp->lock) != 0); /* Wait for lock to free */
+
+ if (lp->shmem_length == IO_ONLY) {
+ outb(0, EWRK3_IOPR);
+ outw(EEPROM_OFFSET(lp->mctbl), EWRK3_PIR1);
+ } else {
+ outb(0, EWRK3_MPR);
+ }
+
+ if (dev->flags & IFF_ALLMULTI) {
+ for (i=0; i<(HASH_TABLE_LEN >> 3); i++) {
+ if (lp->shmem_length == IO_ONLY) {
+ outb(0xff, EWRK3_DATA);
+ } else { /* memset didn't work here */
+ writew(0xffff, p);
+ p++; i++;
+ }
+ }
+ } else {
+ /* Clear table except for broadcast bit */
+ if (lp->shmem_length == IO_ONLY) {
+ for (i=0; i<(HASH_TABLE_LEN >> 4) - 1; i++) {
+ outb(0x00, EWRK3_DATA);
+ }
+ outb(0x80, EWRK3_DATA); i++; /* insert the broadcast bit */
+ for (; i<(HASH_TABLE_LEN >> 3); i++) {
+ outb(0x00, EWRK3_DATA);
+ }
+ } else {
+ memset_io(lp->mctbl, 0, (HASH_TABLE_LEN >> 3));
+ writeb(0x80, (char *)(lp->mctbl + (HASH_TABLE_LEN >> 4) - 1));
+ }
+
+ /* Update table */
+ for (i=0;i<dev->mc_count;i++) { /* for each address in the list */
+ addrs=dmi->dmi_addr;
+ dmi=dmi->next;
+ if ((*addrs & 0x01) == 1) { /* multicast address? */
+ crc = 0xffffffff; /* init CRC for each address */
+ for (byte=0;byte<ETH_ALEN;byte++) { /* for each address byte */
+ /* process each address bit */
+ for (bit = *addrs++,j=0;j<8;j++, bit>>=1) {
+ crc = (crc >> 1) ^ (((crc ^ bit) & 0x01) ? poly : 0);
+ }
+ }
+ hashcode = crc & ((1 << 9) - 1); /* hashcode is 9 LSb of CRC */
+
+ byte = hashcode >> 3; /* bit[3-8] -> byte in filter */
+ bit = 1 << (hashcode & 0x07); /* bit[0-2] -> bit in byte */
+
+ if (lp->shmem_length == IO_ONLY) {
+ u_char tmp;
+
+ outw((short)((long)lp->mctbl) + byte, EWRK3_PIR1);
+ tmp = inb(EWRK3_DATA);
+ tmp |= bit;
+ outw((short)((long)lp->mctbl) + byte, EWRK3_PIR1);
+ outb(tmp, EWRK3_DATA);
+ } else {
+ writeb(readb(lp->mctbl + byte) | bit, lp->mctbl + byte);
+ }
+ }
+ }
+ }
+
+ lp->lock = 0; /* Unlock the page register */
+
+ return;
+}
+
+/*
+** ISA bus I/O device probe
+*/
+static void isa_probe(struct device *dev, u_long ioaddr)
+{
+ int i = num_ewrk3s, maxSlots;
+ u_long iobase;
+
+ if (!ioaddr && autoprobed) return ; /* Been here before ! */
+ if (ioaddr >= 0x400) return; /* Not ISA */
+
+ if (ioaddr == 0) { /* Autoprobing */
+ iobase = EWRK3_IO_BASE; /* Get the first slot address */
+ maxSlots = 24;
+ } else { /* Probe a specific location */
+ iobase = ioaddr;
+ maxSlots = i + 1;
+ }
+
+ for (; (i<maxSlots) && (dev!=NULL);iobase+=EWRK3_IOP_INC, i++) {
+ if (!check_region(iobase, EWRK3_TOTAL_SIZE)) {
+ if (DevicePresent(iobase) == 0) {
+ if ((dev = alloc_device(dev, iobase)) != NULL) {
+ if (ewrk3_hw_init(dev, iobase) == 0) {
+ num_ewrk3s++;
+ }
+ num_eth++;
+ }
+ }
+ } else if (autoprobed) {
+ printk("%s: region already allocated at 0x%04lx.\n", dev->name, iobase);
+ }
+ }
+
+ return;
+}
+
+/*
+** EISA bus I/O device probe. Probe from slot 1 since slot 0 is usually
+** the motherboard.
+*/
+static void eisa_probe(struct device *dev, u_long ioaddr)
+{
+ int i, maxSlots;
+ u_long iobase;
+ char name[EWRK3_STRLEN];
+
+ if (!ioaddr && autoprobed) return ; /* Been here before ! */
+ if (ioaddr < 0x1000) return; /* Not EISA */
+
+ if (ioaddr == 0) { /* Autoprobing */
+ iobase = EISA_SLOT_INC; /* Get the first slot address */
+ i = 1;
+ maxSlots = MAX_EISA_SLOTS;
+ } else { /* Probe a specific location */
+ iobase = ioaddr;
+ i = (ioaddr >> 12);
+ maxSlots = i + 1;
+ }
+
+ for (i=1; (i<maxSlots) && (dev!=NULL); i++, iobase+=EISA_SLOT_INC) {
+ if (EISA_signature(name, EISA_ID) == 0) {
+ if (!check_region(iobase, EWRK3_TOTAL_SIZE)) {
+ if (DevicePresent(iobase) == 0) {
+ if ((dev = alloc_device(dev, iobase)) != NULL) {
+ if (ewrk3_hw_init(dev, iobase) == 0) {
+ num_ewrk3s++;
+ }
+ num_eth++;
+ }
+ }
+ } else if (autoprobed) {
+ printk("%s: region already allocated at 0x%04lx.\n", dev->name, iobase);
+ }
+ }
+ }
+
+ return;
+}
+
+/*
+** Allocate the device by pointing to the next available space in the
+** device structure. Should one not be available, it is created.
+*/
+static struct device *alloc_device(struct device *dev, u_long iobase)
+{
+ int addAutoProbe = 0;
+ struct device *tmp = NULL, *ret;
+ int (*init)(struct device *) = NULL;
+
+ /*
+ ** Check the device structures for an end of list or unused device
+ */
+ if (!loading_module) {
+ while (dev->next != NULL) {
+ if ((dev->base_addr == EWRK3_NDA) || (dev->base_addr == 0)) break;
+ dev = dev->next; /* walk through eth device list */
+ num_eth++; /* increment eth device number */
+ }
+
+ /*
+ ** If an autoprobe is requested for another device, we must re-insert
+ ** the request later in the list. Remember the current position first.
+ */
+ if ((dev->base_addr == 0) && (num_ewrk3s > 0)) {
+ addAutoProbe++;
+ tmp = dev->next; /* point to the next device */
+ init = dev->init; /* remember the probe function */
+ }
+
+ /*
+ ** If at end of list and can't use current entry, malloc one up.
+ ** If memory could not be allocated, print an error message.
+ */
+ if ((dev->next == NULL) &&
+ !((dev->base_addr == EWRK3_NDA) || (dev->base_addr == 0))){
+ dev->next = (struct device *)kmalloc(sizeof(struct device) + 8,
+ GFP_KERNEL);
+
+ dev = dev->next; /* point to the new device */
+ if (dev == NULL) {
+ printk("eth%d: Device not initialised, insufficient memory\n",
+ num_eth);
+ } else {
+ /*
+ ** If the memory was allocated, point to the new memory area
+ ** and initialize it (name, I/O address, next device (NULL) and
+ ** initialisation probe routine).
+ */
+ dev->name = (char *)(dev + sizeof(struct device));
+ if (num_eth > 9999) {
+ sprintf(dev->name,"eth????"); /* New device name */
+ } else {
+ sprintf(dev->name,"eth%d", num_eth);/* New device name */
+ }
+ dev->base_addr = iobase; /* assign the io address */
+ dev->next = NULL; /* mark the end of list */
+ dev->init = &ewrk3_probe; /* initialisation routine */
+ num_ewrk3s++;
+ }
+ }
+ ret = dev; /* return current struct, or NULL */
+
+ /*
+ ** Now figure out what to do with the autoprobe that has to be inserted.
+ ** Firstly, search the (possibly altered) list for an empty space.
+ */
+ if (ret != NULL) {
+ if (addAutoProbe) {
+ for (;(tmp->next!=NULL) && (tmp->base_addr!=EWRK3_NDA); tmp=tmp->next);
+
+ /*
+ ** If no more device structures and can't use the current one, malloc
+ ** one up. If memory could not be allocated, print an error message.
+ */
+ if ((tmp->next == NULL) && !(tmp->base_addr == EWRK3_NDA)) {
+ tmp->next = (struct device *)kmalloc(sizeof(struct device) + 8,
+ GFP_KERNEL);
+ tmp = tmp->next; /* point to the new device */
+ if (tmp == NULL) {
+ printk("%s: Insufficient memory to extend the device list.\n",
+ dev->name);
+ } else {
+ /*
+ ** If the memory was allocated, point to the new memory area
+ ** and initialize it (name, I/O address, next device (NULL) and
+ ** initialisation probe routine).
+ */
+ tmp->name = (char *)(tmp + sizeof(struct device));
+ if (num_eth > 9999) {
+ sprintf(tmp->name,"eth????"); /* New device name */
+ } else {
+ sprintf(tmp->name,"eth%d", num_eth);/* New device name */
+ }
+ tmp->base_addr = 0; /* re-insert the io address */
+ tmp->next = NULL; /* mark the end of list */
+ tmp->init = init; /* initialisation routine */
+ }
+ } else { /* structure already exists */
+ tmp->base_addr = 0; /* re-insert the io address */
+ }
+ }
+ }
+ } else {
+ ret = dev;
+ }
+
+ return ret;
+}
+
+/*
+** Read the EWRK3 EEPROM using this routine
+*/
+static int Read_EEPROM(u_long iobase, u_char eaddr)
+{
+ int i;
+
+ outb((eaddr & 0x3f), EWRK3_PIR1); /* set up 6 bits of address info */
+ outb(EEPROM_RD, EWRK3_IOPR); /* issue read command */
+ for (i=0;i<5000;i++) inb(EWRK3_CSR); /* wait 1msec */
+
+ return inw(EWRK3_EPROM1); /* 16 bits data return */
+}
+
+/*
+** Write the EWRK3 EEPROM using this routine
+*/
+static int Write_EEPROM(short data, u_long iobase, u_char eaddr)
+{
+ int i;
+
+ outb(EEPROM_WR_EN, EWRK3_IOPR); /* issue write enable command */
+ for (i=0;i<5000;i++) inb(EWRK3_CSR); /* wait 1msec */
+ outw(data, EWRK3_EPROM1); /* write data to register */
+ outb((eaddr & 0x3f), EWRK3_PIR1); /* set up 6 bits of address info */
+ outb(EEPROM_WR, EWRK3_IOPR); /* issue write command */
+ for (i=0;i<75000;i++) inb(EWRK3_CSR); /* wait 15msec */
+ outb(EEPROM_WR_DIS, EWRK3_IOPR); /* issue write disable command */
+ for (i=0;i<5000;i++) inb(EWRK3_CSR); /* wait 1msec */
+
+ return 0;
+}
+
+/*
+** Look for a particular board name in the on-board EEPROM.
+*/
+static void EthwrkSignature(char *name, char *eeprom_image)
+{
+ u_long i,j,k;
+ char *signatures[] = EWRK3_SIGNATURE;
+
+ strcpy(name, "");
+ for (i=0;*signatures[i] != '\0' && *name == '\0';i++) {
+ for (j=EEPROM_PNAME7,k=0;j<=EEPROM_PNAME0 && k<strlen(signatures[i]);j++) {
+ if (signatures[i][k] == eeprom_image[j]) { /* track signature */
+ k++;
+ } else { /* lost signature; begin search again */
+ k=0;
+ }
+ }
+ if (k == strlen(signatures[i])) {
+ for (k=0; k<EWRK3_STRLEN; k++) {
+ name[k] = eeprom_image[EEPROM_PNAME7 + k];
+ name[EWRK3_STRLEN] = '\0';
+ }
+ }
+ }
+
+ return; /* return the device name string */
+}
+
+/*
+** Look for a special sequence in the Ethernet station address PROM that
+** is common across all EWRK3 products.
+**
+** Search the Ethernet address ROM for the signature. Since the ROM address
+** counter can start at an arbitrary point, the search must include the entire
+** probe sequence length plus the (length_of_the_signature - 1).
+** Stop the search IMMEDIATELY after the signature is found so that the
+** PROM address counter is correctly positioned at the start of the
+** ethernet address for later read out.
+*/
+
+static int DevicePresent(u_long iobase)
+{
+ union {
+ struct {
+ u32 a;
+ u32 b;
+ } llsig;
+ char Sig[sizeof(u32) << 1];
+ } dev;
+ short sigLength;
+ char data;
+ int i, j, status = 0;
+
+ dev.llsig.a = ETH_PROM_SIG;
+ dev.llsig.b = ETH_PROM_SIG;
+ sigLength = sizeof(u32) << 1;
+
+ for (i=0,j=0;j<sigLength && i<PROBE_LENGTH+sigLength-1;i++) {
+ data = inb(EWRK3_APROM);
+ if (dev.Sig[j] == data) { /* track signature */
+ j++;
+ } else { /* lost signature; begin search again */
+ if (data == dev.Sig[0]) {
+ j=1;
+ } else {
+ j=0;
+ }
+ }
+ }
+
+ if (j!=sigLength) {
+ status = -ENODEV; /* search failed */
+ }
+
+ return status;
+}
+
+static u_char get_hw_addr(struct device *dev, u_char *eeprom_image, char chipType)
+{
+ int i, j, k;
+ u_short chksum;
+ u_char crc, lfsr, sd, status = 0;
+ u_long iobase = dev->base_addr;
+ u16 tmp;
+
+ if (chipType == LeMAC2) {
+ for (crc=0x6a, j=0; j<ETH_ALEN; j++) {
+ sd = dev->dev_addr[j] = eeprom_image[EEPROM_PADDR0 + j];
+ outb(dev->dev_addr[j], EWRK3_PAR0 + j);
+ for (k=0; k<8; k++, sd >>= 1) {
+ lfsr = ((((crc & 0x02) >> 1) ^ (crc & 0x01)) ^ (sd & 0x01)) << 7;
+ crc = (crc >> 1) + lfsr;
+ }
+ }
+ if (crc != eeprom_image[EEPROM_PA_CRC]) status = -1;
+ } else {
+ for (i=0,k=0;i<ETH_ALEN;) {
+ k <<= 1 ;
+ if (k > 0xffff) k-=0xffff;
+
+ k += (u_char) (tmp = inb(EWRK3_APROM));
+ dev->dev_addr[i] = (u_char) tmp;
+ outb(dev->dev_addr[i], EWRK3_PAR0 + i);
+ i++;
+ k += (u_short) ((tmp = inb(EWRK3_APROM)) << 8);
+ dev->dev_addr[i] = (u_char) tmp;
+ outb(dev->dev_addr[i], EWRK3_PAR0 + i);
+ i++;
+
+ if (k > 0xffff) k-=0xffff;
+ }
+ if (k == 0xffff) k=0;
+ chksum = inb(EWRK3_APROM);
+ chksum |= (inb(EWRK3_APROM)<<8);
+ if (k != chksum) status = -1;
+ }
+
+ return status;
+}
+
+/*
+** Look for a particular board name in the EISA configuration space
+*/
+static int EISA_signature(char *name, s32 eisa_id)
+{
+ u_long i;
+ char *signatures[] = EWRK3_SIGNATURE;
+ char ManCode[EWRK3_STRLEN];
+ union {
+ s32 ID;
+ char Id[4];
+ } Eisa;
+ int status = 0;
+
+ *name = '\0';
+ for (i=0; i<4; i++) {
+ Eisa.Id[i] = inb(eisa_id + i);
+ }
+
+ ManCode[0]=(((Eisa.Id[0]>>2)&0x1f)+0x40);
+ ManCode[1]=(((Eisa.Id[1]&0xe0)>>5)+((Eisa.Id[0]&0x03)<<3)+0x40);
+ ManCode[2]=(((Eisa.Id[2]>>4)&0x0f)+0x30);
+ ManCode[3]=((Eisa.Id[2]&0x0f)+0x30);
+ ManCode[4]=(((Eisa.Id[3]>>4)&0x0f)+0x30);
+ ManCode[5]='\0';
+
+ for (i=0;(*signatures[i] != '\0') && (*name == '\0');i++) {
+ if (strstr(ManCode, signatures[i]) != NULL) {
+ strcpy(name,ManCode);
+ status = 1;
+ }
+ }
+
+ return status; /* return the device name string */
+}
+
+/*
+** Perform IOCTL call functions here. Some are privileged operations and the
+** effective uid is checked in those cases.
+*/
+static int ewrk3_ioctl(struct device *dev, struct ifreq *rq, int cmd)
+{
+ struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv;
+ struct ewrk3_ioctl *ioc = (struct ewrk3_ioctl *) &rq->ifr_data;
+ u_long iobase = dev->base_addr;
+ int i, j, status = 0;
+ u_char csr;
+ union {
+ u_char addr[HASH_TABLE_LEN * ETH_ALEN];
+ u_short val[(HASH_TABLE_LEN * ETH_ALEN) >> 1];
+ } tmp;
+
+ switch(ioc->cmd) {
+ case EWRK3_GET_HWADDR: /* Get the hardware address */
+ for (i=0; i<ETH_ALEN; i++) {
+ tmp.addr[i] = dev->dev_addr[i];
+ }
+ ioc->len = ETH_ALEN;
+ if (!(status = verify_area(VERIFY_WRITE, (void *)ioc->data, ioc->len))) {
+ memcpy_tofs(ioc->data, tmp.addr, ioc->len);
+ }
+
+ break;
+ case EWRK3_SET_HWADDR: /* Set the hardware address */
+ if (suser()) {
+ if (!(status = verify_area(VERIFY_READ, (void *)ioc->data, ETH_ALEN))) {
+ csr = inb(EWRK3_CSR);
+ csr |= (CSR_TXD|CSR_RXD);
+ outb(csr, EWRK3_CSR); /* Disable the TX and RX */
+
+ memcpy_fromfs(tmp.addr,ioc->data,ETH_ALEN);
+ for (i=0; i<ETH_ALEN; i++) {
+ dev->dev_addr[i] = tmp.addr[i];
+ outb(tmp.addr[i], EWRK3_PAR0 + i);
+ }
+
+ csr &= ~(CSR_TXD|CSR_RXD); /* Enable the TX and RX */
+ outb(csr, EWRK3_CSR);
+ }
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case EWRK3_SET_PROM: /* Set Promiscuous Mode */
+ if (suser()) {
+ csr = inb(EWRK3_CSR);
+ csr |= CSR_PME;
+ csr &= ~CSR_MCE;
+ outb(csr, EWRK3_CSR);
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case EWRK3_CLR_PROM: /* Clear Promiscuous Mode */
+ if (suser()) {
+ csr = inb(EWRK3_CSR);
+ csr &= ~CSR_PME;
+ outb(csr, EWRK3_CSR);
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case EWRK3_SAY_BOO: /* Say "Boo!" to the kernel log file */
+ printk("%s: Boo!\n", dev->name);
+
+ break;
+ case EWRK3_GET_MCA: /* Get the multicast address table */
+ if (!(status = verify_area(VERIFY_WRITE, ioc->data, ioc->len))) {
+ while (set_bit(0, (void *)&lp->lock) != 0); /* Wait for lock to free */
+ if (lp->shmem_length == IO_ONLY) {
+ outb(0, EWRK3_IOPR);
+ outw(PAGE0_HTE, EWRK3_PIR1);
+ for (i=0; i<(HASH_TABLE_LEN >> 3); i++) {
+ tmp.addr[i] = inb(EWRK3_DATA);
+ }
+ } else {
+ outb(0, EWRK3_MPR);
+ memcpy_fromio(tmp.addr, (char *)(lp->shmem_base + PAGE0_HTE), (HASH_TABLE_LEN >> 3));
+ }
+ ioc->len = (HASH_TABLE_LEN >> 3);
+ memcpy_tofs(ioc->data, tmp.addr, ioc->len);
+ }
+ lp->lock = 0; /* Unlock the page register */
+
+ break;
+ case EWRK3_SET_MCA: /* Set a multicast address */
+ if (suser()) {
+ if (!(status=verify_area(VERIFY_READ, ioc->data, ETH_ALEN*ioc->len))) {
+ memcpy_fromfs(tmp.addr, ioc->data, ETH_ALEN * ioc->len);
+ set_multicast_list(dev);
+ }
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case EWRK3_CLR_MCA: /* Clear all multicast addresses */
+ if (suser()) {
+ set_multicast_list(dev);
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case EWRK3_MCA_EN: /* Enable multicast addressing */
+ if (suser()) {
+ csr = inb(EWRK3_CSR);
+ csr |= CSR_MCE;
+ csr &= ~CSR_PME;
+ outb(csr, EWRK3_CSR);
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case EWRK3_GET_STATS: /* Get the driver statistics */
+ cli();
+ ioc->len = sizeof(lp->pktStats);
+ if (!(status=verify_area(VERIFY_WRITE, ioc->data, ioc->len))) {
+ memcpy_tofs(ioc->data, &lp->pktStats, ioc->len);
+ }
+ sti();
+
+ break;
+ case EWRK3_CLR_STATS: /* Zero out the driver statistics */
+ if (suser()) {
+ cli();
+ memset(&lp->pktStats, 0, sizeof(lp->pktStats));
+ sti();
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case EWRK3_GET_CSR: /* Get the CSR Register contents */
+ tmp.addr[0] = inb(EWRK3_CSR);
+ ioc->len = 1;
+ if (!(status=verify_area(VERIFY_WRITE, ioc->data, ioc->len))) {
+ memcpy_tofs(ioc->data, tmp.addr, ioc->len);
+ }
+
+ break;
+ case EWRK3_SET_CSR: /* Set the CSR Register contents */
+ if (suser()) {
+ if (!(status=verify_area(VERIFY_READ, ioc->data, 1))) {
+ memcpy_fromfs(tmp.addr, ioc->data, 1);
+ outb(tmp.addr[0], EWRK3_CSR);
+ }
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case EWRK3_GET_EEPROM: /* Get the EEPROM contents */
+ if (suser()) {
+ for (i=0; i<(EEPROM_MAX>>1); i++) {
+ tmp.val[i] = (short)Read_EEPROM(iobase, i);
+ }
+ i = EEPROM_MAX;
+ tmp.addr[i++] = inb(EWRK3_CMR); /* Config/Management Reg. */
+ for (j=0;j<ETH_ALEN;j++) {
+ tmp.addr[i++] = inb(EWRK3_PAR0 + j);
+ }
+ ioc->len = EEPROM_MAX + 1 + ETH_ALEN;
+ if (!(status=verify_area(VERIFY_WRITE, ioc->data, ioc->len))) {
+ memcpy_tofs(ioc->data, tmp.addr, ioc->len);
+ }
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case EWRK3_SET_EEPROM: /* Set the EEPROM contents */
+ if (suser()) {
+ if (!(status=verify_area(VERIFY_READ, ioc->data, EEPROM_MAX))) {
+ memcpy_fromfs(tmp.addr, ioc->data, EEPROM_MAX);
+ for (i=0; i<(EEPROM_MAX>>1); i++) {
+ Write_EEPROM(tmp.val[i], iobase, i);
+ }
+ }
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case EWRK3_GET_CMR: /* Get the CMR Register contents */
+ tmp.addr[0] = inb(EWRK3_CMR);
+ ioc->len = 1;
+ if (!(status=verify_area(VERIFY_WRITE, ioc->data, ioc->len))) {
+ memcpy_tofs(ioc->data, tmp.addr, ioc->len);
+ }
+
+ break;
+ case EWRK3_SET_TX_CUT_THRU: /* Set TX cut through mode */
+ if (suser()) {
+ lp->txc = 1;
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case EWRK3_CLR_TX_CUT_THRU: /* Clear TX cut through mode */
+ if (suser()) {
+ lp->txc = 0;
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ default:
+ status = -EOPNOTSUPP;
+ }
+
+ return status;
+}
+
+#ifdef MODULE
+static char devicename[9] = { 0, };
+static struct device thisEthwrk = {
+ devicename, /* device name is inserted by /linux/drivers/net/net_init.c */
+ 0, 0, 0, 0,
+ 0x300, 5, /* I/O address, IRQ */
+ 0, 0, 0, NULL, ewrk3_probe };
+
+static int io=0x300; /* <--- EDIT THESE LINES FOR YOUR CONFIGURATION */
+static int irq=5; /* or use the insmod io= irq= options */
+
+int
+init_module(void)
+{
+ thisEthwrk.base_addr=io;
+ thisEthwrk.irq=irq;
+ if (register_netdev(&thisEthwrk) != 0)
+ return -EIO;
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ release_region(thisEthwrk.base_addr, EWRK3_TOTAL_SIZE);
+
+ if (thisEthwrk.priv) {
+ kfree(thisEthwrk.priv);
+ thisEthwrk.priv = NULL;
+ }
+ thisEthwrk.irq = 0;
+
+ unregister_netdev(&thisEthwrk);
+}
+#endif /* MODULE */
+
+
+/*
+ * Local variables:
+ * kernel-compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O2 -m486 -c ewrk3.c"
+ *
+ * module-compile-command: "gcc -D__KERNEL__ -DMODULE -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O2 -m486 -c ewrk3.c"
+ * End:
+ */
+
diff --git a/i386/i386at/gpl/linux/net/ewrk3.h b/i386/i386at/gpl/linux/net/ewrk3.h
new file mode 100644
index 00000000..b37abf46
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/ewrk3.h
@@ -0,0 +1,322 @@
+/*
+ Written 1994 by David C. Davies.
+
+ Copyright 1994 Digital Equipment Corporation.
+
+ This software may be used and distributed according to the terms of the
+ GNU Public License, incorporated herein by reference.
+
+ The author may be reached as davies@wanton.lkg.dec.com or Digital
+ Equipment Corporation, 550 King Street, Littleton MA 01460.
+
+ =========================================================================
+*/
+
+/*
+** I/O Address Register Map
+*/
+#define EWRK3_CSR iobase+0x00 /* Control and Status Register */
+#define EWRK3_CR iobase+0x01 /* Control Register */
+#define EWRK3_ICR iobase+0x02 /* Interrupt Control Register */
+#define EWRK3_TSR iobase+0x03 /* Transmit Status Register */
+#define EWRK3_RSVD1 iobase+0x04 /* RESERVED */
+#define EWRK3_RSVD2 iobase+0x05 /* RESERVED */
+#define EWRK3_FMQ iobase+0x06 /* Free Memory Queue */
+#define EWRK3_FMQC iobase+0x07 /* Free Memory Queue Counter */
+#define EWRK3_RQ iobase+0x08 /* Receive Queue */
+#define EWRK3_RQC iobase+0x09 /* Receive Queue Counter */
+#define EWRK3_TQ iobase+0x0a /* Transmit Queue */
+#define EWRK3_TQC iobase+0x0b /* Transmit Queue Counter */
+#define EWRK3_TDQ iobase+0x0c /* Transmit Done Queue */
+#define EWRK3_TDQC iobase+0x0d /* Transmit Done Queue Counter */
+#define EWRK3_PIR1 iobase+0x0e /* Page Index Register 1 */
+#define EWRK3_PIR2 iobase+0x0f /* Page Index Register 2 */
+#define EWRK3_DATA iobase+0x10 /* Data Register */
+#define EWRK3_IOPR iobase+0x11 /* I/O Page Register */
+#define EWRK3_IOBR iobase+0x12 /* I/O Base Register */
+#define EWRK3_MPR iobase+0x13 /* Memory Page Register */
+#define EWRK3_MBR iobase+0x14 /* Memory Base Register */
+#define EWRK3_APROM iobase+0x15 /* Address PROM */
+#define EWRK3_EPROM1 iobase+0x16 /* EEPROM Data Register 1 */
+#define EWRK3_EPROM2 iobase+0x17 /* EEPROM Data Register 2 */
+#define EWRK3_PAR0 iobase+0x18 /* Physical Address Register 0 */
+#define EWRK3_PAR1 iobase+0x19 /* Physical Address Register 1 */
+#define EWRK3_PAR2 iobase+0x1a /* Physical Address Register 2 */
+#define EWRK3_PAR3 iobase+0x1b /* Physical Address Register 3 */
+#define EWRK3_PAR4 iobase+0x1c /* Physical Address Register 4 */
+#define EWRK3_PAR5 iobase+0x1d /* Physical Address Register 5 */
+#define EWRK3_CMR iobase+0x1e /* Configuration/Management Register */
+
+/*
+** Control Page Map
+*/
+#define PAGE0_FMQ 0x000 /* Free Memory Queue */
+#define PAGE0_RQ 0x080 /* Receive Queue */
+#define PAGE0_TQ 0x100 /* Transmit Queue */
+#define PAGE0_TDQ 0x180 /* Transmit Done Queue */
+#define PAGE0_HTE 0x200 /* Hash Table Entries */
+#define PAGE0_RSVD 0x240 /* RESERVED */
+#define PAGE0_USRD 0x600 /* User Data */
+
+/*
+** Control and Status Register bit definitions (EWRK3_CSR)
+*/
+#define CSR_RA 0x80 /* Runt Accept */
+#define CSR_PME 0x40 /* Promiscuous Mode Enable */
+#define CSR_MCE 0x20 /* Multicast Enable */
+#define CSR_TNE 0x08 /* TX Done Queue Not Empty */
+#define CSR_RNE 0x04 /* RX Queue Not Empty */
+#define CSR_TXD 0x02 /* TX Disable */
+#define CSR_RXD 0x01 /* RX Disable */
+
+/*
+** Control Register bit definitions (EWRK3_CR)
+*/
+#define CR_APD 0x80 /* Auto Port Disable */
+#define CR_PSEL 0x40 /* Port Select (0->TP port) */
+#define CR_LBCK 0x20 /* LoopBaCK enable */
+#define CR_FDUP 0x10 /* Full DUPlex enable */
+#define CR_FBUS 0x08 /* Fast BUS enable (ISA clk > 8.33MHz) */
+#define CR_EN_16 0x04 /* ENable 16 bit memory accesses */
+#define CR_LED 0x02 /* LED (1-> turn on) */
+
+/*
+** Interrupt Control Register bit definitions (EWRK3_ICR)
+*/
+#define ICR_IE 0x80 /* Interrupt Enable */
+#define ICR_IS 0x60 /* Interrupt Selected */
+#define ICR_TNEM 0x08 /* TNE Mask (0->mask) */
+#define ICR_RNEM 0x04 /* RNE Mask (0->mask) */
+#define ICR_TXDM 0x02 /* TXD Mask (0->mask) */
+#define ICR_RXDM 0x01 /* RXD Mask (0->mask) */
+
+/*
+** Transmit Status Register bit definitions (EWRK3_TSR)
+*/
+#define TSR_NCL 0x80 /* No Carrier Loopback */
+#define TSR_ID 0x40 /* Initially Deferred */
+#define TSR_LCL 0x20 /* Late CoLlision */
+#define TSR_ECL 0x10 /* Excessive CoLlisions */
+#define TSR_RCNTR 0x0f /* Retries CouNTeR */
+
+/*
+** I/O Page Register bit definitions (EWRK3_IOPR)
+*/
+#define EEPROM_INIT 0xc0 /* EEPROM INIT command */
+#define EEPROM_WR_EN 0xc8 /* EEPROM WRITE ENABLE command */
+#define EEPROM_WR 0xd0 /* EEPROM WRITE command */
+#define EEPROM_WR_DIS 0xd8 /* EEPROM WRITE DISABLE command */
+#define EEPROM_RD 0xe0 /* EEPROM READ command */
+
+/*
+** I/O Base Register bit definitions (EWRK3_IOBR)
+*/
+#define EISA_REGS_EN 0x20 /* Enable EISA ID and Control Registers */
+#define EISA_IOB 0x1f /* Compare bits for I/O Base Address */
+
+/*
+** I/O Congiguration/Management Register bit definitions (EWRK3_CMR)
+*/
+#define CMR_RA 0x80 /* Read Ahead */
+#define CMR_WB 0x40 /* Write Behind */
+#define CMR_LINK 0x20 /* 0->TP */
+#define CMR_POLARITY 0x10 /* Informational */
+#define CMR_NO_EEPROM 0x0c /* NO_EEPROM<1:0> pin status */
+#define CMR_HS 0x08 /* Hard Strapped pin status (LeMAC2) */
+#define CMR_PNP 0x04 /* Plug 'n Play */
+#define CMR_DRAM 0x02 /* 0-> 1DRAM, 1-> 2 DRAM on board */
+#define CMR_0WS 0x01 /* Zero Wait State */
+
+/*
+** MAC Receive Status Register bit definitions
+*/
+
+#define R_ROK 0x80 /* Receive OK summary */
+#define R_IAM 0x10 /* Individual Address Match */
+#define R_MCM 0x08 /* MultiCast Match */
+#define R_DBE 0x04 /* Dribble Bit Error */
+#define R_CRC 0x02 /* CRC error */
+#define R_PLL 0x01 /* Phase Lock Lost */
+
+/*
+** MAC Transmit Control Register bit definitions
+*/
+
+#define TCR_SQEE 0x40 /* SQE Enable - look for heartbeat */
+#define TCR_SED 0x20 /* Stop when Error Detected */
+#define TCR_QMODE 0x10 /* Q_MODE */
+#define TCR_LAB 0x08 /* Less Aggressive Backoff */
+#define TCR_PAD 0x04 /* PAD Runt Packets */
+#define TCR_IFC 0x02 /* Insert Frame Check */
+#define TCR_ISA 0x01 /* Insert Source Address */
+
+/*
+** MAC Transmit Status Register bit definitions
+*/
+
+#define T_VSTS 0x80 /* Valid STatuS */
+#define T_CTU 0x40 /* Cut Through Used */
+#define T_SQE 0x20 /* Signal Quality Error */
+#define T_NCL 0x10 /* No Carrier Loopback */
+#define T_LCL 0x08 /* Late Collision */
+#define T_ID 0x04 /* Initially Deferred */
+#define T_COLL 0x03 /* COLLision status */
+#define T_XCOLL 0x03 /* Excessive Collisions */
+#define T_MCOLL 0x02 /* Multiple Collisions */
+#define T_OCOLL 0x01 /* One Collision */
+#define T_NOCOLL 0x00 /* No Collisions */
+#define T_XUR 0x03 /* Excessive Underruns */
+#define T_TXE 0x7f /* TX Errors */
+
+/*
+** EISA Configuration Register bit definitions
+*/
+
+#define EISA_ID iobase + 0x0c80 /* EISA ID Registers */
+#define EISA_ID0 iobase + 0x0c80 /* EISA ID Register 0 */
+#define EISA_ID1 iobase + 0x0c81 /* EISA ID Register 1 */
+#define EISA_ID2 iobase + 0x0c82 /* EISA ID Register 2 */
+#define EISA_ID3 iobase + 0x0c83 /* EISA ID Register 3 */
+#define EISA_CR iobase + 0x0c84 /* EISA Control Register */
+
+/*
+** EEPROM BYTES
+*/
+#define EEPROM_MEMB 0x00
+#define EEPROM_IOB 0x01
+#define EEPROM_EISA_ID0 0x02
+#define EEPROM_EISA_ID1 0x03
+#define EEPROM_EISA_ID2 0x04
+#define EEPROM_EISA_ID3 0x05
+#define EEPROM_MISC0 0x06
+#define EEPROM_MISC1 0x07
+#define EEPROM_PNAME7 0x08
+#define EEPROM_PNAME6 0x09
+#define EEPROM_PNAME5 0x0a
+#define EEPROM_PNAME4 0x0b
+#define EEPROM_PNAME3 0x0c
+#define EEPROM_PNAME2 0x0d
+#define EEPROM_PNAME1 0x0e
+#define EEPROM_PNAME0 0x0f
+#define EEPROM_SWFLAGS 0x10
+#define EEPROM_HWCAT 0x11
+#define EEPROM_NETMAN2 0x12
+#define EEPROM_REVLVL 0x13
+#define EEPROM_NETMAN0 0x14
+#define EEPROM_NETMAN1 0x15
+#define EEPROM_CHIPVER 0x16
+#define EEPROM_SETUP 0x17
+#define EEPROM_PADDR0 0x18
+#define EEPROM_PADDR1 0x19
+#define EEPROM_PADDR2 0x1a
+#define EEPROM_PADDR3 0x1b
+#define EEPROM_PADDR4 0x1c
+#define EEPROM_PADDR5 0x1d
+#define EEPROM_PA_CRC 0x1e
+#define EEPROM_CHKSUM 0x1f
+
+/*
+** EEPROM bytes for checksumming
+*/
+#define EEPROM_MAX 32 /* bytes */
+
+/*
+** EEPROM MISCELLANEOUS FLAGS
+*/
+#define RBE_SHADOW 0x0100 /* Remote Boot Enable Shadow */
+#define READ_AHEAD 0x0080 /* Read Ahead feature */
+#define IRQ_SEL2 0x0070 /* IRQ line selection (LeMAC2) */
+#define IRQ_SEL 0x0060 /* IRQ line selection */
+#define FAST_BUS 0x0008 /* ISA Bus speeds > 8.33MHz */
+#define ENA_16 0x0004 /* Enables 16 bit memory transfers */
+#define WRITE_BEHIND 0x0002 /* Write Behind feature */
+#define _0WS_ENA 0x0001 /* Zero Wait State Enable */
+
+/*
+** EEPROM NETWORK MANAGEMENT FLAGS
+*/
+#define NETMAN_POL 0x04 /* Polarity defeat */
+#define NETMAN_LINK 0x02 /* Link defeat */
+#define NETMAN_CCE 0x01 /* Custom Counters Enable */
+
+/*
+** EEPROM SW FLAGS
+*/
+#define SW_SQE 0x10 /* Signal Quality Error */
+#define SW_LAB 0x08 /* Less Aggressive Backoff */
+#define SW_INIT 0x04 /* Initialized */
+#define SW_TIMEOUT 0x02 /* 0:2.5 mins, 1: 30 secs */
+#define SW_REMOTE 0x01 /* Remote Boot Enable -> 1 */
+
+/*
+** EEPROM SETUP FLAGS
+*/
+#define SETUP_APD 0x80 /* AutoPort Disable */
+#define SETUP_PS 0x40 /* Port Select */
+#define SETUP_MP 0x20 /* MultiPort */
+#define SETUP_1TP 0x10 /* 1 port, TP */
+#define SETUP_1COAX 0x00 /* 1 port, Coax */
+#define SETUP_DRAM 0x02 /* Number of DRAMS on board */
+
+/*
+** EEPROM MANAGEMENT FLAGS
+*/
+#define MGMT_CCE 0x01 /* Custom Counters Enable */
+
+/*
+** EEPROM VERSIONS
+*/
+#define LeMAC 0x11
+#define LeMAC2 0x12
+
+/*
+** Miscellaneous
+*/
+
+#define EEPROM_WAIT_TIME 1000 /* Number of microseconds */
+#define EISA_EN 0x0001 /* Enable EISA bus buffers */
+
+#define HASH_TABLE_LEN 512 /* Bits */
+
+#define XCT 0x80 /* Transmit Cut Through */
+#define PRELOAD 16 /* 4 long words */
+
+#define MASK_INTERRUPTS 1
+#define UNMASK_INTERRUPTS 0
+
+#define EEPROM_OFFSET(a) ((u_short)((u_long)(a)))
+
+/*
+** Include the IOCTL stuff
+*/
+#include <linux/sockios.h>
+
+#define EWRK3IOCTL SIOCDEVPRIVATE
+
+struct ewrk3_ioctl {
+ unsigned short cmd; /* Command to run */
+ unsigned short len; /* Length of the data buffer */
+ unsigned char *data; /* Pointer to the data buffer */
+};
+
+/*
+** Recognised commands for the driver
+*/
+#define EWRK3_GET_HWADDR 0x01 /* Get the hardware address */
+#define EWRK3_SET_HWADDR 0x02 /* Get the hardware address */
+#define EWRK3_SET_PROM 0x03 /* Set Promiscuous Mode */
+#define EWRK3_CLR_PROM 0x04 /* Clear Promiscuous Mode */
+#define EWRK3_SAY_BOO 0x05 /* Say "Boo!" to the kernel log file */
+#define EWRK3_GET_MCA 0x06 /* Get a multicast address */
+#define EWRK3_SET_MCA 0x07 /* Set a multicast address */
+#define EWRK3_CLR_MCA 0x08 /* Clear a multicast address */
+#define EWRK3_MCA_EN 0x09 /* Enable a multicast address group */
+#define EWRK3_GET_STATS 0x0a /* Get the driver statistics */
+#define EWRK3_CLR_STATS 0x0b /* Zero out the driver statistics */
+#define EWRK3_GET_CSR 0x0c /* Get the CSR Register contents */
+#define EWRK3_SET_CSR 0x0d /* Set the CSR Register contents */
+#define EWRK3_GET_EEPROM 0x0e /* Get the EEPROM contents */
+#define EWRK3_SET_EEPROM 0x0f /* Set the EEPROM contents */
+#define EWRK3_GET_CMR 0x10 /* Get the CMR Register contents */
+#define EWRK3_CLR_TX_CUT_THRU 0x11 /* Clear the TX cut through mode */
+#define EWRK3_SET_TX_CUT_THRU 0x12 /* Set the TX cut through mode */
diff --git a/i386/i386at/gpl/linux/net/hp-plus.c b/i386/i386at/gpl/linux/net/hp-plus.c
new file mode 100644
index 00000000..aed7ee01
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/hp-plus.c
@@ -0,0 +1,483 @@
+/* hp-plus.c: A HP PCLAN/plus ethernet driver for linux. */
+/*
+ Written 1994 by Donald Becker.
+
+ This driver is for the Hewlett Packard PC LAN (27***) plus ethercards.
+ These cards are sold under several model numbers, usually 2724*.
+
+ This software may be used and distributed according to the terms
+ of the GNU Public License, incorporated herein by reference.
+
+ The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
+
+ Center of Excellence in Space Data and Information Sciences
+ Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
+
+ As is often the case, a great deal of credit is owed to Russ Nelson.
+ The Crynwr packet driver was my primary source of HP-specific
+ programming information.
+*/
+
+static const char *version =
+"hp-plus.c:v1.10 9/24/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+#include <linux/module.h>
+
+#include <linux/string.h> /* Important -- this inlines word moves. */
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+
+
+#include "8390.h"
+
+/* A zero-terminated list of I/O addresses to be probed. */
+static unsigned int hpplus_portlist[] =
+{0x200, 0x240, 0x280, 0x2C0, 0x300, 0x320, 0x340, 0};
+
+/*
+ The HP EtherTwist chip implementation is a fairly routine DP8390
+ implementation. It allows both shared memory and programmed-I/O buffer
+ access, using a custom interface for both. The programmed-I/O mode is
+ entirely implemented in the HP EtherTwist chip, bypassing the problem
+ ridden built-in 8390 facilities used on NE2000 designs. The shared
+ memory mode is likewise special, with an offset register used to make
+ packets appear at the shared memory base. Both modes use a base and bounds
+ page register to hide the Rx ring buffer wrap -- a packet that spans the
+ end of physical buffer memory appears continuous to the driver. (c.f. the
+ 3c503 and Cabletron E2100)
+
+ A special note: the internal buffer of the board is only 8 bits wide.
+ This lays several nasty traps for the unaware:
+ - the 8390 must be programmed for byte-wide operations
+ - all I/O and memory operations must work on whole words (the access
+ latches are serially preloaded and have no byte-swapping ability).
+
+ This board is laid out in I/O space much like the earlier HP boards:
+ the first 16 locations are for the board registers, and the second 16 are
+ for the 8390. The board is easy to identify, with both a dedicated 16 bit
+ ID register and a constant 0x530* value in the upper bits of the paging
+ register.
+*/
+
+#define HP_ID 0x00 /* ID register, always 0x4850. */
+#define HP_PAGING 0x02 /* Registers visible @ 8-f, see PageName. */
+#define HPP_OPTION 0x04 /* Bitmapped options, see HP_Option. */
+#define HPP_OUT_ADDR 0x08 /* I/O output location in Perf_Page. */
+#define HPP_IN_ADDR 0x0A /* I/O input location in Perf_Page. */
+#define HP_DATAPORT 0x0c /* I/O data transfer in Perf_Page. */
+#define NIC_OFFSET 0x10 /* Offset to the 8390 registers. */
+#define HP_IO_EXTENT 32
+
+#define HP_START_PG 0x00 /* First page of TX buffer */
+#define HP_STOP_PG 0x80 /* Last page +1 of RX ring */
+
+/* The register set selected in HP_PAGING. */
+enum PageName {
+ Perf_Page = 0, /* Normal operation. */
+ MAC_Page = 1, /* The ethernet address (+checksum). */
+ HW_Page = 2, /* EEPROM-loaded hardware parameters. */
+ LAN_Page = 4, /* Transceiver selection, testing, etc. */
+ ID_Page = 6 };
+
+/* The bit definitions for the HPP_OPTION register. */
+enum HP_Option {
+ NICReset = 1, ChipReset = 2, /* Active low, really UNreset. */
+ EnableIRQ = 4, FakeIntr = 8, BootROMEnb = 0x10, IOEnb = 0x20,
+ MemEnable = 0x40, ZeroWait = 0x80, MemDisable = 0x1000, };
+
+int hp_plus_probe(struct device *dev);
+int hpp_probe1(struct device *dev, int ioaddr);
+
+static void hpp_reset_8390(struct device *dev);
+static int hpp_open(struct device *dev);
+static int hpp_close(struct device *dev);
+static void hpp_mem_block_input(struct device *dev, int count,
+ struct sk_buff *skb, int ring_offset);
+static void hpp_mem_block_output(struct device *dev, int count,
+ const unsigned char *buf, const start_page);
+static void hpp_mem_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
+ int ring_page);
+static void hpp_io_block_input(struct device *dev, int count,
+ struct sk_buff *skb, int ring_offset);
+static void hpp_io_block_output(struct device *dev, int count,
+ const unsigned char *buf, const start_page);
+static void hpp_io_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
+ int ring_page);
+
+
+/* Probe a list of addresses for an HP LAN+ adaptor.
+ This routine is almost boilerplate. */
+#ifdef HAVE_DEVLIST
+/* Support for a alternate probe manager, which will eliminate the
+ boilerplate below. */
+struct netdev_entry hpplus_drv =
+{"hpplus", hpp_probe1, HP_IO_EXTENT, hpplus_portlist};
+#else
+
+int hp_plus_probe(struct device *dev)
+{
+ int i;
+ int base_addr = dev ? dev->base_addr : 0;
+
+ if (base_addr > 0x1ff) /* Check a single specified location. */
+ return hpp_probe1(dev, base_addr);
+ else if (base_addr != 0) /* Don't probe at all. */
+ return ENXIO;
+
+ for (i = 0; hpplus_portlist[i]; i++) {
+ int ioaddr = hpplus_portlist[i];
+ if (check_region(ioaddr, HP_IO_EXTENT))
+ continue;
+ if (hpp_probe1(dev, ioaddr) == 0)
+ return 0;
+ }
+
+ return ENODEV;
+}
+#endif
+
+/* Do the interesting part of the probe at a single address. */
+int hpp_probe1(struct device *dev, int ioaddr)
+{
+ int i;
+ unsigned char checksum = 0;
+ const char *name = "HP-PC-LAN+";
+ int mem_start;
+ static unsigned version_printed = 0;
+
+ /* Check for the HP+ signature, 50 48 0x 53. */
+ if (inw(ioaddr + HP_ID) != 0x4850
+ || (inw(ioaddr + HP_PAGING) & 0xfff0) != 0x5300)
+ return ENODEV;
+
+ /* We should have a "dev" from Space.c or the static module table. */
+ if (dev == NULL) {
+ printk("hp-plus.c: Passed a NULL device.\n");
+ dev = init_etherdev(0, 0);
+ }
+
+ if (ei_debug && version_printed++ == 0)
+ printk(version);
+
+ printk("%s: %s at %#3x,", dev->name, name, ioaddr);
+
+ /* Retrieve and checksum the station address. */
+ outw(MAC_Page, ioaddr + HP_PAGING);
+
+ for(i = 0; i < ETHER_ADDR_LEN; i++) {
+ unsigned char inval = inb(ioaddr + 8 + i);
+ dev->dev_addr[i] = inval;
+ checksum += inval;
+ printk(" %2.2x", inval);
+ }
+ checksum += inb(ioaddr + 14);
+
+ if (checksum != 0xff) {
+ printk(" bad checksum %2.2x.\n", checksum);
+ return ENODEV;
+ } else {
+ /* Point at the Software Configuration Flags. */
+ outw(ID_Page, ioaddr + HP_PAGING);
+ printk(" ID %4.4x", inw(ioaddr + 12));
+ }
+
+ /* Allocate dev->priv and fill in 8390 specific dev fields. */
+ if (ethdev_init(dev)) {
+ printk ("hp-plus.c: unable to allocate memory for dev->priv.\n");
+ return -ENOMEM;
+ }
+
+ /* Grab the region so we can find another board if something fails. */
+ request_region(ioaddr, HP_IO_EXTENT,"hp-plus");
+
+ /* Read the IRQ line. */
+ outw(HW_Page, ioaddr + HP_PAGING);
+ {
+ int irq = inb(ioaddr + 13) & 0x0f;
+ int option = inw(ioaddr + HPP_OPTION);
+
+ dev->irq = irq;
+ if (option & MemEnable) {
+ mem_start = inw(ioaddr + 9) << 8;
+ printk(", IRQ %d, memory address %#x.\n", irq, mem_start);
+ } else {
+ mem_start = 0;
+ printk(", IRQ %d, programmed-I/O mode.\n", irq);
+ }
+ }
+
+ /* Set the wrap registers for string I/O reads. */
+ outw((HP_START_PG + TX_2X_PAGES) | ((HP_STOP_PG - 1) << 8), ioaddr + 14);
+
+ /* Set the base address to point to the NIC, not the "real" base! */
+ dev->base_addr = ioaddr + NIC_OFFSET;
+
+ dev->open = &hpp_open;
+ dev->stop = &hpp_close;
+
+ ei_status.name = name;
+ ei_status.word16 = 0; /* Agggghhhhh! Debug time: 2 days! */
+ ei_status.tx_start_page = HP_START_PG;
+ ei_status.rx_start_page = HP_START_PG + TX_2X_PAGES;
+ ei_status.stop_page = HP_STOP_PG;
+
+ ei_status.reset_8390 = &hpp_reset_8390;
+ ei_status.block_input = &hpp_io_block_input;
+ ei_status.block_output = &hpp_io_block_output;
+ ei_status.get_8390_hdr = &hpp_io_get_8390_hdr;
+
+ /* Check if the memory_enable flag is set in the option register. */
+ if (mem_start) {
+ ei_status.block_input = &hpp_mem_block_input;
+ ei_status.block_output = &hpp_mem_block_output;
+ ei_status.get_8390_hdr = &hpp_mem_get_8390_hdr;
+ dev->mem_start = mem_start;
+ dev->rmem_start = dev->mem_start + TX_2X_PAGES*256;
+ dev->mem_end = dev->rmem_end
+ = dev->mem_start + (HP_STOP_PG - HP_START_PG)*256;
+ }
+
+ outw(Perf_Page, ioaddr + HP_PAGING);
+ NS8390_init(dev, 0);
+ /* Leave the 8390 and HP chip reset. */
+ outw(inw(ioaddr + HPP_OPTION) & ~EnableIRQ, ioaddr + HPP_OPTION);
+
+ return 0;
+}
+
+static int
+hpp_open(struct device *dev)
+{
+ int ioaddr = dev->base_addr - NIC_OFFSET;
+ int option_reg;
+
+ if (request_irq(dev->irq, &ei_interrupt, 0, "hp-plus")) {
+ return -EAGAIN;
+ }
+
+ /* Reset the 8390 and HP chip. */
+ option_reg = inw(ioaddr + HPP_OPTION);
+ outw(option_reg & ~(NICReset + ChipReset), ioaddr + HPP_OPTION);
+ SLOW_DOWN_IO; SLOW_DOWN_IO;
+ /* Unreset the board and enable interrupts. */
+ outw(option_reg | (EnableIRQ + NICReset + ChipReset), ioaddr + HPP_OPTION);
+
+ /* Set the wrap registers for programmed-I/O operation. */
+ outw(HW_Page, ioaddr + HP_PAGING);
+ outw((HP_START_PG + TX_2X_PAGES) | ((HP_STOP_PG - 1) << 8), ioaddr + 14);
+
+ /* Select the operational page. */
+ outw(Perf_Page, ioaddr + HP_PAGING);
+
+ ei_open(dev);
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int
+hpp_close(struct device *dev)
+{
+ int ioaddr = dev->base_addr - NIC_OFFSET;
+ int option_reg = inw(ioaddr + HPP_OPTION);
+
+ free_irq(dev->irq);
+ irq2dev_map[dev->irq] = NULL;
+ ei_close(dev);
+ outw((option_reg & ~EnableIRQ) | MemDisable | NICReset | ChipReset,
+ ioaddr + HPP_OPTION);
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static void
+hpp_reset_8390(struct device *dev)
+{
+ int ioaddr = dev->base_addr - NIC_OFFSET;
+ int option_reg = inw(ioaddr + HPP_OPTION);
+
+ if (ei_debug > 1) printk("resetting the 8390 time=%ld...", jiffies);
+
+ outw(option_reg & ~(NICReset + ChipReset), ioaddr + HPP_OPTION);
+ /* Pause a few cycles for the hardware reset to take place. */
+ SLOW_DOWN_IO;
+ SLOW_DOWN_IO;
+ ei_status.txing = 0;
+ outw(option_reg | (EnableIRQ + NICReset + ChipReset), ioaddr + HPP_OPTION);
+
+ SLOW_DOWN_IO; SLOW_DOWN_IO;
+
+
+ if ((inb_p(ioaddr+NIC_OFFSET+EN0_ISR) & ENISR_RESET) == 0)
+ printk("%s: hp_reset_8390() did not complete.\n", dev->name);
+
+ if (ei_debug > 1) printk("8390 reset done (%ld).", jiffies);
+ return;
+}
+
+/* The programmed-I/O version of reading the 4 byte 8390 specific header.
+ Note that transfer with the EtherTwist+ must be on word boundaries. */
+
+static void
+hpp_io_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+ int ioaddr = dev->base_addr - NIC_OFFSET;
+
+ outw((ring_page<<8), ioaddr + HPP_IN_ADDR);
+ insw(ioaddr + HP_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr)>>1);
+}
+
+/* Block input and output, similar to the Crynwr packet driver. */
+
+static void
+hpp_io_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset)
+{
+ int ioaddr = dev->base_addr - NIC_OFFSET;
+ char *buf = skb->data;
+
+ outw(ring_offset, ioaddr + HPP_IN_ADDR);
+ insw(ioaddr + HP_DATAPORT, buf, count>>1);
+ if (count & 0x01)
+ buf[count-1] = inw(ioaddr + HP_DATAPORT);
+}
+
+/* The corresponding shared memory versions of the above 2 functions. */
+
+static void
+hpp_mem_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+ int ioaddr = dev->base_addr - NIC_OFFSET;
+ int option_reg = inw(ioaddr + HPP_OPTION);
+
+ outw((ring_page<<8), ioaddr + HPP_IN_ADDR);
+ outw(option_reg & ~(MemDisable + BootROMEnb), ioaddr + HPP_OPTION);
+ memcpy_fromio(hdr, dev->mem_start, sizeof(struct e8390_pkt_hdr));
+ outw(option_reg, ioaddr + HPP_OPTION);
+ hdr->count = (hdr->count + 3) & ~3; /* Round up allocation. */
+}
+
+static void
+hpp_mem_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset)
+{
+ int ioaddr = dev->base_addr - NIC_OFFSET;
+ int option_reg = inw(ioaddr + HPP_OPTION);
+
+ outw(ring_offset, ioaddr + HPP_IN_ADDR);
+
+ outw(option_reg & ~(MemDisable + BootROMEnb), ioaddr + HPP_OPTION);
+
+ /* Caution: this relies on get_8390_hdr() rounding up count!
+ Also note that we *can't* use eth_io_copy_and_sum() because
+ it will not always copy "count" bytes (e.g. padded IP). */
+
+ memcpy_fromio(skb->data, dev->mem_start, count);
+ outw(option_reg, ioaddr + HPP_OPTION);
+}
+
+/* A special note: we *must* always transfer >=16 bit words.
+ It's always safe to round up, so we do. */
+static void
+hpp_io_block_output(struct device *dev, int count,
+ const unsigned char *buf, const start_page)
+{
+ int ioaddr = dev->base_addr - NIC_OFFSET;
+ outw(start_page << 8, ioaddr + HPP_OUT_ADDR);
+ outsl(ioaddr + HP_DATAPORT, buf, (count+3)>>2);
+ return;
+}
+
+static void
+hpp_mem_block_output(struct device *dev, int count,
+ const unsigned char *buf, const start_page)
+{
+ int ioaddr = dev->base_addr - NIC_OFFSET;
+ int option_reg = inw(ioaddr + HPP_OPTION);
+
+ outw(start_page << 8, ioaddr + HPP_OUT_ADDR);
+ outw(option_reg & ~(MemDisable + BootROMEnb), ioaddr + HPP_OPTION);
+ memcpy_toio(dev->mem_start, buf, (count + 3) & ~3);
+ outw(option_reg, ioaddr + HPP_OPTION);
+
+ return;
+}
+
+
+#ifdef MODULE
+#define MAX_HPP_CARDS 4 /* Max number of HPP cards per module */
+#define NAMELEN 8 /* # of chars for storing dev->name */
+static char namelist[NAMELEN * MAX_HPP_CARDS] = { 0, };
+static struct device dev_hpp[MAX_HPP_CARDS] = {
+ {
+ NULL, /* assign a chunk of namelist[] below */
+ 0, 0, 0, 0,
+ 0, 0,
+ 0, 0, 0, NULL, NULL
+ },
+};
+
+static int io[MAX_HPP_CARDS] = { 0, };
+static int irq[MAX_HPP_CARDS] = { 0, };
+
+/* This is set up so that only a single autoprobe takes place per call.
+ISA device autoprobes on a running machine are not recommended. */
+int
+init_module(void)
+{
+ int this_dev, found = 0;
+
+ for (this_dev = 0; this_dev < MAX_HPP_CARDS; this_dev++) {
+ struct device *dev = &dev_hpp[this_dev];
+ dev->name = namelist+(NAMELEN*this_dev);
+ dev->irq = irq[this_dev];
+ dev->base_addr = io[this_dev];
+ dev->init = hp_plus_probe;
+ if (io[this_dev] == 0) {
+ if (this_dev != 0) break; /* only autoprobe 1st one */
+ printk(KERN_NOTICE "hp-plus.c: Presently autoprobing (not recommended) for a single card.\n");
+ }
+ if (register_netdev(dev) != 0) {
+ printk(KERN_WARNING "hp-plus.c: No HP-Plus card found (i/o = 0x%x).\n", io[this_dev]);
+ if (found != 0) return 0; /* Got at least one. */
+ return -ENXIO;
+ }
+ found++;
+ }
+
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ int this_dev;
+
+ for (this_dev = 0; this_dev < MAX_HPP_CARDS; this_dev++) {
+ struct device *dev = &dev_hpp[this_dev];
+ if (dev->priv != NULL) {
+ /* NB: hpp_close() handles free_irq + irq2dev map */
+ int ioaddr = dev->base_addr - NIC_OFFSET;
+ kfree(dev->priv);
+ dev->priv = NULL;
+ release_region(ioaddr, HP_IO_EXTENT);
+ unregister_netdev(dev);
+ }
+ }
+}
+#endif /* MODULE */
+
+/*
+ * Local variables:
+ * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c hp-plus.c"
+ * version-control: t
+ * kept-new-versions: 5
+ * tab-width: 4
+ * c-indent-level: 4
+ * End:
+ */
diff --git a/i386/i386at/gpl/linux/net/hp.c b/i386/i386at/gpl/linux/net/hp.c
new file mode 100644
index 00000000..d0443a7d
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/hp.c
@@ -0,0 +1,451 @@
+/* hp.c: A HP LAN ethernet driver for linux. */
+/*
+ Written 1993-94 by Donald Becker.
+
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency.
+
+ This software may be used and distributed according to the terms
+ of the GNU Public License, incorporated herein by reference.
+
+ The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
+ Center of Excellence in Space Data and Information Sciences
+ Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
+
+ This is a driver for the HP PC-LAN adaptors.
+
+ Sources:
+ The Crynwr packet driver.
+*/
+
+static const char *version =
+ "hp.c:v1.10 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+
+#include "8390.h"
+
+/* A zero-terminated list of I/O addresses to be probed. */
+static unsigned int hppclan_portlist[] =
+{ 0x300, 0x320, 0x340, 0x280, 0x2C0, 0x200, 0x240, 0};
+
+#define HP_IO_EXTENT 32
+
+#define HP_DATAPORT 0x0c /* "Remote DMA" data port. */
+#define HP_ID 0x07
+#define HP_CONFIGURE 0x08 /* Configuration register. */
+#define HP_RUN 0x01 /* 1 == Run, 0 == reset. */
+#define HP_IRQ 0x0E /* Mask for software-configured IRQ line. */
+#define HP_DATAON 0x10 /* Turn on dataport */
+#define NIC_OFFSET 0x10 /* Offset the 8390 registers. */
+
+#define HP_START_PG 0x00 /* First page of TX buffer */
+#define HP_8BSTOP_PG 0x80 /* Last page +1 of RX ring */
+#define HP_16BSTOP_PG 0xFF /* Same, for 16 bit cards. */
+
+int hp_probe(struct device *dev);
+int hp_probe1(struct device *dev, int ioaddr);
+
+static int hp_open(struct device *dev);
+static int hp_close(struct device *dev);
+static void hp_reset_8390(struct device *dev);
+static void hp_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
+ int ring_page);
+static void hp_block_input(struct device *dev, int count,
+ struct sk_buff *skb , int ring_offset);
+static void hp_block_output(struct device *dev, int count,
+ const unsigned char *buf, const start_page);
+
+static void hp_init_card(struct device *dev);
+
+/* The map from IRQ number to HP_CONFIGURE register setting. */
+/* My default is IRQ5 0 1 2 3 4 5 6 7 8 9 10 11 */
+static char irqmap[16] = { 0, 0, 4, 6, 8,10, 0,14, 0, 4, 2,12,0,0,0,0};
+
+
+/* Probe for an HP LAN adaptor.
+ Also initialize the card and fill in STATION_ADDR with the station
+ address. */
+#ifdef HAVE_DEVLIST
+struct netdev_entry netcard_drv =
+{"hp", hp_probe1, HP_IO_EXTENT, hppclan_portlist};
+#else
+
+int hp_probe(struct device *dev)
+{
+ int i;
+ int base_addr = dev ? dev->base_addr : 0;
+
+ if (base_addr > 0x1ff) /* Check a single specified location. */
+ return hp_probe1(dev, base_addr);
+ else if (base_addr != 0) /* Don't probe at all. */
+ return ENXIO;
+
+ for (i = 0; hppclan_portlist[i]; i++) {
+ int ioaddr = hppclan_portlist[i];
+ if (check_region(ioaddr, HP_IO_EXTENT))
+ continue;
+ if (hp_probe1(dev, ioaddr) == 0)
+ return 0;
+ }
+
+ return ENODEV;
+}
+#endif
+
+int hp_probe1(struct device *dev, int ioaddr)
+{
+ int i, board_id, wordmode;
+ const char *name;
+ static unsigned version_printed = 0;
+
+ /* Check for the HP physical address, 08 00 09 xx xx xx. */
+ /* This really isn't good enough: we may pick up HP LANCE boards
+ also! Avoid the lance 0x5757 signature. */
+ if (inb(ioaddr) != 0x08
+ || inb(ioaddr+1) != 0x00
+ || inb(ioaddr+2) != 0x09
+ || inb(ioaddr+14) == 0x57)
+ return ENODEV;
+
+ /* Set up the parameters based on the board ID.
+ If you have additional mappings, please mail them to me -djb. */
+ if ((board_id = inb(ioaddr + HP_ID)) & 0x80) {
+ name = "HP27247";
+ wordmode = 1;
+ } else {
+ name = "HP27250";
+ wordmode = 0;
+ }
+
+ /* We should have a "dev" from Space.c or the static module table. */
+ if (dev == NULL) {
+ printk("hp.c: Passed a NULL device.\n");
+ dev = init_etherdev(0, 0);
+ }
+
+ if (ei_debug && version_printed++ == 0)
+ printk(version);
+
+ printk("%s: %s (ID %02x) at %#3x,", dev->name, name, board_id, ioaddr);
+
+ for(i = 0; i < ETHER_ADDR_LEN; i++)
+ printk(" %2.2x", dev->dev_addr[i] = inb(ioaddr + i));
+
+ /* Snarf the interrupt now. Someday this could be moved to open(). */
+ if (dev->irq < 2) {
+ int irq_16list[] = { 11, 10, 5, 3, 4, 7, 9, 0};
+ int irq_8list[] = { 7, 5, 3, 4, 9, 0};
+ int *irqp = wordmode ? irq_16list : irq_8list;
+ do {
+ int irq = *irqp;
+ if (request_irq (irq, NULL, 0, "bogus") != -EBUSY) {
+ autoirq_setup(0);
+ /* Twinkle the interrupt, and check if it's seen. */
+ outb_p(irqmap[irq] | HP_RUN, ioaddr + HP_CONFIGURE);
+ outb_p( 0x00 | HP_RUN, ioaddr + HP_CONFIGURE);
+ if (irq == autoirq_report(0) /* It's a good IRQ line! */
+ && request_irq (irq, &ei_interrupt, 0, "hp") == 0) {
+ printk(" selecting IRQ %d.\n", irq);
+ dev->irq = *irqp;
+ break;
+ }
+ }
+ } while (*++irqp);
+ if (*irqp == 0) {
+ printk(" no free IRQ lines.\n");
+ return EBUSY;
+ }
+ } else {
+ if (dev->irq == 2)
+ dev->irq = 9;
+ if (request_irq(dev->irq, ei_interrupt, 0, "hp")) {
+ printk (" unable to get IRQ %d.\n", dev->irq);
+ return EBUSY;
+ }
+ }
+
+ /* Allocate dev->priv and fill in 8390 specific dev fields. */
+ if (ethdev_init(dev)) {
+ printk (" unable to get memory for dev->priv.\n");
+ free_irq(dev->irq);
+ return -ENOMEM;
+ }
+
+ /* Grab the region so we can find another board if something fails. */
+ request_region(ioaddr, HP_IO_EXTENT,"hp");
+
+ /* Set the base address to point to the NIC, not the "real" base! */
+ dev->base_addr = ioaddr + NIC_OFFSET;
+ dev->open = &hp_open;
+ dev->stop = &hp_close;
+
+ ei_status.name = name;
+ ei_status.word16 = wordmode;
+ ei_status.tx_start_page = HP_START_PG;
+ ei_status.rx_start_page = HP_START_PG + TX_PAGES;
+ ei_status.stop_page = wordmode ? HP_16BSTOP_PG : HP_8BSTOP_PG;
+
+ ei_status.reset_8390 = &hp_reset_8390;
+ ei_status.get_8390_hdr = &hp_get_8390_hdr;
+ ei_status.block_input = &hp_block_input;
+ ei_status.block_output = &hp_block_output;
+ hp_init_card(dev);
+
+ return 0;
+}
+
+static int
+hp_open(struct device *dev)
+{
+ ei_open(dev);
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int
+hp_close(struct device *dev)
+{
+ ei_close(dev);
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static void
+hp_reset_8390(struct device *dev)
+{
+ int hp_base = dev->base_addr - NIC_OFFSET;
+ int saved_config = inb_p(hp_base + HP_CONFIGURE);
+
+ if (ei_debug > 1) printk("resetting the 8390 time=%ld...", jiffies);
+ outb_p(0x00, hp_base + HP_CONFIGURE);
+ ei_status.txing = 0;
+ /* Pause just a few cycles for the hardware reset to take place. */
+ SLOW_DOWN_IO;
+ SLOW_DOWN_IO;
+
+ outb_p(saved_config, hp_base + HP_CONFIGURE);
+ SLOW_DOWN_IO; SLOW_DOWN_IO;
+
+ if ((inb_p(hp_base+NIC_OFFSET+EN0_ISR) & ENISR_RESET) == 0)
+ printk("%s: hp_reset_8390() did not complete.\n", dev->name);
+
+ if (ei_debug > 1) printk("8390 reset done (%ld).", jiffies);
+ return;
+}
+
+static void
+hp_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+ int nic_base = dev->base_addr;
+ int saved_config = inb_p(nic_base - NIC_OFFSET + HP_CONFIGURE);
+
+ outb_p(saved_config | HP_DATAON, nic_base - NIC_OFFSET + HP_CONFIGURE);
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base);
+ outb_p(sizeof(struct e8390_pkt_hdr), nic_base + EN0_RCNTLO);
+ outb_p(0, nic_base + EN0_RCNTHI);
+ outb_p(0, nic_base + EN0_RSARLO); /* On page boundary */
+ outb_p(ring_page, nic_base + EN0_RSARHI);
+ outb_p(E8390_RREAD+E8390_START, nic_base);
+
+ if (ei_status.word16)
+ insw(nic_base - NIC_OFFSET + HP_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr)>>1);
+ else
+ insb(nic_base - NIC_OFFSET + HP_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr));
+
+ outb_p(saved_config & (~HP_DATAON), nic_base - NIC_OFFSET + HP_CONFIGURE);
+}
+
+/* Block input and output, similar to the Crynwr packet driver. If you are
+ porting to a new ethercard look at the packet driver source for hints.
+ The HP LAN doesn't use shared memory -- we put the packet
+ out through the "remote DMA" dataport. */
+
+static void
+hp_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset)
+{
+ int nic_base = dev->base_addr;
+ int saved_config = inb_p(nic_base - NIC_OFFSET + HP_CONFIGURE);
+ int xfer_count = count;
+ char *buf = skb->data;
+
+ outb_p(saved_config | HP_DATAON, nic_base - NIC_OFFSET + HP_CONFIGURE);
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base);
+ outb_p(count & 0xff, nic_base + EN0_RCNTLO);
+ outb_p(count >> 8, nic_base + EN0_RCNTHI);
+ outb_p(ring_offset & 0xff, nic_base + EN0_RSARLO);
+ outb_p(ring_offset >> 8, nic_base + EN0_RSARHI);
+ outb_p(E8390_RREAD+E8390_START, nic_base);
+ if (ei_status.word16) {
+ insw(nic_base - NIC_OFFSET + HP_DATAPORT,buf,count>>1);
+ if (count & 0x01)
+ buf[count-1] = inb(nic_base - NIC_OFFSET + HP_DATAPORT), xfer_count++;
+ } else {
+ insb(nic_base - NIC_OFFSET + HP_DATAPORT, buf, count);
+ }
+ /* This is for the ALPHA version only, remove for later releases. */
+ if (ei_debug > 0) { /* DMA termination address check... */
+ int high = inb_p(nic_base + EN0_RSARHI);
+ int low = inb_p(nic_base + EN0_RSARLO);
+ int addr = (high << 8) + low;
+ /* Check only the lower 8 bits so we can ignore ring wrap. */
+ if (((ring_offset + xfer_count) & 0xff) != (addr & 0xff))
+ printk("%s: RX transfer address mismatch, %#4.4x vs. %#4.4x (actual).\n",
+ dev->name, ring_offset + xfer_count, addr);
+ }
+ outb_p(saved_config & (~HP_DATAON), nic_base - NIC_OFFSET + HP_CONFIGURE);
+}
+
+static void
+hp_block_output(struct device *dev, int count,
+ const unsigned char *buf, const start_page)
+{
+ int nic_base = dev->base_addr;
+ int saved_config = inb_p(nic_base - NIC_OFFSET + HP_CONFIGURE);
+
+ outb_p(saved_config | HP_DATAON, nic_base - NIC_OFFSET + HP_CONFIGURE);
+ /* Round the count up for word writes. Do we need to do this?
+ What effect will an odd byte count have on the 8390?
+ I should check someday. */
+ if (ei_status.word16 && (count & 0x01))
+ count++;
+ /* We should already be in page 0, but to be safe... */
+ outb_p(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base);
+
+#ifdef NE8390_RW_BUGFIX
+ /* Handle the read-before-write bug the same way as the
+ Crynwr packet driver -- the NatSemi method doesn't work. */
+ outb_p(0x42, nic_base + EN0_RCNTLO);
+ outb_p(0, nic_base + EN0_RCNTHI);
+ outb_p(0xff, nic_base + EN0_RSARLO);
+ outb_p(0x00, nic_base + EN0_RSARHI);
+#define NE_CMD 0x00
+ outb_p(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+ /* Make certain that the dummy read has occurred. */
+ inb_p(0x61);
+ inb_p(0x61);
+#endif
+
+ outb_p(count & 0xff, nic_base + EN0_RCNTLO);
+ outb_p(count >> 8, nic_base + EN0_RCNTHI);
+ outb_p(0x00, nic_base + EN0_RSARLO);
+ outb_p(start_page, nic_base + EN0_RSARHI);
+
+ outb_p(E8390_RWRITE+E8390_START, nic_base);
+ if (ei_status.word16) {
+ /* Use the 'rep' sequence for 16 bit boards. */
+ outsw(nic_base - NIC_OFFSET + HP_DATAPORT, buf, count>>1);
+ } else {
+ outsb(nic_base - NIC_OFFSET + HP_DATAPORT, buf, count);
+ }
+
+ /* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here -- it's broken! */
+
+ /* This is for the ALPHA version only, remove for later releases. */
+ if (ei_debug > 0) { /* DMA termination address check... */
+ int high = inb_p(nic_base + EN0_RSARHI);
+ int low = inb_p(nic_base + EN0_RSARLO);
+ int addr = (high << 8) + low;
+ if ((start_page << 8) + count != addr)
+ printk("%s: TX Transfer address mismatch, %#4.4x vs. %#4.4x.\n",
+ dev->name, (start_page << 8) + count, addr);
+ }
+ outb_p(saved_config & (~HP_DATAON), nic_base - NIC_OFFSET + HP_CONFIGURE);
+ return;
+}
+
+/* This function resets the ethercard if something screws up. */
+static void
+hp_init_card(struct device *dev)
+{
+ int irq = dev->irq;
+ NS8390_init(dev, 0);
+ outb_p(irqmap[irq&0x0f] | HP_RUN,
+ dev->base_addr - NIC_OFFSET + HP_CONFIGURE);
+ return;
+}
+
+#ifdef MODULE
+#define MAX_HP_CARDS 4 /* Max number of HP cards per module */
+#define NAMELEN 8 /* # of chars for storing dev->name */
+static char namelist[NAMELEN * MAX_HP_CARDS] = { 0, };
+static struct device dev_hp[MAX_HP_CARDS] = {
+ {
+ NULL, /* assign a chunk of namelist[] below */
+ 0, 0, 0, 0,
+ 0, 0,
+ 0, 0, 0, NULL, NULL
+ },
+};
+
+static int io[MAX_HP_CARDS] = { 0, };
+static int irq[MAX_HP_CARDS] = { 0, };
+
+/* This is set up so that only a single autoprobe takes place per call.
+ISA device autoprobes on a running machine are not recommended. */
+int
+init_module(void)
+{
+ int this_dev, found = 0;
+
+ for (this_dev = 0; this_dev < MAX_HP_CARDS; this_dev++) {
+ struct device *dev = &dev_hp[this_dev];
+ dev->name = namelist+(NAMELEN*this_dev);
+ dev->irq = irq[this_dev];
+ dev->base_addr = io[this_dev];
+ dev->init = hp_probe;
+ if (io[this_dev] == 0) {
+ if (this_dev != 0) break; /* only autoprobe 1st one */
+ printk(KERN_NOTICE "hp.c: Presently autoprobing (not recommended) for a single card.\n");
+ }
+ if (register_netdev(dev) != 0) {
+ printk(KERN_WARNING "hp.c: No HP card found (i/o = 0x%x).\n", io[this_dev]);
+ if (found != 0) return 0; /* Got at least one. */
+ return -ENXIO;
+ }
+ found++;
+ }
+
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ int this_dev;
+
+ for (this_dev = 0; this_dev < MAX_HP_CARDS; this_dev++) {
+ struct device *dev = &dev_hp[this_dev];
+ if (dev->priv != NULL) {
+ int ioaddr = dev->base_addr - NIC_OFFSET;
+ kfree(dev->priv);
+ dev->priv = NULL;
+ free_irq(dev->irq);
+ irq2dev_map[dev->irq] = NULL;
+ release_region(ioaddr, HP_IO_EXTENT);
+ unregister_netdev(dev);
+ }
+ }
+}
+#endif /* MODULE */
+
+/*
+ * Local variables:
+ * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c hp.c"
+ * version-control: t
+ * kept-new-versions: 5
+ * tab-width: 4
+ * c-indent-level: 4
+ * End:
+ */
diff --git a/i386/i386at/gpl/linux/net/hp100.c b/i386/i386at/gpl/linux/net/hp100.c
new file mode 100644
index 00000000..d8186bf1
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/hp100.c
@@ -0,0 +1,1144 @@
+/*
+ * hp100.c: Hewlett Packard HP10/100VG ANY LAN ethernet driver for Linux.
+ *
+ * Author: Jaroslav Kysela, <perex@pf.jcu.cz>
+ *
+ * Supports only the following Hewlett Packard cards:
+ *
+ * HP J2577 10/100 EISA card with REVA Cascade chip
+ * HP J2573 10/100 ISA card with REVA Cascade chip
+ * HP 27248B 10 only EISA card with Cascade chip
+ * HP J2577 10/100 EISA card with Cascade chip
+ * HP J2573 10/100 ISA card with Cascade chip
+ * HP J2585 10/100 PCI card
+ *
+ * Other ATT2MD01 Chip based boards might be supported in the future
+ * (there are some minor changes needed).
+ *
+ * This driver is based on the 'hpfepkt' crynwr packet driver.
+ *
+ * This source/code is public free; you can distribute it and/or modify
+ * it under terms of the GNU General Public License (published by the
+ * Free Software Foundation) either version two of this License, or any
+ * later version.
+ * ----------------------------------------------------------------------------
+ *
+ * Note: Some routines (interrupt handling, transmit) assumes that
+ * there is the PERFORMANCE page selected...
+ *
+ * ----------------------------------------------------------------------------
+ *
+ * If you are going to use the module version of this driver, you may
+ * change this values at the "insert time" :
+ *
+ * Variable Description
+ *
+ * hp100_rx_ratio Range 1-99 - onboard memory used for RX
+ * packets in %.
+ * hp100_priority_tx If this variable is nonzero - all outgoing
+ * packets will be transmitted as priority.
+ * hp100_port Adapter port (for example 0x380).
+ *
+ * ----------------------------------------------------------------------------
+ * MY BEST REGARDS GOING TO:
+ *
+ * IPEX s.r.o which lend me two HP J2573 cards and
+ * the HP AdvanceStack 100VG Hub-15 for debugging.
+ *
+ * Russel Nellson <nelson@crynwr.com> for help with obtaining sources
+ * of the 'hpfepkt' packet driver.
+ *
+ * Also thanks to Abacus Electric s.r.o which let me to use their
+ * motherboard for my second computer.
+ *
+ * ----------------------------------------------------------------------------
+ *
+ * TO DO:
+ * ======
+ * - ioctl handling - some runtime setup things
+ * - 100Mb/s Voice Grade AnyLAN network adapter/hub services support
+ * - 802.5 frames
+ * - promiscuous mode
+ * - bridge mode
+ * - cascaded repeater mode
+ * - 100Mbit MAC
+ *
+ * Revision history:
+ * =================
+ *
+ * Version Date Description
+ *
+ * 0.1 14-May-95 Initial writing. ALPHA code was released.
+ * Only HP J2573 on 10Mb/s (two machines) tested.
+ * 0.11 14-Jun-95 Reset interface bug fixed?
+ * Little bug in hp100_close function fixed.
+ * 100Mb/s connection debugged.
+ * 0.12 14-Jul-95 Link down is now handled better.
+ * 0.20 01-Aug-95 Added PCI support for HP J2585A card.
+ * Statistics bug fixed.
+ * 0.21 04-Aug-95 Memory mapped access support for PCI card.
+ * Added priority transmit support for 100Mb/s
+ * Voice Grade AnyLAN network.
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include <linux/types.h>
+#include <linux/config.h> /* for CONFIG_PCI */
+
+#include "hp100.h"
+
+/*
+ * defines
+ */
+
+#define HP100_BUS_ISA 0
+#define HP100_BUS_EISA 1
+#define HP100_BUS_PCI 2
+
+#define HP100_REGION_SIZE 0x20
+
+#define HP100_MAX_PACKET_SIZE (1536+4)
+#define HP100_MIN_PACKET_SIZE 60
+
+#ifndef HP100_DEFAULT_RX_RATIO
+/* default - 65% onboard memory on the card are used for RX packets */
+#define HP100_DEFAULT_RX_RATIO 65
+#endif
+
+#ifndef HP100_DEFAULT_PRIORITY_TX
+/* default - don't enable transmit outgoing packets as priority */
+#define HP100_DEFAULT_PRIORITY_TX 0
+#endif
+
+#ifdef MACH
+#define HP100_IO_MAPPED
+#endif
+
+/*
+ * structures
+ */
+
+struct hp100_eisa_id {
+ u_int id;
+ const char *name;
+ u_char bus;
+};
+
+struct hp100_private {
+ struct hp100_eisa_id *id;
+ u_short soft_model;
+ u_int memory_size;
+ u_short rx_ratio; /* 1 - 99 */
+ u_short priority_tx; /* != 0 - priority tx */
+ short mem_mapped; /* memory mapped access */
+ u_char *mem_ptr_virt; /* virtual memory mapped area, maybe NULL */
+ u_char *mem_ptr_phys; /* physical memory mapped area */
+ short lan_type; /* 10Mb/s, 100Mb/s or -1 (error) */
+ int hub_status; /* login to hub was successfull? */
+ u_char mac1_mode;
+ u_char mac2_mode;
+ struct enet_statistics stats;
+};
+
+/*
+ * variables
+ */
+
+static struct hp100_eisa_id hp100_eisa_ids[] = {
+
+ /* 10/100 EISA card with REVA Cascade chip */
+ { 0x080F1F022, "HP J2577 rev A", HP100_BUS_EISA },
+
+ /* 10/100 ISA card with REVA Cascade chip */
+ { 0x050F1F022, "HP J2573 rev A", HP100_BUS_ISA },
+
+ /* 10 only EISA card with Cascade chip */
+ { 0x02019F022, "HP 27248B", HP100_BUS_EISA },
+
+ /* 10/100 EISA card with Cascade chip */
+ { 0x04019F022, "HP J2577", HP100_BUS_EISA },
+
+ /* 10/100 ISA card with Cascade chip */
+ { 0x05019F022, "HP J2573", HP100_BUS_ISA },
+
+ /* 10/100 PCI card */
+ /* Note: ID for this card is same as PCI vendor/device numbers. */
+ { 0x01030103c, "HP J2585", HP100_BUS_PCI },
+};
+
+int hp100_rx_ratio = HP100_DEFAULT_RX_RATIO;
+int hp100_priority_tx = HP100_DEFAULT_PRIORITY_TX;
+
+/*
+ * prototypes
+ */
+
+static int hp100_probe1( struct device *dev, int ioaddr, int bus );
+static int hp100_open( struct device *dev );
+static int hp100_close( struct device *dev );
+static int hp100_start_xmit( struct sk_buff *skb, struct device *dev );
+static void hp100_rx( struct device *dev );
+static struct enet_statistics *hp100_get_stats( struct device *dev );
+static void hp100_update_stats( struct device *dev );
+static void hp100_clear_stats( int ioaddr );
+static void hp100_set_multicast_list( struct device *dev);
+static void hp100_interrupt( int irq, struct pt_regs *regs );
+
+static void hp100_start_interface( struct device *dev );
+static void hp100_stop_interface( struct device *dev );
+static void hp100_load_eeprom( struct device *dev );
+static int hp100_sense_lan( struct device *dev );
+static int hp100_login_to_vg_hub( struct device *dev );
+static int hp100_down_vg_link( struct device *dev );
+
+/*
+ * probe functions
+ */
+
+int hp100_probe( struct device *dev )
+{
+ int base_addr = dev ? dev -> base_addr : 0;
+ int ioaddr;
+#ifdef CONFIG_PCI
+ int pci_start_index = 0;
+#endif
+
+ if ( base_addr > 0xff ) /* Check a single specified location. */
+ {
+ if ( check_region( base_addr, HP100_REGION_SIZE ) ) return -EINVAL;
+ if ( base_addr < 0x400 )
+ return hp100_probe1( dev, base_addr, HP100_BUS_ISA );
+ else
+ return hp100_probe1( dev, base_addr, HP100_BUS_EISA );
+ }
+ else
+#ifdef CONFIG_PCI
+ if ( base_addr > 0 && base_addr < 8 + 1 )
+ pci_start_index = 0x100 | ( base_addr - 1 );
+ else
+#endif
+ if ( base_addr != 0 ) return -ENXIO;
+
+ /* at first - scan PCI bus(es) */
+
+#ifdef CONFIG_PCI
+ if ( pcibios_present() )
+ {
+ int pci_index;
+
+#ifdef HP100_DEBUG_PCI
+ printk( "hp100: PCI BIOS is present, checking for devices..\n" );
+#endif
+ for ( pci_index = pci_start_index & 7; pci_index < 8; pci_index++ )
+ {
+ u_char pci_bus, pci_device_fn;
+ u_short pci_command;
+
+ if ( pcibios_find_device( PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_J2585A,
+ pci_index, &pci_bus,
+ &pci_device_fn ) != 0 ) break;
+ pcibios_read_config_dword( pci_bus, pci_device_fn,
+ PCI_BASE_ADDRESS_0, &ioaddr );
+
+ ioaddr &= ~3; /* remove I/O space marker in bit 0. */
+
+ if ( check_region( ioaddr, HP100_REGION_SIZE ) ) continue;
+
+ pcibios_read_config_word( pci_bus, pci_device_fn,
+ PCI_COMMAND, &pci_command );
+ if ( !( pci_command & PCI_COMMAND_MASTER ) )
+ {
+#ifdef HP100_DEBUG_PCI
+ printk( "hp100: PCI Master Bit has not been set. Setting...\n" );
+#endif
+ pci_command |= PCI_COMMAND_MASTER;
+ pcibios_write_config_word( pci_bus, pci_device_fn,
+ PCI_COMMAND, pci_command );
+ }
+#ifdef HP100_DEBUG_PCI
+ printk( "hp100: PCI adapter found at 0x%x\n", ioaddr );
+#endif
+ if ( hp100_probe1( dev, ioaddr, HP100_BUS_PCI ) == 0 ) return 0;
+ }
+ }
+ if ( pci_start_index > 0 ) return -ENODEV;
+#endif /* CONFIG_PCI */
+
+ /* at second - probe all EISA possible port regions (if EISA bus present) */
+
+ for ( ioaddr = 0x1c38; EISA_bus && ioaddr < 0x10000; ioaddr += 0x400 )
+ {
+ if ( check_region( ioaddr, HP100_REGION_SIZE ) ) continue;
+ if ( hp100_probe1( dev, ioaddr, HP100_BUS_EISA ) == 0 ) return 0;
+ }
+
+ /* at third - probe all ISA possible port regions */
+
+ for ( ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x20 )
+ {
+ if ( check_region( ioaddr, HP100_REGION_SIZE ) ) continue;
+ if ( hp100_probe1( dev, ioaddr, HP100_BUS_ISA ) == 0 ) return 0;
+ }
+
+ return -ENODEV;
+}
+
+static int hp100_probe1( struct device *dev, int ioaddr, int bus )
+{
+ int i;
+ u_char uc, uc_1;
+ u_int eisa_id;
+ short mem_mapped;
+ u_char *mem_ptr_phys, *mem_ptr_virt;
+ struct hp100_private *lp;
+ struct hp100_eisa_id *eid;
+
+ if ( dev == NULL )
+ {
+#ifdef HP100_DEBUG
+ printk( "hp100_probe1: dev == NULL ?\n" );
+#endif
+ return EIO;
+ }
+
+ if ( bus != HP100_BUS_PCI ) /* don't check PCI cards again */
+ if ( inb( ioaddr + 0 ) != HP100_HW_ID_0 ||
+ inb( ioaddr + 1 ) != HP100_HW_ID_1 ||
+ ( inb( ioaddr + 2 ) & 0xf0 ) != HP100_HW_ID_2_REVA ||
+ inb( ioaddr + 3 ) != HP100_HW_ID_3 )
+ return -ENODEV;
+
+ dev -> base_addr = ioaddr;
+
+#ifdef HP100_DEBUG_PROBE1
+ printk( "hp100_probe1: card found at port 0x%x\n", ioaddr );
+#endif
+
+ hp100_page( ID_MAC_ADDR );
+ for ( i = uc = eisa_id = 0; i < 4; i++ )
+ {
+ eisa_id >>= 8;
+ uc_1 = hp100_inb( BOARD_ID + i );
+ eisa_id |= uc_1 << 24;
+ uc += uc_1;
+ }
+ uc += hp100_inb( BOARD_ID + 4 );
+
+#ifdef HP100_DEBUG_PROBE1
+ printk( "hp100_probe1: EISA ID = 0x%08x checksum = 0x%02x\n", eisa_id, uc );
+#endif
+
+ if ( uc != 0xff ) /* bad checksum? */
+ {
+ printk( "hp100_probe: bad EISA ID checksum at base port 0x%x\n", ioaddr );
+ return -ENODEV;
+ }
+
+ for ( i = 0; i < sizeof( hp100_eisa_ids ) / sizeof( struct hp100_eisa_id ); i++ )
+ if ( ( hp100_eisa_ids[ i ].id & 0xf0ffffff ) == ( eisa_id & 0xf0ffffff ) )
+ break;
+ if ( i >= sizeof( hp100_eisa_ids ) / sizeof( struct hp100_eisa_id ) )
+ {
+ printk( "hp100_probe1: card at port 0x%x isn't known (id = 0x%x)\n", ioaddr, eisa_id );
+ return -ENODEV;
+ }
+ eid = &hp100_eisa_ids[ i ];
+ if ( ( eid -> id & 0x0f000000 ) < ( eisa_id & 0x0f000000 ) )
+ {
+ printk( "hp100_probe1: newer version of card %s at port 0x%x - unsupported\n",
+ eid -> name, ioaddr );
+ return -ENODEV;
+ }
+
+ for ( i = uc = 0; i < 7; i++ )
+ uc += hp100_inb( LAN_ADDR + i );
+ if ( uc != 0xff )
+ {
+ printk( "hp100_probe1: bad lan address checksum (card %s at port 0x%x)\n",
+ eid -> name, ioaddr );
+ return -EIO;
+ }
+
+#ifndef HP100_IO_MAPPED
+ hp100_page( HW_MAP );
+ mem_mapped = ( hp100_inw( OPTION_LSW ) &
+ ( HP100_MEM_EN | HP100_BM_WRITE | HP100_BM_READ ) ) != 0;
+ mem_ptr_phys = mem_ptr_virt = NULL;
+ if ( mem_mapped )
+ {
+ mem_ptr_phys = (u_char *)( hp100_inw( MEM_MAP_LSW ) |
+ ( hp100_inw( MEM_MAP_MSW ) << 16 ) );
+ (u_int)mem_ptr_phys &= ~0x1fff; /* 8k aligment */
+ if ( bus == HP100_BUS_ISA && ( (u_long)mem_ptr_phys & ~0xfffff ) != 0 )
+ {
+ mem_ptr_phys = NULL;
+ mem_mapped = 0;
+ }
+ if ( mem_mapped && bus == HP100_BUS_PCI )
+ {
+ if ( ( mem_ptr_virt = vremap( (u_long)mem_ptr_phys, 0x2000 ) ) == NULL )
+ {
+ printk( "hp100: vremap for high PCI memory at 0x%lx failed\n", (u_long)mem_ptr_phys );
+ mem_ptr_phys = NULL;
+ mem_mapped = 0;
+ }
+ }
+ }
+#else
+ mem_mapped = 0;
+ mem_ptr_phys = mem_ptr_virt = NULL;
+#endif
+
+ if ( ( dev -> priv = kmalloc( sizeof( struct hp100_private ), GFP_KERNEL ) ) == NULL )
+ return -ENOMEM;
+ memset( dev -> priv, 0, sizeof( struct hp100_private ) );
+
+ lp = (struct hp100_private *)dev -> priv;
+ lp -> id = eid;
+ lp -> mem_mapped = mem_mapped;
+ lp -> mem_ptr_phys = mem_ptr_phys;
+ lp -> mem_ptr_virt = mem_ptr_virt;
+ hp100_page( ID_MAC_ADDR );
+ lp -> soft_model = hp100_inb( SOFT_MODEL );
+ lp -> mac1_mode = HP100_MAC1MODE3;
+ lp -> mac2_mode = HP100_MAC2MODE3;
+
+ dev -> base_addr = ioaddr;
+ hp100_page( HW_MAP );
+ dev -> irq = hp100_inb( IRQ_CHANNEL ) & HP100_IRQ_MASK;
+ if ( dev -> irq == 2 ) dev -> irq = 9;
+ lp -> memory_size = 0x200 << ( ( hp100_inb( SRAM ) & 0xe0 ) >> 5 );
+ lp -> rx_ratio = hp100_rx_ratio;
+
+ dev -> open = hp100_open;
+ dev -> stop = hp100_close;
+ dev -> hard_start_xmit = hp100_start_xmit;
+ dev -> get_stats = hp100_get_stats;
+ dev -> set_multicast_list = &hp100_set_multicast_list;
+
+ request_region( dev -> base_addr, HP100_REGION_SIZE, eid -> name );
+
+ hp100_page( ID_MAC_ADDR );
+ for ( i = uc = 0; i < 6; i++ )
+ dev -> dev_addr[ i ] = hp100_inb( LAN_ADDR + i );
+
+ hp100_clear_stats( ioaddr );
+
+ ether_setup( dev );
+
+ lp -> lan_type = hp100_sense_lan( dev );
+
+ printk( "%s: %s at 0x%x, IRQ %d, ",
+ dev -> name, lp -> id -> name, ioaddr, dev -> irq );
+ switch ( bus ) {
+ case HP100_BUS_EISA: printk( "EISA" ); break;
+ case HP100_BUS_PCI: printk( "PCI" ); break;
+ default: printk( "ISA" ); break;
+ }
+ printk( " bus, %dk SRAM (rx/tx %d%%).\n",
+ lp -> memory_size >> ( 10 - 4 ), lp -> rx_ratio );
+ if ( mem_mapped )
+ {
+ printk( "%s: Memory area at 0x%lx-0x%lx",
+ dev -> name, (u_long)mem_ptr_phys, (u_long)mem_ptr_phys + 0x1fff );
+ if ( mem_ptr_virt )
+ printk( " (virtual base 0x%lx)", (u_long)mem_ptr_virt );
+ printk( ".\n" );
+ }
+ printk( "%s: ", dev -> name );
+ if ( lp -> lan_type != HP100_LAN_ERR )
+ printk( "Adapter is attached to " );
+ switch ( lp -> lan_type ) {
+ case HP100_LAN_100:
+ printk( "100Mb/s Voice Grade AnyLAN network.\n" );
+ break;
+ case HP100_LAN_10:
+ printk( "10Mb/s network.\n" );
+ break;
+ default:
+ printk( "Warning! Link down.\n" );
+ }
+
+ hp100_stop_interface( dev );
+
+ return 0;
+}
+
+/*
+ * open/close functions
+ */
+
+static int hp100_open( struct device *dev )
+{
+ int i;
+ int ioaddr = dev -> base_addr;
+ struct hp100_private *lp = (struct hp100_private *)dev -> priv;
+
+ if ( request_irq( dev -> irq, hp100_interrupt, SA_INTERRUPT, lp -> id -> name ) )
+ {
+ printk( "%s: unable to get IRQ %d\n", dev -> name, dev -> irq );
+ return -EAGAIN;
+ }
+ irq2dev_map[ dev -> irq ] = dev;
+
+ MOD_INC_USE_COUNT;
+
+ dev -> tbusy = 0;
+ dev -> trans_start = jiffies;
+ dev -> interrupt = 0;
+ dev -> start = 1;
+
+ lp -> lan_type = hp100_sense_lan( dev );
+ lp -> mac1_mode = HP100_MAC1MODE3;
+ lp -> mac2_mode = HP100_MAC2MODE3;
+
+ hp100_page( MAC_CTRL );
+ hp100_orw( HP100_LINK_BEAT_DIS | HP100_RESET_LB, LAN_CFG_10 );
+
+ hp100_stop_interface( dev );
+ hp100_load_eeprom( dev );
+
+ hp100_outw( HP100_MMAP_DIS | HP100_SET_HB |
+ HP100_IO_EN | HP100_SET_LB, OPTION_LSW );
+ hp100_outw( HP100_DEBUG_EN | HP100_RX_HDR | HP100_EE_EN | HP100_RESET_HB |
+ HP100_FAKE_INT | HP100_RESET_LB, OPTION_LSW );
+ hp100_outw( HP100_ADV_NXT_PKT | HP100_TX_CMD | HP100_RESET_LB |
+ HP100_PRIORITY_TX | ( hp100_priority_tx ? HP100_SET_HB : HP100_RESET_HB ),
+ OPTION_MSW );
+
+ hp100_page( MAC_ADDRESS );
+ for ( i = 0; i < 6; i++ )
+ hp100_outb( dev -> dev_addr[ i ], MAC_ADDR + i );
+ for ( i = 0; i < 8; i++ ) /* setup multicast filter to receive all */
+ hp100_outb( 0xff, HASH_BYTE0 + i );
+ hp100_page( PERFORMANCE );
+ hp100_outw( 0xfefe, IRQ_MASK ); /* mask off all ints */
+ hp100_outw( 0xffff, IRQ_STATUS ); /* ack IRQ */
+ hp100_outw( (HP100_RX_PACKET | HP100_RX_ERROR | HP100_SET_HB) |
+ (HP100_TX_ERROR | HP100_SET_LB ), IRQ_MASK );
+ /* and enable few */
+ hp100_reset_card();
+ hp100_page( MMU_CFG );
+ hp100_outw( ( lp -> memory_size * lp -> rx_ratio ) / 100, RX_MEM_STOP );
+ hp100_outw( lp -> memory_size - 1, TX_MEM_STOP );
+ hp100_unreset_card();
+
+ if ( lp -> lan_type == HP100_LAN_100 )
+ lp -> hub_status = hp100_login_to_vg_hub( dev );
+
+ hp100_start_interface( dev );
+
+ return 0;
+}
+
+static int hp100_close( struct device *dev )
+{
+ int ioaddr = dev -> base_addr;
+ struct hp100_private *lp = (struct hp100_private *)dev -> priv;
+
+ hp100_page( PERFORMANCE );
+ hp100_outw( 0xfefe, IRQ_MASK ); /* mask off all IRQs */
+
+ hp100_stop_interface( dev );
+
+ if ( lp -> lan_type == HP100_LAN_100 ) /* relogin */
+ hp100_login_to_vg_hub( dev );
+
+ dev -> tbusy = 1;
+ dev -> start = 0;
+
+ free_irq( dev -> irq );
+ irq2dev_map[ dev -> irq ] = NULL;
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+/*
+ * transmit
+ */
+
+static int hp100_start_xmit( struct sk_buff *skb, struct device *dev )
+{
+ int i, ok_flag;
+ int ioaddr = dev -> base_addr;
+ u_short val;
+ struct hp100_private *lp = (struct hp100_private *)dev -> priv;
+
+ if ( lp -> lan_type < 0 )
+ {
+ hp100_stop_interface( dev );
+ if ( ( lp -> lan_type = hp100_sense_lan( dev ) ) < 0 )
+ {
+ printk( "%s: no connection found - check wire\n", dev -> name );
+ hp100_start_interface( dev ); /* 10Mb/s RX packets maybe handled */
+ return -EIO;
+ }
+ if ( lp -> lan_type == HP100_LAN_100 )
+ lp -> hub_status = hp100_login_to_vg_hub( dev );
+ hp100_start_interface( dev );
+ }
+
+ if ( ( i = ( hp100_inl( TX_MEM_FREE ) & ~0x7fffffff ) ) < skb -> len + 16 )
+ {
+#ifdef HP100_DEBUG
+ printk( "hp100_start_xmit: rx free mem = 0x%x\n", i );
+#endif
+ if ( jiffies - dev -> trans_start < 2 * HZ ) return -EAGAIN;
+ if ( lp -> lan_type == HP100_LAN_100 && lp -> hub_status < 0 )
+ /* 100Mb/s adapter isn't connected to hub */
+ {
+ printk( "%s: login to 100Mb/s hub retry\n", dev -> name );
+ hp100_stop_interface( dev );
+ lp -> hub_status = hp100_login_to_vg_hub( dev );
+ hp100_start_interface( dev );
+ }
+ else
+ {
+ hp100_ints_off();
+ i = hp100_sense_lan( dev );
+ hp100_page( PERFORMANCE );
+ hp100_ints_on();
+ if ( i == HP100_LAN_ERR )
+ printk( "%s: link down detected\n", dev -> name );
+ else
+ if ( lp -> lan_type != i )
+ {
+ /* it's very heavy - all network setting must be changed!!! */
+ printk( "%s: cable change 10Mb/s <-> 100Mb/s detected\n", dev -> name );
+ lp -> lan_type = i;
+ hp100_stop_interface( dev );
+ if ( lp -> lan_type == HP100_LAN_100 )
+ lp -> hub_status = hp100_login_to_vg_hub( dev );
+ hp100_start_interface( dev );
+ }
+ else
+ {
+ printk( "%s: interface reset\n", dev -> name );
+ hp100_stop_interface( dev );
+ hp100_start_interface( dev );
+ }
+ }
+ dev -> trans_start = jiffies;
+ return -EAGAIN;
+ }
+
+ if ( skb == NULL )
+ {
+ dev_tint( dev );
+ return 0;
+ }
+
+ if ( skb -> len <= 0 ) return 0;
+
+ for ( i = 0; i < 6000 && ( hp100_inw( OPTION_MSW ) & HP100_TX_CMD ); i++ )
+ {
+#ifdef HP100_DEBUG_TX
+ printk( "hp100_start_xmit: busy\n" );
+#endif
+ }
+
+ hp100_ints_off();
+ val = hp100_inw( IRQ_STATUS );
+ hp100_outw( val & HP100_TX_COMPLETE, IRQ_STATUS );
+#ifdef HP100_DEBUG_TX
+ printk( "hp100_start_xmit: irq_status = 0x%x, len = %d\n", val, (int)skb -> len );
+#endif
+ ok_flag = skb -> len >= HP100_MIN_PACKET_SIZE;
+ i = ok_flag ? skb -> len : HP100_MIN_PACKET_SIZE;
+ hp100_outw( i, DATA32 ); /* length to memory manager */
+ hp100_outw( i, FRAGMENT_LEN );
+ if ( lp -> mem_mapped )
+ {
+ if ( lp -> mem_ptr_virt )
+ {
+ memcpy( lp -> mem_ptr_virt, skb -> data, skb -> len );
+ if ( !ok_flag )
+ memset( lp -> mem_ptr_virt, 0, HP100_MIN_PACKET_SIZE - skb -> len );
+ }
+ else
+ {
+ memcpy_toio( lp -> mem_ptr_phys, skb -> data, skb -> len );
+ if ( !ok_flag )
+ memset_io( lp -> mem_ptr_phys, 0, HP100_MIN_PACKET_SIZE - skb -> len );
+ }
+ }
+ else
+ {
+ outsl( ioaddr + HP100_REG_DATA32, skb -> data, ( skb -> len + 3 ) >> 2 );
+ if ( !ok_flag )
+ for ( i = ( skb -> len + 3 ) & ~3; i < HP100_MIN_PACKET_SIZE; i += 4 )
+ hp100_outl( 0, DATA32 );
+ }
+ hp100_outw( HP100_TX_CMD | HP100_SET_LB, OPTION_MSW ); /* send packet */
+ lp -> stats.tx_packets++;
+ dev -> trans_start = jiffies;
+ hp100_ints_on();
+
+ dev_kfree_skb( skb, FREE_WRITE );
+
+#ifdef HP100_DEBUG_TX
+ printk( "hp100_start_xmit: end\n" );
+#endif
+
+ return 0;
+}
+
+/*
+ * receive - called from interrupt handler
+ */
+
+static void hp100_rx( struct device *dev )
+{
+ int packets, pkt_len;
+ int ioaddr = dev -> base_addr;
+ struct hp100_private *lp = (struct hp100_private *)dev -> priv;
+ u_int header;
+ struct sk_buff *skb;
+
+#if 0
+ if ( lp -> lan_type < 0 )
+ {
+ if ( ( lp -> lan_type = hp100_sense_lan( dev ) ) == HP100_LAN_100 )
+ lp -> hub_status = hp100_login_to_vg_hub( dev );
+ hp100_page( PERFORMANCE );
+ }
+#endif
+
+ packets = hp100_inb( RX_PKT_CNT );
+#ifdef HP100_DEBUG
+ if ( packets > 1 )
+ printk( "hp100_rx: waiting packets = %d\n", packets );
+#endif
+ while ( packets-- > 0 )
+ {
+ for ( pkt_len = 0; pkt_len < 6000 && ( hp100_inw( OPTION_MSW ) & HP100_ADV_NXT_PKT ); pkt_len++ )
+ {
+#ifdef HP100_DEBUG_TX
+ printk( "hp100_rx: busy, remaining packets = %d\n", packets );
+#endif
+ }
+ if ( lp -> mem_mapped )
+ {
+ if ( lp -> mem_ptr_virt )
+ header = *(__u32 *)lp -> mem_ptr_virt;
+ else
+ header = readl( lp -> mem_ptr_phys );
+ }
+ else
+ header = hp100_inl( DATA32 );
+ pkt_len = header & HP100_PKT_LEN_MASK;
+#ifdef HP100_DEBUG_RX
+ printk( "hp100_rx: new packet - length = %d, errors = 0x%x, dest = 0x%x\n",
+ header & HP100_PKT_LEN_MASK, ( header >> 16 ) & 0xfff8, ( header >> 16 ) & 7 );
+#endif
+ /*
+ * NOTE! This (and the skb_put() below) depends on the skb-functions
+ * allocating more than asked (notably, aligning the request up to
+ * the next 16-byte length).
+ */
+ skb = dev_alloc_skb( pkt_len );
+ if ( skb == NULL )
+ {
+#ifdef HP100_DEBUG
+ printk( "hp100_rx: couldn't allocate a sk_buff of size %d\n", pkt_len );
+#endif
+ lp -> stats.rx_dropped++;
+ }
+ else
+ {
+ u_char *ptr;
+
+ skb -> dev = dev;
+ ptr = (u_char *)skb_put( skb, pkt_len );
+ if ( lp -> mem_mapped )
+ {
+ if ( lp -> mem_ptr_virt )
+ memcpy( ptr, lp -> mem_ptr_virt, ( pkt_len + 3 ) & ~3 );
+ else
+ memcpy_fromio( ptr, lp -> mem_ptr_phys, ( pkt_len + 3 ) & ~3 );
+ }
+ else
+ insl( ioaddr + HP100_REG_DATA32, ptr, ( pkt_len + 3 ) >> 2 );
+ skb -> protocol = eth_type_trans( skb, dev );
+ netif_rx( skb );
+ lp -> stats.rx_packets++;
+#ifdef HP100_DEBUG_RX
+ printk( "rx: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ ptr[ 0 ], ptr[ 1 ], ptr[ 2 ], ptr[ 3 ], ptr[ 4 ], ptr[ 5 ],
+ ptr[ 6 ], ptr[ 7 ], ptr[ 8 ], ptr[ 9 ], ptr[ 10 ], ptr[ 11 ] );
+#endif
+ }
+ hp100_outw( HP100_ADV_NXT_PKT | HP100_SET_LB, OPTION_MSW );
+ switch ( header & 0x00070000 ) {
+ case (HP100_MULTI_ADDR_HASH<<16):
+ case (HP100_MULTI_ADDR_NO_HASH<<16):
+ lp -> stats.multicast++; break;
+ }
+ }
+#ifdef HP100_DEBUG_RX
+ printk( "hp100_rx: end\n" );
+#endif
+}
+
+/*
+ * statistics
+ */
+
+static struct enet_statistics *hp100_get_stats( struct device *dev )
+{
+ int ioaddr = dev -> base_addr;
+
+ hp100_ints_off();
+ hp100_update_stats( dev );
+ hp100_ints_on();
+ return &((struct hp100_private *)dev -> priv) -> stats;
+}
+
+static void hp100_update_stats( struct device *dev )
+{
+ int ioaddr = dev -> base_addr;
+ u_short val;
+ struct hp100_private *lp = (struct hp100_private *)dev -> priv;
+
+ hp100_page( MAC_CTRL ); /* get all statistics bytes */
+ val = hp100_inw( DROPPED ) & 0x0fff;
+ lp -> stats.rx_errors += val;
+ lp -> stats.rx_over_errors += val;
+ val = hp100_inb( CRC );
+ lp -> stats.rx_errors += val;
+ lp -> stats.rx_crc_errors += val;
+ val = hp100_inb( ABORT );
+ lp -> stats.tx_errors += val;
+ lp -> stats.tx_aborted_errors += val;
+ hp100_page( PERFORMANCE );
+}
+
+static void hp100_clear_stats( int ioaddr )
+{
+ cli();
+ hp100_page( MAC_CTRL ); /* get all statistics bytes */
+ hp100_inw( DROPPED );
+ hp100_inb( CRC );
+ hp100_inb( ABORT );
+ hp100_page( PERFORMANCE );
+ sti();
+}
+
+/*
+ * multicast setup
+ */
+
+/*
+ * Set or clear the multicast filter for this adapter.
+ */
+
+static void hp100_set_multicast_list( struct device *dev)
+{
+ int ioaddr = dev -> base_addr;
+ struct hp100_private *lp = (struct hp100_private *)dev -> priv;
+
+#ifdef HP100_DEBUG_MULTI
+ printk( "hp100_set_multicast_list: num_addrs = %d\n", dev->mc_count);
+#endif
+ cli();
+ hp100_ints_off();
+ hp100_page( MAC_CTRL );
+ hp100_andb( ~(HP100_RX_EN | HP100_TX_EN), MAC_CFG_1 ); /* stop rx/tx */
+
+ if ( dev->flags&IFF_PROMISC)
+ {
+ lp -> mac2_mode = HP100_MAC2MODE6; /* promiscuous mode, all good */
+ lp -> mac1_mode = HP100_MAC1MODE6; /* packets on the net */
+ }
+ else
+ if ( dev->mc_count || dev->flags&IFF_ALLMULTI )
+ {
+ lp -> mac2_mode = HP100_MAC2MODE5; /* multicast mode, packets for me */
+ lp -> mac1_mode = HP100_MAC1MODE5; /* broadcasts and all multicasts */
+ }
+ else
+ {
+ lp -> mac2_mode = HP100_MAC2MODE3; /* normal mode, packets for me */
+ lp -> mac1_mode = HP100_MAC1MODE3; /* and broadcasts */
+ }
+
+ hp100_outb( lp -> mac2_mode, MAC_CFG_2 );
+ hp100_andb( HP100_MAC1MODEMASK, MAC_CFG_1 );
+ hp100_orb( lp -> mac1_mode |
+ HP100_RX_EN | HP100_RX_IDLE | /* enable rx */
+ HP100_TX_EN | HP100_TX_IDLE, MAC_CFG_1 ); /* enable tx */
+ hp100_page( PERFORMANCE );
+ hp100_ints_on();
+ sti();
+}
+
+/*
+ * hardware interrupt handling
+ */
+
+static void hp100_interrupt( int irq, struct pt_regs *regs )
+{
+ struct device *dev = (struct device *)irq2dev_map[ irq ];
+ struct hp100_private *lp;
+ int ioaddr;
+ u_short val;
+
+ if ( dev == NULL ) return;
+ ioaddr = dev -> base_addr;
+ if ( dev -> interrupt )
+ printk( "%s: re-entering the interrupt handler\n", dev -> name );
+ hp100_ints_off();
+ dev -> interrupt = 1;
+ hp100_page( PERFORMANCE );
+ val = hp100_inw( IRQ_STATUS );
+#ifdef HP100_DEBUG_IRQ
+ printk( "hp100_interrupt: irq_status = 0x%x\n", val );
+#endif
+ if ( val & HP100_RX_PACKET )
+ {
+ hp100_rx( dev );
+ hp100_outw( HP100_RX_PACKET, IRQ_STATUS );
+ }
+ if ( val & (HP100_TX_SPACE_AVAIL | HP100_TX_COMPLETE) )
+ {
+ hp100_outw( val & (HP100_TX_SPACE_AVAIL | HP100_TX_COMPLETE), IRQ_STATUS );
+ }
+ if ( val & ( HP100_TX_ERROR | HP100_RX_ERROR ) )
+ {
+ lp = (struct hp100_private *)dev -> priv;
+ hp100_update_stats( dev );
+ hp100_outw( val & (HP100_TX_ERROR | HP100_RX_ERROR), IRQ_STATUS );
+ }
+#ifdef HP100_DEBUG_IRQ
+ printk( "hp100_interrupt: end\n" );
+#endif
+ dev -> interrupt = 0;
+ hp100_ints_on();
+}
+
+/*
+ * some misc functions
+ */
+
+static void hp100_start_interface( struct device *dev )
+{
+ int ioaddr = dev -> base_addr;
+ struct hp100_private *lp = (struct hp100_private *)dev -> priv;
+
+ cli();
+ hp100_unreset_card();
+ hp100_page( MAC_CTRL );
+ hp100_outb( lp -> mac2_mode, MAC_CFG_2 );
+ hp100_andb( HP100_MAC1MODEMASK, MAC_CFG_1 );
+ hp100_orb( lp -> mac1_mode |
+ HP100_RX_EN | HP100_RX_IDLE |
+ HP100_TX_EN | HP100_TX_IDLE, MAC_CFG_1 );
+ hp100_page( PERFORMANCE );
+ hp100_outw( HP100_INT_EN | HP100_SET_LB, OPTION_LSW );
+ hp100_outw( HP100_TRI_INT | HP100_RESET_HB, OPTION_LSW );
+ if ( lp -> mem_mapped )
+ {
+ /* enable memory mapping */
+ hp100_outw( HP100_MMAP_DIS | HP100_RESET_HB, OPTION_LSW );
+ }
+ sti();
+}
+
+static void hp100_stop_interface( struct device *dev )
+{
+ int ioaddr = dev -> base_addr;
+ u_short val;
+
+ hp100_outw( HP100_INT_EN | HP100_RESET_LB |
+ HP100_TRI_INT | HP100_MMAP_DIS | HP100_SET_HB, OPTION_LSW );
+ val = hp100_inw( OPTION_LSW );
+ hp100_page( HW_MAP );
+ hp100_andb( HP100_BM_SLAVE, BM );
+ hp100_page( MAC_CTRL );
+ hp100_andb( ~(HP100_RX_EN | HP100_TX_EN), MAC_CFG_1 );
+ if ( !(val & HP100_HW_RST) ) return;
+ for ( val = 0; val < 6000; val++ )
+ if ( ( hp100_inb( MAC_CFG_1 ) & (HP100_TX_IDLE | HP100_RX_IDLE) ) ==
+ (HP100_TX_IDLE | HP100_RX_IDLE) )
+ return;
+ printk( "%s: hp100_stop_interface - timeout\n", dev -> name );
+}
+
+static void hp100_load_eeprom( struct device *dev )
+{
+ int i;
+ int ioaddr = dev -> base_addr;
+
+ hp100_page( EEPROM_CTRL );
+ hp100_andw( ~HP100_EEPROM_LOAD, EEPROM_CTRL );
+ hp100_orw( HP100_EEPROM_LOAD, EEPROM_CTRL );
+ for ( i = 0; i < 6000; i++ )
+ if ( !( hp100_inw( OPTION_MSW ) & HP100_EE_LOAD ) ) return;
+ printk( "%s: hp100_load_eeprom - timeout\n", dev -> name );
+}
+
+/* return values: LAN_10, LAN_100 or LAN_ERR (not connected or hub is down)... */
+
+static int hp100_sense_lan( struct device *dev )
+{
+ int i;
+ int ioaddr = dev -> base_addr;
+ u_short val_VG, val_10;
+ struct hp100_private *lp = (struct hp100_private *)dev -> priv;
+
+ hp100_page( MAC_CTRL );
+ hp100_orw( HP100_VG_RESET, LAN_CFG_VG );
+ val_10 = hp100_inw( LAN_CFG_10 );
+ val_VG = hp100_inw( LAN_CFG_VG );
+#ifdef HP100_DEBUG_SENSE
+ printk( "hp100_sense_lan: val_VG = 0x%04x, val_10 = 0x%04x\n", val_VG, val_10 );
+#endif
+ if ( val_10 & HP100_LINK_BEAT_ST ) return HP100_LAN_10;
+ if ( lp -> id -> id == 0x02019F022 ) /* HP J27248B doesn't have 100Mb/s interface */
+ return HP100_LAN_ERR;
+ for ( i = 0; i < 2500; i++ )
+ {
+ val_VG = hp100_inw( LAN_CFG_VG );
+ if ( val_VG & HP100_LINK_CABLE_ST ) return HP100_LAN_100;
+ }
+ return HP100_LAN_ERR;
+}
+
+static int hp100_down_vg_link( struct device *dev )
+{
+ int ioaddr = dev -> base_addr;
+ unsigned long time;
+ int i;
+
+ hp100_page( MAC_CTRL );
+ for ( i = 2500; i > 0; i-- )
+ if ( hp100_inw( LAN_CFG_VG ) & HP100_LINK_CABLE_ST ) break;
+ if ( i <= 0 ) /* not signal - not logout */
+ return 0;
+ hp100_andw( ~HP100_LINK_CMD, LAN_CFG_VG );
+ time = jiffies + 10;
+ while ( time > jiffies )
+ if ( !( hp100_inw( LAN_CFG_VG ) & ( HP100_LINK_UP_ST |
+ HP100_LINK_CABLE_ST |
+ HP100_LINK_GOOD_ST ) ) )
+ return 0;
+#ifdef HP100_DEBUG
+ printk( "hp100_down_vg_link: timeout\n" );
+#endif
+ return -EIO;
+}
+
+static int hp100_login_to_vg_hub( struct device *dev )
+{
+ int i;
+ int ioaddr = dev -> base_addr;
+ u_short val;
+ unsigned long time;
+
+ hp100_page( MAC_CTRL );
+ hp100_orw( HP100_VG_RESET, LAN_CFG_VG );
+ time = jiffies + ( HZ / 2 );
+ do {
+ if ( hp100_inw( LAN_CFG_VG ) & HP100_LINK_CABLE_ST ) break;
+ } while ( time > jiffies );
+ if ( time <= jiffies )
+ {
+#ifdef HP100_DEBUG
+ printk( "hp100_login_to_vg_hub: timeout for link\n" );
+#endif
+ return -EIO;
+ }
+
+ if ( hp100_down_vg_link( dev ) < 0 ) /* if fail, try reset VG link */
+ {
+ hp100_andw( ~HP100_VG_RESET, LAN_CFG_VG );
+ hp100_orw( HP100_VG_RESET, LAN_CFG_VG );
+ }
+ /* bring up link */
+ hp100_orw( HP100_LOAD_ADDR | HP100_LINK_CMD, LAN_CFG_VG );
+ for ( i = 2500; i > 0; i-- )
+ if ( hp100_inw( LAN_CFG_VG ) & HP100_LINK_CABLE_ST ) break;
+ if ( i <= 0 )
+ {
+#ifdef HP100_DEBUG
+ printk( "hp100_login_to_vg_hub: timeout for link (bring up)\n" );
+#endif
+ goto down_link;
+ }
+
+ time = jiffies + ( HZ / 2 );
+ do {
+ val = hp100_inw( LAN_CFG_VG );
+ if ( ( val & ( HP100_LINK_UP_ST | HP100_LINK_GOOD_ST ) ) ==
+ ( HP100_LINK_UP_ST | HP100_LINK_GOOD_ST ) )
+ return 0; /* success */
+ } while ( time > jiffies );
+ if ( val & HP100_LINK_GOOD_ST )
+ printk( "%s: 100Mb cable training failed, check cable.\n", dev -> name );
+ else
+ printk( "%s: 100Mb node not accepted by hub, check frame type or security.\n", dev -> name );
+
+down_link:
+ hp100_down_vg_link( dev );
+ hp100_page( MAC_CTRL );
+ hp100_andw( ~( HP100_LOAD_ADDR | HP100_PROM_MODE ), LAN_CFG_VG );
+ hp100_orw( HP100_LINK_CMD, LAN_CFG_VG );
+ return -EIO;
+}
+
+/*
+ * module section
+ */
+
+#ifdef MODULE
+
+static int hp100_port = -1;
+
+static char devicename[9] = { 0, };
+static struct device dev_hp100 = {
+ devicename, /* device name is inserted by linux/drivers/net/net_init.c */
+ 0, 0, 0, 0,
+ 0, 0,
+ 0, 0, 0, NULL, hp100_probe
+};
+
+int init_module( void )
+{
+ if (hp100_port == 0 && !EISA_bus)
+ printk("HP100: You should not use auto-probing with insmod!\n");
+ if ( hp100_port > 0 )
+ dev_hp100.base_addr = hp100_port;
+ if ( register_netdev( &dev_hp100 ) != 0 )
+ return -EIO;
+ return 0;
+}
+
+void cleanup_module( void )
+{
+ unregister_netdev( &dev_hp100 );
+ release_region( dev_hp100.base_addr, HP100_REGION_SIZE );
+ if ( ((struct hp100_private *)dev_hp100.priv) -> mem_ptr_virt )
+ vfree( ((struct hp100_private *)dev_hp100.priv) -> mem_ptr_virt );
+ kfree_s( dev_hp100.priv, sizeof( struct hp100_private ) );
+ dev_hp100.priv = NULL;
+}
+
+#endif
diff --git a/i386/i386at/gpl/linux/net/hp100.h b/i386/i386at/gpl/linux/net/hp100.h
new file mode 100644
index 00000000..9f14f95a
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/hp100.h
@@ -0,0 +1,374 @@
+/*
+ * hp100.h: Hewlett Packard HP10/100VG ANY LAN ethernet driver for Linux.
+ *
+ * Author: Jaroslav Kysela, <perex@pf.jcu.cz>
+ *
+ * Header file...
+ *
+ * This driver is based on the 'hpfepkt' crynwr packet driver.
+ *
+ * This source/code is public free; you can distribute it and/or modify
+ * it under terms of the GNU General Public License (published by the
+ * Free Software Foundation) either version two of this License, or any
+ * later version.
+ */
+
+/****************************************************************************
+ * Hardware Constants
+ ****************************************************************************/
+
+/*
+ * ATT2MD01 Register Page Constants
+ */
+
+#define HP100_PAGE_PERFORMANCE 0x0 /* Page 0 */
+#define HP100_PAGE_MAC_ADDRESS 0x1 /* Page 1 */
+#define HP100_PAGE_HW_MAP 0x2 /* Page 2 */
+#define HP100_PAGE_EEPROM_CTRL 0x3 /* Page 3 */
+#define HP100_PAGE_MAC_CTRL 0x4 /* Page 4 */
+#define HP100_PAGE_MMU_CFG 0x5 /* Page 5 */
+#define HP100_PAGE_ID_MAC_ADDR 0x6 /* Page 6 */
+#define HP100_PAGE_MMU_POINTER 0x7 /* Page 7 */
+
+/*
+ * ATT2MD01 Register Addresses
+ */
+
+/* Present on all pages */
+
+#define HP100_REG_HW_ID 0x00 /* R: (16) Unique card ID */
+#define HP100_REG_TRACE 0x00 /* W: (16) Used for debug output */
+#define HP100_REG_PAGING 0x02 /* R: (16),15:4 Card ID */
+ /* W: (16),3:0 Switch pages */
+#define HP100_REG_OPTION_LSW 0x04 /* RW: (16) Select card functions */
+#define HP100_REG_OPTION_MSW 0x06 /* RW: (16) Select card functions */
+
+/* Page 0 - Performance */
+
+#define HP100_REG_IRQ_STATUS 0x08 /* RW: (16) Which ints are pending */
+#define HP100_REG_IRQ_MASK 0x0a /* RW: (16) Select ints to allow */
+#define HP100_REG_FRAGMENT_LEN 0x0c /* RW: (16)12:0 Current fragment len */
+#define HP100_REG_OFFSET 0x0e /* RW: (16)12:0 Offset to start read */
+#define HP100_REG_DATA32 0x10 /* RW: (32) I/O mode data port */
+#define HP100_REG_DATA16 0x12 /* RW: WORDs must be read from here */
+#define HP100_REG_TX_MEM_FREE 0x14 /* RD: (32) Amount of free Tx mem */
+#define HP100_REG_RX_PKT_CNT 0x18 /* RD: (8) Rx count of pkts on card */
+#define HP100_REG_TX_PKT_CNT 0x19 /* RD: (8) Tx count of pkts on card */
+
+/* Page 1 - MAC Address/Hash Table */
+
+#define HP100_REG_MAC_ADDR 0x08 /* RW: (8) Cards MAC address */
+#define HP100_REG_HASH_BYTE0 0x10 /* RW: (8) Cards multicast filter */
+
+/* Page 2 - Hardware Mapping */
+
+#define HP100_REG_MEM_MAP_LSW 0x08 /* RW: (16) LSW of cards mem addr */
+#define HP100_REG_MEM_MAP_MSW 0x0a /* RW: (16) MSW of cards mem addr */
+#define HP100_REG_IO_MAP 0x0c /* RW: (8) Cards I/O address */
+#define HP100_REG_IRQ_CHANNEL 0x0d /* RW: (8) IRQ and edge/level int */
+#define HP100_REG_SRAM 0x0e /* RW: (8) How much RAM on card */
+#define HP100_REG_BM 0x0f /* RW: (8) Controls BM functions */
+
+/* Page 3 - EEPROM/Boot ROM */
+
+#define HP100_REG_EEPROM_CTRL 0x08 /* RW: (16) Used to load EEPROM */
+
+/* Page 4 - LAN Configuration */
+
+#define HP100_REG_LAN_CFG_10 0x08 /* RW: (16) Set 10M XCVR functions */
+#define HP100_REG_LAN_CFG_VG 0x0a /* RW: (16) Set 100M XCVR functions */
+#define HP100_REG_MAC_CFG_1 0x0c /* RW: (8) Types of pkts to accept */
+#define HP100_REG_MAC_CFG_2 0x0d /* RW: (8) Misc MAC functions */
+/* The follow clear when read: */
+#define HP100_REG_DROPPED 0x10 /* R: (16),11:0 Pkts cant fit in mem*/
+#define HP100_REG_CRC 0x12 /* R: (8) Pkts with CRC */
+#define HP100_REG_ABORT 0x13 /* R: (8) Aborted Tx pkts */
+
+/* Page 5 - MMU */
+
+#define HP100_REG_RX_MEM_STOP 0x0c /* RW: (16) End of Rx ring addr */
+#define HP100_REG_TX_MEM_STOP 0x0e /* RW: (16) End of Tx ring addr */
+
+/* Page 6 - Card ID/Physical LAN Address */
+
+#define HP100_REG_BOARD_ID 0x08 /* R: (8) EISA/ISA card ID */
+#define HP100_REG_BOARD_IO_CHCK 0x0c /* R: (8) Added to ID to get FFh */
+#define HP100_REG_SOFT_MODEL 0x0d /* R: (8) Config program defined */
+#define HP100_REG_LAN_ADDR 0x10 /* R: (8) MAC addr of card */
+#define HP100_REG_LAN_ADDR_CHCK 0x16 /* R: (8) Added to addr to get FFh */
+
+/* Page 7 - MMU Current Pointers */
+
+#define HP100_REG_RX_MEM_BR 0x08 /* R: (16) Current begin of Rx ring */
+#define HP100_REG_RX_MEM_ER 0x0a /* R: (16) Current end of Rx ring */
+#define HP100_REG_TX_MEM_BR 0x0c /* R: (16) Current begin of Tx ring */
+#define HP100_REG_TX_MEM_ER 0x0e /* R: (16) Current end of Rx ring */
+#define HP100_REG_MEM_DEBUG 0x1a /* RW: (16) Used for memory tests */
+
+/*
+ * HardwareIDReg bits/masks
+ */
+
+#define HP100_HW_ID_0 0x50 /* Hardware ID bytes. */
+#define HP100_HW_ID_1 0x48
+#define HP100_HW_ID_2_REVA 0x50 /* Rev. A ID. NOTE: lower nibble not used */
+#define HP100_HW_ID_3 0x53
+
+/*
+ * OptionLSWReg bits/masks
+ */
+
+#define HP100_DEBUG_EN 0x8000 /* 0:Disable, 1:Enable Debug Dump Pointer */
+#define HP100_RX_HDR 0x4000 /* 0:Disable, 1:Enable putting pkt into */
+ /* system memory before Rx interrupt */
+#define HP100_MMAP_DIS 0x2000 /* 0:Enable, 1:Disable memory mapping. */
+ /* MMAP_DIS must be 0 and MEM_EN must */
+ /* be 1 for memory-mapped mode to be */
+ /* enabled */
+#define HP100_EE_EN 0x1000 /* 0:Disable,1:Enable EEPROM writing */
+#define HP100_BM_WRITE 0x0800 /* 0:Slave, 1:Bus Master for Tx data */
+#define HP100_BM_READ 0x0400 /* 0:Slave, 1:Bus Master for Rx data */
+#define HP100_TRI_INT 0x0200 /* 0:Dont, 1:Do tri-state the int */
+#define HP100_MEM_EN 0x0040 /* Config program set this to */
+ /* 0:Disable, 1:Enable mem map. */
+ /* See MMAP_DIS. */
+#define HP100_IO_EN 0x0020 /* 0:Disable, 1:Enable I/O transfers */
+#define HP100_BOOT_EN 0x0010 /* 0:Disable, 1:Enable boot ROM access */
+#define HP100_FAKE_INT 0x0008 /* 0:No int, 1:int */
+#define HP100_INT_EN 0x0004 /* 0:Disable, 1:Enable ints from card */
+#define HP100_HW_RST 0x0002 /* 0:Reset, 1:Out of reset */
+
+/*
+ * OptionMSWReg bits/masks
+ */
+#define HP100_PRIORITY_TX 0x0080 /* 0:Don't, 1:Do all Tx pkts as priority */
+#define HP100_EE_LOAD 0x0040 /* 1:EEPROM loading, 0 when done */
+#define HP100_ADV_NXT_PKT 0x0004 /* 1:Advance to next pkt in Rx queue, */
+ /* h/w will set to 0 when done */
+#define HP100_TX_CMD 0x0002 /* 1:Tell h/w download done, h/w will set */
+ /* to 0 when done */
+
+/*
+ * InterruptStatusReg/InterruptMaskReg bits/masks. These bits will 0 when a 1
+ * is written to them.
+ */
+#define HP100_RX_PACKET 0x0400 /* 0:No, 1:Yes pkt has been Rx */
+#define HP100_RX_ERROR 0x0200 /* 0:No, 1:Yes Rx pkt had error */
+#define HP100_TX_SPACE_AVAIL 0x0010 /* 0:<8192, 1:>=8192 Tx free bytes */
+#define HP100_TX_COMPLETE 0x0008 /* 0:No, 1:Yes a Tx has completed */
+#define HP100_TX_ERROR 0x0002 /* 0:No, 1:Yes Tx pkt had error */
+
+/*
+ * TxMemoryFreeCountReg bits/masks.
+ */
+#define HP100_AUTO_COMPARE 0x8000 /* Says at least 8k is available for Tx. */
+ /* NOTE: This mask is for the upper */
+ /* word of the register. */
+
+/*
+ * IRQChannelReg bits/masks.
+ */
+#define HP100_ZERO_WAIT_EN 0x80 /* 0:No, 1:Yes assers NOWS signal */
+#define HP100_LEVEL_IRQ 0x10 /* 0:Edge, 1:Level type interrupts. */
+ /* Only valid on EISA cards. */
+#define HP100_IRQ_MASK 0x0F /* Isolate the IRQ bits */
+
+/*
+ * SRAMReg bits/masks.
+ */
+#define HP100_RAM_SIZE_MASK 0xe0 /* AND to get SRAM size index */
+#define HP100_RAM_SIZE_SHIFT 0x05 /* Shift count to put index in lower bits */
+
+/*
+ * BMReg bits/masks.
+ */
+#define HP100_BM_SLAVE 0x04 /* 0:Slave, 1:BM mode */
+
+/*
+ * EEPROMControlReg bits/masks.
+ */
+#define HP100_EEPROM_LOAD 0x0001 /* 0->1 loads the EEPROM into registers. */
+ /* When it goes back to 0, load is */
+ /* complete. This should take ~600us. */
+
+/*
+ * LANCntrCfg10Reg bits/masks.
+ */
+#define HP100_SQU_ST 0x0100 /* 0:No, 1:Yes collision signal sent */
+ /* after Tx. Only used for AUI. */
+#define HP100_MAC10_SEL 0x00c0 /* Get bits to indicate MAC */
+#define HP100_AUI_SEL 0x0020 /* Status of AUI selection */
+#define HP100_LOW_TH 0x0010 /* 0:No, 1:Yes allow better cabling */
+#define HP100_LINK_BEAT_DIS 0x0008 /* 0:Enable, 1:Disable link beat */
+#define HP100_LINK_BEAT_ST 0x0004 /* 0:No, 1:Yes link beat being Rx */
+#define HP100_R_ROL_ST 0x0002 /* 0:No, 1:Yes Rx twisted pair has been */
+ /* reversed */
+#define HP100_AUI_ST 0x0001 /* 0:No, 1:Yes use AUI on TP card */
+
+/* MAC Selection, use with MAC10_SEL bits */
+#define HP100_AUTO_SEL_10 0x0 /* Auto select */
+#define HP100_XCVR_LXT901_10 0x1 /* LXT901 10BaseT transceiver */
+#define HP100_XCVR_7213 0x2 /* 7213 transceiver */
+#define HP100_XCVR_82503 0x3 /* 82503 transceiver */
+
+
+/*
+ * LANCntrCfgVGReg bits/masks.
+ */
+#define HP100_FRAME_FORMAT 0x0800 /* 0:802.3, 1:802.5 frames */
+#define HP100_BRIDGE 0x0400 /* 0:No, 1:Yes tell hub it's a bridge */
+#define HP100_PROM_MODE 0x0200 /* 0:No, 1:Yes tell hub card is */
+ /* promiscuous */
+#define HP100_REPEATER 0x0100 /* 0:No, 1:Yes tell hub MAC wants to be */
+ /* a cascaded repeater */
+#define HP100_MAC100_SEL 0x0080 /* 0:No, 1:Yes use 100 Mbit MAC */
+#define HP100_LINK_UP_ST 0x0040 /* 0:No, 1:Yes endnode logged in */
+#define HP100_LINK_CABLE_ST 0x0020 /* 0:No, 1:Yes cable can hear tones from */
+ /* hub */
+#define HP100_LOAD_ADDR 0x0010 /* 0->1 card addr will be sent to hub. */
+ /* 100ms later the link status bits are */
+ /* valid */
+#define HP100_LINK_CMD 0x0008 /* 0->1 link will attempt to log in. */
+ /* 100ms later the link status bits are */
+ /* valid */
+#define HP100_LINK_GOOD_ST 0x0002 /* 0:No, 1:Yes cable passed training */
+#define HP100_VG_RESET 0x0001 /* 0:Yes, 1:No reset the 100VG MAC */
+
+
+/*
+ * MACConfiguration1Reg bits/masks.
+ */
+#define HP100_RX_IDLE 0x80 /* 0:Yes, 1:No currently receiving pkts */
+#define HP100_TX_IDLE 0x40 /* 0:Yes, 1:No currently Txing pkts */
+#define HP100_RX_EN 0x20 /* 0:No, 1:Yes allow receiving of pkts */
+#define HP100_TX_EN 0x10 /* 0:No, 1:Yes allow transmiting of pkts */
+#define HP100_ACC_ERRORED 0x08 /* 0:No, 1:Yes allow Rx of errored pkts */
+#define HP100_ACC_MC 0x04 /* 0:No, 1:Yes allow Rx of multicast pkts */
+#define HP100_ACC_BC 0x02 /* 0:No, 1:Yes allow Rx of broadcast pkts */
+#define HP100_ACC_PHY 0x01 /* 0:No, 1:Yes allow Rx of ALL physical pkts */
+
+#define HP100_MAC1MODEMASK 0xf0 /* Hide ACC bits */
+#define HP100_MAC1MODE1 0x00 /* Receive nothing, must also disable RX */
+#define HP100_MAC1MODE2 0x00
+#define HP100_MAC1MODE3 HP100_MAC1MODE2 | HP100_ACC_BC
+#define HP100_MAC1MODE4 HP100_MAC1MODE3 | HP100_ACC_MC
+#define HP100_MAC1MODE5 HP100_MAC1MODE4 /* set mc hash to all ones also */
+#define HP100_MAC1MODE6 HP100_MAC1MODE5 | HP100_ACC_PHY /* Promiscuous */
+
+/* Note MODE6 will receive all GOOD packets on the LAN. This really needs
+ a mode 7 defined to be LAN Analyzer mode, which will receive errored and
+ runt packets, and keep the CRC bytes. */
+
+#define HP100_MAC1MODE7 MAC1MODE6 OR ACC_ERRORED
+
+/*
+ * MACConfiguration2Reg bits/masks.
+ */
+#define HP100_TR_MODE 0x80 /* 0:No, 1:Yes support Token Ring formats */
+#define HP100_TX_SAME 0x40 /* 0:No, 1:Yes Tx same packet continuous */
+#define HP100_LBK_XCVR 0x20 /* 0:No, 1:Yes loopback through MAC & */
+ /* transceiver */
+#define HP100_LBK_MAC 0x10 /* 0:No, 1:Yes loopback through MAC */
+#define HP100_CRC_I 0x08 /* 0:No, 1:Yes inhibit CRC on Tx packets */
+#define HP100_KEEP_CRC 0x02 /* 0:No, 1:Yes keep CRC on Rx packets. */
+ /* The length will reflect this. */
+
+#define HP100_MAC2MODEMASK 0x02
+#define HP100_MAC2MODE1 0x00
+#define HP100_MAC2MODE2 0x00
+#define HP100_MAC2MODE3 0x00
+#define HP100_MAC2MODE4 0x00
+#define HP100_MAC2MODE5 0x00
+#define HP100_MAC2MODE6 0x00
+#define HP100_MAC2MODE7 KEEP_CRC
+
+/*
+ * Set/Reset bits
+ */
+#define HP100_SET_HB 0x0100 /* 0:Set fields to 0 whose mask is 1 */
+#define HP100_SET_LB 0x0001 /* HB sets upper byte, LB sets lower byte */
+#define HP100_RESET_HB 0x0000 /* For readability when resetting bits */
+#define HP100_RESET_LB 0x0000 /* For readability when resetting bits */
+
+/*
+ * Misc. Constants
+ */
+#define HP100_LAN_100 100 /* lan_type value for VG */
+#define HP100_LAN_10 10 /* lan_type value for 10BaseT */
+#define HP100_LAN_ERR (-1) /* lan_type value for link down */
+
+/*
+ * Receive Header Definition.
+ */
+
+struct hp100_rx_header {
+ u_short rx_length; /* Pkt length is bits 12:0 */
+ u_short rx_status; /* status of the packet */
+};
+
+#define HP100_PKT_LEN_MASK 0x1FFF /* AND with RxLength to get length bits */
+
+/* Receive Packet Status. Note, the error bits are only valid if ACC_ERRORED
+ bit in the MAC Configuration Register 1 is set. */
+
+#define HP100_RX_PRI 0x8000 /* 0:No, 1:Yes packet is priority */
+#define HP100_SDF_ERR 0x4000 /* 0:No, 1:Yes start of frame error */
+#define HP100_SKEW_ERR 0x2000 /* 0:No, 1:Yes skew out of range */
+#define HP100_BAD_SYMBOL_ERR 0x1000 /* 0:No, 1:Yes invalid symbol received */
+#define HP100_RCV_IPM_ERR 0x0800 /* 0:No, 1:Yes pkt had an invalid packet */
+ /* marker */
+#define HP100_SYMBOL_BAL_ERR 0x0400 /* 0:No, 1:Yes symbol balance error */
+#define HP100_VG_ALN_ERR 0x0200 /* 0:No, 1:Yes non-octet received */
+#define HP100_TRUNC_ERR 0x0100 /* 0:No, 1:Yes the packet was truncated */
+#define HP100_RUNT_ERR 0x0040 /* 0:No, 1:Yes pkt length < Min Pkt */
+ /* Length Reg. */
+#define HP100_ALN_ERR 0x0010 /* 0:No, 1:Yes align error. */
+#define HP100_CRC_ERR 0x0008 /* 0:No, 1:Yes CRC occurred. */
+
+/* The last three bits indicate the type of destination address */
+
+#define HP100_MULTI_ADDR_HASH 0x0006 /* 110: Addr multicast, matched hash */
+#define HP100_BROADCAST_ADDR 0x0003 /* x11: Addr broadcast */
+#define HP100_MULTI_ADDR_NO_HASH 0x0002 /* 010: Addr multicast, didn't match hash */
+#define HP100_PHYS_ADDR_MATCH 0x0001 /* x01: Addr was physical and mine */
+#define HP100_PHYS_ADDR_NO_MATCH 0x0000 /* x00: Addr was physical but not mine */
+
+/*
+ * macros
+ */
+
+#define hp100_inb( reg ) \
+ inb( ioaddr + HP100_REG_##reg )
+#define hp100_inw( reg ) \
+ inw( ioaddr + HP100_REG_##reg )
+#define hp100_inl( reg ) \
+ inl( ioaddr + HP100_REG_##reg )
+#define hp100_outb( data, reg ) \
+ outb( data, ioaddr + HP100_REG_##reg )
+#define hp100_outw( data, reg ) \
+ outw( data, ioaddr + HP100_REG_##reg )
+#define hp100_outl( data, reg ) \
+ outl( data, ioaddr + HP100_REG_##reg )
+#define hp100_orb( data, reg ) \
+ outb( inb( ioaddr + HP100_REG_##reg ) | (data), ioaddr + HP100_REG_##reg )
+#define hp100_orw( data, reg ) \
+ outw( inw( ioaddr + HP100_REG_##reg ) | (data), ioaddr + HP100_REG_##reg )
+#define hp100_andb( data, reg ) \
+ outb( inb( ioaddr + HP100_REG_##reg ) & (data), ioaddr + HP100_REG_##reg )
+#define hp100_andw( data, reg ) \
+ outw( inw( ioaddr + HP100_REG_##reg ) & (data), ioaddr + HP100_REG_##reg )
+
+#define hp100_page( page ) \
+ outw( HP100_PAGE_##page, ioaddr + HP100_REG_PAGING )
+#define hp100_ints_off() \
+ outw( HP100_INT_EN | HP100_RESET_LB, ioaddr + HP100_REG_OPTION_LSW )
+#define hp100_ints_on() \
+ outw( HP100_INT_EN | HP100_SET_LB, ioaddr + HP100_REG_OPTION_LSW )
+#define hp100_mem_map_enable() \
+ outw( HP100_MMAP_DIS | HP100_RESET_HB, ioaddr + HP100_REG_OPTION_LSW )
+#define hp100_mem_map_disable() \
+ outw( HP100_MMAP_DIS | HP100_SET_HB, ioaddr + HP100_REG_OPTION_LSW )
+#define hp100_reset_card() \
+ outw( HP100_HW_RST | HP100_RESET_LB, ioaddr + HP100_REG_OPTION_LSW )
+#define hp100_unreset_card() \
+ outw( HP100_HW_RST | HP100_SET_LB, ioaddr + HP100_REG_OPTION_LSW )
diff --git a/i386/i386at/gpl/linux/net/i82586.h b/i386/i386at/gpl/linux/net/i82586.h
new file mode 100644
index 00000000..ff229e98
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/i82586.h
@@ -0,0 +1,408 @@
+/*
+ * Intel 82586 IEEE 802.3 Ethernet LAN Coprocessor.
+ *
+ * See:
+ * Intel Microcommunications 1991
+ * p1-1 to p1-37
+ * Intel order No. 231658
+ * ISBN 1-55512-119-5
+ *
+ * Unfortunately, the above chapter mentions neither
+ * the System Configuration Pointer (SCP) nor the
+ * Intermediate System Configuration Pointer (ISCP),
+ * so we probably need to look elsewhere for the
+ * whole story -- some recommend the "Intel LAN
+ * Components manual" but I have neither a copy
+ * nor a full reference. But "elsewhere" may be
+ * in the same publication...
+ * The description of a later device, the
+ * "82596CA High-Performance 32-Bit Local Area Network
+ * Coprocessor", (ibid. p1-38 to p1-109) does mention
+ * the SCP and ISCP and also has an i82586 compatibility
+ * mode. Even more useful is "AP-235 An 82586 Data Link
+ * Driver" (ibid. p1-337 to p1-417).
+ */
+
+#define I82586_MEMZ (64 * 1024)
+
+#define I82586_SCP_ADDR (I82586_MEMZ - sizeof(scp_t))
+
+#define ADDR_LEN 6
+#define I82586NULL 0xFFFF
+
+#define toff(t,p,f) (unsigned short)((void *)(&((t *)((void *)0 + (p)))->f) - (void *)0)
+
+/*
+ * System Configuration Pointer (SCP).
+ */
+typedef struct scp_t scp_t;
+struct scp_t
+{
+ unsigned short scp_sysbus; /* 82586 bus width: */
+#define SCP_SY_16BBUS (0x0 << 0) /* 16 bits */
+#define SCP_SY_8BBUS (0x1 << 0) /* 8 bits. */
+ unsigned short scp_junk[2]; /* Unused */
+ unsigned short scp_iscpl; /* lower 16 bits of ISCP_ADDR */
+ unsigned short scp_iscph; /* upper 16 bits of ISCP_ADDR */
+};
+
+/*
+ * Intermediate System Configuration Pointer (ISCP).
+ */
+typedef struct iscp_t iscp_t;
+struct iscp_t
+{
+ unsigned short iscp_busy; /* set by CPU before first CA, */
+ /* cleared by 82586 after read. */
+ unsigned short iscp_offset; /* offset of SCB */
+ unsigned short iscp_basel; /* base of SCB */
+ unsigned short iscp_baseh; /* " */
+};
+
+/*
+ * System Control Block (SCB).
+ * The 82586 writes its status to scb_status and then
+ * raises an interrupt to alert the CPU.
+ * The CPU writes a command to scb_command and
+ * then issues a Channel Attention (CA) to alert the 82586.
+ */
+typedef struct scb_t scb_t;
+struct scb_t
+{
+ unsigned short scb_status; /* Status of 82586 */
+#define SCB_ST_INT (0xF << 12) /* Some of: */
+#define SCB_ST_CX (0x1 << 15) /* Cmd completed */
+#define SCB_ST_FR (0x1 << 14) /* Frame received */
+#define SCB_ST_CNA (0x1 << 13) /* Cmd unit not active */
+#define SCB_ST_RNR (0x1 << 12) /* Rcv unit not ready */
+#define SCB_ST_JUNK0 (0x1 << 11) /* 0 */
+#define SCB_ST_CUS (0x7 << 8) /* Cmd unit status */
+#define SCB_ST_CUS_IDLE (0 << 8) /* Idle */
+#define SCB_ST_CUS_SUSP (1 << 8) /* Suspended */
+#define SCB_ST_CUS_ACTV (2 << 8) /* Active */
+#define SCB_ST_JUNK1 (0x1 << 7) /* 0 */
+#define SCB_ST_RUS (0x7 << 4) /* Rcv unit status */
+#define SCB_ST_RUS_IDLE (0 << 4) /* Idle */
+#define SCB_ST_RUS_SUSP (1 << 4) /* Suspended */
+#define SCB_ST_RUS_NRES (2 << 4) /* No resources */
+#define SCB_ST_RUS_RDY (4 << 4) /* Ready */
+ unsigned short scb_command; /* Next command */
+#define SCB_CMD_ACK_CX (0x1 << 15) /* Ack cmd completion */
+#define SCB_CMD_ACK_FR (0x1 << 14) /* Ack frame received */
+#define SCB_CMD_ACK_CNA (0x1 << 13) /* Ack CU not active */
+#define SCB_CMD_ACK_RNR (0x1 << 12) /* Ack RU not ready */
+#define SCB_CMD_JUNKX (0x1 << 11) /* Unused */
+#define SCB_CMD_CUC (0x7 << 8) /* Command Unit command */
+#define SCB_CMD_CUC_NOP (0 << 8) /* Nop */
+#define SCB_CMD_CUC_GO (1 << 8) /* Start cbl_offset */
+#define SCB_CMD_CUC_RES (2 << 8) /* Resume execution */
+#define SCB_CMD_CUC_SUS (3 << 8) /* Suspend " */
+#define SCB_CMD_CUC_ABT (4 << 8) /* Abort " */
+#define SCB_CMD_RESET (0x1 << 7) /* Reset chip (hardware) */
+#define SCB_CMD_RUC (0x7 << 4) /* Receive Unit command */
+#define SCB_CMD_RUC_NOP (0 << 4) /* Nop */
+#define SCB_CMD_RUC_GO (1 << 4) /* Start rfa_offset */
+#define SCB_CMD_RUC_RES (2 << 4) /* Resume reception */
+#define SCB_CMD_RUC_SUS (3 << 4) /* Suspend " */
+#define SCB_CMD_RUC_ABT (4 << 4) /* Abort " */
+ unsigned short scb_cbl_offset; /* Offset of first command unit */
+ /* Action Command */
+ unsigned short scb_rfa_offset; /* Offset of first Receive */
+ /* Frame Descriptor in the */
+ /* Receive Frame Area */
+ unsigned short scb_crcerrs; /* Properly aligned frames */
+ /* received with a CRC error */
+ unsigned short scb_alnerrs; /* Misaligned frames received */
+ /* with a CRC error */
+ unsigned short scb_rscerrs; /* Frames lost due to no space */
+ unsigned short scb_ovrnerrs; /* Frames lost due to slow bus */
+};
+
+#define scboff(p,f) toff(scb_t, p, f)
+
+/*
+ * The eight Action Commands.
+ */
+typedef enum acmd_e acmd_e;
+enum acmd_e
+{
+ acmd_nop = 0, /* Do nothing */
+ acmd_ia_setup = 1, /* Load an (ethernet) address into the */
+ /* 82586 */
+ acmd_configure = 2, /* Update the 82586 operating parameters */
+ acmd_mc_setup = 3, /* Load a list of (ethernet) multicast */
+ /* addresses into the 82586 */
+ acmd_transmit = 4, /* Transmit a frame */
+ acmd_tdr = 5, /* Perform a Time Domain Reflectometer */
+ /* test on the serial link */
+ acmd_dump = 6, /* Copy 82586 registers to memory */
+ acmd_diagnose = 7, /* Run an internal self test */
+};
+
+/*
+ * Generic Action Command header.
+ */
+typedef struct ach_t ach_t;
+struct ach_t
+{
+ unsigned short ac_status; /* Command status: */
+#define AC_SFLD_C (0x1 << 15) /* Command completed */
+#define AC_SFLD_B (0x1 << 14) /* Busy executing */
+#define AC_SFLD_OK (0x1 << 13) /* Completed error free */
+#define AC_SFLD_A (0x1 << 12) /* Command aborted */
+#define AC_SFLD_FAIL (0x1 << 11) /* Selftest failed */
+#define AC_SFLD_S10 (0x1 << 10) /* No carrier sense */
+ /* during transmission */
+#define AC_SFLD_S9 (0x1 << 9) /* Tx unsuccessful: */
+ /* (stopped) lost CTS */
+#define AC_SFLD_S8 (0x1 << 8) /* Tx unsuccessful: */
+ /* (stopped) slow DMA */
+#define AC_SFLD_S7 (0x1 << 7) /* Tx deferred: */
+ /* other link traffic */
+#define AC_SFLD_S6 (0x1 << 6) /* Heart Beat: collision */
+ /* detect after last tx */
+#define AC_SFLD_S5 (0x1 << 5) /* Tx stopped: */
+ /* excessive collisions */
+#define AC_SFLD_MAXCOL (0xF << 0) /* Collision count */
+ unsigned short ac_command; /* Command specifier: */
+#define AC_CFLD_EL (0x1 << 15) /* End of command list */
+#define AC_CFLD_S (0x1 << 14) /* Suspend on completion */
+#define AC_CFLD_I (0x1 << 13) /* Interrupt on completion */
+#define AC_CFLD_CMD (0x7 << 0) /* acmd_e */
+ unsigned short ac_link; /* Next Action Command */
+};
+
+#define acoff(p,f) toff(ach_t, p, f)
+
+/*
+ * The Nop Action Command.
+ */
+typedef struct ac_nop_t ac_nop_t;
+struct ac_nop_t
+{
+ ach_t nop_h;
+};
+
+/*
+ * The IA-Setup Action Command.
+ */
+typedef struct ac_ias_t ac_ias_t;
+struct ac_ias_t
+{
+ ach_t ias_h;
+ unsigned char ias_addr[ADDR_LEN]; /* The (ethernet) address */
+};
+
+/*
+ * The Configure Action Command.
+ */
+typedef struct ac_cfg_t ac_cfg_t;
+struct ac_cfg_t
+{
+ ach_t cfg_h;
+ unsigned char cfg_byte_cnt; /* Size foll data: 4-12 */
+#define AC_CFG_BYTE_CNT(v) (((v) & 0xF) << 0)
+ unsigned char cfg_fifolim; /* FIFO threshold */
+#define AC_CFG_FIFOLIM(v) (((v) & 0xF) << 0)
+ unsigned char cfg_byte8;
+#define AC_CFG_SAV_BF(v) (((v) & 0x1) << 7) /* Save rxd bad frames */
+#define AC_CFG_SRDY(v) (((v) & 0x1) << 6) /* SRDY/ARDY pin means */
+ /* external sync. */
+ unsigned char cfg_byte9;
+#define AC_CFG_ELPBCK(v) (((v) & 0x1) << 7) /* External loopback */
+#define AC_CFG_ILPBCK(v) (((v) & 0x1) << 6) /* Internal loopback */
+#define AC_CFG_PRELEN(v) (((v) & 0x3) << 4) /* Preamble length */
+#define AC_CFG_PLEN_2 0 /* 2 bytes */
+#define AC_CFG_PLEN_4 1 /* 4 bytes */
+#define AC_CFG_PLEN_8 2 /* 8 bytes */
+#define AC_CFG_PLEN_16 3 /* 16 bytes */
+#define AC_CFG_ALOC(v) (((v) & 0x1) << 3) /* Addr/len data is */
+ /* explicit in buffers */
+#define AC_CFG_ADDRLEN(v) (((v) & 0x7) << 0) /* Bytes per address */
+ unsigned char cfg_byte10;
+#define AC_CFG_BOFMET(v) (((v) & 0x1) << 7) /* Use alternate expo. */
+ /* backoff method */
+#define AC_CFG_ACR(v) (((v) & 0x7) << 4) /* Accelerated cont. res. */
+#define AC_CFG_LINPRIO(v) (((v) & 0x7) << 0) /* Linear priority */
+ unsigned char cfg_ifs; /* Interframe spacing */
+ unsigned char cfg_slotl; /* Slot time (low byte) */
+ unsigned char cfg_byte13;
+#define AC_CFG_RETRYNUM(v) (((v) & 0xF) << 4) /* Max. collision retry */
+#define AC_CFG_SLTTMHI(v) (((v) & 0x7) << 0) /* Slot time (high bits) */
+ unsigned char cfg_byte14;
+#define AC_CFG_FLGPAD(v) (((v) & 0x1) << 7) /* Pad with HDLC flags */
+#define AC_CFG_BTSTF(v) (((v) & 0x1) << 6) /* Do HDLC bitstuffing */
+#define AC_CFG_CRC16(v) (((v) & 0x1) << 5) /* 16 bit CCITT CRC */
+#define AC_CFG_NCRC(v) (((v) & 0x1) << 4) /* Insert no CRC */
+#define AC_CFG_TNCRS(v) (((v) & 0x1) << 3) /* Tx even if no carrier */
+#define AC_CFG_MANCH(v) (((v) & 0x1) << 2) /* Manchester coding */
+#define AC_CFG_BCDIS(v) (((v) & 0x1) << 1) /* Disable broadcast */
+#define AC_CFG_PRM(v) (((v) & 0x1) << 0) /* Promiscuous mode */
+ unsigned char cfg_byte15;
+#define AC_CFG_ICDS(v) (((v) & 0x1) << 7) /* Internal collision */
+ /* detect source */
+#define AC_CFG_CDTF(v) (((v) & 0x7) << 4) /* Collision detect */
+ /* filter in bit times */
+#define AC_CFG_ICSS(v) (((v) & 0x1) << 3) /* Internal carrier */
+ /* sense source */
+#define AC_CFG_CSTF(v) (((v) & 0x7) << 0) /* Carrier sense */
+ /* filter in bit times */
+ unsigned short cfg_min_frm_len;
+#define AC_CFG_MNFRM(v) (((v) & 0xFF) << 0) /* Min. bytes/frame (<= 255) */
+};
+
+/*
+ * The MC-Setup Action Command.
+ */
+typedef struct ac_mcs_t ac_mcs_t;
+struct ac_mcs_t
+{
+ ach_t mcs_h;
+ unsigned short mcs_cnt; /* No. of bytes of MC addresses */
+ unsigned short mcs_data[3]; /* The first MC address .. */
+};
+
+/*
+ * The Transmit Action Command.
+ */
+typedef struct ac_tx_t ac_tx_t;
+struct ac_tx_t
+{
+ ach_t tx_h;
+ unsigned short tx_tbd_offset; /* Address of list of buffers. */
+#if 0
+Linux packets are passed down with the destination MAC address
+and length/type field already prepended to the data,
+so we do not need to insert it. Consistent with this
+we must also set the AC_CFG_ALOC(..) flag during the
+ac_cfg_t action command.
+ unsigned char tx_addr[ADDR_LEN]; /* The frame dest. address */
+ unsigned short tx_length; /* The frame length */
+#endif /* 0 */
+};
+
+/*
+ * The Time Domain Reflectometer Action Command.
+ */
+typedef struct ac_tdr_t ac_tdr_t;
+struct ac_tdr_t
+{
+ ach_t tdr_h;
+ unsigned short tdr_result; /* Result. */
+#define AC_TDR_LNK_OK (0x1 << 15) /* No link problem */
+#define AC_TDR_XCVR_PRB (0x1 << 14) /* Txcvr cable problem */
+#define AC_TDR_ET_OPN (0x1 << 13) /* Open on the link */
+#define AC_TDR_ET_SRT (0x1 << 12) /* Short on the link */
+#define AC_TDR_TIME (0x7FF << 0) /* Distance to problem */
+ /* site in transmit */
+ /* clock cycles */
+};
+
+/*
+ * The Dump Action Command.
+ */
+typedef struct ac_dmp_t ac_dmp_t;
+struct ac_dmp_t
+{
+ ach_t dmp_h;
+ unsigned short dmp_offset; /* Result. */
+};
+
+/*
+ * Size of the result of the dump command.
+ */
+#define DUMPBYTES 170
+
+/*
+ * The Diagnose Action Command.
+ */
+typedef struct ac_dgn_t ac_dgn_t;
+struct ac_dgn_t
+{
+ ach_t dgn_h;
+};
+
+/*
+ * Transmit Buffer Descriptor (TBD).
+ */
+typedef struct tbd_t tbd_t;
+struct tbd_t
+{
+ unsigned short tbd_status; /* Written by the CPU */
+#define TBD_STATUS_EOF (0x1 << 15) /* This TBD is the */
+ /* last for this frame */
+#define TBD_STATUS_ACNT (0x3FFF << 0) /* Actual count of data */
+ /* bytes in this buffer */
+ unsigned short tbd_next_bd_offset; /* Next in list */
+ unsigned short tbd_bufl; /* Buffer address (low) */
+ unsigned short tbd_bufh; /* " " (high) */
+};
+
+/*
+ * Receive Buffer Descriptor (RBD).
+ */
+typedef struct rbd_t rbd_t;
+struct rbd_t
+{
+ unsigned short rbd_status; /* Written by the 82586 */
+#define RBD_STATUS_EOF (0x1 << 15) /* This RBD is the */
+ /* last for this frame */
+#define RBD_STATUS_F (0x1 << 14) /* ACNT field is valid */
+#define RBD_STATUS_ACNT (0x3FFF << 0) /* Actual no. of data */
+ /* bytes in this buffer */
+ unsigned short rbd_next_rbd_offset; /* Next rbd in list */
+ unsigned short rbd_bufl; /* Data pointer (low) */
+ unsigned short rbd_bufh; /* " " (high) */
+ unsigned short rbd_el_size; /* EL+Data buf. size */
+#define RBD_EL (0x1 << 15) /* This BD is the */
+ /* last in the list */
+#define RBD_SIZE (0x3FFF << 0) /* No. of bytes the */
+ /* buffer can hold */
+};
+
+#define rbdoff(p,f) toff(rbd_t, p, f)
+
+/*
+ * Frame Descriptor (FD).
+ */
+typedef struct fd_t fd_t;
+struct fd_t
+{
+ unsigned short fd_status; /* Written by the 82586 */
+#define FD_STATUS_C (0x1 << 15) /* Completed storing frame */
+#define FD_STATUS_B (0x1 << 14) /* FD was consumed by RU */
+#define FD_STATUS_OK (0x1 << 13) /* Frame rxd successfully */
+#define FD_STATUS_S11 (0x1 << 11) /* CRC error */
+#define FD_STATUS_S10 (0x1 << 10) /* Alignment error */
+#define FD_STATUS_S9 (0x1 << 9) /* Ran out of resources */
+#define FD_STATUS_S8 (0x1 << 8) /* Rx DMA overrun */
+#define FD_STATUS_S7 (0x1 << 7) /* Frame too short */
+#define FD_STATUS_S6 (0x1 << 6) /* No EOF flag */
+ unsigned short fd_command; /* Command */
+#define FD_COMMAND_EL (0x1 << 15) /* Last FD in list */
+#define FD_COMMAND_S (0x1 << 14) /* Suspend RU after rx */
+ unsigned short fd_link_offset; /* Next FD */
+ unsigned short fd_rbd_offset; /* First RBD (data) */
+ /* Prepared by CPU, */
+ /* updated by 82586 */
+#if 0
+I think the rest is unused since we
+have set AC_CFG_ALOC(..). However, just
+in case, we leave the space.
+#endif /* 0 */
+ unsigned char fd_dest[ADDR_LEN]; /* Destination address */
+ /* Written by 82586 */
+ unsigned char fd_src[ADDR_LEN]; /* Source address */
+ /* Written by 82586 */
+ unsigned short fd_length; /* Frame length or type */
+ /* Written by 82586 */
+};
+
+#define fdoff(p,f) toff(fd_t, p, f)
+
+/*
+ * This software may only be used and distributed
+ * according to the terms of the GNU Public License.
+ *
+ * For more details, see wavelan.c.
+ */
diff --git a/i386/i386at/gpl/linux/net/iow.h b/i386/i386at/gpl/linux/net/iow.h
new file mode 100644
index 00000000..6e15688f
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/iow.h
@@ -0,0 +1,6 @@
+#ifndef _ASM_IOW_H
+#define _ASM_IOW_H
+
+/* no longer used */
+
+#endif
diff --git a/i386/i386at/gpl/linux/net/lance.c b/i386/i386at/gpl/linux/net/lance.c
new file mode 100644
index 00000000..4a388f77
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/lance.c
@@ -0,0 +1,1129 @@
+/* lance.c: An AMD LANCE ethernet driver for linux. */
+/*
+ Written 1993,1994,1995 by Donald Becker.
+
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency.
+ This software may be used and distributed according to the terms
+ of the GNU Public License, incorporated herein by reference.
+
+ This driver is for the Allied Telesis AT1500 and HP J2405A, and should work
+ with most other LANCE-based bus-master (NE2100 clone) ethercards.
+
+ The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
+ Center of Excellence in Space Data and Information Sciences
+ Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
+*/
+
+static const char *version = "lance.c:v1.08 4/10/95 dplatt@3do.com\n";
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+static unsigned int lance_portlist[] = {0x300, 0x320, 0x340, 0x360, 0};
+void lance_probe1(int ioaddr);
+
+#ifdef HAVE_DEVLIST
+struct netdev_entry lance_drv =
+{"lance", lance_probe1, LANCE_TOTAL_SIZE, lance_portlist};
+#endif
+
+#ifdef LANCE_DEBUG
+int lance_debug = LANCE_DEBUG;
+#else
+int lance_debug = 1;
+#endif
+
+/*
+ Theory of Operation
+
+I. Board Compatibility
+
+This device driver is designed for the AMD 79C960, the "PCnet-ISA
+single-chip ethernet controller for ISA". This chip is used in a wide
+variety of boards from vendors such as Allied Telesis, HP, Kingston,
+and Boca. This driver is also intended to work with older AMD 7990
+designs, such as the NE1500 and NE2100, and newer 79C961. For convenience,
+I use the name LANCE to refer to all of the AMD chips, even though it properly
+refers only to the original 7990.
+
+II. Board-specific settings
+
+The driver is designed to work the boards that use the faster
+bus-master mode, rather than in shared memory mode. (Only older designs
+have on-board buffer memory needed to support the slower shared memory mode.)
+
+Most ISA boards have jumpered settings for the I/O base, IRQ line, and DMA
+channel. This driver probes the likely base addresses:
+{0x300, 0x320, 0x340, 0x360}.
+After the board is found it generates a DMA-timeout interrupt and uses
+autoIRQ to find the IRQ line. The DMA channel can be set with the low bits
+of the otherwise-unused dev->mem_start value (aka PARAM1). If unset it is
+probed for by enabling each free DMA channel in turn and checking if
+initialization succeeds.
+
+The HP-J2405A board is an exception: with this board it's easy to read the
+EEPROM-set values for the base, IRQ, and DMA. (Of course you must already
+_know_ the base address -- that field is for writing the EEPROM.)
+
+III. Driver operation
+
+IIIa. Ring buffers
+The LANCE uses ring buffers of Tx and Rx descriptors. Each entry describes
+the base and length of the data buffer, along with status bits. The length
+of these buffers is set by LANCE_LOG_{RX,TX}_BUFFERS, which is log_2() of
+the buffer length (rather than being directly the buffer length) for
+implementation ease. The current values are 2 (Tx) and 4 (Rx), which leads to
+ring sizes of 4 (Tx) and 16 (Rx). Increasing the number of ring entries
+needlessly uses extra space and reduces the chance that an upper layer will
+be able to reorder queued Tx packets based on priority. Decreasing the number
+of entries makes it more difficult to achieve back-to-back packet transmission
+and increases the chance that Rx ring will overflow. (Consider the worst case
+of receiving back-to-back minimum-sized packets.)
+
+The LANCE has the capability to "chain" both Rx and Tx buffers, but this driver
+statically allocates full-sized (slightly oversized -- PKT_BUF_SZ) buffers to
+avoid the administrative overhead. For the Rx side this avoids dynamically
+allocating full-sized buffers "just in case", at the expense of a
+memory-to-memory data copy for each packet received. For most systems this
+is a good tradeoff: the Rx buffer will always be in low memory, the copy
+is inexpensive, and it primes the cache for later packet processing. For Tx
+the buffers are only used when needed as low-memory bounce buffers.
+
+IIIB. 16M memory limitations.
+For the ISA bus master mode all structures used directly by the LANCE,
+the initialization block, Rx and Tx rings, and data buffers, must be
+accessible from the ISA bus, i.e. in the lower 16M of real memory.
+This is a problem for current Linux kernels on >16M machines. The network
+devices are initialized after memory initialization, and the kernel doles out
+memory from the top of memory downward. The current solution is to have a
+special network initialization routine that's called before memory
+initialization; this will eventually be generalized for all network devices.
+As mentioned before, low-memory "bounce-buffers" are used when needed.
+
+IIIC. Synchronization
+The driver runs as two independent, single-threaded flows of control. One
+is the send-packet routine, which enforces single-threaded use by the
+dev->tbusy flag. The other thread is the interrupt handler, which is single
+threaded by the hardware and other software.
+
+The send packet thread has partial control over the Tx ring and 'dev->tbusy'
+flag. It sets the tbusy flag whenever it's queuing a Tx packet. If the next
+queue slot is empty, it clears the tbusy flag when finished otherwise it sets
+the 'lp->tx_full' flag.
+
+The interrupt handler has exclusive control over the Rx ring and records stats
+from the Tx ring. (The Tx-done interrupt can't be selectively turned off, so
+we can't avoid the interrupt overhead by having the Tx routine reap the Tx
+stats.) After reaping the stats, it marks the queue entry as empty by setting
+the 'base' to zero. Iff the 'lp->tx_full' flag is set, it clears both the
+tx_full and tbusy flags.
+
+*/
+
+/* Set the number of Tx and Rx buffers, using Log_2(# buffers).
+ Reasonable default values are 4 Tx buffers, and 16 Rx buffers.
+ That translates to 2 (4 == 2^^2) and 4 (16 == 2^^4). */
+#ifndef LANCE_LOG_TX_BUFFERS
+#define LANCE_LOG_TX_BUFFERS 4
+#define LANCE_LOG_RX_BUFFERS 4
+#endif
+
+#define TX_RING_SIZE (1 << (LANCE_LOG_TX_BUFFERS))
+#define TX_RING_MOD_MASK (TX_RING_SIZE - 1)
+#define TX_RING_LEN_BITS ((LANCE_LOG_TX_BUFFERS) << 29)
+
+#define RX_RING_SIZE (1 << (LANCE_LOG_RX_BUFFERS))
+#define RX_RING_MOD_MASK (RX_RING_SIZE - 1)
+#define RX_RING_LEN_BITS ((LANCE_LOG_RX_BUFFERS) << 29)
+
+#define PKT_BUF_SZ 1544
+
+/* Offsets from base I/O address. */
+#define LANCE_DATA 0x10
+#define LANCE_ADDR 0x12
+#define LANCE_RESET 0x14
+#define LANCE_BUS_IF 0x16
+#define LANCE_TOTAL_SIZE 0x18
+
+/* The LANCE Rx and Tx ring descriptors. */
+struct lance_rx_head {
+ int base;
+ short buf_length; /* This length is 2s complement (negative)! */
+ short msg_length; /* This length is "normal". */
+};
+
+struct lance_tx_head {
+ int base;
+ short length; /* Length is 2s complement (negative)! */
+ short misc;
+};
+
+/* The LANCE initialization block, described in databook. */
+struct lance_init_block {
+ unsigned short mode; /* Pre-set mode (reg. 15) */
+ unsigned char phys_addr[6]; /* Physical ethernet address */
+ unsigned filter[2]; /* Multicast filter (unused). */
+ /* Receive and transmit ring base, along with extra bits. */
+ unsigned rx_ring; /* Tx and Rx ring base pointers */
+ unsigned tx_ring;
+};
+
+struct lance_private {
+ /* The Tx and Rx ring entries must be aligned on 8-byte boundaries.
+ This is always true for kmalloc'ed memory */
+ struct lance_rx_head rx_ring[RX_RING_SIZE];
+ struct lance_tx_head tx_ring[TX_RING_SIZE];
+ struct lance_init_block init_block;
+ const char *name;
+ /* The saved address of a sent-in-place packet/buffer, for skfree(). */
+ struct sk_buff* tx_skbuff[TX_RING_SIZE];
+ long rx_buffs; /* Address of Rx and Tx buffers. */
+ /* Tx low-memory "bounce buffer" address. */
+ char (*tx_bounce_buffs)[PKT_BUF_SZ];
+ int cur_rx, cur_tx; /* The next free ring entry */
+ int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */
+ int dma;
+ struct enet_statistics stats;
+ unsigned char chip_version; /* See lance_chip_type. */
+ char tx_full;
+ char lock;
+};
+
+#define LANCE_MUST_PAD 0x00000001
+#define LANCE_ENABLE_AUTOSELECT 0x00000002
+#define LANCE_MUST_REINIT_RING 0x00000004
+#define LANCE_MUST_UNRESET 0x00000008
+#define LANCE_HAS_MISSED_FRAME 0x00000010
+
+/* A mapping from the chip ID number to the part number and features.
+ These are from the datasheets -- in real life the '970 version
+ reportedly has the same ID as the '965. */
+static struct lance_chip_type {
+ int id_number;
+ const char *name;
+ int flags;
+} chip_table[] = {
+ {0x0000, "LANCE 7990", /* Ancient lance chip. */
+ LANCE_MUST_PAD + LANCE_MUST_UNRESET},
+ {0x0003, "PCnet/ISA 79C960", /* 79C960 PCnet/ISA. */
+ LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
+ LANCE_HAS_MISSED_FRAME},
+ {0x2260, "PCnet/ISA+ 79C961", /* 79C961 PCnet/ISA+, Plug-n-Play. */
+ LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
+ LANCE_HAS_MISSED_FRAME},
+ {0x2420, "PCnet/PCI 79C970", /* 79C970 or 79C974 PCnet-SCSI, PCI. */
+ LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
+ LANCE_HAS_MISSED_FRAME},
+ /* Bug: the PCnet/PCI actually uses the PCnet/VLB ID number, so just call
+ it the PCnet32. */
+ {0x2430, "PCnet32", /* 79C965 PCnet for VL bus. */
+ LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
+ LANCE_HAS_MISSED_FRAME},
+ {0x0, "PCnet (unknown)",
+ LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
+ LANCE_HAS_MISSED_FRAME},
+};
+
+enum {OLD_LANCE = 0, PCNET_ISA=1, PCNET_ISAP=2, PCNET_PCI=3, PCNET_VLB=4, LANCE_UNKNOWN=5};
+
+/* Non-zero only if the current card is a PCI with BIOS-set IRQ. */
+static unsigned char pci_irq_line = 0;
+
+/* Non-zero if lance_probe1() needs to allocate low-memory bounce buffers.
+ Assume yes until we know the memory size. */
+static unsigned char lance_need_isa_bounce_buffers = 1;
+
+static int lance_open(struct device *dev);
+static void lance_init_ring(struct device *dev);
+static int lance_start_xmit(struct sk_buff *skb, struct device *dev);
+static int lance_rx(struct device *dev);
+static void lance_interrupt(int irq, struct pt_regs *regs);
+static int lance_close(struct device *dev);
+static struct enet_statistics *lance_get_stats(struct device *dev);
+static void set_multicast_list(struct device *dev);
+
+
+
+/* This lance probe is unlike the other board probes in 1.0.*. The LANCE may
+ have to allocate a contiguous low-memory region for bounce buffers.
+ This requirement is satisfied by having the lance initialization occur
+ before the memory management system is started, and thus well before the
+ other probes. */
+
+int lance_init(void)
+{
+ int *port;
+
+ if (high_memory <= 16*1024*1024)
+ lance_need_isa_bounce_buffers = 0;
+
+#if defined(CONFIG_PCI)
+ if (pcibios_present()) {
+ int pci_index;
+ printk("lance.c: PCI bios is present, checking for devices...\n");
+ for (pci_index = 0; pci_index < 8; pci_index++) {
+ unsigned char pci_bus, pci_device_fn;
+ unsigned int pci_ioaddr;
+ unsigned short pci_command;
+
+ if (pcibios_find_device (PCI_VENDOR_ID_AMD,
+ PCI_DEVICE_ID_AMD_LANCE, pci_index,
+ &pci_bus, &pci_device_fn) != 0)
+ break;
+ pcibios_read_config_byte(pci_bus, pci_device_fn,
+ PCI_INTERRUPT_LINE, &pci_irq_line);
+ pcibios_read_config_dword(pci_bus, pci_device_fn,
+ PCI_BASE_ADDRESS_0, &pci_ioaddr);
+ /* Remove I/O space marker in bit 0. */
+ pci_ioaddr &= ~3;
+ /* PCI Spec 2.1 states that it is either the driver or PCI card's
+ * responsibility to set the PCI Master Enable Bit if needed.
+ * (From Mark Stockton <marks@schooner.sys.hou.compaq.com>)
+ */
+ pcibios_read_config_word(pci_bus, pci_device_fn,
+ PCI_COMMAND, &pci_command);
+ if ( ! (pci_command & PCI_COMMAND_MASTER)) {
+ printk("PCI Master Bit has not been set. Setting...\n");
+ pci_command |= PCI_COMMAND_MASTER;
+ pcibios_write_config_word(pci_bus, pci_device_fn,
+ PCI_COMMAND, pci_command);
+ }
+ printk("Found PCnet/PCI at %#x, irq %d.\n",
+ pci_ioaddr, pci_irq_line);
+ lance_probe1(pci_ioaddr);
+ pci_irq_line = 0;
+ }
+ }
+#endif /* defined(CONFIG_PCI) */
+
+ for (port = lance_portlist; *port; port++) {
+ int ioaddr = *port;
+
+ if ( check_region(ioaddr, LANCE_TOTAL_SIZE) == 0) {
+ /* Detect "normal" 0x57 0x57 and the NI6510EB 0x52 0x44
+ signatures w/ minimal I/O reads */
+ char offset15, offset14 = inb(ioaddr + 14);
+
+ if ((offset14 == 0x52 || offset14 == 0x57) &&
+ ((offset15 = inb(ioaddr + 15)) == 0x57 || offset15 == 0x44))
+ lance_probe1(ioaddr);
+ }
+ }
+
+ return 0;
+}
+
+void lance_probe1(int ioaddr)
+{
+ struct device *dev;
+ struct lance_private *lp;
+ short dma_channels; /* Mark spuriously-busy DMA channels */
+ int i, reset_val, lance_version;
+ const char *chipname;
+ /* Flags for specific chips or boards. */
+ unsigned char hpJ2405A = 0; /* HP ISA adaptor */
+ int hp_builtin = 0; /* HP on-board ethernet. */
+ static int did_version = 0; /* Already printed version info. */
+
+ /* First we look for special cases.
+ Check for HP's on-board ethernet by looking for 'HP' in the BIOS.
+ There are two HP versions, check the BIOS for the configuration port.
+ This method provided by L. Julliard, Laurent_Julliard@grenoble.hp.com.
+ */
+ if ( *((unsigned short *) 0x000f0102) == 0x5048) {
+ static const short ioaddr_table[] = { 0x300, 0x320, 0x340, 0x360};
+ int hp_port = ( *((unsigned char *) 0x000f00f1) & 1) ? 0x499 : 0x99;
+ /* We can have boards other than the built-in! Verify this is on-board. */
+ if ((inb(hp_port) & 0xc0) == 0x80
+ && ioaddr_table[inb(hp_port) & 3] == ioaddr)
+ hp_builtin = hp_port;
+ }
+ /* We also recognize the HP Vectra on-board here, but check below. */
+ hpJ2405A = (inb(ioaddr) == 0x08 && inb(ioaddr+1) == 0x00
+ && inb(ioaddr+2) == 0x09);
+
+ /* Reset the LANCE. */
+ reset_val = inw(ioaddr+LANCE_RESET); /* Reset the LANCE */
+
+ /* The Un-Reset needed is only needed for the real NE2100, and will
+ confuse the HP board. */
+ if (!hpJ2405A)
+ outw(reset_val, ioaddr+LANCE_RESET);
+
+ outw(0x0000, ioaddr+LANCE_ADDR); /* Switch to window 0 */
+ if (inw(ioaddr+LANCE_DATA) != 0x0004)
+ return;
+
+ /* Get the version of the chip. */
+ outw(88, ioaddr+LANCE_ADDR);
+ if (inw(ioaddr+LANCE_ADDR) != 88) {
+ lance_version = 0;
+ } else { /* Good, it's a newer chip. */
+ int chip_version = inw(ioaddr+LANCE_DATA);
+ outw(89, ioaddr+LANCE_ADDR);
+ chip_version |= inw(ioaddr+LANCE_DATA) << 16;
+ if (lance_debug > 2)
+ printk(" LANCE chip version is %#x.\n", chip_version);
+ if ((chip_version & 0xfff) != 0x003)
+ return;
+ chip_version = (chip_version >> 12) & 0xffff;
+ for (lance_version = 1; chip_table[lance_version].id_number; lance_version++) {
+ if (chip_table[lance_version].id_number == chip_version)
+ break;
+ }
+ }
+
+ dev = init_etherdev(0, 0);
+ chipname = chip_table[lance_version].name;
+ printk("%s: %s at %#3x,", dev->name, chipname, ioaddr);
+
+ /* There is a 16 byte station address PROM at the base address.
+ The first six bytes are the station address. */
+ for (i = 0; i < 6; i++)
+ printk(" %2.2x", dev->dev_addr[i] = inb(ioaddr + i));
+
+ dev->base_addr = ioaddr;
+ request_region(ioaddr, LANCE_TOTAL_SIZE, chip_table[lance_version].name);
+
+ /* Make certain the data structures used by the LANCE are aligned and DMAble. */
+ lp = (struct lance_private *) kmalloc(sizeof(*lp), GFP_DMA | GFP_KERNEL);
+ memset(lp, 0, sizeof(*lp));
+ dev->priv = lp;
+ lp->name = chipname;
+ lp->rx_buffs = (unsigned long) kmalloc(PKT_BUF_SZ*RX_RING_SIZE, GFP_DMA | GFP_KERNEL);
+ lp->tx_bounce_buffs = NULL;
+ if (lance_need_isa_bounce_buffers)
+ lp->tx_bounce_buffs = kmalloc(PKT_BUF_SZ*TX_RING_SIZE, GFP_DMA | GFP_KERNEL);
+
+ lp->chip_version = lance_version;
+
+ lp->init_block.mode = 0x0003; /* Disable Rx and Tx. */
+ for (i = 0; i < 6; i++)
+ lp->init_block.phys_addr[i] = dev->dev_addr[i];
+ lp->init_block.filter[0] = 0x00000000;
+ lp->init_block.filter[1] = 0x00000000;
+ lp->init_block.rx_ring = (int)lp->rx_ring | RX_RING_LEN_BITS;
+ lp->init_block.tx_ring = (int)lp->tx_ring | TX_RING_LEN_BITS;
+
+ outw(0x0001, ioaddr+LANCE_ADDR);
+ inw(ioaddr+LANCE_ADDR);
+ outw((short) (int) &lp->init_block, ioaddr+LANCE_DATA);
+ outw(0x0002, ioaddr+LANCE_ADDR);
+ inw(ioaddr+LANCE_ADDR);
+ outw(((int)&lp->init_block) >> 16, ioaddr+LANCE_DATA);
+ outw(0x0000, ioaddr+LANCE_ADDR);
+ inw(ioaddr+LANCE_ADDR);
+
+ if (pci_irq_line) {
+ dev->dma = 4; /* Native bus-master, no DMA channel needed. */
+ dev->irq = pci_irq_line;
+ } else if (hp_builtin) {
+ static const char dma_tbl[4] = {3, 5, 6, 0};
+ static const char irq_tbl[4] = {3, 4, 5, 9};
+ unsigned char port_val = inb(hp_builtin);
+ dev->dma = dma_tbl[(port_val >> 4) & 3];
+ dev->irq = irq_tbl[(port_val >> 2) & 3];
+ printk(" HP Vectra IRQ %d DMA %d.\n", dev->irq, dev->dma);
+ } else if (hpJ2405A) {
+ static const char dma_tbl[4] = {3, 5, 6, 7};
+ static const char irq_tbl[8] = {3, 4, 5, 9, 10, 11, 12, 15};
+ short reset_val = inw(ioaddr+LANCE_RESET);
+ dev->dma = dma_tbl[(reset_val >> 2) & 3];
+ dev->irq = irq_tbl[(reset_val >> 4) & 7];
+ printk(" HP J2405A IRQ %d DMA %d.\n", dev->irq, dev->dma);
+ } else if (lance_version == PCNET_ISAP) { /* The plug-n-play version. */
+ short bus_info;
+ outw(8, ioaddr+LANCE_ADDR);
+ bus_info = inw(ioaddr+LANCE_BUS_IF);
+ dev->dma = bus_info & 0x07;
+ dev->irq = (bus_info >> 4) & 0x0F;
+ } else {
+ /* The DMA channel may be passed in PARAM1. */
+ if (dev->mem_start & 0x07)
+ dev->dma = dev->mem_start & 0x07;
+ }
+
+ if (dev->dma == 0) {
+ /* Read the DMA channel status register, so that we can avoid
+ stuck DMA channels in the DMA detection below. */
+ dma_channels = ((inb(DMA1_STAT_REG) >> 4) & 0x0f) |
+ (inb(DMA2_STAT_REG) & 0xf0);
+ }
+ if (dev->irq >= 2)
+ printk(" assigned IRQ %d", dev->irq);
+ else {
+ /* To auto-IRQ we enable the initialization-done and DMA error
+ interrupts. For ISA boards we get a DMA error, but VLB and PCI
+ boards will work. */
+ autoirq_setup(0);
+
+ /* Trigger an initialization just for the interrupt. */
+ outw(0x0041, ioaddr+LANCE_DATA);
+
+ dev->irq = autoirq_report(1);
+ if (dev->irq)
+ printk(", probed IRQ %d", dev->irq);
+ else {
+ printk(", failed to detect IRQ line.\n");
+ return;
+ }
+
+ /* Check for the initialization done bit, 0x0100, which means
+ that we don't need a DMA channel. */
+ if (inw(ioaddr+LANCE_DATA) & 0x0100)
+ dev->dma = 4;
+ }
+
+ if (dev->dma == 4) {
+ printk(", no DMA needed.\n");
+ } else if (dev->dma) {
+ if (request_dma(dev->dma, chipname)) {
+ printk("DMA %d allocation failed.\n", dev->dma);
+ return;
+ } else
+ printk(", assigned DMA %d.\n", dev->dma);
+ } else { /* OK, we have to auto-DMA. */
+ for (i = 0; i < 4; i++) {
+ static const char dmas[] = { 5, 6, 7, 3 };
+ int dma = dmas[i];
+ int boguscnt;
+
+ /* Don't enable a permanently busy DMA channel, or the machine
+ will hang. */
+ if (test_bit(dma, &dma_channels))
+ continue;
+ outw(0x7f04, ioaddr+LANCE_DATA); /* Clear the memory error bits. */
+ if (request_dma(dma, chipname))
+ continue;
+ set_dma_mode(dma, DMA_MODE_CASCADE);
+ enable_dma(dma);
+
+ /* Trigger an initialization. */
+ outw(0x0001, ioaddr+LANCE_DATA);
+ for (boguscnt = 100; boguscnt > 0; --boguscnt)
+ if (inw(ioaddr+LANCE_DATA) & 0x0900)
+ break;
+ if (inw(ioaddr+LANCE_DATA) & 0x0100) {
+ dev->dma = dma;
+ printk(", DMA %d.\n", dev->dma);
+ break;
+ } else {
+ disable_dma(dma);
+ free_dma(dma);
+ }
+ }
+ if (i == 4) { /* Failure: bail. */
+ printk("DMA detection failed.\n");
+ return;
+ }
+ }
+
+ if (chip_table[lp->chip_version].flags & LANCE_ENABLE_AUTOSELECT) {
+ /* Turn on auto-select of media (10baseT or BNC) so that the user
+ can watch the LEDs even if the board isn't opened. */
+ outw(0x0002, ioaddr+LANCE_ADDR);
+ outw(0x0002, ioaddr+LANCE_BUS_IF);
+ }
+
+ if (lance_debug > 0 && did_version++ == 0)
+ printk(version);
+
+ /* The LANCE-specific entries in the device structure. */
+ dev->open = &lance_open;
+ dev->hard_start_xmit = &lance_start_xmit;
+ dev->stop = &lance_close;
+ dev->get_stats = &lance_get_stats;
+ dev->set_multicast_list = &set_multicast_list;
+
+ return;
+}
+
+
+static int
+lance_open(struct device *dev)
+{
+ struct lance_private *lp = (struct lance_private *)dev->priv;
+ int ioaddr = dev->base_addr;
+ int i;
+
+ if (dev->irq == 0 ||
+ request_irq(dev->irq, &lance_interrupt, 0, lp->name)) {
+ return -EAGAIN;
+ }
+
+ /* We used to allocate DMA here, but that was silly.
+ DMA lines can't be shared! We now permanently allocate them. */
+
+ irq2dev_map[dev->irq] = dev;
+
+ /* Reset the LANCE */
+ inw(ioaddr+LANCE_RESET);
+
+ /* The DMA controller is used as a no-operation slave, "cascade mode". */
+ if (dev->dma != 4) {
+ enable_dma(dev->dma);
+ set_dma_mode(dev->dma, DMA_MODE_CASCADE);
+ }
+
+ /* Un-Reset the LANCE, needed only for the NE2100. */
+ if (chip_table[lp->chip_version].flags & LANCE_MUST_UNRESET)
+ outw(0, ioaddr+LANCE_RESET);
+
+ if (chip_table[lp->chip_version].flags & LANCE_ENABLE_AUTOSELECT) {
+ /* This is 79C960-specific: Turn on auto-select of media (AUI, BNC). */
+ outw(0x0002, ioaddr+LANCE_ADDR);
+ outw(0x0002, ioaddr+LANCE_BUS_IF);
+ }
+
+ if (lance_debug > 1)
+ printk("%s: lance_open() irq %d dma %d tx/rx rings %#x/%#x init %#x.\n",
+ dev->name, dev->irq, dev->dma, (int) lp->tx_ring, (int) lp->rx_ring,
+ (int) &lp->init_block);
+
+ lance_init_ring(dev);
+ /* Re-initialize the LANCE, and start it when done. */
+ outw(0x0001, ioaddr+LANCE_ADDR);
+ outw((short) (int) &lp->init_block, ioaddr+LANCE_DATA);
+ outw(0x0002, ioaddr+LANCE_ADDR);
+ outw(((int)&lp->init_block) >> 16, ioaddr+LANCE_DATA);
+
+ outw(0x0004, ioaddr+LANCE_ADDR);
+ outw(0x0915, ioaddr+LANCE_DATA);
+
+ outw(0x0000, ioaddr+LANCE_ADDR);
+ outw(0x0001, ioaddr+LANCE_DATA);
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+ i = 0;
+ while (i++ < 100)
+ if (inw(ioaddr+LANCE_DATA) & 0x0100)
+ break;
+ /*
+ * We used to clear the InitDone bit, 0x0100, here but Mark Stockton
+ * reports that doing so triggers a bug in the '974.
+ */
+ outw(0x0042, ioaddr+LANCE_DATA);
+
+ if (lance_debug > 2)
+ printk("%s: LANCE open after %d ticks, init block %#x csr0 %4.4x.\n",
+ dev->name, i, (int) &lp->init_block, inw(ioaddr+LANCE_DATA));
+
+ return 0; /* Always succeed */
+}
+
+/* The LANCE has been halted for one reason or another (busmaster memory
+ arbitration error, Tx FIFO underflow, driver stopped it to reconfigure,
+ etc.). Modern LANCE variants always reload their ring-buffer
+ configuration when restarted, so we must reinitialize our ring
+ context before restarting. As part of this reinitialization,
+ find all packets still on the Tx ring and pretend that they had been
+ sent (in effect, drop the packets on the floor) - the higher-level
+ protocols will time out and retransmit. It'd be better to shuffle
+ these skbs to a temp list and then actually re-Tx them after
+ restarting the chip, but I'm too lazy to do so right now. dplatt@3do.com
+*/
+
+static void
+lance_purge_tx_ring(struct device *dev)
+{
+ struct lance_private *lp = (struct lance_private *)dev->priv;
+ int i;
+
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ if (lp->tx_skbuff[i]) {
+ dev_kfree_skb(lp->tx_skbuff[i],FREE_WRITE);
+ lp->tx_skbuff[i] = NULL;
+ }
+ }
+}
+
+
+/* Initialize the LANCE Rx and Tx rings. */
+static void
+lance_init_ring(struct device *dev)
+{
+ struct lance_private *lp = (struct lance_private *)dev->priv;
+ int i;
+
+ lp->lock = 0, lp->tx_full = 0;
+ lp->cur_rx = lp->cur_tx = 0;
+ lp->dirty_rx = lp->dirty_tx = 0;
+
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ lp->rx_ring[i].base = (lp->rx_buffs + i*PKT_BUF_SZ) | 0x80000000;
+ lp->rx_ring[i].buf_length = -PKT_BUF_SZ;
+ }
+ /* The Tx buffer address is filled in as needed, but we do need to clear
+ the upper ownership bit. */
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ lp->tx_ring[i].base = 0;
+ }
+
+ lp->init_block.mode = 0x0000;
+ for (i = 0; i < 6; i++)
+ lp->init_block.phys_addr[i] = dev->dev_addr[i];
+ lp->init_block.filter[0] = 0x00000000;
+ lp->init_block.filter[1] = 0x00000000;
+ lp->init_block.rx_ring = (int)lp->rx_ring | RX_RING_LEN_BITS;
+ lp->init_block.tx_ring = (int)lp->tx_ring | TX_RING_LEN_BITS;
+}
+
+static void
+lance_restart(struct device *dev, unsigned int csr0_bits, int must_reinit)
+{
+ struct lance_private *lp = (struct lance_private *)dev->priv;
+
+ if (must_reinit ||
+ (chip_table[lp->chip_version].flags & LANCE_MUST_REINIT_RING)) {
+ lance_purge_tx_ring(dev);
+ lance_init_ring(dev);
+ }
+ outw(0x0000, dev->base_addr + LANCE_ADDR);
+ outw(csr0_bits, dev->base_addr + LANCE_DATA);
+}
+
+static int
+lance_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+ struct lance_private *lp = (struct lance_private *)dev->priv;
+ int ioaddr = dev->base_addr;
+ int entry;
+ unsigned long flags;
+
+ /* Transmitter timeout, serious problems. */
+ if (dev->tbusy) {
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < 20)
+ return 1;
+ outw(0, ioaddr+LANCE_ADDR);
+ printk("%s: transmit timed out, status %4.4x, resetting.\n",
+ dev->name, inw(ioaddr+LANCE_DATA));
+ outw(0x0004, ioaddr+LANCE_DATA);
+ lp->stats.tx_errors++;
+#ifndef final_version
+ {
+ int i;
+ printk(" Ring data dump: dirty_tx %d cur_tx %d%s cur_rx %d.",
+ lp->dirty_tx, lp->cur_tx, lp->tx_full ? " (full)" : "",
+ lp->cur_rx);
+ for (i = 0 ; i < RX_RING_SIZE; i++)
+ printk("%s %08x %04x %04x", i & 0x3 ? "" : "\n ",
+ lp->rx_ring[i].base, -lp->rx_ring[i].buf_length,
+ lp->rx_ring[i].msg_length);
+ for (i = 0 ; i < TX_RING_SIZE; i++)
+ printk("%s %08x %04x %04x", i & 0x3 ? "" : "\n ",
+ lp->tx_ring[i].base, -lp->tx_ring[i].length,
+ lp->tx_ring[i].misc);
+ printk("\n");
+ }
+#endif
+ lance_restart(dev, 0x0043, 1);
+
+ dev->tbusy=0;
+ dev->trans_start = jiffies;
+
+ return 0;
+ }
+
+ if (skb == NULL) {
+ dev_tint(dev);
+ return 0;
+ }
+
+ if (skb->len <= 0)
+ return 0;
+
+ if (lance_debug > 3) {
+ outw(0x0000, ioaddr+LANCE_ADDR);
+ printk("%s: lance_start_xmit() called, csr0 %4.4x.\n", dev->name,
+ inw(ioaddr+LANCE_DATA));
+ outw(0x0000, ioaddr+LANCE_DATA);
+ }
+
+ /* Block a timer-based transmit from overlapping. This could better be
+ done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
+ if (set_bit(0, (void*)&dev->tbusy) != 0) {
+ printk("%s: Transmitter access conflict.\n", dev->name);
+ return 1;
+ }
+
+ if (set_bit(0, (void*)&lp->lock) != 0) {
+ if (lance_debug > 0)
+ printk("%s: tx queue lock!.\n", dev->name);
+ /* don't clear dev->tbusy flag. */
+ return 1;
+ }
+
+ /* Fill in a Tx ring entry */
+
+ /* Mask to ring buffer boundary. */
+ entry = lp->cur_tx & TX_RING_MOD_MASK;
+
+ /* Caution: the write order is important here, set the base address
+ with the "ownership" bits last. */
+
+ /* The old LANCE chips doesn't automatically pad buffers to min. size. */
+ if (chip_table[lp->chip_version].flags & LANCE_MUST_PAD) {
+ lp->tx_ring[entry].length =
+ -(ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN);
+ } else
+ lp->tx_ring[entry].length = -skb->len;
+
+ lp->tx_ring[entry].misc = 0x0000;
+
+ /* If any part of this buffer is >16M we must copy it to a low-memory
+ buffer. */
+ if ((int)(skb->data) + skb->len > 0x01000000) {
+ if (lance_debug > 5)
+ printk("%s: bouncing a high-memory packet (%#x).\n",
+ dev->name, (int)(skb->data));
+ memcpy(&lp->tx_bounce_buffs[entry], skb->data, skb->len);
+ lp->tx_ring[entry].base =
+ (int)(lp->tx_bounce_buffs + entry) | 0x83000000;
+ dev_kfree_skb (skb, FREE_WRITE);
+ } else {
+ lp->tx_skbuff[entry] = skb;
+ lp->tx_ring[entry].base = (int)(skb->data) | 0x83000000;
+ }
+ lp->cur_tx++;
+
+ /* Trigger an immediate send poll. */
+ outw(0x0000, ioaddr+LANCE_ADDR);
+ outw(0x0048, ioaddr+LANCE_DATA);
+
+ dev->trans_start = jiffies;
+
+ save_flags(flags);
+ cli();
+ lp->lock = 0;
+ if (lp->tx_ring[(entry+1) & TX_RING_MOD_MASK].base == 0)
+ dev->tbusy=0;
+ else
+ lp->tx_full = 1;
+ restore_flags(flags);
+
+ return 0;
+}
+
+/* The LANCE interrupt handler. */
+static void
+lance_interrupt(int irq, struct pt_regs * regs)
+{
+ struct device *dev = (struct device *)(irq2dev_map[irq]);
+ struct lance_private *lp;
+ int csr0, ioaddr, boguscnt=10;
+ int must_restart;
+
+ if (dev == NULL) {
+ printk ("lance_interrupt(): irq %d for unknown device.\n", irq);
+ return;
+ }
+
+ ioaddr = dev->base_addr;
+ lp = (struct lance_private *)dev->priv;
+ if (dev->interrupt)
+ printk("%s: Re-entering the interrupt handler.\n", dev->name);
+
+ dev->interrupt = 1;
+
+ outw(0x00, dev->base_addr + LANCE_ADDR);
+ while ((csr0 = inw(dev->base_addr + LANCE_DATA)) & 0x8600
+ && --boguscnt >= 0) {
+ /* Acknowledge all of the current interrupt sources ASAP. */
+ outw(csr0 & ~0x004f, dev->base_addr + LANCE_DATA);
+
+ must_restart = 0;
+
+ if (lance_debug > 5)
+ printk("%s: interrupt csr0=%#2.2x new csr=%#2.2x.\n",
+ dev->name, csr0, inw(dev->base_addr + LANCE_DATA));
+
+ if (csr0 & 0x0400) /* Rx interrupt */
+ lance_rx(dev);
+
+ if (csr0 & 0x0200) { /* Tx-done interrupt */
+ int dirty_tx = lp->dirty_tx;
+
+ while (dirty_tx < lp->cur_tx) {
+ int entry = dirty_tx & TX_RING_MOD_MASK;
+ int status = lp->tx_ring[entry].base;
+
+ if (status < 0)
+ break; /* It still hasn't been Txed */
+
+ lp->tx_ring[entry].base = 0;
+
+ if (status & 0x40000000) {
+ /* There was an major error, log it. */
+ int err_status = lp->tx_ring[entry].misc;
+ lp->stats.tx_errors++;
+ if (err_status & 0x0400) lp->stats.tx_aborted_errors++;
+ if (err_status & 0x0800) lp->stats.tx_carrier_errors++;
+ if (err_status & 0x1000) lp->stats.tx_window_errors++;
+ if (err_status & 0x4000) {
+ /* Ackk! On FIFO errors the Tx unit is turned off! */
+ lp->stats.tx_fifo_errors++;
+ /* Remove this verbosity later! */
+ printk("%s: Tx FIFO error! Status %4.4x.\n",
+ dev->name, csr0);
+ /* Restart the chip. */
+ must_restart = 1;
+ }
+ } else {
+ if (status & 0x18000000)
+ lp->stats.collisions++;
+ lp->stats.tx_packets++;
+ }
+
+ /* We must free the original skb if it's not a data-only copy
+ in the bounce buffer. */
+ if (lp->tx_skbuff[entry]) {
+ dev_kfree_skb(lp->tx_skbuff[entry],FREE_WRITE);
+ lp->tx_skbuff[entry] = 0;
+ }
+ dirty_tx++;
+ }
+
+#ifndef final_version
+ if (lp->cur_tx - dirty_tx >= TX_RING_SIZE) {
+ printk("out-of-sync dirty pointer, %d vs. %d, full=%d.\n",
+ dirty_tx, lp->cur_tx, lp->tx_full);
+ dirty_tx += TX_RING_SIZE;
+ }
+#endif
+
+ if (lp->tx_full && dev->tbusy
+ && dirty_tx > lp->cur_tx - TX_RING_SIZE + 2) {
+ /* The ring is no longer full, clear tbusy. */
+ lp->tx_full = 0;
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+ }
+
+ lp->dirty_tx = dirty_tx;
+ }
+
+ /* Log misc errors. */
+ if (csr0 & 0x4000) lp->stats.tx_errors++; /* Tx babble. */
+ if (csr0 & 0x1000) lp->stats.rx_errors++; /* Missed a Rx frame. */
+ if (csr0 & 0x0800) {
+ printk("%s: Bus master arbitration failure, status %4.4x.\n",
+ dev->name, csr0);
+ /* Restart the chip. */
+ must_restart = 1;
+ }
+
+ if (must_restart) {
+ /* stop the chip to clear the error condition, then restart */
+ outw(0x0000, dev->base_addr + LANCE_ADDR);
+ outw(0x0004, dev->base_addr + LANCE_DATA);
+ lance_restart(dev, 0x0002, 0);
+ }
+ }
+
+ /* Clear any other interrupt, and set interrupt enable. */
+ outw(0x0000, dev->base_addr + LANCE_ADDR);
+ outw(0x7940, dev->base_addr + LANCE_DATA);
+
+ if (lance_debug > 4)
+ printk("%s: exiting interrupt, csr%d=%#4.4x.\n",
+ dev->name, inw(ioaddr + LANCE_ADDR),
+ inw(dev->base_addr + LANCE_DATA));
+
+ dev->interrupt = 0;
+ return;
+}
+
+static int
+lance_rx(struct device *dev)
+{
+ struct lance_private *lp = (struct lance_private *)dev->priv;
+ int entry = lp->cur_rx & RX_RING_MOD_MASK;
+ int i;
+
+ /* If we own the next entry, it's a new packet. Send it up. */
+ while (lp->rx_ring[entry].base >= 0) {
+ int status = lp->rx_ring[entry].base >> 24;
+
+ if (status != 0x03) { /* There was an error. */
+ /* There is a tricky error noted by John Murphy,
+ <murf@perftech.com> to Russ Nelson: Even with full-sized
+ buffers it's possible for a jabber packet to use two
+ buffers, with only the last correctly noting the error. */
+ if (status & 0x01) /* Only count a general error at the */
+ lp->stats.rx_errors++; /* end of a packet.*/
+ if (status & 0x20) lp->stats.rx_frame_errors++;
+ if (status & 0x10) lp->stats.rx_over_errors++;
+ if (status & 0x08) lp->stats.rx_crc_errors++;
+ if (status & 0x04) lp->stats.rx_fifo_errors++;
+ lp->rx_ring[entry].base &= 0x03ffffff;
+ }
+ else
+ {
+ /* Malloc up new buffer, compatible with net-2e. */
+ short pkt_len = (lp->rx_ring[entry].msg_length & 0xfff)-4;
+ struct sk_buff *skb;
+
+ if(pkt_len<60)
+ {
+ printk("%s: Runt packet!\n",dev->name);
+ lp->stats.rx_errors++;
+ }
+ else
+ {
+ skb = dev_alloc_skb(pkt_len+2);
+ if (skb == NULL)
+ {
+ printk("%s: Memory squeeze, deferring packet.\n", dev->name);
+ for (i=0; i < RX_RING_SIZE; i++)
+ if (lp->rx_ring[(entry+i) & RX_RING_MOD_MASK].base < 0)
+ break;
+
+ if (i > RX_RING_SIZE -2)
+ {
+ lp->stats.rx_dropped++;
+ lp->rx_ring[entry].base |= 0x80000000;
+ lp->cur_rx++;
+ }
+ break;
+ }
+ skb->dev = dev;
+ skb_reserve(skb,2); /* 16 byte align */
+ skb_put(skb,pkt_len); /* Make room */
+ eth_copy_and_sum(skb,
+ (unsigned char *)(lp->rx_ring[entry].base & 0x00ffffff),
+ pkt_len,0);
+ skb->protocol=eth_type_trans(skb,dev);
+ netif_rx(skb);
+ lp->stats.rx_packets++;
+ }
+ }
+ /* The docs say that the buffer length isn't touched, but Andrew Boyd
+ of QNX reports that some revs of the 79C965 clear it. */
+ lp->rx_ring[entry].buf_length = -PKT_BUF_SZ;
+ lp->rx_ring[entry].base |= 0x80000000;
+ entry = (++lp->cur_rx) & RX_RING_MOD_MASK;
+ }
+
+ /* We should check that at least two ring entries are free. If not,
+ we should free one and mark stats->rx_dropped++. */
+
+ return 0;
+}
+
+static int
+lance_close(struct device *dev)
+{
+ int ioaddr = dev->base_addr;
+ struct lance_private *lp = (struct lance_private *)dev->priv;
+
+ dev->start = 0;
+ dev->tbusy = 1;
+
+ if (chip_table[lp->chip_version].flags & LANCE_HAS_MISSED_FRAME) {
+ outw(112, ioaddr+LANCE_ADDR);
+ lp->stats.rx_missed_errors = inw(ioaddr+LANCE_DATA);
+ }
+ outw(0, ioaddr+LANCE_ADDR);
+
+ if (lance_debug > 1)
+ printk("%s: Shutting down ethercard, status was %2.2x.\n",
+ dev->name, inw(ioaddr+LANCE_DATA));
+
+ /* We stop the LANCE here -- it occasionally polls
+ memory if we don't. */
+ outw(0x0004, ioaddr+LANCE_DATA);
+
+ if (dev->dma != 4)
+ disable_dma(dev->dma);
+
+ free_irq(dev->irq);
+
+ irq2dev_map[dev->irq] = 0;
+
+ return 0;
+}
+
+static struct enet_statistics *
+lance_get_stats(struct device *dev)
+{
+ struct lance_private *lp = (struct lance_private *)dev->priv;
+ short ioaddr = dev->base_addr;
+ short saved_addr;
+ unsigned long flags;
+
+ if (chip_table[lp->chip_version].flags & LANCE_HAS_MISSED_FRAME) {
+ save_flags(flags);
+ cli();
+ saved_addr = inw(ioaddr+LANCE_ADDR);
+ outw(112, ioaddr+LANCE_ADDR);
+ lp->stats.rx_missed_errors = inw(ioaddr+LANCE_DATA);
+ outw(saved_addr, ioaddr+LANCE_ADDR);
+ restore_flags(flags);
+ }
+
+ return &lp->stats;
+}
+
+/* Set or clear the multicast filter for this adaptor.
+ */
+
+static void set_multicast_list(struct device *dev)
+{
+ short ioaddr = dev->base_addr;
+
+ outw(0, ioaddr+LANCE_ADDR);
+ outw(0x0004, ioaddr+LANCE_DATA); /* Temporarily stop the lance. */
+
+ if (dev->flags&IFF_PROMISC) {
+ /* Log any net taps. */
+ printk("%s: Promiscuous mode enabled.\n", dev->name);
+ outw(15, ioaddr+LANCE_ADDR);
+ outw(0x8000, ioaddr+LANCE_DATA); /* Set promiscuous mode */
+ } else {
+ short multicast_table[4];
+ int i;
+ int num_addrs=dev->mc_count;
+ if(dev->flags&IFF_ALLMULTI)
+ num_addrs=1;
+ /* FIXIT: We don't use the multicast table, but rely on upper-layer filtering. */
+ memset(multicast_table, (num_addrs == 0) ? 0 : -1, sizeof(multicast_table));
+ for (i = 0; i < 4; i++) {
+ outw(8 + i, ioaddr+LANCE_ADDR);
+ outw(multicast_table[i], ioaddr+LANCE_DATA);
+ }
+ outw(15, ioaddr+LANCE_ADDR);
+ outw(0x0000, ioaddr+LANCE_DATA); /* Unset promiscuous mode */
+ }
+
+ lance_restart(dev, 0x0142, 0); /* Resume normal operation */
+
+}
+
+
+/*
+ * Local variables:
+ * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c lance.c"
+ * c-indent-level: 4
+ * tab-width: 4
+ * End:
+ */
diff --git a/i386/i386at/gpl/linux/net/ne.c b/i386/i386at/gpl/linux/net/ne.c
new file mode 100644
index 00000000..4da4efb7
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/ne.c
@@ -0,0 +1,733 @@
+/* ne.c: A general non-shared-memory NS8390 ethernet driver for linux. */
+/*
+ Written 1992-94 by Donald Becker.
+
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency.
+
+ This software may be used and distributed according to the terms
+ of the GNU Public License, incorporated herein by reference.
+
+ The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
+ Center of Excellence in Space Data and Information Sciences
+ Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
+
+ This driver should work with many programmed-I/O 8390-based ethernet
+ boards. Currently it supports the NE1000, NE2000, many clones,
+ and some Cabletron products.
+
+ Changelog:
+
+ Paul Gortmaker : use ENISR_RDC to monitor Tx PIO uploads, made
+ sanity checks and bad clone support optional.
+ Paul Gortmaker : new reset code, reset card after probe at boot.
+ Paul Gortmaker : multiple card support for module users.
+ Paul Gortmaker : Support for PCI ne2k clones, similar to lance.c
+
+*/
+
+/* Routines for the NatSemi-based designs (NE[12]000). */
+
+static const char *version =
+ "ne.c:v1.10 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#include <asm/system.h>
+#include <asm/io.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include "8390.h"
+
+/* Some defines that people can play with if so inclined. */
+
+/* Do we support clones that don't adhere to 14,15 of the SAprom ? */
+#define SUPPORT_NE_BAD_CLONES
+
+/* Do we perform extra sanity checks on stuff ? */
+/* #define NE_SANITY_CHECK */
+
+/* Do we implement the read before write bugfix ? */
+/* #define NE_RW_BUGFIX */
+
+/* Do we have a non std. amount of memory? (in units of 256 byte pages) */
+/* #define PACKETBUF_MEMSIZE 0x40 */
+
+/* ---- No user-serviceable parts below ---- */
+
+/* A zero-terminated list of I/O addresses to be probed. */
+static unsigned int netcard_portlist[] =
+{ 0x300, 0x280, 0x320, 0x340, 0x360, 0};
+
+#ifdef SUPPORT_NE_BAD_CLONES
+/* A list of bad clones that we none-the-less recognize. */
+static struct { const char *name8, *name16; unsigned char SAprefix[4];}
+bad_clone_list[] = {
+ {"DE100", "DE200", {0x00, 0xDE, 0x01,}},
+ {"DE120", "DE220", {0x00, 0x80, 0xc8,}},
+ {"DFI1000", "DFI2000", {'D', 'F', 'I',}}, /* Original, eh? */
+ {"EtherNext UTP8", "EtherNext UTP16", {0x00, 0x00, 0x79}},
+ {"NE1000","NE2000-invalid", {0x00, 0x00, 0xd8}}, /* Ancient real NE1000. */
+ {"NN1000", "NN2000", {0x08, 0x03, 0x08}}, /* Outlaw no-name clone. */
+ {"4-DIM8","4-DIM16", {0x00,0x00,0x4d,}}, /* Outlaw 4-Dimension cards. */
+ {"Con-Intl_8", "Con-Intl_16", {0x00, 0x00, 0x24}}, /* Connect Int'nl */
+ {0,}
+};
+#endif
+
+#define NE_BASE (dev->base_addr)
+#define NE_CMD 0x00
+#define NE_DATAPORT 0x10 /* NatSemi-defined port window offset. */
+#define NE_RESET 0x1f /* Issue a read to reset, a write to clear. */
+#define NE_IO_EXTENT 0x20
+
+#define NE1SM_START_PG 0x20 /* First page of TX buffer */
+#define NE1SM_STOP_PG 0x40 /* Last page +1 of RX ring */
+#define NESM_START_PG 0x40 /* First page of TX buffer */
+#define NESM_STOP_PG 0x80 /* Last page +1 of RX ring */
+
+/* Non-zero only if the current card is a PCI with BIOS-set IRQ. */
+static unsigned char pci_irq_line = 0;
+
+int ne_probe(struct device *dev);
+static int ne_probe1(struct device *dev, int ioaddr);
+
+static int ne_open(struct device *dev);
+static int ne_close(struct device *dev);
+
+static void ne_reset_8390(struct device *dev);
+static void ne_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
+ int ring_page);
+static void ne_block_input(struct device *dev, int count,
+ struct sk_buff *skb, int ring_offset);
+static void ne_block_output(struct device *dev, const int count,
+ const unsigned char *buf, const int start_page);
+
+
+/* Probe for various non-shared-memory ethercards.
+
+ NEx000-clone boards have a Station Address PROM (SAPROM) in the packet
+ buffer memory space. NE2000 clones have 0x57,0x57 in bytes 0x0e,0x0f of
+ the SAPROM, while other supposed NE2000 clones must be detected by their
+ SA prefix.
+
+ Reading the SAPROM from a word-wide card with the 8390 set in byte-wide
+ mode results in doubled values, which can be detected and compensated for.
+
+ The probe is also responsible for initializing the card and filling
+ in the 'dev' and 'ei_status' structures.
+
+ We use the minimum memory size for some ethercard product lines, iff we can't
+ distinguish models. You can increase the packet buffer size by setting
+ PACKETBUF_MEMSIZE. Reported Cabletron packet buffer locations are:
+ E1010 starts at 0x100 and ends at 0x2000.
+ E1010-x starts at 0x100 and ends at 0x8000. ("-x" means "more memory")
+ E2010 starts at 0x100 and ends at 0x4000.
+ E2010-x starts at 0x100 and ends at 0xffff. */
+
+#ifdef HAVE_DEVLIST
+struct netdev_entry netcard_drv =
+{"ne", ne_probe1, NE_IO_EXTENT, netcard_portlist};
+#else
+
+/* Note that this probe only picks up one card at a time, even for multiple
+ PCI ne2k cards. Use "ether=0,0,eth1" if you have a second PCI ne2k card.
+ This keeps things consistent regardless of the bus type of the card. */
+
+int ne_probe(struct device *dev)
+{
+ int i;
+ int base_addr = dev ? dev->base_addr : 0;
+
+ /* First check any supplied i/o locations. User knows best. <cough> */
+ if (base_addr > 0x1ff) /* Check a single specified location. */
+ return ne_probe1(dev, base_addr);
+ else if (base_addr != 0) /* Don't probe at all. */
+ return ENXIO;
+
+ /* Then look for any installed PCI clones */
+#if defined(CONFIG_PCI)
+ if (pcibios_present()) {
+ int pci_index;
+ for (pci_index = 0; pci_index < 8; pci_index++) {
+ unsigned char pci_bus, pci_device_fn;
+ unsigned int pci_ioaddr;
+
+ /* Currently only Realtek are making PCI ne2k clones. */
+ if (pcibios_find_device (PCI_VENDOR_ID_REALTEK,
+ PCI_DEVICE_ID_REALTEK_8029, pci_index,
+ &pci_bus, &pci_device_fn) != 0)
+ break; /* OK, now try to probe for std. ISA card */
+ pcibios_read_config_byte(pci_bus, pci_device_fn,
+ PCI_INTERRUPT_LINE, &pci_irq_line);
+ pcibios_read_config_dword(pci_bus, pci_device_fn,
+ PCI_BASE_ADDRESS_0, &pci_ioaddr);
+ /* Strip the I/O address out of the returned value */
+ pci_ioaddr &= PCI_BASE_ADDRESS_IO_MASK;
+ /* Avoid already found cards from previous ne_probe() calls */
+ if (check_region(pci_ioaddr, NE_IO_EXTENT))
+ continue;
+ printk("ne.c: PCI BIOS reports ne2000 clone at i/o %#x, irq %d.\n",
+ pci_ioaddr, pci_irq_line);
+ if (ne_probe1(dev, pci_ioaddr) != 0) { /* Shouldn't happen. */
+ printk(KERN_ERR "ne.c: Probe of PCI card at %#x failed.\n", pci_ioaddr);
+ break; /* Hrmm, try to probe for ISA card... */
+ }
+ pci_irq_line = 0;
+ return 0;
+ }
+ }
+#endif /* defined(CONFIG_PCI) */
+
+ /* Last resort. The semi-risky ISA auto-probe. */
+ for (i = 0; netcard_portlist[i]; i++) {
+ int ioaddr = netcard_portlist[i];
+ if (check_region(ioaddr, NE_IO_EXTENT))
+ continue;
+ if (ne_probe1(dev, ioaddr) == 0)
+ return 0;
+ }
+
+ return ENODEV;
+}
+#endif
+
+static int ne_probe1(struct device *dev, int ioaddr)
+{
+ int i;
+ unsigned char SA_prom[32];
+ int wordlength = 2;
+ const char *name = NULL;
+ int start_page, stop_page;
+ int neX000, ctron;
+ int reg0 = inb_p(ioaddr);
+ static unsigned version_printed = 0;
+
+ if (reg0 == 0xFF)
+ return ENODEV;
+
+ /* Do a preliminary verification that we have a 8390. */
+ { int regd;
+ outb_p(E8390_NODMA+E8390_PAGE1+E8390_STOP, ioaddr + E8390_CMD);
+ regd = inb_p(ioaddr + 0x0d);
+ outb_p(0xff, ioaddr + 0x0d);
+ outb_p(E8390_NODMA+E8390_PAGE0, ioaddr + E8390_CMD);
+ inb_p(ioaddr + EN0_COUNTER0); /* Clear the counter by reading. */
+ if (inb_p(ioaddr + EN0_COUNTER0) != 0) {
+ outb_p(reg0, ioaddr);
+ outb_p(regd, ioaddr + 0x0d); /* Restore the old values. */
+ return ENODEV;
+ }
+ }
+
+ if (ei_debug && version_printed++ == 0)
+ printk(version);
+
+ printk("NE*000 ethercard probe at %#3x:", ioaddr);
+
+ /* Reset card. Who knows what dain-bramaged state it was left in. */
+ { unsigned long reset_start_time = jiffies;
+
+ /* DON'T change these to inb_p/outb_p or reset will fail on clones. */
+ outb(inb(ioaddr + NE_RESET), ioaddr + NE_RESET);
+
+ while ((inb_p(ioaddr + EN0_ISR) & ENISR_RESET) == 0)
+ if (jiffies - reset_start_time > 2*HZ/100) {
+ printk(" not found (no reset ack).\n");
+ return ENODEV;
+ }
+
+ outb_p(0xff, ioaddr + EN0_ISR); /* Ack all intr. */
+ }
+
+ /* Read the 16 bytes of station address PROM.
+ We must first initialize registers, similar to NS8390_init(eifdev, 0).
+ We can't reliably read the SAPROM address without this.
+ (I learned the hard way!). */
+ {
+ struct {unsigned char value, offset; } program_seq[] = {
+ {E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD}, /* Select page 0*/
+ {0x48, EN0_DCFG}, /* Set byte-wide (0x48) access. */
+ {0x00, EN0_RCNTLO}, /* Clear the count regs. */
+ {0x00, EN0_RCNTHI},
+ {0x00, EN0_IMR}, /* Mask completion irq. */
+ {0xFF, EN0_ISR},
+ {E8390_RXOFF, EN0_RXCR}, /* 0x20 Set to monitor */
+ {E8390_TXOFF, EN0_TXCR}, /* 0x02 and loopback mode. */
+ {32, EN0_RCNTLO},
+ {0x00, EN0_RCNTHI},
+ {0x00, EN0_RSARLO}, /* DMA starting at 0x0000. */
+ {0x00, EN0_RSARHI},
+ {E8390_RREAD+E8390_START, E8390_CMD},
+ };
+ for (i = 0; i < sizeof(program_seq)/sizeof(program_seq[0]); i++)
+ outb_p(program_seq[i].value, ioaddr + program_seq[i].offset);
+
+ }
+ for(i = 0; i < 32 /*sizeof(SA_prom)*/; i+=2) {
+ SA_prom[i] = inb(ioaddr + NE_DATAPORT);
+ SA_prom[i+1] = inb(ioaddr + NE_DATAPORT);
+ if (SA_prom[i] != SA_prom[i+1])
+ wordlength = 1;
+ }
+
+ if (wordlength == 2) {
+ /* We must set the 8390 for word mode. */
+ outb_p(0x49, ioaddr + EN0_DCFG);
+ /* We used to reset the ethercard here, but it doesn't seem
+ to be necessary. */
+ /* Un-double the SA_prom values. */
+ for (i = 0; i < 16; i++)
+ SA_prom[i] = SA_prom[i+i];
+ start_page = NESM_START_PG;
+ stop_page = NESM_STOP_PG;
+ } else {
+ start_page = NE1SM_START_PG;
+ stop_page = NE1SM_STOP_PG;
+ }
+
+ neX000 = (SA_prom[14] == 0x57 && SA_prom[15] == 0x57);
+ ctron = (SA_prom[0] == 0x00 && SA_prom[1] == 0x00 && SA_prom[2] == 0x1d);
+
+ /* Set up the rest of the parameters. */
+ if (neX000) {
+ name = (wordlength == 2) ? "NE2000" : "NE1000";
+ } else if (ctron) {
+ name = (wordlength == 2) ? "Ctron-8" : "Ctron-16";
+ start_page = 0x01;
+ stop_page = (wordlength == 2) ? 0x40 : 0x20;
+ } else {
+#ifdef SUPPORT_NE_BAD_CLONES
+ /* Ack! Well, there might be a *bad* NE*000 clone there.
+ Check for total bogus addresses. */
+ for (i = 0; bad_clone_list[i].name8; i++) {
+ if (SA_prom[0] == bad_clone_list[i].SAprefix[0] &&
+ SA_prom[1] == bad_clone_list[i].SAprefix[1] &&
+ SA_prom[2] == bad_clone_list[i].SAprefix[2]) {
+ if (wordlength == 2) {
+ name = bad_clone_list[i].name16;
+ } else {
+ name = bad_clone_list[i].name8;
+ }
+ break;
+ }
+ }
+ if (bad_clone_list[i].name8 == NULL) {
+ printk(" not found (invalid signature %2.2x %2.2x).\n",
+ SA_prom[14], SA_prom[15]);
+ return ENXIO;
+ }
+#else
+ printk(" not found.\n");
+ return ENXIO;
+#endif
+
+ }
+
+ /* We should have a "dev" from Space.c or the static module table. */
+ if (dev == NULL) {
+ printk("ne.c: Passed a NULL device.\n");
+ dev = init_etherdev(0, 0);
+ }
+
+ if (pci_irq_line) {
+ dev->irq = pci_irq_line;
+ }
+
+ if (dev->irq < 2) {
+ autoirq_setup(0);
+ outb_p(0x50, ioaddr + EN0_IMR); /* Enable one interrupt. */
+ outb_p(0x00, ioaddr + EN0_RCNTLO);
+ outb_p(0x00, ioaddr + EN0_RCNTHI);
+ outb_p(E8390_RREAD+E8390_START, ioaddr); /* Trigger it... */
+ outb_p(0x00, ioaddr + EN0_IMR); /* Mask it again. */
+ dev->irq = autoirq_report(0);
+ if (ei_debug > 2)
+ printk(" autoirq is %d\n", dev->irq);
+ } else if (dev->irq == 2)
+ /* Fixup for users that don't know that IRQ 2 is really IRQ 9,
+ or don't know which one to set. */
+ dev->irq = 9;
+
+ if (! dev->irq) {
+ printk(" failed to detect IRQ line.\n");
+ return EAGAIN;
+ }
+
+ /* Snarf the interrupt now. There's no point in waiting since we cannot
+ share and the board will usually be enabled. */
+ {
+ int irqval = request_irq(dev->irq, ei_interrupt, 0, name);
+ if (irqval) {
+ printk (" unable to get IRQ %d (irqval=%d).\n", dev->irq, irqval);
+ return EAGAIN;
+ }
+ }
+
+ dev->base_addr = ioaddr;
+
+ /* Allocate dev->priv and fill in 8390 specific dev fields. */
+ if (ethdev_init(dev)) {
+ printk (" unable to get memory for dev->priv.\n");
+ free_irq(dev->irq);
+ return -ENOMEM;
+ }
+
+ request_region(ioaddr, NE_IO_EXTENT, name);
+
+ for(i = 0; i < ETHER_ADDR_LEN; i++) {
+ printk(" %2.2x", SA_prom[i]);
+ dev->dev_addr[i] = SA_prom[i];
+ }
+
+ printk("\n%s: %s found at %#x, using IRQ %d.\n",
+ dev->name, name, ioaddr, dev->irq);
+
+ ei_status.name = name;
+ ei_status.tx_start_page = start_page;
+ ei_status.stop_page = stop_page;
+ ei_status.word16 = (wordlength == 2);
+
+ ei_status.rx_start_page = start_page + TX_PAGES;
+#ifdef PACKETBUF_MEMSIZE
+ /* Allow the packet buffer size to be overridden by know-it-alls. */
+ ei_status.stop_page = ei_status.tx_start_page + PACKETBUF_MEMSIZE;
+#endif
+
+ ei_status.reset_8390 = &ne_reset_8390;
+ ei_status.block_input = &ne_block_input;
+ ei_status.block_output = &ne_block_output;
+ ei_status.get_8390_hdr = &ne_get_8390_hdr;
+ dev->open = &ne_open;
+ dev->stop = &ne_close;
+ NS8390_init(dev, 0);
+ return 0;
+}
+
+static int
+ne_open(struct device *dev)
+{
+ ei_open(dev);
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int
+ne_close(struct device *dev)
+{
+ if (ei_debug > 1)
+ printk("%s: Shutting down ethercard.\n", dev->name);
+ ei_close(dev);
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+/* Hard reset the card. This used to pause for the same period that a
+ 8390 reset command required, but that shouldn't be necessary. */
+static void
+ne_reset_8390(struct device *dev)
+{
+ unsigned long reset_start_time = jiffies;
+
+ if (ei_debug > 1) printk("resetting the 8390 t=%ld...", jiffies);
+
+ /* DON'T change these to inb_p/outb_p or reset will fail on clones. */
+ outb(inb(NE_BASE + NE_RESET), NE_BASE + NE_RESET);
+
+ ei_status.txing = 0;
+ ei_status.dmaing = 0;
+
+ /* This check _should_not_ be necessary, omit eventually. */
+ while ((inb_p(NE_BASE+EN0_ISR) & ENISR_RESET) == 0)
+ if (jiffies - reset_start_time > 2*HZ/100) {
+ printk("%s: ne_reset_8390() did not complete.\n", dev->name);
+ break;
+ }
+ outb_p(ENISR_RESET, NE_BASE + EN0_ISR); /* Ack intr. */
+}
+
+/* Grab the 8390 specific header. Similar to the block_input routine, but
+ we don't need to be concerned with ring wrap as the header will be at
+ the start of a page, so we optimize accordingly. */
+
+static void
+ne_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+
+ int nic_base = dev->base_addr;
+
+ /* This *shouldn't* happen. If it does, it's the last thing you'll see */
+ if (ei_status.dmaing) {
+ printk("%s: DMAing conflict in ne_get_8390_hdr "
+ "[DMAstat:%d][irqlock:%d][intr:%d].\n",
+ dev->name, ei_status.dmaing, ei_status.irqlock,
+ dev->interrupt);
+ return;
+ }
+
+ ei_status.dmaing |= 0x01;
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD);
+ outb_p(sizeof(struct e8390_pkt_hdr), nic_base + EN0_RCNTLO);
+ outb_p(0, nic_base + EN0_RCNTHI);
+ outb_p(0, nic_base + EN0_RSARLO); /* On page boundary */
+ outb_p(ring_page, nic_base + EN0_RSARHI);
+ outb_p(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+
+ if (ei_status.word16)
+ insw(NE_BASE + NE_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr)>>1);
+ else
+ insb(NE_BASE + NE_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr));
+
+ outb_p(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */
+ ei_status.dmaing &= ~0x01;
+}
+
+/* Block input and output, similar to the Crynwr packet driver. If you
+ are porting to a new ethercard, look at the packet driver source for hints.
+ The NEx000 doesn't share the on-board packet memory -- you have to put
+ the packet out through the "remote DMA" dataport using outb. */
+
+static void
+ne_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset)
+{
+#ifdef NE_SANITY_CHECK
+ int xfer_count = count;
+#endif
+ int nic_base = dev->base_addr;
+ char *buf = skb->data;
+
+ /* This *shouldn't* happen. If it does, it's the last thing you'll see */
+ if (ei_status.dmaing) {
+ printk("%s: DMAing conflict in ne_block_input "
+ "[DMAstat:%d][irqlock:%d][intr:%d].\n",
+ dev->name, ei_status.dmaing, ei_status.irqlock,
+ dev->interrupt);
+ return;
+ }
+ ei_status.dmaing |= 0x01;
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD);
+ outb_p(count & 0xff, nic_base + EN0_RCNTLO);
+ outb_p(count >> 8, nic_base + EN0_RCNTHI);
+ outb_p(ring_offset & 0xff, nic_base + EN0_RSARLO);
+ outb_p(ring_offset >> 8, nic_base + EN0_RSARHI);
+ outb_p(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+ if (ei_status.word16) {
+ insw(NE_BASE + NE_DATAPORT,buf,count>>1);
+ if (count & 0x01) {
+ buf[count-1] = inb(NE_BASE + NE_DATAPORT);
+#ifdef NE_SANITY_CHECK
+ xfer_count++;
+#endif
+ }
+ } else {
+ insb(NE_BASE + NE_DATAPORT, buf, count);
+ }
+
+#ifdef NE_SANITY_CHECK
+ /* This was for the ALPHA version only, but enough people have
+ been encountering problems so it is still here. If you see
+ this message you either 1) have a slightly incompatible clone
+ or 2) have noise/speed problems with your bus. */
+ if (ei_debug > 1) { /* DMA termination address check... */
+ int addr, tries = 20;
+ do {
+ /* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here
+ -- it's broken for Rx on some cards! */
+ int high = inb_p(nic_base + EN0_RSARHI);
+ int low = inb_p(nic_base + EN0_RSARLO);
+ addr = (high << 8) + low;
+ if (((ring_offset + xfer_count) & 0xff) == low)
+ break;
+ } while (--tries > 0);
+ if (tries <= 0)
+ printk("%s: RX transfer address mismatch,"
+ "%#4.4x (expected) vs. %#4.4x (actual).\n",
+ dev->name, ring_offset + xfer_count, addr);
+ }
+#endif
+ outb_p(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */
+ ei_status.dmaing &= ~0x01;
+}
+
+static void
+ne_block_output(struct device *dev, int count,
+ const unsigned char *buf, const int start_page)
+{
+ int nic_base = NE_BASE;
+ unsigned long dma_start;
+#ifdef NE_SANITY_CHECK
+ int retries = 0;
+#endif
+
+ /* Round the count up for word writes. Do we need to do this?
+ What effect will an odd byte count have on the 8390?
+ I should check someday. */
+ if (ei_status.word16 && (count & 0x01))
+ count++;
+
+ /* This *shouldn't* happen. If it does, it's the last thing you'll see */
+ if (ei_status.dmaing) {
+ printk("%s: DMAing conflict in ne_block_output."
+ "[DMAstat:%d][irqlock:%d][intr:%d]\n",
+ dev->name, ei_status.dmaing, ei_status.irqlock,
+ dev->interrupt);
+ return;
+ }
+ ei_status.dmaing |= 0x01;
+ /* We should already be in page 0, but to be safe... */
+ outb_p(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base + NE_CMD);
+
+#ifdef NE_SANITY_CHECK
+ retry:
+#endif
+
+#ifdef NE8390_RW_BUGFIX
+ /* Handle the read-before-write bug the same way as the
+ Crynwr packet driver -- the NatSemi method doesn't work.
+ Actually this doesn't always work either, but if you have
+ problems with your NEx000 this is better than nothing! */
+ outb_p(0x42, nic_base + EN0_RCNTLO);
+ outb_p(0x00, nic_base + EN0_RCNTHI);
+ outb_p(0x42, nic_base + EN0_RSARLO);
+ outb_p(0x00, nic_base + EN0_RSARHI);
+ outb_p(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+ /* Make certain that the dummy read has occurred. */
+ SLOW_DOWN_IO;
+ SLOW_DOWN_IO;
+ SLOW_DOWN_IO;
+#endif
+
+ outb_p(ENISR_RDC, nic_base + EN0_ISR);
+
+ /* Now the normal output. */
+ outb_p(count & 0xff, nic_base + EN0_RCNTLO);
+ outb_p(count >> 8, nic_base + EN0_RCNTHI);
+ outb_p(0x00, nic_base + EN0_RSARLO);
+ outb_p(start_page, nic_base + EN0_RSARHI);
+
+ outb_p(E8390_RWRITE+E8390_START, nic_base + NE_CMD);
+ if (ei_status.word16) {
+ outsw(NE_BASE + NE_DATAPORT, buf, count>>1);
+ } else {
+ outsb(NE_BASE + NE_DATAPORT, buf, count);
+ }
+
+ dma_start = jiffies;
+
+#ifdef NE_SANITY_CHECK
+ /* This was for the ALPHA version only, but enough people have
+ been encountering problems so it is still here. */
+ if (ei_debug > 1) { /* DMA termination address check... */
+ int addr, tries = 20;
+ do {
+ int high = inb_p(nic_base + EN0_RSARHI);
+ int low = inb_p(nic_base + EN0_RSARLO);
+ addr = (high << 8) + low;
+ if ((start_page << 8) + count == addr)
+ break;
+ } while (--tries > 0);
+ if (tries <= 0) {
+ printk("%s: Tx packet transfer address mismatch,"
+ "%#4.4x (expected) vs. %#4.4x (actual).\n",
+ dev->name, (start_page << 8) + count, addr);
+ if (retries++ == 0)
+ goto retry;
+ }
+ }
+#endif
+
+ while ((inb_p(nic_base + EN0_ISR) & ENISR_RDC) == 0)
+ if (jiffies - dma_start > 2*HZ/100) { /* 20ms */
+ printk("%s: timeout waiting for Tx RDC.\n", dev->name);
+ ne_reset_8390(dev);
+ NS8390_init(dev,1);
+ break;
+ }
+
+ outb_p(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */
+ ei_status.dmaing &= ~0x01;
+ return;
+}
+
+
+#ifdef MODULE
+#define MAX_NE_CARDS 4 /* Max number of NE cards per module */
+#define NAMELEN 8 /* # of chars for storing dev->name */
+static char namelist[NAMELEN * MAX_NE_CARDS] = { 0, };
+static struct device dev_ne[MAX_NE_CARDS] = {
+ {
+ NULL, /* assign a chunk of namelist[] below */
+ 0, 0, 0, 0,
+ 0, 0,
+ 0, 0, 0, NULL, NULL
+ },
+};
+
+static int io[MAX_NE_CARDS] = { 0, };
+static int irq[MAX_NE_CARDS] = { 0, };
+
+/* This is set up so that no autoprobe takes place. We can't guarantee
+that the ne2k probe is the last 8390 based probe to take place (as it
+is at boot) and so the probe will get confused by any other 8390 cards.
+ISA device autoprobes on a running machine are not recommended anyway. */
+
+int
+init_module(void)
+{
+ int this_dev, found = 0;
+
+ for (this_dev = 0; this_dev < MAX_NE_CARDS; this_dev++) {
+ struct device *dev = &dev_ne[this_dev];
+ dev->name = namelist+(NAMELEN*this_dev);
+ dev->irq = irq[this_dev];
+ dev->base_addr = io[this_dev];
+ dev->init = ne_probe;
+ if (io[this_dev] == 0) {
+ if (this_dev != 0) break; /* only complain once */
+ printk(KERN_NOTICE "ne.c: Module autoprobing not allowed. Append \"io=0xNNN\" value(s).\n");
+ return -EPERM;
+ }
+ if (register_netdev(dev) != 0) {
+ printk(KERN_WARNING "ne.c: No NE*000 card found (i/o = 0x%x).\n", io[this_dev]);
+ if (found != 0) return 0; /* Got at least one. */
+ return -ENXIO;
+ }
+ found++;
+ }
+
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ int this_dev;
+
+ for (this_dev = 0; this_dev < MAX_NE_CARDS; this_dev++) {
+ struct device *dev = &dev_ne[this_dev];
+ if (dev->priv != NULL) {
+ kfree(dev->priv);
+ dev->priv = NULL;
+ free_irq(dev->irq);
+ irq2dev_map[dev->irq] = NULL;
+ release_region(dev->base_addr, NE_IO_EXTENT);
+ unregister_netdev(dev);
+ }
+ }
+}
+#endif /* MODULE */
+
+/*
+ * Local variables:
+ * compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -I/usr/src/linux/net/tcp -c ne.c"
+ * version-control: t
+ * kept-new-versions: 5
+ * End:
+ */
diff --git a/i386/i386at/gpl/linux/net/net_init.c b/i386/i386at/gpl/linux/net/net_init.c
new file mode 100644
index 00000000..cedee941
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/net_init.c
@@ -0,0 +1,380 @@
+/* netdrv_init.c: Initialization for network devices. */
+/*
+ Written 1993,1994,1995 by Donald Becker.
+
+ The author may be reached as becker@cesdis.gsfc.nasa.gov or
+ C/O Center of Excellence in Space Data and Information Sciences
+ Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
+
+ This file contains the initialization for the "pl14+" style ethernet
+ drivers. It should eventually replace most of drivers/net/Space.c.
+ It's primary advantage is that it's able to allocate low-memory buffers.
+ A secondary advantage is that the dangerous NE*000 netcards can reserve
+ their I/O port region before the SCSI probes start.
+
+ Modifications/additions by Bjorn Ekwall <bj0rn@blox.se>:
+ ethdev_index[MAX_ETH_CARDS]
+ register_netdev() / unregister_netdev()
+
+ Modifications by Wolfgang Walter
+ Use dev_close cleanly so we always shut things down tidily.
+
+ Changed 29/10/95, Alan Cox to pass sockaddr's around for mac addresses.
+*/
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/malloc.h>
+#include <linux/if_ether.h>
+#include <linux/if_arp.h>
+#include <linux/string.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/trdevice.h>
+#ifdef CONFIG_NET_ALIAS
+#include <linux/net_alias.h>
+#endif
+
+/* The network devices currently exist only in the socket namespace, so these
+ entries are unused. The only ones that make sense are
+ open start the ethercard
+ close stop the ethercard
+ ioctl To get statistics, perhaps set the interface port (AUI, BNC, etc.)
+ One can also imagine getting raw packets using
+ read & write
+ but this is probably better handled by a raw packet socket.
+
+ Given that almost all of these functions are handled in the current
+ socket-based scheme, putting ethercard devices in /dev/ seems pointless.
+
+ [Removed all support for /dev network devices. When someone adds
+ streams then by magic we get them, but otherwise they are un-needed
+ and a space waste]
+*/
+
+/* The list of used and available "eth" slots (for "eth0", "eth1", etc.) */
+#define MAX_ETH_CARDS 16 /* same as the number if irq's in irq2dev[] */
+static struct device *ethdev_index[MAX_ETH_CARDS];
+
+/* Fill in the fields of the device structure with ethernet-generic values.
+
+ If no device structure is passed, a new one is constructed, complete with
+ a SIZEOF_PRIVATE private data area.
+
+ If an empty string area is passed as dev->name, or a new structure is made,
+ a new name string is constructed. The passed string area should be 8 bytes
+ long.
+ */
+
+struct device *
+init_etherdev(struct device *dev, int sizeof_priv)
+{
+ int new_device = 0;
+ int i;
+
+ /* Use an existing correctly named device in Space.c:dev_base. */
+ if (dev == NULL) {
+ int alloc_size = sizeof(struct device) + sizeof("eth%d ")
+ + sizeof_priv + 3;
+ struct device *cur_dev;
+ char pname[8]; /* Putative name for the device. */
+
+ for (i = 0; i < MAX_ETH_CARDS; ++i)
+ if (ethdev_index[i] == NULL) {
+ sprintf(pname, "eth%d", i);
+ for (cur_dev = dev_base; cur_dev; cur_dev = cur_dev->next)
+ if (strcmp(pname, cur_dev->name) == 0) {
+ dev = cur_dev;
+ dev->init = NULL;
+ sizeof_priv = (sizeof_priv + 3) & ~3;
+ dev->priv = sizeof_priv
+ ? kmalloc(sizeof_priv, GFP_KERNEL)
+ : NULL;
+ if (dev->priv) memset(dev->priv, 0, sizeof_priv);
+ goto found;
+ }
+ }
+
+ alloc_size &= ~3; /* Round to dword boundary. */
+
+ dev = (struct device *)kmalloc(alloc_size, GFP_KERNEL);
+ memset(dev, 0, alloc_size);
+ if (sizeof_priv)
+ dev->priv = (void *) (dev + 1);
+ dev->name = sizeof_priv + (char *)(dev + 1);
+ new_device = 1;
+ }
+
+ found: /* From the double loop above. */
+
+ if (dev->name &&
+ ((dev->name[0] == '\0') || (dev->name[0] == ' '))) {
+ for (i = 0; i < MAX_ETH_CARDS; ++i)
+ if (ethdev_index[i] == NULL) {
+ sprintf(dev->name, "eth%d", i);
+ ethdev_index[i] = dev;
+ break;
+ }
+ }
+
+ ether_setup(dev); /* Hmmm, should this be called here? */
+
+ if (new_device) {
+ /* Append the device to the device queue. */
+ struct device **old_devp = &dev_base;
+ while ((*old_devp)->next)
+ old_devp = & (*old_devp)->next;
+ (*old_devp)->next = dev;
+ dev->next = 0;
+ }
+ return dev;
+}
+
+
+static int eth_mac_addr(struct device *dev, void *p)
+{
+ struct sockaddr *addr=p;
+ if(dev->start)
+ return -EBUSY;
+ memcpy(dev->dev_addr, addr->sa_data,dev->addr_len);
+ return 0;
+}
+
+void ether_setup(struct device *dev)
+{
+ int i;
+
+ /* Fill in the fields of the device structure with ethernet-generic values.
+ This should be in a common file instead of per-driver. */
+ for (i = 0; i < DEV_NUMBUFFS; i++)
+ skb_queue_head_init(&dev->buffs[i]);
+
+ /* register boot-defined "eth" devices */
+ if (dev->name && (strncmp(dev->name, "eth", 3) == 0)) {
+ i = simple_strtoul(dev->name + 3, NULL, 0);
+ if (ethdev_index[i] == NULL) {
+ ethdev_index[i] = dev;
+ }
+ else if (dev != ethdev_index[i]) {
+ /* Really shouldn't happen! */
+#ifdef MACH
+ panic ("ether_setup: Ouch! Someone else took %s, i = %d\n",
+ dev->name, i);
+#else
+ printk("ether_setup: Ouch! Someone else took %s, i = %d\n",
+ dev->name, i);
+#endif
+ }
+ }
+
+#ifndef MACH
+ dev->hard_header = eth_header;
+ dev->rebuild_header = eth_rebuild_header;
+ dev->set_mac_address = eth_mac_addr;
+ dev->header_cache_bind = eth_header_cache_bind;
+ dev->header_cache_update= eth_header_cache_update;
+#endif
+
+ dev->type = ARPHRD_ETHER;
+ dev->hard_header_len = ETH_HLEN;
+ dev->mtu = 1500; /* eth_mtu */
+ dev->addr_len = ETH_ALEN;
+ dev->tx_queue_len = 100; /* Ethernet wants good queues */
+
+ memset(dev->broadcast,0xFF, ETH_ALEN);
+
+ /* New-style flags. */
+ dev->flags = IFF_BROADCAST|IFF_MULTICAST;
+ dev->family = AF_INET;
+ dev->pa_addr = 0;
+ dev->pa_brdaddr = 0;
+ dev->pa_mask = 0;
+ dev->pa_alen = 4;
+}
+
+#ifdef CONFIG_TR
+
+void tr_setup(struct device *dev)
+{
+ int i;
+ /* Fill in the fields of the device structure with ethernet-generic values.
+ This should be in a common file instead of per-driver. */
+ for (i = 0; i < DEV_NUMBUFFS; i++)
+ skb_queue_head_init(&dev->buffs[i]);
+
+ dev->hard_header = tr_header;
+ dev->rebuild_header = tr_rebuild_header;
+
+ dev->type = ARPHRD_IEEE802;
+ dev->hard_header_len = TR_HLEN;
+ dev->mtu = 2000; /* bug in fragmenter...*/
+ dev->addr_len = TR_ALEN;
+ dev->tx_queue_len = 100; /* Long queues on tr */
+
+ memset(dev->broadcast,0xFF, TR_ALEN);
+
+ /* New-style flags. */
+ dev->flags = IFF_BROADCAST;
+ dev->family = AF_INET;
+ dev->pa_addr = 0;
+ dev->pa_brdaddr = 0;
+ dev->pa_mask = 0;
+ dev->pa_alen = 4;
+}
+
+#endif
+
+int ether_config(struct device *dev, struct ifmap *map)
+{
+ if (map->mem_start != (u_long)(-1))
+ dev->mem_start = map->mem_start;
+ if (map->mem_end != (u_long)(-1))
+ dev->mem_end = map->mem_end;
+ if (map->base_addr != (u_short)(-1))
+ dev->base_addr = map->base_addr;
+ if (map->irq != (u_char)(-1))
+ dev->irq = map->irq;
+ if (map->dma != (u_char)(-1))
+ dev->dma = map->dma;
+ if (map->port != (u_char)(-1))
+ dev->if_port = map->port;
+ return 0;
+}
+
+int register_netdev(struct device *dev)
+{
+ struct device *d = dev_base;
+ unsigned long flags;
+ int i=MAX_ETH_CARDS;
+
+ save_flags(flags);
+ cli();
+
+ if (dev && dev->init) {
+ if (dev->name &&
+ ((dev->name[0] == '\0') || (dev->name[0] == ' '))) {
+ for (i = 0; i < MAX_ETH_CARDS; ++i)
+ if (ethdev_index[i] == NULL) {
+ sprintf(dev->name, "eth%d", i);
+ printk("loading device '%s'...\n", dev->name);
+ ethdev_index[i] = dev;
+ break;
+ }
+ }
+
+ sti(); /* device probes assume interrupts enabled */
+ if (dev->init(dev) != 0) {
+ if (i < MAX_ETH_CARDS) ethdev_index[i] = NULL;
+ restore_flags(flags);
+ return -EIO;
+ }
+ cli();
+
+ /* Add device to end of chain */
+ if (dev_base) {
+ while (d->next)
+ d = d->next;
+ d->next = dev;
+ }
+ else
+ dev_base = dev;
+ dev->next = NULL;
+ }
+ restore_flags(flags);
+ return 0;
+}
+
+void unregister_netdev(struct device *dev)
+{
+ struct device *d = dev_base;
+ unsigned long flags;
+ int i;
+
+ save_flags(flags);
+ cli();
+
+ if (dev == NULL)
+ {
+ printk("was NULL\n");
+ restore_flags(flags);
+ return;
+ }
+ /* else */
+ if (dev->start)
+ printk("ERROR '%s' busy and not MOD_IN_USE.\n", dev->name);
+
+ /*
+ * must jump over main_device+aliases
+ * avoid alias devices unregistration so that only
+ * net_alias module manages them
+ */
+#ifdef CONFIG_NET_ALIAS
+ if (dev_base == dev)
+ dev_base = net_alias_nextdev(dev);
+ else
+ {
+ while(d && (net_alias_nextdev(d) != dev)) /* skip aliases */
+ d = net_alias_nextdev(d);
+
+ if (d && (net_alias_nextdev(d) == dev))
+ {
+ /*
+ * Critical: Bypass by consider devices as blocks (maindev+aliases)
+ */
+ net_alias_nextdev_set(d, net_alias_nextdev(dev));
+ }
+#else
+ if (dev_base == dev)
+ dev_base = dev->next;
+ else
+ {
+ while (d && (d->next != dev))
+ d = d->next;
+
+ if (d && (d->next == dev))
+ {
+ d->next = dev->next;
+ }
+#endif
+ else
+ {
+ printk("unregister_netdev: '%s' not found\n", dev->name);
+ restore_flags(flags);
+ return;
+ }
+ }
+ for (i = 0; i < MAX_ETH_CARDS; ++i)
+ {
+ if (ethdev_index[i] == dev)
+ {
+ ethdev_index[i] = NULL;
+ break;
+ }
+ }
+
+ restore_flags(flags);
+
+ /*
+ * You can i.e use a interfaces in a route though it is not up.
+ * We call close_dev (which is changed: it will down a device even if
+ * dev->flags==0 (but it will not call dev->stop if IFF_UP
+ * is not set).
+ * This will call notifier_call_chain(&netdev_chain, NETDEV_DOWN, dev),
+ * dev_mc_discard(dev), ....
+ */
+
+ dev_close(dev);
+}
+
+
+/*
+ * Local variables:
+ * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c net_init.c"
+ * version-control: t
+ * kept-new-versions: 5
+ * tab-width: 4
+ * End:
+ */
diff --git a/i386/i386at/gpl/linux/net/ni52.c b/i386/i386at/gpl/linux/net/ni52.c
new file mode 100644
index 00000000..13d12359
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/ni52.c
@@ -0,0 +1,1110 @@
+/*
+ * net-3-driver for the NI5210 card (i82586 Ethernet chip)
+ *
+ * This is an extension to the Linux operating system, and is covered by the
+ * same Gnu Public License that covers that work.
+ *
+ * Alphacode 0.62 (95/01/19) for Linux 1.1.82 (or later)
+ * Copyrights (c) 1994,1995 by M.Hipp (Michael.Hipp@student.uni-tuebingen.de)
+ * [feel free to mail ....]
+ *
+ * CAN YOU PLEASE REPORT ME YOUR PERFORMANCE EXPERIENCES !!.
+ *
+ * If you find a bug, please report me:
+ * The kernel panic output and any kmsg from the ni52 driver
+ * the ni5210-driver-version and the linux-kernel version
+ * how many shared memory (memsize) on the netcard,
+ * bootprom: yes/no, base_addr, mem_start
+ * maybe the ni5210-card revision and the i82586 version
+ *
+ * autoprobe for: base_addr: 0x300,0x280,0x360,0x320,0x340
+ * mem_start: 0xc8000,0xd0000,0xd4000,0xd8000 (8K and 16K)
+ *
+ * sources:
+ * skeleton.c from Donald Becker
+ *
+ * I have also done a look in the following sources: (mail me if you need them)
+ * crynwr-packet-driver by Russ Nelson
+ * Garret A. Wollman's (fourth) i82586-driver for BSD
+ * (before getting an i82596 (yes 596 not 586) manual, the existing drivers helped
+ * me a lot to understand this tricky chip.)
+ *
+ * Known Problems:
+ * The internal sysbus seems to be slow. So we often lose packets because of
+ * overruns while receiving from a fast remote host.
+ * This can slow down TCP connections. Maybe the newer ni5210 cards are better.
+ *
+ * IMPORTANT NOTE:
+ * On fast networks, it's a (very) good idea to have 16K shared memory. With
+ * 8K, we can store only 4 receive frames, so it can (easily) happen that a remote
+ * machine 'overruns' our system.
+ *
+ * Known i82586 bugs (I'm sure, there are many more!):
+ * Running the NOP-mode, the i82586 sometimes seems to forget to report
+ * every xmit-interrupt until we restart the CU.
+ * Another MAJOR bug is, that the RU sometimes seems to ignore the EL-Bit
+ * in the RBD-Struct which indicates an end of the RBD queue.
+ * Instead, the RU fetches another (randomly selected and
+ * usually used) RBD and begins to fill it. (Maybe, this happens only if
+ * the last buffer from the previous RFD fits exact into the queue and
+ * the next RFD can't fetch an initial RBD. Anyone knows more? )
+ */
+
+/*
+ * 18.Nov.95: Mcast changes (AC).
+ *
+ * 19.Jan.95: verified (MH)
+ *
+ * 19.Sep.94: Added Multicast support (not tested yet) (MH)
+ *
+ * 18.Sep.94: Workaround for 'EL-Bug'. Removed flexible RBD-handling.
+ * Now, every RFD has exact one RBD. (MH)
+ *
+ * 14.Sep.94: added promiscuous mode, a few cleanups (MH)
+ *
+ * 19.Aug.94: changed request_irq() parameter (MH)
+ *
+ * 20.July.94: removed cleanup bugs, removed a 16K-mem-probe-bug (MH)
+ *
+ * 19.July.94: lotsa cleanups .. (MH)
+ *
+ * 17.July.94: some patches ... verified to run with 1.1.29 (MH)
+ *
+ * 4.July.94: patches for Linux 1.1.24 (MH)
+ *
+ * 26.March.94: patches for Linux 1.0 and iomem-auto-probe (MH)
+ *
+ * 30.Sep.93: Added nop-chain .. driver now runs with only one Xmit-Buff, too (MH)
+ *
+ * < 30.Sep.93: first versions
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include "ni52.h"
+
+#define DEBUG /* debug on */
+#define SYSBUSVAL 1 /* 8 Bit */
+
+#define ni_attn586() {outb(0,dev->base_addr+NI52_ATTENTION);}
+#define ni_reset586() {outb(0,dev->base_addr+NI52_RESET);}
+
+#define make32(ptr16) (p->memtop + (short) (ptr16) )
+#define make24(ptr32) ((char *) (ptr32) - p->base)
+#define make16(ptr32) ((unsigned short) ((unsigned long) (ptr32) - (unsigned long) p->memtop ))
+
+/******************* how to calculate the buffers *****************************
+
+ * IMPORTANT NOTE: if you configure only one NUM_XMIT_BUFFS, the driver works
+ * --------------- in a different (more stable?) mode. Only in this mode it's
+ * possible to configure the driver with 'NO_NOPCOMMANDS'
+
+sizeof(scp)=12; sizeof(scb)=16; sizeof(iscp)=8;
+sizeof(scp)+sizeof(iscp)+sizeof(scb) = 36 = INIT
+sizeof(rfd) = 24; sizeof(rbd) = 12;
+sizeof(tbd) = 8; sizeof(transmit_cmd) = 16;
+sizeof(nop_cmd) = 8;
+
+ * if you don't know the driver, better do not change this values: */
+
+#define RECV_BUFF_SIZE 1524 /* slightly oversized */
+#define XMIT_BUFF_SIZE 1524 /* slightly oversized */
+#define NUM_XMIT_BUFFS 1 /* config for both, 8K and 16K shmem */
+#define NUM_RECV_BUFFS_8 4 /* config for 8K shared mem */
+#define NUM_RECV_BUFFS_16 9 /* config for 16K shared mem */
+#define NO_NOPCOMMANDS /* only possible with NUM_XMIT_BUFFS=1 */
+
+/**************************************************************************/
+
+#define DELAY(x) {int i=jiffies; \
+ if(loops_per_sec == 1) \
+ while(i+(x)>jiffies); \
+ else \
+ __delay((loops_per_sec>>5)*x); \
+ }
+
+/* a much shorter delay: */
+#define DELAY_16(); { __delay( (loops_per_sec>>16)+1 ); }
+
+/* wait for command with timeout: */
+#define WAIT_4_SCB_CMD() { int i; \
+ for(i=0;i<1024;i++) { \
+ if(!p->scb->cmd) break; \
+ DELAY_16(); \
+ if(i == 1023) { \
+ printk("%s: scb_cmd timed out .. resetting i82586\n",dev->name); \
+ ni_reset586(); } } }
+
+
+#define NI52_TOTAL_SIZE 16
+#define NI52_ADDR0 0x02
+#define NI52_ADDR1 0x07
+#define NI52_ADDR2 0x01
+
+#ifndef HAVE_PORTRESERVE
+#define check_region(ioaddr, size) 0
+#define request_region(ioaddr, size,name) do ; while (0)
+#endif
+
+static int ni52_probe1(struct device *dev,int ioaddr);
+static void ni52_interrupt(int irq,struct pt_regs *reg_ptr);
+static int ni52_open(struct device *dev);
+static int ni52_close(struct device *dev);
+static int ni52_send_packet(struct sk_buff *,struct device *);
+static struct enet_statistics *ni52_get_stats(struct device *dev);
+static void set_multicast_list(struct device *dev);
+
+/* helper-functions */
+static int init586(struct device *dev);
+static int check586(struct device *dev,char *where,unsigned size);
+static void alloc586(struct device *dev);
+static void startrecv586(struct device *dev);
+static void *alloc_rfa(struct device *dev,void *ptr);
+static void ni52_rcv_int(struct device *dev);
+static void ni52_xmt_int(struct device *dev);
+static void ni52_rnr_int(struct device *dev);
+
+struct priv
+{
+ struct enet_statistics stats;
+ unsigned long base;
+ char *memtop;
+ volatile struct rfd_struct *rfd_last,*rfd_top,*rfd_first;
+ volatile struct scp_struct *scp; /* volatile is important */
+ volatile struct iscp_struct *iscp; /* volatile is important */
+ volatile struct scb_struct *scb; /* volatile is important */
+ volatile struct tbd_struct *xmit_buffs[NUM_XMIT_BUFFS];
+ volatile struct transmit_cmd_struct *xmit_cmds[NUM_XMIT_BUFFS];
+#if (NUM_XMIT_BUFFS == 1)
+ volatile struct nop_cmd_struct *nop_cmds[2];
+#else
+ volatile struct nop_cmd_struct *nop_cmds[NUM_XMIT_BUFFS];
+#endif
+ volatile int nop_point,num_recv_buffs;
+ volatile char *xmit_cbuffs[NUM_XMIT_BUFFS];
+ volatile int xmit_count,xmit_last;
+};
+
+
+/**********************************************
+ * close device
+ */
+
+static int ni52_close(struct device *dev)
+{
+ free_irq(dev->irq);
+ irq2dev_map[dev->irq] = 0;
+
+ ni_reset586(); /* the hard way to stop the receiver */
+
+ dev->start = 0;
+ dev->tbusy = 0;
+
+ return 0;
+}
+
+/**********************************************
+ * open device
+ */
+
+static int ni52_open(struct device *dev)
+{
+ alloc586(dev);
+ init586(dev);
+ startrecv586(dev);
+
+ if(request_irq(dev->irq, &ni52_interrupt,0,"ni52"))
+ {
+ ni_reset586();
+ return -EAGAIN;
+ }
+ irq2dev_map[dev->irq] = dev;
+
+ dev->interrupt = 0;
+ dev->tbusy = 0;
+ dev->start = 1;
+
+ return 0; /* most done by init */
+}
+
+/**********************************************
+ * Check to see if there's an 82586 out there.
+ */
+
+static int check586(struct device *dev,char *where,unsigned size)
+{
+ struct priv *p = (struct priv *) dev->priv;
+ char *iscp_addrs[2];
+ int i;
+
+ p->base = (unsigned long) where + size - 0x01000000;
+ p->memtop = where + size;
+ p->scp = (struct scp_struct *)(p->base + SCP_DEFAULT_ADDRESS);
+ memset((char *)p->scp,0, sizeof(struct scp_struct));
+ p->scp->sysbus = SYSBUSVAL; /* 1 = 8Bit-Bus, 0 = 16 Bit */
+
+ iscp_addrs[0] = where;
+ iscp_addrs[1]= (char *) p->scp - sizeof(struct iscp_struct);
+
+ for(i=0;i<2;i++)
+ {
+ p->iscp = (struct iscp_struct *) iscp_addrs[i];
+ memset((char *)p->iscp,0, sizeof(struct iscp_struct));
+
+ p->scp->iscp = make24(p->iscp);
+ p->iscp->busy = 1;
+
+ ni_reset586();
+ ni_attn586();
+ DELAY(2); /* wait a while... */
+
+ if(p->iscp->busy) /* i82586 clears 'busy' after successful init */
+ return 0;
+ }
+ return 1;
+}
+
+/******************************************************************
+ * set iscp at the right place, called by ni52_probe1 and open586.
+ */
+
+void alloc586(struct device *dev)
+{
+ struct priv *p = (struct priv *) dev->priv;
+
+ ni_reset586();
+ DELAY(2);
+
+ p->scp = (struct scp_struct *) (p->base + SCP_DEFAULT_ADDRESS);
+ p->scb = (struct scb_struct *) (dev->mem_start);
+ p->iscp = (struct iscp_struct *) ((char *)p->scp - sizeof(struct iscp_struct));
+
+ memset((char *) p->iscp,0,sizeof(struct iscp_struct));
+ memset((char *) p->scp ,0,sizeof(struct scp_struct));
+
+ p->scp->iscp = make24(p->iscp);
+ p->scp->sysbus = SYSBUSVAL;
+ p->iscp->scb_offset = make16(p->scb);
+
+ p->iscp->busy = 1;
+ ni_reset586();
+ ni_attn586();
+
+ DELAY(2);
+
+ if(p->iscp->busy)
+ printk("%s: Init-Problems (alloc).\n",dev->name);
+
+ memset((char *)p->scb,0,sizeof(struct scb_struct));
+}
+
+/**********************************************
+ * probe the ni5210-card
+ */
+
+int ni52_probe(struct device *dev)
+{
+ int *port, ports[] = {0x300, 0x280, 0x360 , 0x320 , 0x340, 0};
+ int base_addr = dev->base_addr;
+
+ if (base_addr > 0x1ff) /* Check a single specified location. */
+ if( (inb(base_addr+NI52_MAGIC1) == NI52_MAGICVAL1) &&
+ (inb(base_addr+NI52_MAGIC2) == NI52_MAGICVAL2))
+ return ni52_probe1(dev, base_addr);
+ else if (base_addr > 0) /* Don't probe at all. */
+ return ENXIO;
+
+ for (port = ports; *port; port++) {
+ int ioaddr = *port;
+ if (check_region(ioaddr, NI52_TOTAL_SIZE))
+ continue;
+ if( !(inb(ioaddr+NI52_MAGIC1) == NI52_MAGICVAL1) ||
+ !(inb(ioaddr+NI52_MAGIC2) == NI52_MAGICVAL2))
+ continue;
+
+ dev->base_addr = ioaddr;
+ if (ni52_probe1(dev, ioaddr) == 0)
+ return 0;
+ }
+
+ dev->base_addr = base_addr;
+ return ENODEV;
+}
+
+static int ni52_probe1(struct device *dev,int ioaddr)
+{
+ long memaddrs[] = { 0xd0000,0xd2000,0xc8000,0xca000,0xd4000,0xd6000,0xd8000, 0 };
+ int i,size;
+
+ for(i=0;i<ETH_ALEN;i++)
+ dev->dev_addr[i] = inb(dev->base_addr+i);
+
+ if(dev->dev_addr[0] != NI52_ADDR0 || dev->dev_addr[1] != NI52_ADDR1
+ || dev->dev_addr[2] != NI52_ADDR2)
+ return ENODEV;
+
+ printk("%s: Ni52 found at %#3lx, ",dev->name,dev->base_addr);
+
+ request_region(ioaddr,NI52_TOTAL_SIZE,"ni52");
+
+ dev->priv = (void *) kmalloc(sizeof(struct priv),GFP_KERNEL);
+ /* warning: we don't free it on errors */
+ if (dev->priv == NULL)
+ return -ENOMEM;
+ memset((char *) dev->priv,0,sizeof(struct priv));
+
+ /*
+ * check (or search) IO-Memory, 8K and 16K
+ */
+ if(dev->mem_start != 0) /* no auto-mem-probe */
+ {
+ size = 0x4000; /* check for 16K mem */
+ if(!check586(dev,(char *) dev->mem_start,size)) {
+ size = 0x2000; /* check for 8K mem */
+ if(!check586(dev,(char *) dev->mem_start,size)) {
+ printk("?memprobe, Can't find memory at 0x%lx!\n",dev->mem_start);
+ return ENODEV;
+ }
+ }
+ }
+ else
+ {
+ for(i=0;;i++)
+ {
+ if(!memaddrs[i]) {
+ printk("?memprobe, Can't find io-memory!\n");
+ return ENODEV;
+ }
+ dev->mem_start = memaddrs[i];
+ size = 0x2000; /* check for 8K mem */
+ if(check586(dev,(char *)dev->mem_start,size)) /* 8K-check */
+ break;
+ size = 0x4000; /* check for 16K mem */
+ if(check586(dev,(char *)dev->mem_start,size)) /* 16K-check */
+ break;
+ }
+ }
+ dev->mem_end = dev->mem_start + size; /* set mem_end showed by 'ifconfig' */
+
+ ((struct priv *) (dev->priv))->base = dev->mem_start + size - 0x01000000;
+ alloc586(dev);
+
+ /* set number of receive-buffs according to memsize */
+ if(size == 0x2000)
+ ((struct priv *) dev->priv)->num_recv_buffs = NUM_RECV_BUFFS_8;
+ else
+ ((struct priv *) dev->priv)->num_recv_buffs = NUM_RECV_BUFFS_16;
+
+ printk("Memaddr: 0x%lx, Memsize: %d, ",dev->mem_start,size);
+
+ if(dev->irq < 2)
+ {
+ autoirq_setup(0);
+ ni_reset586();
+ ni_attn586();
+ if(!(dev->irq = autoirq_report(2)))
+ {
+ printk("?autoirq, Failed to detect IRQ line!\n");
+ return 1;
+ }
+ }
+ else if(dev->irq == 2)
+ dev->irq = 9;
+
+ printk("IRQ %d.\n",dev->irq);
+
+ dev->open = &ni52_open;
+ dev->stop = &ni52_close;
+ dev->get_stats = &ni52_get_stats;
+ dev->hard_start_xmit = &ni52_send_packet;
+ dev->set_multicast_list = &set_multicast_list;
+
+ dev->if_port = 0;
+
+ ether_setup(dev);
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 0;
+
+ return 0;
+}
+
+/**********************************************
+ * init the chip (ni52-interrupt should be disabled?!)
+ * needs a correct 'allocated' memory
+ */
+
+static int init586(struct device *dev)
+{
+ void *ptr;
+ unsigned long s;
+ int i,result=0;
+ struct priv *p = (struct priv *) dev->priv;
+ volatile struct configure_cmd_struct *cfg_cmd;
+ volatile struct iasetup_cmd_struct *ias_cmd;
+ volatile struct tdr_cmd_struct *tdr_cmd;
+ volatile struct mcsetup_cmd_struct *mc_cmd;
+ struct dev_mc_list *dmi=dev->mc_list;
+ int num_addrs=dev->mc_count;
+
+ ptr = (void *) ((char *)p->scb + sizeof(struct scb_struct));
+
+ cfg_cmd = (struct configure_cmd_struct *)ptr; /* configure-command */
+ cfg_cmd->cmd_status = 0;
+ cfg_cmd->cmd_cmd = CMD_CONFIGURE | CMD_LAST;
+ cfg_cmd->cmd_link = 0xffff;
+
+ cfg_cmd->byte_cnt = 0x0a; /* number of cfg bytes */
+ cfg_cmd->fifo = 0x08; /* fifo-limit (8=tx:32/rx:64) */
+ cfg_cmd->sav_bf = 0x40; /* hold or discard bad recv frames (bit 7) */
+ cfg_cmd->adr_len = 0x2e; /* addr_len |!src_insert |pre-len |loopback */
+ cfg_cmd->priority = 0x00;
+ cfg_cmd->ifs = 0x60;
+ cfg_cmd->time_low = 0x00;
+ cfg_cmd->time_high = 0xf2;
+ cfg_cmd->promisc = 0;
+ if(dev->flags&(IFF_ALLMULTI|IFF_PROMISC))
+ {
+ cfg_cmd->promisc=1;
+ dev->flags|=IFF_PROMISC;
+ }
+ cfg_cmd->carr_coll = 0x00;
+
+ p->scb->cbl_offset = make16(cfg_cmd);
+
+ p->scb->cmd = CUC_START; /* cmd.-unit start */
+ ni_attn586();
+
+ s = jiffies; /* warning: only active with interrupts on !! */
+ while(!(cfg_cmd->cmd_status & STAT_COMPL))
+ if(jiffies-s > 30) break;
+
+ if((cfg_cmd->cmd_status & (STAT_OK|STAT_COMPL)) != (STAT_COMPL|STAT_OK))
+ {
+ printk("%s (ni52): configure command failed: %x\n",dev->name,cfg_cmd->cmd_status);
+ return 1;
+ }
+
+ /*
+ * individual address setup
+ */
+ ias_cmd = (struct iasetup_cmd_struct *)ptr;
+
+ ias_cmd->cmd_status = 0;
+ ias_cmd->cmd_cmd = CMD_IASETUP | CMD_LAST;
+ ias_cmd->cmd_link = 0xffff;
+
+ memcpy((char *)&ias_cmd->iaddr,(char *) dev->dev_addr,ETH_ALEN);
+
+ p->scb->cbl_offset = make16(ias_cmd);
+
+ p->scb->cmd = CUC_START; /* cmd.-unit start */
+ ni_attn586();
+
+ s = jiffies;
+ while(!(ias_cmd->cmd_status & STAT_COMPL))
+ if(jiffies-s > 30) break;
+
+ if((ias_cmd->cmd_status & (STAT_OK|STAT_COMPL)) != (STAT_OK|STAT_COMPL)) {
+ printk("%s (ni52): individual address setup command failed: %04x\n",dev->name,ias_cmd->cmd_status);
+ return 1;
+ }
+
+ /*
+ * TDR, wire check .. e.g. no resistor e.t.c
+ */
+ tdr_cmd = (struct tdr_cmd_struct *)ptr;
+
+ tdr_cmd->cmd_status = 0;
+ tdr_cmd->cmd_cmd = CMD_TDR | CMD_LAST;
+ tdr_cmd->cmd_link = 0xffff;
+ tdr_cmd->status = 0;
+
+ p->scb->cbl_offset = make16(tdr_cmd);
+
+ p->scb->cmd = CUC_START; /* cmd.-unit start */
+ ni_attn586();
+
+ s = jiffies;
+ while(!(tdr_cmd->cmd_status & STAT_COMPL))
+ if(jiffies - s > 30) {
+ printk("%s: Problems while running the TDR.\n",dev->name);
+ result = 1;
+ }
+
+ if(!result)
+ {
+ DELAY(2); /* wait for result */
+ result = tdr_cmd->status;
+
+ p->scb->cmd = p->scb->status & STAT_MASK;
+ ni_attn586(); /* ack the interrupts */
+
+ if(result & TDR_LNK_OK) ;
+ else if(result & TDR_XCVR_PRB)
+ printk("%s: TDR: Transceiver problem!\n",dev->name);
+ else if(result & TDR_ET_OPN)
+ printk("%s: TDR: No correct termination %d clocks away.\n",dev->name,result & TDR_TIMEMASK);
+ else if(result & TDR_ET_SRT)
+ {
+ if (result & TDR_TIMEMASK) /* time == 0 -> strange :-) */
+ printk("%s: TDR: Detected a short circuit %d clocks away.\n",dev->name,result & TDR_TIMEMASK);
+ }
+ else
+ printk("%s: TDR: Unknown status %04x\n",dev->name,result);
+ }
+
+ /*
+ * ack interrupts
+ */
+ p->scb->cmd = p->scb->status & STAT_MASK;
+ ni_attn586();
+
+ /*
+ * alloc nop/xmit-cmds
+ */
+#if (NUM_XMIT_BUFFS == 1)
+ for(i=0;i<2;i++)
+ {
+ p->nop_cmds[i] = (struct nop_cmd_struct *)ptr;
+ p->nop_cmds[i]->cmd_cmd = CMD_NOP;
+ p->nop_cmds[i]->cmd_status = 0;
+ p->nop_cmds[i]->cmd_link = make16((p->nop_cmds[i]));
+ ptr = (char *) ptr + sizeof(struct nop_cmd_struct);
+ }
+ p->xmit_cmds[0] = (struct transmit_cmd_struct *)ptr; /* transmit cmd/buff 0 */
+ ptr = (char *) ptr + sizeof(struct transmit_cmd_struct);
+#else
+ for(i=0;i<NUM_XMIT_BUFFS;i++)
+ {
+ p->nop_cmds[i] = (struct nop_cmd_struct *)ptr;
+ p->nop_cmds[i]->cmd_cmd = CMD_NOP;
+ p->nop_cmds[i]->cmd_status = 0;
+ p->nop_cmds[i]->cmd_link = make16((p->nop_cmds[i]));
+ ptr = (char *) ptr + sizeof(struct nop_cmd_struct);
+ p->xmit_cmds[i] = (struct transmit_cmd_struct *)ptr; /*transmit cmd/buff 0*/
+ ptr = (char *) ptr + sizeof(struct transmit_cmd_struct);
+ }
+#endif
+
+ ptr = alloc_rfa(dev,(void *)ptr); /* init receive-frame-area */
+
+ /*
+ * Multicast setup
+ */
+
+ if(dev->mc_count)
+ { /* I don't understand this: do we really need memory after the init? */
+ int len = ((char *) p->iscp - (char *) ptr - 8) / 6;
+ if(len <= 0)
+ {
+ printk("%s: Ooooops, no memory for MC-Setup!\n",dev->name);
+ }
+ else
+ {
+ if(len < num_addrs)
+ {
+ /* BUG - should go ALLMULTI in this case */
+ num_addrs = len;
+ printk("%s: Sorry, can only apply %d MC-Address(es).\n",dev->name,num_addrs);
+ }
+ mc_cmd = (struct mcsetup_cmd_struct *) ptr;
+ mc_cmd->cmd_status = 0;
+ mc_cmd->cmd_cmd = CMD_MCSETUP | CMD_LAST;
+ mc_cmd->cmd_link = 0xffff;
+ mc_cmd->mc_cnt = num_addrs * 6;
+ for(i=0;i<num_addrs;i++)
+ {
+ memcpy((char *) mc_cmd->mc_list[i], dmi->dmi_addr,6);
+ dmi=dmi->next;
+ }
+ p->scb->cbl_offset = make16(mc_cmd);
+ p->scb->cmd = CUC_START;
+ ni_attn586();
+ s = jiffies;
+ while(!(mc_cmd->cmd_status & STAT_COMPL))
+ if(jiffies - s > 30)
+ break;
+ if(!(mc_cmd->cmd_status & STAT_COMPL))
+ printk("%s: Can't apply multicast-address-list.\n",dev->name);
+ }
+ }
+
+ /*
+ * alloc xmit-buffs / init xmit_cmds
+ */
+ for(i=0;i<NUM_XMIT_BUFFS;i++)
+ {
+ p->xmit_cbuffs[i] = (char *)ptr; /* char-buffs */
+ ptr = (char *) ptr + XMIT_BUFF_SIZE;
+ p->xmit_buffs[i] = (struct tbd_struct *)ptr; /* TBD */
+ ptr = (char *) ptr + sizeof(struct tbd_struct);
+ if((void *)ptr > (void *)p->iscp)
+ {
+ printk("%s: not enough shared-mem for your configuration!\n",dev->name);
+ return 1;
+ }
+ memset((char *)(p->xmit_cmds[i]) ,0, sizeof(struct transmit_cmd_struct));
+ memset((char *)(p->xmit_buffs[i]),0, sizeof(struct tbd_struct));
+ p->xmit_cmds[i]->cmd_status = STAT_COMPL;
+ p->xmit_cmds[i]->cmd_cmd = CMD_XMIT | CMD_INT;
+ p->xmit_cmds[i]->tbd_offset = make16((p->xmit_buffs[i]));
+ p->xmit_buffs[i]->next = 0xffff;
+ p->xmit_buffs[i]->buffer = make24((p->xmit_cbuffs[i]));
+ }
+
+ p->xmit_count = 0;
+ p->xmit_last = 0;
+#ifndef NO_NOPCOMMANDS
+ p->nop_point = 0;
+#endif
+
+ /*
+ * 'start transmitter' (nop-loop)
+ */
+#ifndef NO_NOPCOMMANDS
+ p->scb->cbl_offset = make16(p->nop_cmds[0]);
+ p->scb->cmd = CUC_START;
+ ni_attn586();
+ WAIT_4_SCB_CMD();
+#else
+ p->xmit_cmds[0]->cmd_link = 0xffff;
+ p->xmit_cmds[0]->cmd_cmd = CMD_XMIT | CMD_LAST | CMD_INT;
+#endif
+
+ return 0;
+}
+
+/******************************************************
+ * This is a helper routine for ni52_rnr_int() and init586().
+ * It sets up the Receive Frame Area (RFA).
+ */
+
+static void *alloc_rfa(struct device *dev,void *ptr)
+{
+ volatile struct rfd_struct *rfd = (struct rfd_struct *)ptr;
+ volatile struct rbd_struct *rbd;
+ int i;
+ struct priv *p = (struct priv *) dev->priv;
+
+ memset((char *) rfd,0,sizeof(struct rfd_struct)*p->num_recv_buffs);
+ p->rfd_first = rfd;
+
+ for(i = 0; i < p->num_recv_buffs; i++)
+ rfd[i].next = make16(rfd + (i+1) % p->num_recv_buffs);
+ rfd[p->num_recv_buffs-1].last = RFD_SUSP; /* RU suspend */
+
+ ptr = (void *) (rfd + p->num_recv_buffs);
+
+ rbd = (struct rbd_struct *) ptr;
+ ptr = (void *) (rbd + p->num_recv_buffs);
+
+ /* clr descriptors */
+ memset((char *) rbd,0,sizeof(struct rbd_struct)*p->num_recv_buffs);
+
+ for(i=0;i<p->num_recv_buffs;i++)
+ {
+ rbd[i].next = make16((rbd + (i+1) % p->num_recv_buffs));
+ rbd[i].size = RECV_BUFF_SIZE;
+ rbd[i].buffer = make24(ptr);
+ ptr = (char *) ptr + RECV_BUFF_SIZE;
+ }
+
+ p->rfd_top = p->rfd_first;
+ p->rfd_last = p->rfd_first + p->num_recv_buffs - 1;
+
+ p->scb->rfa_offset = make16(p->rfd_first);
+ p->rfd_first->rbd_offset = make16(rbd);
+
+ return ptr;
+}
+
+
+/**************************************************
+ * Interrupt Handler ...
+ */
+
+static void ni52_interrupt(int irq,struct pt_regs *reg_ptr)
+{
+ struct device *dev = (struct device *) irq2dev_map[irq];
+ unsigned short stat;
+ struct priv *p;
+
+ if (dev == NULL) {
+ printk ("ni52-interrupt: irq %d for unknown device.\n",(int) -(((struct pt_regs *)reg_ptr)->orig_eax+2));
+ return;
+ }
+ p = (struct priv *) dev->priv;
+
+ dev->interrupt = 1;
+
+ while((stat=p->scb->status & STAT_MASK))
+ {
+ p->scb->cmd = stat;
+ ni_attn586(); /* ack inter. */
+
+ if(stat & STAT_CX) /* command with I-bit set complete */
+ ni52_xmt_int(dev);
+
+ if(stat & STAT_FR) /* received a frame */
+ ni52_rcv_int(dev);
+
+#ifndef NO_NOPCOMMANDS
+ if(stat & STAT_CNA) /* CU went 'not ready' */
+ {
+ if(dev->start)
+ printk("%s: oops! CU has left active state. stat: %04x/%04x.\n",dev->name,(int) stat,(int) p->scb->status);
+ }
+#endif
+
+ if(stat & STAT_RNR) /* RU went 'not ready' */
+ {
+ if(p->scb->status & RU_SUSPEND) /* special case: RU_SUSPEND */
+ {
+ WAIT_4_SCB_CMD();
+ p->scb->cmd = RUC_RESUME;
+ ni_attn586();
+ }
+ else
+ {
+ printk("%s: Receiver-Unit went 'NOT READY': %04x/%04x.\n",dev->name,(int) stat,(int) p->scb->status);
+ ni52_rnr_int(dev);
+ }
+ }
+ WAIT_4_SCB_CMD(); /* wait for ack. (ni52_xmt_int can be faster than ack!!) */
+ if(p->scb->cmd) /* timed out? */
+ break;
+ }
+
+ dev->interrupt = 0;
+}
+
+/*******************************************************
+ * receive-interrupt
+ */
+
+static void ni52_rcv_int(struct device *dev)
+{
+ int status;
+ unsigned short totlen;
+ struct sk_buff *skb;
+ struct rbd_struct *rbd;
+ struct priv *p = (struct priv *) dev->priv;
+
+ for(;(status = p->rfd_top->status) & STAT_COMPL;)
+ {
+ rbd = (struct rbd_struct *) make32(p->rfd_top->rbd_offset);
+
+ if(status & STAT_OK) /* frame received without error? */
+ {
+ if( (totlen = rbd->status) & RBD_LAST) /* the first and the last buffer? */
+ {
+ totlen &= RBD_MASK; /* length of this frame */
+ rbd->status = 0;
+ skb = (struct sk_buff *) dev_alloc_skb(totlen+2);
+ if(skb != NULL)
+ {
+ skb->dev = dev;
+ skb_reserve(skb,2); /* 16 byte alignment */
+ memcpy(skb_put(skb,totlen),(char *) p->base+(unsigned long) rbd->buffer, totlen);
+ skb->protocol=eth_type_trans(skb,dev);
+ netif_rx(skb);
+ p->stats.rx_packets++;
+ }
+ else
+ p->stats.rx_dropped++;
+ }
+ else
+ {
+ printk("%s: received oversized frame.\n",dev->name);
+ p->stats.rx_dropped++;
+ }
+ }
+ else /* frame !(ok), only with 'save-bad-frames' */
+ {
+ printk("%s: oops! rfd-error-status: %04x\n",dev->name,status);
+ p->stats.rx_errors++;
+ }
+ p->rfd_top->status = 0;
+ p->rfd_top->last = RFD_SUSP;
+ p->rfd_last->last = 0; /* delete RU_SUSP */
+ p->rfd_last = p->rfd_top;
+ p->rfd_top = (struct rfd_struct *) make32(p->rfd_top->next); /* step to next RFD */
+ }
+}
+
+/**********************************************************
+ * handle 'Receiver went not ready'.
+ */
+
+static void ni52_rnr_int(struct device *dev)
+{
+ struct priv *p = (struct priv *) dev->priv;
+
+ p->stats.rx_errors++;
+
+ WAIT_4_SCB_CMD(); /* wait for the last cmd */
+ p->scb->cmd = RUC_ABORT; /* usually the RU is in the 'no resource'-state .. abort it now. */
+ ni_attn586();
+ WAIT_4_SCB_CMD(); /* wait for accept cmd. */
+
+ alloc_rfa(dev,(char *)p->rfd_first);
+ startrecv586(dev); /* restart RU */
+
+ printk("%s: Receive-Unit restarted. Status: %04x\n",dev->name,p->scb->status);
+
+}
+
+/**********************************************************
+ * handle xmit - interrupt
+ */
+
+static void ni52_xmt_int(struct device *dev)
+{
+ int status;
+ struct priv *p = (struct priv *) dev->priv;
+
+ status = p->xmit_cmds[p->xmit_last]->cmd_status;
+ if(!(status & STAT_COMPL))
+ printk("%s: strange .. xmit-int without a 'COMPLETE'\n",dev->name);
+
+ if(status & STAT_OK)
+ {
+ p->stats.tx_packets++;
+ p->stats.collisions += (status & TCMD_MAXCOLLMASK);
+ }
+ else
+ {
+ p->stats.tx_errors++;
+ if(status & TCMD_LATECOLL) {
+ printk("%s: late collision detected.\n",dev->name);
+ p->stats.collisions++;
+ }
+ else if(status & TCMD_NOCARRIER) {
+ p->stats.tx_carrier_errors++;
+ printk("%s: no carrier detected.\n",dev->name);
+ }
+ else if(status & TCMD_LOSTCTS)
+ printk("%s: loss of CTS detected.\n",dev->name);
+ else if(status & TCMD_UNDERRUN) {
+ p->stats.tx_fifo_errors++;
+ printk("%s: DMA underrun detected.\n",dev->name);
+ }
+ else if(status & TCMD_MAXCOLL) {
+ printk("%s: Max. collisions exceeded.\n",dev->name);
+ p->stats.collisions += 16;
+ }
+ }
+
+#if (NUM_XMIT_BUFFS != 1)
+ if( (++p->xmit_last) == NUM_XMIT_BUFFS)
+ p->xmit_last = 0;
+#endif
+
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+}
+
+/***********************************************************
+ * (re)start the receiver
+ */
+
+static void startrecv586(struct device *dev)
+{
+ struct priv *p = (struct priv *) dev->priv;
+
+ p->scb->rfa_offset = make16(p->rfd_first);
+ p->scb->cmd = RUC_START;
+ ni_attn586(); /* start cmd. */
+ WAIT_4_SCB_CMD(); /* wait for accept cmd. (no timeout!!) */
+}
+
+/******************************************************
+ * send frame
+ */
+
+static int ni52_send_packet(struct sk_buff *skb, struct device *dev)
+{
+ int len,i;
+#ifndef NO_NOPCOMMANDS
+ int next_nop;
+#endif
+ struct priv *p = (struct priv *) dev->priv;
+
+ if(dev->tbusy)
+ {
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < 5)
+ return 1;
+
+ if(p->scb->status & CU_ACTIVE) /* COMMAND-UNIT active? */
+ {
+ dev->tbusy = 0;
+#ifdef DEBUG
+ printk("%s: strange ... timeout with CU active?!?\n",dev->name);
+ printk("%s: X0: %04x N0: %04x N1: %04x %d\n",dev->name,(int)p->xmit_cmds[0]->cmd_status,(int)p->nop_cmds[0]->cmd_status,(int)p->nop_cmds[1]->cmd_status,(int)p->nop_point);
+#endif
+ p->scb->cmd = CUC_ABORT;
+ ni_attn586();
+ WAIT_4_SCB_CMD();
+ p->scb->cbl_offset = make16(p->nop_cmds[p->nop_point]);
+ p->scb->cmd = CUC_START;
+ ni_attn586();
+ WAIT_4_SCB_CMD();
+ dev->trans_start = jiffies;
+ return 0;
+ }
+ else
+ {
+#ifdef DEBUG
+ printk("%s: xmitter timed out, try to restart! stat: %04x\n",dev->name,p->scb->status);
+ printk("%s: command-stats: %04x %04x\n",dev->name,p->xmit_cmds[0]->cmd_status,p->xmit_cmds[1]->cmd_status);
+#endif
+ ni52_close(dev);
+ ni52_open(dev);
+ }
+ dev->trans_start = jiffies;
+ return 0;
+ }
+
+ if(skb == NULL)
+ {
+ dev_tint(dev);
+ return 0;
+ }
+
+ if (skb->len <= 0)
+ return 0;
+ if(skb->len > XMIT_BUFF_SIZE)
+ {
+ printk("%s: Sorry, max. framelength is %d bytes. The length of your frame is %ld bytes.\n",dev->name,XMIT_BUFF_SIZE,skb->len);
+ return 0;
+ }
+
+ if (set_bit(0, (void*)&dev->tbusy) != 0)
+ printk("%s: Transmitter access conflict.\n", dev->name);
+ else
+ {
+ memcpy((char *)p->xmit_cbuffs[p->xmit_count],(char *)(skb->data),skb->len);
+ len = (ETH_ZLEN < skb->len) ? skb->len : ETH_ZLEN;
+
+#if (NUM_XMIT_BUFFS == 1)
+# ifdef NO_NOPCOMMANDS
+ p->xmit_buffs[0]->size = TBD_LAST | len;
+ for(i=0;i<16;i++)
+ {
+ p->scb->cbl_offset = make16(p->xmit_cmds[0]);
+ p->scb->cmd = CUC_START;
+ p->xmit_cmds[0]->cmd_status = 0;
+
+ ni_attn586();
+ dev->trans_start = jiffies;
+ if(!i)
+ dev_kfree_skb(skb,FREE_WRITE);
+ WAIT_4_SCB_CMD();
+ if( (p->scb->status & CU_ACTIVE)) /* test it, because CU sometimes doesn't start immediately */
+ break;
+ if(p->xmit_cmds[0]->cmd_status)
+ break;
+ if(i==15)
+ printk("%s: Can't start transmit-command.\n",dev->name);
+ }
+# else
+ next_nop = (p->nop_point + 1) & 0x1;
+ p->xmit_buffs[0]->size = TBD_LAST | len;
+
+ p->xmit_cmds[0]->cmd_link = p->nop_cmds[next_nop]->cmd_link
+ = make16((p->nop_cmds[next_nop]));
+ p->xmit_cmds[0]->cmd_status = p->nop_cmds[next_nop]->cmd_status = 0;
+
+ p->nop_cmds[p->nop_point]->cmd_link = make16((p->xmit_cmds[0]));
+ dev->trans_start = jiffies;
+ p->nop_point = next_nop;
+ dev_kfree_skb(skb,FREE_WRITE);
+# endif
+#else
+ p->xmit_buffs[p->xmit_count]->size = TBD_LAST | len;
+ if( (next_nop = p->xmit_count + 1) == NUM_XMIT_BUFFS )
+ next_nop = 0;
+
+ p->xmit_cmds[p->xmit_count]->cmd_status = 0;
+ p->xmit_cmds[p->xmit_count]->cmd_link = p->nop_cmds[next_nop]->cmd_link
+ = make16((p->nop_cmds[next_nop]));
+ p->nop_cmds[next_nop]->cmd_status = 0;
+
+ p->nop_cmds[p->xmit_count]->cmd_link = make16((p->xmit_cmds[p->xmit_count]));
+ dev->trans_start = jiffies;
+ p->xmit_count = next_nop;
+
+ cli();
+ if(p->xmit_count != p->xmit_last)
+ dev->tbusy = 0;
+ sti();
+ dev_kfree_skb(skb,FREE_WRITE);
+#endif
+ }
+ return 0;
+}
+
+/*******************************************
+ * Someone wanna have the statistics
+ */
+
+static struct enet_statistics *ni52_get_stats(struct device *dev)
+{
+ struct priv *p = (struct priv *) dev->priv;
+ unsigned short crc,aln,rsc,ovrn;
+
+ crc = p->scb->crc_errs; /* get error-statistic from the ni82586 */
+ p->scb->crc_errs -= crc;
+ aln = p->scb->aln_errs;
+ p->scb->aln_errs -= aln;
+ rsc = p->scb->rsc_errs;
+ p->scb->rsc_errs -= rsc;
+ ovrn = p->scb->ovrn_errs;
+ p->scb->ovrn_errs -= ovrn;
+
+ p->stats.rx_crc_errors += crc;
+ p->stats.rx_fifo_errors += ovrn;
+ p->stats.rx_frame_errors += aln;
+ p->stats.rx_dropped += rsc;
+
+ return &p->stats;
+}
+
+/********************************************************
+ * Set MC list ..
+ */
+
+static void set_multicast_list(struct device *dev)
+{
+ if(!dev->start)
+ {
+ printk("%s: Can't apply promiscuous/multicastmode to a not running interface.\n",dev->name);
+ return;
+ }
+
+ dev->start = 0;
+ alloc586(dev);
+ init586(dev);
+ startrecv586(dev);
+ dev->start = 1;
+}
+
+/*
+ * END: linux/drivers/net/ni52.c
+ */
diff --git a/i386/i386at/gpl/linux/net/ni52.h b/i386/i386at/gpl/linux/net/ni52.h
new file mode 100644
index 00000000..23b0a0e8
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/ni52.h
@@ -0,0 +1,284 @@
+/*
+ * Intel i82586 Ethernet definitions
+ *
+ * This is an extension to the Linux operating system, and is covered by the
+ * same Gnu Public License that covers that work.
+ *
+ * copyrights (c) 1994 by Michael Hipp (mhipp@student.uni-tuebingen.de)
+ *
+ * I have done a look in the following sources:
+ * crynwr-packet-driver by Russ Nelson
+ * Garret A. Wollman's i82586-driver for BSD
+ */
+
+
+#define NI52_RESET 0 /* writing to this address, resets the i82586 */
+#define NI52_ATTENTION 1 /* channel attention, kick the 586 */
+#define NI52_TENA 3 /* 2-5 possibly wrong, Xmit enable */
+#define NI52_TDIS 2 /* Xmit disable */
+#define NI52_INTENA 5 /* Interrupt enable */
+#define NI52_INTDIS 4 /* Interrupt disable */
+#define NI52_MAGIC1 6 /* dunno exact function */
+#define NI52_MAGIC2 7 /* dunno exact function */
+
+#define NI52_MAGICVAL1 0x00 /* magic-values for ni5210 card */
+#define NI52_MAGICVAL2 0x55
+
+/*
+ * where to find the System Configuration Pointer (SCP)
+ */
+#define SCP_DEFAULT_ADDRESS 0xfffff4
+
+
+/*
+ * System Configuration Pointer Struct
+ */
+
+struct scp_struct
+{
+ unsigned short zero_dum0; /* has to be zero */
+ unsigned char sysbus; /* 0=16Bit,1=8Bit */
+ unsigned char zero_dum1; /* has to be zero for 586 */
+ unsigned short zero_dum2;
+ unsigned short zero_dum3;
+ char *iscp; /* pointer to the iscp-block */
+};
+
+
+/*
+ * Intermediate System Configuration Pointer (ISCP)
+ */
+struct iscp_struct
+{
+ unsigned char busy; /* 586 clears after successful init */
+ unsigned char zero_dummy; /* hast to be zero */
+ unsigned short scb_offset; /* pointeroffset to the scb_base */
+ char *scb_base; /* base-address of all 16-bit offsets */
+};
+
+/*
+ * System Control Block (SCB)
+ */
+struct scb_struct
+{
+ unsigned short status; /* status word */
+ unsigned short cmd; /* command word */
+ unsigned short cbl_offset; /* pointeroffset, command block list */
+ unsigned short rfa_offset; /* pointeroffset, receive frame area */
+ unsigned short crc_errs; /* CRC-Error counter */
+ unsigned short aln_errs; /* allignmenterror counter */
+ unsigned short rsc_errs; /* Resourceerror counter */
+ unsigned short ovrn_errs; /* OVerrunerror counter */
+};
+
+/*
+ * possible command values for the command word
+ */
+#define RUC_MASK 0x0070 /* mask for RU commands */
+#define RUC_NOP 0x0000 /* NOP-command */
+#define RUC_START 0x0010 /* start RU */
+#define RUC_RESUME 0x0020 /* resume RU after suspend */
+#define RUC_SUSPEND 0x0030 /* suspend RU */
+#define RUC_ABORT 0x0040 /* abort receiver operation immediately */
+
+#define CUC_MASK 0x0700 /* mask for CU command */
+#define CUC_NOP 0x0000 /* NOP-command */
+#define CUC_START 0x0100 /* start execution of 1. cmd on the CBL */
+#define CUC_RESUME 0x0200 /* resume after suspend */
+#define CUC_SUSPEND 0x0300 /* Suspend CU */
+#define CUC_ABORT 0x0400 /* abort command operation immediately */
+
+#define ACK_MASK 0xf000 /* mask for ACK command */
+#define ACK_CX 0x8000 /* acknowledges STAT_CX */
+#define ACK_FR 0x4000 /* ack. STAT_FR */
+#define ACK_CNA 0x2000 /* ack. STAT_CNA */
+#define ACK_RNR 0x1000 /* ack. STAT_RNR */
+
+/*
+ * possible status values for the status word
+ */
+#define STAT_MASK 0xf000 /* mask for cause of interrupt */
+#define STAT_CX 0x8000 /* CU finished cmd with its I bit set */
+#define STAT_FR 0x4000 /* RU finished receiving a frame */
+#define STAT_CNA 0x2000 /* CU left active state */
+#define STAT_RNR 0x1000 /* RU left ready state */
+
+#define CU_STATUS 0x700 /* CU status, 0=idle */
+#define CU_SUSPEND 0x100 /* CU is suspended */
+#define CU_ACTIVE 0x200 /* CU is active */
+
+#define RU_STATUS 0x70 /* RU status, 0=idle */
+#define RU_SUSPEND 0x10 /* RU suspended */
+#define RU_NOSPACE 0x20 /* RU no resources */
+#define RU_READY 0x40 /* RU is ready */
+
+/*
+ * Receive Frame Descriptor (RFD)
+ */
+struct rfd_struct
+{
+ unsigned short status; /* status word */
+ unsigned short last; /* Bit15,Last Frame on List / Bit14,suspend */
+ unsigned short next; /* linkoffset to next RFD */
+ unsigned short rbd_offset; /* pointeroffset to RBD-buffer */
+ unsigned char dest[6]; /* ethernet-address, destination */
+ unsigned char source[6]; /* ethernet-address, source */
+ unsigned short length; /* 802.3 frame-length */
+ unsigned short zero_dummy; /* dummy */
+};
+
+#define RFD_LAST 0x8000 /* last: last rfd in the list */
+#define RFD_SUSP 0x4000 /* last: suspend RU after */
+#define RFD_ERRMASK 0x0fe1 /* status: errormask */
+#define RFD_MATCHADD 0x0002 /* status: Destinationaddress !matches IA */
+#define RFD_RNR 0x0200 /* status: receiver out of resources */
+
+/*
+ * Receive Buffer Descriptor (RBD)
+ */
+struct rbd_struct
+{
+ unsigned short status; /* status word,number of used bytes in buff */
+ unsigned short next; /* pointeroffset to next RBD */
+ char *buffer; /* receive buffer address pointer */
+ unsigned short size; /* size of this buffer */
+ unsigned short zero_dummy; /* dummy */
+};
+
+#define RBD_LAST 0x8000 /* last buffer */
+#define RBD_USED 0x4000 /* this buffer has data */
+#define RBD_MASK 0x3fff /* size-mask for length */
+
+/*
+ * Statusvalues for Commands/RFD
+ */
+#define STAT_COMPL 0x8000 /* status: frame/command is complete */
+#define STAT_BUSY 0x4000 /* status: frame/command is busy */
+#define STAT_OK 0x2000 /* status: frame/command is ok */
+
+/*
+ * Action-Commands
+ */
+#define CMD_NOP 0x0000 /* NOP */
+#define CMD_IASETUP 0x0001 /* initial address setup command */
+#define CMD_CONFIGURE 0x0002 /* configure command */
+#define CMD_MCSETUP 0x0003 /* MC setup command */
+#define CMD_XMIT 0x0004 /* transmit command */
+#define CMD_TDR 0x0005 /* time domain reflectometer (TDR) command */
+#define CMD_DUMP 0x0006 /* dump command */
+#define CMD_DIAGNOSE 0x0007 /* diagnose command */
+
+/*
+ * Action command bits
+ */
+#define CMD_LAST 0x8000 /* indicates last command in the CBL */
+#define CMD_SUSPEND 0x4000 /* suspend CU after this CB */
+#define CMD_INT 0x2000 /* generate interrupt after execution */
+
+/*
+ * NOP - command
+ */
+struct nop_cmd_struct
+{
+ unsigned short cmd_status; /* status of this command */
+ unsigned short cmd_cmd; /* the command itself (+bits) */
+ unsigned short cmd_link; /* offsetpointer to next command */
+};
+
+/*
+ * IA Setup command
+ */
+struct iasetup_cmd_struct
+{
+ unsigned short cmd_status;
+ unsigned short cmd_cmd;
+ unsigned short cmd_link;
+ unsigned char iaddr[6];
+};
+
+/*
+ * Configure command
+ */
+struct configure_cmd_struct
+{
+ unsigned short cmd_status;
+ unsigned short cmd_cmd;
+ unsigned short cmd_link;
+ unsigned char byte_cnt; /* size of the config-cmd */
+ unsigned char fifo; /* fifo/recv monitor */
+ unsigned char sav_bf; /* save bad frames (bit7=1)*/
+ unsigned char adr_len; /* adr_len(0-2),al_loc(3),pream(4-5),loopbak(6-7)*/
+ unsigned char priority; /* lin_prio(0-2),exp_prio(4-6),bof_metd(7) */
+ unsigned char ifs; /* inter frame spacing */
+ unsigned char time_low; /* slot time low */
+ unsigned char time_high; /* slot time high(0-2) and max. retries(4-7) */
+ unsigned char promisc; /* promisc-mode(0) , et al (1-7) */
+ unsigned char carr_coll; /* carrier(0-3)/collision(4-7) stuff */
+ unsigned char fram_len; /* minimal frame len */
+ unsigned char dummy; /* dummy */
+};
+
+/*
+ * Multicast Setup command
+ */
+struct mcsetup_cmd_struct
+{
+ unsigned short cmd_status;
+ unsigned short cmd_cmd;
+ unsigned short cmd_link;
+ unsigned short mc_cnt; /* number of bytes in the MC-List */
+ unsigned char mc_list[0][6]; /* pointer to 6 bytes entries */
+};
+
+/*
+ * transmit command
+ */
+struct transmit_cmd_struct
+{
+ unsigned short cmd_status;
+ unsigned short cmd_cmd;
+ unsigned short cmd_link;
+ unsigned short tbd_offset; /* pointeroffset to TBD */
+ unsigned char dest[6]; /* destination address of the frame */
+ unsigned short length; /* user defined: 802.3 length / Ether type */
+};
+
+#define TCMD_ERRMASK 0x0fa0
+#define TCMD_MAXCOLLMASK 0x000f
+#define TCMD_MAXCOLL 0x0020
+#define TCMD_HEARTBEAT 0x0040
+#define TCMD_DEFERRED 0x0080
+#define TCMD_UNDERRUN 0x0100
+#define TCMD_LOSTCTS 0x0200
+#define TCMD_NOCARRIER 0x0400
+#define TCMD_LATECOLL 0x0800
+
+struct tdr_cmd_struct
+{
+ unsigned short cmd_status;
+ unsigned short cmd_cmd;
+ unsigned short cmd_link;
+ unsigned short status;
+};
+
+#define TDR_LNK_OK 0x8000 /* No link problem identified */
+#define TDR_XCVR_PRB 0x4000 /* indicates a transceiver problem */
+#define TDR_ET_OPN 0x2000 /* open, no correct termination */
+#define TDR_ET_SRT 0x1000 /* TDR detected a short circuit */
+#define TDR_TIMEMASK 0x07ff /* mask for the time field */
+
+/*
+ * Transmit Buffer Descriptor (TBD)
+ */
+struct tbd_struct
+{
+ unsigned short size; /* size + EOF-Flag(15) */
+ unsigned short next; /* pointeroffset to next TBD */
+ char *buffer; /* pointer to buffer */
+};
+
+#define TBD_LAST 0x8000 /* EOF-Flag, indicates last buffer in list */
+
+
+
+
diff --git a/i386/i386at/gpl/linux/net/ni65.c b/i386/i386at/gpl/linux/net/ni65.c
new file mode 100644
index 00000000..44a58112
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/ni65.c
@@ -0,0 +1,648 @@
+/*
+ * ni6510 (am7990 'lance' chip) driver for Linux-net-3 by MH
+ * Alphacode v0.33 (94/08/22) for 1.1.47 (or later)
+ *
+ * ----------------------------------------------------------
+ * WARNING: DOESN'T WORK ON MACHINES WITH MORE THAN 16MB !!!!
+ * ----------------------------------------------------------
+ *
+ * copyright (c) 1994 M.Hipp
+ *
+ * This is an extension to the Linux operating system, and is covered by the
+ * same Gnu Public License that covers the Linux-kernel.
+ *
+ * comments/bugs/suggestions can be sent to:
+ * Michael Hipp
+ * email: mhipp@student.uni-tuebingen.de
+ *
+ * sources:
+ * some things are from the 'ni6510-packet-driver for dos by Russ Nelson'
+ * and from the original drivers by D.Becker
+ */
+
+/*
+ * Nov.18: multicast tweaked (AC).
+ *
+ * Aug.22: changes in xmit_intr (ack more than one xmitted-packet), ni65_send_packet (p->lock) (MH)
+ *
+ * July.16: fixed bugs in recv_skb and skb-alloc stuff (MH)
+ */
+
+/*
+ * known BUGS: 16MB limit
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include "ni65.h"
+
+/************************************
+ * skeleton-stuff
+ */
+
+#ifndef HAVE_PORTRESERVE
+#define check_region(ioaddr, size) 0
+#define request_region(ioaddr, size,name) do ; while (0)
+#endif
+
+#ifndef NET_DEBUG
+#define NET_DEBUG 2
+#endif
+/*
+static unsigned int net_debug = NET_DEBUG;
+*/
+
+#define NI65_TOTAL_SIZE 16
+
+#define SA_ADDR0 0x02
+#define SA_ADDR1 0x07
+#define SA_ADDR2 0x01
+#define CARD_ID0 0x00
+#define CARD_ID1 0x55
+
+/*****************************************/
+
+#define PORT dev->base_addr
+
+#define RMDNUM 8
+#define RMDNUMMASK 0x6000 /* log2(RMDNUM)<<13 */
+#define TMDNUM 4
+#define TMDNUMMASK 0x4000 /* log2(TMDNUM)<<13 */
+
+#define R_BUF_SIZE 1518
+#define T_BUF_SIZE 1518
+
+#define MEMSIZE 8+RMDNUM*8+TMDNUM*8
+
+#define L_DATAREG 0x00
+#define L_ADDRREG 0x02
+
+#define L_RESET 0x04
+#define L_CONFIG 0x05
+#define L_EBASE 0x08
+
+/*
+ * to access the am7990-regs, you have to write
+ * reg-number into L_ADDRREG, then you can access it using L_DATAREG
+ */
+#define CSR0 0x00
+#define CSR1 0x01
+#define CSR2 0x02
+#define CSR3 0x03
+
+/* if you #define NO_STATIC the driver is faster but you will have (more) problems with >16MB memory */
+#undef NO_STATIC
+
+#define writereg(val,reg) {outw(reg,PORT+L_ADDRREG);inw(PORT+L_ADDRREG); \
+ outw(val,PORT+L_DATAREG);inw(PORT+L_DATAREG);}
+#define readreg(reg) (outw(reg,PORT+L_ADDRREG),inw(PORT+L_ADDRREG),\
+ inw(PORT+L_DATAREG))
+#define writedatareg(val) {outw(val,PORT+L_DATAREG);inw(PORT+L_DATAREG);}
+
+static int ni65_probe1(struct device *dev,int);
+static void ni65_interrupt(int irq, struct pt_regs *regs);
+ static void recv_intr(struct device *dev);
+ static void xmit_intr(struct device *dev);
+static int ni65_open(struct device *dev);
+ static int am7990_reinit(struct device *dev);
+static int ni65_send_packet(struct sk_buff *skb, struct device *dev);
+static int ni65_close(struct device *dev);
+static struct enet_statistics *ni65_get_stats(struct device *);
+
+static void set_multicast_list(struct device *dev);
+
+struct priv
+{
+ struct init_block ib;
+ void *memptr;
+ struct rmd *rmdhead;
+ struct tmd *tmdhead;
+ int rmdnum;
+ int tmdnum,tmdlast;
+ struct sk_buff *recv_skb[RMDNUM];
+ void *tmdbufs[TMDNUM];
+ int lock,xmit_queued;
+ struct enet_statistics stats;
+};
+
+int irqtab[] = { 9,12,15,5 }; /* irq config-translate */
+int dmatab[] = { 0,3,5,6 }; /* dma config-translate */
+
+/*
+ * open (most done by init)
+ */
+
+static int ni65_open(struct device *dev)
+{
+ if(am7990_reinit(dev))
+ {
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+ return 0;
+ }
+ else
+ {
+ dev->start = 0;
+ return -EAGAIN;
+ }
+}
+
+static int ni65_close(struct device *dev)
+{
+ outw(0,PORT+L_RESET); /* that's the hard way */
+ dev->tbusy = 1;
+ dev->start = 0;
+ return 0;
+}
+
+/*
+ * Probe The Card (not the lance-chip)
+ * and set hardaddress
+ */
+
+int ni65_probe(struct device *dev)
+{
+ int *port, ports[] = {0x300,0x320,0x340,0x360, 0};
+ int base_addr = dev->base_addr;
+
+ if (base_addr > 0x1ff) /* Check a single specified location. */
+ return ni65_probe1(dev, base_addr);
+ else if (base_addr > 0) /* Don't probe at all. */
+ return ENXIO;
+
+ for (port = ports; *port; port++)
+ {
+ int ioaddr = *port;
+ if (check_region(ioaddr, NI65_TOTAL_SIZE))
+ continue;
+ if( !(inb(ioaddr+L_EBASE+6) == CARD_ID0) ||
+ !(inb(ioaddr+L_EBASE+7) == CARD_ID1) )
+ continue;
+ dev->base_addr = ioaddr;
+ if (ni65_probe1(dev, ioaddr) == 0)
+ return 0;
+ }
+
+ dev->base_addr = base_addr;
+ return ENODEV;
+}
+
+
+static int ni65_probe1(struct device *dev,int ioaddr)
+{
+ int i;
+ unsigned char station_addr[6];
+ struct priv *p;
+
+ for(i=0;i<6;i++)
+ station_addr[i] = dev->dev_addr[i] = inb(PORT+L_EBASE+i);
+
+ if(station_addr[0] != SA_ADDR0 || station_addr[1] != SA_ADDR1)
+ {
+ printk("%s: wrong Hardaddress \n",dev->name);
+ return ENODEV;
+ }
+
+ if(dev->irq == 0)
+ dev->irq = irqtab[(inw(PORT+L_CONFIG)>>2)&3];
+ if(dev->dma == 0)
+ dev->dma = dmatab[inw(PORT+L_CONFIG)&3];
+
+ printk("%s: %s found at %#3lx, IRQ %d DMA %d.\n", dev->name,
+ "network card", dev->base_addr, dev->irq,dev->dma);
+
+ {
+ int irqval = request_irq(dev->irq, &ni65_interrupt,0,"ni65");
+ if (irqval) {
+ printk ("%s: unable to get IRQ %d (irqval=%d).\n",
+ dev->name,dev->irq, irqval);
+ return EAGAIN;
+ }
+ if(request_dma(dev->dma, "ni65") != 0)
+ {
+ printk("%s: Can't request dma-channel %d\n",dev->name,(int) dev->dma);
+ free_irq(dev->irq);
+ return EAGAIN;
+ }
+ }
+ irq2dev_map[dev->irq] = dev;
+
+ /* Grab the region so we can find another board if autoIRQ fails. */
+ request_region(ioaddr,NI65_TOTAL_SIZE,"ni65");
+
+ p = dev->priv = (void *) kmalloc(sizeof(struct priv),GFP_KERNEL);
+ if (p == NULL)
+ return -ENOMEM;
+ memset((char *) dev->priv,0,sizeof(struct priv));
+
+ dev->open = ni65_open;
+ dev->stop = ni65_close;
+ dev->hard_start_xmit = ni65_send_packet;
+ dev->get_stats = ni65_get_stats;
+ dev->set_multicast_list = set_multicast_list;
+
+ ether_setup(dev);
+
+ dev->flags &= ~IFF_MULTICAST;
+ dev->interrupt = 0;
+ dev->tbusy = 0;
+ dev->start = 0;
+
+ if( (p->memptr = kmalloc(MEMSIZE,GFP_KERNEL)) == NULL) {
+ printk("%s: Can't alloc TMD/RMD-buffer.\n",dev->name);
+ return EAGAIN;
+ }
+ if( (unsigned long) (p->memptr + MEMSIZE) & 0xff000000) {
+ printk("%s: Can't alloc TMD/RMD buffer in lower 16MB!\n",dev->name);
+ return EAGAIN;
+ }
+ p->tmdhead = (struct tmd *) ((( (unsigned long)p->memptr ) + 8) & 0xfffffff8);
+ p->rmdhead = (struct rmd *) (p->tmdhead + TMDNUM);
+
+#ifndef NO_STATIC
+ for(i=0;i<TMDNUM;i++)
+ {
+ if( (p->tmdbufs[i] = kmalloc(T_BUF_SIZE,GFP_ATOMIC)) == NULL) {
+ printk("%s: Can't alloc Xmit-Mem.\n",dev->name);
+ return EAGAIN;
+ }
+ if( (unsigned long) (p->tmdbufs[i]+T_BUF_SIZE) & 0xff000000) {
+ printk("%s: Can't alloc Xmit-Mem in lower 16MB!\n",dev->name);
+ return EAGAIN;
+ }
+ }
+#endif
+
+ for(i=0;i<RMDNUM;i++)
+ {
+ if( (p->recv_skb[i] = dev_alloc_skb(R_BUF_SIZE)) == NULL) {
+ printk("%s: unable to alloc recv-mem\n",dev->name);
+ return EAGAIN;
+ }
+ if( (unsigned long) (p->recv_skb[i]->data + R_BUF_SIZE) & 0xff000000) {
+ printk("%s: unable to alloc receive-memory in lower 16MB!\n",dev->name);
+ return EAGAIN;
+ }
+ }
+
+ return 0; /* we've found everything */
+}
+
+/*
+ * init lance (write init-values .. init-buffers) (open-helper)
+ */
+
+static int am7990_reinit(struct device *dev)
+{
+ int i,j;
+ struct tmd *tmdp;
+ struct rmd *rmdp;
+ struct priv *p = (struct priv *) dev->priv;
+
+ p->lock = 0;
+ p->xmit_queued = 0;
+
+ disable_dma(dev->dma); /* I've never worked with dma, but we do it like the packetdriver */
+ set_dma_mode(dev->dma,DMA_MODE_CASCADE);
+ enable_dma(dev->dma);
+
+ outw(0,PORT+L_RESET); /* first: reset the card */
+ if(inw(PORT+L_DATAREG) != 0x4)
+ {
+ printk("%s: can't RESET ni6510 card: %04x\n",dev->name,(int) inw(PORT+L_DATAREG));
+ disable_dma(dev->dma);
+ free_dma(dev->dma);
+ free_irq(dev->irq);
+ return 0;
+ }
+
+ /* here: memset all buffs to zero */
+
+ memset(p->memptr,0,MEMSIZE);
+
+ p->tmdnum = 0; p->tmdlast = 0;
+ for(i=0;i<TMDNUM;i++)
+ {
+ tmdp = p->tmdhead + i;
+#ifndef NO_STATIC
+ tmdp->u.buffer = (unsigned long) p->tmdbufs[i];
+#endif
+ tmdp->u.s.status = XMIT_START | XMIT_END;
+ }
+
+ p->rmdnum = 0;
+ for(i=0;i<RMDNUM;i++)
+ {
+ rmdp = p->rmdhead + i;
+ rmdp->u.buffer = (unsigned long) p->recv_skb[i]->data;
+ rmdp->u.s.status = RCV_OWN;
+ rmdp->blen = -R_BUF_SIZE;
+ rmdp->mlen = 0;
+ }
+
+ for(i=0;i<6;i++)
+ {
+ p->ib.eaddr[i] = dev->dev_addr[i];
+ }
+ p->ib.mode = 0;
+ for(i=0;i<8;i++)
+ p->ib.filter[i] = 0;
+ p->ib.trplow = (unsigned short) (( (unsigned long) p->tmdhead ) & 0xffff);
+ p->ib.trphigh = (unsigned short) ((( (unsigned long) p->tmdhead )>>16) & 0x00ff) | TMDNUMMASK;
+ p->ib.rrplow = (unsigned short) (( (unsigned long) p->rmdhead ) & 0xffff);
+ p->ib.rrphigh = (unsigned short) ((( (unsigned long) p->rmdhead )>>16) & 0x00ff) | RMDNUMMASK;
+
+ writereg(0,CSR3); /* busmaster/no word-swap */
+ writereg((unsigned short) (((unsigned long) &(p->ib)) & 0xffff),CSR1);
+ writereg((unsigned short) (((unsigned long) &(p->ib))>>16),CSR2);
+
+ writereg(CSR0_INIT,CSR0); /* this changes L_ADDRREG to CSR0 */
+
+ /*
+ * NOW, WE NEVER WILL CHANGE THE L_ADDRREG, CSR0 IS ALWAYS SELECTED
+ */
+
+ for(i=0;i<5;i++)
+ {
+ for(j=0;j<2000000;j++); /* wait a while */
+ if(inw(PORT+L_DATAREG) & CSR0_IDON) break; /* init ok ? */
+ }
+ if(i == 5)
+ {
+ printk("%s: can't init am7990, status: %04x\n",dev->name,(int) inw(PORT+L_DATAREG));
+ disable_dma(dev->dma);
+ free_dma(dev->dma);
+ free_irq(dev->irq);
+ return 0; /* false */
+ }
+
+ writedatareg(CSR0_CLRALL | CSR0_INEA | CSR0_STRT); /* start lance , enable interrupts */
+
+ return 1; /* OK */
+}
+
+/*
+ * interrupt handler
+ */
+
+static void ni65_interrupt(int irq, struct pt_regs * regs)
+{
+ int csr0;
+ struct device *dev = (struct device *) irq2dev_map[irq];
+
+ if (dev == NULL) {
+ printk ("net_interrupt(): irq %d for unknown device.\n", irq);
+ return;
+ }
+
+ csr0 = inw(PORT+L_DATAREG);
+ writedatareg(csr0 & CSR0_CLRALL); /* ack interrupts, disable int. */
+
+ dev->interrupt = 1;
+
+ if(csr0 & CSR0_ERR)
+ {
+ struct priv *p = (struct priv *) dev->priv;
+
+ if(csr0 & CSR0_BABL)
+ p->stats.tx_errors++;
+ if(csr0 & CSR0_MISS)
+ p->stats.rx_errors++;
+ }
+
+ if(csr0 & CSR0_RINT) /* RECV-int? */
+ {
+ recv_intr(dev);
+ }
+ if(csr0 & CSR0_TINT) /* XMIT-int? */
+ {
+ xmit_intr(dev);
+ }
+
+ writedatareg(CSR0_INEA); /* reenable inter. */
+ dev->interrupt = 0;
+
+ return;
+}
+
+/*
+ * We have received an Xmit-Interrupt ..
+ * send a new packet if necessary
+ */
+
+static void xmit_intr(struct device *dev)
+{
+ int tmdstat;
+ struct tmd *tmdp;
+ struct priv *p = (struct priv *) dev->priv;
+
+#ifdef NO_STATIC
+ struct sk_buff *skb;
+#endif
+
+ while(p->xmit_queued)
+ {
+ tmdp = p->tmdhead + p->tmdlast;
+ tmdstat = tmdp->u.s.status;
+ if(tmdstat & XMIT_OWN)
+ break;
+#ifdef NO_STATIC
+ skb = (struct sk_buff *) p->tmdbufs[p->tmdlast];
+ dev_kfree_skb(skb,FREE_WRITE);
+#endif
+
+ if(tmdstat & XMIT_ERR)
+ {
+ printk("%s: xmit-error: %04x %04x\n",dev->name,(int) tmdstat,(int) tmdp->status2);
+ if(tmdp->status2 & XMIT_TDRMASK)
+ printk("%s: tdr-problems (e.g. no resistor)\n",dev->name);
+
+ /* checking some errors */
+ if(tmdp->status2 & XMIT_RTRY)
+ p->stats.tx_aborted_errors++;
+ if(tmdp->status2 & XMIT_LCAR)
+ p->stats.tx_carrier_errors++;
+ p->stats.tx_errors++;
+ tmdp->status2 = 0;
+ }
+ else
+ p->stats.tx_packets++;
+
+ p->tmdlast = (p->tmdlast + 1) & (TMDNUM-1);
+ if(p->tmdlast == p->tmdnum)
+ p->xmit_queued = 0;
+ }
+
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+}
+
+/*
+ * We have received a packet
+ */
+
+static void recv_intr(struct device *dev)
+{
+ struct rmd *rmdp;
+ int rmdstat,len;
+ struct sk_buff *skb,*skb1;
+ struct priv *p = (struct priv *) dev->priv;
+
+ rmdp = p->rmdhead + p->rmdnum;
+ while(!( (rmdstat = rmdp->u.s.status) & RCV_OWN))
+ {
+ if( (rmdstat & (RCV_START | RCV_END)) != (RCV_START | RCV_END) ) /* is packet start & end? */
+ {
+ if(rmdstat & RCV_START)
+ {
+ p->stats.rx_errors++;
+ p->stats.rx_length_errors++;
+ printk("%s: packet too long\n",dev->name);
+ }
+ rmdp->u.s.status = RCV_OWN; /* change owner */
+ }
+ else if(rmdstat & RCV_ERR)
+ {
+ printk("%s: receive-error: %04x\n",dev->name,(int) rmdstat );
+ p->stats.rx_errors++;
+ if(rmdstat & RCV_FRAM) p->stats.rx_frame_errors++;
+ if(rmdstat & RCV_OFLO) p->stats.rx_over_errors++;
+ if(rmdstat & RCV_CRC) p->stats.rx_crc_errors++;
+ rmdp->u.s.status = RCV_OWN;
+ printk("%s: lance-status: %04x\n",dev->name,(int) inw(PORT+L_DATAREG));
+ }
+ else
+ {
+ len = (rmdp->mlen & 0x0fff) - 4; /* -4: ignore FCS */
+ skb = dev_alloc_skb(R_BUF_SIZE);
+ if(skb != NULL)
+ {
+ if( (unsigned long) (skb->data + R_BUF_SIZE) & 0xff000000) {
+ memcpy(skb_put(skb,len),p->recv_skb[p->rmdnum]->data,len);
+ skb1 = skb;
+ }
+ else {
+ skb1 = p->recv_skb[p->rmdnum];
+ p->recv_skb[p->rmdnum] = skb;
+ rmdp->u.buffer = (unsigned long) skb_put(skb1,len);
+ }
+ rmdp->u.s.status = RCV_OWN;
+ rmdp->mlen = 0; /* not necc ???? */
+ skb1->dev = dev;
+ p->stats.rx_packets++;
+ skb1->protocol=eth_type_trans(skb1,dev);
+ netif_rx(skb1);
+ }
+ else
+ {
+ rmdp->u.s.status = RCV_OWN;
+ printk("%s: can't alloc new sk_buff\n",dev->name);
+ p->stats.rx_dropped++;
+ }
+ }
+ p->rmdnum++; p->rmdnum &= RMDNUM-1;
+ rmdp = p->rmdhead + p->rmdnum;
+ }
+}
+
+/*
+ * kick xmitter ..
+ */
+
+static int ni65_send_packet(struct sk_buff *skb, struct device *dev)
+{
+ struct priv *p = (struct priv *) dev->priv;
+ struct tmd *tmdp;
+
+ if(dev->tbusy)
+ {
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < 25)
+ return 1;
+
+ printk("%s: xmitter timed out, try to restart!\n",dev->name);
+ am7990_reinit(dev);
+ dev->tbusy=0;
+ dev->trans_start = jiffies;
+ }
+
+ if(skb == NULL)
+ {
+ dev_tint(dev);
+ return 0;
+ }
+
+ if (skb->len <= 0)
+ return 0;
+
+ if (set_bit(0, (void*)&dev->tbusy) != 0)
+ {
+ printk("%s: Transmitter access conflict.\n", dev->name);
+ return 1;
+ }
+ if(set_bit(0,(void*) &p->lock) != 0)
+ {
+ printk("%s: Queue was locked!\n",dev->name);
+ return 1;
+ }
+
+ {
+ short len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+
+ tmdp = p->tmdhead + p->tmdnum;
+
+#ifdef NO_STATIC
+ tmdp->u.buffer = (unsigned long) (skb->data);
+ p->tmdbufs[p->tmdnum] = skb;
+#else
+ memcpy((char *) (tmdp->u.buffer & 0x00ffffff),(char *)skb->data,skb->len);
+ dev_kfree_skb (skb, FREE_WRITE);
+#endif
+ tmdp->blen = -len;
+ tmdp->u.s.status = XMIT_OWN | XMIT_START | XMIT_END;
+
+ cli();
+ p->xmit_queued = 1;
+ writedatareg(CSR0_TDMD | CSR0_INEA); /* enable xmit & interrupt */
+ p->tmdnum++; p->tmdnum &= TMDNUM-1;
+
+ if( !((p->tmdhead + p->tmdnum)->u.s.status & XMIT_OWN) )
+ dev->tbusy = 0;
+ p->lock = 0;
+ sti();
+
+ dev->trans_start = jiffies;
+
+ }
+
+ return 0;
+}
+
+static struct enet_statistics *ni65_get_stats(struct device *dev)
+{
+ return &((struct priv *) dev->priv)->stats;
+}
+
+static void set_multicast_list(struct device *dev)
+{
+}
+
+/*
+ * END of ni65.c
+ */
+
diff --git a/i386/i386at/gpl/linux/net/ni65.h b/i386/i386at/gpl/linux/net/ni65.h
new file mode 100644
index 00000000..144523fa
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/ni65.h
@@ -0,0 +1,130 @@
+/* am7990 (lance) definitions
+ *
+ * This is a extension to the Linux operating system, and is covered by
+ * same Gnu Public License that covers that work.
+ *
+ * Michael Hipp
+ * email: mhipp@student.uni-tuebingen.de
+ *
+ * sources: (mail me or ask archie if you need them)
+ * crynwr-packet-driver
+ */
+
+/*
+ * Control and Status Register 0 (CSR0) bit definitions
+ * (R=Readable) (W=Writeable) (S=Set on write) (C-Clear on write)
+ *
+ */
+
+#define CSR0_ERR 0x8000 /* Error summary (R) */
+#define CSR0_BABL 0x4000 /* Babble transmitter timeout error (RC) */
+#define CSR0_CERR 0x2000 /* Collision Error (RC) */
+#define CSR0_MISS 0x1000 /* Missed packet (RC) */
+#define CSR0_MERR 0x0800 /* Memory Error (RC) */
+#define CSR0_RINT 0x0400 /* Receiver Interrupt (RC) */
+#define CSR0_TINT 0x0200 /* Transmit Interrupt (RC) */
+#define CSR0_IDON 0x0100 /* Initialization Done (RC) */
+#define CSR0_INTR 0x0080 /* Interrupt Flag (R) */
+#define CSR0_INEA 0x0040 /* Interrupt Enable (RW) */
+#define CSR0_RXON 0x0020 /* Receiver on (R) */
+#define CSR0_TXON 0x0010 /* Transmitter on (R) */
+#define CSR0_TDMD 0x0008 /* Transmit Demand (RS) */
+#define CSR0_STOP 0x0004 /* Stop (RS) */
+#define CSR0_STRT 0x0002 /* Start (RS) */
+#define CSR0_INIT 0x0001 /* Initialize (RS) */
+
+#define CSR0_CLRALL 0x7f00 /* mask for all clearable bits */
+/*
+ * Initialization Block Mode operation Bit Definitions.
+ */
+
+#define M_PROM 0x8000 /* Promiscuous Mode */
+#define M_INTL 0x0040 /* Internal Loopback */
+#define M_DRTY 0x0020 /* Disable Retry */
+#define M_COLL 0x0010 /* Force Collision */
+#define M_DTCR 0x0008 /* Disable Transmit CRC) */
+#define M_LOOP 0x0004 /* Loopback */
+#define M_DTX 0x0002 /* Disable the Transmitter */
+#define M_DRX 0x0001 /* Disable the Receiver */
+
+
+/*
+ * Receive message descriptor bit definitions.
+ */
+
+#define RCV_OWN 0x80 /* owner bit 0 = host, 1 = lance */
+#define RCV_ERR 0x40 /* Error Summary */
+#define RCV_FRAM 0x20 /* Framing Error */
+#define RCV_OFLO 0x10 /* Overflow Error */
+#define RCV_CRC 0x08 /* CRC Error */
+#define RCV_BUF_ERR 0x04 /* Buffer Error */
+#define RCV_START 0x02 /* Start of Packet */
+#define RCV_END 0x01 /* End of Packet */
+
+
+/*
+ * Transmit message descriptor bit definitions.
+ */
+
+#define XMIT_OWN 0x80 /* owner bit 0 = host, 1 = lance */
+#define XMIT_ERR 0x40 /* Error Summary */
+#define XMIT_RETRY 0x10 /* more the 1 retry needed to Xmit */
+#define XMIT_1_RETRY 0x08 /* one retry needed to Xmit */
+#define XMIT_DEF 0x04 /* Deferred */
+#define XMIT_START 0x02 /* Start of Packet */
+#define XMIT_END 0x01 /* End of Packet */
+
+/*
+ * transmit status (2) (valid if XMIT_ERR == 1)
+ */
+
+#define XMIT_RTRY 0x0200 /* Failed after 16 retransmissions */
+#define XMIT_LCAR 0x0400 /* Loss of Carrier */
+#define XMIT_LCOL 0x1000 /* Late collision */
+#define XMIT_RESERV 0x2000 /* Reserved */
+#define XMIT_UFLO 0x4000 /* Underflow (late memory) */
+#define XMIT_BUFF 0x8000 /* Buffering error (no ENP) */
+#define XMIT_TDRMASK 0x003f /* time-domain-reflectometer-value */
+
+struct init_block
+{
+ unsigned short mode;
+ unsigned char eaddr[6];
+ unsigned char filter[8];
+ unsigned short rrplow; /* receive ring pointer (align 8) */
+ unsigned short rrphigh; /* bit 13-15: number of rmd's (power of 2) */
+ unsigned short trplow; /* transmit ring pointer (align 8) */
+ unsigned short trphigh; /* bit 13-15: number of tmd's (power of 2) */
+};
+
+struct rmd /* Receive Message Descriptor */
+{
+ union
+ {
+ volatile unsigned long buffer;
+ struct
+ {
+ volatile unsigned char dummy[3];
+ volatile unsigned char status;
+ } s;
+ } u;
+ short blen;
+ volatile unsigned short mlen;
+};
+
+struct tmd
+{
+ union
+ {
+ volatile unsigned long buffer;
+ struct
+ {
+ volatile unsigned char dummy[3];
+ volatile unsigned char status;
+ } s;
+ } u;
+ unsigned short blen;
+ volatile unsigned short status2;
+};
+
+
diff --git a/i386/i386at/gpl/linux/net/seeq8005.c b/i386/i386at/gpl/linux/net/seeq8005.c
new file mode 100644
index 00000000..5799d80c
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/seeq8005.c
@@ -0,0 +1,760 @@
+/* seeq8005.c: A network driver for linux. */
+/*
+ Based on skeleton.c,
+ Written 1993-94 by Donald Becker.
+ See the skeleton.c file for further copyright information.
+
+ This software may be used and distributed according to the terms
+ of the GNU Public License, incorporated herein by reference.
+
+ The author may be reached as hamish@zot.apana.org.au
+
+ This file is a network device driver for the SEEQ 8005 chipset and
+ the Linux operating system.
+
+*/
+
+static const char *version =
+ "seeq8005.c:v1.00 8/07/95 Hamish Coleman (hamish@zot.apana.org.au)\n";
+
+/*
+ Sources:
+ SEEQ 8005 databook
+
+ Version history:
+ 1.00 Public release. cosmetic changes (no warnings now)
+ 0.68 Turning per- packet,interrupt debug messages off - testing for release.
+ 0.67 timing problems/bad buffer reads seem to be fixed now
+ 0.63 *!@$ protocol=eth_type_trans -- now packets flow
+ 0.56 Send working
+ 0.48 Receive working
+*/
+
+#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/in.h>
+#include <linux/malloc.h>
+#include <linux/string.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 "seeq8005.h"
+
+/* First, a few definitions that the brave might change. */
+/* A zero-terminated list of I/O addresses to be probed. */
+static unsigned int seeq8005_portlist[] =
+ { 0x300, 0x320, 0x340, 0x360, 0};
+
+/* use 0 for production, 1 for verification, >2 for debug */
+#ifndef NET_DEBUG
+#define NET_DEBUG 1
+#endif
+static unsigned int net_debug = NET_DEBUG;
+
+/* Information that need to be kept for each board. */
+struct net_local {
+ struct enet_statistics stats;
+ unsigned short receive_ptr; /* What address in packet memory do we expect a recv_pkt_header? */
+ long open_time; /* Useless example local info. */
+};
+
+/* The station (ethernet) address prefix, used for IDing the board. */
+#define SA_ADDR0 0x00
+#define SA_ADDR1 0x80
+#define SA_ADDR2 0x4b
+
+/* Index to functions, as function prototypes. */
+
+extern int seeq8005_probe(struct device *dev);
+
+static int seeq8005_probe1(struct device *dev, int ioaddr);
+static int seeq8005_open(struct device *dev);
+static int seeq8005_send_packet(struct sk_buff *skb, struct device *dev);
+static void seeq8005_interrupt(int irq, struct pt_regs *regs);
+static void seeq8005_rx(struct device *dev);
+static int seeq8005_close(struct device *dev);
+static struct enet_statistics *seeq8005_get_stats(struct device *dev);
+static void set_multicast_list(struct device *dev);
+
+/* Example routines you must write ;->. */
+#define tx_done(dev) (inw(SEEQ_STATUS) & SEEQSTAT_TX_ON)
+extern void hardware_send_packet(struct device *dev, char *buf, int length);
+extern void seeq8005_init(struct device *dev, int startp);
+inline void wait_for_buffer(struct device *dev);
+
+
+/* Check for a network adaptor of this type, and return '0' iff one exists.
+ If dev->base_addr == 0, probe all likely locations.
+ If dev->base_addr == 1, always return failure.
+ If dev->base_addr == 2, allocate space for the device and return success
+ (detachable devices only).
+ */
+#ifdef HAVE_DEVLIST
+/* Support for a alternate probe manager, which will eliminate the
+ boilerplate below. */
+struct netdev_entry seeq8005_drv =
+{"seeq8005", seeq8005_probe1, SEEQ8005_IO_EXTENT, seeq8005_portlist};
+#else
+int
+seeq8005_probe(struct device *dev)
+{
+ int i;
+ int base_addr = dev ? dev->base_addr : 0;
+
+ if (base_addr > 0x1ff) /* Check a single specified location. */
+ return seeq8005_probe1(dev, base_addr);
+ else if (base_addr != 0) /* Don't probe at all. */
+ return ENXIO;
+
+ for (i = 0; seeq8005_portlist[i]; i++) {
+ int ioaddr = seeq8005_portlist[i];
+ if (check_region(ioaddr, SEEQ8005_IO_EXTENT))
+ continue;
+ if (seeq8005_probe1(dev, ioaddr) == 0)
+ return 0;
+ }
+
+ return ENODEV;
+}
+#endif
+
+/* This is the real probe routine. Linux has a history of friendly device
+ probes on the ISA bus. A good device probes avoids doing writes, and
+ verifies that the correct device exists and functions. */
+
+static int seeq8005_probe1(struct device *dev, int ioaddr)
+{
+ static unsigned version_printed = 0;
+ int i,j;
+ unsigned char SA_prom[32];
+ int old_cfg1;
+ int old_cfg2;
+ int old_stat;
+ int old_dmaar;
+ int old_rear;
+
+ if (net_debug>1)
+ printk("seeq8005: probing at 0x%x\n",ioaddr);
+
+ old_stat = inw(SEEQ_STATUS); /* read status register */
+ if (old_stat == 0xffff)
+ return ENODEV; /* assume that 0xffff == no device */
+ if ( (old_stat & 0x1800) != 0x1800 ) { /* assume that unused bits are 1, as my manual says */
+ if (net_debug>1) {
+ printk("seeq8005: reserved stat bits != 0x1800\n");
+ printk(" == 0x%04x\n",old_stat);
+ }
+ return ENODEV;
+ }
+
+ old_rear = inw(SEEQ_REA);
+ if (old_rear == 0xffff) {
+ outw(0,SEEQ_REA);
+ if (inw(SEEQ_REA) == 0xffff) { /* assume that 0xffff == no device */
+ return ENODEV;
+ }
+ } else if ((old_rear & 0xff00) != 0xff00) { /* assume that unused bits are 1 */
+ if (net_debug>1) {
+ printk("seeq8005: unused rear bits != 0xff00\n");
+ printk(" == 0x%04x\n",old_rear);
+ }
+ return ENODEV;
+ }
+
+ old_cfg2 = inw(SEEQ_CFG2); /* read CFG2 register */
+ old_cfg1 = inw(SEEQ_CFG1);
+ old_dmaar = inw(SEEQ_DMAAR);
+
+ if (net_debug>4) {
+ printk("seeq8005: stat = 0x%04x\n",old_stat);
+ printk("seeq8005: cfg1 = 0x%04x\n",old_cfg1);
+ printk("seeq8005: cfg2 = 0x%04x\n",old_cfg2);
+ printk("seeq8005: raer = 0x%04x\n",old_rear);
+ printk("seeq8005: dmaar= 0x%04x\n",old_dmaar);
+ }
+
+ outw( SEEQCMD_FIFO_WRITE | SEEQCMD_SET_ALL_OFF, SEEQ_CMD); /* setup for reading PROM */
+ outw( 0, SEEQ_DMAAR); /* set starting PROM address */
+ outw( SEEQCFG1_BUFFER_PROM, SEEQ_CFG1); /* set buffer to look at PROM */
+
+
+ j=0;
+ for(i=0; i <32; i++) {
+ j+= SA_prom[i] = inw(SEEQ_BUFFER) & 0xff;
+ }
+
+#if 0
+ /* untested because I only have the one card */
+ if ( (j&0xff) != 0 ) { /* checksum appears to be 8bit = 0 */
+ if (net_debug>1) { /* check this before deciding that we have a card */
+ printk("seeq8005: prom sum error\n");
+ }
+ outw( old_stat, SEEQ_STATUS);
+ outw( old_dmaar, SEEQ_DMAAR);
+ outw( old_cfg1, SEEQ_CFG1);
+ return ENODEV;
+ }
+#endif
+
+ outw( SEEQCFG2_RESET, SEEQ_CFG2); /* reset the card */
+ SLOW_DOWN_IO; /* have to wait 4us after a reset - should be fixed */
+ SLOW_DOWN_IO;
+ SLOW_DOWN_IO;
+ SLOW_DOWN_IO;
+ outw( SEEQCMD_SET_ALL_OFF, SEEQ_CMD);
+
+ if (net_debug) {
+ printk("seeq8005: prom sum = 0x%08x\n",j);
+ for(j=0; j<32; j+=16) {
+ printk("seeq8005: prom %02x: ",j);
+ for(i=0;i<16;i++) {
+ printk("%02x ",SA_prom[j|i]);
+ }
+ printk(" ");
+ for(i=0;i<16;i++) {
+ if ((SA_prom[j|i]>31)&&(SA_prom[j|i]<127)) {
+ printk("%c", SA_prom[j|i]);
+ } else {
+ printk(" ");
+ }
+ }
+ printk("\n");
+ }
+ }
+
+#if 0
+ /*
+ * testing the packet buffer memory doesnt work yet
+ * but all other buffer accesses do
+ * - fixing is not a priority
+ */
+ if (net_debug>1) { /* test packet buffer memory */
+ printk("seeq8005: testing packet buffer ... ");
+ outw( SEEQCFG1_BUFFER_BUFFER, SEEQ_CFG1);
+ outw( SEEQCMD_FIFO_WRITE | SEEQCMD_SET_ALL_OFF, SEEQ_CMD);
+ outw( 0 , SEEQ_DMAAR);
+ for(i=0;i<32768;i++) {
+ outw(0x5a5a, SEEQ_BUFFER);
+ }
+ j=jiffies+HZ;
+ while ( ((inw(SEEQ_STATUS) & SEEQSTAT_FIFO_EMPTY) != SEEQSTAT_FIFO_EMPTY) && jiffies < j )
+ mb();
+ outw( 0 , SEEQ_DMAAR);
+ while ( ((inw(SEEQ_STATUS) & SEEQSTAT_WINDOW_INT) != SEEQSTAT_WINDOW_INT) && jiffies < j+HZ)
+ mb();
+ if ( (inw(SEEQ_STATUS) & SEEQSTAT_WINDOW_INT) == SEEQSTAT_WINDOW_INT)
+ outw( SEEQCMD_WINDOW_INT_ACK | (inw(SEEQ_STATUS)& SEEQCMD_INT_MASK), SEEQ_CMD);
+ outw( SEEQCMD_FIFO_READ | SEEQCMD_SET_ALL_OFF, SEEQ_CMD);
+ j=0;
+ for(i=0;i<32768;i++) {
+ if (inw(SEEQ_BUFFER) != 0x5a5a)
+ j++;
+ }
+ if (j) {
+ printk("%i\n",j);
+ } else {
+ printk("ok.\n");
+ }
+ }
+#endif
+
+ /* Allocate a new 'dev' if needed. */
+ if (dev == NULL)
+ dev = init_etherdev(0, sizeof(struct net_local));
+
+ if (net_debug && version_printed++ == 0)
+ printk(version);
+
+ printk("%s: %s found at %#3x, ", dev->name, "seeq8005", ioaddr);
+
+ /* Fill in the 'dev' fields. */
+ dev->base_addr = ioaddr;
+
+ /* Retrieve and print the ethernet address. */
+ for (i = 0; i < 6; i++)
+ printk(" %2.2x", dev->dev_addr[i] = SA_prom[i+6]);
+
+ if (dev->irq == 0xff)
+ ; /* Do nothing: a user-level program will set it. */
+ else if (dev->irq < 2) { /* "Auto-IRQ" */
+ autoirq_setup(0);
+
+ outw( SEEQCMD_RX_INT_EN | SEEQCMD_SET_RX_ON | SEEQCMD_SET_RX_OFF, SEEQ_CMD );
+
+ dev->irq = autoirq_report(0);
+
+ if (net_debug >= 2)
+ printk(" autoirq is %d\n", dev->irq);
+ } else if (dev->irq == 2)
+ /* Fixup for users that don't know that IRQ 2 is really IRQ 9,
+ * or don't know which one to set.
+ */
+ dev->irq = 9;
+
+#if 0
+ {
+ int irqval = request_irq(dev->irq, &seeq8005_interrupt, 0, "seeq8005");
+ if (irqval) {
+ printk ("%s: unable to get IRQ %d (irqval=%d).\n", dev->name,
+ dev->irq, irqval);
+ return EAGAIN;
+ }
+ }
+#endif
+
+ /* Grab the region so we can find another board if autoIRQ fails. */
+ request_region(ioaddr, SEEQ8005_IO_EXTENT,"seeq8005");
+
+ /* Initialize the device structure. */
+ dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
+ if (dev->priv == NULL)
+ return -ENOMEM;
+ memset(dev->priv, 0, sizeof(struct net_local));
+
+ dev->open = seeq8005_open;
+ dev->stop = seeq8005_close;
+ dev->hard_start_xmit = seeq8005_send_packet;
+ dev->get_stats = seeq8005_get_stats;
+ dev->set_multicast_list = &set_multicast_list;
+
+ /* Fill in the fields of the device structure with ethernet values. */
+ ether_setup(dev);
+
+ dev->flags &= ~IFF_MULTICAST;
+
+ return 0;
+}
+
+
+/* Open/initialize the board. This is called (in the current kernel)
+ sometime after booting when the 'ifconfig' program is run.
+
+ This routine should set everything up anew at each open, even
+ registers that "should" only need to be set once at boot, so that
+ there is non-reboot way to recover if something goes wrong.
+ */
+static int
+seeq8005_open(struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+
+ {
+ int irqval = request_irq(dev->irq, &seeq8005_interrupt, 0, "seeq8005");
+ if (irqval) {
+ printk ("%s: unable to get IRQ %d (irqval=%d).\n", dev->name,
+ dev->irq, irqval);
+ return EAGAIN;
+ }
+ }
+ irq2dev_map[dev->irq] = dev;
+
+ /* Reset the hardware here. Don't forget to set the station address. */
+ seeq8005_init(dev, 1);
+
+ lp->open_time = jiffies;
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+ return 0;
+}
+
+static int
+seeq8005_send_packet(struct sk_buff *skb, struct device *dev)
+{
+ int ioaddr = dev->base_addr;
+
+ if (dev->tbusy) {
+ /* If we get here, some higher level has decided we are broken.
+ There should really be a "kick me" function call instead. */
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < 5)
+ return 1;
+ printk("%s: transmit timed out, %s?\n", dev->name,
+ tx_done(dev) ? "IRQ conflict" : "network cable problem");
+ /* Try to restart the adaptor. */
+ seeq8005_init(dev, 1);
+ dev->tbusy=0;
+ dev->trans_start = jiffies;
+ }
+
+ /* If some higher layer thinks we've missed an tx-done interrupt
+ we are passed NULL. Caution: dev_tint() handles the cli()/sti()
+ itself. */
+ if (skb == NULL) {
+ dev_tint(dev);
+ return 0;
+ }
+
+ /* Block a timer-based transmit from overlapping. This could better be
+ done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
+ if (set_bit(0, (void*)&dev->tbusy) != 0)
+ printk("%s: Transmitter access conflict.\n", dev->name);
+ else {
+ short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+ unsigned char *buf = skb->data;
+
+ hardware_send_packet(dev, buf, length);
+ dev->trans_start = jiffies;
+ }
+ dev_kfree_skb (skb, FREE_WRITE);
+
+ /* You might need to clean up and record Tx statistics here. */
+
+ return 0;
+}
+
+/* The typical workload of the driver:
+ Handle the network interface interrupts. */
+static void
+seeq8005_interrupt(int irq, struct pt_regs * regs)
+{
+ struct device *dev = (struct device *)(irq2dev_map[irq]);
+ struct net_local *lp;
+ int ioaddr, status, boguscount = 0;
+
+ if (dev == NULL) {
+ printk ("net_interrupt(): irq %d for unknown device.\n", irq);
+ return;
+ }
+
+ if (dev->interrupt)
+ printk ("%s: Re-entering the interrupt handler.\n", dev->name);
+ dev->interrupt = 1;
+
+ ioaddr = dev->base_addr;
+ lp = (struct net_local *)dev->priv;
+
+ status = inw(SEEQ_STATUS);
+ do {
+ if (net_debug >2) {
+ printk("%s: int, status=0x%04x\n",dev->name,status);
+ }
+
+ if (status & SEEQSTAT_WINDOW_INT) {
+ outw( SEEQCMD_WINDOW_INT_ACK | (status & SEEQCMD_INT_MASK), SEEQ_CMD);
+ if (net_debug) {
+ printk("%s: window int!\n",dev->name);
+ }
+ }
+ if (status & SEEQSTAT_TX_INT) {
+ outw( SEEQCMD_TX_INT_ACK | (status & SEEQCMD_INT_MASK), SEEQ_CMD);
+ lp->stats.tx_packets++;
+ dev->tbusy = 0;
+ mark_bh(NET_BH); /* Inform upper layers. */
+ }
+ if (status & SEEQSTAT_RX_INT) {
+ /* Got a packet(s). */
+ seeq8005_rx(dev);
+ }
+ status = inw(SEEQ_STATUS);
+ } while ( (++boguscount < 10) && (status & SEEQSTAT_ANY_INT)) ;
+
+ if(net_debug>2) {
+ printk("%s: eoi\n",dev->name);
+ }
+ dev->interrupt = 0;
+ return;
+}
+
+/* We have a good packet(s), get it/them out of the buffers. */
+static void
+seeq8005_rx(struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ int boguscount = 10;
+ int pkt_hdr;
+ int ioaddr = dev->base_addr;
+
+ do {
+ int next_packet;
+ int pkt_len;
+ int i;
+ int status;
+
+ status = inw(SEEQ_STATUS);
+ outw( lp->receive_ptr, SEEQ_DMAAR);
+ outw(SEEQCMD_FIFO_READ | SEEQCMD_RX_INT_ACK | (status & SEEQCMD_INT_MASK), SEEQ_CMD);
+ wait_for_buffer(dev);
+ next_packet = ntohs(inw(SEEQ_BUFFER));
+ pkt_hdr = inw(SEEQ_BUFFER);
+
+ if (net_debug>2) {
+ printk("%s: 0x%04x recv next=0x%04x, hdr=0x%04x\n",dev->name,lp->receive_ptr,next_packet,pkt_hdr);
+ }
+
+ if ((next_packet == 0) || ((pkt_hdr & SEEQPKTH_CHAIN)==0)) { /* Read all the frames? */
+ return; /* Done for now */
+ }
+
+ if ((pkt_hdr & SEEQPKTS_DONE)==0)
+ break;
+
+ if (next_packet < lp->receive_ptr) {
+ pkt_len = (next_packet + 0x10000 - ((DEFAULT_TEA+1)<<8)) - lp->receive_ptr - 4;
+ } else {
+ pkt_len = next_packet - lp->receive_ptr - 4;
+ }
+
+ if (next_packet < ((DEFAULT_TEA+1)<<8)) { /* is the next_packet address sane? */
+ printk("%s: recv packet ring corrupt, resetting board\n",dev->name);
+ seeq8005_init(dev,1);
+ return;
+ }
+
+ lp->receive_ptr = next_packet;
+
+ if (net_debug>2) {
+ printk("%s: recv len=0x%04x\n",dev->name,pkt_len);
+ }
+
+ if (pkt_hdr & SEEQPKTS_ANY_ERROR) { /* There was an error. */
+ lp->stats.rx_errors++;
+ if (pkt_hdr & SEEQPKTS_SHORT) lp->stats.rx_frame_errors++;
+ if (pkt_hdr & SEEQPKTS_DRIB) lp->stats.rx_frame_errors++;
+ if (pkt_hdr & SEEQPKTS_OVERSIZE) lp->stats.rx_over_errors++;
+ if (pkt_hdr & SEEQPKTS_CRC_ERR) lp->stats.rx_crc_errors++;
+ /* skip over this packet */
+ outw( SEEQCMD_FIFO_WRITE | SEEQCMD_DMA_INT_ACK | (status & SEEQCMD_INT_MASK), SEEQ_CMD);
+ outw( (lp->receive_ptr & 0xff00)>>8, SEEQ_REA);
+ } else {
+ /* Malloc up new buffer. */
+ struct sk_buff *skb;
+ unsigned char *buf;
+
+ skb = dev_alloc_skb(pkt_len);
+ if (skb == NULL) {
+ printk("%s: Memory squeeze, dropping packet.\n", dev->name);
+ lp->stats.rx_dropped++;
+ break;
+ }
+ skb->dev = dev;
+ skb_reserve(skb, 2); /* align data on 16 byte */
+ buf = skb_put(skb,pkt_len);
+
+ insw(SEEQ_BUFFER, buf, (pkt_len + 1) >> 1);
+
+ if (net_debug>2) {
+ char * p = buf;
+ printk("%s: recv ",dev->name);
+ for(i=0;i<14;i++) {
+ printk("%02x ",*(p++)&0xff);
+ }
+ printk("\n");
+ }
+
+ skb->protocol=eth_type_trans(skb,dev);
+ netif_rx(skb);
+ lp->stats.rx_packets++;
+ }
+ } while ((--boguscount) && (pkt_hdr & SEEQPKTH_CHAIN));
+
+ /* If any worth-while packets have been received, netif_rx()
+ has done a mark_bh(NET_BH) for us and will work on them
+ when we get to the bottom-half routine. */
+ return;
+}
+
+/* The inverse routine to net_open(). */
+static int
+seeq8005_close(struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+
+ lp->open_time = 0;
+
+ dev->tbusy = 1;
+ dev->start = 0;
+
+ /* Flush the Tx and disable Rx here. */
+ outw( SEEQCMD_SET_ALL_OFF, SEEQ_CMD);
+
+ free_irq(dev->irq);
+
+ irq2dev_map[dev->irq] = 0;
+
+ /* Update the statistics here. */
+
+ return 0;
+
+}
+
+/* Get the current statistics. This may be called with the card open or
+ closed. */
+static struct enet_statistics *
+seeq8005_get_stats(struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+
+ return &lp->stats;
+}
+
+/* Set or clear the multicast filter for this adaptor.
+ num_addrs == -1 Promiscuous mode, receive all packets
+ num_addrs == 0 Normal mode, clear multicast list
+ num_addrs > 0 Multicast mode, receive normal and MC packets, and do
+ best-effort filtering.
+ */
+static void
+set_multicast_list(struct device *dev)
+{
+/*
+ * I _could_ do upto 6 addresses here, but wont (yet?)
+ */
+
+#if 0
+ int ioaddr = dev->base_addr;
+/*
+ * hmm, not even sure if my matching works _anyway_ - seem to be receiving
+ * _everything_ . . .
+ */
+
+ if (num_addrs) { /* Enable promiscuous mode */
+ outw( (inw(SEEQ_CFG1) & ~SEEQCFG1_MATCH_MASK)| SEEQCFG1_MATCH_ALL, SEEQ_CFG1);
+ dev->flags|=IFF_PROMISC;
+ } else { /* Disable promiscuous mode, use normal mode */
+ outw( (inw(SEEQ_CFG1) & ~SEEQCFG1_MATCH_MASK)| SEEQCFG1_MATCH_BROAD, SEEQ_CFG1);
+ }
+#endif
+}
+
+void seeq8005_init(struct device *dev, int startp)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+ int i;
+
+ outw(SEEQCFG2_RESET, SEEQ_CFG2); /* reset device */
+ SLOW_DOWN_IO; /* have to wait 4us after a reset - should be fixed */
+ SLOW_DOWN_IO;
+ SLOW_DOWN_IO;
+ SLOW_DOWN_IO;
+
+ outw( SEEQCMD_FIFO_WRITE | SEEQCMD_SET_ALL_OFF, SEEQ_CMD);
+ outw( 0, SEEQ_DMAAR); /* load start address into both low and high byte */
+/* wait_for_buffer(dev); */ /* I think that you only need a wait for memory buffer */
+ outw( SEEQCFG1_BUFFER_MAC0, SEEQ_CFG1);
+
+ for(i=0;i<6;i++) { /* set Station address */
+ outb(dev->dev_addr[i], SEEQ_BUFFER);
+ SLOW_DOWN_IO;
+ }
+
+ outw( SEEQCFG1_BUFFER_TEA, SEEQ_CFG1); /* set xmit end area pointer to 16K */
+ outb( DEFAULT_TEA, SEEQ_BUFFER); /* this gives us 16K of send buffer and 48K of recv buffer */
+
+ lp->receive_ptr = (DEFAULT_TEA+1)<<8; /* so we can find our packet_header */
+ outw( lp->receive_ptr, SEEQ_RPR); /* Receive Pointer Register is set to recv buffer memory */
+
+ outw( 0x00ff, SEEQ_REA); /* Receive Area End */
+
+ if (net_debug>4) {
+ printk("%s: SA0 = ",dev->name);
+
+ outw( SEEQCMD_FIFO_READ | SEEQCMD_SET_ALL_OFF, SEEQ_CMD);
+ outw( 0, SEEQ_DMAAR);
+ outw( SEEQCFG1_BUFFER_MAC0, SEEQ_CFG1);
+
+ for(i=0;i<6;i++) {
+ printk("%02x ",inb(SEEQ_BUFFER));
+ }
+ printk("\n");
+ }
+
+ outw( SEEQCFG1_MAC0_EN | SEEQCFG1_MATCH_BROAD | SEEQCFG1_BUFFER_BUFFER, SEEQ_CFG1);
+ outw( SEEQCFG2_AUTO_REA | SEEQCFG2_CTRLO, SEEQ_CFG2);
+ outw( SEEQCMD_SET_RX_ON | SEEQCMD_TX_INT_EN | SEEQCMD_RX_INT_EN, SEEQ_CMD);
+
+ if (net_debug>4) {
+ int old_cfg1;
+ old_cfg1 = inw(SEEQ_CFG1);
+ printk("%s: stat = 0x%04x\n",dev->name,inw(SEEQ_STATUS));
+ printk("%s: cfg1 = 0x%04x\n",dev->name,old_cfg1);
+ printk("%s: cfg2 = 0x%04x\n",dev->name,inw(SEEQ_CFG2));
+ printk("%s: raer = 0x%04x\n",dev->name,inw(SEEQ_REA));
+ printk("%s: dmaar= 0x%04x\n",dev->name,inw(SEEQ_DMAAR));
+
+ }
+}
+
+
+void hardware_send_packet(struct device * dev, char *buf, int length)
+{
+ int ioaddr = dev->base_addr;
+ int status = inw(SEEQ_STATUS);
+ int transmit_ptr = 0;
+ int tmp;
+
+ if (net_debug>4) {
+ printk("%s: send 0x%04x\n",dev->name,length);
+ }
+
+ /* Set FIFO to writemode and set packet-buffer address */
+ outw( SEEQCMD_FIFO_WRITE | (status & SEEQCMD_INT_MASK), SEEQ_CMD);
+ outw( transmit_ptr, SEEQ_DMAAR);
+
+ /* output SEEQ Packet header barfage */
+ outw( htons(length + 4), SEEQ_BUFFER);
+ outw( SEEQPKTH_XMIT | SEEQPKTH_DATA_FOLLOWS | SEEQPKTH_XMIT_INT_EN, SEEQ_BUFFER );
+
+ /* blat the buffer */
+ outsw( SEEQ_BUFFER, buf, (length +1) >> 1);
+ /* paranoia !! */
+ outw( 0, SEEQ_BUFFER);
+ outw( 0, SEEQ_BUFFER);
+
+ /* set address of start of transmit chain */
+ outw( transmit_ptr, SEEQ_TPR);
+
+ /* drain FIFO */
+ tmp = jiffies;
+ while ( (((status=inw(SEEQ_STATUS)) & SEEQSTAT_FIFO_EMPTY) == 0) && (jiffies < tmp + HZ))
+ mb();
+
+ /* doit ! */
+ outw( SEEQCMD_WINDOW_INT_ACK | SEEQCMD_SET_TX_ON | (status & SEEQCMD_INT_MASK), SEEQ_CMD);
+
+}
+
+
+/*
+ * wait_for_buffer
+ *
+ * This routine waits for the SEEQ chip to assert that the FIFO is ready
+ * by checking for a window interrupt, and then clearing it
+ */
+inline void wait_for_buffer(struct device * dev)
+{
+ int ioaddr = dev->base_addr;
+ int tmp;
+ int status;
+
+ tmp = jiffies + HZ;
+ while ( ( ((status=inw(SEEQ_STATUS)) & SEEQSTAT_WINDOW_INT) != SEEQSTAT_WINDOW_INT) && jiffies < tmp)
+ mb();
+
+ if ( (status & SEEQSTAT_WINDOW_INT) == SEEQSTAT_WINDOW_INT)
+ outw( SEEQCMD_WINDOW_INT_ACK | (status & SEEQCMD_INT_MASK), SEEQ_CMD);
+}
+
+
+/*
+ * Local variables:
+ * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c skeleton.c"
+ * version-control: t
+ * kept-new-versions: 5
+ * tab-width: 4
+ * End:
+ */
diff --git a/i386/i386at/gpl/linux/net/seeq8005.h b/i386/i386at/gpl/linux/net/seeq8005.h
new file mode 100644
index 00000000..7122340c
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/seeq8005.h
@@ -0,0 +1,156 @@
+/*
+ * defines, etc for the seeq8005
+ */
+
+/*
+ * This file is distributed under GPL.
+ *
+ * This style and layout of this file is also copied
+ * from many of the other linux network device drivers.
+ */
+
+/* The number of low I/O ports used by the ethercard. */
+#define SEEQ8005_IO_EXTENT 16
+
+#define SEEQ_B (ioaddr)
+
+#define SEEQ_CMD (SEEQ_B) /* Write only */
+#define SEEQ_STATUS (SEEQ_B) /* Read only */
+#define SEEQ_CFG1 (SEEQ_B + 2)
+#define SEEQ_CFG2 (SEEQ_B + 4)
+#define SEEQ_REA (SEEQ_B + 6) /* Receive End Area Register */
+#define SEEQ_RPR (SEEQ_B + 10) /* Receive Pointer Register */
+#define SEEQ_TPR (SEEQ_B + 12) /* Transmit Pointer Register */
+#define SEEQ_DMAAR (SEEQ_B + 14) /* DMA Address Register */
+#define SEEQ_BUFFER (SEEQ_B + 8) /* Buffer Window Register */
+
+#define DEFAULT_TEA (0x3f)
+
+#define SEEQCMD_DMA_INT_EN (0x0001) /* DMA Interrupt Enable */
+#define SEEQCMD_RX_INT_EN (0x0002) /* Receive Interrupt Enable */
+#define SEEQCMD_TX_INT_EN (0x0004) /* Transmit Interrupt Enable */
+#define SEEQCMD_WINDOW_INT_EN (0x0008) /* What the hell is this for?? */
+#define SEEQCMD_INT_MASK (0x000f)
+
+#define SEEQCMD_DMA_INT_ACK (0x0010) /* DMA ack */
+#define SEEQCMD_RX_INT_ACK (0x0020)
+#define SEEQCMD_TX_INT_ACK (0x0040)
+#define SEEQCMD_WINDOW_INT_ACK (0x0080)
+#define SEEQCMD_ACK_ALL (0x00f0)
+
+#define SEEQCMD_SET_DMA_ON (0x0100) /* Enables DMA Request logic */
+#define SEEQCMD_SET_RX_ON (0x0200) /* Enables Packet RX */
+#define SEEQCMD_SET_TX_ON (0x0400) /* Starts TX run */
+#define SEEQCMD_SET_DMA_OFF (0x0800)
+#define SEEQCMD_SET_RX_OFF (0x1000)
+#define SEEQCMD_SET_TX_OFF (0x2000)
+#define SEEQCMD_SET_ALL_OFF (0x3800) /* set all logic off */
+
+#define SEEQCMD_FIFO_READ (0x4000) /* Set FIFO to read mode (read from Buffer) */
+#define SEEQCMD_FIFO_WRITE (0x8000) /* Set FIFO to write mode */
+
+#define SEEQSTAT_DMA_INT_EN (0x0001) /* Status of interrupt enable */
+#define SEEQSTAT_RX_INT_EN (0x0002)
+#define SEEQSTAT_TX_INT_EN (0x0004)
+#define SEEQSTAT_WINDOW_INT_EN (0x0008)
+
+#define SEEQSTAT_DMA_INT (0x0010) /* Interrupt flagged */
+#define SEEQSTAT_RX_INT (0x0020)
+#define SEEQSTAT_TX_INT (0x0040)
+#define SEEQSTAT_WINDOW_INT (0x0080)
+#define SEEQSTAT_ANY_INT (0x00f0)
+
+#define SEEQSTAT_DMA_ON (0x0100) /* DMA logic on */
+#define SEEQSTAT_RX_ON (0x0200) /* Packet RX on */
+#define SEEQSTAT_TX_ON (0x0400) /* TX running */
+
+#define SEEQSTAT_FIFO_FULL (0x2000)
+#define SEEQSTAT_FIFO_EMPTY (0x4000)
+#define SEEQSTAT_FIFO_DIR (0x8000) /* 1=read, 0=write */
+
+#define SEEQCFG1_BUFFER_MASK (0x000f) /* define what mapps into the BUFFER register */
+#define SEEQCFG1_BUFFER_MAC0 (0x0000) /* MAC station addresses 0-5 */
+#define SEEQCFG1_BUFFER_MAC1 (0x0001)
+#define SEEQCFG1_BUFFER_MAC2 (0x0002)
+#define SEEQCFG1_BUFFER_MAC3 (0x0003)
+#define SEEQCFG1_BUFFER_MAC4 (0x0004)
+#define SEEQCFG1_BUFFER_MAC5 (0x0005)
+#define SEEQCFG1_BUFFER_PROM (0x0006) /* The Address/CFG PROM */
+#define SEEQCFG1_BUFFER_TEA (0x0007) /* Transmit end area */
+#define SEEQCFG1_BUFFER_BUFFER (0x0008) /* Packet buffer memory */
+#define SEEQCFG1_BUFFER_INT_VEC (0x0009) /* Interrupt Vector */
+
+#define SEEQCFG1_DMA_INTVL_MASK (0x0030)
+#define SEEQCFG1_DMA_CONT (0x0000)
+#define SEEQCFG1_DMA_800ns (0x0010)
+#define SEEQCFG1_DMA_1600ns (0x0020)
+#define SEEQCFG1_DMA_3200ns (0x0030)
+
+#define SEEQCFG1_DMA_LEN_MASK (0x00c0)
+#define SEEQCFG1_DMA_LEN1 (0x0000)
+#define SEEQCFG1_DMA_LEN2 (0x0040)
+#define SEEQCFG1_DMA_LEN4 (0x0080)
+#define SEEQCFG1_DMA_LEN8 (0x00c0)
+
+#define SEEQCFG1_MAC_MASK (0x3f00) /* Dis/enable bits for MAC addresses */
+#define SEEQCFG1_MAC0_EN (0x0100)
+#define SEEQCFG1_MAC1_EN (0x0200)
+#define SEEQCFG1_MAC2_EN (0x0400)
+#define SEEQCFG1_MAC3_EN (0x0800)
+#define SEEQCFG1_MAC4_EN (0x1000)
+#define SEEQCFG1_MAC5_EN (0x2000)
+
+#define SEEQCFG1_MATCH_MASK (0xc000) /* Packet matching logic cfg bits */
+#define SEEQCFG1_MATCH_SPECIFIC (0x0000) /* only matching MAC addresses */
+#define SEEQCFG1_MATCH_BROAD (0x4000) /* matching and broadcast addresses */
+#define SEEQCFG1_MATCH_MULTI (0x8000) /* matching, broadcast and multicast */
+#define SEEQCFG1_MATCH_ALL (0xc000) /* Promiscuous mode */
+
+#define SEEQCFG1_DEFAULT (SEEQCFG1_BUFFER_BUFFER | SEEQCFG1_MAC0_EN | SEEQCFG1_MATCH_BROAD)
+
+#define SEEQCFG2_BYTE_SWAP (0x0001) /* 0=Intel byte-order */
+#define SEEQCFG2_AUTO_REA (0x0002) /* if set, Receive End Area will be updated when reading from Buffer */
+
+#define SEEQCFG2_CRC_ERR_EN (0x0008) /* enables receiving of packets with CRC errors */
+#define SEEQCFG2_DRIBBLE_EN (0x0010) /* enables receiving of non-aligned packets */
+#define SEEQCFG2_SHORT_EN (0x0020) /* enables receiving of short packets */
+
+#define SEEQCFG2_SLOTSEL (0x0040) /* 0= standard IEEE802.3, 1= smaller,faster, non-standard */
+#define SEEQCFG2_NO_PREAM (0x0080) /* 1= user supplies Xmit preamble bytes */
+#define SEEQCFG2_ADDR_LEN (0x0100) /* 1= 2byte addresses */
+#define SEEQCFG2_REC_CRC (0x0200) /* 0= received packets will have CRC stripped from them */
+#define SEEQCFG2_XMIT_NO_CRC (0x0400) /* dont xmit CRC with each packet (user supplies it) */
+#define SEEQCFG2_LOOPBACK (0x0800)
+#define SEEQCFG2_CTRLO (0x1000)
+#define SEEQCFG2_RESET (0x8000) /* software Hard-reset bit */
+
+struct seeq_pkt_hdr {
+ unsigned short next; /* address of next packet header */
+ unsigned char babble_int:1, /* enable int on >1514 byte packet */
+ coll_int:1, /* enable int on collision */
+ coll_16_int:1, /* enable int on >15 collision */
+ xmit_int:1, /* enable int on success (or xmit with <15 collision) */
+ unused:1,
+ data_follows:1, /* if not set, process this as a header and pointer only */
+ chain_cont:1, /* if set, more headers in chain only cmd bit valid in recv header */
+ xmit_recv:1; /* if set, a xmit packet, else a receive packet.*/
+ unsigned char status;
+};
+
+#define SEEQPKTH_BAB_INT_EN (0x01) /* xmit only */
+#define SEEQPKTH_COL_INT_EN (0x02) /* xmit only */
+#define SEEQPKTH_COL16_INT_EN (0x04) /* xmit only */
+#define SEEQPKTH_XMIT_INT_EN (0x08) /* xmit only */
+#define SEEQPKTH_DATA_FOLLOWS (0x20) /* supposedly in xmit only */
+#define SEEQPKTH_CHAIN (0x40) /* more headers follow */
+#define SEEQPKTH_XMIT (0x80)
+
+#define SEEQPKTS_BABBLE (0x0100) /* xmit only */
+#define SEEQPKTS_OVERSIZE (0x0100) /* recv only */
+#define SEEQPKTS_COLLISION (0x0200) /* xmit only */
+#define SEEQPKTS_CRC_ERR (0x0200) /* recv only */
+#define SEEQPKTS_COLL16 (0x0400) /* xmit only */
+#define SEEQPKTS_DRIB (0x0400) /* recv only */
+#define SEEQPKTS_SHORT (0x0800) /* recv only */
+#define SEEQPKTS_DONE (0x8000)
+#define SEEQPKTS_ANY_ERROR (0x0f00)
diff --git a/i386/i386at/gpl/linux/net/sk_g16.c b/i386/i386at/gpl/linux/net/sk_g16.c
new file mode 100644
index 00000000..83989485
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/sk_g16.c
@@ -0,0 +1,2111 @@
+/*-
+ * Copyright (C) 1994 by PJD Weichmann & SWS Bern, Switzerland
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU Public License, incorporated herein by reference.
+ *
+ * Module : sk_g16.c
+ *
+ * Version : $Revision: 1.1.1.1 $
+ *
+ * Author : Patrick J.D. Weichmann
+ *
+ * Date Created : 94/05/26
+ * Last Updated : $Date: 1997/02/25 21:27:39 $
+ *
+ * Description : Schneider & Koch G16 Ethernet Device Driver for
+ * Linux Kernel >= 1.1.22
+ * Update History :
+ *
+-*/
+
+static const char *rcsid = "$Id: sk_g16.c,v 1.1.1.1 1997/02/25 21:27:39 thomas Exp $";
+
+/*
+ * The Schneider & Koch (SK) G16 Network device driver is based
+ * on the 'ni6510' driver from Michael Hipp which can be found at
+ * ftp://sunsite.unc.edu/pub/Linux/system/Network/drivers/nidrivers.tar.gz
+ *
+ * Sources: 1) ni6510.c by M. Hipp
+ * 2) depca.c by D.C. Davies
+ * 3) skeleton.c by D. Becker
+ * 4) Am7990 Local Area Network Controller for Ethernet (LANCE),
+ * AMD, Pub. #05698, June 1989
+ *
+ * Many Thanks for helping me to get things working to:
+ *
+ * A. Cox (A.Cox@swansea.ac.uk)
+ * M. Hipp (mhipp@student.uni-tuebingen.de)
+ * R. Bolz (Schneider & Koch, Germany)
+ *
+ * See README.sk_g16 for details about limitations and bugs for the
+ * current version.
+ *
+ * To Do:
+ * - Support of SK_G8 and other SK Network Cards.
+ * - Autoset memory mapped RAM. Check for free memory and then
+ * configure RAM correctly.
+ * - SK_close should really set card in to initial state.
+ * - Test if IRQ 3 is not switched off. Use autoirq() functionality.
+ * (as in /drivers/net/skeleton.c)
+ * - Implement Multicast addressing. At minimum something like
+ * in depca.c.
+ * - Redo the statistics part.
+ * - Try to find out if the board is in 8 Bit or 16 Bit slot.
+ * If in 8 Bit mode don't use IRQ 11.
+ * - (Try to make it slightly faster.)
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/fcntl.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/malloc.h>
+#include <linux/ioport.h>
+#include <linux/string.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/bitops.h>
+#include <linux/errno.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include "sk_g16.h"
+
+/*
+ * Schneider & Koch Card Definitions
+ * =================================
+ */
+
+#define SK_NAME "SK_G16"
+
+/*
+ * SK_G16 Configuration
+ * --------------------
+ */
+
+/*
+ * Abbreviations
+ * -------------
+ *
+ * RAM - used for the 16KB shared memory
+ * Boot_ROM, ROM - are used for referencing the BootEPROM
+ *
+ * SK_BOOT_ROM and SK_ADDR are symbolic constants used to configure
+ * the behaviour of the driver and the SK_G16.
+ *
+ * ! See sk_g16.install on how to install and configure the driver !
+ *
+ * SK_BOOT_ROM defines if the Boot_ROM should be switched off or not.
+ *
+ * SK_ADDR defines the address where the RAM will be mapped into the real
+ * host memory.
+ * valid addresses are from 0xa0000 to 0xfc000 in 16Kbyte steps.
+ */
+
+#define SK_BOOT_ROM 1 /* 1=BootROM on 0=off */
+
+#define SK_ADDR 0xcc000
+
+/*
+ * In POS3 are bits A14-A19 of the address bus. These bits can be set
+ * to choose the RAM address. Thats why we only can choose the RAM address
+ * in 16KB steps.
+ */
+
+#define POS_ADDR (rom_addr>>14) /* Do not change this line */
+
+/*
+ * SK_G16 I/O PORT's + IRQ's + Boot_ROM locations
+ * ----------------------------------------------
+ */
+
+/*
+ * As nearly every card has also SK_G16 a specified I/O Port region and
+ * only a few possible IRQ's.
+ * In the Installation Guide from Schneider & Koch is listed a possible
+ * Interrupt IRQ2. IRQ2 is always IRQ9 in boards with two cascaded interrupt
+ * controllers. So we use in SK_IRQS IRQ9.
+ */
+
+/* Don't touch any of the following #defines. */
+
+#define SK_IO_PORTS { 0x100, 0x180, 0x208, 0x220, 0x288, 0x320, 0x328, 0x390, 0 }
+
+#define SK_IRQS { 3, 5, 9, 11, 0 }
+
+#define SK_BOOT_ROM_LOCATIONS { 0xc0000, 0xc4000, 0xc8000, 0xcc000, 0xd0000, 0xd4000, 0xd8000, 0xdc000, 0 }
+
+#define SK_BOOT_ROM_ID { 0x55, 0xaa, 0x10, 0x50, 0x06, 0x33 }
+
+/*
+ * SK_G16 POS REGISTERS
+ * --------------------
+ */
+
+/*
+ * SK_G16 has a Programmable Option Select (POS) Register.
+ * The POS is composed of 8 separate registers (POS0-7) which
+ * are I/O mapped on an address set by the W1 switch.
+ *
+ */
+
+#define SK_POS_SIZE 8 /* 8 I/O Ports are used by SK_G16 */
+
+#define SK_POS0 ioaddr /* Card-ID Low (R) */
+#define SK_POS1 ioaddr+1 /* Card-ID High (R) */
+#define SK_POS2 ioaddr+2 /* Card-Enable, Boot-ROM Disable (RW) */
+#define SK_POS3 ioaddr+3 /* Base address of RAM */
+#define SK_POS4 ioaddr+4 /* IRQ */
+
+/* POS5 - POS7 are unused */
+
+/*
+ * SK_G16 MAC PREFIX
+ * -----------------
+ */
+
+/*
+ * Scheider & Koch manufacturer code (00:00:a5).
+ * This must be checked, that we are sure it is a SK card.
+ */
+
+#define SK_MAC0 0x00
+#define SK_MAC1 0x00
+#define SK_MAC2 0x5a
+
+/*
+ * SK_G16 ID
+ * ---------
+ */
+
+/*
+ * If POS0,POS1 contain the following ID, then we know
+ * at which I/O Port Address we are.
+ */
+
+#define SK_IDLOW 0xfd
+#define SK_IDHIGH 0x6a
+
+
+/*
+ * LANCE POS Bit definitions
+ * -------------------------
+ */
+
+#define SK_ROM_RAM_ON (POS2_CARD)
+#define SK_ROM_RAM_OFF (POS2_EPROM)
+#define SK_ROM_ON (inb(SK_POS2) & POS2_CARD)
+#define SK_ROM_OFF (inb(SK_POS2) | POS2_EPROM)
+#define SK_RAM_ON (inb(SK_POS2) | POS2_CARD)
+#define SK_RAM_OFF (inb(SK_POS2) & POS2_EPROM)
+
+#define POS2_CARD 0x0001 /* 1 = SK_G16 on 0 = off */
+#define POS2_EPROM 0x0002 /* 1 = Boot EPROM off 0 = on */
+
+/*
+ * SK_G16 Memory mapped Registers
+ * ------------------------------
+ *
+ */
+
+#define SK_IOREG (board->ioreg) /* LANCE data registers. */
+#define SK_PORT (board->port) /* Control, Status register */
+#define SK_IOCOM (board->iocom) /* I/O Command */
+
+/*
+ * SK_G16 Status/Control Register bits
+ * -----------------------------------
+ *
+ * (C) Controlreg (S) Statusreg
+ */
+
+/*
+ * Register transfer: 0 = no transfer
+ * 1 = transferring data between LANCE and I/O reg
+ */
+#define SK_IORUN 0x20
+
+/*
+ * LANCE interrupt: 0 = LANCE interrupt occurred
+ * 1 = no LANCE interrupt occurred
+ */
+#define SK_IRQ 0x10
+
+#define SK_RESET 0x08 /* Reset SK_CARD: 0 = RESET 1 = normal */
+#define SK_RW 0x02 /* 0 = write to 1 = read from */
+#define SK_ADR 0x01 /* 0 = REG DataPort 1 = RAP Reg addr port */
+
+
+#define SK_RREG SK_RW /* Transferdirection to read from lance */
+#define SK_WREG 0 /* Transferdirection to write to lance */
+#define SK_RAP SK_ADR /* Destination Register RAP */
+#define SK_RDATA 0 /* Destination Register REG DataPort */
+
+/*
+ * SK_G16 I/O Command
+ * ------------------
+ */
+
+/*
+ * Any bitcombination sets the internal I/O bit (transfer will start)
+ * when written to I/O Command
+ */
+
+#define SK_DOIO 0x80 /* Do Transfer */
+
+/*
+ * LANCE RAP (Register Address Port).
+ * ---------------------------------
+ */
+
+/*
+ * The LANCE internal registers are selected through the RAP.
+ * The Registers are:
+ *
+ * CSR0 - Status and Control flags
+ * CSR1 - Low order bits of initialize block (bits 15:00)
+ * CSR2 - High order bits of initialize block (bits 07:00, 15:08 are reserved)
+ * CSR3 - Allows redefinition of the Bus Master Interface.
+ * This register must be set to 0x0002, which means BSWAP = 0,
+ * ACON = 1, BCON = 0;
+ *
+ */
+
+#define CSR0 0x00
+#define CSR1 0x01
+#define CSR2 0x02
+#define CSR3 0x03
+
+/*
+ * General Definitions
+ * ===================
+ */
+
+/*
+ * Set the number of Tx and Rx buffers, using Log_2(# buffers).
+ * We have 16KB RAM which can be accessed by the LANCE. In the
+ * memory are not only the buffers but also the ring descriptors and
+ * the initialize block.
+ * Don't change anything unless you really know what you do.
+ */
+
+#define LC_LOG_TX_BUFFERS 1 /* (2 == 2^^1) 2 Transmit buffers */
+#define LC_LOG_RX_BUFFERS 3 /* (8 == 2^^3) 8 Receive buffers */
+
+/* Descriptor ring sizes */
+
+#define TMDNUM (1 << (LC_LOG_TX_BUFFERS)) /* 2 Transmit descriptor rings */
+#define RMDNUM (1 << (LC_LOG_RX_BUFFERS)) /* 8 Receive Buffers */
+
+/* Define Mask for setting RMD, TMD length in the LANCE init_block */
+
+#define TMDNUMMASK (LC_LOG_TX_BUFFERS << 29)
+#define RMDNUMMASK (LC_LOG_RX_BUFFERS << 29)
+
+/*
+ * Data Buffer size is set to maximum packet length.
+ */
+
+#define PKT_BUF_SZ 1518
+
+/*
+ * The number of low I/O ports used by the ethercard.
+ */
+
+#define ETHERCARD_TOTAL_SIZE SK_POS_SIZE
+
+/*
+ * Portreserve is there to mark the Card I/O Port region as used.
+ * Check_region is to check if the region at ioaddr with the size "size"
+ * is free or not.
+ * Snarf_region allocates the I/O Port region.
+ */
+
+#ifndef HAVE_PORTRESERVE
+
+#define check_region(ioaddr, size) 0
+#define request_region(ioaddr, size,name) do ; while (0)
+
+#endif
+
+/*
+ * SK_DEBUG
+ *
+ * Here you can choose what level of debugging wanted.
+ *
+ * If SK_DEBUG and SK_DEBUG2 are undefined, then only the
+ * necessary messages will be printed.
+ *
+ * If SK_DEBUG is defined, there will be many debugging prints
+ * which can help to find some mistakes in configuration or even
+ * in the driver code.
+ *
+ * If SK_DEBUG2 is defined, many many messages will be printed
+ * which normally you don't need. I used this to check the interrupt
+ * routine.
+ *
+ * (If you define only SK_DEBUG2 then only the messages for
+ * checking interrupts will be printed!)
+ *
+ * Normal way of live is:
+ *
+ * For the whole thing get going let both symbolic constants
+ * undefined. If you face any problems and you know what's going
+ * on (you know something about the card and you can interpret some
+ * hex LANCE register output) then define SK_DEBUG
+ *
+ */
+
+#undef SK_DEBUG /* debugging */
+#undef SK_DEBUG2 /* debugging with more verbose report */
+
+#ifdef SK_DEBUG
+#define PRINTK(x) printk x
+#else
+#define PRINTK(x) /**/
+#endif
+
+#ifdef SK_DEBUG2
+#define PRINTK2(x) printk x
+#else
+#define PRINTK2(x) /**/
+#endif
+
+/*
+ * SK_G16 RAM
+ *
+ * The components are memory mapped and can be set in a region from
+ * 0x00000 through 0xfc000 in 16KB steps.
+ *
+ * The Network components are: dual ported RAM, Prom, I/O Reg, Status-,
+ * Controlregister and I/O Command.
+ *
+ * dual ported RAM: This is the only memory region which the LANCE chip
+ * has access to. From the Lance it is addressed from 0x0000 to
+ * 0x3fbf. The host accesses it normally.
+ *
+ * PROM: The PROM obtains the ETHERNET-MAC-Address. It is realised as a
+ * 8-Bit PROM, this means only the 16 even addresses are used of the
+ * 32 Byte Address region. Access to a odd address results in invalid
+ * data.
+ *
+ * LANCE I/O Reg: The I/O Reg is build of 4 single Registers, Low-Byte Write,
+ * Hi-Byte Write, Low-Byte Read, Hi-Byte Read.
+ * Transfer from or to the LANCE is always in 16Bit so Low and High
+ * registers are always relevant.
+ *
+ * The Data from the Readregister is not the data in the Writeregister!!
+ *
+ * Port: Status- and Controlregister.
+ * Two different registers which share the same address, Status is
+ * read-only, Control is write-only.
+ *
+ * I/O Command:
+ * Any bitcombination written in here starts the transmission between
+ * Host and LANCE.
+ */
+
+typedef struct
+{
+ unsigned char ram[0x3fc0]; /* 16KB dual ported ram */
+ unsigned char rom[0x0020]; /* 32Byte PROM containing 6Byte MAC */
+ unsigned char res1[0x0010]; /* reserved */
+ unsigned volatile short ioreg;/* LANCE I/O Register */
+ unsigned volatile char port; /* Statusregister and Controlregister */
+ unsigned char iocom; /* I/O Command Register */
+} SK_RAM;
+
+/* struct */
+
+/*
+ * This is the structure for the dual ported ram. We
+ * have exactly 16 320 Bytes. In here there must be:
+ *
+ * - Initialize Block (starting at a word boundary)
+ * - Receive and Transmit Descriptor Rings (quadword boundary)
+ * - Data Buffers (arbitrary boundary)
+ *
+ * This is because LANCE has on SK_G16 only access to the dual ported
+ * RAM and nowhere else.
+ */
+
+struct SK_ram
+{
+ struct init_block ib;
+ struct tmd tmde[TMDNUM];
+ struct rmd rmde[RMDNUM];
+ char tmdbuf[TMDNUM][PKT_BUF_SZ];
+ char rmdbuf[RMDNUM][PKT_BUF_SZ];
+};
+
+/*
+ * Structure where all necessary information is for ring buffer
+ * management and statistics.
+ */
+
+struct priv
+{
+ struct SK_ram *ram; /* dual ported ram structure */
+ struct rmd *rmdhead; /* start of receive ring descriptors */
+ struct tmd *tmdhead; /* start of transmit ring descriptors */
+ int rmdnum; /* actual used ring descriptor */
+ int tmdnum; /* actual transmit descriptor for transmitting data */
+ int tmdlast; /* last sent descriptor used for error handling, etc */
+ void *rmdbufs[RMDNUM]; /* pointer to the receive buffers */
+ void *tmdbufs[TMDNUM]; /* pointer to the transmit buffers */
+ struct enet_statistics stats; /* Device driver statistics */
+};
+
+/* global variable declaration */
+
+/* IRQ map used to reserve a IRQ (see SK_open()) */
+
+/* extern void *irq2dev_map[16]; */ /* Declared in <linux/ioport.h> */
+
+/* static variables */
+
+static SK_RAM *board; /* pointer to our memory mapped board components */
+
+/* Macros */
+
+
+/* Function Prototypes */
+
+/*
+ * Device Driver functions
+ * -----------------------
+ * See for short explanation of each function its definitions header.
+ */
+
+int SK_init(struct device *dev);
+static int SK_probe(struct device *dev, short ioaddr);
+
+static int SK_open(struct device *dev);
+static int SK_send_packet(struct sk_buff *skb, struct device *dev);
+static void SK_interrupt(int irq, struct pt_regs * regs);
+static void SK_rxintr(struct device *dev);
+static void SK_txintr(struct device *dev);
+static int SK_close(struct device *dev);
+
+static struct enet_statistics *SK_get_stats(struct device *dev);
+
+unsigned int SK_rom_addr(void);
+
+static void set_multicast_list(struct device *dev);
+
+/*
+ * LANCE Functions
+ * ---------------
+ */
+
+static int SK_lance_init(struct device *dev, unsigned short mode);
+void SK_reset_board(void);
+void SK_set_RAP(int reg_number);
+int SK_read_reg(int reg_number);
+int SK_rread_reg(void);
+void SK_write_reg(int reg_number, int value);
+
+/*
+ * Debugging functions
+ * -------------------
+ */
+
+void SK_print_pos(struct device *dev, char *text);
+void SK_print_dev(struct device *dev, char *text);
+void SK_print_ram(struct device *dev);
+
+
+/*-
+ * Function : SK_init
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/05/26
+ *
+ * Description : Check for a SK_G16 network adaptor and initialize it.
+ * This function gets called by dev_init which initializes
+ * all Network devices.
+ *
+ * Parameters : I : struct device *dev - structure preconfigured
+ * from Space.c
+ * Return Value : 0 = Driver Found and initialized
+ * Errors : ENODEV - no device found
+ * ENXIO - not probed
+ * Globals : None
+ * Update History :
+ * YY/MM/DD uid Description
+-*/
+
+/*
+ * Check for a network adaptor of this type, and return '0' if one exists.
+ * If dev->base_addr == 0, probe all likely locations.
+ * If dev->base_addr == 1, always return failure.
+ * If dev->base_addr == 2, allocate space for the device and return success
+ * (detachable devices only).
+ */
+
+int SK_init(struct device *dev)
+{
+ int ioaddr = 0; /* I/O port address used for POS regs */
+ int *port, ports[] = SK_IO_PORTS; /* SK_G16 supported ports */
+
+ /* get preconfigured base_addr from dev which is done in Space.c */
+ int base_addr = dev->base_addr;
+
+ PRINTK(("%s: %s", SK_NAME, rcsid));
+ rcsid = NULL; /* We do not want to use this further */
+
+ if (base_addr > 0x0ff) /* Check a single specified address */
+ {
+ /* Check if on specified address is a SK_G16 */
+
+ if ( (inb(SK_POS0) == SK_IDLOW) ||
+ (inb(SK_POS1) == SK_IDHIGH) )
+ {
+ return SK_probe(dev, base_addr);
+ }
+
+ return ENODEV; /* Sorry, but on specified address NO SK_G16 */
+ }
+ else if (base_addr > 0) /* Don't probe at all */
+ {
+ return ENXIO;
+ }
+
+ /* Autoprobe base_addr */
+
+ for (port = &ports[0]; *port; port++)
+ {
+ ioaddr = *port; /* we need ioaddr for accessing POS regs */
+
+ /* Check if I/O Port region is used by another board */
+
+ if (check_region(ioaddr, ETHERCARD_TOTAL_SIZE))
+ {
+ continue; /* Try next Port address */
+ }
+
+ /* Check if at ioaddr is a SK_G16 */
+
+ if ( !(inb(SK_POS0) == SK_IDLOW) ||
+ !(inb(SK_POS1) == SK_IDHIGH) )
+ {
+ continue; /* Try next Port address */
+ }
+
+ dev->base_addr = ioaddr; /* Set I/O Port Address */
+
+ if (SK_probe(dev, ioaddr) == 0)
+ {
+ return 0; /* Card found and initialized */
+ }
+ }
+
+ dev->base_addr = base_addr; /* Write back original base_addr */
+
+ return ENODEV; /* Failed to find or init driver */
+
+} /* End of SK_init */
+
+
+/*-
+ * Function : SK_probe
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/05/26
+ *
+ * Description : This function is called by SK_init and
+ * does the main part of initialization.
+ *
+ * Parameters : I : struct device *dev - SK_G16 device structure
+ * I : short ioaddr - I/O Port address where POS is.
+ * Return Value : 0 = Initialization done
+ * Errors : ENODEV - No SK_G16 found
+ * -1 - Configuration problem
+ * Globals : irq2dev_map - Which device uses which IRQ
+ * : board - pointer to SK_RAM
+ * Update History :
+ * YY/MM/DD uid Description
+ * 94/06/30 pwe SK_ADDR now checked and at the correct place
+-*/
+
+int SK_probe(struct device *dev, short ioaddr)
+{
+ int i,j; /* Counters */
+ int sk_addr_flag = 0; /* SK ADDR correct? 1 - no, 0 - yes */
+ unsigned int rom_addr; /* used to store RAM address used for POS_ADDR */
+
+ struct priv *p; /* SK_G16 private structure */
+
+ if (SK_ADDR & 0x3fff || SK_ADDR < 0xa0000)
+ {
+
+ sk_addr_flag = 1;
+
+ /*
+ * Now here we could use a routine which searches for a free
+ * place in the ram and set SK_ADDR if found. TODO.
+ */
+ }
+
+ if (SK_BOOT_ROM) /* Shall we keep Boot_ROM on ? */
+ {
+ PRINTK(("## %s: SK_BOOT_ROM is set.\n", SK_NAME));
+
+ rom_addr = SK_rom_addr();
+
+ if (rom_addr == 0) /* No Boot_ROM found */
+ {
+ if (sk_addr_flag) /* No or Invalid SK_ADDR is defined */
+ {
+ printk("%s: SK_ADDR %#08x is not valid. Check configuration.\n",
+ dev->name, SK_ADDR);
+ return -1;
+ }
+
+ rom_addr = SK_ADDR; /* assign predefined address */
+
+ PRINTK(("## %s: NO Bootrom found \n", SK_NAME));
+
+ outb(SK_ROM_RAM_OFF, SK_POS2); /* Boot_ROM + RAM off */
+ outb(POS_ADDR, SK_POS3); /* Set RAM address */
+ outb(SK_RAM_ON, SK_POS2); /* enable RAM */
+ }
+ else if (rom_addr == SK_ADDR)
+ {
+ printk("%s: RAM + ROM are set to the same address %#08x\n"
+ " Check configuration. Now switching off Boot_ROM\n",
+ SK_NAME, rom_addr);
+
+ outb(SK_ROM_RAM_OFF, SK_POS2); /* Boot_ROM + RAM off*/
+ outb(POS_ADDR, SK_POS3); /* Set RAM address */
+ outb(SK_RAM_ON, SK_POS2); /* enable RAM */
+ }
+ else
+ {
+ PRINTK(("## %s: Found ROM at %#08x\n", SK_NAME, rom_addr));
+ PRINTK(("## %s: Keeping Boot_ROM on\n", SK_NAME));
+
+ if (sk_addr_flag) /* No or Invalid SK_ADDR is defined */
+ {
+ printk("%s: SK_ADDR %#08x is not valid. Check configuration.\n",
+ dev->name, SK_ADDR);
+ return -1;
+ }
+
+ rom_addr = SK_ADDR;
+
+ outb(SK_ROM_RAM_OFF, SK_POS2); /* Boot_ROM + RAM off */
+ outb(POS_ADDR, SK_POS3); /* Set RAM address */
+ outb(SK_ROM_RAM_ON, SK_POS2); /* RAM on, BOOT_ROM on */
+ }
+ }
+ else /* Don't keep Boot_ROM */
+ {
+ PRINTK(("## %s: SK_BOOT_ROM is not set.\n", SK_NAME));
+
+ if (sk_addr_flag) /* No or Invalid SK_ADDR is defined */
+ {
+ printk("%s: SK_ADDR %#08x is not valid. Check configuration.\n",
+ dev->name, SK_ADDR);
+ return -1;
+ }
+
+ rom_addr = SK_rom_addr(); /* Try to find a Boot_ROM */
+
+ /* IF we find a Boot_ROM disable it */
+
+ outb(SK_ROM_RAM_OFF, SK_POS2); /* Boot_ROM + RAM off */
+
+ /* We found a Boot_ROM and it's gone. Set RAM address on
+ * Boot_ROM address.
+ */
+
+ if (rom_addr)
+ {
+ printk("%s: We found Boot_ROM at %#08x. Now setting RAM on"
+ "that address\n", SK_NAME, rom_addr);
+
+ outb(POS_ADDR, SK_POS3); /* Set RAM on Boot_ROM address */
+ }
+ else /* We did not find a Boot_ROM, use predefined SK_ADDR for ram */
+ {
+ if (sk_addr_flag) /* No or Invalid SK_ADDR is defined */
+ {
+ printk("%s: SK_ADDR %#08x is not valid. Check configuration.\n",
+ dev->name, SK_ADDR);
+ return -1;
+ }
+
+ rom_addr = SK_ADDR;
+
+ outb(POS_ADDR, SK_POS3); /* Set RAM address */
+ }
+ outb(SK_RAM_ON, SK_POS2); /* enable RAM */
+ }
+
+#ifdef SK_DEBUG
+ SK_print_pos(dev, "POS registers after ROM, RAM config");
+#endif
+
+ board = (SK_RAM *) rom_addr;
+
+ /* Read in station address */
+ for (i = 0, j = 0; i < ETH_ALEN; i++, j+=2)
+ {
+ dev->dev_addr[i] = board->rom[j];
+ }
+
+ /* Check for manufacturer code */
+ if (!(dev->dev_addr[0] == SK_MAC0 &&
+ dev->dev_addr[1] == SK_MAC1 &&
+ dev->dev_addr[2] == SK_MAC2) )
+ {
+ PRINTK(("## %s: We did not find SK_G16 at RAM location.\n",
+ SK_NAME));
+ return ENODEV; /* NO SK_G16 found */
+ }
+
+ printk("%s: %s found at %#3x, HW addr: %#04x:%02x:%02x:%02x:%02x:%02x\n",
+ dev->name,
+ "Schneider & Koch Netcard",
+ (unsigned int) dev->base_addr,
+ dev->dev_addr[0],
+ dev->dev_addr[1],
+ dev->dev_addr[2],
+ dev->dev_addr[3],
+ dev->dev_addr[4],
+ dev->dev_addr[5]);
+
+ /* Grab the I/O Port region */
+ request_region(ioaddr, ETHERCARD_TOTAL_SIZE,"sk_g16");
+
+ /* Initialize device structure */
+
+ /* Allocate memory for private structure */
+ p = dev->priv = (void *) kmalloc(sizeof(struct priv), GFP_KERNEL);
+ if (p == NULL)
+ return -ENOMEM;
+ memset((char *) dev->priv, 0, sizeof(struct priv)); /* clear memory */
+
+ /* Assign our Device Driver functions */
+
+ dev->open = &SK_open;
+ dev->stop = &SK_close;
+ dev->hard_start_xmit = &SK_send_packet;
+ dev->get_stats = &SK_get_stats;
+ dev->set_multicast_list = &set_multicast_list;
+
+
+ /* Set the generic fields of the device structure */
+
+ ether_setup(dev);
+
+ dev->flags &= ~IFF_MULTICAST;
+
+ /* Initialize private structure */
+
+ p->ram = (struct SK_ram *) rom_addr; /* Set dual ported RAM addr */
+ p->tmdhead = &(p->ram)->tmde[0]; /* Set TMD head */
+ p->rmdhead = &(p->ram)->rmde[0]; /* Set RMD head */
+
+ /* Initialize buffer pointers */
+
+ for (i = 0; i < TMDNUM; i++)
+ {
+ p->tmdbufs[i] = &(p->ram)->tmdbuf[i];
+ }
+
+ for (i = 0; i < RMDNUM; i++)
+ {
+ p->rmdbufs[i] = &(p->ram)->rmdbuf[i];
+ }
+
+#ifdef SK_DEBUG
+ SK_print_pos(dev, "End of SK_probe");
+ SK_print_ram(dev);
+#endif
+
+ return 0; /* Initialization done */
+
+} /* End of SK_probe() */
+
+
+/*-
+ * Function : SK_open
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/05/26
+ *
+ * Description : This function is called sometimes after booting
+ * when ifconfig program is run.
+ *
+ * This function requests an IRQ, sets the correct
+ * IRQ in the card. Then calls SK_lance_init() to
+ * init and start the LANCE chip. Then if everything is
+ * ok returns with 0 (OK), which means SK_G16 is now
+ * opened and operational.
+ *
+ * (Called by dev_open() /net/inet/dev.c)
+ *
+ * Parameters : I : struct device *dev - SK_G16 device structure
+ * Return Value : 0 - Device opened
+ * Errors : -EAGAIN - Open failed
+ * Globals : irq2dev_map - which device uses which irq
+ * Side Effects : None
+ * Update History :
+ * YY/MM/DD uid Description
+-*/
+
+static int SK_open(struct device *dev)
+{
+ int i = 0;
+ int irqval = 0;
+ int ioaddr = dev->base_addr;
+
+ int irqtab[] = SK_IRQS;
+
+ struct priv *p = (struct priv *)dev->priv;
+
+ PRINTK(("## %s: At beginning of SK_open(). CSR0: %#06x\n",
+ SK_NAME, SK_read_reg(CSR0)));
+
+ if (dev->irq == 0) /* Autoirq */
+ {
+ i = 0;
+
+ /*
+ * Check if one IRQ out of SK_IRQS is free and install
+ * interrupt handler.
+ * Most done by request_irq().
+ * irqval: 0 - interrupt handler installed for IRQ irqtab[i]
+ * -EBUSY - interrupt busy
+ * -EINVAL - irq > 15 or handler = NULL
+ */
+
+ do
+ {
+ irqval = request_irq(irqtab[i], &SK_interrupt, 0, "sk_g16");
+ i++;
+ } while (irqval && irqtab[i]);
+
+ if (irqval) /* We tried every possible IRQ but no success */
+ {
+ printk("%s: unable to get an IRQ\n", dev->name);
+ return -EAGAIN;
+ }
+
+ dev->irq = irqtab[--i];
+
+ outb(i<<2, SK_POS4); /* Set Card on probed IRQ */
+
+ }
+ else if (dev->irq == 2) /* IRQ2 is always IRQ9 */
+ {
+ if (request_irq(9, &SK_interrupt, 0, "sk_g16"))
+ {
+ printk("%s: unable to get IRQ 9\n", dev->name);
+ return -EAGAIN;
+ }
+ dev->irq = 9;
+
+ /*
+ * Now we set card on IRQ2.
+ * This can be confusing, but remember that IRQ2 on the network
+ * card is in reality IRQ9
+ */
+ outb(0x08, SK_POS4); /* set card to IRQ2 */
+
+ }
+ else /* Check IRQ as defined in Space.c */
+ {
+ int i = 0;
+
+ /* check if IRQ free and valid. Then install Interrupt handler */
+
+ if (request_irq(dev->irq, &SK_interrupt, 0, "sk_g16"))
+ {
+ printk("%s: unable to get selected IRQ\n", dev->name);
+ return -EAGAIN;
+ }
+
+ switch(dev->irq)
+ {
+ case 3: i = 0;
+ break;
+ case 5: i = 1;
+ break;
+ case 2: i = 2;
+ break;
+ case 11:i = 3;
+ break;
+ default:
+ printk("%s: Preselected IRQ %d is invalid for %s boards",
+ dev->name,
+ dev->irq,
+ SK_NAME);
+ return -EAGAIN;
+ }
+
+ outb(i<<2, SK_POS4); /* Set IRQ on card */
+ }
+
+ irq2dev_map[dev->irq] = dev; /* Set IRQ as used by us */
+
+ printk("%s: Schneider & Koch G16 at %#3x, IRQ %d, shared mem at %#08x\n",
+ dev->name, (unsigned int)dev->base_addr,
+ (int) dev->irq, (unsigned int) p->ram);
+
+ if (!(i = SK_lance_init(dev, 0))) /* LANCE init OK? */
+ {
+
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+
+#ifdef SK_DEBUG
+
+ /*
+ * This debug block tries to stop LANCE,
+ * reinit LANCE with transmitter and receiver disabled,
+ * then stop again and reinit with NORMAL_MODE
+ */
+
+ printk("## %s: After lance init. CSR0: %#06x\n",
+ SK_NAME, SK_read_reg(CSR0));
+ SK_write_reg(CSR0, CSR0_STOP);
+ printk("## %s: LANCE stopped. CSR0: %#06x\n",
+ SK_NAME, SK_read_reg(CSR0));
+ SK_lance_init(dev, MODE_DTX | MODE_DRX);
+ printk("## %s: Reinit with DTX + DRX off. CSR0: %#06x\n",
+ SK_NAME, SK_read_reg(CSR0));
+ SK_write_reg(CSR0, CSR0_STOP);
+ printk("## %s: LANCE stopped. CSR0: %#06x\n",
+ SK_NAME, SK_read_reg(CSR0));
+ SK_lance_init(dev, MODE_NORMAL);
+ printk("## %s: LANCE back to normal mode. CSR0: %#06x\n",
+ SK_NAME, SK_read_reg(CSR0));
+ SK_print_pos(dev, "POS regs before returning OK");
+
+#endif /* SK_DEBUG */
+
+ return 0; /* SK_open() is successful */
+ }
+ else /* LANCE init failed */
+ {
+
+ PRINTK(("## %s: LANCE init failed: CSR0: %#06x\n",
+ SK_NAME, SK_read_reg(CSR0)));
+
+ dev->start = 0; /* Device not ready */
+ return -EAGAIN;
+ }
+
+} /* End of SK_open() */
+
+
+/*-
+ * Function : SK_lance_init
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/05/26
+ *
+ * Description : Reset LANCE chip, fill RMD, TMD structures with
+ * start values and Start LANCE.
+ *
+ * Parameters : I : struct device *dev - SK_G16 device structure
+ * I : int mode - put LANCE into "mode" see data-sheet for
+ * more info.
+ * Return Value : 0 - Init done
+ * Errors : -1 - Init failed
+ * Update History :
+ * YY/MM/DD uid Description
+-*/
+
+static int SK_lance_init(struct device *dev, unsigned short mode)
+{
+ int i;
+ struct priv *p = (struct priv *) dev->priv;
+ struct tmd *tmdp;
+ struct rmd *rmdp;
+
+ PRINTK(("## %s: At beginning of LANCE init. CSR0: %#06x\n",
+ SK_NAME, SK_read_reg(CSR0)));
+
+ /* Reset LANCE */
+ SK_reset_board();
+
+ /* Initialize TMD's with start values */
+ p->tmdnum = 0; /* First descriptor for transmitting */
+ p->tmdlast = 0; /* First descriptor for reading stats */
+
+ for (i = 0; i < TMDNUM; i++) /* Init all TMD's */
+ {
+ tmdp = p->tmdhead + i;
+
+ tmdp->u.buffer = (unsigned long) p->tmdbufs[i]; /* assign buffer */
+
+ /* Mark TMD as start and end of packet */
+ tmdp->u.s.status = TX_STP | TX_ENP;
+ }
+
+
+ /* Initialize RMD's with start values */
+
+ p->rmdnum = 0; /* First RMD which will be used */
+
+ for (i = 0; i < RMDNUM; i++) /* Init all RMD's */
+ {
+ rmdp = p->rmdhead + i;
+
+
+ rmdp->u.buffer = (unsigned long) p->rmdbufs[i]; /* assign buffer */
+
+ /*
+ * LANCE must be owner at beginning so that he can fill in
+ * receiving packets, set status and release RMD
+ */
+
+ rmdp->u.s.status = RX_OWN;
+
+ rmdp->blen = -PKT_BUF_SZ; /* Buffer Size in a two's complement */
+
+ rmdp->mlen = 0; /* init message length */
+
+ }
+
+ /* Fill LANCE Initialize Block */
+
+ (p->ram)->ib.mode = mode; /* Set operation mode */
+
+ for (i = 0; i < ETH_ALEN; i++) /* Set physical address */
+ {
+ (p->ram)->ib.paddr[i] = dev->dev_addr[i];
+ }
+
+ for (i = 0; i < 8; i++) /* Set multicast, logical address */
+ {
+ (p->ram)->ib.laddr[i] = 0; /* We do not use logical addressing */
+ }
+
+ /* Set ring descriptor pointers and set number of descriptors */
+
+ (p->ram)->ib.rdrp = (int) p->rmdhead | RMDNUMMASK;
+ (p->ram)->ib.tdrp = (int) p->tmdhead | TMDNUMMASK;
+
+ /* Prepare LANCE Control and Status Registers */
+
+ cli();
+
+ SK_write_reg(CSR3, CSR3_ACON); /* Ale Control !!!THIS MUST BE SET!!!! */
+
+ /*
+ * LANCE addresses the RAM from 0x0000 to 0x3fbf and has no access to
+ * PC Memory locations.
+ *
+ * In structure SK_ram is defined that the first thing in ram
+ * is the initialization block. So his address is for LANCE always
+ * 0x0000
+ *
+ * CSR1 contains low order bits 15:0 of initialization block address
+ * CSR2 is built of:
+ * 7:0 High order bits 23:16 of initialization block address
+ * 15:8 reserved, must be 0
+ */
+
+ /* Set initialization block address (must be on word boundary) */
+ SK_write_reg(CSR1, 0); /* Set low order bits 15:0 */
+ SK_write_reg(CSR2, 0); /* Set high order bits 23:16 */
+
+
+ PRINTK(("## %s: After setting CSR1-3. CSR0: %#06x\n",
+ SK_NAME, SK_read_reg(CSR0)));
+
+ /* Initialize LANCE */
+
+ /*
+ * INIT = Initialize, when set, causes the LANCE to begin the
+ * initialization procedure and access the Init Block.
+ */
+
+ SK_write_reg(CSR0, CSR0_INIT);
+
+ sti();
+
+ /* Wait until LANCE finished initialization */
+
+ SK_set_RAP(CSR0); /* Register Address Pointer to CSR0 */
+
+ for (i = 0; (i < 100) && !(SK_rread_reg() & CSR0_IDON); i++)
+ ; /* Wait until init done or go ahead if problems (i>=100) */
+
+ if (i >= 100) /* Something is wrong ! */
+ {
+ printk("%s: can't init am7990, status: %04x "
+ "init_block: %#08x\n",
+ dev->name, (int) SK_read_reg(CSR0),
+ (unsigned int) &(p->ram)->ib);
+
+#ifdef SK_DEBUG
+ SK_print_pos(dev, "LANCE INIT failed");
+ SK_print_dev(dev,"Device Structure:");
+#endif
+
+ return -1; /* LANCE init failed */
+ }
+
+ PRINTK(("## %s: init done after %d ticks\n", SK_NAME, i));
+
+ /* Clear Initialize done, enable Interrupts, start LANCE */
+
+ SK_write_reg(CSR0, CSR0_IDON | CSR0_INEA | CSR0_STRT);
+
+ PRINTK(("## %s: LANCE started. CSR0: %#06x\n", SK_NAME,
+ SK_read_reg(CSR0)));
+
+ return 0; /* LANCE is up and running */
+
+} /* End of SK_lance_init() */
+
+
+
+/*-
+ * Function : SK_send_packet
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/05/27
+ *
+ * Description : Writes an socket buffer into a transmit descriptor
+ * and starts transmission.
+ *
+ * Parameters : I : struct sk_buff *skb - packet to transfer
+ * I : struct device *dev - SK_G16 device structure
+ * Return Value : 0 - OK
+ * 1 - Could not transmit (dev_queue_xmit will queue it)
+ * and try to sent it later
+ * Globals : None
+ * Side Effects : None
+ * Update History :
+ * YY/MM/DD uid Description
+-*/
+
+static int SK_send_packet(struct sk_buff *skb, struct device *dev)
+{
+ struct priv *p = (struct priv *) dev->priv;
+ struct tmd *tmdp;
+
+ if (dev->tbusy)
+ {
+ /* if Transmitter more than 150ms busy -> time_out */
+
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < 15)
+ {
+ return 1; /* We have to try transmit later */
+ }
+
+ printk("%s: xmitter timed out, try to restart!\n", dev->name);
+
+ SK_lance_init(dev, MODE_NORMAL); /* Reinit LANCE */
+
+ dev->tbusy = 0; /* Clear Transmitter flag */
+
+ dev->trans_start = jiffies; /* Mark Start of transmission */
+
+ }
+
+ /*
+ * If some upper Layer thinks we missed a transmit done interrupt
+ * we are passed NULL.
+ * (dev_queue_xmit net/inet/dev.c
+ */
+
+ if (skb == NULL)
+ {
+ /*
+ * Dequeue packets from transmit queue and send them.
+ */
+ dev_tint(dev);
+
+ return 0;
+ }
+
+ PRINTK2(("## %s: SK_send_packet() called, CSR0 %#04x.\n",
+ SK_NAME, SK_read_reg(CSR0)));
+
+
+ /*
+ * Block a timer-based transmit from overlapping.
+ * This means check if we are already in.
+ */
+
+ if (set_bit(0, (void *) &dev->tbusy) != 0) /* dev->tbusy already set ? */
+ {
+ printk("%s: Transmitter access conflict.\n", dev->name);
+ }
+ else
+ {
+ /* Evaluate Packet length */
+ short len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+
+ tmdp = p->tmdhead + p->tmdnum; /* Which descriptor for transmitting */
+
+ /* Fill in Transmit Message Descriptor */
+
+ /* Copy data into dual ported ram */
+
+ memcpy((char *) (tmdp->u.buffer & 0x00ffffff), (char *)skb->data,
+ skb->len);
+
+ tmdp->blen = -len; /* set length to transmit */
+
+ /*
+ * Packet start and end is always set because we use the maximum
+ * packet length as buffer length.
+ * Relinquish ownership to LANCE
+ */
+
+ tmdp->u.s.status = TX_OWN | TX_STP | TX_ENP;
+
+ /* Start Demand Transmission */
+ SK_write_reg(CSR0, CSR0_TDMD | CSR0_INEA);
+
+ dev->trans_start = jiffies; /* Mark start of transmission */
+
+ /* Set pointer to next transmit buffer */
+ p->tmdnum++;
+ p->tmdnum &= TMDNUM-1;
+
+ /* Do we own the next transmit buffer ? */
+ if (! ((p->tmdhead + p->tmdnum)->u.s.status & TX_OWN) )
+ {
+ /*
+ * We own next buffer and are ready to transmit, so
+ * clear busy flag
+ */
+ dev->tbusy = 0;
+ }
+ }
+ dev_kfree_skb(skb, FREE_WRITE);
+ return 0;
+} /* End of SK_send_packet */
+
+
+/*-
+ * Function : SK_interrupt
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/05/27
+ *
+ * Description : SK_G16 interrupt handler which checks for LANCE
+ * Errors, handles transmit and receive interrupts
+ *
+ * Parameters : I : int irq, struct pt_regs * regs -
+ * Return Value : None
+ * Errors : None
+ * Globals : None
+ * Side Effects : None
+ * Update History :
+ * YY/MM/DD uid Description
+-*/
+
+static void SK_interrupt(int irq, struct pt_regs * regs)
+{
+ int csr0;
+ struct device *dev = (struct device *) irq2dev_map[irq];
+ struct priv *p = (struct priv *) dev->priv;
+
+
+ PRINTK2(("## %s: SK_interrupt(). status: %#06x\n",
+ SK_NAME, SK_read_reg(CSR0)));
+
+ if (dev == NULL)
+ {
+ printk("SK_interrupt(): IRQ %d for unknown device.\n", irq);
+ }
+
+
+ if (dev->interrupt)
+ {
+ printk("%s: Re-entering the interrupt handler.\n", dev->name);
+ }
+
+ csr0 = SK_read_reg(CSR0); /* store register for checking */
+
+ dev->interrupt = 1; /* We are handling an interrupt */
+
+ /*
+ * Acknowledge all of the current interrupt sources, disable
+ * Interrupts (INEA = 0)
+ */
+
+ SK_write_reg(CSR0, csr0 & CSR0_CLRALL);
+
+ if (csr0 & CSR0_ERR) /* LANCE Error */
+ {
+ printk("%s: error: %04x\n", dev->name, csr0);
+
+ if (csr0 & CSR0_MISS) /* No place to store packet ? */
+ {
+ p->stats.rx_dropped++;
+ }
+ }
+
+ if (csr0 & CSR0_RINT) /* Receive Interrupt (packet arrived) */
+ {
+ SK_rxintr(dev);
+ }
+
+ if (csr0 & CSR0_TINT) /* Transmit interrupt (packet sent) */
+ {
+ SK_txintr(dev);
+ }
+
+ SK_write_reg(CSR0, CSR0_INEA); /* Enable Interrupts */
+
+ dev->interrupt = 0; /* We are out */
+} /* End of SK_interrupt() */
+
+
+/*-
+ * Function : SK_txintr
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/05/27
+ *
+ * Description : After sending a packet we check status, update
+ * statistics and relinquish ownership of transmit
+ * descriptor ring.
+ *
+ * Parameters : I : struct device *dev - SK_G16 device structure
+ * Return Value : None
+ * Errors : None
+ * Globals : None
+ * Update History :
+ * YY/MM/DD uid Description
+-*/
+
+static void SK_txintr(struct device *dev)
+{
+ int tmdstat;
+ struct tmd *tmdp;
+ struct priv *p = (struct priv *) dev->priv;
+
+
+ PRINTK2(("## %s: SK_txintr() status: %#06x\n",
+ SK_NAME, SK_read_reg(CSR0)));
+
+ tmdp = p->tmdhead + p->tmdlast; /* Which buffer we sent at last ? */
+
+ /* Set next buffer */
+ p->tmdlast++;
+ p->tmdlast &= TMDNUM-1;
+
+ tmdstat = tmdp->u.s.status & 0xff00; /* filter out status bits 15:08 */
+
+ /*
+ * We check status of transmitted packet.
+ * see LANCE data-sheet for error explanation
+ */
+ if (tmdstat & TX_ERR) /* Error occurred */
+ {
+ printk("%s: TX error: %04x %04x\n", dev->name, (int) tmdstat,
+ (int) tmdp->status2);
+
+ if (tmdp->status2 & TX_TDR) /* TDR problems? */
+ {
+ printk("%s: tdr-problems \n", dev->name);
+ }
+
+ if (tmdp->status2 & TX_RTRY) /* Failed in 16 attempts to transmit ? */
+ p->stats.tx_aborted_errors++;
+ if (tmdp->status2 & TX_LCOL) /* Late collision ? */
+ p->stats.tx_window_errors++;
+ if (tmdp->status2 & TX_LCAR) /* Loss of Carrier ? */
+ p->stats.tx_carrier_errors++;
+ if (tmdp->status2 & TX_UFLO) /* Underflow error ? */
+ {
+ p->stats.tx_fifo_errors++;
+
+ /*
+ * If UFLO error occurs it will turn transmitter of.
+ * So we must reinit LANCE
+ */
+
+ SK_lance_init(dev, MODE_NORMAL);
+ }
+
+ p->stats.tx_errors++;
+
+ tmdp->status2 = 0; /* Clear error flags */
+ }
+ else if (tmdstat & TX_MORE) /* Collisions occurred ? */
+ {
+ /*
+ * Here I have a problem.
+ * I only know that there must be one or up to 15 collisions.
+ * Thats why TX_MORE is set, because after 16 attempts TX_RTRY
+ * will be set which means couldn't send packet aborted transfer.
+ *
+ * First I did not have this in but then I thought at minimum
+ * we see that something was not ok.
+ * If anyone knows something better than this to handle this
+ * please report it. (see Email addresses in the README file)
+ */
+
+ p->stats.collisions++;
+ }
+ else /* Packet sent without any problems */
+ {
+ p->stats.tx_packets++;
+ }
+
+ /*
+ * We mark transmitter not busy anymore, because now we have a free
+ * transmit descriptor which can be filled by SK_send_packet and
+ * afterwards sent by the LANCE
+ */
+
+ dev->tbusy = 0;
+
+ /*
+ * mark_bh(NET_BH);
+ * This will cause net_bh() to run after this interrupt handler.
+ *
+ * The function which do handle slow IRQ parts is do_bottom_half()
+ * which runs at normal kernel priority, that means all interrupt are
+ * enabled. (see kernel/irq.c)
+ *
+ * net_bh does something like this:
+ * - check if already in net_bh
+ * - try to transmit something from the send queue
+ * - if something is in the receive queue send it up to higher
+ * levels if it is a known protocol
+ * - try to transmit something from the send queue
+ */
+
+ mark_bh(NET_BH);
+
+} /* End of SK_txintr() */
+
+
+/*-
+ * Function : SK_rxintr
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/05/27
+ *
+ * Description : Buffer sent, check for errors, relinquish ownership
+ * of the receive message descriptor.
+ *
+ * Parameters : I : SK_G16 device structure
+ * Return Value : None
+ * Globals : None
+ * Update History :
+ * YY/MM/DD uid Description
+-*/
+
+static void SK_rxintr(struct device *dev)
+{
+
+ struct rmd *rmdp;
+ int rmdstat;
+ struct priv *p = (struct priv *) dev->priv;
+
+ PRINTK2(("## %s: SK_rxintr(). CSR0: %#06x\n",
+ SK_NAME, SK_read_reg(CSR0)));
+
+ rmdp = p->rmdhead + p->rmdnum;
+
+ /* As long as we own the next entry, check status and send
+ * it up to higher layer
+ */
+
+ while (!( (rmdstat = rmdp->u.s.status) & RX_OWN))
+ {
+ /*
+ * Start and end of packet must be set, because we use
+ * the ethernet maximum packet length (1518) as buffer size.
+ *
+ * Because our buffers are at maximum OFLO and BUFF errors are
+ * not to be concerned (see Data sheet)
+ */
+
+ if ((rmdstat & (RX_STP | RX_ENP)) != (RX_STP | RX_ENP))
+ {
+ /* Start of a frame > 1518 Bytes ? */
+
+ if (rmdstat & RX_STP)
+ {
+ p->stats.rx_errors++; /* bad packet received */
+ p->stats.rx_length_errors++; /* packet to long */
+
+ printk("%s: packet too long\n", dev->name);
+ }
+
+ /*
+ * All other packets will be ignored until a new frame with
+ * start (RX_STP) set follows.
+ *
+ * What we do is just give descriptor free for new incoming
+ * packets.
+ */
+
+ rmdp->u.s.status = RX_OWN; /* Relinquish ownership to LANCE */
+
+ }
+ else if (rmdstat & RX_ERR) /* Receive Error ? */
+ {
+ printk("%s: RX error: %04x\n", dev->name, (int) rmdstat);
+
+ p->stats.rx_errors++;
+
+ if (rmdstat & RX_FRAM) p->stats.rx_frame_errors++;
+ if (rmdstat & RX_CRC) p->stats.rx_crc_errors++;
+
+ rmdp->u.s.status = RX_OWN; /* Relinquish ownership to LANCE */
+
+ }
+ else /* We have a packet which can be queued for the upper layers */
+ {
+
+ int len = (rmdp->mlen & 0x0fff); /* extract message length from receive buffer */
+ struct sk_buff *skb;
+
+ skb = dev_alloc_skb(len+2); /* allocate socket buffer */
+
+ if (skb == NULL) /* Could not get mem ? */
+ {
+
+ /*
+ * Couldn't allocate sk_buffer so we give descriptor back
+ * to Lance, update statistics and go ahead.
+ */
+
+ rmdp->u.s.status = RX_OWN; /* Relinquish ownership to LANCE */
+ printk("%s: Couldn't allocate sk_buff, deferring packet.\n",
+ dev->name);
+ p->stats.rx_dropped++;
+
+ break; /* Jump out */
+ }
+
+ /* Prepare sk_buff to queue for upper layers */
+
+ skb->dev = dev;
+ skb_reserve(skb,2); /* Align IP header on 16 byte boundary */
+
+ /*
+ * Copy data out of our receive descriptor into sk_buff.
+ *
+ * (rmdp->u.buffer & 0x00ffffff) -> get address of buffer and
+ * ignore status fields)
+ */
+
+ memcpy(skb_put(skb,len), (unsigned char *) (rmdp->u.buffer & 0x00ffffff),
+ len);
+
+
+ /*
+ * Notify the upper protocol layers that there is another packet
+ * to handle
+ *
+ * netif_rx() always succeeds. see /net/inet/dev.c for more.
+ */
+
+ skb->protocol=eth_type_trans(skb,dev);
+ netif_rx(skb); /* queue packet and mark it for processing */
+
+ /*
+ * Packet is queued and marked for processing so we
+ * free our descriptor and update statistics
+ */
+
+ rmdp->u.s.status = RX_OWN;
+ p->stats.rx_packets++;
+
+
+ p->rmdnum++;
+ p->rmdnum %= RMDNUM;
+
+ rmdp = p->rmdhead + p->rmdnum;
+ }
+ }
+} /* End of SK_rxintr() */
+
+
+/*-
+ * Function : SK_close
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/05/26
+ *
+ * Description : close gets called from dev_close() and should
+ * deinstall the card (free_irq, mem etc).
+ *
+ * Parameters : I : struct device *dev - our device structure
+ * Return Value : 0 - closed device driver
+ * Errors : None
+ * Globals : None
+ * Update History :
+ * YY/MM/DD uid Description
+-*/
+
+/* I have tried to set BOOT_ROM on and RAM off but then, after a 'ifconfig
+ * down' the system stops. So I don't shut set card to init state.
+ */
+
+static int SK_close(struct device *dev)
+{
+
+ PRINTK(("## %s: SK_close(). CSR0: %#06x\n",
+ SK_NAME, SK_read_reg(CSR0)));
+
+ dev->tbusy = 1; /* Transmitter busy */
+ dev->start = 0; /* Card down */
+
+ printk("%s: Shutting %s down CSR0 %#06x\n", dev->name, SK_NAME,
+ (int) SK_read_reg(CSR0));
+
+ SK_write_reg(CSR0, CSR0_STOP); /* STOP the LANCE */
+
+ free_irq(dev->irq); /* Free IRQ */
+ irq2dev_map[dev->irq] = 0; /* Mark IRQ as unused */
+
+ return 0; /* always succeed */
+
+} /* End of SK_close() */
+
+
+/*-
+ * Function : SK_get_stats
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/05/26
+ *
+ * Description : Return current status structure to upper layers.
+ * It is called by sprintf_stats (dev.c).
+ *
+ * Parameters : I : struct device *dev - our device structure
+ * Return Value : struct enet_statistics * - our current statistics
+ * Errors : None
+ * Side Effects : None
+ * Update History :
+ * YY/MM/DD uid Description
+-*/
+
+static struct enet_statistics *SK_get_stats(struct device *dev)
+{
+
+ struct priv *p = (struct priv *) dev->priv;
+
+ PRINTK(("## %s: SK_get_stats(). CSR0: %#06x\n",
+ SK_NAME, SK_read_reg(CSR0)));
+
+ return &p->stats; /* Return Device status */
+
+} /* End of SK_get_stats() */
+
+
+/*-
+ * Function : set_multicast_list
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/05/26
+ *
+ * Description : This function gets called when a program performs
+ * a SIOCSIFFLAGS call. Ifconfig does this if you call
+ * 'ifconfig [-]allmulti' which enables or disables the
+ * Promiscuous mode.
+ * Promiscuous mode is when the Network card accepts all
+ * packets, not only the packets which match our MAC
+ * Address. It is useful for writing a network monitor,
+ * but it is also a security problem. You have to remember
+ * that all information on the net is not encrypted.
+ *
+ * Parameters : I : struct device *dev - SK_G16 device Structure
+ * Return Value : None
+ * Errors : None
+ * Globals : None
+ * Update History :
+ * YY/MM/DD uid Description
+ * 95/10/18 ACox Noew multicast calling scheme
+-*/
+
+
+/* Set or clear the multicast filter for SK_G16.
+ */
+
+static void set_multicast_list(struct device *dev)
+{
+
+ if (dev->flags&IFF_PROMISC)
+ {
+ /* Reinitialize LANCE with MODE_PROM set */
+ SK_lance_init(dev, MODE_PROM);
+ }
+ else if (dev->mc_count==0 && !(dev->flags&IFF_ALLMULTI))
+ {
+ /* Reinitialize LANCE without MODE_PROM */
+ SK_lance_init(dev, MODE_NORMAL);
+ }
+ else
+ {
+ /* Multicast with logical address filter on */
+ /* Reinitialize LANCE without MODE_PROM */
+ SK_lance_init(dev, MODE_NORMAL);
+
+ /* Not implemented yet. */
+ }
+} /* End of set_multicast_list() */
+
+
+
+/*-
+ * Function : SK_rom_addr
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/06/01
+ *
+ * Description : Try to find a Boot_ROM at all possible locations
+ *
+ * Parameters : None
+ * Return Value : Address where Boot_ROM is
+ * Errors : 0 - Did not find Boot_ROM
+ * Globals : None
+ * Update History :
+ * YY/MM/DD uid Description
+-*/
+
+unsigned int SK_rom_addr(void)
+{
+ int i,j;
+ int rom_found = 0;
+ unsigned int rom_location[] = SK_BOOT_ROM_LOCATIONS;
+ unsigned char rom_id[] = SK_BOOT_ROM_ID;
+ unsigned char *test_byte;
+
+ /* Autodetect Boot_ROM */
+ PRINTK(("## %s: Autodetection of Boot_ROM\n", SK_NAME));
+
+ for (i = 0; (rom_location[i] != 0) && (rom_found == 0); i++)
+ {
+
+ PRINTK(("## Trying ROM location %#08x", rom_location[i]));
+
+ rom_found = 1;
+ for (j = 0; j < 6; j++)
+ {
+ test_byte = (unsigned char *) (rom_location[i]+j);
+ PRINTK((" %02x ", *test_byte));
+
+ if(!(*test_byte == rom_id[j]))
+ {
+ rom_found = 0;
+ }
+ }
+ PRINTK(("\n"));
+ }
+
+ if (rom_found == 1)
+ {
+ PRINTK(("## %s: Boot_ROM found at %#08x\n",
+ SK_NAME, rom_location[(i-1)]));
+
+ return (rom_location[--i]);
+ }
+ else
+ {
+ PRINTK(("%s: No Boot_ROM found\n", SK_NAME));
+ return 0;
+ }
+} /* End of SK_rom_addr() */
+
+
+
+/* LANCE access functions
+ *
+ * ! CSR1-3 can only be accessed when in CSR0 the STOP bit is set !
+ */
+
+
+/*-
+ * Function : SK_reset_board
+ *
+ * Author : Patrick J.D. Weichmann
+ *
+ * Date Created : 94/05/25
+ *
+ * Description : This function resets SK_G16 and all components, but
+ * POS registers are not changed
+ *
+ * Parameters : None
+ * Return Value : None
+ * Errors : None
+ * Globals : SK_RAM *board - SK_RAM structure pointer
+ *
+ * Update History :
+ * YY/MM/DD uid Description
+-*/
+
+void SK_reset_board(void)
+{
+ int i;
+
+ SK_PORT = 0x00; /* Reset active */
+ for (i = 0; i < 10 ; i++) /* Delay min 5ms */
+ ;
+ SK_PORT = SK_RESET; /* Set back to normal operation */
+
+} /* End of SK_reset_board() */
+
+
+/*-
+ * Function : SK_set_RAP
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/05/25
+ *
+ * Description : Set LANCE Register Address Port to register
+ * for later data transfer.
+ *
+ * Parameters : I : reg_number - which CSR to read/write from/to
+ * Return Value : None
+ * Errors : None
+ * Globals : SK_RAM *board - SK_RAM structure pointer
+ * Update History :
+ * YY/MM/DD uid Description
+-*/
+
+void SK_set_RAP(int reg_number)
+{
+ SK_IOREG = reg_number;
+ SK_PORT = SK_RESET | SK_RAP | SK_WREG;
+ SK_IOCOM = SK_DOIO;
+
+ while (SK_PORT & SK_IORUN)
+ ;
+} /* End of SK_set_RAP() */
+
+
+/*-
+ * Function : SK_read_reg
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/05/25
+ *
+ * Description : Set RAP and read data from a LANCE CSR register
+ *
+ * Parameters : I : reg_number - which CSR to read from
+ * Return Value : Register contents
+ * Errors : None
+ * Globals : SK_RAM *board - SK_RAM structure pointer
+ * Update History :
+ * YY/MM/DD uid Description
+-*/
+
+int SK_read_reg(int reg_number)
+{
+ SK_set_RAP(reg_number);
+
+ SK_PORT = SK_RESET | SK_RDATA | SK_RREG;
+ SK_IOCOM = SK_DOIO;
+
+ while (SK_PORT & SK_IORUN)
+ ;
+ return (SK_IOREG);
+
+} /* End of SK_read_reg() */
+
+
+/*-
+ * Function : SK_rread_reg
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/05/28
+ *
+ * Description : Read data from preseted register.
+ * This function requires that you know which
+ * Register is actually set. Be aware that CSR1-3
+ * can only be accessed when in CSR0 STOP is set.
+ *
+ * Return Value : Register contents
+ * Errors : None
+ * Globals : SK_RAM *board - SK_RAM structure pointer
+ * Update History :
+ * YY/MM/DD uid Description
+-*/
+
+int SK_rread_reg(void)
+{
+ SK_PORT = SK_RESET | SK_RDATA | SK_RREG;
+
+ SK_IOCOM = SK_DOIO;
+
+ while (SK_PORT & SK_IORUN)
+ ;
+ return (SK_IOREG);
+
+} /* End of SK_rread_reg() */
+
+
+/*-
+ * Function : SK_write_reg
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/05/25
+ *
+ * Description : This function sets the RAP then fills in the
+ * LANCE I/O Reg and starts Transfer to LANCE.
+ * It waits until transfer has ended which is max. 7 ms
+ * and then it returns.
+ *
+ * Parameters : I : reg_number - which CSR to write to
+ * I : value - what value to fill into register
+ * Return Value : None
+ * Errors : None
+ * Globals : SK_RAM *board - SK_RAM structure pointer
+ * Update History :
+ * YY/MM/DD uid Description
+-*/
+
+void SK_write_reg(int reg_number, int value)
+{
+ SK_set_RAP(reg_number);
+
+ SK_IOREG = value;
+ SK_PORT = SK_RESET | SK_RDATA | SK_WREG;
+ SK_IOCOM = SK_DOIO;
+
+ while (SK_PORT & SK_IORUN)
+ ;
+} /* End of SK_write_reg */
+
+
+
+/*
+ * Debugging functions
+ * -------------------
+ */
+
+/*-
+ * Function : SK_print_pos
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/05/25
+ *
+ * Description : This function prints out the 4 POS (Programmable
+ * Option Select) Registers. Used mainly to debug operation.
+ *
+ * Parameters : I : struct device *dev - SK_G16 device structure
+ * I : char * - Text which will be printed as title
+ * Return Value : None
+ * Errors : None
+ * Update History :
+ * YY/MM/DD uid Description
+-*/
+
+void SK_print_pos(struct device *dev, char *text)
+{
+ int ioaddr = dev->base_addr;
+
+ unsigned char pos0 = inb(SK_POS0),
+ pos1 = inb(SK_POS1),
+ pos2 = inb(SK_POS2),
+ pos3 = inb(SK_POS3),
+ pos4 = inb(SK_POS4);
+
+
+ printk("## %s: %s.\n"
+ "## pos0=%#4x pos1=%#4x pos2=%#04x pos3=%#08x pos4=%#04x\n",
+ SK_NAME, text, pos0, pos1, pos2, (pos3<<14), pos4);
+
+} /* End of SK_print_pos() */
+
+
+
+/*-
+ * Function : SK_print_dev
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/05/25
+ *
+ * Description : This function simply prints out the important fields
+ * of the device structure.
+ *
+ * Parameters : I : struct device *dev - SK_G16 device structure
+ * I : char *text - Title for printing
+ * Return Value : None
+ * Errors : None
+ * Update History :
+ * YY/MM/DD uid Description
+-*/
+
+void SK_print_dev(struct device *dev, char *text)
+{
+ if (dev == NULL)
+ {
+ printk("## %s: Device Structure. %s\n", SK_NAME, text);
+ printk("## DEVICE == NULL\n");
+ }
+ else
+ {
+ printk("## %s: Device Structure. %s\n", SK_NAME, text);
+ printk("## Device Name: %s Base Address: %#06lx IRQ: %d\n",
+ dev->name, dev->base_addr, dev->irq);
+
+ printk("## FLAGS: start: %d tbusy: %ld int: %d\n",
+ dev->start, dev->tbusy, dev->interrupt);
+
+ printk("## next device: %#08x init function: %#08x\n",
+ (int) dev->next, (int) dev->init);
+ }
+
+} /* End of SK_print_dev() */
+
+
+
+/*-
+ * Function : SK_print_ram
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/06/02
+ *
+ * Description : This function is used to check how are things set up
+ * in the 16KB RAM. Also the pointers to the receive and
+ * transmit descriptor rings and rx and tx buffers locations.
+ * It contains a minor bug in printing, but has no effect to the values
+ * only newlines are not correct.
+ *
+ * Parameters : I : struct device *dev - SK_G16 device structure
+ * Return Value : None
+ * Errors : None
+ * Globals : None
+ * Update History :
+ * YY/MM/DD uid Description
+-*/
+
+void SK_print_ram(struct device *dev)
+{
+
+ int i;
+ struct priv *p = (struct priv *) dev->priv;
+
+ printk("## %s: RAM Details.\n"
+ "## RAM at %#08x tmdhead: %#08x rmdhead: %#08x initblock: %#08x\n",
+ SK_NAME,
+ (unsigned int) p->ram,
+ (unsigned int) p->tmdhead,
+ (unsigned int) p->rmdhead,
+ (unsigned int) &(p->ram)->ib);
+
+ printk("## ");
+
+ for(i = 0; i < TMDNUM; i++)
+ {
+ if (!(i % 3)) /* Every third line do a newline */
+ {
+ printk("\n## ");
+ }
+ printk("tmdbufs%d: %#08x ", (i+1), (int) p->tmdbufs[i]);
+ }
+ printk("## ");
+
+ for(i = 0; i < RMDNUM; i++)
+ {
+ if (!(i % 3)) /* Every third line do a newline */
+ {
+ printk("\n## ");
+ }
+ printk("rmdbufs%d: %#08x ", (i+1), (int) p->rmdbufs[i]);
+ }
+ printk("\n");
+
+} /* End of SK_print_ram() */
+
diff --git a/i386/i386at/gpl/linux/net/sk_g16.h b/i386/i386at/gpl/linux/net/sk_g16.h
new file mode 100644
index 00000000..3a92f1f9
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/sk_g16.h
@@ -0,0 +1,171 @@
+/*-
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU Public License, incorporated herein by reference.
+ *
+ * Module : sk_g16.h
+ * Version : $Revision: 1.1.1.1 $
+ *
+ * Author : M.Hipp (mhipp@student.uni-tuebingen.de)
+ * changes by : Patrick J.D. Weichmann
+ *
+ * Date Created : 94/05/25
+ *
+ * Description : In here are all necessary definitions of
+ * the am7990 (LANCE) chip used for writing a
+ * network device driver which uses this chip
+ *
+ * $Log: sk_g16.h,v $
+ * Revision 1.1.1.1 1996/10/30 01:39:56 thomas
+ * Imported from UK22
+ *
+ * Revision 1.3 1996/03/25 20:24:35 goel
+ * Linux driver merge.
+ *
+-*/
+
+#ifndef SK_G16_H
+
+#define SK_G16_H
+
+
+/*
+ * Control and Status Register 0 (CSR0) bit definitions
+ *
+ * (R=Readable) (W=Writeable) (S=Set on write) (C-Clear on write)
+ *
+ */
+
+#define CSR0_ERR 0x8000 /* Error summary (R) */
+#define CSR0_BABL 0x4000 /* Babble transmitter timeout error (RC) */
+#define CSR0_CERR 0x2000 /* Collision Error (RC) */
+#define CSR0_MISS 0x1000 /* Missed packet (RC) */
+#define CSR0_MERR 0x0800 /* Memory Error (RC) */
+#define CSR0_RINT 0x0400 /* Receiver Interrupt (RC) */
+#define CSR0_TINT 0x0200 /* Transmit Interrupt (RC) */
+#define CSR0_IDON 0x0100 /* Initialization Done (RC) */
+#define CSR0_INTR 0x0080 /* Interrupt Flag (R) */
+#define CSR0_INEA 0x0040 /* Interrupt Enable (RW) */
+#define CSR0_RXON 0x0020 /* Receiver on (R) */
+#define CSR0_TXON 0x0010 /* Transmitter on (R) */
+#define CSR0_TDMD 0x0008 /* Transmit Demand (RS) */
+#define CSR0_STOP 0x0004 /* Stop (RS) */
+#define CSR0_STRT 0x0002 /* Start (RS) */
+#define CSR0_INIT 0x0001 /* Initialize (RS) */
+
+#define CSR0_CLRALL 0x7f00 /* mask for all clearable bits */
+
+/*
+ * Control and Status Register 3 (CSR3) bit definitions
+ *
+ */
+
+#define CSR3_BSWAP 0x0004 /* Byte Swap (RW) */
+#define CSR3_ACON 0x0002 /* ALE Control (RW) */
+#define CSR3_BCON 0x0001 /* Byte Control (RW) */
+
+/*
+ * Initialization Block Mode operation Bit Definitions.
+ */
+
+#define MODE_PROM 0x8000 /* Promiscuous Mode */
+#define MODE_INTL 0x0040 /* Internal Loopback */
+#define MODE_DRTY 0x0020 /* Disable Retry */
+#define MODE_COLL 0x0010 /* Force Collision */
+#define MODE_DTCR 0x0008 /* Disable Transmit CRC) */
+#define MODE_LOOP 0x0004 /* Loopback */
+#define MODE_DTX 0x0002 /* Disable the Transmitter */
+#define MODE_DRX 0x0001 /* Disable the Receiver */
+
+#define MODE_NORMAL 0x0000 /* Normal operation mode */
+
+/*
+ * Receive message descriptor status bit definitions.
+ */
+
+#define RX_OWN 0x80 /* Owner bit 0 = host, 1 = lance */
+#define RX_ERR 0x40 /* Error Summary */
+#define RX_FRAM 0x20 /* Framing Error */
+#define RX_OFLO 0x10 /* Overflow Error */
+#define RX_CRC 0x08 /* CRC Error */
+#define RX_BUFF 0x04 /* Buffer Error */
+#define RX_STP 0x02 /* Start of Packet */
+#define RX_ENP 0x01 /* End of Packet */
+
+
+/*
+ * Transmit message descriptor status bit definitions.
+ */
+
+#define TX_OWN 0x80 /* Owner bit 0 = host, 1 = lance */
+#define TX_ERR 0x40 /* Error Summary */
+#define TX_MORE 0x10 /* More the 1 retry needed to Xmit */
+#define TX_ONE 0x08 /* One retry needed to Xmit */
+#define TX_DEF 0x04 /* Deferred */
+#define TX_STP 0x02 /* Start of Packet */
+#define TX_ENP 0x01 /* End of Packet */
+
+/*
+ * Transmit status (2) (valid if TX_ERR == 1)
+ */
+
+#define TX_BUFF 0x8000 /* Buffering error (no ENP) */
+#define TX_UFLO 0x4000 /* Underflow (late memory) */
+#define TX_LCOL 0x1000 /* Late collision */
+#define TX_LCAR 0x0400 /* Loss of Carrier */
+#define TX_RTRY 0x0200 /* Failed after 16 retransmissions */
+#define TX_TDR 0x003f /* Time-domain-reflectometer-value */
+
+
+/*
+ * Structures used for Communication with the LANCE
+ */
+
+/* LANCE Initialize Block */
+
+struct init_block
+{
+ unsigned short mode; /* Mode Register */
+ unsigned char paddr[6]; /* Physical Address (MAC) */
+ unsigned char laddr[8]; /* Logical Filter Address (not used) */
+ unsigned int rdrp; /* Receive Descriptor Ring pointer */
+ unsigned int tdrp; /* Transmit Descriptor Ring pointer */
+};
+
+
+/* Receive Message Descriptor Entry */
+
+struct rmd
+{
+ union
+ {
+ unsigned long buffer; /* Address of buffer */
+ struct
+ {
+ unsigned char unused[3];
+ unsigned volatile char status; /* Status Bits */
+ } s;
+ } u;
+ volatile short blen; /* Buffer Length (two's complement) */
+ unsigned short mlen; /* Message Byte Count */
+};
+
+
+/* Transmit Message Descriptor Entry */
+
+struct tmd
+{
+ union
+ {
+ unsigned long buffer; /* Address of buffer */
+ struct
+ {
+ unsigned char unused[3];
+ unsigned volatile char status; /* Status Bits */
+ } s;
+ } u;
+ unsigned short blen; /* Buffer Length (two's complement) */
+ unsigned volatile short status2; /* Error Status Bits */
+};
+
+#endif /* End of SK_G16_H */
diff --git a/i386/i386at/gpl/linux/net/smc-ultra.c b/i386/i386at/gpl/linux/net/smc-ultra.c
new file mode 100644
index 00000000..f13cd0a7
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/smc-ultra.c
@@ -0,0 +1,419 @@
+/* smc-ultra.c: A SMC Ultra ethernet driver for linux. */
+/*
+ Written 1993,1994,1995 by Donald Becker.
+
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency.
+
+ This software may be used and distributed according to the terms
+ of the GNU Public License, incorporated herein by reference.
+
+ The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
+ Center of Excellence in Space Data and Information Sciences
+ Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
+
+ This is a driver for the SMC Ultra and SMC EtherEZ ethercards.
+
+ This driver uses the cards in the 8390-compatible, shared memory mode.
+ Most of the run-time complexity is handled by the generic code in
+ 8390.c. The code in this file is responsible for
+
+ ultra_probe() Detecting and initializing the card.
+ ultra_probe1()
+
+ ultra_open() The card-specific details of starting, stopping
+ ultra_reset_8390() and resetting the 8390 NIC core.
+ ultra_close()
+
+ ultra_block_input() Routines for reading and writing blocks of
+ ultra_block_output() packet buffer memory.
+
+ This driver enables the shared memory only when doing the actual data
+ transfers to avoid a bug in early version of the card that corrupted
+ data transferred by a AHA1542.
+
+ This driver does not support the programmed-I/O data transfer mode of
+ the EtherEZ. That support (if available) is smc-ez.c. Nor does it
+ use the non-8390-compatible "Altego" mode. (No support currently planned.)
+
+ Changelog:
+
+ Paul Gortmaker : multiple card support for module users.
+*/
+
+static const char *version =
+ "smc-ultra.c:v1.12 1/18/95 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include "8390.h"
+
+/* A zero-terminated list of I/O addresses to be probed. */
+static unsigned int ultra_portlist[] =
+{0x200, 0x220, 0x240, 0x280, 0x300, 0x340, 0x380, 0};
+
+int ultra_probe(struct device *dev);
+int ultra_probe1(struct device *dev, int ioaddr);
+
+static int ultra_open(struct device *dev);
+static void ultra_reset_8390(struct device *dev);
+static void ultra_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
+ int ring_page);
+static void ultra_block_input(struct device *dev, int count,
+ struct sk_buff *skb, int ring_offset);
+static void ultra_block_output(struct device *dev, int count,
+ const unsigned char *buf, const start_page);
+static int ultra_close_card(struct device *dev);
+
+
+#define START_PG 0x00 /* First page of TX buffer */
+
+#define ULTRA_CMDREG 0 /* Offset to ASIC command register. */
+#define ULTRA_RESET 0x80 /* Board reset, in ULTRA_CMDREG. */
+#define ULTRA_MEMENB 0x40 /* Enable the shared memory. */
+#define ULTRA_NIC_OFFSET 16 /* NIC register offset from the base_addr. */
+#define ULTRA_IO_EXTENT 32
+
+/* Probe for the Ultra. This looks like a 8013 with the station
+ address PROM at I/O ports <base>+8 to <base>+13, with a checksum
+ following.
+*/
+#ifdef HAVE_DEVLIST
+struct netdev_entry ultra_drv =
+{"ultra", ultra_probe1, NETCARD_IO_EXTENT, netcard_portlist};
+#else
+
+int ultra_probe(struct device *dev)
+{
+ int i;
+ int base_addr = dev ? dev->base_addr : 0;
+
+ if (base_addr > 0x1ff) /* Check a single specified location. */
+ return ultra_probe1(dev, base_addr);
+ else if (base_addr != 0) /* Don't probe at all. */
+ return ENXIO;
+
+ for (i = 0; ultra_portlist[i]; i++) {
+ int ioaddr = ultra_portlist[i];
+ if (check_region(ioaddr, ULTRA_IO_EXTENT))
+ continue;
+ if (ultra_probe1(dev, ioaddr) == 0)
+ return 0;
+ }
+
+ return ENODEV;
+}
+#endif
+
+int ultra_probe1(struct device *dev, int ioaddr)
+{
+ int i;
+ int checksum = 0;
+ const char *model_name;
+ unsigned char eeprom_irq = 0;
+ static unsigned version_printed = 0;
+ /* Values from various config regs. */
+ unsigned char num_pages, irqreg, addr;
+ unsigned char idreg = inb(ioaddr + 7);
+ unsigned char reg4 = inb(ioaddr + 4) & 0x7f;
+
+ /* Check the ID nibble. */
+ if ((idreg & 0xF0) != 0x20 /* SMC Ultra */
+ && (idreg & 0xF0) != 0x40) /* SMC EtherEZ */
+ return ENODEV;
+
+ /* Select the station address register set. */
+ outb(reg4, ioaddr + 4);
+
+ for (i = 0; i < 8; i++)
+ checksum += inb(ioaddr + 8 + i);
+ if ((checksum & 0xff) != 0xFF)
+ return ENODEV;
+
+ /* We should have a "dev" from Space.c or the static module table. */
+ if (dev == NULL) {
+ printk("smc-ultra.c: Passed a NULL device.\n");
+ dev = init_etherdev(0, 0);
+ }
+
+ if (ei_debug && version_printed++ == 0)
+ printk(version);
+
+ model_name = (idreg & 0xF0) == 0x20 ? "SMC Ultra" : "SMC EtherEZ";
+
+ printk("%s: %s at %#3x,", dev->name, model_name, ioaddr);
+
+ for (i = 0; i < 6; i++)
+ printk(" %2.2X", dev->dev_addr[i] = inb(ioaddr + 8 + i));
+
+ /* Switch from the station address to the alternate register set and
+ read the useful registers there. */
+ outb(0x80 | reg4, ioaddr + 4);
+
+ /* Enabled FINE16 mode to avoid BIOS ROM width mismatches @ reboot. */
+ outb(0x80 | inb(ioaddr + 0x0c), ioaddr + 0x0c);
+ irqreg = inb(ioaddr + 0xd);
+ addr = inb(ioaddr + 0xb);
+
+ /* Switch back to the station address register set so that the MS-DOS driver
+ can find the card after a warm boot. */
+ outb(reg4, ioaddr + 4);
+
+ if (dev->irq < 2) {
+ unsigned char irqmap[] = {0, 9, 3, 5, 7, 10, 11, 15};
+ int irq;
+
+ /* The IRQ bits are split. */
+ irq = irqmap[((irqreg & 0x40) >> 4) + ((irqreg & 0x0c) >> 2)];
+
+ if (irq == 0) {
+ printk(", failed to detect IRQ line.\n");
+ return -EAGAIN;
+ }
+ dev->irq = irq;
+ eeprom_irq = 1;
+ }
+
+ /* Allocate dev->priv and fill in 8390 specific dev fields. */
+ if (ethdev_init(dev)) {
+ printk (", no memory for dev->priv.\n");
+ return -ENOMEM;
+ }
+
+ /* OK, we are certain this is going to work. Setup the device. */
+ request_region(ioaddr, ULTRA_IO_EXTENT, model_name);
+
+ /* The 8390 isn't at the base address, so fake the offset */
+ dev->base_addr = ioaddr+ULTRA_NIC_OFFSET;
+
+ {
+ int addr_tbl[4] = {0x0C0000, 0x0E0000, 0xFC0000, 0xFE0000};
+ short num_pages_tbl[4] = {0x20, 0x40, 0x80, 0xff};
+
+ dev->mem_start = ((addr & 0x0f) << 13) + addr_tbl[(addr >> 6) & 3] ;
+ num_pages = num_pages_tbl[(addr >> 4) & 3];
+ }
+
+ ei_status.name = model_name;
+ ei_status.word16 = 1;
+ ei_status.tx_start_page = START_PG;
+ ei_status.rx_start_page = START_PG + TX_PAGES;
+ ei_status.stop_page = num_pages;
+
+ dev->rmem_start = dev->mem_start + TX_PAGES*256;
+ dev->mem_end = dev->rmem_end
+ = dev->mem_start + (ei_status.stop_page - START_PG)*256;
+
+ printk(",%s IRQ %d memory %#lx-%#lx.\n", eeprom_irq ? "" : "assigned ",
+ dev->irq, dev->mem_start, dev->mem_end-1);
+
+ ei_status.reset_8390 = &ultra_reset_8390;
+ ei_status.block_input = &ultra_block_input;
+ ei_status.block_output = &ultra_block_output;
+ ei_status.get_8390_hdr = &ultra_get_8390_hdr;
+ dev->open = &ultra_open;
+ dev->stop = &ultra_close_card;
+ NS8390_init(dev, 0);
+
+ return 0;
+}
+
+static int
+ultra_open(struct device *dev)
+{
+ int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */
+
+ if (request_irq(dev->irq, ei_interrupt, 0, ei_status.name))
+ return -EAGAIN;
+
+ outb(ULTRA_MEMENB, ioaddr); /* Enable memory, 16 bit mode. */
+ outb(0x80, ioaddr + 5);
+ outb(0x01, ioaddr + 6); /* Enable interrupts and memory. */
+ ei_open(dev);
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static void
+ultra_reset_8390(struct device *dev)
+{
+ int cmd_port = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC base addr */
+
+ outb(ULTRA_RESET, cmd_port);
+ if (ei_debug > 1) printk("resetting Ultra, t=%ld...", jiffies);
+ ei_status.txing = 0;
+
+ outb(ULTRA_MEMENB, cmd_port);
+
+ if (ei_debug > 1) printk("reset done\n");
+ return;
+}
+
+/* Grab the 8390 specific header. Similar to the block_input routine, but
+ we don't need to be concerned with ring wrap as the header will be at
+ the start of a page, so we optimize accordingly. */
+
+static void
+ultra_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+
+ unsigned long hdr_start = dev->mem_start + ((ring_page - START_PG)<<8);
+
+ outb(ULTRA_MEMENB, dev->base_addr - ULTRA_NIC_OFFSET); /* shmem on */
+#ifdef notdef
+ /* Officially this is what we are doing, but the readl() is faster */
+ memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr));
+#else
+ ((unsigned int*)hdr)[0] = readl(hdr_start);
+#endif
+ outb(0x00, dev->base_addr - ULTRA_NIC_OFFSET); /* shmem off */
+}
+
+/* Block input and output are easy on shared memory ethercards, the only
+ complication is when the ring buffer wraps. */
+
+static void
+ultra_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset)
+{
+ unsigned long xfer_start = dev->mem_start + ring_offset - (START_PG<<8);
+
+ /* Enable shared memory. */
+ outb(ULTRA_MEMENB, dev->base_addr - ULTRA_NIC_OFFSET);
+
+ if (xfer_start + count > dev->rmem_end) {
+ /* We must wrap the input move. */
+ int semi_count = dev->rmem_end - xfer_start;
+ memcpy_fromio(skb->data, xfer_start, semi_count);
+ count -= semi_count;
+ memcpy_fromio(skb->data + semi_count, dev->rmem_start, count);
+ } else {
+ /* Packet is in one chunk -- we can copy + cksum. */
+ eth_io_copy_and_sum(skb, xfer_start, count, 0);
+ }
+
+ outb(0x00, dev->base_addr - ULTRA_NIC_OFFSET); /* Disable memory. */
+}
+
+static void
+ultra_block_output(struct device *dev, int count, const unsigned char *buf,
+ int start_page)
+{
+ unsigned long shmem = dev->mem_start + ((start_page - START_PG)<<8);
+
+ /* Enable shared memory. */
+ outb(ULTRA_MEMENB, dev->base_addr - ULTRA_NIC_OFFSET);
+
+ memcpy_toio(shmem, buf, count);
+
+ outb(0x00, dev->base_addr - ULTRA_NIC_OFFSET); /* Disable memory. */
+}
+
+static int
+ultra_close_card(struct device *dev)
+{
+ int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* CMDREG */
+
+ dev->start = 0;
+ dev->tbusy = 1;
+
+ if (ei_debug > 1)
+ printk("%s: Shutting down ethercard.\n", dev->name);
+
+ outb(0x00, ioaddr + 6); /* Disable interrupts. */
+ free_irq(dev->irq);
+ irq2dev_map[dev->irq] = 0;
+
+ NS8390_init(dev, 0);
+
+ /* We should someday disable shared memory and change to 8-bit mode
+ "just in case"... */
+
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+}
+
+
+#ifdef MODULE
+#define MAX_ULTRA_CARDS 4 /* Max number of Ultra cards per module */
+#define NAMELEN 8 /* # of chars for storing dev->name */
+static char namelist[NAMELEN * MAX_ULTRA_CARDS] = { 0, };
+static struct device dev_ultra[MAX_ULTRA_CARDS] = {
+ {
+ NULL, /* assign a chunk of namelist[] below */
+ 0, 0, 0, 0,
+ 0, 0,
+ 0, 0, 0, NULL, NULL
+ },
+};
+
+static int io[MAX_ULTRA_CARDS] = { 0, };
+static int irq[MAX_ULTRA_CARDS] = { 0, };
+
+/* This is set up so that only a single autoprobe takes place per call.
+ISA device autoprobes on a running machine are not recommended. */
+int
+init_module(void)
+{
+ int this_dev, found = 0;
+
+ for (this_dev = 0; this_dev < MAX_ULTRA_CARDS; this_dev++) {
+ struct device *dev = &dev_ultra[this_dev];
+ dev->name = namelist+(NAMELEN*this_dev);
+ dev->irq = irq[this_dev];
+ dev->base_addr = io[this_dev];
+ dev->init = ultra_probe;
+ if (io[this_dev] == 0) {
+ if (this_dev != 0) break; /* only autoprobe 1st one */
+ printk(KERN_NOTICE "smc-ultra.c: Presently autoprobing (not recommended) for a single card.\n");
+ }
+ if (register_netdev(dev) != 0) {
+ printk(KERN_WARNING "smc-ultra.c: No SMC Ultra card found (i/o = 0x%x).\n", io[this_dev]);
+ if (found != 0) return 0; /* Got at least one. */
+ return -ENXIO;
+ }
+ found++;
+ }
+
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ int this_dev;
+
+ for (this_dev = 0; this_dev < MAX_ULTRA_CARDS; this_dev++) {
+ struct device *dev = &dev_ultra[this_dev];
+ if (dev->priv != NULL) {
+ /* NB: ultra_close_card() does free_irq + irq2dev */
+ int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET;
+ kfree(dev->priv);
+ dev->priv = NULL;
+ release_region(ioaddr, ULTRA_IO_EXTENT);
+ unregister_netdev(dev);
+ }
+ }
+}
+#endif /* MODULE */
+
+
+/*
+ * Local variables:
+ * compile-command: "gcc -D__KERNEL__ -Wall -O6 -I/usr/src/linux/net/inet -c smc-ultra.c"
+ * version-control: t
+ * kept-new-versions: 5
+ * c-indent-level: 4
+ * tab-width: 4
+ * End:
+ */
diff --git a/i386/i386at/gpl/linux/net/tulip.c b/i386/i386at/gpl/linux/net/tulip.c
new file mode 100644
index 00000000..3386a9b8
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/tulip.c
@@ -0,0 +1,782 @@
+/* tulip.c: A DEC 21040 ethernet driver for linux. */
+/*
+ NOTICE: this version works with kernels 1.1.82 and later only!
+ Written 1994,1995 by Donald Becker.
+
+ This software may be used and distributed according to the terms
+ of the GNU Public License, incorporated herein by reference.
+
+ This driver is for the SMC EtherPower PCI ethernet adapter.
+ It should work with most other DEC 21*40-based ethercards.
+
+ The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
+ Center of Excellence in Space Data and Information Sciences
+ Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
+*/
+
+static const char *version = "tulip.c:v0.05 1/20/95 becker@cesdis.gsfc.nasa.gov\n";
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+/* The total size is unusually large: The 21040 aligns each of its 16
+ longword-wide registers on a quadword boundary. */
+#define TULIP_TOTAL_SIZE 0x80
+
+#ifdef HAVE_DEVLIST
+struct netdev_entry tulip_drv =
+{"Tulip", tulip_pci_probe, TULIP_TOTAL_SIZE, NULL};
+#endif
+
+#define TULIP_DEBUG 1
+#ifdef TULIP_DEBUG
+int tulip_debug = TULIP_DEBUG;
+#else
+int tulip_debug = 1;
+#endif
+
+/*
+ Theory of Operation
+
+I. Board Compatibility
+
+This device driver is designed for the DECchip 21040 "Tulip", Digital's
+single-chip ethernet controller for PCI, as used on the SMC EtherPower
+ethernet adapter.
+
+II. Board-specific settings
+
+PCI bus devices are configured by the system at boot time, so no jumpers
+need to be set on the board. The system BIOS should be set to assign the
+PCI INTA signal to an otherwise unused system IRQ line. While it's
+physically possible to shared PCI interrupt lines, the kernel doesn't
+support it.
+
+III. Driver operation
+
+IIIa. Ring buffers
+The Tulip can use either ring buffers or lists of Tx and Rx descriptors.
+The current driver uses a statically allocated Rx ring of descriptors and
+buffers, and a list of the Tx buffers.
+
+IIIC. Synchronization
+The driver runs as two independent, single-threaded flows of control. One
+is the send-packet routine, which enforces single-threaded use by the
+dev->tbusy flag. The other thread is the interrupt handler, which is single
+threaded by the hardware and other software.
+
+The send packet thread has partial control over the Tx ring and 'dev->tbusy'
+flag. It sets the tbusy flag whenever it's queuing a Tx packet. If the next
+queue slot is empty, it clears the tbusy flag when finished otherwise it sets
+the 'tp->tx_full' flag.
+
+The interrupt handler has exclusive control over the Rx ring and records stats
+from the Tx ring. (The Tx-done interrupt can't be selectively turned off, so
+we can't avoid the interrupt overhead by having the Tx routine reap the Tx
+stats.) After reaping the stats, it marks the queue entry as empty by setting
+the 'base' to zero. Iff the 'tp->tx_full' flag is set, it clears both the
+tx_full and tbusy flags.
+
+IV. Notes
+
+Thanks to Duke Kamstra of SMC for providing an EtherPower board.
+
+The DEC databook doesn't document which Rx filter settings accept broadcast
+packets. Nor does it document how to configure the part to configure the
+serial subsystem for normal (vs. loopback) operation or how to have it
+autoswitch between internal 10baseT, SIA and AUI transceivers.
+
+The databook claims that CSR13, CSR14, and CSR15 should each be the last
+register of the set CSR12-15 written. Hmmm, now how is that possible?
+*/
+
+#define DEC_VENDOR_ID 0x1011 /* Hex 'D' :-> */
+#define DEC_21040_ID 0x0002 /* Change for 21140. */
+
+/* Keep the ring sizes a power of two for efficiency. */
+#define TX_RING_SIZE 4
+#define RX_RING_SIZE 4
+#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/
+
+/* Offsets to the Command and Status Registers, "CSRs". All accesses
+ must be longword instructions and quadword aligned. */
+enum tulip_offsets {
+ CSR0=0, CSR1=0x08, CSR2=0x10, CSR3=0x18, CSR4=0x20, CSR5=0x28,
+ CSR6=0x30, CSR7=0x38, CSR8=0x40, CSR9=0x48, CSR10=0x50, CSR11=0x58,
+ CSR12=0x60, CSR13=0x68, CSR14=0x70, CSR15=0x78 };
+
+/* The Tulip Rx and Tx buffer descriptors. */
+struct tulip_rx_desc {
+ int status;
+ int length;
+ char *buffer1, *buffer2; /* We use only buffer 1. */
+};
+
+struct tulip_tx_desc {
+ int status;
+ int length;
+ char *buffer1, *buffer2; /* We use only buffer 1. */
+};
+
+struct tulip_private {
+ char devname[8]; /* Used only for kernel debugging. */
+ struct tulip_rx_desc rx_ring[RX_RING_SIZE];
+ struct tulip_tx_desc tx_ring[TX_RING_SIZE];
+ /* The saved address of a sent-in-place packet/buffer, for skfree(). */
+ struct sk_buff* tx_skbuff[TX_RING_SIZE];
+ long rx_buffs; /* Address of temporary Rx buffers. */
+ struct enet_statistics stats;
+ int setup_frame[48]; /* Pseudo-Tx frame to init address table. */
+ unsigned int cur_rx, cur_tx; /* The next free ring entry */
+ unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */
+ unsigned int tx_full:1;
+ int pad0, pad1; /* Used for 8-byte alignment */
+};
+
+static void tulip_probe1(int ioaddr, int irq);
+static int tulip_open(struct device *dev);
+static void tulip_init_ring(struct device *dev);
+static int tulip_start_xmit(struct sk_buff *skb, struct device *dev);
+static int tulip_rx(struct device *dev);
+static void tulip_interrupt(int irq, struct pt_regs *regs);
+static int tulip_close(struct device *dev);
+static struct enet_statistics *tulip_get_stats(struct device *dev);
+static void set_multicast_list(struct device *dev);
+static int set_mac_address(struct device *dev, void *addr);
+
+
+
+#ifndef MODULE
+/* This 21040 probe is unlike most other board probes. We can use memory
+ efficiently by allocating a large contiguous region and dividing it
+ ourselves. This is done by having the initialization occur before
+ the 'kmalloc()' memory management system is started. */
+
+int dec21040_init(void)
+{
+
+ if (pcibios_present()) {
+ int pci_index;
+ for (pci_index = 0; pci_index < 8; pci_index++) {
+ unsigned char pci_bus, pci_device_fn, pci_irq_line;
+ unsigned long pci_ioaddr;
+
+ if (pcibios_find_device (DEC_VENDOR_ID, DEC_21040_ID, pci_index,
+ &pci_bus, &pci_device_fn) != 0)
+ break;
+ pcibios_read_config_byte(pci_bus, pci_device_fn,
+ PCI_INTERRUPT_LINE, &pci_irq_line);
+ pcibios_read_config_dword(pci_bus, pci_device_fn,
+ PCI_BASE_ADDRESS_0, &pci_ioaddr);
+ /* Remove I/O space marker in bit 0. */
+ pci_ioaddr &= ~3;
+ if (tulip_debug > 2)
+ printk("Found DEC PCI Tulip at I/O %#lx, IRQ %d.\n",
+ pci_ioaddr, pci_irq_line);
+ tulip_probe1(pci_ioaddr, pci_irq_line);
+ }
+ }
+
+ return 0;
+}
+#endif
+#ifdef MODULE
+static int tulip_probe(struct device *dev)
+{
+ printk("tulip: This driver does not yet install properly from module!\n");
+ return -1;
+}
+#endif
+
+static void tulip_probe1(int ioaddr, int irq)
+{
+ static int did_version = 0; /* Already printed version info. */
+ struct device *dev;
+ struct tulip_private *tp;
+ int i;
+
+ if (tulip_debug > 0 && did_version++ == 0)
+ printk(version);
+
+ dev = init_etherdev(0, 0);
+
+ printk("%s: DEC 21040 Tulip at %#3x,", dev->name, ioaddr);
+
+ /* Stop the chip's Tx and Rx processes. */
+ outl(inl(ioaddr + CSR6) & ~0x2002, ioaddr + CSR6);
+ /* Clear the missed-packet counter. */
+ inl(ioaddr + CSR8) & 0xffff;
+
+ /* The station address ROM is read byte serially. The register must
+ be polled, waiting for the value to be read bit serially from the
+ EEPROM.
+ */
+ outl(0, ioaddr + CSR9); /* Reset the pointer with a dummy write. */
+ for (i = 0; i < 6; i++) {
+ int value, boguscnt = 100000;
+ do
+ value = inl(ioaddr + CSR9);
+ while (value < 0 && --boguscnt > 0);
+ printk(" %2.2x", dev->dev_addr[i] = value);
+ }
+ printk(", IRQ %d\n", irq);
+
+ /* We do a request_region() only to register /proc/ioports info. */
+ request_region(ioaddr, TULIP_TOTAL_SIZE, "DEC Tulip Ethernet");
+
+ dev->base_addr = ioaddr;
+ dev->irq = irq;
+
+ /* Make certain the data structures are quadword aligned. */
+ tp = kmalloc(sizeof(*tp), GFP_KERNEL | GFP_DMA);
+ dev->priv = tp;
+ tp->rx_buffs = kmalloc(PKT_BUF_SZ*RX_RING_SIZE, GFP_KERNEL | GFP_DMA);
+
+ /* The Tulip-specific entries in the device structure. */
+ dev->open = &tulip_open;
+ dev->hard_start_xmit = &tulip_start_xmit;
+ dev->stop = &tulip_close;
+ dev->get_stats = &tulip_get_stats;
+ dev->set_multicast_list = &set_multicast_list;
+ dev->set_mac_address = &set_mac_address;
+
+ return;
+}
+
+
+static int
+tulip_open(struct device *dev)
+{
+ struct tulip_private *tp = (struct tulip_private *)dev->priv;
+ int ioaddr = dev->base_addr;
+
+ /* Reset the chip, holding bit 0 set at least 10 PCI cycles. */
+ outl(0xfff80001, ioaddr + CSR0);
+ SLOW_DOWN_IO;
+ /* Deassert reset. Set 8 longword cache alignment, 8 longword burst.
+ Cache alignment bits 15:14 Burst length 13:8
+ 0000 No alignment 0x00000000 unlimited 0800 8 longwords
+ 4000 8 longwords 0100 1 longword 1000 16 longwords
+ 8000 16 longwords 0200 2 longwords 2000 32 longwords
+ C000 32 longwords 0400 4 longwords
+ Wait the specified 50 PCI cycles after a reset by initializing
+ Tx and Rx queues and the address filter list. */
+ outl(0xfff84800, ioaddr + CSR0);
+
+ if (irq2dev_map[dev->irq] != NULL
+ || (irq2dev_map[dev->irq] = dev) == NULL
+ || dev->irq == 0
+ || request_irq(dev->irq, &tulip_interrupt, 0, "DEC 21040 Tulip")) {
+ return -EAGAIN;
+ }
+
+ if (tulip_debug > 1)
+ printk("%s: tulip_open() irq %d.\n", dev->name, dev->irq);
+
+ tulip_init_ring(dev);
+
+ /* Fill the whole address filter table with our physical address. */
+ {
+ unsigned short *eaddrs = (unsigned short *)dev->dev_addr;
+ int *setup_frm = tp->setup_frame, i;
+
+ /* You must add the broadcast address when doing perfect filtering! */
+ *setup_frm++ = 0xffff;
+ *setup_frm++ = 0xffff;
+ *setup_frm++ = 0xffff;
+ /* Fill the rest of the accept table with our physical address. */
+ for (i = 1; i < 16; i++) {
+ *setup_frm++ = eaddrs[0];
+ *setup_frm++ = eaddrs[1];
+ *setup_frm++ = eaddrs[2];
+ }
+ /* Put the setup frame on the Tx list. */
+ tp->tx_ring[0].length = 0x08000000 | 192;
+ tp->tx_ring[0].buffer1 = (char *)tp->setup_frame;
+ tp->tx_ring[0].buffer2 = 0;
+ tp->tx_ring[0].status = 0x80000000;
+
+ tp->cur_tx++, tp->dirty_tx++;
+ }
+
+ outl((int)tp->rx_ring, ioaddr + CSR3);
+ outl((int)tp->tx_ring, ioaddr + CSR4);
+
+ /* Turn on the xcvr interface. */
+ outl(0x00000000, ioaddr + CSR13);
+ outl(0x00000004, ioaddr + CSR13);
+
+ /* Start the chip's Tx and Rx processes. */
+ outl(0xfffe2002, ioaddr + CSR6);
+
+ /* Trigger an immediate transmit demand to process the setup frame. */
+ outl(0, ioaddr + CSR1);
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+
+ /* Enable interrupts by setting the interrupt mask. */
+ outl(0xFFFFFFFF, ioaddr + CSR7);
+
+ if (tulip_debug > 2) {
+ printk("%s: Done tulip_open(), CSR0 %8.8x, CSR13 %8.8x.\n",
+ dev->name, inl(ioaddr + CSR0), inl(ioaddr + CSR13));
+ }
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
+static void
+tulip_init_ring(struct device *dev)
+{
+ struct tulip_private *tp = (struct tulip_private *)dev->priv;
+ int i;
+
+ tp->tx_full = 0;
+ tp->cur_rx = tp->cur_tx = 0;
+ tp->dirty_rx = tp->dirty_tx = 0;
+
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ tp->rx_ring[i].status = 0x80000000; /* Owned by Tulip chip */
+ tp->rx_ring[i].length = PKT_BUF_SZ;
+ tp->rx_ring[i].buffer1 = (char *)(tp->rx_buffs + i*PKT_BUF_SZ);
+ tp->rx_ring[i].buffer2 = (char *)&tp->rx_ring[i+1];
+ }
+ /* Mark the last entry as wrapping the ring. */
+ tp->rx_ring[i-1].length = PKT_BUF_SZ | 0x02000000;
+ tp->rx_ring[i-1].buffer2 = (char *)&tp->rx_ring[0];
+
+ /* The Tx buffer descriptor is filled in as needed, but we
+ do need to clear the ownership bit. */
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ tp->tx_ring[i].status = 0x00000000;
+ }
+}
+
+static int
+tulip_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+ struct tulip_private *tp = (struct tulip_private *)dev->priv;
+ int ioaddr = dev->base_addr;
+ int entry;
+
+ /* Transmitter timeout, serious problems. */
+ if (dev->tbusy) {
+ int tickssofar = jiffies - dev->trans_start;
+ int i;
+ if (tickssofar < 20)
+ return 1;
+ printk("%s: transmit timed out, status %8.8x, SIA %8.8x %8.8x %8.8x %8.8x, resetting...\n",
+ dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR12),
+ inl(ioaddr + CSR13), inl(ioaddr + CSR14), inl(ioaddr + CSR15));
+ printk(" Rx ring %8.8x: ", (int)tp->rx_ring);
+ for (i = 0; i < RX_RING_SIZE; i++)
+ printk(" %8.8x", (unsigned int)tp->rx_ring[i].status);
+ printk("\n Tx ring %8.8x: ", (int)tp->tx_ring);
+ for (i = 0; i < TX_RING_SIZE; i++)
+ printk(" %8.8x", (unsigned int)tp->tx_ring[i].status);
+ printk("\n");
+
+ tp->stats.tx_errors++;
+ /* We should reinitialize the hardware here. */
+ dev->tbusy=0;
+ dev->trans_start = jiffies;
+ return 0;
+ }
+
+ if (skb == NULL || skb->len <= 0) {
+ printk("%s: Obsolete driver layer request made: skbuff==NULL.\n",
+ dev->name);
+ dev_tint(dev);
+ return 0;
+ }
+
+ /* Block a timer-based transmit from overlapping. This could better be
+ done with atomic_swap(1, dev->tbusy), but set_bit() works as well.
+ If this ever occurs the queue layer is doing something evil! */
+ if (set_bit(0, (void*)&dev->tbusy) != 0) {
+ printk("%s: Transmitter access conflict.\n", dev->name);
+ return 1;
+ }
+
+ /* Caution: the write order is important here, set the base address
+ with the "ownership" bits last. */
+
+ /* Calculate the next Tx descriptor entry. */
+ entry = tp->cur_tx % TX_RING_SIZE;
+
+ tp->tx_full = 1;
+ tp->tx_skbuff[entry] = skb;
+ tp->tx_ring[entry].length = skb->len |
+ (entry == TX_RING_SIZE-1 ? 0xe2000000 : 0xe0000000);
+ tp->tx_ring[entry].buffer1 = skb->data;
+ tp->tx_ring[entry].buffer2 = 0;
+ tp->tx_ring[entry].status = 0x80000000; /* Pass ownership to the chip. */
+
+ tp->cur_tx++;
+
+ /* Trigger an immediate transmit demand. */
+ outl(0, ioaddr + CSR1);
+
+ dev->trans_start = jiffies;
+
+ return 0;
+}
+
+/* The interrupt handler does all of the Rx thread work and cleans up
+ after the Tx thread. */
+static void tulip_interrupt(int irq, struct pt_regs *regs)
+{
+ struct device *dev = (struct device *)(irq2dev_map[irq]);
+ struct tulip_private *lp;
+ int csr5, ioaddr, boguscnt=10;
+
+ if (dev == NULL) {
+ printk ("tulip_interrupt(): irq %d for unknown device.\n", irq);
+ return;
+ }
+
+ ioaddr = dev->base_addr;
+ lp = (struct tulip_private *)dev->priv;
+ if (dev->interrupt)
+ printk("%s: Re-entering the interrupt handler.\n", dev->name);
+
+ dev->interrupt = 1;
+
+ do {
+ csr5 = inl(ioaddr + CSR5);
+ /* Acknowledge all of the current interrupt sources ASAP. */
+ outl(csr5 & 0x0001ffff, ioaddr + CSR5);
+
+ if (tulip_debug > 4)
+ printk("%s: interrupt csr5=%#8.8x new csr5=%#8.8x.\n",
+ dev->name, csr5, inl(dev->base_addr + CSR5));
+
+ if ((csr5 & 0x00018000) == 0)
+ break;
+
+ if (csr5 & 0x0040) /* Rx interrupt */
+ tulip_rx(dev);
+
+ if (csr5 & 0x0001) { /* Tx-done interrupt */
+ int dirty_tx = lp->dirty_tx;
+
+ while (dirty_tx < lp->cur_tx) {
+ int entry = dirty_tx % TX_RING_SIZE;
+ int status = lp->tx_ring[entry].status;
+
+ if (status < 0)
+ break; /* It still hasn't been Txed */
+
+ if (status & 0x8000) {
+ /* There was an major error, log it. */
+ lp->stats.tx_errors++;
+ if (status & 0x4104) lp->stats.tx_aborted_errors++;
+ if (status & 0x0C00) lp->stats.tx_carrier_errors++;
+ if (status & 0x0200) lp->stats.tx_window_errors++;
+ if (status & 0x0002) lp->stats.tx_fifo_errors++;
+ if (status & 0x0080) lp->stats.tx_heartbeat_errors++;
+#ifdef ETHER_STATS
+ if (status & 0x0100) lp->stats.collisions16++;
+#endif
+ } else {
+#ifdef ETHER_STATS
+ if (status & 0x0001) lp->stats.tx_deferred++;
+#endif
+ lp->stats.collisions += (status >> 3) & 15;
+ lp->stats.tx_packets++;
+ }
+
+ /* Free the original skb. */
+ dev_kfree_skb(lp->tx_skbuff[entry], FREE_WRITE);
+ dirty_tx++;
+ }
+
+#ifndef final_version
+ if (lp->cur_tx - dirty_tx >= TX_RING_SIZE) {
+ printk("out-of-sync dirty pointer, %d vs. %d, full=%d.\n",
+ dirty_tx, lp->cur_tx, lp->tx_full);
+ dirty_tx += TX_RING_SIZE;
+ }
+#endif
+
+ if (lp->tx_full && dev->tbusy
+ && dirty_tx > lp->cur_tx - TX_RING_SIZE + 2) {
+ /* The ring is no longer full, clear tbusy. */
+ lp->tx_full = 0;
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+ }
+
+ lp->dirty_tx = dirty_tx;
+ }
+
+ /* Log errors. */
+ if (csr5 & 0x8000) { /* Abnormal error summary bit. */
+ if (csr5 & 0x0008) lp->stats.tx_errors++; /* Tx babble. */
+ if (csr5 & 0x0100) { /* Missed a Rx frame. */
+ lp->stats.rx_errors++;
+ lp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
+ }
+ if (csr5 & 0x0800) {
+ printk("%s: Something Wicked happened! %8.8x.\n",
+ dev->name, csr5);
+ /* Hmmmmm, it's not clear what to do here. */
+ }
+ }
+ if (--boguscnt < 0) {
+ printk("%s: Too much work at interrupt, csr5=0x%8.8x.\n",
+ dev->name, csr5);
+ /* Clear all interrupt sources. */
+ outl(0x0001ffff, ioaddr + CSR5);
+ break;
+ }
+ } while (1);
+
+ if (tulip_debug > 3)
+ printk("%s: exiting interrupt, csr5=%#4.4x.\n",
+ dev->name, inl(ioaddr + CSR5));
+
+ /* Special code for testing *only*. */
+ {
+ static int stopit = 10;
+ if (dev->start == 0 && --stopit < 0) {
+ printk("%s: Emergency stop, looping startup interrupt.\n",
+ dev->name);
+ free_irq(irq);
+ }
+ }
+
+ dev->interrupt = 0;
+ return;
+}
+
+static int
+tulip_rx(struct device *dev)
+{
+ struct tulip_private *lp = (struct tulip_private *)dev->priv;
+ int entry = lp->cur_rx % RX_RING_SIZE;
+ int i;
+
+ if (tulip_debug > 4)
+ printk(" In tulip_rx().\n");
+ /* If we own the next entry, it's a new packet. Send it up. */
+ while (lp->rx_ring[entry].status >= 0) {
+ int status = lp->rx_ring[entry].status;
+
+ if (tulip_debug > 4)
+ printk(" tulip_rx() status was %8.8x.\n", status);
+ if ((status & 0x0300) != 0x0300) {
+ printk("%s: Ethernet frame spanned multiple buffers, status %8.8x!\n",
+ dev->name, status);
+ } else if (status & 0x8000) {
+ /* There was a fatal error. */
+ lp->stats.rx_errors++; /* end of a packet.*/
+ if (status & 0x0890) lp->stats.rx_length_errors++;
+ if (status & 0x0004) lp->stats.rx_frame_errors++;
+ if (status & 0x0002) lp->stats.rx_crc_errors++;
+ if (status & 0x0001) lp->stats.rx_fifo_errors++;
+ } else {
+ /* Malloc up new buffer, compatible with net-2e. */
+ short pkt_len = lp->rx_ring[entry].status >> 16;
+ struct sk_buff *skb;
+
+ skb = dev_alloc_skb(pkt_len+2);
+ if (skb == NULL) {
+ printk("%s: Memory squeeze, deferring packet.\n", dev->name);
+ /* Check that at least two ring entries are free.
+ If not, free one and mark stats->rx_dropped++. */
+ for (i=0; i < RX_RING_SIZE; i++)
+ if (lp->rx_ring[(entry+i) % RX_RING_SIZE].status < 0)
+ break;
+
+ if (i > RX_RING_SIZE -2) {
+ lp->stats.rx_dropped++;
+ lp->rx_ring[entry].status = 0x80000000;
+ lp->cur_rx++;
+ }
+ break;
+ }
+ skb->dev = dev;
+ skb_reserve(skb,2); /* 16 byte align the data fields */
+ memcpy(skb_put(skb,pkt_len), lp->rx_ring[entry].buffer1, pkt_len);
+ skb->protocol=eth_type_trans(skb,dev);
+ netif_rx(skb);
+ lp->stats.rx_packets++;
+ }
+
+ lp->rx_ring[entry].status = 0x80000000;
+ entry = (++lp->cur_rx) % RX_RING_SIZE;
+ }
+
+ return 0;
+}
+
+static int
+tulip_close(struct device *dev)
+{
+ int ioaddr = dev->base_addr;
+ struct tulip_private *tp = (struct tulip_private *)dev->priv;
+
+ dev->start = 0;
+ dev->tbusy = 1;
+
+ if (tulip_debug > 1)
+ printk("%s: Shutting down ethercard, status was %2.2x.\n",
+ dev->name, inl(ioaddr + CSR5));
+
+ /* Disable interrupts by clearing the interrupt mask. */
+ outl(0x00000000, ioaddr + CSR7);
+ /* Stop the chip's Tx and Rx processes. */
+ outl(inl(ioaddr + CSR6) & ~0x2002, ioaddr + CSR6);
+
+ tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
+
+ free_irq(dev->irq);
+ irq2dev_map[dev->irq] = 0;
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static struct enet_statistics *
+tulip_get_stats(struct device *dev)
+{
+ struct tulip_private *tp = (struct tulip_private *)dev->priv;
+ short ioaddr = dev->base_addr;
+
+ tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
+
+ return &tp->stats;
+}
+
+/*
+ * Set or clear the multicast filter for this adaptor.
+ */
+
+static void set_multicast_list(struct device *dev)
+{
+ short ioaddr = dev->base_addr;
+ int csr6 = inl(ioaddr + CSR6) & ~0x00D5;
+
+ if (dev->flags&IFF_PROMISC)
+ { /* Set promiscuous. */
+ outl(csr6 | 0x00C0, ioaddr + CSR6);
+ /* Log any net taps. */
+ printk("%s: Promiscuous mode enabled.\n", dev->name);
+ }
+ else if (dev->mc_count > 15 || (dev->flags&IFF_ALLMULTI))
+ {
+ /* Too many to filter perfectly -- accept all multicasts. */
+ outl(csr6 | 0x0080, ioaddr + CSR6);
+ }
+ else
+ {
+ struct tulip_private *tp = (struct tulip_private *)dev->priv;
+ struct dev_mc_list *dmi=dev->mc_list;
+ int *setup_frm = tp->setup_frame;
+ unsigned short *eaddrs;
+ int i;
+
+ /* We have <= 15 addresses that we can use the wonderful
+ 16 address perfect filtering of the Tulip. Note that only
+ the low shortword of setup_frame[] is valid. */
+ outl(csr6 | 0x0000, ioaddr + CSR6);
+ i=0;
+ while(dmi)
+ {
+ eaddrs=(unsigned short *)dmi->dmi_addr;
+ dmi=dmi->next;
+ i++;
+ *setup_frm++ = *eaddrs++;
+ *setup_frm++ = *eaddrs++;
+ *setup_frm++ = *eaddrs++;
+ }
+ /* Fill the rest of the table with our physical address. */
+ eaddrs = (unsigned short *)dev->dev_addr;
+ do {
+ *setup_frm++ = eaddrs[0];
+ *setup_frm++ = eaddrs[1];
+ *setup_frm++ = eaddrs[2];
+ } while (++i < 16);
+
+ /* Now add this frame to the Tx list. */
+ }
+}
+
+static int
+set_mac_address(struct device *dev, void *addr)
+{
+ int i;
+ struct sockaddr *sa=(struct sockaddr *)addr;
+ if (dev->start)
+ return -EBUSY;
+ printk("%s: Setting MAC address to ", dev->name);
+ for (i = 0; i < 6; i++)
+ printk(" %2.2x", dev->dev_addr[i] = sa->sa_data[i]);
+ printk(".\n");
+ return 0;
+}
+
+#ifdef MODULE
+static char devicename[9] = { 0, };
+static struct device dev_tulip = {
+ devicename, /* device name is inserted by linux/drivers/net/net_init.c */
+ 0, 0, 0, 0,
+ 0, 0,
+ 0, 0, 0, NULL, tulip_probe
+};
+
+static int io = 0;
+static int irq = 0;
+
+int init_module(void)
+{
+ printk("tulip: Sorry, modularization is not completed\n");
+ return -EIO;
+#if 0
+ if (io == 0)
+ printk("tulip: You should not use auto-probing with insmod!\n");
+ dev_tulip.base_addr = io;
+ dev_tulip.irq = irq;
+ if (register_netdev(&dev_tulip) != 0) {
+ printk("tulip: register_netdev() returned non-zero.\n");
+ return -EIO;
+ }
+ return 0;
+#endif
+}
+
+void
+cleanup_module(void)
+{
+ unregister_netdev(&dev_tulip);
+}
+#endif /* MODULE */
+
+/*
+ * Local variables:
+ * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c tulip.c"
+ * c-indent-level: 4
+ * tab-width: 4
+ * End:
+ */
diff --git a/i386/i386at/gpl/linux/net/wavelan.c b/i386/i386at/gpl/linux/net/wavelan.c
new file mode 100644
index 00000000..4d09badd
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/wavelan.c
@@ -0,0 +1,2526 @@
+/*
+ * AT&T GIS (nee NCR) WaveLAN card:
+ * An Ethernet-like radio transceiver
+ * controlled by an Intel 82586 coprocessor.
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/stat.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/string.h>
+#include <linux/delay.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>
+#include <linux/timer.h>
+#include <linux/proc_fs.h>
+#define STRUCT_CHECK 1
+#ifdef MACH
+#include <net/i82586.h>
+#else
+#include "i82586.h"
+#endif
+#include "wavelan.h"
+
+#ifndef WAVELAN_DEBUG
+#define WAVELAN_DEBUG 0
+#endif /* WAVELAN_DEBUG */
+
+#define WATCHDOG_JIFFIES 512 /* TODO: express in HZ. */
+#define ENABLE_FULL_PROMISCUOUS 0x10000
+
+#define nels(a) (sizeof(a) / sizeof(a[0]))
+
+typedef struct device device;
+typedef struct enet_statistics en_stats;
+typedef struct net_local net_local;
+typedef struct timer_list timer_list;
+
+struct net_local
+{
+ en_stats stats;
+ unsigned int tx_n_in_use;
+ unsigned char nwid[2];
+ unsigned short hacr;
+ unsigned short rx_head;
+ unsigned short rx_last;
+ unsigned short tx_first_free;
+ unsigned short tx_first_in_use;
+ unsigned int nresets;
+ unsigned int correct_nwid;
+ unsigned int wrong_nwid;
+ unsigned int promiscuous;
+ unsigned int full_promiscuous;
+ timer_list watchdog;
+ device *dev;
+ net_local *prev;
+ net_local *next;
+};
+
+extern int wavelan_probe(device *); /* See Space.c */
+
+static const char *version = "wavelan.c:v7 95/4/8\n";
+
+/*
+ * Entry point forward declarations.
+ */
+static int wavelan_probe1(device *, unsigned short);
+static int wavelan_open(device *);
+static int wavelan_send_packet(struct sk_buff *, device *);
+static void wavelan_interrupt(int, struct pt_regs *);
+static int wavelan_close(device *);
+static en_stats *wavelan_get_stats(device *);
+static void wavelan_set_multicast_list(device *);
+static int wavelan_get_info(char*, char**, off_t, int, int);
+
+/*
+ * Other forward declarations.
+ */
+static void wavelan_cu_show_one(device *, net_local *, int, unsigned short);
+static void wavelan_cu_start(device *);
+static void wavelan_ru_start(device *);
+static void wavelan_watchdog(unsigned long);
+#if 0
+static void wavelan_psa_show(psa_t *);
+static void wavelan_mmc_show(unsigned short);
+#endif /* 0 */
+static void wavelan_scb_show(unsigned short);
+static void wavelan_ru_show(device *);
+static void wavelan_cu_show(device *);
+static void wavelan_dev_show(device *);
+static void wavelan_local_show(device *);
+
+static unsigned int wavelan_debug = WAVELAN_DEBUG;
+static net_local *first_wavelan = (net_local *)0;
+
+static
+unsigned long
+wavelan_splhi(void)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ return flags;
+}
+
+static
+void
+wavelan_splx(unsigned long flags)
+{
+ restore_flags(flags);
+}
+
+static
+unsigned short
+hasr_read(unsigned short ioaddr)
+{
+ return inw(HASR(ioaddr));
+}
+
+static
+void
+hacr_write(unsigned short ioaddr, int hacr)
+{
+ outw(hacr, HACR(ioaddr));
+}
+
+static
+void
+hacr_write_slow(unsigned short ioaddr, int hacr)
+{
+ hacr_write(ioaddr, hacr);
+ /* delay might only be needed sometimes */
+ udelay(1000);
+}
+
+/*
+ * Set the channel attention bit.
+ */
+static
+void
+set_chan_attn(unsigned short ioaddr, unsigned short current_hacr)
+{
+ hacr_write(ioaddr, current_hacr | HACR_CA);
+}
+
+/*
+ * Reset, and then set host adaptor into default mode.
+ */
+static
+void
+wavelan_reset(unsigned short ioaddr)
+{
+ hacr_write_slow(ioaddr, HACR_RESET);
+ hacr_write(ioaddr, HACR_DEFAULT);
+}
+
+static
+void
+wavelan_16_off(unsigned short ioaddr, unsigned short hacr)
+{
+ hacr &= ~HACR_16BITS;
+
+ hacr_write(ioaddr, hacr);
+}
+
+static
+void
+wavelan_16_on(unsigned short ioaddr, unsigned short hacr)
+{
+ hacr |= HACR_16BITS;
+
+ hacr_write(ioaddr, hacr);
+}
+
+static
+void
+wavelan_ints_off(device *dev)
+{
+ unsigned short ioaddr;
+ net_local *lp;
+ unsigned long x;
+
+ ioaddr = dev->base_addr;
+ lp = (net_local *)dev->priv;
+
+ x = wavelan_splhi();
+
+ lp->hacr &= ~HACR_INTRON;
+ hacr_write(ioaddr, lp->hacr);
+
+ wavelan_splx(x);
+}
+
+static
+void
+wavelan_ints_on(device *dev)
+{
+ unsigned short ioaddr;
+ net_local *lp;
+ unsigned long x;
+
+ ioaddr = dev->base_addr;
+ lp = (net_local *)dev->priv;
+
+ x = wavelan_splhi();
+
+ lp->hacr |= HACR_INTRON;
+ hacr_write(ioaddr, lp->hacr);
+
+ wavelan_splx(x);
+}
+
+/*
+ * Read bytes from the PSA.
+ */
+static
+void
+psa_read(unsigned short ioaddr, unsigned short hacr, int o, unsigned char *b, int n)
+{
+ wavelan_16_off(ioaddr, hacr);
+
+ while (n-- > 0)
+ {
+ outw(o, PIOR2(ioaddr));
+ o++;
+ *b++ = inb(PIOP2(ioaddr));
+ }
+
+ wavelan_16_on(ioaddr, hacr);
+}
+
+#if defined(IRQ_SET_WORKS)
+/*
+ * Write bytes to the PSA.
+ */
+static
+void
+psa_write(unsigned short ioaddr, unsigned short hacr, int o, unsigned char *b, int n)
+{
+ wavelan_16_off(ioaddr, hacr);
+
+ while (n-- > 0)
+ {
+ outw(o, PIOR2(ioaddr));
+ o++;
+ outb(*b, PIOP2(ioaddr));
+ b++;
+ }
+
+ wavelan_16_on(ioaddr, hacr);
+}
+#endif /* defined(IRQ_SET_WORKS) */
+
+/*
+ * Read bytes from the on-board RAM.
+ */
+static
+void
+obram_read(unsigned short ioaddr, unsigned short o, unsigned char *b, int n)
+{
+ n = (n + 1) / (sizeof(unsigned short) / sizeof(unsigned char));
+
+ outw(o, PIOR1(ioaddr));
+
+ insw(PIOP1(ioaddr), (unsigned short *)b, n);
+}
+
+/*
+ * Write bytes to the on-board RAM.
+ */
+static
+void
+obram_write(unsigned short ioaddr, unsigned short o, unsigned char *b, int n)
+{
+ n = (n + 1) / (sizeof(unsigned short) / sizeof(unsigned char));
+
+ outw(o, PIOR1(ioaddr));
+
+ outsw(PIOP1(ioaddr), (unsigned short *)b, n);
+}
+
+/*
+ * Read bytes from the MMC.
+ */
+static
+void
+mmc_read(unsigned short ioaddr, unsigned short o, unsigned char *b, int n)
+{
+ while (n-- > 0)
+ {
+ while (inw(HASR(ioaddr)) & HASR_MMC_BUSY)
+ ;
+
+ outw(o << 1, MMCR(ioaddr));
+ o++;
+
+ while (inw(HASR(ioaddr)) & HASR_MMC_BUSY)
+ ;
+
+ *b++ = (unsigned char)(inw(MMCR(ioaddr)) >> 8);
+ }
+}
+
+/*
+ * Write bytes to the MMC.
+ */
+static
+void
+mmc_write(unsigned short ioaddr, unsigned short o, unsigned char *b, int n)
+{
+ while (n-- > 0)
+ {
+ while (inw(HASR(ioaddr)) & HASR_MMC_BUSY)
+ ;
+
+ outw((unsigned short)(((unsigned short)*b << 8) | (o << 1) | 1), MMCR(ioaddr));
+ b++;
+ o++;
+ }
+}
+
+static int irqvals[] =
+{
+ 0, 0, 0, 0x01,
+ 0x02, 0x04, 0, 0x08,
+ 0, 0, 0x10, 0x20,
+ 0x40, 0, 0, 0x80,
+};
+
+#if defined(IRQ_SET_WORKS)
+static
+int
+wavelan_unmap_irq(int irq, unsigned char *irqval)
+{
+ if (irq < 0 || irq >= nels(irqvals) || irqvals[irq] == 0)
+ return -1;
+
+ *irqval = (unsigned char)irqvals[irq];
+
+ return 0;
+}
+#endif /* defined(IRQ_SET_WORKS) */
+
+/*
+ * Map values from the irq parameter register to irq numbers.
+ */
+static
+int
+wavelan_map_irq(unsigned char irqval)
+{
+ int irq;
+
+ for (irq = 0; irq < nels(irqvals); irq++)
+ {
+ if (irqvals[irq] == (int)irqval)
+ return irq;
+ }
+
+ return -1;
+}
+
+/*
+ * Initialize the Modem Management Controller.
+ */
+static
+void
+wavelan_mmc_init(device *dev, psa_t *psa)
+{
+ unsigned short ioaddr;
+ net_local *lp;
+ mmw_t m;
+ int configured;
+
+ ioaddr = dev->base_addr;
+ lp = (net_local *)dev->priv;
+ memset(&m, 0x00, sizeof(m));
+
+ /*
+ * configured = psa->psa_conf_status & 1;
+ *
+ * For now we use the persistent PSA
+ * information as little as possible, thereby
+ * allowing us to return to the same known state
+ * during a hardware reset.
+ */
+ configured = 0;
+
+ /*
+ * Set default modem control parameters.
+ * See NCR document 407-0024326 Rev. A.
+ */
+ m.mmw_jabber_enable = 0x01;
+ m.mmw_anten_sel = MMW_ANTEN_SEL_ALG_EN;
+ m.mmw_ifs = 0x20;
+ m.mmw_mod_delay = 0x04;
+ m.mmw_jam_time = 0x38;
+
+ m.mmw_encr_enable = 0;
+ m.mmw_des_io_invert = 0;
+ m.mmw_freeze = 0;
+ m.mmw_decay_prm = 0;
+ m.mmw_decay_updat_prm = 0;
+
+ if (configured)
+ {
+ /*
+ * Use configuration defaults from parameter storage area.
+ */
+ if (psa->psa_undefined & 1)
+ m.mmw_loopt_sel = 0x00;
+ else
+ m.mmw_loopt_sel = MMW_LOOPT_SEL_UNDEFINED;
+
+ m.mmw_thr_pre_set = psa->psa_thr_pre_set & 0x3F;
+ m.mmw_quality_thr = psa->psa_quality_thr & 0x0F;
+ }
+ else
+ {
+ if (lp->promiscuous && lp->full_promiscuous)
+ m.mmw_loopt_sel = MMW_LOOPT_SEL_UNDEFINED;
+ else
+ m.mmw_loopt_sel = 0x00;
+
+ /*
+ * 0x04 for AT,
+ * 0x01 for MCA.
+ */
+ if (psa->psa_comp_number & 1)
+ m.mmw_thr_pre_set = 0x01;
+ else
+ m.mmw_thr_pre_set = 0x04;
+
+ m.mmw_quality_thr = 0x03;
+ }
+
+ m.mmw_netw_id_l = lp->nwid[1];
+ m.mmw_netw_id_h = lp->nwid[0];
+
+ mmc_write(ioaddr, 0, (unsigned char *)&m, sizeof(m));
+}
+
+static
+void
+wavelan_ack(device *dev)
+{
+ unsigned short ioaddr;
+ net_local *lp;
+ unsigned short scb_cs;
+ int i;
+
+ ioaddr = dev->base_addr;
+ lp = (net_local *)dev->priv;
+
+ obram_read(ioaddr, scboff(OFFSET_SCB, scb_status), (unsigned char *)&scb_cs, sizeof(scb_cs));
+ scb_cs &= SCB_ST_INT;
+
+ if (scb_cs == 0)
+ return;
+
+ obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), (unsigned char *)&scb_cs, sizeof(scb_cs));
+
+ set_chan_attn(ioaddr, lp->hacr);
+
+ for (i = 1000; i > 0; i--)
+ {
+ obram_read(ioaddr, scboff(OFFSET_SCB, scb_command), (unsigned char *)&scb_cs, sizeof(scb_cs));
+ if (scb_cs == 0)
+ break;
+
+ udelay(1000);
+ }
+
+ if (i <= 0)
+ printk("%s: wavelan_ack(): board not accepting command.\n", dev->name);
+}
+
+/*
+ * Set channel attention bit and busy wait until command has
+ * completed, then acknowledge the command completion.
+ */
+static
+int
+wavelan_synchronous_cmd(device *dev, const char *str)
+{
+ unsigned short ioaddr;
+ net_local *lp;
+ unsigned short scb_cmd;
+ ach_t cb;
+ int i;
+
+ ioaddr = dev->base_addr;
+ lp = (net_local *)dev->priv;
+
+ scb_cmd = SCB_CMD_CUC & SCB_CMD_CUC_GO;
+ obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), (unsigned char *)&scb_cmd, sizeof(scb_cmd));
+
+ set_chan_attn(ioaddr, lp->hacr);
+
+ for (i = 64; i > 0; i--)
+ {
+ obram_read(ioaddr, OFFSET_CU, (unsigned char *)&cb, sizeof(cb));
+ if (cb.ac_status & AC_SFLD_C)
+ break;
+
+ udelay(1000);
+ }
+
+ if (i <= 0 || !(cb.ac_status & AC_SFLD_OK))
+ {
+ printk("%s: %s failed; status = 0x%x\n", dev->name, str, cb.ac_status);
+ wavelan_scb_show(ioaddr);
+ return -1;
+ }
+
+ wavelan_ack(dev);
+
+ return 0;
+}
+
+static
+int
+wavelan_hardware_reset(device *dev)
+{
+ unsigned short ioaddr;
+ psa_t psa;
+ net_local *lp;
+ scp_t scp;
+ iscp_t iscp;
+ scb_t scb;
+ ach_t cb;
+ int i;
+ ac_cfg_t cfg;
+ ac_ias_t ias;
+
+ if (wavelan_debug > 0)
+ printk("%s: ->wavelan_hardware_reset(dev=0x%x)\n", dev->name, (unsigned int)dev);
+
+ ioaddr = dev->base_addr;
+ lp = (net_local *)dev->priv;
+
+ lp->nresets++;
+
+ wavelan_reset(ioaddr);
+ lp->hacr = HACR_DEFAULT;
+
+ /*
+ * Clear the onboard RAM.
+ */
+ {
+ unsigned char zeroes[512];
+
+ memset(&zeroes[0], 0x00, sizeof(zeroes));
+
+ for (i = 0; i < I82586_MEMZ; i += sizeof(zeroes))
+ obram_write(ioaddr, i, &zeroes[0], sizeof(zeroes));
+ }
+
+ psa_read(ioaddr, lp->hacr, 0, (unsigned char *)&psa, sizeof(psa));
+
+ wavelan_mmc_init(dev, &psa);
+
+ /*
+ * Construct the command unit structures:
+ * scp, iscp, scb, cb.
+ */
+ memset(&scp, 0x00, sizeof(scp));
+ scp.scp_sysbus = SCP_SY_16BBUS;
+ scp.scp_iscpl = OFFSET_ISCP;
+ obram_write(ioaddr, OFFSET_SCP, (unsigned char *)&scp, sizeof(scp));
+
+ memset(&iscp, 0x00, sizeof(iscp));
+ iscp.iscp_busy = 1;
+ iscp.iscp_offset = OFFSET_SCB;
+ obram_write(ioaddr, OFFSET_ISCP, (unsigned char *)&iscp, sizeof(iscp));
+
+ memset(&scb, 0x00, sizeof(scb));
+ scb.scb_command = SCB_CMD_RESET;
+ scb.scb_cbl_offset = OFFSET_CU;
+ scb.scb_rfa_offset = OFFSET_RU;
+ obram_write(ioaddr, OFFSET_SCB, (unsigned char *)&scb, sizeof(scb));
+
+ set_chan_attn(ioaddr, lp->hacr);
+
+ for (i = 1000; i > 0; i--)
+ {
+ obram_read(ioaddr, OFFSET_ISCP, (unsigned char *)&iscp, sizeof(iscp));
+
+ if (iscp.iscp_busy == (unsigned short)0)
+ break;
+
+ udelay(1000);
+ }
+
+ if (i <= 0)
+ {
+ printk("%s: wavelan_hardware_reset(): iscp_busy timeout.\n", dev->name);
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_hardware_reset(): -1\n", dev->name);
+ return -1;
+ }
+
+ for (i = 15; i > 0; i--)
+ {
+ obram_read(ioaddr, OFFSET_SCB, (unsigned char *)&scb, sizeof(scb));
+
+ if (scb.scb_status == (SCB_ST_CX | SCB_ST_CNA))
+ break;
+
+ udelay(1000);
+ }
+
+ if (i <= 0)
+ {
+ printk("%s: wavelan_hardware_reset(): status: expected 0x%02x, got 0x%02x.\n", dev->name, SCB_ST_CX | SCB_ST_CNA, scb.scb_status);
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_hardware_reset(): -1\n", dev->name);
+ return -1;
+ }
+
+ wavelan_ack(dev);
+
+ memset(&cb, 0x00, sizeof(cb));
+ cb.ac_command = AC_CFLD_EL | (AC_CFLD_CMD & acmd_diagnose);
+ cb.ac_link = OFFSET_CU;
+ obram_write(ioaddr, OFFSET_CU, (unsigned char *)&cb, sizeof(cb));
+
+ if (wavelan_synchronous_cmd(dev, "diag()") == -1)
+ {
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_hardware_reset(): -1\n", dev->name);
+ return -1;
+ }
+
+ obram_read(ioaddr, OFFSET_CU, (unsigned char *)&cb, sizeof(cb));
+ if (cb.ac_status & AC_SFLD_FAIL)
+ {
+ printk("%s: wavelan_hardware_reset(): i82586 Self Test failed.\n", dev->name);
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_hardware_reset(): -1\n", dev->name);
+ return -1;
+ }
+
+ memset(&cfg, 0x00, sizeof(cfg));
+
+#if 0
+ /*
+ * The default board configuration.
+ */
+ cfg.fifolim_bytecnt = 0x080c;
+ cfg.addrlen_mode = 0x2600;
+ cfg.linprio_interframe = 0x7820; /* IFS=120, ACS=2 */
+ cfg.slot_time = 0xf00c; /* slottime=12 */
+ cfg.hardware = 0x0008; /* tx even w/o CD */
+ cfg.min_frame_len = 0x0040;
+#endif /* 0 */
+
+ /*
+ * For Linux we invert AC_CFG_ALOC(..) so as to conform
+ * to the way that net packets reach us from above.
+ * (See also ac_tx_t.)
+ */
+ cfg.cfg_byte_cnt = AC_CFG_BYTE_CNT(sizeof(ac_cfg_t) - sizeof(ach_t));
+ cfg.cfg_fifolim = AC_CFG_FIFOLIM(8);
+ cfg.cfg_byte8 = AC_CFG_SAV_BF(0) |
+ AC_CFG_SRDY(0);
+ cfg.cfg_byte9 = AC_CFG_ELPBCK(0) |
+ AC_CFG_ILPBCK(0) |
+ AC_CFG_PRELEN(AC_CFG_PLEN_2) |
+ AC_CFG_ALOC(1) |
+ AC_CFG_ADDRLEN(WAVELAN_ADDR_SIZE);
+ cfg.cfg_byte10 = AC_CFG_BOFMET(0) |
+ AC_CFG_ACR(0) |
+ AC_CFG_LINPRIO(0);
+ cfg.cfg_ifs = 32;
+ cfg.cfg_slotl = 0;
+ cfg.cfg_byte13 = AC_CFG_RETRYNUM(15) |
+ AC_CFG_SLTTMHI(2);
+ cfg.cfg_byte14 = AC_CFG_FLGPAD(0) |
+ AC_CFG_BTSTF(0) |
+ AC_CFG_CRC16(0) |
+ AC_CFG_NCRC(0) |
+ AC_CFG_TNCRS(1) |
+ AC_CFG_MANCH(0) |
+ AC_CFG_BCDIS(0) |
+ AC_CFG_PRM(lp->promiscuous);
+ cfg.cfg_byte15 = AC_CFG_ICDS(0) |
+ AC_CFG_CDTF(0) |
+ AC_CFG_ICSS(0) |
+ AC_CFG_CSTF(0);
+/*
+ cfg.cfg_min_frm_len = AC_CFG_MNFRM(64);
+*/
+ cfg.cfg_min_frm_len = AC_CFG_MNFRM(8);
+
+ cfg.cfg_h.ac_command = AC_CFLD_EL | (AC_CFLD_CMD & acmd_configure);
+ cfg.cfg_h.ac_link = OFFSET_CU;
+ obram_write(ioaddr, OFFSET_CU, (unsigned char *)&cfg, sizeof(cfg));
+
+ if (wavelan_synchronous_cmd(dev, "reset()-configure") == -1)
+ {
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_hardware_reset(): -1\n", dev->name);
+
+ return -1;
+ }
+
+ memset(&ias, 0x00, sizeof(ias));
+ ias.ias_h.ac_command = AC_CFLD_EL | (AC_CFLD_CMD & acmd_ia_setup);
+ ias.ias_h.ac_link = OFFSET_CU;
+ memcpy(&ias.ias_addr[0], (unsigned char *)&dev->dev_addr[0], sizeof(ias.ias_addr));
+ obram_write(ioaddr, OFFSET_CU, (unsigned char *)&ias, sizeof(ias));
+
+ if (wavelan_synchronous_cmd(dev, "reset()-address") == -1)
+ {
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_hardware_reset(): -1\n", dev->name);
+
+ return -1;
+ }
+
+ wavelan_ints_on(dev);
+
+ if (wavelan_debug > 4)
+ wavelan_scb_show(ioaddr);
+
+ wavelan_ru_start(dev);
+ wavelan_cu_start(dev);
+
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_hardware_reset(): 0\n", dev->name);
+
+ return 0;
+}
+
+#if STRUCT_CHECK == 1
+
+static
+const char *
+wavelan_struct_check(void)
+{
+#define SC(t,s,n) if (sizeof(t) != s) return n
+ SC(psa_t, PSA_SIZE, "psa_t");
+ SC(mmw_t, MMW_SIZE, "mmw_t");
+ SC(mmr_t, MMR_SIZE, "mmr_t");
+ SC(ha_t, HA_SIZE, "ha_t");
+#undef SC
+
+ return (char *)0;
+}
+
+#endif /* STRUCT_CHECK == 1 */
+
+/*
+ * Check for a network adaptor of this type.
+ * Return '0' iff one exists.
+ * (There seem to be different interpretations of
+ * the initial value of dev->base_addr.
+ * We follow the example in drivers/net/ne.c.)
+ */
+int
+wavelan_probe(device *dev)
+{
+ int i;
+ int r;
+ short base_addr;
+ static unsigned short iobase[] =
+ {
+#if 0
+ Leave out 0x3C0 for now -- seems to clash
+ with some video controllers.
+ Leave out the others too -- we will always
+ use 0x390 and leave 0x300 for the Ethernet device.
+ 0x300, 0x390, 0x3E0, 0x3C0,
+#endif /* 0 */
+ 0x390,
+ };
+
+ if (wavelan_debug > 0)
+ printk("%s: ->wavelan_probe(dev=0x%x (base_addr=0x%x))\n", dev->name, (unsigned int)dev, (unsigned int)dev->base_addr);
+
+#if STRUCT_CHECK == 1
+ if (wavelan_struct_check() != (char *)0)
+ {
+ printk("%s: structure/compiler botch: \"%s\"\n", dev->name, wavelan_struct_check());
+
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_probe(): ENODEV\n", dev->name);
+
+ return ENODEV;
+ }
+#endif /* STRUCT_CHECK == 1 */
+
+ base_addr = dev->base_addr;
+
+ if (base_addr < 0)
+ {
+ /*
+ * Don't probe at all.
+ */
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_probe(): ENXIO\n", dev->name);
+ return ENXIO;
+ }
+
+ if (base_addr > 0x100)
+ {
+ /*
+ * Check a single specified location.
+ */
+ r = wavelan_probe1(dev, base_addr);
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_probe(): %d\n", dev->name, r);
+ return r;
+ }
+
+ for (i = 0; i < nels(iobase); i++)
+ {
+ if (check_region(iobase[i], sizeof(ha_t)))
+ continue;
+
+ if (wavelan_probe1(dev, iobase[i]) == 0)
+ {
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_probe(): 0\n", dev->name);
+ proc_net_register(&(struct proc_dir_entry) {
+ PROC_NET_WAVELAN, 7, "wavelan",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ wavelan_get_info
+ });
+
+ return 0;
+ }
+ }
+
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_probe(): ENODEV\n", dev->name);
+
+ return ENODEV;
+}
+
+static
+int
+wavelan_probe1(device *dev, unsigned short ioaddr)
+{
+ psa_t psa;
+ int irq;
+ int i;
+ net_local *lp;
+ int enable_full_promiscuous;
+
+ if (wavelan_debug > 0)
+ printk("%s: ->wavelan_probe1(dev=0x%x, ioaddr=0x%x)\n", dev->name, (unsigned int)dev, ioaddr);
+
+ wavelan_reset(ioaddr);
+
+ psa_read(ioaddr, HACR_DEFAULT, 0, (unsigned char *)&psa, sizeof(psa));
+
+ /*
+ * Check the first three octets of the MAC address
+ * for the manufacturer's code.
+ */
+ if
+ (
+ psa.psa_univ_mac_addr[0] != SA_ADDR0
+ ||
+ psa.psa_univ_mac_addr[1] != SA_ADDR1
+ ||
+ psa.psa_univ_mac_addr[2] != SA_ADDR2
+ )
+ {
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_probe1(): ENODEV\n", dev->name);
+ return ENODEV;
+ }
+
+ printk("%s: WaveLAN at %#x,", dev->name, ioaddr);
+
+ if (dev->irq != 0)
+ {
+ printk("[WARNING: explicit IRQ value %d ignored: using PSA value instead]", dev->irq);
+#if defined(IRQ_SET_WORKS)
+Leave this out until I can get it to work -- BJ.
+ if (wavelan_unmap_irq(dev->irq, &psa.psa_int_req_no) == -1)
+ {
+ printk(" could not wavelan_unmap_irq(%d, ..) -- ignored.\n", dev->irq);
+ dev->irq = 0;
+ }
+ else
+ {
+ psa_write(ioaddr, HACR_DEFAULT, (char *)&psa.psa_int_req_no - (char *)&psa, (unsigned char *)&psa.psa_int_req_no, sizeof(psa.psa_int_req_no));
+ wavelan_reset(ioaddr);
+ }
+#endif /* defined(IRQ_SET_WORKS) */
+ }
+
+ if ((irq = wavelan_map_irq(psa.psa_int_req_no)) == -1)
+ {
+ printk(" could not wavelan_map_irq(%d).\n", psa.psa_int_req_no);
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_probe1(): EAGAIN\n", dev->name);
+ return EAGAIN;
+ }
+
+ dev->irq = irq;
+
+ request_region(ioaddr, sizeof(ha_t), "wavelan");
+ dev->base_addr = ioaddr;
+
+ /*
+ * The third numeric argument to LILO's
+ * `ether=' control line arrives here as `dev->mem_start'.
+ *
+ * If bit 16 of dev->mem_start is non-zero we enable
+ * full promiscuity.
+ *
+ * If either of the least significant two bytes of
+ * dev->mem_start are non-zero we use them instead
+ * of the PSA NWID.
+ */
+ enable_full_promiscuous = (dev->mem_start & ENABLE_FULL_PROMISCUOUS) == ENABLE_FULL_PROMISCUOUS;
+ dev->mem_start &= ~ENABLE_FULL_PROMISCUOUS;
+
+ if (dev->mem_start != 0)
+ {
+ psa.psa_nwid[0] = (dev->mem_start >> 8) & 0xFF;
+ psa.psa_nwid[1] = (dev->mem_start >> 0) & 0xFF;
+ }
+
+ dev->mem_start = 0x0000;
+ dev->mem_end = 0x0000;
+ dev->if_port = 0;
+
+ memcpy(&dev->dev_addr[0], &psa.psa_univ_mac_addr[0], WAVELAN_ADDR_SIZE);
+
+ for (i = 0; i < WAVELAN_ADDR_SIZE; i++)
+ printk("%s%02x", (i == 0) ? " " : ":", dev->dev_addr[i]);
+
+ printk(", IRQ %d", dev->irq);
+ if (enable_full_promiscuous)
+ printk(", promisc");
+ printk(", nwid 0x%02x%02x", psa.psa_nwid[0], psa.psa_nwid[1]);
+
+ printk(", PC");
+ switch (psa.psa_comp_number)
+ {
+ case PSA_COMP_PC_AT_915:
+ case PSA_COMP_PC_AT_2400:
+ printk("-AT");
+ break;
+
+ case PSA_COMP_PC_MC_915:
+ case PSA_COMP_PC_MC_2400:
+ printk("-MC");
+ break;
+
+ case PSA_COMP_PCMCIA_915:
+ printk("MCIA");
+ break;
+
+ default:
+ printk("???");
+ break;
+ }
+
+ printk(", ");
+ switch (psa.psa_subband)
+ {
+ case PSA_SUBBAND_915:
+ printk("915");
+ break;
+
+ case PSA_SUBBAND_2425:
+ printk("2425");
+ break;
+
+ case PSA_SUBBAND_2460:
+ printk("2460");
+ break;
+
+ case PSA_SUBBAND_2484:
+ printk("2484");
+ break;
+
+ case PSA_SUBBAND_2430_5:
+ printk("2430.5");
+ break;
+
+ default:
+ printk("???");
+ break;
+ }
+ printk(" MHz");
+
+ printk("\n");
+
+ if (wavelan_debug > 0)
+ printk(version);
+
+ dev->priv = kmalloc(sizeof(net_local), GFP_KERNEL);
+ if (dev->priv == NULL)
+ return -ENOMEM;
+ memset(dev->priv, 0x00, sizeof(net_local));
+ lp = (net_local *)dev->priv;
+
+ if (first_wavelan == (net_local *)0)
+ {
+ first_wavelan = lp;
+ lp->prev = lp;
+ lp->next = lp;
+ }
+ else
+ {
+ lp->prev = first_wavelan->prev;
+ lp->next = first_wavelan;
+ first_wavelan->prev->next = lp;
+ first_wavelan->prev = lp;
+ }
+ lp->dev = dev;
+
+ lp->hacr = HACR_DEFAULT;
+
+ lp->full_promiscuous = enable_full_promiscuous;
+ lp->nwid[0] = psa.psa_nwid[0];
+ lp->nwid[1] = psa.psa_nwid[1];
+
+ lp->watchdog.function = wavelan_watchdog;
+ lp->watchdog.data = (unsigned long)dev;
+
+ dev->open = wavelan_open;
+ dev->stop = wavelan_close;
+ dev->hard_start_xmit = wavelan_send_packet;
+ dev->get_stats = wavelan_get_stats;
+ dev->set_multicast_list = &wavelan_set_multicast_list;
+
+ /*
+ * Fill in the fields of the device structure
+ * with ethernet-generic values.
+ */
+ ether_setup(dev);
+
+ dev->flags &= ~IFF_MULTICAST; /* Not yet supported */
+
+ dev->mtu = WAVELAN_MTU;
+
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_probe1(): 0\n", dev->name);
+
+ return 0;
+}
+
+/*
+ * Construct the fd and rbd structures.
+ * Start the receive unit.
+ */
+static
+void
+wavelan_ru_start(device *dev)
+{
+ unsigned short ioaddr;
+ net_local *lp;
+ unsigned short scb_cs;
+ fd_t fd;
+ rbd_t rbd;
+ unsigned short rx;
+ unsigned short rx_next;
+ int i;
+
+ ioaddr = dev->base_addr;
+ lp = (net_local *)dev->priv;
+
+ obram_read(ioaddr, scboff(OFFSET_SCB, scb_status), (unsigned char *)&scb_cs, sizeof(scb_cs));
+ if ((scb_cs & SCB_ST_RUS) == SCB_ST_RUS_RDY)
+ return;
+
+ lp->rx_head = OFFSET_RU;
+
+ for (i = 0, rx = lp->rx_head; i < NRXBLOCKS; i++, rx = rx_next)
+ {
+ rx_next = (i == NRXBLOCKS - 1) ? lp->rx_head : rx + RXBLOCKZ;
+
+ fd.fd_status = 0;
+ fd.fd_command = (i == NRXBLOCKS - 1) ? FD_COMMAND_EL : 0;
+ fd.fd_link_offset = rx_next;
+ fd.fd_rbd_offset = rx + sizeof(fd);
+ obram_write(ioaddr, rx, (unsigned char *)&fd, sizeof(fd));
+
+ rbd.rbd_status = 0;
+ rbd.rbd_next_rbd_offset = I82586NULL;
+ rbd.rbd_bufl = rx + sizeof(fd) + sizeof(rbd);
+ rbd.rbd_bufh = 0;
+ rbd.rbd_el_size = RBD_EL | (RBD_SIZE & MAXDATAZ);
+ obram_write(ioaddr, rx + sizeof(fd), (unsigned char *)&rbd, sizeof(rbd));
+
+ lp->rx_last = rx;
+ }
+
+ obram_write(ioaddr, scboff(OFFSET_SCB, scb_rfa_offset), (unsigned char *)&lp->rx_head, sizeof(lp->rx_head));
+
+ scb_cs = SCB_CMD_RUC_GO;
+ obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), (unsigned char *)&scb_cs, sizeof(scb_cs));
+
+ set_chan_attn(ioaddr, lp->hacr);
+
+ for (i = 1000; i > 0; i--)
+ {
+ obram_read(ioaddr, scboff(OFFSET_SCB, scb_command), (unsigned char *)&scb_cs, sizeof(scb_cs));
+ if (scb_cs == 0)
+ break;
+
+ udelay(1000);
+ }
+
+ if (i <= 0)
+ printk("%s: wavelan_ru_start(): board not accepting command.\n", dev->name);
+}
+
+/*
+ * Initialise the transmit blocks.
+ * Start the command unit executing the NOP
+ * self-loop of the first transmit block.
+ */
+static
+void
+wavelan_cu_start(device *dev)
+{
+ unsigned short ioaddr;
+ net_local *lp;
+ int i;
+ unsigned short txblock;
+ unsigned short first_nop;
+ unsigned short scb_cs;
+
+ ioaddr = dev->base_addr;
+ lp = (net_local *)dev->priv;
+
+ lp->tx_first_free = OFFSET_CU;
+ lp->tx_first_in_use = I82586NULL;
+
+ for
+ (
+ i = 0, txblock = OFFSET_CU;
+ i < NTXBLOCKS;
+ i++, txblock += TXBLOCKZ
+ )
+ {
+ ac_tx_t tx;
+ ac_nop_t nop;
+ tbd_t tbd;
+ unsigned short tx_addr;
+ unsigned short nop_addr;
+ unsigned short tbd_addr;
+ unsigned short buf_addr;
+
+ tx_addr = txblock;
+ nop_addr = tx_addr + sizeof(tx);
+ tbd_addr = nop_addr + sizeof(nop);
+ buf_addr = tbd_addr + sizeof(tbd);
+
+ tx.tx_h.ac_status = 0;
+ tx.tx_h.ac_command = acmd_transmit | AC_CFLD_I;
+ tx.tx_h.ac_link = nop_addr;
+ tx.tx_tbd_offset = tbd_addr;
+ obram_write(ioaddr, tx_addr, (unsigned char *)&tx, sizeof(tx));
+
+ nop.nop_h.ac_status = 0;
+ nop.nop_h.ac_command = acmd_nop;
+ nop.nop_h.ac_link = nop_addr;
+ obram_write(ioaddr, nop_addr, (unsigned char *)&nop, sizeof(nop));
+
+ tbd.tbd_status = TBD_STATUS_EOF;
+ tbd.tbd_next_bd_offset = I82586NULL;
+ tbd.tbd_bufl = buf_addr;
+ tbd.tbd_bufh = 0;
+ obram_write(ioaddr, tbd_addr, (unsigned char *)&tbd, sizeof(tbd));
+ }
+
+ first_nop = OFFSET_CU + (NTXBLOCKS - 1) * TXBLOCKZ + sizeof(ac_tx_t);
+ obram_write(ioaddr, scboff(OFFSET_SCB, scb_cbl_offset), (unsigned char *)&first_nop, sizeof(first_nop));
+
+ scb_cs = SCB_CMD_CUC_GO;
+ obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), (unsigned char *)&scb_cs, sizeof(scb_cs));
+
+ set_chan_attn(ioaddr, lp->hacr);
+
+ for (i = 1000; i > 0; i--)
+ {
+ obram_read(ioaddr, scboff(OFFSET_SCB, scb_command), (unsigned char *)&scb_cs, sizeof(scb_cs));
+ if (scb_cs == 0)
+ break;
+
+ udelay(1000);
+ }
+
+ if (i <= 0)
+ printk("%s: wavelan_cu_start(): board not accepting command.\n", dev->name);
+
+ lp->tx_n_in_use = 0;
+ dev->tbusy = 0;
+}
+
+static
+int
+wavelan_open(device *dev)
+{
+ unsigned short ioaddr;
+ net_local *lp;
+ unsigned long x;
+ int r;
+
+ if (wavelan_debug > 0)
+ printk("%s: ->wavelan_open(dev=0x%x)\n", dev->name, (unsigned int)dev);
+
+ ioaddr = dev->base_addr;
+ lp = (net_local *)dev->priv;
+
+ if (dev->irq == 0)
+ {
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_open(): -ENXIO\n", dev->name);
+ return -ENXIO;
+ }
+
+ if
+ (
+ irq2dev_map[dev->irq] != (device *)0
+ /* This is always true, but avoid the false IRQ. */
+ ||
+ (irq2dev_map[dev->irq] = dev) == (device *)0
+ ||
+ request_irq(dev->irq, &wavelan_interrupt, 0, "WaveLAN") != 0
+ )
+ {
+ irq2dev_map[dev->irq] = (device *)0;
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_open(): -EAGAIN\n", dev->name);
+ return -EAGAIN;
+ }
+
+ x = wavelan_splhi();
+ if ((r = wavelan_hardware_reset(dev)) != -1)
+ {
+ dev->interrupt = 0;
+ dev->start = 1;
+ }
+ wavelan_splx(x);
+
+ if (r == -1)
+ {
+ free_irq(dev->irq);
+ irq2dev_map[dev->irq] = (device *)0;
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_open(): -EAGAIN(2)\n", dev->name);
+ return -EAGAIN;
+ }
+
+ MOD_INC_USE_COUNT;
+
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_open(): 0\n", dev->name);
+
+ return 0;
+}
+
+static
+void
+hardware_send_packet(device *dev, void *buf, short length)
+{
+ unsigned short ioaddr;
+ net_local *lp;
+ unsigned short txblock;
+ unsigned short txpred;
+ unsigned short tx_addr;
+ unsigned short nop_addr;
+ unsigned short tbd_addr;
+ unsigned short buf_addr;
+ ac_tx_t tx;
+ ac_nop_t nop;
+ tbd_t tbd;
+ unsigned long x;
+
+ ioaddr = dev->base_addr;
+ lp = (net_local *)dev->priv;
+
+ x = wavelan_splhi();
+
+ txblock = lp->tx_first_free;
+ txpred = txblock - TXBLOCKZ;
+ if (txpred < OFFSET_CU)
+ txpred += NTXBLOCKS * TXBLOCKZ;
+ lp->tx_first_free += TXBLOCKZ;
+ if (lp->tx_first_free >= OFFSET_CU + NTXBLOCKS * TXBLOCKZ)
+ lp->tx_first_free -= NTXBLOCKS * TXBLOCKZ;
+
+/*
+if (lp->tx_n_in_use > 0)
+ printk("%c", "0123456789abcdefghijk"[lp->tx_n_in_use]);
+*/
+
+ lp->tx_n_in_use++;
+
+ tx_addr = txblock;
+ nop_addr = tx_addr + sizeof(tx);
+ tbd_addr = nop_addr + sizeof(nop);
+ buf_addr = tbd_addr + sizeof(tbd);
+
+ /*
+ * Transmit command.
+ */
+ tx.tx_h.ac_status = 0;
+ obram_write(ioaddr, toff(ac_tx_t, tx_addr, tx_h.ac_status), (unsigned char *)&tx.tx_h.ac_status, sizeof(tx.tx_h.ac_status));
+
+ /*
+ * NOP command.
+ */
+ nop.nop_h.ac_status = 0;
+ obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_status), (unsigned char *)&nop.nop_h.ac_status, sizeof(nop.nop_h.ac_status));
+ nop.nop_h.ac_link = nop_addr;
+ obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_link), (unsigned char *)&nop.nop_h.ac_link, sizeof(nop.nop_h.ac_link));
+
+ /*
+ * Transmit buffer descriptor.
+ */
+ tbd.tbd_status = TBD_STATUS_EOF | (TBD_STATUS_ACNT & length);
+ tbd.tbd_next_bd_offset = I82586NULL;
+ tbd.tbd_bufl = buf_addr;
+ tbd.tbd_bufh = 0;
+ obram_write(ioaddr, tbd_addr, (unsigned char *)&tbd, sizeof(tbd));
+
+ /*
+ * Data.
+ */
+ obram_write(ioaddr, buf_addr, buf, length);
+
+ /*
+ * Overwrite the predecessor NOP link
+ * so that it points to this txblock.
+ */
+ nop_addr = txpred + sizeof(tx);
+ nop.nop_h.ac_status = 0;
+ obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_status), (unsigned char *)&nop.nop_h.ac_status, sizeof(nop.nop_h.ac_status));
+ nop.nop_h.ac_link = txblock;
+ obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_link), (unsigned char *)&nop.nop_h.ac_link, sizeof(nop.nop_h.ac_link));
+
+ if (lp->tx_first_in_use == I82586NULL)
+ lp->tx_first_in_use = txblock;
+
+ if (lp->tx_n_in_use < NTXBLOCKS - 1)
+ dev->tbusy = 0;
+
+ dev->trans_start = jiffies;
+
+ if (lp->watchdog.next == (timer_list *)0)
+ wavelan_watchdog((unsigned long)dev);
+
+ wavelan_splx(x);
+
+ if (wavelan_debug > 4)
+ {
+ unsigned char *a;
+
+ a = (unsigned char *)buf;
+
+ printk
+ (
+ "%s: tx: dest %02x:%02x:%02x:%02x:%02x:%02x, length %d, tbd.tbd_bufl 0x%x.\n",
+ dev->name,
+ a[0], a[1], a[2], a[3], a[4], a[5],
+ length,
+ buf_addr
+ );
+ }
+}
+
+static
+int
+wavelan_send_packet(struct sk_buff *skb, device *dev)
+{
+ unsigned short ioaddr;
+
+ ioaddr = dev->base_addr;
+
+ if (dev->tbusy)
+ {
+ /*
+ * If we get here, some higher level
+ * has decided we are broken.
+ */
+ int tickssofar;
+
+ tickssofar = jiffies - dev->trans_start;
+
+ /*
+ * But for the moment, we will rely on wavelan_watchdog()
+ * instead as it allows finer control over exactly when we
+ * make the determination of failure.
+ *
+ if (tickssofar < 5)
+ */
+ return 1;
+
+ wavelan_scb_show(ioaddr);
+ wavelan_ru_show(dev);
+ wavelan_cu_show(dev);
+ wavelan_dev_show(dev);
+ wavelan_local_show(dev);
+
+ printk("%s: transmit timed out -- resetting board.\n", dev->name);
+
+ (void)wavelan_hardware_reset(dev);
+ }
+
+ /*
+ * If some higher layer thinks we've missed
+ * a tx-done interrupt we are passed NULL.
+ * Caution: dev_tint() handles the cli()/sti() itself.
+ */
+ if (skb == (struct sk_buff *)0)
+ {
+ dev_tint(dev);
+ return 0;
+ }
+
+ /*
+ * Block a timer-based transmit from overlapping.
+ */
+ if (set_bit(0, (void *)&dev->tbusy) == 0)
+ {
+ short length;
+ unsigned char *buf;
+
+ length = (ETH_ZLEN < skb->len) ? skb->len : ETH_ZLEN;
+ buf = skb->data;
+
+ hardware_send_packet(dev, buf, length);
+ }
+ else
+ printk("%s: Transmitter access conflict.\n", dev->name);
+
+ dev_kfree_skb(skb, FREE_WRITE);
+
+ return 0;
+}
+
+#if 0
+static
+int
+addrcmp(unsigned char *a0, unsigned char *a1)
+{
+ int i;
+
+ for (i = 0; i < WAVELAN_ADDR_SIZE; i++)
+ {
+ if (a0[i] != a1[i])
+ return a0[i] - a1[i];
+ }
+
+ return 0;
+}
+#endif /* 0 */
+
+/*
+ * Transfer as many packets as we can
+ * from the device RAM.
+ * Called by the interrupt handler.
+ */
+static
+void
+wavelan_receive(device *dev)
+{
+ unsigned short ioaddr;
+ net_local *lp;
+ int nreaped;
+
+ ioaddr = dev->base_addr;
+ lp = (net_local *)dev->priv;
+ nreaped = 0;
+
+ for (;;)
+ {
+ fd_t fd;
+ rbd_t rbd;
+ ushort pkt_len;
+ int sksize;
+ struct sk_buff *skb;
+
+ obram_read(ioaddr, lp->rx_head, (unsigned char *)&fd, sizeof(fd));
+
+ if ((fd.fd_status & FD_STATUS_C) != FD_STATUS_C)
+ break;
+
+ nreaped++;
+
+ if
+ (
+ (fd.fd_status & (FD_STATUS_B | FD_STATUS_OK))
+ !=
+ (FD_STATUS_B | FD_STATUS_OK)
+ )
+ {
+ /*
+ * Not sure about this one -- it does not seem
+ * to be an error so we will keep quiet about it.
+ if ((fd.fd_status & FD_STATUS_B) != FD_STATUS_B)
+ printk("%s: frame not consumed by RU.\n", dev->name);
+ */
+
+ if ((fd.fd_status & FD_STATUS_OK) != FD_STATUS_OK)
+ printk("%s: frame not received successfully.\n", dev->name);
+ }
+
+ if ((fd.fd_status & (FD_STATUS_S6 | FD_STATUS_S7 | FD_STATUS_S8 | FD_STATUS_S9 | FD_STATUS_S10 | FD_STATUS_S11)) != 0)
+ {
+ lp->stats.rx_errors++;
+
+ if ((fd.fd_status & FD_STATUS_S6) != 0)
+ printk("%s: no EOF flag.\n", dev->name);
+
+ if ((fd.fd_status & FD_STATUS_S7) != 0)
+ {
+ lp->stats.rx_length_errors++;
+ printk("%s: frame too short.\n", dev->name);
+ }
+
+ if ((fd.fd_status & FD_STATUS_S8) != 0)
+ {
+ lp->stats.rx_over_errors++;
+ printk("%s: rx DMA overrun.\n", dev->name);
+ }
+
+ if ((fd.fd_status & FD_STATUS_S9) != 0)
+ {
+ lp->stats.rx_fifo_errors++;
+ printk("%s: ran out of resources.\n", dev->name);
+ }
+
+ if ((fd.fd_status & FD_STATUS_S10) != 0)
+ {
+ lp->stats.rx_frame_errors++;
+ printk("%s: alignment error.\n", dev->name);
+ }
+
+ if ((fd.fd_status & FD_STATUS_S11) != 0)
+ {
+ lp->stats.rx_crc_errors++;
+ printk("%s: CRC error.\n", dev->name);
+ }
+ }
+
+ if (fd.fd_rbd_offset == I82586NULL)
+ printk("%s: frame has no data.\n", dev->name);
+ else
+ {
+ obram_read(ioaddr, fd.fd_rbd_offset, (unsigned char *)&rbd, sizeof(rbd));
+
+ if ((rbd.rbd_status & RBD_STATUS_EOF) != RBD_STATUS_EOF)
+ printk("%s: missing EOF flag.\n", dev->name);
+
+ if ((rbd.rbd_status & RBD_STATUS_F) != RBD_STATUS_F)
+ printk("%s: missing F flag.\n", dev->name);
+
+ pkt_len = rbd.rbd_status & RBD_STATUS_ACNT;
+
+#if 0
+ {
+ unsigned char addr[WAVELAN_ADDR_SIZE];
+ int i;
+ static unsigned char toweraddr[WAVELAN_ADDR_SIZE] =
+ {
+ 0x08, 0x00, 0x0e, 0x20, 0x3e, 0xd3,
+ };
+
+ obram_read(ioaddr, rbd.rbd_bufl + sizeof(addr), &addr[0], sizeof(addr));
+ if
+ (
+ /*
+ addrcmp(&addr[0], &dev->dev_addr[0]) != 0
+ &&
+ */
+ addrcmp(&addr[0], toweraddr) != 0
+ )
+ {
+ printk("%s: foreign MAC source addr=", dev->name);
+ for (i = 0; i < WAVELAN_ADDR_SIZE; i++)
+ printk("%s%02x", (i == 0) ? "" : ":", addr[i]);
+ printk("\n");
+ }
+ }
+#endif /* 0 */
+
+ if (wavelan_debug > 5)
+ {
+ unsigned char addr[WAVELAN_ADDR_SIZE];
+ unsigned short ltype;
+ int i;
+
+#if 0
+ printk("%s: fd_dest=", dev->name);
+ for (i = 0; i < WAVELAN_ADDR_SIZE; i++)
+ printk("%s%02x", (i == 0) ? "" : ":", fd.fd_dest[i]);
+ printk("\n");
+
+ printk("%s: fd_src=", dev->name);
+ for (i = 0; i < WAVELAN_ADDR_SIZE; i++)
+ printk("%s%02x", (i == 0) ? "" : ":", fd.fd_src[i]);
+ printk("\n");
+ printk("%s: fd_length=%d\n", dev->name, fd.fd_length);
+#endif /* 0 */
+
+ obram_read(ioaddr, rbd.rbd_bufl, &addr[0], sizeof(addr));
+ printk("%s: dest=", dev->name);
+ for (i = 0; i < WAVELAN_ADDR_SIZE; i++)
+ printk("%s%02x", (i == 0) ? "" : ":", addr[i]);
+ printk("\n");
+
+ obram_read(ioaddr, rbd.rbd_bufl + sizeof(addr), &addr[0], sizeof(addr));
+ printk("%s: src=", dev->name);
+ for (i = 0; i < WAVELAN_ADDR_SIZE; i++)
+ printk("%s%02x", (i == 0) ? "" : ":", addr[i]);
+ printk("\n");
+
+ obram_read(ioaddr, rbd.rbd_bufl + sizeof(addr) * 2, (unsigned char *)&ltype, sizeof(ltype));
+ printk("%s: ntohs(length/type)=0x%04x\n", dev->name, ntohs(ltype));
+ }
+
+ sksize = pkt_len;
+
+ if ((skb = dev_alloc_skb(sksize)) == (struct sk_buff *)0)
+ {
+ printk("%s: could not alloc_skb(%d, GFP_ATOMIC).\n", dev->name, sksize);
+ lp->stats.rx_dropped++;
+ }
+ else
+ {
+ skb->dev = dev;
+
+ obram_read(ioaddr, rbd.rbd_bufl, skb_put(skb,pkt_len), pkt_len);
+
+ if (wavelan_debug > 5)
+ {
+ int i;
+ int maxi;
+
+ printk("%s: pkt_len=%d, data=\"", dev->name, pkt_len);
+
+ if ((maxi = pkt_len) > 16)
+ maxi = 16;
+
+ for (i = 0; i < maxi; i++)
+ {
+ unsigned char c;
+
+ c = skb->data[i];
+ if (c >= ' ' && c <= '~')
+ printk(" %c", skb->data[i]);
+ else
+ printk("%02x", skb->data[i]);
+ }
+
+ if (maxi < pkt_len)
+ printk("..");
+
+ printk("\"\n\n");
+ }
+
+ skb->protocol=eth_type_trans(skb,dev);
+ netif_rx(skb);
+
+ lp->stats.rx_packets++;
+ }
+ }
+
+ fd.fd_status = 0;
+ obram_write(ioaddr, fdoff(lp->rx_head, fd_status), (unsigned char *)&fd.fd_status, sizeof(fd.fd_status));
+
+ fd.fd_command = FD_COMMAND_EL;
+ obram_write(ioaddr, fdoff(lp->rx_head, fd_command), (unsigned char *)&fd.fd_command, sizeof(fd.fd_command));
+
+ fd.fd_command = 0;
+ obram_write(ioaddr, fdoff(lp->rx_last, fd_command), (unsigned char *)&fd.fd_command, sizeof(fd.fd_command));
+
+ lp->rx_last = lp->rx_head;
+ lp->rx_head = fd.fd_link_offset;
+ }
+
+/*
+ if (nreaped > 1)
+ printk("r%d", nreaped);
+*/
+}
+
+/*
+ * Command completion interrupt.
+ * Reclaim as many freed tx buffers as we can.
+ */
+static
+int
+wavelan_complete(device *dev, unsigned short ioaddr, net_local *lp)
+{
+ int nreaped;
+
+ nreaped = 0;
+
+ for (;;)
+ {
+ unsigned short tx_status;
+
+ if (lp->tx_first_in_use == I82586NULL)
+ break;
+
+ obram_read(ioaddr, acoff(lp->tx_first_in_use, ac_status), (unsigned char *)&tx_status, sizeof(tx_status));
+
+ if ((tx_status & AC_SFLD_C) == 0)
+ break;
+
+ nreaped++;
+
+ --lp->tx_n_in_use;
+
+/*
+if (lp->tx_n_in_use > 0)
+ printk("%c", "0123456789abcdefghijk"[lp->tx_n_in_use]);
+*/
+
+ if (lp->tx_n_in_use <= 0)
+ lp->tx_first_in_use = I82586NULL;
+ else
+ {
+ lp->tx_first_in_use += TXBLOCKZ;
+ if (lp->tx_first_in_use >= OFFSET_CU + NTXBLOCKS * TXBLOCKZ)
+ lp->tx_first_in_use -= NTXBLOCKS * TXBLOCKZ;
+ }
+
+ if (tx_status & AC_SFLD_OK)
+ {
+ int ncollisions;
+
+ lp->stats.tx_packets++;
+ ncollisions = tx_status & AC_SFLD_MAXCOL;
+ lp->stats.collisions += ncollisions;
+ /*
+ if (ncollisions > 0)
+ printk("%s: tx completed after %d collisions.\n", dev->name, ncollisions);
+ */
+ }
+ else
+ {
+ lp->stats.tx_errors++;
+ if (tx_status & AC_SFLD_S10)
+ {
+ lp->stats.tx_carrier_errors++;
+ if (wavelan_debug > 0)
+ printk("%s: tx error: no CS.\n", dev->name);
+ }
+ if (tx_status & AC_SFLD_S9)
+ {
+ lp->stats.tx_carrier_errors++;
+ printk("%s: tx error: lost CTS.\n", dev->name);
+ }
+ if (tx_status & AC_SFLD_S8)
+ {
+ lp->stats.tx_fifo_errors++;
+ printk("%s: tx error: slow DMA.\n", dev->name);
+ }
+ if (tx_status & AC_SFLD_S6)
+ {
+ lp->stats.tx_heartbeat_errors++;
+ if (wavelan_debug > 0)
+ printk("%s: tx error: heart beat.\n", dev->name);
+ }
+ if (tx_status & AC_SFLD_S5)
+ {
+ lp->stats.tx_aborted_errors++;
+ if (wavelan_debug > 0)
+ printk("%s: tx error: too many collisions.\n", dev->name);
+ }
+ }
+
+ if (wavelan_debug > 5)
+ printk("%s: tx completed, tx_status 0x%04x.\n", dev->name, tx_status);
+ }
+
+/*
+ if (nreaped > 1)
+ printk("c%d", nreaped);
+*/
+
+ /*
+ * Inform upper layers.
+ */
+ if (lp->tx_n_in_use < NTXBLOCKS - 1)
+ {
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+ }
+
+ return nreaped;
+}
+
+static
+void
+wavelan_watchdog(unsigned long a)
+{
+ device *dev;
+ net_local *lp;
+ unsigned short ioaddr;
+ unsigned long x;
+ unsigned int nreaped;
+
+ x = wavelan_splhi();
+
+ dev = (device *)a;
+ ioaddr = dev->base_addr;
+ lp = (net_local *)dev->priv;
+
+ if (lp->tx_n_in_use <= 0)
+ {
+ wavelan_splx(x);
+ return;
+ }
+
+ lp->watchdog.expires = jiffies+WATCHDOG_JIFFIES;
+ add_timer(&lp->watchdog);
+
+ if (jiffies - dev->trans_start < WATCHDOG_JIFFIES)
+ {
+ wavelan_splx(x);
+ return;
+ }
+
+ nreaped = wavelan_complete(dev, ioaddr, lp);
+
+ printk("%s: warning: wavelan_watchdog(): %d reaped, %d remain.\n", dev->name, nreaped, lp->tx_n_in_use);
+ /*
+ wavelan_scb_show(ioaddr);
+ wavelan_ru_show(dev);
+ wavelan_cu_show(dev);
+ wavelan_dev_show(dev);
+ wavelan_local_show(dev);
+ */
+
+ wavelan_splx(x);
+}
+
+static
+void
+wavelan_interrupt(int irq, struct pt_regs *regs)
+{
+ device *dev;
+ unsigned short ioaddr;
+ net_local *lp;
+ unsigned short hasr;
+ unsigned short status;
+ unsigned short ack_cmd;
+
+ if ((dev = (device *)(irq2dev_map[irq])) == (device *)0)
+ {
+ printk("wavelan_interrupt(): irq %d for unknown device.\n", irq);
+ return;
+ }
+
+ ioaddr = dev->base_addr;
+ lp = (net_local *)dev->priv;
+
+ dev->interrupt = 1;
+
+ if ((hasr = hasr_read(ioaddr)) & HASR_MMC_INTR)
+ {
+ unsigned char dce_status;
+
+ /*
+ * Interrupt from the modem management controller.
+ * This will clear it -- ignored for now.
+ */
+ mmc_read(ioaddr, mmroff(0, mmr_dce_status), &dce_status, sizeof(dce_status));
+ if (wavelan_debug > 0)
+ printk("%s: warning: wavelan_interrupt(): unexpected mmc interrupt: status 0x%04x.\n", dev->name, dce_status);
+ }
+
+ if ((hasr & HASR_82586_INTR) == 0)
+ {
+ dev->interrupt = 0;
+ if (wavelan_debug > 0)
+ printk("%s: warning: wavelan_interrupt() but (hasr & HASR_82586_INTR) == 0.\n", dev->name);
+ return;
+ }
+
+ obram_read(ioaddr, scboff(OFFSET_SCB, scb_status), (unsigned char *)&status, sizeof(status));
+
+ /*
+ * Acknowledge the interrupt(s).
+ */
+ ack_cmd = status & SCB_ST_INT;
+
+ obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), (unsigned char *)&ack_cmd, sizeof(ack_cmd));
+
+ set_chan_attn(ioaddr, lp->hacr);
+
+ if (wavelan_debug > 5)
+ printk("%s: interrupt, status 0x%04x.\n", dev->name, status);
+
+ if ((status & SCB_ST_CX) == SCB_ST_CX)
+ {
+ /*
+ * Command completed.
+ */
+ if (wavelan_debug > 5)
+ printk("%s: command completed.\n", dev->name);
+ (void)wavelan_complete(dev, ioaddr, lp);
+ }
+
+ if ((status & SCB_ST_FR) == SCB_ST_FR)
+ {
+ /*
+ * Frame received.
+ */
+ if (wavelan_debug > 5)
+ printk("%s: received packet.\n", dev->name);
+ wavelan_receive(dev);
+ }
+
+ if
+ (
+ (status & SCB_ST_CNA) == SCB_ST_CNA
+ ||
+ (((status & SCB_ST_CUS) != SCB_ST_CUS_ACTV) && dev->start)
+ )
+ {
+ printk("%s: warning: CU inactive -- restarting.\n", dev->name);
+
+ (void)wavelan_hardware_reset(dev);
+ }
+
+ if
+ (
+ (status & SCB_ST_RNR) == SCB_ST_RNR
+ ||
+ (((status & SCB_ST_RUS) != SCB_ST_RUS_RDY) && dev->start)
+ )
+ {
+ printk("%s: warning: RU not ready -- restarting.\n", dev->name);
+
+ (void)wavelan_hardware_reset(dev);
+ }
+
+ dev->interrupt = 0;
+}
+
+static
+int
+wavelan_close(device *dev)
+{
+ unsigned short ioaddr;
+ net_local *lp;
+ unsigned short scb_cmd;
+
+ if (wavelan_debug > 0)
+ printk("%s: ->wavelan_close(dev=0x%x)\n", dev->name, (unsigned int)dev);
+
+ ioaddr = dev->base_addr;
+ lp = (net_local *)dev->priv;
+
+ dev->tbusy = 1;
+ dev->start = 0;
+
+ /*
+ * Flush the Tx and disable Rx.
+ */
+ scb_cmd = (SCB_CMD_CUC & SCB_CMD_CUC_SUS) | (SCB_CMD_RUC & SCB_CMD_RUC_SUS);
+ obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), (unsigned char *)&scb_cmd, sizeof(scb_cmd));
+ set_chan_attn(ioaddr, lp->hacr);
+
+ wavelan_ints_off(dev);
+
+ free_irq(dev->irq);
+ irq2dev_map[dev->irq] = (device *)0;
+
+ /*
+ * Release the ioport-region.
+ */
+ release_region(ioaddr, sizeof(ha_t));
+
+ MOD_DEC_USE_COUNT;
+
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_close(): 0\n", dev->name);
+
+ return 0;
+}
+
+/*
+ * Get the current statistics.
+ * This may be called with the card open or closed.
+ */
+static
+en_stats *
+wavelan_get_stats(device *dev)
+{
+ net_local *lp;
+
+ lp = (net_local *)dev->priv;
+
+ return &lp->stats;
+}
+
+static
+void
+wavelan_set_multicast_list(device *dev)
+{
+ net_local *lp;
+ unsigned long x;
+
+ if (wavelan_debug > 0)
+ printk("%s: ->wavelan_set_multicast_list(dev=0x%x)", dev->name, dev);
+
+ lp = (net_local *)dev->priv;
+
+ if(dev->flags&IFF_PROMISC)
+ {
+ /*
+ * Promiscuous mode: receive all packets.
+ */
+ lp->promiscuous = 1;
+ x = wavelan_splhi();
+ (void)wavelan_hardware_reset(dev);
+ wavelan_splx(x);
+ }
+#if MULTICAST_IS_ADDED
+ else if((dev->flags&IFF_ALLMULTI)||dev->mc_list)
+ {
+
+
+ }
+#endif
+ else
+ {
+ /*
+ * Normal mode: disable promiscuous mode,
+ * clear multicast list.
+ */
+ lp->promiscuous = 0;
+ x = wavelan_splhi();
+ (void)wavelan_hardware_reset(dev);
+ wavelan_splx(x);
+ }
+
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_set_multicast_list()\n", dev->name);
+}
+
+/*
+ * Extra WaveLAN-specific device data.
+ * "cat /proc/net/wavelan" -- see fs/proc/net.c.
+ */
+static
+int
+sprintf_stats(char *buffer, device *dev)
+{
+ net_local *lp;
+ unsigned char v;
+ mmr_t m;
+
+ lp = (net_local *)dev->priv;
+
+ if (lp == (net_local *)0)
+ return sprintf(buffer, "%6s: No statistics available.\n", dev->name);
+
+ v = (unsigned char)1;
+ mmc_write(dev->base_addr, mmwoff(0, mmw_freeze), &v, sizeof(v));
+
+ mmc_read(dev->base_addr, mmroff(0, mmr_dce_status), &m.mmr_dce_status, sizeof(m.mmr_dce_status));
+ mmc_read(dev->base_addr, mmroff(0, mmr_correct_nwid_h), &m.mmr_correct_nwid_h, sizeof(m.mmr_correct_nwid_h));
+ mmc_read(dev->base_addr, mmroff(0, mmr_correct_nwid_l), &m.mmr_correct_nwid_l, sizeof(m.mmr_correct_nwid_l));
+ mmc_read(dev->base_addr, mmroff(0, mmr_wrong_nwid_h), &m.mmr_wrong_nwid_h, sizeof(m.mmr_wrong_nwid_h));
+ mmc_read(dev->base_addr, mmroff(0, mmr_wrong_nwid_l), &m.mmr_wrong_nwid_l, sizeof(m.mmr_wrong_nwid_l));
+ mmc_read(dev->base_addr, mmroff(0, mmr_signal_lvl), &m.mmr_signal_lvl, sizeof(m.mmr_signal_lvl));
+ mmc_read(dev->base_addr, mmroff(0, mmr_silence_lvl), &m.mmr_silence_lvl, sizeof(m.mmr_silence_lvl));
+ mmc_read(dev->base_addr, mmroff(0, mmr_sgnl_qual), &m.mmr_sgnl_qual, sizeof(m.mmr_sgnl_qual));
+
+ v = (unsigned char)0;
+ mmc_write(dev->base_addr, mmwoff(0, mmw_freeze), &v, sizeof(v));
+
+ lp->correct_nwid += (m.mmr_correct_nwid_h << 8) | m.mmr_correct_nwid_l;
+ lp->wrong_nwid += (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l;
+
+ return sprintf
+ (
+ buffer,
+ "%6s: %02x %08x %08x %02x %02x %02x %02x %u\n",
+ dev->name,
+ m.mmr_dce_status,
+ lp->correct_nwid,
+ lp->wrong_nwid,
+ m.mmr_signal_lvl,
+ m.mmr_silence_lvl,
+ m.mmr_sgnl_qual,
+ lp->tx_n_in_use,
+ lp->nresets
+ );
+}
+
+static int
+wavelan_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+ int len;
+ off_t begin;
+ off_t pos;
+ int size;
+ unsigned long x;
+
+ len = 0;
+ begin = 0;
+ pos = 0;
+
+ size = sprintf(buffer, "%s", "Iface | dce +nwid -nwid lvl slnc qual ntxq nrst\n");
+
+ pos += size;
+ len += size;
+
+ x = wavelan_splhi();
+
+ if (first_wavelan != (net_local *)0)
+ {
+ net_local *lp;
+
+ lp = first_wavelan;
+ do
+ {
+ size = sprintf_stats(buffer + len, lp->dev);
+
+ len += size;
+ pos = begin + len;
+
+ if (pos < offset)
+ {
+ len = 0;
+ begin = pos;
+ }
+
+ if (pos > offset + length)
+ break;
+ }
+ while ((lp = lp->next) != first_wavelan);
+ }
+
+ wavelan_splx(x);
+
+ *start = buffer + (offset - begin); /* Start of wanted data */
+ len -= (offset - begin); /* Start slop */
+ if (len > length)
+ len = length; /* Ending slop */
+
+ return len;
+}
+
+#if defined(MODULE)
+static char devicename[9] = { 0, };
+static struct device dev_wavelan =
+{
+ devicename, /* device name is inserted by linux/drivers/net/net_init.c */
+ 0, 0, 0, 0,
+ 0, 0,
+ 0, 0, 0, NULL, wavelan_probe
+};
+
+static int io = 0x390; /* Default from above.. */
+static int irq = 0;
+
+int
+init_module(void)
+{
+ dev_wavelan.base_addr = io;
+ dev_wavelan.irq = irq;
+ if (register_netdev(&dev_wavelan) != 0)
+ return -EIO;
+
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ proc_net_unregister(PROC_NET_WAVELAN);
+ unregister_netdev(&dev_wavelan);
+ kfree_s(dev_wavelan.priv, sizeof(struct net_local));
+ dev_wavelan.priv = NULL;
+}
+#endif /* defined(MODULE) */
+
+static
+void
+wavelan_cu_show_one(device *dev, net_local *lp, int i, unsigned short p)
+{
+ unsigned short ioaddr;
+ ac_tx_t actx;
+
+ ioaddr = dev->base_addr;
+
+ printk("%d: 0x%x:", i, p);
+
+ obram_read(ioaddr, p, (unsigned char *)&actx, sizeof(actx));
+ printk(" status=0x%x,", actx.tx_h.ac_status);
+ printk(" command=0x%x,", actx.tx_h.ac_command);
+
+/*
+ {
+ tbd_t tbd;
+
+ obram_read(ioaddr, actx.tx_tbd_offset, (unsigned char *)&tbd, sizeof(tbd));
+ printk(" tbd_status=0x%x,", tbd.tbd_status);
+ }
+*/
+
+ printk("|");
+}
+
+#if 0
+static
+void
+wavelan_psa_show(psa_t *p)
+{
+ printk("psa:");
+
+ printk("psa_io_base_addr_1: 0x%02x,", p->psa_io_base_addr_1);
+ printk("psa_io_base_addr_2: 0x%02x,", p->psa_io_base_addr_2);
+ printk("psa_io_base_addr_3: 0x%02x,", p->psa_io_base_addr_3);
+ printk("psa_io_base_addr_4: 0x%02x,", p->psa_io_base_addr_4);
+ printk("psa_rem_boot_addr_1: 0x%02x,", p->psa_rem_boot_addr_1);
+ printk("psa_rem_boot_addr_2: 0x%02x,", p->psa_rem_boot_addr_2);
+ printk("psa_rem_boot_addr_3: 0x%02x,", p->psa_rem_boot_addr_3);
+ printk("psa_holi_params: 0x%02x,", p->psa_holi_params);
+ printk("psa_int_req_no: %d,", p->psa_int_req_no);
+ printk
+ (
+ "psa_univ_mac_addr[]: %02x:%02x:%02x:%02x:%02x:%02x,",
+ p->psa_univ_mac_addr[0],
+ p->psa_univ_mac_addr[1],
+ p->psa_univ_mac_addr[2],
+ p->psa_univ_mac_addr[3],
+ p->psa_univ_mac_addr[4],
+ p->psa_univ_mac_addr[5]
+ );
+ printk
+ (
+ "psa_local_mac_addr[]: %02x:%02x:%02x:%02x:%02x:%02x,",
+ p->psa_local_mac_addr[0],
+ p->psa_local_mac_addr[1],
+ p->psa_local_mac_addr[2],
+ p->psa_local_mac_addr[3],
+ p->psa_local_mac_addr[4],
+ p->psa_local_mac_addr[5]
+ );
+ printk("psa_univ_local_sel: %d,", p->psa_univ_local_sel);
+ printk("psa_comp_number: %d,", p->psa_comp_number);
+ printk("psa_thr_pre_set: 0x%02x,", p->psa_thr_pre_set);
+ printk("psa_feature_select/decay_prm: 0x%02x,", p->psa_feature_select);
+ printk("psa_subband/decay_update_prm: %d,", p->psa_subband);
+ printk("psa_quality_thr: 0x%02x,", p->psa_quality_thr);
+ printk("psa_mod_delay: 0x%02x,", p->psa_mod_delay);
+ printk("psa_nwid: 0x%02x%02x,", p->psa_nwid[0], p->psa_nwid[1]);
+ printk("psa_undefined: %d,", p->psa_undefined);
+ printk("psa_encryption_select: %d,", p->psa_encryption_select);
+ printk
+ (
+ "psa_encryption_key[]: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x,",
+ p->psa_encryption_key[0],
+ p->psa_encryption_key[1],
+ p->psa_encryption_key[2],
+ p->psa_encryption_key[3],
+ p->psa_encryption_key[4],
+ p->psa_encryption_key[5],
+ p->psa_encryption_key[6],
+ p->psa_encryption_key[7]
+ );
+ printk("psa_databus_width: %d,", p->psa_databus_width);
+ printk("psa_call_code/auto_squelch: 0x%02x,", p->psa_call_code);
+ printk("psa_no_of_retries: %d,", p->psa_no_of_retries);
+ printk("psa_acr: %d,", p->psa_acr);
+ printk("psa_dump_count: %d,", p->psa_dump_count);
+ printk("psa_nwid_prefix: 0x%02x,", p->psa_nwid_prefix);
+ printk("psa_conf_status: %d,", p->psa_conf_status);
+ printk("psa_crc: 0x%02x%02x,", p->psa_crc[0], p->psa_crc[1]);
+ printk("psa_crc_status: 0x%02x,", p->psa_crc_status);
+
+ printk("\n");
+}
+
+static
+void
+wavelan_mmc_show(unsigned short ioaddr)
+{
+ mmr_t m;
+
+ mmc_read(ioaddr, 0, (unsigned char *)&m, sizeof(m));
+
+ printk("mmr:");
+ printk(" des_status: 0x%x", m.mmr_des_status);
+ printk(" des_avail: 0x%x", m.mmr_des_avail);
+ printk(" des_io_invert: 0x%x", m.mmr_des_io_invert);
+ printk
+ (
+ " dce_status: 0x%x[%s%s%s%s]",
+ m.mmr_dce_status & 0x0F,
+ (m.mmr_dce_status & MMR_DCE_STATUS_ENERG_DET) ? "energy detected," : "",
+ (m.mmr_dce_status & MMR_DCE_STATUS_LOOPT_IND) ? "loop test indicated," : "",
+ (m.mmr_dce_status & MMR_DCE_STATUS_XMTITR_IND) ? "transmitter on," : "",
+ (m.mmr_dce_status & MMR_DCE_STATUS_JBR_EXPIRED) ? "jabber timer expired," : ""
+ );
+ printk(" correct_nwid: %d", m.mmr_correct_nwid_h << 8 | m.mmr_correct_nwid_l);
+ printk(" wrong_nwid: %d", (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l);
+ printk(" thr_pre_set: 0x%x", m.mmr_thr_pre_set);
+ printk(" signal_lvl: %d", m.mmr_signal_lvl);
+ printk(" silence_lvl: %d", m.mmr_silence_lvl);
+ printk(" sgnl_qual: 0x%x", m.mmr_sgnl_qual);
+ printk(" netw_id_l: %x", m.mmr_netw_id_l);
+
+ printk("\n");
+}
+#endif /* 0 */
+
+static
+void
+wavelan_scb_show(unsigned short ioaddr)
+{
+ scb_t scb;
+
+ obram_read(ioaddr, OFFSET_SCB, (unsigned char *)&scb, sizeof(scb));
+
+ printk("scb:");
+
+ printk(" status:");
+ printk
+ (
+ " stat 0x%x[%s%s%s%s]",
+ (scb.scb_status & (SCB_ST_CX | SCB_ST_FR | SCB_ST_CNA | SCB_ST_RNR)) >> 12,
+ (scb.scb_status & SCB_ST_CX) ? "cmd completion interrupt," : "",
+ (scb.scb_status & SCB_ST_FR) ? "frame received," : "",
+ (scb.scb_status & SCB_ST_CNA) ? "cmd unit not active," : "",
+ (scb.scb_status & SCB_ST_RNR) ? "rcv unit not ready," : ""
+ );
+ printk
+ (
+ " cus 0x%x[%s%s%s]",
+ (scb.scb_status & SCB_ST_CUS) >> 8,
+ ((scb.scb_status & SCB_ST_CUS) == SCB_ST_CUS_IDLE) ? "idle" : "",
+ ((scb.scb_status & SCB_ST_CUS) == SCB_ST_CUS_SUSP) ? "suspended" : "",
+ ((scb.scb_status & SCB_ST_CUS) == SCB_ST_CUS_ACTV) ? "active" : ""
+ );
+ printk
+ (
+ " rus 0x%x[%s%s%s%s]",
+ (scb.scb_status & SCB_ST_RUS) >> 4,
+ ((scb.scb_status & SCB_ST_RUS) == SCB_ST_RUS_IDLE) ? "idle" : "",
+ ((scb.scb_status & SCB_ST_RUS) == SCB_ST_RUS_SUSP) ? "suspended" : "",
+ ((scb.scb_status & SCB_ST_RUS) == SCB_ST_RUS_NRES) ? "no resources" : "",
+ ((scb.scb_status & SCB_ST_RUS) == SCB_ST_RUS_RDY) ? "ready" : ""
+ );
+
+ printk(" command:");
+ printk
+ (
+ " ack 0x%x[%s%s%s%s]",
+ (scb.scb_command & (SCB_CMD_ACK_CX | SCB_CMD_ACK_FR | SCB_CMD_ACK_CNA | SCB_CMD_ACK_RNR)) >> 12,
+ (scb.scb_command & SCB_CMD_ACK_CX) ? "ack cmd completion," : "",
+ (scb.scb_command & SCB_CMD_ACK_FR) ? "ack frame received," : "",
+ (scb.scb_command & SCB_CMD_ACK_CNA) ? "ack CU not active," : "",
+ (scb.scb_command & SCB_CMD_ACK_RNR) ? "ack RU not ready," : ""
+ );
+ printk
+ (
+ " cuc 0x%x[%s%s%s%s%s]",
+ (scb.scb_command & SCB_CMD_CUC) >> 8,
+ ((scb.scb_command & SCB_CMD_CUC) == SCB_CMD_CUC_NOP) ? "nop" : "",
+ ((scb.scb_command & SCB_CMD_CUC) == SCB_CMD_CUC_GO) ? "start cbl_offset" : "",
+ ((scb.scb_command & SCB_CMD_CUC) == SCB_CMD_CUC_RES) ? "resume execution" : "",
+ ((scb.scb_command & SCB_CMD_CUC) == SCB_CMD_CUC_SUS) ? "suspend execution" : "",
+ ((scb.scb_command & SCB_CMD_CUC) == SCB_CMD_CUC_ABT) ? "abort execution" : ""
+ );
+ printk
+ (
+ " ruc 0x%x[%s%s%s%s%s]",
+ (scb.scb_command & SCB_CMD_RUC) >> 4,
+ ((scb.scb_command & SCB_CMD_RUC) == SCB_CMD_RUC_NOP) ? "nop" : "",
+ ((scb.scb_command & SCB_CMD_RUC) == SCB_CMD_RUC_GO) ? "start rfa_offset" : "",
+ ((scb.scb_command & SCB_CMD_RUC) == SCB_CMD_RUC_RES) ? "resume reception" : "",
+ ((scb.scb_command & SCB_CMD_RUC) == SCB_CMD_RUC_SUS) ? "suspend reception" : "",
+ ((scb.scb_command & SCB_CMD_RUC) == SCB_CMD_RUC_ABT) ? "abort reception" : ""
+ );
+
+ printk(" cbl_offset 0x%x", scb.scb_cbl_offset);
+ printk(" rfa_offset 0x%x", scb.scb_rfa_offset);
+
+ printk(" crcerrs %d", scb.scb_crcerrs);
+ printk(" alnerrs %d", scb.scb_alnerrs);
+ printk(" rscerrs %d", scb.scb_rscerrs);
+ printk(" ovrnerrs %d", scb.scb_ovrnerrs);
+
+ printk("\n");
+}
+
+static
+void
+wavelan_ru_show(device *dev)
+{
+ net_local *lp;
+
+ lp = (net_local *)dev->priv;
+
+ printk("ru:");
+ /*
+ * Not implemented yet...
+ */
+ printk("\n");
+}
+
+static
+void
+wavelan_cu_show(device *dev)
+{
+ net_local *lp;
+ unsigned int i;
+ unsigned short p;
+
+ lp = (net_local *)dev->priv;
+
+ printk("cu:");
+ printk("\n");
+
+ for (i = 0, p = lp->tx_first_in_use; i < NTXBLOCKS; i++)
+ {
+ wavelan_cu_show_one(dev, lp, i, p);
+
+ p += TXBLOCKZ;
+ if (p >= OFFSET_CU + NTXBLOCKS * TXBLOCKZ)
+ p -= NTXBLOCKS * TXBLOCKZ;
+ }
+}
+
+static
+void
+wavelan_dev_show(device *dev)
+{
+ printk("dev:");
+ printk(" start=%d,", dev->start);
+ printk(" tbusy=%ld,", dev->tbusy);
+ printk(" interrupt=%d,", dev->interrupt);
+ printk(" trans_start=%ld,", dev->trans_start);
+ printk(" flags=0x%x,", dev->flags);
+ printk("\n");
+}
+
+static
+void
+wavelan_local_show(device *dev)
+{
+ net_local *lp;
+
+ lp = (net_local *)dev->priv;
+
+ printk("local:");
+ printk(" tx_n_in_use=%d,", lp->tx_n_in_use);
+ printk(" hacr=0x%x,", lp->hacr);
+ printk(" rx_head=0x%x,", lp->rx_head);
+ printk(" rx_last=0x%x,", lp->rx_last);
+ printk(" tx_first_free=0x%x,", lp->tx_first_free);
+ printk(" tx_first_in_use=0x%x,", lp->tx_first_in_use);
+ printk("\n");
+}
+
+/*
+ * This software may only be used and distributed
+ * according to the terms of the GNU Public License.
+ *
+ * This software was developed as a component of the
+ * Linux operating system.
+ * It is based on other device drivers and information
+ * either written or supplied by:
+ * Ajay Bakre (bakre@paul.rutgers.edu),
+ * Donald Becker (becker@cesdis.gsfc.nasa.gov),
+ * Loeke Brederveld (Loeke.Brederveld@Utrecht.NCR.com),
+ * Anders Klemets (klemets@it.kth.se),
+ * Vladimir V. Kolpakov (w@stier.koenig.ru),
+ * Marc Meertens (Marc.Meertens@Utrecht.NCR.com),
+ * Pauline Middelink (middelin@polyware.iaf.nl),
+ * Robert Morris (rtm@das.harvard.edu),
+ * Girish Welling (welling@paul.rutgers.edu),
+ *
+ * Thanks go also to:
+ * James Ashton (jaa101@syseng.anu.edu.au),
+ * Alan Cox (iialan@iiit.swan.ac.uk),
+ * Allan Creighton (allanc@cs.usyd.edu.au),
+ * Matthew Geier (matthew@cs.usyd.edu.au),
+ * Remo di Giovanni (remo@cs.usyd.edu.au),
+ * Eckhard Grah (grah@wrcs1.urz.uni-wuppertal.de),
+ * Vipul Gupta (vgupta@cs.binghamton.edu),
+ * Mark Hagan (mhagan@wtcpost.daytonoh.NCR.COM),
+ * Tim Nicholson (tim@cs.usyd.edu.au),
+ * Ian Parkin (ian@cs.usyd.edu.au),
+ * John Rosenberg (johnr@cs.usyd.edu.au),
+ * George Rossi (george@phm.gov.au),
+ * Arthur Scott (arthur@cs.usyd.edu.au),
+ * Peter Storey,
+ * for their assistance and advice.
+ *
+ * Please send bug reports, updates, comments to:
+ *
+ * Bruce Janson Email: bruce@cs.usyd.edu.au
+ * Basser Department of Computer Science Phone: +61-2-351-3423
+ * University of Sydney, N.S.W., 2006, AUSTRALIA Fax: +61-2-351-3838
+ */
diff --git a/i386/i386at/gpl/linux/net/wavelan.h b/i386/i386at/gpl/linux/net/wavelan.h
new file mode 100644
index 00000000..3eb221c0
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/wavelan.h
@@ -0,0 +1,252 @@
+#define WAVELAN_ADDR_SIZE 6 /* Size of a MAC address */
+#define SA_ADDR0 0x08 /* First octet of WaveLAN MAC addresses */
+#define SA_ADDR1 0x00 /* Second octet of WaveLAN MAC addresses */
+#define SA_ADDR2 0x0E /* Third octet of WaveLAN MAC addresses */
+#define WAVELAN_MTU 1500 /* Maximum size of WaveLAN packet */
+
+/*
+ * Parameter Storage Area (PSA).
+ */
+typedef struct psa_t psa_t;
+struct psa_t
+{
+ unsigned char psa_io_base_addr_1; /* Base address 1 ??? */
+ unsigned char psa_io_base_addr_2; /* Base address 2 */
+ unsigned char psa_io_base_addr_3; /* Base address 3 */
+ unsigned char psa_io_base_addr_4; /* Base address 4 */
+ unsigned char psa_rem_boot_addr_1; /* Remote Boot Address 1 */
+ unsigned char psa_rem_boot_addr_2; /* Remote Boot Address 2 */
+ unsigned char psa_rem_boot_addr_3; /* Remote Boot Address 3 */
+ unsigned char psa_holi_params; /* HOst Lan Interface (HOLI) Parameters */
+ unsigned char psa_int_req_no; /* Interrupt Request Line */
+ unsigned char psa_unused0[7]; /* unused */
+ unsigned char psa_univ_mac_addr[WAVELAN_ADDR_SIZE]; /* Universal (factory) MAC Address */
+ unsigned char psa_local_mac_addr[WAVELAN_ADDR_SIZE]; /* Local MAC Address */
+ unsigned char psa_univ_local_sel; /* Universal Local Selection */
+#define PSA_UNIVERSAL 0 /* Universal (factory) */
+#define PSA_LOCAL 1 /* Local */
+ unsigned char psa_comp_number; /* Compatibility Number: */
+#define PSA_COMP_PC_AT_915 0 /* PC-AT 915 MHz */
+#define PSA_COMP_PC_MC_915 1 /* PC-MC 915 MHz */
+#define PSA_COMP_PC_AT_2400 2 /* PC-AT 2.4 GHz */
+#define PSA_COMP_PC_MC_2400 3 /* PC-MC 2.4 GHz */
+#define PSA_COMP_PCMCIA_915 4 /* PCMCIA 915 MHz */
+ unsigned char psa_thr_pre_set; /* Modem Threshold Preset */
+ unsigned char psa_feature_select; /* ??? */
+#if 0
+ <alias for above>
+ unsigned char psa_decay_prm; /* Modem Decay */
+#endif /* 0 */
+ unsigned char psa_subband; /* Subband */
+#define PSA_SUBBAND_915 0 /* 915 MHz */
+#define PSA_SUBBAND_2425 1 /* 2425 MHz */
+#define PSA_SUBBAND_2460 2 /* 2460 MHz */
+#define PSA_SUBBAND_2484 3 /* 2484 MHz */
+#define PSA_SUBBAND_2430_5 4 /* 2430.5 MHz */
+#if 0
+ <alias for above>
+ unsigned char psa_decay_updat_prm; /* Modem Decay Update ??? */
+#endif /* 0 */
+ unsigned char psa_quality_thr; /* Modem Quality Threshold */
+ unsigned char psa_mod_delay; /* Modem Delay ??? */
+ unsigned char psa_nwid[2]; /* Network ID */
+ unsigned char psa_undefined; /* undefined */
+ unsigned char psa_encryption_select; /* Encryption On Off */
+ unsigned char psa_encryption_key[8]; /* Encryption Key */
+ unsigned char psa_databus_width; /* 8/16 bit bus width */
+ unsigned char psa_call_code; /* ??? */
+#if 0
+ <alias for above>
+ unsigned char psa_auto_squelch; /* Automatic Squelch level On off ??? */
+#endif /* 0 */
+ unsigned char psa_no_of_retries; /* LAN Cont. No of retries */
+ unsigned char psa_acr; /* LAN Cont. ACR */
+ unsigned char psa_dump_count; /* number of Dump Commands in TFB */
+ unsigned char psa_unused1[4]; /* unused */
+ unsigned char psa_nwid_prefix; /* ??? */
+ unsigned char psa_unused2[3]; /* unused */
+ unsigned char psa_conf_status; /* Card Configuration Status */
+ unsigned char psa_crc[2]; /* CRC over PSA */
+ unsigned char psa_crc_status; /* CRC Valid Flag */
+};
+#if STRUCT_CHECK == 1
+#define PSA_SIZE 64
+#endif /* STRUCT_CHECK == 1 */
+
+/*
+ * Modem Management Controller (MMC) write structure.
+ */
+typedef struct mmw_t mmw_t;
+struct mmw_t
+{
+ unsigned char mmw_encr_key[8]; /* encryption key */
+ unsigned char mmw_encr_enable; /* enable/disable encryption */
+ unsigned char mmw_unused0[1]; /* unused */
+ unsigned char mmw_des_io_invert; /* ??? */
+ unsigned char mmw_unused1[5]; /* unused */
+ unsigned char mmw_loopt_sel; /* looptest selection */
+#define MMW_LOOPT_SEL_UNDEFINED 0x40 /* undefined */
+#define MMW_LOOPT_SEL_INT 0x20 /* activate Attention Request */
+#define MMW_LOOPT_SEL_LS 0x10 /* looptest without collision avoidance */
+#define MMW_LOOPT_SEL_LT3A 0x08 /* looptest 3a */
+#define MMW_LOOPT_SEL_LT3B 0x04 /* looptest 3b */
+#define MMW_LOOPT_SEL_LT3C 0x02 /* looptest 3c */
+#define MMW_LOOPT_SEL_LT3D 0x01 /* looptest 3d */
+ unsigned char mmw_jabber_enable; /* jabber timer enable */
+ unsigned char mmw_freeze; /* freeze / unfreeze signal level */
+ unsigned char mmw_anten_sel; /* antenna selection */
+#define MMW_ANTEN_SEL_SEL 0x01 /* direct antenna selection */
+#define MMW_ANTEN_SEL_ALG_EN 0x02 /* antenna selection algorithm enable */
+ unsigned char mmw_ifs; /* inter frame spacing */
+ unsigned char mmw_mod_delay; /* modem delay */
+ unsigned char mmw_jam_time; /* jamming time */
+ unsigned char mmw_unused2[1]; /* unused */
+ unsigned char mmw_thr_pre_set; /* level threshold preset */
+ unsigned char mmw_decay_prm; /* decay parameters */
+ unsigned char mmw_decay_updat_prm; /* decay update parameters */
+ unsigned char mmw_quality_thr; /* quality (z-quotient) threshold */
+ unsigned char mmw_netw_id_l; /* NWID low order byte */
+ unsigned char mmw_netw_id_h; /* NWID high order byte */
+};
+#if STRUCT_CHECK == 1
+#define MMW_SIZE 30
+#endif /* STRUCT_CHECK == 1 */
+
+#define mmwoff(p,f) (unsigned short)((void *)(&((mmw_t *)((void *)0 + (p)))->f) - (void *)0)
+
+/*
+ * Modem Management Controller (MMC) read structure.
+ */
+typedef struct mmr_t mmr_t;
+struct mmr_t
+{
+ unsigned char mmr_unused0[8]; /* unused */
+ unsigned char mmr_des_status; /* encryption status */
+ unsigned char mmr_des_avail; /* encryption available (0x55 read) */
+ unsigned char mmr_des_io_invert; /* des I/O invert register */
+ unsigned char mmr_unused1[5]; /* unused */
+ unsigned char mmr_dce_status; /* DCE status */
+#define MMR_DCE_STATUS_ENERG_DET 0x01 /* energy detected */
+#define MMR_DCE_STATUS_LOOPT_IND 0x02 /* loop test indicated */
+#define MMR_DCE_STATUS_XMTITR_IND 0x04 /* transmitter on */
+#define MMR_DCE_STATUS_JBR_EXPIRED 0x08 /* jabber timer expired */
+ unsigned char mmr_unused2[3]; /* unused */
+ unsigned char mmr_correct_nwid_l; /* no. of correct NWID's rxd (low) */
+ unsigned char mmr_correct_nwid_h; /* no. of correct NWID's rxd (high) */
+ unsigned char mmr_wrong_nwid_l; /* count of wrong NWID's received (low) */
+ unsigned char mmr_wrong_nwid_h; /* count of wrong NWID's received (high) */
+ unsigned char mmr_thr_pre_set; /* level threshold preset */
+ unsigned char mmr_signal_lvl; /* signal level */
+ unsigned char mmr_silence_lvl; /* silence level */
+ unsigned char mmr_sgnl_qual; /* signal quality */
+#define MMR_SGNL_QUAL_0 0x01 /* signal quality 0 */
+#define MMR_SGNL_QUAL_1 0x02 /* signal quality 1 */
+#define MMR_SGNL_QUAL_2 0x04 /* signal quality 2 */
+#define MMR_SGNL_QUAL_3 0x08 /* signal quality 3 */
+#define MMR_SGNL_QUAL_S_A 0x80 /* currently selected antenna */
+ unsigned char mmr_netw_id_l; /* NWID low order byte ??? */
+ unsigned char mmr_unused3[1]; /* unused */
+};
+#if STRUCT_CHECK == 1
+#define MMR_SIZE 30
+#endif /* STRUCT_CHECK == 1 */
+
+#define MMR_LEVEL_MASK 0x3F
+
+#define mmroff(p,f) (unsigned short)((void *)(&((mmr_t *)((void *)0 + (p)))->f) - (void *)0)
+
+/*
+ * Host Adaptor structure.
+ * (base is board port address).
+ */
+typedef union hacs_u hacs_u;
+union hacs_u
+{
+ unsigned short hu_command; /* Command register */
+#define HACR_RESET 0x0001 /* Reset board */
+#define HACR_CA 0x0002 /* Set Channel Attention for 82586 */
+#define HACR_16BITS 0x0004 /* 16 bits operation (0 => 8bits) */
+#define HACR_OUT0 0x0008 /* General purpose output pin 0 */
+ /* not used - must be 1 */
+#define HACR_OUT1 0x0010 /* General purpose output pin 1 */
+ /* not used - must be 1 */
+#define HACR_82586_INT_ENABLE 0x0020 /* Enable 82586 interrupts */
+#define HACR_MMC_INT_ENABLE 0x0040 /* Enable MMC interrupts */
+#define HACR_INTR_CLR_ENABLE 0x0080 /* Enable interrupt status read/clear */
+ unsigned short hu_status; /* Status Register */
+#define HASR_82586_INTR 0x0001 /* Interrupt request from 82586 */
+#define HASR_MMC_INTR 0x0002 /* Interrupt request from MMC */
+#define HASR_MMC_BUSY 0x0004 /* MMC busy indication */
+#define HASR_PSA_BUSY 0x0008 /* LAN parameter storage area busy */
+};
+
+typedef struct ha_t ha_t;
+struct ha_t
+{
+ hacs_u ha_cs; /* Command and status registers */
+#define ha_command ha_cs.hu_command
+#define ha_status ha_cs.hu_status
+ unsigned short ha_mmcr; /* Modem Management Ctrl Register */
+ unsigned short ha_pior0; /* Program I/O Address Register Port 0 */
+ unsigned short ha_piop0; /* Program I/O Port 0 */
+ unsigned short ha_pior1; /* Program I/O Address Register Port 1 */
+ unsigned short ha_piop1; /* Program I/O Port 1 */
+ unsigned short ha_pior2; /* Program I/O Address Register Port 2 */
+ unsigned short ha_piop2; /* Program I/O Port 2 */
+};
+#if STRUCT_CHECK == 1
+#define HA_SIZE 16
+#endif /* STRUCT_CHECK == 1 */
+
+#define hoff(p,f) (unsigned short)((void *)(&((ha_t *)((void *)0 + (p)))->f) - (void *)0)
+#define HACR(p) hoff(p, ha_command)
+#define HASR(p) hoff(p, ha_status)
+#define MMCR(p) hoff(p, ha_mmcr)
+#define PIOR0(p) hoff(p, ha_pior0)
+#define PIOP0(p) hoff(p, ha_piop0)
+#define PIOR1(p) hoff(p, ha_pior1)
+#define PIOP1(p) hoff(p, ha_piop1)
+#define PIOR2(p) hoff(p, ha_pior2)
+#define PIOP2(p) hoff(p, ha_piop2)
+
+/*
+ * Program I/O Mode Register values.
+ */
+#define STATIC_PIO 0 /* Mode 1: static mode */
+ /* RAM access ??? */
+#define AUTOINCR_PIO 1 /* Mode 2: auto increment mode */
+ /* RAM access ??? */
+#define AUTODECR_PIO 2 /* Mode 3: auto decrement mode */
+ /* RAM access ??? */
+#define PARAM_ACCESS_PIO 3 /* Mode 4: LAN parameter access mode */
+ /* Parameter access. */
+#define PIO_MASK 3 /* register mask */
+#define PIOM(cmd,piono) ((u_short)cmd << 10 << (piono * 2))
+
+#define HACR_DEFAULT (HACR_OUT0 | HACR_OUT1 | HACR_16BITS | PIOM(STATIC_PIO, 0) | PIOM(AUTOINCR_PIO, 1) | PIOM(PARAM_ACCESS_PIO, 2))
+#define HACR_INTRON (HACR_82586_INT_ENABLE | HACR_MMC_INT_ENABLE | HACR_INTR_CLR_ENABLE)
+
+#define MAXDATAZ (WAVELAN_ADDR_SIZE + WAVELAN_ADDR_SIZE + 2 + WAVELAN_MTU)
+
+/*
+ * Onboard 64k RAM layout.
+ * (Offsets from 0x0000.)
+ */
+#define OFFSET_RU 0x0000
+#define OFFSET_CU 0x8000
+#define OFFSET_SCB (OFFSET_ISCP - sizeof(scb_t))
+#define OFFSET_ISCP (OFFSET_SCP - sizeof(iscp_t))
+#define OFFSET_SCP I82586_SCP_ADDR
+
+#define RXBLOCKZ (sizeof(fd_t) + sizeof(rbd_t) + MAXDATAZ)
+#define TXBLOCKZ (sizeof(ac_tx_t) + sizeof(ac_nop_t) + sizeof(tbd_t) + MAXDATAZ)
+
+#define NRXBLOCKS ((OFFSET_CU - OFFSET_RU) / RXBLOCKZ)
+#define NTXBLOCKS ((OFFSET_SCB - OFFSET_CU) / TXBLOCKZ)
+
+/*
+ * This software may only be used and distributed
+ * according to the terms of the GNU Public License.
+ *
+ * For more details, see wavelan.c.
+ */
diff --git a/i386/i386at/gpl/linux/net/wd.c b/i386/i386at/gpl/linux/net/wd.c
new file mode 100644
index 00000000..5eaa6585
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/wd.c
@@ -0,0 +1,513 @@
+/* wd.c: A WD80x3 ethernet driver for linux. */
+/*
+ Written 1993-94 by Donald Becker.
+
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency.
+
+ This software may be used and distributed according to the terms
+ of the GNU Public License, incorporated herein by reference.
+
+ The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
+ Center of Excellence in Space Data and Information Sciences
+ Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
+
+ This is a driver for WD8003 and WD8013 "compatible" ethercards.
+
+ Thanks to Russ Nelson (nelson@crnwyr.com) for loaning me a WD8013.
+
+ Changelog:
+
+ Paul Gortmaker : multiple card support for module users, support
+ for non-standard memory sizes.
+
+
+*/
+
+static const char *version =
+ "wd.c:v1.10 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include "8390.h"
+
+/* A zero-terminated list of I/O addresses to be probed. */
+static unsigned int wd_portlist[] =
+{0x300, 0x280, 0x380, 0x240, 0};
+
+int wd_probe(struct device *dev);
+int wd_probe1(struct device *dev, int ioaddr);
+
+static int wd_open(struct device *dev);
+static void wd_reset_8390(struct device *dev);
+static void wd_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
+ int ring_page);
+static void wd_block_input(struct device *dev, int count,
+ struct sk_buff *skb, int ring_offset);
+static void wd_block_output(struct device *dev, int count,
+ const unsigned char *buf, const start_page);
+static int wd_close_card(struct device *dev);
+
+
+#define WD_START_PG 0x00 /* First page of TX buffer */
+#define WD03_STOP_PG 0x20 /* Last page +1 of RX ring */
+#define WD13_STOP_PG 0x40 /* Last page +1 of RX ring */
+
+#define WD_CMDREG 0 /* Offset to ASIC command register. */
+#define WD_RESET 0x80 /* Board reset, in WD_CMDREG. */
+#define WD_MEMENB 0x40 /* Enable the shared memory. */
+#define WD_CMDREG5 5 /* Offset to 16-bit-only ASIC register 5. */
+#define ISA16 0x80 /* Enable 16 bit access from the ISA bus. */
+#define NIC16 0x40 /* Enable 16 bit access from the 8390. */
+#define WD_NIC_OFFSET 16 /* Offset to the 8390 from the base_addr. */
+#define WD_IO_EXTENT 32
+
+
+/* Probe for the WD8003 and WD8013. These cards have the station
+ address PROM at I/O ports <base>+8 to <base>+13, with a checksum
+ following. A Soundblaster can have the same checksum as an WDethercard,
+ so we have an extra exclusionary check for it.
+
+ The wd_probe1() routine initializes the card and fills the
+ station address field. */
+
+#ifdef HAVE_DEVLIST
+struct netdev_entry wd_drv =
+{"wd", wd_probe1, WD_IO_EXTENT, wd_portlist};
+#else
+
+int wd_probe(struct device *dev)
+{
+ int i;
+ int base_addr = dev ? dev->base_addr : 0;
+
+ if (base_addr > 0x1ff) /* Check a single specified location. */
+ return wd_probe1(dev, base_addr);
+ else if (base_addr != 0) /* Don't probe at all. */
+ return ENXIO;
+
+ for (i = 0; wd_portlist[i]; i++) {
+ int ioaddr = wd_portlist[i];
+ if (check_region(ioaddr, WD_IO_EXTENT))
+ continue;
+ if (wd_probe1(dev, ioaddr) == 0)
+ return 0;
+ }
+
+ return ENODEV;
+}
+#endif
+
+int wd_probe1(struct device *dev, int ioaddr)
+{
+ int i;
+ int checksum = 0;
+ int ancient = 0; /* An old card without config registers. */
+ int word16 = 0; /* 0 = 8 bit, 1 = 16 bit */
+ const char *model_name;
+ static unsigned version_printed = 0;
+
+ for (i = 0; i < 8; i++)
+ checksum += inb(ioaddr + 8 + i);
+ if (inb(ioaddr + 8) == 0xff /* Extra check to avoid soundcard. */
+ || inb(ioaddr + 9) == 0xff
+ || (checksum & 0xff) != 0xFF)
+ return ENODEV;
+
+ /* We should have a "dev" from Space.c or the static module table. */
+ if (dev == NULL) {
+ printk("wd.c: Passed a NULL device.\n");
+ dev = init_etherdev(0, 0);
+ }
+
+ /* Check for semi-valid mem_start/end values if supplied. */
+ if ((dev->mem_start % 0x2000) || (dev->mem_end % 0x2000)) {
+ printk(KERN_WARNING "wd.c: user supplied mem_start or mem_end not on 8kB boundary - ignored.\n");
+ dev->mem_start = 0;
+ dev->mem_end = 0;
+ }
+
+ if (ei_debug && version_printed++ == 0)
+ printk(version);
+
+ printk("%s: WD80x3 at %#3x, ", dev->name, ioaddr);
+ for (i = 0; i < 6; i++)
+ printk(" %2.2X", dev->dev_addr[i] = inb(ioaddr + 8 + i));
+
+ /* The following PureData probe code was contributed by
+ Mike Jagdis <jaggy@purplet.demon.co.uk>. Puredata does software
+ configuration differently from others so we have to check for them.
+ This detects an 8 bit, 16 bit or dumb (Toshiba, jumpered) card.
+ */
+ if (inb(ioaddr+0) == 'P' && inb(ioaddr+1) == 'D') {
+ unsigned char reg5 = inb(ioaddr+5);
+
+ switch (inb(ioaddr+2)) {
+ case 0x03: word16 = 0; model_name = "PDI8023-8"; break;
+ case 0x05: word16 = 0; model_name = "PDUC8023"; break;
+ case 0x0a: word16 = 1; model_name = "PDI8023-16"; break;
+ /* Either 0x01 (dumb) or they've released a new version. */
+ default: word16 = 0; model_name = "PDI8023"; break;
+ }
+ dev->mem_start = ((reg5 & 0x1c) + 0xc0) << 12;
+ dev->irq = (reg5 & 0xe0) == 0xe0 ? 10 : (reg5 >> 5) + 1;
+ } else { /* End of PureData probe */
+ /* This method of checking for a 16-bit board is borrowed from the
+ we.c driver. A simpler method is just to look in ASIC reg. 0x03.
+ I'm comparing the two method in alpha test to make certain they
+ return the same result. */
+ /* Check for the old 8 bit board - it has register 0/8 aliasing.
+ Do NOT check i>=6 here -- it hangs the old 8003 boards! */
+ for (i = 0; i < 6; i++)
+ if (inb(ioaddr+i) != inb(ioaddr+8+i))
+ break;
+ if (i >= 6) {
+ ancient = 1;
+ model_name = "WD8003-old";
+ word16 = 0;
+ } else {
+ int tmp = inb(ioaddr+1); /* fiddle with 16bit bit */
+ outb( tmp ^ 0x01, ioaddr+1 ); /* attempt to clear 16bit bit */
+ if (((inb( ioaddr+1) & 0x01) == 0x01) /* A 16 bit card */
+ && (tmp & 0x01) == 0x01 ) { /* In a 16 slot. */
+ int asic_reg5 = inb(ioaddr+WD_CMDREG5);
+ /* Magic to set ASIC to word-wide mode. */
+ outb( NIC16 | (asic_reg5&0x1f), ioaddr+WD_CMDREG5);
+ outb(tmp, ioaddr+1);
+ model_name = "WD8013";
+ word16 = 1; /* We have a 16bit board here! */
+ } else {
+ model_name = "WD8003";
+ word16 = 0;
+ }
+ outb(tmp, ioaddr+1); /* Restore original reg1 value. */
+ }
+#ifndef final_version
+ if ( !ancient && (inb(ioaddr+1) & 0x01) != (word16 & 0x01))
+ printk("\nWD80?3: Bus width conflict, %d (probe) != %d (reg report).",
+ word16 ? 16 : 8, (inb(ioaddr+1) & 0x01) ? 16 : 8);
+#endif
+ }
+
+#if defined(WD_SHMEM) && WD_SHMEM > 0x80000
+ /* Allow a compile-time override. */
+ dev->mem_start = WD_SHMEM;
+#else
+ if (dev->mem_start == 0) {
+ /* Sanity and old 8003 check */
+ int reg0 = inb(ioaddr);
+ if (reg0 == 0xff || reg0 == 0) {
+ /* Future plan: this could check a few likely locations first. */
+ dev->mem_start = 0xd0000;
+ printk(" assigning address %#lx", dev->mem_start);
+ } else {
+ int high_addr_bits = inb(ioaddr+WD_CMDREG5) & 0x1f;
+ /* Some boards don't have the register 5 -- it returns 0xff. */
+ if (high_addr_bits == 0x1f || word16 == 0)
+ high_addr_bits = 0x01;
+ dev->mem_start = ((reg0&0x3f) << 13) + (high_addr_bits << 19);
+ }
+ }
+#endif
+
+ /* The 8390 isn't at the base address -- the ASIC regs are there! */
+ dev->base_addr = ioaddr+WD_NIC_OFFSET;
+
+ if (dev->irq < 2) {
+ int irqmap[] = {9,3,5,7,10,11,15,4};
+ int reg1 = inb(ioaddr+1);
+ int reg4 = inb(ioaddr+4);
+ if (ancient || reg1 == 0xff) { /* Ack!! No way to read the IRQ! */
+ short nic_addr = ioaddr+WD_NIC_OFFSET;
+
+ /* We have an old-style ethercard that doesn't report its IRQ
+ line. Do autoirq to find the IRQ line. Note that this IS NOT
+ a reliable way to trigger an interrupt. */
+ outb_p(E8390_NODMA + E8390_STOP, nic_addr);
+ outb(0x00, nic_addr+EN0_IMR); /* Disable all intrs. */
+ autoirq_setup(0);
+ outb_p(0xff, nic_addr + EN0_IMR); /* Enable all interrupts. */
+ outb_p(0x00, nic_addr + EN0_RCNTLO);
+ outb_p(0x00, nic_addr + EN0_RCNTHI);
+ outb(E8390_RREAD+E8390_START, nic_addr); /* Trigger it... */
+ dev->irq = autoirq_report(2);
+ outb_p(0x00, nic_addr+EN0_IMR); /* Mask all intrs. again. */
+
+ if (ei_debug > 2)
+ printk(" autoirq is %d", dev->irq);
+ if (dev->irq < 2)
+ dev->irq = word16 ? 10 : 5;
+ } else
+ dev->irq = irqmap[((reg4 >> 5) & 0x03) + (reg1 & 0x04)];
+ } else if (dev->irq == 2) /* Fixup bogosity: IRQ2 is really IRQ9 */
+ dev->irq = 9;
+
+ /* Snarf the interrupt now. There's no point in waiting since we cannot
+ share and the board will usually be enabled. */
+ if (request_irq(dev->irq, ei_interrupt, 0, model_name)) {
+ printk (" unable to get IRQ %d.\n", dev->irq);
+ return EAGAIN;
+ }
+
+ /* Allocate dev->priv and fill in 8390 specific dev fields. */
+ if (ethdev_init(dev)) {
+ printk (" unable to get memory for dev->priv.\n");
+ free_irq(dev->irq);
+ return -ENOMEM;
+ }
+
+ /* OK, were are certain this is going to work. Setup the device. */
+ request_region(ioaddr, WD_IO_EXTENT, model_name);
+
+ ei_status.name = model_name;
+ ei_status.word16 = word16;
+ ei_status.tx_start_page = WD_START_PG;
+ ei_status.rx_start_page = WD_START_PG + TX_PAGES;
+
+ /* Don't map in the shared memory until the board is actually opened. */
+ dev->rmem_start = dev->mem_start + TX_PAGES*256;
+
+ /* Some cards (eg WD8003EBT) can be jumpered for more (32k!) memory. */
+ if (dev->mem_end != 0) {
+ ei_status.stop_page = (dev->mem_end - dev->mem_start)/256;
+ } else {
+ ei_status.stop_page = word16 ? WD13_STOP_PG : WD03_STOP_PG;
+ dev->mem_end = dev->mem_start + (ei_status.stop_page - WD_START_PG)*256;
+ }
+ dev->rmem_end = dev->mem_end;
+
+ printk(" %s, IRQ %d, shared memory at %#lx-%#lx.\n",
+ model_name, dev->irq, dev->mem_start, dev->mem_end-1);
+
+ ei_status.reset_8390 = &wd_reset_8390;
+ ei_status.block_input = &wd_block_input;
+ ei_status.block_output = &wd_block_output;
+ ei_status.get_8390_hdr = &wd_get_8390_hdr;
+ dev->open = &wd_open;
+ dev->stop = &wd_close_card;
+ NS8390_init(dev, 0);
+
+#if 1
+ /* Enable interrupt generation on softconfig cards -- M.U */
+ /* .. but possibly potentially unsafe - Donald */
+ if (inb(ioaddr+14) & 0x20)
+ outb(inb(ioaddr+4)|0x80, ioaddr+4);
+#endif
+
+ return 0;
+}
+
+static int
+wd_open(struct device *dev)
+{
+ int ioaddr = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
+
+ /* Map in the shared memory. Always set register 0 last to remain
+ compatible with very old boards. */
+ ei_status.reg0 = ((dev->mem_start>>13) & 0x3f) | WD_MEMENB;
+ ei_status.reg5 = ((dev->mem_start>>19) & 0x1f) | NIC16;
+
+ if (ei_status.word16)
+ outb(ei_status.reg5, ioaddr+WD_CMDREG5);
+ outb(ei_status.reg0, ioaddr); /* WD_CMDREG */
+
+ ei_open(dev);
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static void
+wd_reset_8390(struct device *dev)
+{
+ int wd_cmd_port = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
+
+ outb(WD_RESET, wd_cmd_port);
+ if (ei_debug > 1) printk("resetting the WD80x3 t=%lu...", jiffies);
+ ei_status.txing = 0;
+
+ /* Set up the ASIC registers, just in case something changed them. */
+ outb((((dev->mem_start>>13) & 0x3f)|WD_MEMENB), wd_cmd_port);
+ if (ei_status.word16)
+ outb(NIC16 | ((dev->mem_start>>19) & 0x1f), wd_cmd_port+WD_CMDREG5);
+
+ if (ei_debug > 1) printk("reset done\n");
+ return;
+}
+
+/* Grab the 8390 specific header. Similar to the block_input routine, but
+ we don't need to be concerned with ring wrap as the header will be at
+ the start of a page, so we optimize accordingly. */
+
+static void
+wd_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+
+ int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
+ unsigned long hdr_start = dev->mem_start + ((ring_page - WD_START_PG)<<8);
+
+ /* We'll always get a 4 byte header read followed by a packet read, so
+ we enable 16 bit mode before the header, and disable after the body. */
+ if (ei_status.word16)
+ outb(ISA16 | ei_status.reg5, wd_cmdreg+WD_CMDREG5);
+
+#ifdef notdef
+ /* Officially this is what we are doing, but the readl() is faster */
+ memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr));
+#else
+ ((unsigned int*)hdr)[0] = readl(hdr_start);
+#endif
+}
+
+/* Block input and output are easy on shared memory ethercards, and trivial
+ on the Western digital card where there is no choice of how to do it.
+ The only complications are that the ring buffer wraps, and need to map
+ switch between 8- and 16-bit modes. */
+
+static void
+wd_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset)
+{
+ int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
+ unsigned long xfer_start = dev->mem_start + ring_offset - (WD_START_PG<<8);
+
+ if (xfer_start + count > dev->rmem_end) {
+ /* We must wrap the input move. */
+ int semi_count = dev->rmem_end - xfer_start;
+ memcpy_fromio(skb->data, xfer_start, semi_count);
+ count -= semi_count;
+ memcpy_fromio(skb->data + semi_count, dev->rmem_start, count);
+ } else {
+ /* Packet is in one chunk -- we can copy + cksum. */
+ eth_io_copy_and_sum(skb, xfer_start, count, 0);
+ }
+
+ /* Turn off 16 bit access so that reboot works. ISA brain-damage */
+ if (ei_status.word16)
+ outb(ei_status.reg5, wd_cmdreg+WD_CMDREG5);
+}
+
+static void
+wd_block_output(struct device *dev, int count, const unsigned char *buf,
+ int start_page)
+{
+ int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
+ long shmem = dev->mem_start + ((start_page - WD_START_PG)<<8);
+
+
+ if (ei_status.word16) {
+ /* Turn on and off 16 bit access so that reboot works. */
+ outb(ISA16 | ei_status.reg5, wd_cmdreg+WD_CMDREG5);
+ memcpy_toio(shmem, buf, count);
+ outb(ei_status.reg5, wd_cmdreg+WD_CMDREG5);
+ } else
+ memcpy_toio(shmem, buf, count);
+}
+
+
+static int
+wd_close_card(struct device *dev)
+{
+ int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
+
+ if (ei_debug > 1)
+ printk("%s: Shutting down ethercard.\n", dev->name);
+ ei_close(dev);
+
+ /* Change from 16-bit to 8-bit shared memory so reboot works. */
+ if (ei_status.word16)
+ outb(ei_status.reg5, wd_cmdreg + WD_CMDREG5 );
+
+ /* And disable the shared memory. */
+ outb(ei_status.reg0 & ~WD_MEMENB, wd_cmdreg);
+
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+}
+
+
+#ifdef MODULE
+#define MAX_WD_CARDS 4 /* Max number of wd cards per module */
+#define NAMELEN 8 /* # of chars for storing dev->name */
+static char namelist[NAMELEN * MAX_WD_CARDS] = { 0, };
+static struct device dev_wd[MAX_WD_CARDS] = {
+ {
+ NULL, /* assign a chunk of namelist[] below */
+ 0, 0, 0, 0,
+ 0, 0,
+ 0, 0, 0, NULL, NULL
+ },
+};
+
+static int io[MAX_WD_CARDS] = { 0, };
+static int irq[MAX_WD_CARDS] = { 0, };
+static int mem[MAX_WD_CARDS] = { 0, };
+static int mem_end[MAX_WD_CARDS] = { 0, }; /* for non std. mem size */
+
+/* This is set up so that only a single autoprobe takes place per call.
+ISA device autoprobes on a running machine are not recommended. */
+int
+init_module(void)
+{
+ int this_dev, found = 0;
+
+ for (this_dev = 0; this_dev < MAX_WD_CARDS; this_dev++) {
+ struct device *dev = &dev_wd[this_dev];
+ dev->name = namelist+(NAMELEN*this_dev);
+ dev->irq = irq[this_dev];
+ dev->base_addr = io[this_dev];
+ dev->mem_start = mem[this_dev];
+ dev->mem_end = mem_end[this_dev];
+ dev->init = wd_probe;
+ if (io[this_dev] == 0) {
+ if (this_dev != 0) break; /* only autoprobe 1st one */
+ printk(KERN_NOTICE "wd.c: Presently autoprobing (not recommended) for a single card.\n");
+ }
+ if (register_netdev(dev) != 0) {
+ printk(KERN_WARNING "wd.c: No wd80x3 card found (i/o = 0x%x).\n", io[this_dev]);
+ if (found != 0) return 0; /* Got at least one. */
+ return -ENXIO;
+ }
+ found++;
+ }
+
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ int this_dev;
+
+ for (this_dev = 0; this_dev < MAX_WD_CARDS; this_dev++) {
+ struct device *dev = &dev_wd[this_dev];
+ if (dev->priv != NULL) {
+ int ioaddr = dev->base_addr - WD_NIC_OFFSET;
+ kfree(dev->priv);
+ dev->priv = NULL;
+ free_irq(dev->irq);
+ irq2dev_map[dev->irq] = NULL;
+ release_region(ioaddr, WD_IO_EXTENT);
+ unregister_netdev(dev);
+ }
+ }
+}
+#endif /* MODULE */
+
+
+/*
+ * Local variables:
+ * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c wd.c"
+ * version-control: t
+ * tab-width: 4
+ * kept-new-versions: 5
+ * End:
+ */
diff --git a/i386/i386at/gpl/linux/net/znet.c b/i386/i386at/gpl/linux/net/znet.c
new file mode 100644
index 00000000..9f44928e
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/znet.c
@@ -0,0 +1,746 @@
+/* znet.c: An Zenith Z-Note ethernet driver for linux. */
+
+static const char *version = "znet.c:v1.02 9/23/94 becker@cesdis.gsfc.nasa.gov\n";
+
+/*
+ Written by Donald Becker.
+
+ The author may be reached as becker@cesdis.gsfc.nasa.gov.
+ This driver is based on the Linux skeleton driver. The copyright of the
+ skeleton driver is held by the United States Government, as represented
+ by DIRNSA, and it is released under the GPL.
+
+ Thanks to Mike Hollick for alpha testing and suggestions.
+
+ References:
+ The Crynwr packet driver.
+
+ "82593 CSMA/CD Core LAN Controller" Intel datasheet, 1992
+ Intel Microcommunications Databook, Vol. 1, 1990.
+ As usual with Intel, the documentation is incomplete and inaccurate.
+ I had to read the Crynwr packet driver to figure out how to actually
+ use the i82593, and guess at what register bits matched the loosely
+ related i82586.
+
+ Theory of Operation
+
+ The i82593 used in the Zenith Z-Note series operates using two(!) slave
+ DMA channels, one interrupt, and one 8-bit I/O port.
+
+ While there several ways to configure '593 DMA system, I chose the one
+ that seemed commensurate with the highest system performance in the face
+ of moderate interrupt latency: Both DMA channels are configured as
+ recirculating ring buffers, with one channel (#0) dedicated to Rx and
+ the other channel (#1) to Tx and configuration. (Note that this is
+ different than the Crynwr driver, where the Tx DMA channel is initialized
+ before each operation. That approach simplifies operation and Tx error
+ recovery, but requires additional I/O in normal operation and precludes
+ transmit buffer chaining.)
+
+ Both rings are set to 8192 bytes using {TX,RX}_RING_SIZE. This provides
+ a reasonable ring size for Rx, while simplifying DMA buffer allocation --
+ DMA buffers must not cross a 128K boundary. (In truth the size selection
+ was influenced by my lack of '593 documentation. I thus was constrained
+ to use the Crynwr '593 initialization table, which sets the Rx ring size
+ to 8K.)
+
+ Despite my usual low opinion about Intel-designed parts, I must admit
+ that the bulk data handling of the i82593 is a good design for
+ an integrated system, like a laptop, where using two slave DMA channels
+ doesn't pose a problem. I still take issue with using only a single I/O
+ port. In the same controlled environment there are essentially no
+ limitations on I/O space, and using multiple locations would eliminate
+ the need for multiple operations when looking at status registers,
+ setting the Rx ring boundary, or switching to promiscuous mode.
+
+ I also question Zenith's selection of the '593: one of the advertised
+ advantages of earlier Intel parts was that if you figured out the magic
+ initialization incantation you could use the same part on many different
+ network types. Zenith's use of the "FriendlyNet" (sic) connector rather
+ than an on-board transceiver leads me to believe that they were planning
+ to take advantage of this. But, uhmmm, the '593 omits all but ethernet
+ functionality from the serial subsystem.
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+
+#ifndef ZNET_DEBUG
+#define ZNET_DEBUG 1
+#endif
+static unsigned int znet_debug = ZNET_DEBUG;
+
+/* The DMA modes we need aren't in <dma.h>. */
+#define DMA_RX_MODE 0x14 /* Auto init, I/O to mem, ++, demand. */
+#define DMA_TX_MODE 0x18 /* Auto init, Mem to I/O, ++, demand. */
+#define dma_page_eq(ptr1, ptr2) ((long)(ptr1)>>17 == (long)(ptr2)>>17)
+#define DMA_BUF_SIZE 8192
+#define RX_BUF_SIZE 8192
+#define TX_BUF_SIZE 8192
+
+/* Commands to the i82593 channel 0. */
+#define CMD0_CHNL_0 0x00
+#define CMD0_CHNL_1 0x10 /* Switch to channel 1. */
+#define CMD0_NOP (CMD0_CHNL_0)
+#define CMD0_PORT_1 CMD0_CHNL_1
+#define CMD1_PORT_0 1
+#define CMD0_IA_SETUP 1
+#define CMD0_CONFIGURE 2
+#define CMD0_MULTICAST_LIST 3
+#define CMD0_TRANSMIT 4
+#define CMD0_DUMP 6
+#define CMD0_DIAGNOSE 7
+#define CMD0_Rx_ENABLE 8
+#define CMD0_Rx_DISABLE 10
+#define CMD0_Rx_STOP 11
+#define CMD0_RETRANSMIT 12
+#define CMD0_ABORT 13
+#define CMD0_RESET 14
+
+#define CMD0_ACK 0x80
+
+#define CMD0_STAT0 (0 << 5)
+#define CMD0_STAT1 (1 << 5)
+#define CMD0_STAT2 (2 << 5)
+#define CMD0_STAT3 (3 << 5)
+
+#define net_local znet_private
+struct znet_private {
+ int rx_dma, tx_dma;
+ struct enet_statistics stats;
+ /* The starting, current, and end pointers for the packet buffers. */
+ ushort *rx_start, *rx_cur, *rx_end;
+ ushort *tx_start, *tx_cur, *tx_end;
+ ushort tx_buf_len; /* Tx buffer length, in words. */
+};
+
+/* Only one can be built-in;-> */
+static struct znet_private zn;
+static ushort dma_buffer1[DMA_BUF_SIZE/2];
+static ushort dma_buffer2[DMA_BUF_SIZE/2];
+static ushort dma_buffer3[DMA_BUF_SIZE/2 + 8];
+
+/* The configuration block. What an undocumented nightmare. The first
+ set of values are those suggested (without explanation) for ethernet
+ in the Intel 82586 databook. The rest appear to be completely undocumented,
+ except for cryptic notes in the Crynwr packet driver. This driver uses
+ the Crynwr values verbatim. */
+
+static unsigned char i593_init[] = {
+ 0xAA, /* 0: 16-byte input & 80-byte output FIFO. */
+ /* threshold, 96-byte FIFO, 82593 mode. */
+ 0x88, /* 1: Continuous w/interrupts, 128-clock DMA.*/
+ 0x2E, /* 2: 8-byte preamble, NO address insertion, */
+ /* 6-byte Ethernet address, loopback off.*/
+ 0x00, /* 3: Default priorities & backoff methods. */
+ 0x60, /* 4: 96-bit interframe spacing. */
+ 0x00, /* 5: 512-bit slot time (low-order). */
+ 0xF2, /* 6: Slot time (high-order), 15 COLL retries. */
+ 0x00, /* 7: Promisc-off, broadcast-on, default CRC. */
+ 0x00, /* 8: Default carrier-sense, collision-detect. */
+ 0x40, /* 9: 64-byte minimum frame length. */
+ 0x5F, /* A: Type/length checks OFF, no CRC input,
+ "jabber" termination, etc. */
+ 0x00, /* B: Full-duplex disabled. */
+ 0x3F, /* C: Default multicast addresses & backoff. */
+ 0x07, /* D: Default IFS retriggering. */
+ 0x31, /* E: Internal retransmit, drop "runt" packets,
+ synchr. DRQ deassertion, 6 status bytes. */
+ 0x22, /* F: Receive ring-buffer size (8K),
+ receive-stop register enable. */
+};
+
+struct netidblk {
+ char magic[8]; /* The magic number (string) "NETIDBLK" */
+ unsigned char netid[8]; /* The physical station address */
+ char nettype, globalopt;
+ char vendor[8]; /* The machine vendor and product name. */
+ char product[8];
+ char irq1, irq2; /* Interrupts, only one is currently used. */
+ char dma1, dma2;
+ short dma_mem_misc[8]; /* DMA buffer locations (unused in Linux). */
+ short iobase1, iosize1;
+ short iobase2, iosize2; /* Second iobase unused. */
+ char driver_options; /* Misc. bits */
+ char pad;
+};
+
+int znet_probe(struct device *dev);
+static int znet_open(struct device *dev);
+static int znet_send_packet(struct sk_buff *skb, struct device *dev);
+static void znet_interrupt(int irq, struct pt_regs *regs);
+static void znet_rx(struct device *dev);
+static int znet_close(struct device *dev);
+static struct enet_statistics *net_get_stats(struct device *dev);
+static void set_multicast_list(struct device *dev);
+static void hardware_init(struct device *dev);
+static void update_stop_hit(short ioaddr, unsigned short rx_stop_offset);
+
+#ifdef notdef
+static struct sigaction znet_sigaction = { &znet_interrupt, 0, 0, NULL, };
+#endif
+
+
+/* The Z-Note probe is pretty easy. The NETIDBLK exists in the safe-to-probe
+ BIOS area. We just scan for the signature, and pull the vital parameters
+ out of the structure. */
+
+int znet_probe(struct device *dev)
+{
+ int i;
+ struct netidblk *netinfo;
+ char *p;
+
+ /* This code scans the region 0xf0000 to 0xfffff for a "NETIDBLK". */
+ for(p = (char *)0xf0000; p < (char *)0x100000; p++)
+ if (*p == 'N' && strncmp(p, "NETIDBLK", 8) == 0)
+ break;
+
+ if (p >= (char *)0x100000) {
+ if (znet_debug > 1)
+ printk(KERN_INFO "No Z-Note ethernet adaptor found.\n");
+ return ENODEV;
+ }
+ netinfo = (struct netidblk *)p;
+ dev->base_addr = netinfo->iobase1;
+ dev->irq = netinfo->irq1;
+
+ printk(KERN_INFO "%s: ZNET at %#3lx,", dev->name, dev->base_addr);
+
+ /* The station address is in the "netidblk" at 0x0f0000. */
+ for (i = 0; i < 6; i++)
+ printk(" %2.2x", dev->dev_addr[i] = netinfo->netid[i]);
+
+ printk(", using IRQ %d DMA %d and %d.\n", dev->irq, netinfo->dma1,
+ netinfo->dma2);
+
+ if (znet_debug > 1) {
+ printk(KERN_INFO "%s: vendor '%16.16s' IRQ1 %d IRQ2 %d DMA1 %d DMA2 %d.\n",
+ dev->name, netinfo->vendor,
+ netinfo->irq1, netinfo->irq2,
+ netinfo->dma1, netinfo->dma2);
+ printk(KERN_INFO "%s: iobase1 %#x size %d iobase2 %#x size %d net type %2.2x.\n",
+ dev->name, netinfo->iobase1, netinfo->iosize1,
+ netinfo->iobase2, netinfo->iosize2, netinfo->nettype);
+ }
+
+ if (znet_debug > 0)
+ printk("%s%s", KERN_INFO, version);
+
+ dev->priv = (void *) &zn;
+ zn.rx_dma = netinfo->dma1;
+ zn.tx_dma = netinfo->dma2;
+
+ /* These should never fail. You can't add devices to a sealed box! */
+ if (request_irq(dev->irq, &znet_interrupt, 0, "ZNet")
+ || request_dma(zn.rx_dma,"ZNet rx")
+ || request_dma(zn.tx_dma,"ZNet tx")) {
+ printk(KERN_WARNING "%s: Not opened -- resource busy?!?\n", dev->name);
+ return EBUSY;
+ }
+ irq2dev_map[dev->irq] = dev;
+
+ /* Allocate buffer memory. We can cross a 128K boundary, so we
+ must be careful about the allocation. It's easiest to waste 8K. */
+ if (dma_page_eq(dma_buffer1, &dma_buffer1[RX_BUF_SIZE/2-1]))
+ zn.rx_start = dma_buffer1;
+ else
+ zn.rx_start = dma_buffer2;
+
+ if (dma_page_eq(dma_buffer3, &dma_buffer3[RX_BUF_SIZE/2-1]))
+ zn.tx_start = dma_buffer3;
+ else
+ zn.tx_start = dma_buffer2;
+ zn.rx_end = zn.rx_start + RX_BUF_SIZE/2;
+ zn.tx_buf_len = TX_BUF_SIZE/2;
+ zn.tx_end = zn.tx_start + zn.tx_buf_len;
+
+ /* The ZNET-specific entries in the device structure. */
+ dev->open = &znet_open;
+ dev->hard_start_xmit = &znet_send_packet;
+ dev->stop = &znet_close;
+ dev->get_stats = net_get_stats;
+ dev->set_multicast_list = &set_multicast_list;
+
+ /* Fill in the 'dev' with ethernet-generic values. */
+ ether_setup(dev);
+
+ return 0;
+}
+
+
+static int znet_open(struct device *dev)
+{
+ int ioaddr = dev->base_addr;
+
+ if (znet_debug > 2)
+ printk(KERN_DEBUG "%s: znet_open() called.\n", dev->name);
+
+ /* Turn on the 82501 SIA, using zenith-specific magic. */
+ outb(0x10, 0xe6); /* Select LAN control register */
+ outb(inb(0xe7) | 0x84, 0xe7); /* Turn on LAN power (bit 2). */
+ /* According to the Crynwr driver we should wait 50 msec. for the
+ LAN clock to stabilize. My experiments indicates that the '593 can
+ be initialized immediately. The delay is probably needed for the
+ DC-to-DC converter to come up to full voltage, and for the oscillator
+ to be spot-on at 20Mhz before transmitting.
+ Until this proves to be a problem we rely on the higher layers for the
+ delay and save allocating a timer entry. */
+
+ /* This follows the packet driver's lead, and checks for success. */
+ if (inb(ioaddr) != 0x10 && inb(ioaddr) != 0x00)
+ printk(KERN_WARNING "%s: Problem turning on the transceiver power.\n",
+ dev->name);
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ hardware_init(dev);
+ dev->start = 1;
+
+ return 0;
+}
+
+static int znet_send_packet(struct sk_buff *skb, struct device *dev)
+{
+ int ioaddr = dev->base_addr;
+
+ if (znet_debug > 4)
+ printk(KERN_DEBUG "%s: ZNet_send_packet(%ld).\n", dev->name, dev->tbusy);
+
+ /* Transmitter timeout, likely just recovery after suspending the machine. */
+ if (dev->tbusy) {
+ ushort event, tx_status, rx_offset, state;
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < 10)
+ return 1;
+ outb(CMD0_STAT0, ioaddr); event = inb(ioaddr);
+ outb(CMD0_STAT1, ioaddr); tx_status = inw(ioaddr);
+ outb(CMD0_STAT2, ioaddr); rx_offset = inw(ioaddr);
+ outb(CMD0_STAT3, ioaddr); state = inb(ioaddr);
+ printk(KERN_WARNING "%s: transmit timed out, status %02x %04x %04x %02x,"
+ " resetting.\n", dev->name, event, tx_status, rx_offset, state);
+ if (tx_status == 0x0400)
+ printk(KERN_WARNING "%s: Tx carrier error, check transceiver cable.\n",
+ dev->name);
+ outb(CMD0_RESET, ioaddr);
+ hardware_init(dev);
+ }
+
+ if (skb == NULL) {
+ dev_tint(dev);
+ return 0;
+ }
+
+ /* Check that the part hasn't reset itself, probably from suspend. */
+ outb(CMD0_STAT0, ioaddr);
+ if (inw(ioaddr) == 0x0010
+ && inw(ioaddr) == 0x0000
+ && inw(ioaddr) == 0x0010)
+ hardware_init(dev);
+
+ /* Block a timer-based transmit from overlapping. This could better be
+ done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
+ if (set_bit(0, (void*)&dev->tbusy) != 0)
+ printk(KERN_WARNING "%s: Transmitter access conflict.\n", dev->name);
+ else {
+ short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+ unsigned char *buf = (void *)skb->data;
+ ushort *tx_link = zn.tx_cur - 1;
+ ushort rnd_len = (length + 1)>>1;
+
+ {
+ short dma_port = ((zn.tx_dma&3)<<2) + IO_DMA2_BASE;
+ unsigned addr = inb(dma_port);
+ addr |= inb(dma_port) << 8;
+ addr <<= 1;
+ if (((int)zn.tx_cur & 0x1ffff) != addr)
+ printk(KERN_WARNING "Address mismatch at Tx: %#x vs %#x.\n",
+ (int)zn.tx_cur & 0xffff, addr);
+ zn.tx_cur = (ushort *)(((int)zn.tx_cur & 0xfe0000) | addr);
+ }
+
+ if (zn.tx_cur >= zn.tx_end)
+ zn.tx_cur = zn.tx_start;
+ *zn.tx_cur++ = length;
+ if (zn.tx_cur + rnd_len + 1 > zn.tx_end) {
+ int semi_cnt = (zn.tx_end - zn.tx_cur)<<1; /* Cvrt to byte cnt. */
+ memcpy(zn.tx_cur, buf, semi_cnt);
+ rnd_len -= semi_cnt>>1;
+ memcpy(zn.tx_start, buf + semi_cnt, length - semi_cnt);
+ zn.tx_cur = zn.tx_start + rnd_len;
+ } else {
+ memcpy(zn.tx_cur, buf, skb->len);
+ zn.tx_cur += rnd_len;
+ }
+ *zn.tx_cur++ = 0;
+ cli(); {
+ *tx_link = CMD0_TRANSMIT + CMD0_CHNL_1;
+ /* Is this always safe to do? */
+ outb(CMD0_TRANSMIT + CMD0_CHNL_1,ioaddr);
+ } sti();
+
+ dev->trans_start = jiffies;
+ if (znet_debug > 4)
+ printk(KERN_DEBUG "%s: Transmitter queued, length %d.\n", dev->name, length);
+ }
+ dev_kfree_skb(skb, FREE_WRITE);
+ return 0;
+}
+
+/* The ZNET interrupt handler. */
+static void znet_interrupt(int irq, struct pt_regs * regs)
+{
+ struct device *dev = irq2dev_map[irq];
+ int ioaddr;
+ int boguscnt = 20;
+
+ if (dev == NULL) {
+ printk(KERN_WARNING "znet_interrupt(): IRQ %d for unknown device.\n", irq);
+ return;
+ }
+
+ dev->interrupt = 1;
+ ioaddr = dev->base_addr;
+
+ outb(CMD0_STAT0, ioaddr);
+ do {
+ ushort status = inb(ioaddr);
+ if (znet_debug > 5) {
+ ushort result, rx_ptr, running;
+ outb(CMD0_STAT1, ioaddr);
+ result = inw(ioaddr);
+ outb(CMD0_STAT2, ioaddr);
+ rx_ptr = inw(ioaddr);
+ outb(CMD0_STAT3, ioaddr);
+ running = inb(ioaddr);
+ printk(KERN_DEBUG "%s: interrupt, status %02x, %04x %04x %02x serial %d.\n",
+ dev->name, status, result, rx_ptr, running, boguscnt);
+ }
+ if ((status & 0x80) == 0)
+ break;
+
+ if ((status & 0x0F) == 4) { /* Transmit done. */
+ struct net_local *lp = (struct net_local *)dev->priv;
+ int tx_status;
+ outb(CMD0_STAT1, ioaddr);
+ tx_status = inw(ioaddr);
+ /* It's undocumented, but tx_status seems to match the i82586. */
+ if (tx_status & 0x2000) {
+ lp->stats.tx_packets++;
+ lp->stats.collisions += tx_status & 0xf;
+ } else {
+ if (tx_status & 0x0600) lp->stats.tx_carrier_errors++;
+ if (tx_status & 0x0100) lp->stats.tx_fifo_errors++;
+ if (!(tx_status & 0x0040)) lp->stats.tx_heartbeat_errors++;
+ if (tx_status & 0x0020) lp->stats.tx_aborted_errors++;
+ /* ...and the catch-all. */
+ if ((tx_status | 0x0760) != 0x0760)
+ lp->stats.tx_errors++;
+ }
+ dev->tbusy = 0;
+ mark_bh(NET_BH); /* Inform upper layers. */
+ }
+
+ if ((status & 0x40)
+ || (status & 0x0f) == 11) {
+ znet_rx(dev);
+ }
+ /* Clear the interrupts we've handled. */
+ outb(CMD0_ACK,ioaddr);
+ } while (boguscnt--);
+
+ dev->interrupt = 0;
+ return;
+}
+
+static void znet_rx(struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+ int boguscount = 1;
+ short next_frame_end_offset = 0; /* Offset of next frame start. */
+ short *cur_frame_end;
+ short cur_frame_end_offset;
+
+ outb(CMD0_STAT2, ioaddr);
+ cur_frame_end_offset = inw(ioaddr);
+
+ if (cur_frame_end_offset == zn.rx_cur - zn.rx_start) {
+ printk(KERN_WARNING "%s: Interrupted, but nothing to receive, offset %03x.\n",
+ dev->name, cur_frame_end_offset);
+ return;
+ }
+
+ /* Use same method as the Crynwr driver: construct a forward list in
+ the same area of the backwards links we now have. This allows us to
+ pass packets to the upper layers in the order they were received --
+ important for fast-path sequential operations. */
+ while (zn.rx_start + cur_frame_end_offset != zn.rx_cur
+ && ++boguscount < 5) {
+ unsigned short hi_cnt, lo_cnt, hi_status, lo_status;
+ int count, status;
+
+ if (cur_frame_end_offset < 4) {
+ /* Oh no, we have a special case: the frame trailer wraps around
+ the end of the ring buffer. We've saved space at the end of
+ the ring buffer for just this problem. */
+ memcpy(zn.rx_end, zn.rx_start, 8);
+ cur_frame_end_offset += (RX_BUF_SIZE/2);
+ }
+ cur_frame_end = zn.rx_start + cur_frame_end_offset - 4;
+
+ lo_status = *cur_frame_end++;
+ hi_status = *cur_frame_end++;
+ status = ((hi_status & 0xff) << 8) + (lo_status & 0xff);
+ lo_cnt = *cur_frame_end++;
+ hi_cnt = *cur_frame_end++;
+ count = ((hi_cnt & 0xff) << 8) + (lo_cnt & 0xff);
+
+ if (znet_debug > 5)
+ printk(KERN_DEBUG "Constructing trailer at location %03x, %04x %04x %04x %04x"
+ " count %#x status %04x.\n",
+ cur_frame_end_offset<<1, lo_status, hi_status, lo_cnt, hi_cnt,
+ count, status);
+ cur_frame_end[-4] = status;
+ cur_frame_end[-3] = next_frame_end_offset;
+ cur_frame_end[-2] = count;
+ next_frame_end_offset = cur_frame_end_offset;
+ cur_frame_end_offset -= ((count + 1)>>1) + 3;
+ if (cur_frame_end_offset < 0)
+ cur_frame_end_offset += RX_BUF_SIZE/2;
+ };
+
+ /* Now step forward through the list. */
+ do {
+ ushort *this_rfp_ptr = zn.rx_start + next_frame_end_offset;
+ int status = this_rfp_ptr[-4];
+ int pkt_len = this_rfp_ptr[-2];
+
+ if (znet_debug > 5)
+ printk(KERN_DEBUG "Looking at trailer ending at %04x status %04x length %03x"
+ " next %04x.\n", next_frame_end_offset<<1, status, pkt_len,
+ this_rfp_ptr[-3]<<1);
+ /* Once again we must assume that the i82586 docs apply. */
+ if ( ! (status & 0x2000)) { /* There was an error. */
+ lp->stats.rx_errors++;
+ if (status & 0x0800) lp->stats.rx_crc_errors++;
+ if (status & 0x0400) lp->stats.rx_frame_errors++;
+ if (status & 0x0200) lp->stats.rx_over_errors++; /* Wrong. */
+ if (status & 0x0100) lp->stats.rx_fifo_errors++;
+ if (status & 0x0080) lp->stats.rx_length_errors++;
+ } else if (pkt_len > 1536) {
+ lp->stats.rx_length_errors++;
+ } else {
+ /* Malloc up new buffer. */
+ struct sk_buff *skb;
+
+ skb = dev_alloc_skb(pkt_len);
+ if (skb == NULL) {
+ if (znet_debug)
+ printk(KERN_WARNING "%s: Memory squeeze, dropping packet.\n", dev->name);
+ lp->stats.rx_dropped++;
+ break;
+ }
+ skb->dev = dev;
+
+ if (&zn.rx_cur[(pkt_len+1)>>1] > zn.rx_end) {
+ int semi_cnt = (zn.rx_end - zn.rx_cur)<<1;
+ memcpy(skb_put(skb,semi_cnt), zn.rx_cur, semi_cnt);
+ memcpy(skb_put(skb,pkt_len-semi_cnt), zn.rx_start,
+ pkt_len - semi_cnt);
+ } else {
+ memcpy(skb_put(skb,pkt_len), zn.rx_cur, pkt_len);
+ if (znet_debug > 6) {
+ unsigned int *packet = (unsigned int *) skb->data;
+ printk(KERN_DEBUG "Packet data is %08x %08x %08x %08x.\n", packet[0],
+ packet[1], packet[2], packet[3]);
+ }
+ }
+ skb->protocol=eth_type_trans(skb,dev);
+ netif_rx(skb);
+ lp->stats.rx_packets++;
+ }
+ zn.rx_cur = this_rfp_ptr;
+ if (zn.rx_cur >= zn.rx_end)
+ zn.rx_cur -= RX_BUF_SIZE/2;
+ update_stop_hit(ioaddr, (zn.rx_cur - zn.rx_start)<<1);
+ next_frame_end_offset = this_rfp_ptr[-3];
+ if (next_frame_end_offset == 0) /* Read all the frames? */
+ break; /* Done for now */
+ this_rfp_ptr = zn.rx_start + next_frame_end_offset;
+ } while (--boguscount);
+
+ /* If any worth-while packets have been received, dev_rint()
+ has done a mark_bh(INET_BH) for us and will work on them
+ when we get to the bottom-half routine. */
+ return;
+}
+
+/* The inverse routine to znet_open(). */
+static int znet_close(struct device *dev)
+{
+ int ioaddr = dev->base_addr;
+
+ dev->tbusy = 1;
+ dev->start = 0;
+
+ outb(CMD0_RESET, ioaddr); /* CMD0_RESET */
+
+ disable_dma(zn.rx_dma);
+ disable_dma(zn.tx_dma);
+
+ free_irq(dev->irq);
+
+ if (znet_debug > 1)
+ printk(KERN_DEBUG "%s: Shutting down ethercard.\n", dev->name);
+ /* Turn off transceiver power. */
+ outb(0x10, 0xe6); /* Select LAN control register */
+ outb(inb(0xe7) & ~0x84, 0xe7); /* Turn on LAN power (bit 2). */
+
+ return 0;
+}
+
+/* Get the current statistics. This may be called with the card open or
+ closed. */
+static struct enet_statistics *net_get_stats(struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+
+ return &lp->stats;
+}
+
+/* Set or clear the multicast filter for this adaptor.
+ As a side effect this routine must also initialize the device parameters.
+ This is taken advantage of in open().
+
+ N.B. that we change i593_init[] in place. This (properly) makes the
+ mode change persistent, but must be changed if this code is moved to
+ a multiple adaptor environment.
+ */
+static void set_multicast_list(struct device *dev)
+{
+ short ioaddr = dev->base_addr;
+
+ if (dev->flags&IFF_PROMISC) {
+ /* Enable promiscuous mode */
+ i593_init[7] &= ~3; i593_init[7] |= 1;
+ i593_init[13] &= ~8; i593_init[13] |= 8;
+ } else if (dev->mc_list || (dev->flags&IFF_ALLMULTI)) {
+ /* Enable accept-all-multicast mode */
+ i593_init[7] &= ~3; i593_init[7] |= 0;
+ i593_init[13] &= ~8; i593_init[13] |= 8;
+ } else { /* Enable normal mode. */
+ i593_init[7] &= ~3; i593_init[7] |= 0;
+ i593_init[13] &= ~8; i593_init[13] |= 0;
+ }
+ *zn.tx_cur++ = sizeof(i593_init);
+ memcpy(zn.tx_cur, i593_init, sizeof(i593_init));
+ zn.tx_cur += sizeof(i593_init)/2;
+ outb(CMD0_CONFIGURE+CMD0_CHNL_1, ioaddr);
+#ifdef not_tested
+ if (num_addrs > 0) {
+ int addrs_len = 6*num_addrs;
+ *zn.tx_cur++ = addrs_len;
+ memcpy(zn.tx_cur, addrs, addrs_len);
+ outb(CMD0_MULTICAST_LIST+CMD0_CHNL_1, ioaddr);
+ zn.tx_cur += addrs_len>>1;
+ }
+#endif
+}
+
+void show_dma(void)
+{
+ short dma_port = ((zn.tx_dma&3)<<2) + IO_DMA2_BASE;
+ unsigned addr = inb(dma_port);
+ addr |= inb(dma_port) << 8;
+ printk("Addr: %04x cnt:%3x...", addr<<1, get_dma_residue(zn.tx_dma));
+}
+
+/* Initialize the hardware. We have to do this when the board is open()ed
+ or when we come out of suspend mode. */
+static void hardware_init(struct device *dev)
+{
+ short ioaddr = dev->base_addr;
+
+ zn.rx_cur = zn.rx_start;
+ zn.tx_cur = zn.tx_start;
+
+ /* Reset the chip, and start it up. */
+ outb(CMD0_RESET, ioaddr);
+
+ cli(); { /* Protect against a DMA flip-flop */
+ disable_dma(zn.rx_dma); /* reset by an interrupting task. */
+ clear_dma_ff(zn.rx_dma);
+ set_dma_mode(zn.rx_dma, DMA_RX_MODE);
+ set_dma_addr(zn.rx_dma, (unsigned int) zn.rx_start);
+ set_dma_count(zn.rx_dma, RX_BUF_SIZE);
+ enable_dma(zn.rx_dma);
+ /* Now set up the Tx channel. */
+ disable_dma(zn.tx_dma);
+ clear_dma_ff(zn.tx_dma);
+ set_dma_mode(zn.tx_dma, DMA_TX_MODE);
+ set_dma_addr(zn.tx_dma, (unsigned int) zn.tx_start);
+ set_dma_count(zn.tx_dma, zn.tx_buf_len<<1);
+ enable_dma(zn.tx_dma);
+ } sti();
+
+ if (znet_debug > 1)
+ printk(KERN_DEBUG "%s: Initializing the i82593, tx buf %p... ", dev->name,
+ zn.tx_start);
+ /* Do an empty configure command, just like the Crynwr driver. This
+ resets to chip to its default values. */
+ *zn.tx_cur++ = 0;
+ *zn.tx_cur++ = 0;
+ printk("stat:%02x ", inb(ioaddr)); show_dma();
+ outb(CMD0_CONFIGURE+CMD0_CHNL_1, ioaddr);
+ *zn.tx_cur++ = sizeof(i593_init);
+ memcpy(zn.tx_cur, i593_init, sizeof(i593_init));
+ zn.tx_cur += sizeof(i593_init)/2;
+ printk("stat:%02x ", inb(ioaddr)); show_dma();
+ outb(CMD0_CONFIGURE+CMD0_CHNL_1, ioaddr);
+ *zn.tx_cur++ = 6;
+ memcpy(zn.tx_cur, dev->dev_addr, 6);
+ zn.tx_cur += 3;
+ printk("stat:%02x ", inb(ioaddr)); show_dma();
+ outb(CMD0_IA_SETUP + CMD0_CHNL_1, ioaddr);
+ printk("stat:%02x ", inb(ioaddr)); show_dma();
+
+ update_stop_hit(ioaddr, 8192);
+ if (znet_debug > 1) printk("enabling Rx.\n");
+ outb(CMD0_Rx_ENABLE+CMD0_CHNL_0, ioaddr);
+ dev->tbusy = 0;
+}
+
+static void update_stop_hit(short ioaddr, unsigned short rx_stop_offset)
+{
+ outb(CMD0_PORT_1, ioaddr);
+ if (znet_debug > 5)
+ printk(KERN_DEBUG "Updating stop hit with value %02x.\n",
+ (rx_stop_offset >> 6) | 0x80);
+ outb((rx_stop_offset >> 6) | 0x80, ioaddr);
+ outb(CMD1_PORT_0, ioaddr);
+}
+
+/*
+ * Local variables:
+ * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c znet.c"
+ * version-control: t
+ * kept-new-versions: 5
+ * c-indent-level: 4
+ * tab-width: 4
+ * End:
+ */