patch-2.1.132 linux/drivers/net/lance.c
Next file: linux/drivers/net/lne390.c
Previous file: linux/drivers/net/irda/w83977af_ir.c
Back to the patch index
Back to the overall index
- Lines: 567
- Date:
Fri Dec 18 09:37:40 1998
- Orig file:
v2.1.131/linux/drivers/net/lance.c
- Orig date:
Fri Oct 23 22:01:21 1998
diff -u --recursive --new-file v2.1.131/linux/drivers/net/lance.c linux/drivers/net/lance.c
@@ -1,6 +1,6 @@
-/* lance.c: An AMD LANCE ethernet driver for linux. */
+/* lance.c: An AMD LANCE/PCnet ethernet driver for Linux. */
/*
- Written 1993,1994,1995 by Donald Becker.
+ Written/copyright 1993-1998 by Donald Becker.
Copyright 1993 United States Government as represented by the
Director, National Security Agency.
@@ -8,13 +8,12 @@
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.
+ with most other LANCE-based bus-master (NE2100/NE2500) 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
-
Fixing alignment problem with 1.3.* kernel and some minor changes
by Andrey V. Savochkin, 1996.
@@ -28,14 +27,28 @@
But I should to inform you that I'm not an expert in the LANCE card
and it may occurs that you will receive no answer on your mail
to Donald Becker. I didn't receive any answer on all my letters
- to him. Who knows why... But may be you are more lucky? ;-)
+ to him. Who knows why... But may be you are more lucky? ;->
SAW
- Fixed 7990 autoIRQ failure and reversed unneeded alignment. 8/20/96 djb
+
+ Thomas Bogendoerfer (tsbogend@bigbug.franken.de):
+ - added support for Linux/Alpha, but removed most of it, because
+ it worked only for the PCI chip.
+ - added hook for the 32bit lance driver
+ - added PCnetPCI II (79C970A) to chip table
+ Paul Gortmaker (gpg109@rsphy1.anu.edu.au):
+ - hopefully fix above so Linux/Alpha can use ISA cards too.
+ 8/20/96 Fixed 7990 autoIRQ failure and reversed unneeded alignment -djb
+ v1.12 10/27/97 Module support -djb
+ v1.14 2/3/98 Module support modified, made PCI support optional -djb
+
+ Forward ported v1.14 to 2.1.129, merged the PCI and misc changes from
+ the 2.1 version of the old driver - Alan Cox
*/
-static const char *version = "lance.c:v1.09 Aug 20 1996 dplatt@3do.com, becker@cesdis.gsfc.nasa.gov\n";
+static const char *version = "lance.c:v1.14ac 1998/11/20 dplatt@3do.com, becker@cesdis.gsfc.nasa.gov\n";
#include <linux/config.h>
+#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/string.h>
@@ -54,13 +67,9 @@
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
-static unsigned int lance_portlist[] __initdata = {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
+static unsigned int lance_portlist[] __initdata = { 0x300, 0x320, 0x340, 0x360, 0};
+int lance_probe(struct device *dev);
+int lance_probe1(struct device *dev, int ioaddr, int irq, int options);
#ifdef LANCE_DEBUG
int lance_debug = LANCE_DEBUG;
@@ -96,7 +105,7 @@
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
+The HP-J2405A board is an exception: with this board it is 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.)
@@ -147,36 +156,19 @@
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
+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
+the 'base' to zero. Iff the 'lp->tx_full' flag is set, it clears both the
tx_full and tbusy flags.
*/
-/* Memory accessed from LANCE card must be aligned on 8-byte boundaries.
- But we can't believe that kmalloc()'ed memory satisfies it. -- SAW */
-#define LANCE_KMALLOC(x) \
- ((void *) (((unsigned long)kmalloc((x)+7, GFP_DMA | GFP_KERNEL)+7) & ~7))
-
-/*
- * Changes:
- * Thomas Bogendoerfer (tsbogend@alpha.franken.de):
- * - added support for Linux/Alpha, but removed most of it, because
- * it worked only for the PCI chip.
- * - added hook for the 32bit lance driver
- * - added PCnetPCI II (79C970A) to chip table
- * - made 32bit driver standalone
- * - changed setting of autoselect bit
- *
- * Paul Gortmaker (gpg109@rsphy1.anu.edu.au):
- * - hopefully fix above so Linux/Alpha can use ISA cards too.
- */
-
/* Set the number of Tx and Rx buffers, using Log_2(# buffers).
Reasonable default values are 16 Tx buffers, and 16 Rx buffers.
- That translates to 4 and 4 (16 == 2^^4). */
+ That translates to 4 and 4 (16 == 2^^4).
+ This is a compile-time option for efficiency.
+ */
#ifndef LANCE_LOG_TX_BUFFERS
#define LANCE_LOG_TX_BUFFERS 4
#define LANCE_LOG_RX_BUFFERS 4
@@ -230,6 +222,8 @@
const char *name;
/* The saved address of a sent-in-place packet/buffer, for skfree(). */
struct sk_buff* tx_skbuff[TX_RING_SIZE];
+ /* The addresses of receive-in-place skbuffs. */
+ struct sk_buff* rx_skbuff[RX_RING_SIZE];
unsigned long rx_buffs; /* Address of Rx and Tx buffers. */
/* Tx low-memory "bounce buffer" address. */
char (*tx_bounce_buffs)[PKT_BUF_SZ];
@@ -247,7 +241,6 @@
#define LANCE_MUST_REINIT_RING 0x00000004
#define LANCE_MUST_UNRESET 0x00000008
#define LANCE_HAS_MISSED_FRAME 0x00000010
-#define PCNET32_POSSIBLE 0x00000020
/* A mapping from the chip ID number to the part number and features.
These are from the datasheets -- in real life the '970 version
@@ -267,21 +260,15 @@
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 + PCNET32_POSSIBLE},
+ 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 + PCNET32_POSSIBLE},
+ LANCE_HAS_MISSED_FRAME},
{0x2621, "PCnet/PCI-II 79C970A", /* 79C970A PCInetPCI II. */
LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
- LANCE_HAS_MISSED_FRAME + PCNET32_POSSIBLE},
- {0x2623, "PCnet/FAST 79C971", /* 79C971 PCInetFAST. */
- LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
- LANCE_HAS_MISSED_FRAME + PCNET32_POSSIBLE},
- {0x2624, "PCnet/FAST+ 79C972", /* 79C972 PCInetFAST+. */
- LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
- LANCE_HAS_MISSED_FRAME + PCNET32_POSSIBLE},
+ LANCE_HAS_MISSED_FRAME},
{0x0, "PCnet (unknown)",
LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
LANCE_HAS_MISSED_FRAME},
@@ -298,7 +285,7 @@
static int lance_open(struct device *dev);
static int lance_open_fail(struct device *dev);
-static void lance_init_ring(struct device *dev);
+static void lance_init_ring(struct device *dev, int mode);
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, void *dev_id, struct pt_regs *regs);
@@ -308,24 +295,83 @@
-/* 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. */
+#ifdef MODULE
+#define MAX_CARDS 8 /* Max number of interfaces (cards) per module */
+#define IF_NAMELEN 8 /* # of chars for storing dev->name */
+
+static int io[MAX_CARDS] = { 0, };
+static int dma[MAX_CARDS] = { 0, };
+static int irq[MAX_CARDS] = { 0, };
+
+static char ifnames[MAX_CARDS][IF_NAMELEN] = { {0, }, };
+static struct device dev_lance[MAX_CARDS] =
+{{
+ 0, /* device name is inserted by linux/drivers/net/net_init.c */
+ 0, 0, 0, 0,
+ 0, 0,
+ 0, 0, 0, NULL, NULL}};
+
+int init_module(void)
+{
+ int this_dev, found = 0;
+
+ for (this_dev = 0; this_dev < MAX_CARDS; this_dev++) {
+ struct device *dev = &dev_lance[this_dev];
+ dev->name = ifnames[this_dev];
+ dev->irq = irq[this_dev];
+ dev->base_addr = io[this_dev];
+ dev->dma = dma[this_dev];
+ dev->init = lance_probe;
+ if (io[this_dev] == 0) {
+ if (this_dev != 0) break; /* only complain once */
+ printk(KERN_NOTICE "lance.c: Module autoprobing not allowed. Append \"io=0xNNN\" value(s).\n");
+ return -EPERM;
+ }
+ if (register_netdev(dev) != 0) {
+ printk(KERN_WARNING "lance.c: No PCnet/LANCE 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_CARDS; this_dev++) {
+ struct device *dev = &dev_lance[this_dev];
+ if (dev->priv != NULL) {
+ kfree(dev->priv);
+ dev->priv = NULL;
+ free_dma(dev->dma);
+ release_region(dev->base_addr, LANCE_TOTAL_SIZE);
+ unregister_netdev(dev);
+ }
+ }
+}
+#endif /* MODULE */
-__initfunc(int lance_init(void))
+/* Starting in v2.1.*, the LANCE/PCnet probe is now similar to the other
+ board probes now that kmalloc() can allocate ISA DMA-able regions.
+ This also allows the LANCE driver to be used as a module.
+ */
+int lance_probe(struct device *dev)
{
- int *port;
+ int *port, result;
- if (virt_to_bus(high_memory) <= 16*1024*1024)
+ if (high_memory <= 16*1024*1024)
lance_need_isa_bounce_buffers = 0;
-#if defined(CONFIG_PCI) && !(defined(CONFIG_PCNET32) || defined(CONFIG_PCNET32_MODULE))
+#if defined(CONFIG_PCI)
if (pci_present()) {
- struct pci_dev *pdev = NULL;
+ struct pci_dev *pdev = NULL;
if (lance_debug > 1)
- printk("lance.c: PCI is present, checking for devices...\n");
+ printk("lance.c: PCI bios is present, checking for devices...\n");
+
while ((pdev = pci_find_device(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_LANCE, pdev))) {
unsigned int pci_ioaddr;
unsigned short pci_command;
@@ -344,8 +390,9 @@
}
printk("Found PCnet/PCI at %#x, irq %d.\n",
pci_ioaddr, pci_irq_line);
- lance_probe1(pci_ioaddr);
+ result = lance_probe1(dev, pci_ioaddr, pci_irq_line, 0);
pci_irq_line = 0;
+ if (!result) return 0;
}
}
#endif /* defined(CONFIG_PCI) */
@@ -359,16 +406,17 @@
char offset15, offset14 = inb(ioaddr + 14);
if ((offset14 == 0x52 || offset14 == 0x57) &&
- ((offset15 = inb(ioaddr + 15)) == 0x57 || offset15 == 0x44))
- lance_probe1(ioaddr);
+ ((offset15 = inb(ioaddr + 15)) == 0x57 || offset15 == 0x44)) {
+ result = lance_probe1(dev, ioaddr, 0, 0);
+ if ( !result ) return 0;
+ }
}
}
- return 0;
+ return -ENODEV;
}
-__initfunc(void lance_probe1(int ioaddr))
+__initfunc(int lance_probe1(struct device *dev, int ioaddr, int irq, int options))
{
- struct device *dev;
struct lance_private *lp;
short dma_channels; /* Mark spuriously-busy DMA channels */
int i, reset_val, lance_version;
@@ -406,7 +454,7 @@
outw(0x0000, ioaddr+LANCE_ADDR); /* Switch to window 0 */
if (inw(ioaddr+LANCE_DATA) != 0x0004)
- return;
+ return -ENODEV;
/* Get the version of the chip. */
outw(88, ioaddr+LANCE_ADDR);
@@ -419,24 +467,17 @@
if (lance_debug > 2)
printk(" LANCE chip version is %#x.\n", chip_version);
if ((chip_version & 0xfff) != 0x003)
- return;
+ return -ENODEV;
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;
}
}
-
-#if defined(CONFIG_PCNET32) || defined (CONFIG_PCNET32_MODULE)
- /*
- * if pcnet32 is configured and the chip is capable of 32bit mode
- * leave the card alone
- */
- if (chip_table[lance_version].flags & PCNET32_POSSIBLE)
- return;
-#endif
- dev = init_etherdev(0, 0);
+ /* We can't use init_etherdev() to allocate dev->priv because it must
+ a ISA DMA-able region. */
+ dev = init_etherdev(dev, 0);
dev->open = lance_open_fail;
chipname = chip_table[lance_version].name;
printk("%s: %s at %#3x,", dev->name, chipname, ioaddr);
@@ -450,6 +491,7 @@
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 *)(((unsigned long)kmalloc(sizeof(*lp)+7,
GFP_DMA | GFP_KERNEL)+7) & ~7);
if (lance_debug > 6) printk(" (#0x%05lx)", (unsigned long)lp);
@@ -483,9 +525,9 @@
outw(0x0000, ioaddr+LANCE_ADDR);
inw(ioaddr+LANCE_ADDR);
- if (pci_irq_line) {
+ if (irq) { /* Set iff PCI card. */
dev->dma = 4; /* Native bus-master, no DMA channel needed. */
- dev->irq = pci_irq_line;
+ dev->irq = irq;
} else if (hp_builtin) {
static const char dma_tbl[4] = {3, 5, 6, 0};
static const char irq_tbl[4] = {3, 4, 5, 9};
@@ -534,7 +576,7 @@
printk(", probed IRQ %d", dev->irq);
else {
printk(", failed to detect IRQ line.\n");
- return;
+ return -ENODEV;
}
/* Check for the initialization done bit, 0x0100, which means
@@ -548,7 +590,7 @@
} else if (dev->dma) {
if (request_dma(dev->dma, chipname)) {
printk("DMA %d allocation failed.\n", dev->dma);
- return;
+ return -ENODEV;
} else
printk(", assigned DMA %d.\n", dev->dma);
} else { /* OK, we have to auto-DMA. */
@@ -588,7 +630,7 @@
}
if (i == 4) { /* Failure: bail. */
printk("DMA detection failed.\n");
- return;
+ return -ENODEV;
}
}
@@ -601,7 +643,7 @@
dev->irq = autoirq_report(4);
if (dev->irq == 0) {
printk(" Failed to detect the 7990 IRQ line.\n");
- return;
+ return -ENODEV;
}
printk(" Auto-IRQ detected IRQ%d.\n", dev->irq);
}
@@ -610,8 +652,8 @@
/* 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);
- /* set autoselect and clean xmausel */
- outw((inw(ioaddr+LANCE_BUS_IF) & 0xfffe) | 0x0002, ioaddr+LANCE_BUS_IF);
+ /* Don't touch 10base2 power bit. */
+ outw(inw(ioaddr+LANCE_BUS_IF) | 0x0002, ioaddr+LANCE_BUS_IF);
}
if (lance_debug > 0 && did_version++ == 0)
@@ -624,7 +666,7 @@
dev->get_stats = lance_get_stats;
dev->set_multicast_list = set_multicast_list;
- return;
+ return 0;
}
static int
@@ -647,6 +689,8 @@
return -EAGAIN;
}
+ MOD_INC_USE_COUNT;
+
/* We used to allocate DMA here, but that was silly.
DMA lines can't be shared! We now permanently allocate them. */
@@ -668,9 +712,9 @@
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);
- /* set autoselect and clean xmausel */
- outw((inw(ioaddr+LANCE_BUS_IF) & 0xfffe) | 0x0002, ioaddr+LANCE_BUS_IF);
- }
+ /* Only touch autoselect bit. */
+ outw(inw(ioaddr+LANCE_BUS_IF) | 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",
@@ -679,7 +723,7 @@
(u32) virt_to_bus(lp->rx_ring),
(u32) virt_to_bus(&lp->init_block));
- lance_init_ring(dev);
+ lance_init_ring(dev, GFP_KERNEL);
/* Re-initialize the LANCE, and start it when done. */
outw(0x0001, ioaddr+LANCE_ADDR);
outw((short) (u32) virt_to_bus(&lp->init_block), ioaddr+LANCE_DATA);
@@ -741,7 +785,7 @@
/* Initialize the LANCE Rx and Tx rings. */
static void
-lance_init_ring(struct device *dev)
+lance_init_ring(struct device *dev, int gfp)
{
struct lance_private *lp = (struct lance_private *)dev->priv;
int i;
@@ -751,12 +795,26 @@
lp->dirty_rx = lp->dirty_tx = 0;
for (i = 0; i < RX_RING_SIZE; i++) {
- lp->rx_ring[i].base = (u32)virt_to_bus((char *)lp->rx_buffs + i*PKT_BUF_SZ) | 0x80000000;
+ struct sk_buff *skb;
+ void *rx_buff;
+
+ skb = alloc_skb(PKT_BUF_SZ, GFP_DMA | gfp);
+ lp->rx_skbuff[i] = skb;
+ if (skb) {
+ skb->dev = dev;
+ rx_buff = skb->tail;
+ } else
+ rx_buff = kmalloc(PKT_BUF_SZ, GFP_DMA | gfp);
+ if (rx_buff == NULL)
+ lp->rx_ring[i].base = 0;
+ else
+ lp->rx_ring[i].base = (u32)virt_to_bus(rx_buff) | 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_skbuff[i] = 0;
lp->tx_ring[i].base = 0;
}
@@ -777,7 +835,7 @@
if (must_reinit ||
(chip_table[lp->chip_version].flags & LANCE_MUST_REINIT_RING)) {
lance_purge_tx_ring(dev);
- lance_init_ring(dev);
+ lance_init_ring(dev, GFP_ATOMIC);
}
outw(0x0000, dev->base_addr + LANCE_ADDR);
outw(csr0_bits, dev->base_addr + LANCE_DATA);
@@ -872,14 +930,14 @@
memcpy(&lp->tx_bounce_buffs[entry], skb->data, skb->len);
lp->tx_ring[entry].base =
((u32)virt_to_bus((lp->tx_bounce_buffs + entry)) & 0xffffff) | 0x83000000;
- dev_kfree_skb (skb);
+ dev_kfree_skb(skb);
} else {
lp->tx_skbuff[entry] = skb;
lp->tx_ring[entry].base = ((u32)virt_to_bus(skb->data) & 0xffffff) | 0x83000000;
}
lp->cur_tx++;
- lp->stats.tx_bytes+=skb->len;
-
+ lp->stats.tx_bytes += skb->len;
+
/* Trigger an immediate send poll. */
outw(0x0000, ioaddr+LANCE_ADDR);
outw(0x0048, ioaddr+LANCE_DATA);
@@ -915,7 +973,7 @@
ioaddr = dev->base_addr;
lp = (struct lance_private *)dev->priv;
if (dev->interrupt)
- printk("%s: Re-entering the interrupt handler.\n", dev->name);
+ printk(KERN_WARNING "%s: Re-entering the interrupt handler.\n", dev->name);
dev->interrupt = 1;
@@ -1110,6 +1168,7 @@
{
int ioaddr = dev->base_addr;
struct lance_private *lp = (struct lance_private *)dev->priv;
+ int i;
dev->start = 0;
dev->tbusy = 1;
@@ -1134,9 +1193,23 @@
disable_dma(dev->dma);
release_dma_lock(flags);
}
-
free_irq(dev->irq, dev);
+ /* Free all the skbuffs in the Rx and Tx queues. */
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ struct sk_buff *skb = lp->rx_skbuff[i];
+ lp->rx_skbuff[i] = 0;
+ lp->rx_ring[i].base = 0; /* Not owned by LANCE chip. */
+ if (skb)
+ dev_kfree_skb(skb);
+ }
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ if (lp->tx_skbuff[i])
+ dev_kfree_skb(lp->tx_skbuff[i]);
+ lp->tx_skbuff[i] = 0;
+ }
+
+ MOD_DEC_USE_COUNT;
return 0;
}
@@ -1195,11 +1268,3 @@
}
-
-/*
- * 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:
- */
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov