summaryrefslogtreecommitdiffstats
path: root/drivers/net/tulip.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/tulip.c')
-rw-r--r--drivers/net/tulip.c737
1 files changed, 737 insertions, 0 deletions
diff --git a/drivers/net/tulip.c b/drivers/net/tulip.c
new file mode 100644
index 000000000..4a4cde71e
--- /dev/null
+++ b/drivers/net/tulip.c
@@ -0,0 +1,737 @@
+/* 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 char *version = "tulip.c:v0.05 1/20/95 becker@cesdis.gsfc.nasa.gov\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>
+
+/* This will be in linux/etherdevice.h someday. */
+struct device *init_etherdev(struct device *dev, int sizeof_private,
+ unsigned long *mem_startp);
+
+/* 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 unsigned long tulip_probe1(unsigned long mem_start, 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, int num_addrs, void *addrs);
+static int set_mac_address(struct device *dev, void *addr);
+
+
+
+/* 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. */
+
+unsigned long dec21040_init(unsigned long mem_start, unsigned long mem_end)
+{
+
+ 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);
+ mem_start = tulip_probe1(mem_start, pci_ioaddr, pci_irq_line);
+ }
+ }
+
+ return mem_start;
+}
+
+unsigned long tulip_probe1(unsigned long mem_start, 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, sizeof(struct tulip_private)
+ + PKT_BUF_SZ*RX_RING_SIZE,
+ &mem_start);
+
+ 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. */
+ dev->priv = (void *)(((int)dev->priv + 7) & ~7);
+ tp = (struct tulip_private *)dev->priv;
+ tp->rx_buffs = (long)dev->priv + sizeof(struct tulip_private);
+
+ /* 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;
+#ifdef HAVE_MULTICAST
+ dev->set_multicast_list = &set_multicast_list;
+#endif
+#ifdef HAVE_SET_MAC_ADDR
+ dev->set_mac_address = &set_mac_address;
+#endif
+
+ return mem_start;
+}
+
+
+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));
+ }
+ 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 = alloc_skb(pkt_len, GFP_ATOMIC);
+ 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->len = pkt_len;
+ skb->dev = dev;
+ memcpy(skb->data, 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;
+
+ 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.
+ 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, int num_addrs, void *addrs)
+{
+ short ioaddr = dev->base_addr;
+ int csr6 = inl(ioaddr + CSR6) & ~0x00D5;
+
+ if (num_addrs > 15) {
+ /* Too many to filter perfectly -- accept all multicasts. */
+ outl(csr6 | 0x0080, ioaddr + CSR6);
+ } else if (num_addrs < 0) { /* Set promiscuous. */
+ outl(csr6 | 0x00C0, ioaddr + CSR6);
+ /* Log any net taps. */
+ printk("%s: Promiscuous mode enabled.\n", dev->name);
+ } else {
+ struct tulip_private *tp = (struct tulip_private *)dev->priv;
+ int *setup_frm = tp->setup_frame;
+ unsigned short *eaddrs = addrs;
+ 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);
+ for(i = 0; i < num_addrs; 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;
+ 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] = ((unsigned char *)addr)[i]);
+ printk(".\n");
+ return 0;
+}
+
+
+/*
+ * 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:
+ */