845 lines
26 KiB
C
845 lines
26 KiB
C
|
/* lance.c: An AMD LANCE ethernet driver for linux. */
|
|||
|
/*
|
|||
|
Written 1993 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@super.org or
|
|||
|
C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
|
|||
|
*/
|
|||
|
|
|||
|
static char *version = "lance.c:v0.14g 12/21/93 becker@super.org\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 <asm/bitops.h>
|
|||
|
#include <asm/io.h>
|
|||
|
#include <asm/dma.h>
|
|||
|
|
|||
|
#include "dev.h"
|
|||
|
#include "eth.h"
|
|||
|
#include "skbuff.h"
|
|||
|
#include "arp.h"
|
|||
|
|
|||
|
#ifndef HAVE_PORTRESERVE
|
|||
|
#define check_region(addr, size) 0
|
|||
|
#define snarf_region(addr, size) 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
|
|||
|
|
|||
|
struct device *init_etherdev(struct device *dev, int sizeof_private,
|
|||
|
unsigned long *mem_startp);
|
|||
|
|
|||
|
#ifdef LANCE_DEBUG
|
|||
|
int lance_debug = LANCE_DEBUG;
|
|||
|
#else
|
|||
|
int lance_debug = 1;
|
|||
|
#endif
|
|||
|
|
|||
|
#ifndef LANCE_DMA
|
|||
|
#define LANCE_DMA 5
|
|||
|
#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. For convenience, I use the name
|
|||
|
LANCE to refer to either AMD chip.
|
|||
|
|
|||
|
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 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 an DMA-timeout interrupt and uses
|
|||
|
autoIRQ to find the IRQ line. The DMA channel defaults to LANCE_DMA, or it
|
|||
|
can be set with the low bits of the otherwise-unused dev->mem_start value.
|
|||
|
|
|||
|
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, but that entry is for changing 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 an 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
|
|||
|
accessable 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 2's complement (negative)! */
|
|||
|
short msg_length; /* This length is "normal". */
|
|||
|
};
|
|||
|
|
|||
|
struct lance_tx_head {
|
|||
|
int base;
|
|||
|
short length; /* Length is 2's 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 {
|
|||
|
char devname[8];
|
|||
|
/* These must aligned on 8-byte boundaries. */
|
|||
|
struct lance_rx_head rx_ring[RX_RING_SIZE];
|
|||
|
struct lance_tx_head tx_ring[TX_RING_SIZE];
|
|||
|
struct lance_init_block init_block;
|
|||
|
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;
|
|||
|
char old_lance;
|
|||
|
int pad0, pad1; /* Used for alignment */
|
|||
|
};
|
|||
|
|
|||
|
static unsigned long lance_probe1(short ioaddr, unsigned long mem_start);
|
|||
|
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 reg_ptr);
|
|||
|
static int lance_close(struct device *dev);
|
|||
|
static struct enet_statistics *lance_get_stats(struct device *dev);
|
|||
|
#ifdef HAVE_MULTICAST
|
|||
|
static void set_multicast_list(struct device *dev, int num_addrs, void *addrs);
|
|||
|
#endif
|
|||
|
|
|||
|
|
|||
|
|
|||
|
unsigned long lance_init(unsigned long mem_start, unsigned long mem_end)
|
|||
|
{
|
|||
|
int *port, ports[] = {0x300, 0x320, 0x340, 0x360, 0};
|
|||
|
|
|||
|
for (port = &ports[0]; *port; port++) {
|
|||
|
int ioaddr = *port;
|
|||
|
|
|||
|
if ( check_region(ioaddr, LANCE_TOTAL_SIZE) == 0
|
|||
|
&& inb(ioaddr + 14) == 0x57
|
|||
|
&& inb(ioaddr + 15) == 0x57) {
|
|||
|
mem_start = lance_probe1(ioaddr, mem_start);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return mem_start;
|
|||
|
}
|
|||
|
|
|||
|
static unsigned long lance_probe1(short ioaddr, unsigned long mem_start)
|
|||
|
{
|
|||
|
struct device *dev;
|
|||
|
struct lance_private *lp;
|
|||
|
int hpJ2405A = 0;
|
|||
|
int i, reset_val;
|
|||
|
|
|||
|
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 mem_start;
|
|||
|
|
|||
|
dev = init_etherdev(0, sizeof(struct lance_private)
|
|||
|
+ PKT_BUF_SZ*(RX_RING_SIZE + TX_RING_SIZE),
|
|||
|
&mem_start);
|
|||
|
|
|||
|
printk("%s: LANCE at %#3x,", dev->name, 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;
|
|||
|
snarf_region(ioaddr, LANCE_TOTAL_SIZE);
|
|||
|
|
|||
|
/* Make certain the data structures used by the LANCE are aligned. */
|
|||
|
dev->priv = (void *)(((int)dev->priv + 7) & ~7);
|
|||
|
lp = (struct lance_private *)dev->priv;
|
|||
|
lp->rx_buffs = (long)dev->priv + sizeof(struct lance_private);
|
|||
|
lp->tx_bounce_buffs = (char (*)[PKT_BUF_SZ])
|
|||
|
(lp->rx_buffs + PKT_BUF_SZ*RX_RING_SIZE);
|
|||
|
|
|||
|
#ifndef final_version
|
|||
|
/* This should never happen. */
|
|||
|
if ((int)(lp->rx_ring) & 0x07) {
|
|||
|
printk(" **ERROR** LANCE Rx and Tx rings not on even boundary.\n");
|
|||
|
return mem_start;
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
outw(88, ioaddr+LANCE_ADDR);
|
|||
|
lp->old_lance = (inw(ioaddr+LANCE_DATA) != 0x3003);
|
|||
|
|
|||
|
#if defined(notdef)
|
|||
|
printk(lp->old_lance ? " original LANCE (%04x)" : " PCnet-ISA LANCE (%04x)",
|
|||
|
inw(ioaddr+LANCE_DATA));
|
|||
|
#endif
|
|||
|
|
|||
|
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);
|
|||
|
outw((short) (int) &lp->init_block, ioaddr+LANCE_DATA);
|
|||
|
outw(0x0002, ioaddr+LANCE_ADDR);
|
|||
|
outw(((int)&lp->init_block) >> 16, ioaddr+LANCE_DATA);
|
|||
|
outw(0x0000, ioaddr+LANCE_ADDR);
|
|||
|
|
|||
|
if (hpJ2405A) {
|
|||
|
char dma_tbl[4] = {3, 5, 6, 7};
|
|||
|
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 {
|
|||
|
/* The DMA channel may be passed in on this parameter. */
|
|||
|
if (dev->mem_start & 0x07)
|
|||
|
dev->dma = dev->mem_start & 0x07;
|
|||
|
else if (dev->dma == 0)
|
|||
|
dev->dma = LANCE_DMA;
|
|||
|
|
|||
|
/* 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) {
|
|||
|
|
|||
|
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, fixed at DMA %d.\n",
|
|||
|
dev->irq, dev->dma);
|
|||
|
else {
|
|||
|
printk(", failed to detect IRQ line.\n");
|
|||
|
return mem_start;
|
|||
|
}
|
|||
|
} else
|
|||
|
printk(" assigned IRQ %d DMA %d.\n", dev->irq, dev->dma);
|
|||
|
}
|
|||
|
|
|||
|
if (! lp->old_lance) {
|
|||
|
/* 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)
|
|||
|
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;
|
|||
|
#ifdef HAVE_MULTICAST
|
|||
|
dev->set_multicast_list = &set_multicast_list;
|
|||
|
#endif
|
|||
|
|
|||
|
return mem_start;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
static int
|
|||
|
lance_open(struct device *dev)
|
|||
|
{
|
|||
|
struct lance_private *lp = (struct lance_private *)dev->priv;
|
|||
|
int ioaddr = dev->base_addr;
|
|||
|
int i;
|
|||
|
|
|||
|
if (request_irq(dev->irq, &lance_interrupt)) {
|
|||
|
return -EAGAIN;
|
|||
|
}
|
|||
|
|
|||
|
if (request_dma(dev->dma)) {
|
|||
|
free_irq(dev->irq);
|
|||
|
return -EAGAIN;
|
|||
|
}
|
|||
|
irq2dev_map[dev->irq] = dev;
|
|||
|
|
|||
|
/* Reset the LANCE */
|
|||
|
inw(ioaddr+LANCE_RESET);
|
|||
|
|
|||
|
/* The DMA controller is used as a no-operation slave, "cascade mode". */
|
|||
|
enable_dma(dev->dma);
|
|||
|
set_dma_mode(dev->dma, DMA_MODE_CASCADE);
|
|||
|
|
|||
|
/* Un-Reset the LANCE, needed only for the NE2100. */
|
|||
|
if (lp->old_lance)
|
|||
|
outw(0, ioaddr+LANCE_RESET);
|
|||
|
|
|||
|
if (! lp->old_lance) {
|
|||
|
/* 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(0x0d15, 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;
|
|||
|
outw(0x0142, 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 */
|
|||
|
}
|
|||
|
|
|||
|
/* 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->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 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;
|
|||
|
|
|||
|
/* Transmitter timeout, serious problems. */
|
|||
|
if (dev->tbusy) {
|
|||
|
int tickssofar = jiffies - dev->trans_start;
|
|||
|
if (tickssofar < 10)
|
|||
|
return 1;
|
|||
|
outw(0, ioaddr+LANCE_ADDR);
|
|||
|
printk("%s: transmit timed out, status %4.4x, resetting.\n",
|
|||
|
dev->name, inw(ioaddr+LANCE_DATA));
|
|||
|
outw(0x0001, ioaddr+LANCE_DATA);
|
|||
|
lp->stats.tx_errors++;
|
|||
|
#ifndef final_version
|
|||
|
{
|
|||
|
int i;
|
|||
|
printk(" Ring data dump: dirty_tx %d cur_tx %d cur_rx %d.",
|
|||
|
lp->dirty_tx, lp->cur_tx, 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_init_ring(dev);
|
|||
|
outw(0x0043, ioaddr+LANCE_DATA);
|
|||
|
|
|||
|
dev->tbusy=0;
|
|||
|
dev->trans_start = jiffies;
|
|||
|
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
if (skb == NULL) {
|
|||
|
dev_tint(dev);
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
/* Fill in the ethernet header. */
|
|||
|
if (!skb->arp && dev->rebuild_header(skb->data, dev)) {
|
|||
|
skb->dev = dev;
|
|||
|
arp_queue (skb);
|
|||
|
return 0;
|
|||
|
}
|
|||
|
skb->arp=1;
|
|||
|
|
|||
|
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);
|
|||
|
|
|||
|
/* 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 (lp->old_lance) {
|
|||
|
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;
|
|||
|
if (skb->free)
|
|||
|
kfree_skb (skb, FREE_WRITE);
|
|||
|
} else
|
|||
|
{
|
|||
|
/* Gimme!!! */
|
|||
|
if(skb->free==0)
|
|||
|
skb_kept_by_device(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;
|
|||
|
|
|||
|
if (lp->tx_ring[(entry+1) & TX_RING_MOD_MASK].base == 0)
|
|||
|
dev->tbusy=0;
|
|||
|
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
/* The LANCE interrupt handler. */
|
|||
|
static void
|
|||
|
lance_interrupt(int reg_ptr)
|
|||
|
{
|
|||
|
int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2);
|
|||
|
struct device *dev = (struct device *)(irq2dev_map[irq]);
|
|||
|
struct lance_private *lp;
|
|||
|
int csr0, ioaddr;
|
|||
|
|
|||
|
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);
|
|||
|
csr0 = inw(dev->base_addr + LANCE_DATA);
|
|||
|
|
|||
|
/* Acknowledge all of the current interrupt sources ASAP. */
|
|||
|
outw(csr0 & ~0x004f, dev->base_addr + LANCE_DATA);
|
|||
|
|
|||
|
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;
|
|||
|
void *databuff;
|
|||
|
|
|||
|
if (status < 0)
|
|||
|
break; /* It still hasn't been Txed */
|
|||
|
|
|||
|
lp->tx_ring[entry].base = 0;
|
|||
|
databuff = (void*)(status & 0x00ffffff);
|
|||
|
|
|||
|
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) lp->stats.tx_fifo_errors++;
|
|||
|
/* We should re-init() after the FIFO error. */
|
|||
|
} else if (status & 0x18000000)
|
|||
|
lp->stats.collisions++;
|
|||
|
else
|
|||
|
lp->stats.tx_packets++;
|
|||
|
|
|||
|
/* We don't free the skb if it's a data-only copy in the bounce
|
|||
|
buffer. The address checks here are sorted -- the first test
|
|||
|
should always work. */
|
|||
|
if (databuff >= (void*)(&lp->tx_bounce_buffs[TX_RING_SIZE])
|
|||
|
|| databuff < (void*)(lp->tx_bounce_buffs)) {
|
|||
|
struct sk_buff *skb = ((struct sk_buff *)databuff) - 1;
|
|||
|
if (skb->free)
|
|||
|
kfree_skb(skb, FREE_WRITE);
|
|||
|
else
|
|||
|
skb_device_release(skb,FREE_WRITE);
|
|||
|
/* Warning: skb may well vanish at the point you call device_release! */
|
|||
|
}
|
|||
|
dirty_tx++;
|
|||
|
}
|
|||
|
|
|||
|
#ifndef final_version
|
|||
|
if (lp->cur_tx - dirty_tx >= TX_RING_SIZE) {
|
|||
|
printk("out-of-sync dirty pointer, %d vs. %d.\n",
|
|||
|
dirty_tx, lp->cur_tx);
|
|||
|
dirty_tx += TX_RING_SIZE;
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
if (dev->tbusy && dirty_tx > lp->cur_tx - TX_RING_SIZE + 2) {
|
|||
|
/* The ring is no longer full, clear tbusy. */
|
|||
|
dev->tbusy = 0;
|
|||
|
mark_bh(INET_BH);
|
|||
|
}
|
|||
|
|
|||
|
lp->dirty_tx = dirty_tx;
|
|||
|
}
|
|||
|
|
|||
|
if (csr0 & 0x8000) {
|
|||
|
if (csr0 & 0x4000) lp->stats.tx_errors++;
|
|||
|
if (csr0 & 0x1000) lp->stats.rx_errors++;
|
|||
|
}
|
|||
|
|
|||
|
/* Clear the interrupts we've handled. */
|
|||
|
outw(0x0000, dev->base_addr + LANCE_ADDR);
|
|||
|
outw(0x7f40, 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;
|
|||
|
|
|||
|
/* 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 & 0x40) { /* There was an error. */
|
|||
|
lp->stats.rx_errors++;
|
|||
|
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++;
|
|||
|
} else {
|
|||
|
/* Malloc up new buffer, compatible with net-2e. */
|
|||
|
short pkt_len = lp->rx_ring[entry].msg_length;
|
|||
|
int sksize = sizeof(struct sk_buff) + pkt_len;
|
|||
|
struct sk_buff *skb;
|
|||
|
|
|||
|
skb = alloc_skb(sksize, GFP_ATOMIC);
|
|||
|
if (skb == NULL) {
|
|||
|
printk("%s: Memory squeeze, deferring packet.\n", dev->name);
|
|||
|
lp->stats.rx_dropped++; /* Really, deferred. */
|
|||
|
break;
|
|||
|
}
|
|||
|
skb->mem_len = sksize;
|
|||
|
skb->mem_addr = skb;
|
|||
|
skb->len = pkt_len;
|
|||
|
skb->dev = dev;
|
|||
|
memcpy(skb->data,
|
|||
|
(unsigned char *)(lp->rx_ring[entry].base & 0x00ffffff),
|
|||
|
pkt_len);
|
|||
|
#ifdef HAVE_NETIF_RX
|
|||
|
netif_rx(skb);
|
|||
|
#else
|
|||
|
skb->lock = 0;
|
|||
|
if (dev_rint((unsigned char*)skb, pkt_len, IN_SKBUFF, dev) != 0) {
|
|||
|
kfree_skbmem(skb, sksize);
|
|||
|
lp->stats.rx_dropped++;
|
|||
|
break;
|
|||
|
}
|
|||
|
#endif
|
|||
|
lp->stats.rx_packets++;
|
|||
|
}
|
|||
|
|
|||
|
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;
|
|||
|
|
|||
|
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);
|
|||
|
|
|||
|
disable_dma(dev->dma);
|
|||
|
|
|||
|
free_irq(dev->irq);
|
|||
|
free_dma(dev->dma);
|
|||
|
|
|||
|
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;
|
|||
|
|
|||
|
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);
|
|||
|
sti();
|
|||
|
|
|||
|
return &lp->stats;
|
|||
|
}
|
|||
|
|
|||
|
#ifdef HAVE_MULTICAST
|
|||
|
/* 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;
|
|||
|
|
|||
|
/* We take the simple way out and always enable promiscuous mode. */
|
|||
|
outw(0, ioaddr+LANCE_ADDR);
|
|||
|
outw(0x0004, ioaddr+LANCE_DATA); /* Temporarily stop the lance. */
|
|||
|
|
|||
|
outw(15, ioaddr+LANCE_ADDR);
|
|||
|
if (num_addrs >= 0) {
|
|||
|
short multicast_table[4];
|
|||
|
int i;
|
|||
|
/* 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(0x0000, ioaddr+LANCE_DATA); /* Unset promiscuous mode */
|
|||
|
} else {
|
|||
|
outw(0x8000, ioaddr+LANCE_DATA); /* Set promiscuous mode */
|
|||
|
}
|
|||
|
|
|||
|
outw(0, ioaddr+LANCE_ADDR);
|
|||
|
outw(0x0142, ioaddr+LANCE_DATA); /* Resume normal operation. */
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
/*
|
|||
|
* Local variables:
|
|||
|
* compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c lance.c"
|
|||
|
* End:
|
|||
|
*/
|