patch-2.0.31 linux/drivers/net/3c509.c
Next file: linux/drivers/net/3c59x.c
Previous file: linux/drivers/isdn/teles/teles.h
Back to the patch index
Back to the overall index
- Lines: 330
- Date:
Fri Sep 5 11:20:20 1997
- Orig file:
v2.0.30/linux/drivers/net/3c509.c
- Orig date:
Tue Oct 29 17:42:40 1996
diff -u --recursive --new-file v2.0.30/linux/drivers/net/3c509.c linux/drivers/net/3c509.c
@@ -1,8 +1,8 @@
/* 3c509.c: A 3c509 EtherLink3 ethernet driver for linux. */
/*
- Written 1993-1995 by Donald Becker.
+ Written 1993-1997 by Donald Becker.
- Copyright 1994,1995 by Donald Becker.
+ Copyright 1994-1997 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,
@@ -31,9 +31,17 @@
what the docs say some people definitely
get problems with lower (but in card spec)
delays
+ v1.10 4/21/97 Fixed module code so that multiple cards may be detected,
+ other cleanups. -djb
*/
-static char *version = "3c509.c:1.07 6/15/95 becker@cesdis.gsfc.nasa.gov\n";
+static char *version = "3c509.c:1.12 6/4/97 becker@cesdis.gsfc.nasa.gov\n";
+/* A few values that may be tweaked. */
+
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT (400*HZ/1000)
+/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
+#define INTR_WORK 10
#include <linux/module.h>
@@ -64,7 +72,7 @@
/* To minimize the size of the driver source I only define operating
constants if they are used several times. You'll need the manual
- if you want to understand driver details. */
+ anyway if you want to understand driver details. */
/* Offsets from base I/O address. */
#define EL3_DATA 0x00
#define EL3_CMD 0x0e
@@ -115,11 +123,13 @@
struct el3_private {
struct enet_statistics stats;
+ struct device *next_dev;
/* skb send-queue */
int head, size;
struct sk_buff *queue[SKB_QUEUE_SIZE];
};
static int id_port = 0x100;
+static struct device *el3_root_dev = NULL;
static ushort id_read_eeprom(int index);
static ushort read_eeprom(short ioaddr, int index);
@@ -130,9 +140,7 @@
static struct enet_statistics *el3_get_stats(struct device *dev);
static int el3_rx(struct device *dev);
static int el3_close(struct device *dev);
-#ifdef HAVE_MULTICAST
static void set_multicast_list(struct device *dev);
-#endif
@@ -140,7 +148,7 @@
{
short lrs_state = 0xff, i;
ushort ioaddr, irq, if_port;
- short *phys_addr = (short *)dev->dev_addr;
+ short phys_addr[3];
static int current_tag = 0;
/* First check all slots of the EISA bus. The next slot address to
@@ -195,6 +203,8 @@
outb(0x02, 0xA79); /* Return to WaitForKey state. */
/* Select an open I/O location at 0x1*0 to do contention select. */
for (id_port = 0x100; id_port < 0x200; id_port += 0x10) {
+ if (check_region(id_port, 1))
+ continue;
outb(0x00, id_port);
outb(0xff, id_port);
if (inb(id_port) & 0x01)
@@ -210,13 +220,6 @@
on cards as they are found. Cards with their tag set will not
respond to subsequent ID sequences. */
- if (check_region(id_port,1)) {
- static int once = 1;
- if (once) printk("3c509: Somebody has reserved 0x%x, can't do ID_PORT lookup, nor card auto-probing\n",id_port);
- once = 0;
- return -ENODEV;
- }
-
outb(0x00, id_port);
outb(0x00, id_port);
for(i = 0; i < 255; i++) {
@@ -247,13 +250,13 @@
if_port = iobase >> 14;
ioaddr = 0x200 + ((iobase & 0x1f) << 4);
}
- if (dev->irq > 1 && dev->irq < 16)
+ if (dev && dev->irq > 1 && dev->irq < 16)
irq = dev->irq;
else
irq = id_read_eeprom(9) >> 12;
- if (dev->base_addr != 0
- && dev->base_addr != (unsigned short)ioaddr) {
+ if (dev && dev->base_addr != 0
+ && dev->base_addr != (unsigned short)ioaddr) {
return -ENODEV;
}
@@ -270,9 +273,14 @@
/* Free the interrupt so that some other card can use it. */
outw(0x0f00, ioaddr + WN0_IRQ);
found:
+ if (dev == NULL) {
+ dev = init_etherdev(dev, sizeof(struct el3_private));
+ }
+ memcpy(dev->dev_addr, phys_addr, sizeof(phys_addr));
dev->base_addr = ioaddr;
dev->irq = irq;
- dev->if_port = if_port;
+ dev->if_port = (dev->mem_start & 0x1f) ? dev->mem_start & 3 : if_port;
+
request_region(dev->base_addr, EL3_IO_EXTENT, "3c509");
{
@@ -287,11 +295,15 @@
printk(", IRQ %d.\n", dev->irq);
/* Make up a EL3-specific-data structure. */
- dev->priv = kmalloc(sizeof(struct el3_private), GFP_KERNEL);
+ if (dev->priv == NULL)
+ dev->priv = kmalloc(sizeof(struct el3_private), GFP_KERNEL);
if (dev->priv == NULL)
return -ENOMEM;
memset(dev->priv, 0, sizeof(struct el3_private));
+ ((struct el3_private *)dev->priv)->next_dev = el3_root_dev;
+ el3_root_dev = dev;
+
if (el3_debug > 0)
printk(version);
@@ -300,9 +312,7 @@
dev->hard_start_xmit = &el3_start_xmit;
dev->stop = &el3_close;
dev->get_stats = &el3_get_stats;
-#ifdef HAVE_MULTICAST
- dev->set_multicast_list = &set_multicast_list;
-#endif
+ dev->set_multicast_list = &set_multicast_list;
/* Fill in the generic fields of the device structure. */
ether_setup(dev);
@@ -354,7 +364,6 @@
outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD);
if (request_irq(dev->irq, &el3_interrupt, 0, "3c509", dev)) {
- irq2dev_map[dev->irq] = NULL;
return -EAGAIN;
}
@@ -430,7 +439,7 @@
/* Transmitter timeout, serious problems. */
if (dev->tbusy) {
int tickssofar = jiffies - dev->trans_start;
- if (tickssofar < 40*HZ/100)
+ if (tickssofar < TX_TIMEOUT)
return 1;
printk("%s: transmit timed out, Tx_status %2.2x status %4.4x "
"Tx FIFO room %d.\n",
@@ -444,14 +453,6 @@
dev->tbusy = 0;
}
- if (skb == NULL) {
- dev_tint(dev);
- return 0;
- }
-
- if (skb->len <= 0)
- return 0;
-
if (el3_debug > 4) {
printk("%s: el3_start_xmit(length = %ld) called, status %4.4x.\n",
dev->name, skb->len, inw(ioaddr + EL3_STATUS));
@@ -483,7 +484,11 @@
outw(skb->len, ioaddr + TX_FIFO);
outw(0x00, ioaddr + TX_FIFO);
/* ... and the packet rounded to a doubleword. */
+#ifdef __powerpc__
+ outsl_unswapped(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
+#else
outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
+#endif
dev->trans_start = jiffies;
if (inw(ioaddr + TX_FREE) > 1536) {
@@ -516,7 +521,7 @@
{
struct device *dev = (struct device *)dev_id;
int ioaddr, status;
- int i = 0;
+ int i = INTR_WORK;
if (dev == NULL) {
printk ("el3_interrupt(): irq %d for unknown device.\n", irq);
@@ -568,7 +573,7 @@
}
}
- if (++i > 10) {
+ if (--i < 0) {
printk("%s: Infinite loop in interrupt, status %4.4x.\n",
dev->name, status);
/* Clear all interrupts. */
@@ -667,13 +672,18 @@
pkt_len, rx_status);
if (skb != NULL) {
skb->dev = dev;
- skb_reserve(skb,2); /* Align IP on 16 byte */
+ skb_reserve(skb, 2); /* Align IP on 16 byte */
/* 'skb->data' points to the start of sk_buff data area. */
- insl(ioaddr+RX_FIFO, skb_put(skb,pkt_len),
- (pkt_len + 3) >> 2);
+#ifdef __powerpc__
+ insl_unswapped(ioaddr+RX_FIFO, skb_put(skb,pkt_len),
+ (pkt_len + 3) >> 2);
+#else
+ insl(ioaddr + RX_FIFO, skb_put(skb,pkt_len),
+ (pkt_len + 3) >> 2);
+#endif
- skb->protocol=eth_type_trans(skb,dev);
+ skb->protocol = eth_type_trans(skb,dev);
netif_rx(skb);
outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */
lp->stats.rx_packets++;
@@ -692,7 +702,6 @@
return 0;
}
-#ifdef HAVE_MULTICAST
/*
* Set or clear the multicast filter for this adaptor.
*/
@@ -717,7 +726,6 @@
else
outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD);
}
-#endif
static int
el3_close(struct device *dev)
@@ -758,43 +766,50 @@
}
#ifdef MODULE
-static char devicename[9] = { 0, };
-static struct device dev_3c509 = {
- devicename, /* device name is inserted by linux/drivers/net/net_init.c */
- 0, 0, 0, 0,
- 0, 0,
- 0, 0, 0, NULL, el3_probe };
-
-static int io = 0;
-static int irq = 0;
+/* Parameter that may be passed into the module. */
+static int debug = -1;
+static int irq[] = {-1, -1, -1, -1, -1, -1, -1, -1};
+static int xcvr[] = {-1, -1, -1, -1, -1, -1, -1, -1};
int
init_module(void)
{
- dev_3c509.base_addr = io;
- dev_3c509.irq = irq;
- if (!EISA_bus && !io) {
- printk("3c509: WARNING! Module load-time probing works reliably only for EISA bus!!\n");
+ int el3_cards = 0;
+
+ if (debug >= 0)
+ el3_debug = debug;
+
+ el3_root_dev = NULL;
+ while (el3_probe(0) == 0) {
+ if (irq[el3_cards] > 1)
+ el3_root_dev->irq = irq[el3_cards];
+ if (xcvr[el3_cards] >= 0)
+ el3_root_dev->if_port = xcvr[el3_cards];
+ el3_cards++;
}
- if (register_netdev(&dev_3c509) != 0)
- return -EIO;
- return 0;
+
+ return el3_cards ? 0 : -ENODEV;
}
void
cleanup_module(void)
{
- unregister_netdev(&dev_3c509);
- kfree_s(dev_3c509.priv,sizeof(struct el3_private));
- dev_3c509.priv=NULL;
- /* If we don't do this, we can't re-insmod it later. */
- release_region(dev_3c509.base_addr, EL3_IO_EXTENT);
+ struct device *next_dev;
+
+ /* No need to check MOD_IN_USE, as sys_delete_module() checks. */
+ while (el3_root_dev) {
+ next_dev = ((struct el3_private *)el3_root_dev->priv)->next_dev;
+ unregister_netdev(el3_root_dev);
+ release_region(el3_root_dev->base_addr, EL3_IO_EXTENT);
+ kfree(el3_root_dev);
+ el3_root_dev = next_dev;
+ }
}
#endif /* MODULE */
/*
* Local variables:
- * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c 3c509.c"
+ * compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c509.c"
* version-control: t
* kept-new-versions: 5
* tab-width: 4
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov