patch-2.1.105 linux/drivers/net/pcnet32.c
Next file: linux/drivers/net/shaper.c
Previous file: linux/drivers/net/ne2k-pci.c
Back to the patch index
Back to the overall index
- Lines: 582
- Date:
Sun Jun 7 10:16:57 1998
- Orig file:
v2.1.104/linux/drivers/net/pcnet32.c
- Orig date:
Mon Apr 6 17:41:00 1998
diff -u --recursive --new-file v2.1.104/linux/drivers/net/pcnet32.c linux/drivers/net/pcnet32.c
@@ -1,6 +1,6 @@
/* pcnet32.c: An AMD PCnet32 ethernet driver for linux. */
/*
- * Copyright 1996,97 Thomas Bogendoerfer
+ * Copyright 1996,97,98 Thomas Bogendoerfer
*
* Derived from the lance driver written 1993,1994,1995 by Donald Becker.
*
@@ -13,9 +13,14 @@
* This driver is for PCnet32 and PCnetPCI based ethercards
*/
-static const char *version = "pcnet32.c:v0.23 8.2.97 tsbogend@alpha.franken.de\n";
+static const char *version = "pcnet32.c:v1.00 30.5.98 tsbogend@alpha.franken.de\n";
#include <linux/config.h>
+#include <linux/module.h>
+#ifdef MODVERSIONS
+#include <linux/modversions.h>
+#endif
+
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/string.h>
@@ -25,6 +30,7 @@
#include <linux/malloc.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
+#include <linux/delay.h>
#include <linux/init.h>
#include <asm/bitops.h>
#include <asm/io.h>
@@ -36,12 +42,15 @@
static unsigned int pcnet32_portlist[] __initdata = {0x300, 0x320, 0x340, 0x360, 0};
-#ifdef PCNET32_DEBUG
-static int pcnet32_debug = PCNET32_DEBUG;
-#else
static int pcnet32_debug = 1;
+
+#ifdef MODULE
+static struct device *pcnet32_dev = NULL;
#endif
+static const int max_interrupt_work = 20;
+static const int rx_copybreak = 200;
+
/*
* Theory of Operation
*
@@ -58,7 +67,7 @@
* only tested on Alpha Noname Board
* v0.02: changed IRQ handling for new interrupt scheme (dev_id)
* tested on a ASUS SP3G
- * v0.10: fixed an odd problem with the 79C794 in a Compaq Deskpro XL
+ * v0.10: fixed an odd problem with the 79C974 in a Compaq Deskpro XL
* looks like the 974 doesn't like stopping and restarting in a
* short period of time; now we do a reinit of the lance; the
* bug was triggered by doing ifconfig eth0 <ip> broadcast <addr>
@@ -77,7 +86,18 @@
* in arch/i386/bios32.c
* v0.21: added endian conversion for ppc, from work by cort@cs.nmt.edu
* v0.22: added printing of status to ring dump
- * v0.23: changed enet_statistics to net_device_stats
+ * v0.23: changed enet_statistics to net_devive_stats
+ * v0.90: added multicast filter
+ * added module support
+ * changed irq probe to new style
+ * added PCnetFast chip id
+ * added fix for receive stalls with Intel saturn chipsets
+ * added in-place rx skbs like in the tulip driver
+ * minor cleanups
+ * v0.91: added PCnetFast+ chip id
+ * back port to 2.0.x
+ * v1.00: added some stuff from Donald Becker's 2.0.34 version
+ * added support for byte counters in net_dev_stats
*/
@@ -108,6 +128,8 @@
#define PCNET32_BUS_IF 0x16
#define PCNET32_TOTAL_SIZE 0x18
+#define CRC_POLYNOMIAL_LE 0xedb88320UL /* Ethernet CRC, little endian */
+
/* The PCNET32 Rx and Tx ring descriptors. */
struct pcnet32_rx_head {
u32 base;
@@ -145,14 +167,17 @@
struct pcnet32_init_block init_block;
const char *name;
/* The saved address of a sent-in-place packet/buffer, for skfree(). */
- struct sk_buff* tx_skbuff[TX_RING_SIZE];
- unsigned long rx_buffs; /* Address of Rx and Tx buffers. */
+ struct sk_buff *tx_skbuff[TX_RING_SIZE];
+ struct sk_buff *rx_skbuff[RX_RING_SIZE];
int cur_rx, cur_tx; /* The next free ring entry */
int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */
struct net_device_stats stats;
char tx_full;
unsigned long lock;
char shared_irq; /* shared irq possible */
+#ifdef MODULE
+ struct device *next;
+#endif
};
int pcnet32_probe(struct device *dev);
@@ -204,9 +229,6 @@
pci_command |= PCI_COMMAND_MASTER|PCI_COMMAND_IO;
pci_write_config_word(pdev, PCI_COMMAND, pci_command);
}
-#ifdef __powerpc__
- irq_line = 15;
-#endif
printk("Found PCnet/PCI at %#x, irq %d.\n",
ioaddr, irq_line);
@@ -275,17 +297,20 @@
case 0x2621:
chipname = "PCnet/PCI II 79C970A";
break;
+ case 0x2623:
+ chipname = "PCnet/FAST 79C971";
+ break;
+ case 0x2624:
+ chipname = "PCnet/FAST+ 79C972";
+ break;
default:
printk("pcnet32: PCnet version %#x, no PCnet32 chip.\n",chip_version);
return ENODEV;
}
}
- /* We should have a "dev" from Space.c or the static module table. */
- if (dev == NULL) {
- printk(KERN_ERR "pcnet32.c: Passed a NULL device.\n");
+ if (dev == NULL)
dev = init_etherdev(0, 0);
- }
printk("%s: %s at %#3x,", dev->name, chipname, ioaddr);
@@ -304,7 +329,6 @@
dev->priv = lp;
lp->name = chipname;
lp->shared_irq = shared;
- lp->rx_buffs = (unsigned long) kmalloc(PKT_BUF_SZ*RX_RING_SIZE, GFP_DMA | GFP_KERNEL);
lp->init_block.mode = le16_to_cpu(0x0003); /* Disable Rx and Tx. */
lp->init_block.tlen_rlen = le16_to_cpu(TX_RING_LEN_BITS | RX_RING_LEN_BITS);
@@ -335,17 +359,18 @@
if (dev->irq >= 2)
printk(" assigned IRQ %d.\n", dev->irq);
else {
+ unsigned long irq_mask = probe_irq_on();
+
/*
* To auto-IRQ we enable the initialization-done and DMA error
* interrupts. For ISA boards we get a DMA error, but VLB and PCI
* boards will work.
*/
- autoirq_setup(0);
-
/* Trigger an initialization just for the interrupt. */
outw(0x0041, ioaddr+PCNET32_DATA);
-
- dev->irq = autoirq_report(1);
+ mdelay (1);
+
+ dev->irq = probe_irq_off (irq_mask);
if (dev->irq)
printk(", probed IRQ %d.\n", dev->irq);
else {
@@ -368,6 +393,11 @@
dev->stop = &pcnet32_close;
dev->get_stats = &pcnet32_get_stats;
dev->set_multicast_list = &pcnet32_set_multicast_list;
+
+#ifdef MODULE
+ lp->next = pcnet32_dev;
+ pcnet32_dev = dev;
+#endif
/* Fill in the generic fields of the device structure. */
ether_setup(dev);
@@ -441,6 +471,8 @@
printk("%s: PCNET32 open after %d ticks, init block %#x csr0 %4.4x.\n",
dev->name, i, (u32) virt_to_bus(&lp->init_block), inw(ioaddr+PCNET32_DATA));
+ MOD_INC_USE_COUNT;
+
return 0; /* Always succeed */
}
@@ -478,15 +510,23 @@
{
struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv;
int i;
+ struct sk_buff *skb;
lp->lock = 0, lp->tx_full = 0;
lp->cur_rx = lp->cur_tx = 0;
lp->dirty_rx = lp->dirty_tx = 0;
for (i = 0; i < RX_RING_SIZE; i++) {
- lp->rx_ring[i].base = (u32)le32_to_cpu(virt_to_bus((char *)lp->rx_buffs + i*PKT_BUF_SZ));
+ skb = dev_alloc_skb (PKT_BUF_SZ);
+ if (skb) {
+ lp->rx_skbuff[i] = skb;
+ skb_reserve (skb, 2);
+ lp->rx_ring[i].base = (u32)le32_to_cpu(virt_to_bus(skb->tail));
lp->rx_ring[i].buf_length = le16_to_cpu(-PKT_BUF_SZ);
- lp->rx_ring[i].status = le16_to_cpu(0x8000);
+ lp->rx_ring[i].status = le16_to_cpu(0x8000);
+ }
+ else
+ break;
}
/* The Tx buffer address is filled in as needed, but we do need to clear
the upper ownership bit. */
@@ -503,7 +543,7 @@
}
static void
-pcnet32_restart(struct device *dev, unsigned int csr0_bits, int must_reinit)
+pcnet32_restart(struct device *dev, unsigned int csr0_bits)
{
int i;
unsigned int ioaddr = dev->base_addr;
@@ -557,9 +597,9 @@
printk("\n");
}
#endif
- pcnet32_restart(dev, 0x0042, 1);
+ pcnet32_restart(dev, 0x0042);
- dev->tbusy=0;
+ dev->tbusy = 0;
dev->trans_start = jiffies;
return 0;
@@ -603,6 +643,7 @@
lp->tx_ring[entry].status = le16_to_cpu(0x8300);
lp->cur_tx++;
+ lp->stats.tx_bytes += skb->len;
/* Trigger an immediate send poll. */
outw(0x0000, ioaddr+PCNET32_ADDR);
@@ -614,7 +655,7 @@
cli();
lp->lock = 0;
if (lp->tx_ring[(entry+1) & TX_RING_MOD_MASK].base == 0)
- dev->tbusy=0;
+ clear_bit (0, (void *)&dev->tbusy);
else
lp->tx_full = 1;
restore_flags(flags);
@@ -629,8 +670,7 @@
struct device *dev = (struct device *)dev_id;
struct pcnet32_private *lp;
unsigned int csr0, ioaddr;
- int boguscnt=10;
- int must_restart;
+ int boguscnt = max_interrupt_work;
if (dev == NULL) {
printk ("pcnet32_interrupt(): irq %d for unknown device.\n", irq);
@@ -645,13 +685,10 @@
dev->interrupt = 1;
outw(0x00, dev->base_addr + PCNET32_ADDR);
- while ((csr0 = inw(dev->base_addr + PCNET32_DATA)) & 0x8600
- && --boguscnt >= 0) {
+ while ((csr0 = inw(dev->base_addr + PCNET32_DATA)) & 0x8600 && --boguscnt >= 0) {
/* Acknowledge all of the current interrupt sources ASAP. */
outw(csr0 & ~0x004f, dev->base_addr + PCNET32_DATA);
- must_restart = 0;
-
if (pcnet32_debug > 5)
printk("%s: interrupt csr0=%#2.2x new csr=%#2.2x.\n",
dev->name, csr0, inw(dev->base_addr + PCNET32_DATA));
@@ -684,8 +721,10 @@
/* Remove this verbosity later! */
printk("%s: Tx FIFO error! Status %4.4x.\n",
dev->name, csr0);
- /* Restart the chip. */
- must_restart = 1;
+ /* stop the chip to clear the error condition, then restart */
+ outw(0x0000, dev->base_addr + PCNET32_ADDR);
+ outw(0x0004, dev->base_addr + PCNET32_DATA);
+ pcnet32_restart(dev, 0x0002);
}
} else {
if (status & 0x1800)
@@ -713,28 +752,31 @@
&& dirty_tx > lp->cur_tx - TX_RING_SIZE + 2) {
/* The ring is no longer full, clear tbusy. */
lp->tx_full = 0;
- dev->tbusy = 0;
+ clear_bit(0, (void *)&dev->tbusy);
mark_bh(NET_BH);
}
-
lp->dirty_tx = dirty_tx;
}
/* Log misc errors. */
if (csr0 & 0x4000) lp->stats.tx_errors++; /* Tx babble. */
- if (csr0 & 0x1000) lp->stats.rx_errors++; /* Missed a Rx frame. */
+ if (csr0 & 0x1000) {
+ /*
+ * this happens when our receive ring is full. This shouldn't
+ * be a problem as we will see normal rx interrupts for the frames
+ * in the receive ring. But there are some PCI chipsets (I can reproduce
+ * this on SP3G with Intel saturn chipset) which have sometimes problems
+ * and will fill up the receive ring with error descriptors. In this
+ * situation we don't get a rx interrupt, but a missed frame interrupt sooner
+ * or later. So we try to clean up our receive ring here.
+ */
+ pcnet32_rx(dev);
+ lp->stats.rx_errors++; /* Missed a Rx frame. */
+ }
if (csr0 & 0x0800) {
printk("%s: Bus master arbitration failure, status %4.4x.\n",
dev->name, csr0);
- /* Restart the chip. */
- must_restart = 1;
- }
-
- if (must_restart) {
- /* stop the chip to clear the error condition, then restart */
- outw(0x0000, dev->base_addr + PCNET32_ADDR);
- outw(0x0004, dev->base_addr + PCNET32_DATA);
- pcnet32_restart(dev, 0x0002, 0);
+ /* unlike for the lance, there is no restart needed */
}
}
@@ -757,7 +799,7 @@
struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv;
int entry = lp->cur_rx & RX_RING_MOD_MASK;
int i;
-
+
/* If we own the next entry, it's a new packet. Send it up. */
while ((short)le16_to_cpu(lp->rx_ring[entry].status) >= 0) {
int status = (short)le16_to_cpu(lp->rx_ring[entry].status) >> 8;
@@ -781,38 +823,53 @@
short pkt_len = (le32_to_cpu(lp->rx_ring[entry].msg_length) & 0xfff)-4;
struct sk_buff *skb;
- if(pkt_len<60)
- {
+ if(pkt_len < 60) {
printk("%s: Runt packet!\n",dev->name);
lp->stats.rx_errors++;
- }
- else
- {
+ } else {
+ int rx_in_place = 0;
+
+ if (pkt_len > rx_copybreak) {
+ struct sk_buff *newskb;
+
+ if ((newskb = dev_alloc_skb (PKT_BUF_SZ))) {
+ skb_reserve (newskb, 2);
+ skb = lp->rx_skbuff[entry];
+ skb_put (skb, pkt_len);
+ lp->rx_skbuff[entry] = newskb;
+ newskb->dev = dev;
+ lp->rx_ring[entry].base = le32_to_cpu(virt_to_bus(newskb->tail));
+ rx_in_place = 1;
+ } else
+ skb = NULL;
+ } else
skb = dev_alloc_skb(pkt_len+2);
- if (skb == NULL)
- {
- printk("%s: Memory squeeze, deferring packet.\n", dev->name);
- for (i=0; i < RX_RING_SIZE; i++)
- if ((short)le16_to_cpu(lp->rx_ring[(entry+i) & RX_RING_MOD_MASK].status) < 0)
- break;
-
- if (i > RX_RING_SIZE -2)
- {
- lp->stats.rx_dropped++;
- lp->rx_ring[entry].status |= le16_to_cpu(0x8000);
- lp->cur_rx++;
- }
+
+ if (skb == NULL) {
+ printk("%s: Memory squeeze, deferring packet.\n", dev->name);
+ for (i=0; i < RX_RING_SIZE; i++)
+ if ((short)le16_to_cpu(lp->rx_ring[(entry+i) & RX_RING_MOD_MASK].status) < 0)
break;
+
+ if (i > RX_RING_SIZE -2) {
+ lp->stats.rx_dropped++;
+ lp->rx_ring[entry].status |= le16_to_cpu(0x8000);
+ lp->cur_rx++;
}
- skb->dev = dev;
+ break;
+ }
+ skb->dev = dev;
+ if (!rx_in_place) {
skb_reserve(skb,2); /* 16 byte align */
skb_put(skb,pkt_len); /* Make room */
eth_copy_and_sum(skb,
(unsigned char *)bus_to_virt(le32_to_cpu(lp->rx_ring[entry].base)),
pkt_len,0);
- skb->protocol=eth_type_trans(skb,dev);
- netif_rx(skb);
- lp->stats.rx_packets++;
+ }
+ lp->stats.rx_bytes += skb->len;
+ skb->protocol=eth_type_trans(skb,dev);
+ netif_rx(skb);
+ lp->stats.rx_packets++;
}
}
/* The docs say that the buffer length isn't touched, but Andrew Boyd
@@ -833,9 +890,10 @@
{
unsigned int ioaddr = dev->base_addr;
struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv;
+ int i;
dev->start = 0;
- dev->tbusy = 1;
+ set_bit (0, (void *)&dev->tbusy);
outw(112, ioaddr+PCNET32_ADDR);
lp->stats.rx_missed_errors = inw(ioaddr+PCNET32_DATA);
@@ -851,6 +909,22 @@
outw(0x0004, ioaddr+PCNET32_DATA);
free_irq(dev->irq, dev);
+
+ /* free all allocated skbuffs */
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ lp->rx_ring[i].status = 0;
+ if (lp->rx_skbuff[i])
+ dev_kfree_skb(lp->rx_skbuff[i]);
+ lp->rx_skbuff[i] = NULL;
+ }
+
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ if (lp->tx_skbuff[i])
+ dev_kfree_skb(lp->tx_skbuff[i]);
+ lp->rx_skbuff[i] = NULL;
+ }
+
+ MOD_DEC_USE_COUNT;
return 0;
}
@@ -874,6 +948,57 @@
return &lp->stats;
}
+
+/* taken from the sunlance driver, which it took from the depca driver */
+static void pcnet32_load_multicast (struct device *dev)
+{
+ struct pcnet32_private *lp = (struct pcnet32_private *) dev->priv;
+ volatile struct pcnet32_init_block *ib = &lp->init_block;
+ volatile u16 *mcast_table = (u16 *)&ib->filter;
+ struct dev_mc_list *dmi=dev->mc_list;
+ char *addrs;
+ int i, j, bit, byte;
+ u32 crc, poly = CRC_POLYNOMIAL_LE;
+
+ /* set all multicast bits */
+ if (dev->flags & IFF_ALLMULTI){
+ ib->filter [0] = 0xffffffff;
+ ib->filter [1] = 0xffffffff;
+ return;
+ }
+ /* clear the multicast filter */
+ ib->filter [0] = 0;
+ ib->filter [1] = 0;
+
+ /* Add addresses */
+ for (i = 0; i < dev->mc_count; i++){
+ addrs = dmi->dmi_addr;
+ dmi = dmi->next;
+
+ /* multicast address? */
+ if (!(*addrs & 1))
+ continue;
+
+ crc = 0xffffffff;
+ for (byte = 0; byte < 6; byte++)
+ for (bit = *addrs++, j = 0; j < 8; j++, bit >>= 1) {
+ int test;
+
+ test = ((bit ^ crc) & 0x01);
+ crc >>= 1;
+
+ if (test) {
+ crc = crc ^ poly;
+ }
+ }
+
+ crc = crc >> 26;
+ mcast_table [crc >> 4] |= 1 << (crc & 0xf);
+ }
+ return;
+}
+
+
/* Set or clear the multicast filter for this adaptor.
*/
@@ -885,22 +1010,53 @@
if (dev->flags&IFF_PROMISC) {
/* Log any net taps. */
printk("%s: Promiscuous mode enabled.\n", dev->name);
- lp->init_block.mode = 0x8000;
+ lp->init_block.mode = le16_to_cpu(0x8000);
} else {
- int num_addrs=dev->mc_count;
- if(dev->flags&IFF_ALLMULTI)
- num_addrs=1;
- /* FIXIT: We don't use the multicast table, but rely on upper-layer filtering. */
- memset(lp->init_block.filter , (num_addrs == 0) ? 0 : -1, sizeof(lp->init_block.filter));
- lp->init_block.mode = 0x0000;
+ lp->init_block.mode = 0x0000;
+ pcnet32_load_multicast (dev);
}
outw(0, ioaddr+PCNET32_ADDR);
outw(0x0004, ioaddr+PCNET32_DATA); /* Temporarily stop the lance. */
- pcnet32_restart(dev, 0x0042, 0); /* Resume normal operation */
+ pcnet32_restart(dev, 0x0042); /* Resume normal operation */
+}
+
+#ifdef MODULE
+MODULE_PARM(debug, "i");
+MODULE_PARM(max_interrupt_work, "i");
+MODULE_PARM(rx_copybreak, "i");
+/* An additional parameter that may be passed in... */
+static int debug = -1;
+
+int
+init_module(void)
+{
+ if (debug > 0)
+ pcnet32_debug = debug;
+
+ pcnet32_dev = NULL;
+ return pcnet32_probe(NULL);
}
+
+void
+cleanup_module(void)
+{
+ struct device *next_dev;
+
+ /* No need to check MOD_IN_USE, as sys_delete_module() checks. */
+ while (pcnet32_dev) {
+ next_dev = ((struct pcnet32_private *) pcnet32_dev->priv)->next;
+ unregister_netdev(pcnet32_dev);
+ release_region(pcnet32_dev->base_addr, PCNET32_TOTAL_SIZE);
+ kfree(pcnet32_dev->priv);
+ kfree(pcnet32_dev);
+ pcnet32_dev = next_dev;
+ }
+}
+#endif /* MODULE */
+
/*
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov