diff options
Diffstat (limited to 'i386/i386at/gpl/linux/net/tulip.c')
-rw-r--r-- | i386/i386at/gpl/linux/net/tulip.c | 782 |
1 files changed, 782 insertions, 0 deletions
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: + */ |