patch-2.0.4 linux/drivers/net/3c59x.c
Next file: linux/drivers/net/arcnet.c
Previous file: linux/drivers/char/baycom.c
Back to the patch index
Back to the overall index
- Lines: 742
- Date:
Mon Jul 8 13:45:55 1996
- Orig file:
v2.0.3/linux/drivers/net/3c59x.c
- Orig date:
Wed Apr 17 09:06:31 1996
diff -u --recursive --new-file v2.0.3/linux/drivers/net/3c59x.c linux/drivers/net/3c59x.c
@@ -7,24 +7,24 @@
This driver is for the 3Com "Vortex" series ethercards. Members of
the series include the 3c590 PCI EtherLink III and 3c595-Tx PCI Fast
- EtherLink. It also works with the 10Mbs-only 3c590 PCI EtherLink III.
+ EtherLink.
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 char *version = "3c59x.c:v0.13 2/13/96 becker@cesdis.gsfc.nasa.gov\n";
+static char *version = "3c59x.c:v0.25 5/17/96 becker@cesdis.gsfc.nasa.gov\n";
/* "Knobs" that turn on special features. */
+/* Enable the experimental automatic media selection code. */
+#define AUTOMEDIA 1
+
/* Allow the use of bus master transfers instead of programmed-I/O for the
Tx process. Bus master transfers are always disabled by default, but
iff this is set they may be turned on using 'options'. */
#define VORTEX_BUS_MASTER
-/* Put out somewhat more debugging messages. (0 - no msg, 1 minimal msgs). */
-#define VORTEX_DEBUG 1
-
#include <linux/module.h>
#include <linux/kernel.h>
@@ -46,10 +46,23 @@
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
-#ifdef HAVE_SHARED_IRQ
-#define USE_SHARED_IRQ
-#include <linux/shared_irq.h>
-#endif
+#define RUN_AT(x) (jiffies + (x))
+#define DEV_ALLOC_SKB(len) dev_alloc_skb(len + 2)
+
+#define FREE_IRQ(irqnum, dev) free_irq(irqnum, dev)
+#define REQUEST_IRQ(i,h,f,n, instance) request_irq(i,h,f,n, instance)
+#define IRQ(irq, dev_id, pt_regs) (irq, dev_id, pt_regs)
+
+/* "Knobs" for adjusting internal parameters. */
+/* Put out somewhat more debugging messages. (0 - no msg, 1 minimal msgs). */
+#define VORTEX_DEBUG 2
+
+/* Number of times to check to see if the Tx FIFO has space, used in some
+ limited cases. */
+#define WAIT_TX_AVAIL 200
+
+/* Operational parameter that usually are not changed. */
+#define TX_TIMEOUT 40 /* Time in jiffies before concluding Tx hung */
/* The total size is twice that of the original EtherLinkIII series: the
runtime register window, window 1, is now always mapped in. */
@@ -117,8 +130,8 @@
Thanks to Cameron Spitzer and Terry Murphy of 3Com for providing both
3c590 and 3c595 boards.
The name "Vortex" is the internal 3Com project name for the PCI ASIC, and
-the not-yet-released (3/95) EISA version is called "Demon". According to
-Terry these names come from rides at the local amusement park.
+the EISA version is called "Demon". According to Terry these names come
+from rides at the local amusement park.
The new chips support both ethernet (1.5K) and FDDI (4.5K) packet sizes!
This driver only supports ethernet packets because of the skbuff allocation
@@ -176,6 +189,7 @@
};
enum Window0 {
Wn0EepromCmd = 10, /* Window 0: EEPROM command register. */
+ Wn0EepromData = 12, /* Window 0: EEPROM results register. */
};
enum Win0_EEPROM_bits {
EEPROM_Read = 0x80, EEPROM_WRITE = 0x40, EEPROM_ERASE = 0xC0,
@@ -206,7 +220,10 @@
Wn4_Media = 0x0A, /* Window 4: Various transcvr/media bits. */
};
enum Win4_Media_bits {
- Media_TP = 0x00C0, /* Enable link beat and jabber for 10baseT. */
+ Media_SQE = 0x0008, /* Enable SQE error counting for AUI. */
+ Media_10TP = 0x00C0, /* Enable link beat and jabber for 10baseT. */
+ Media_Lnk = 0x0080, /* Enable just link beat for 100TX/100FX. */
+ Media_LnkBeat = 0x0800,
};
enum Window7 { /* Window 7: Bus Master control. */
Wn7_MasterAddr = 0, Wn7_MasterLen = 6, Wn7_MasterStatus = 12,
@@ -217,17 +234,36 @@
const char *product_name;
struct device *next_module;
struct enet_statistics stats;
-#ifdef VORTEX_BUS_MASTER
struct sk_buff *tx_skb; /* Packet being eaten by bus master ctrl. */
-#endif
struct timer_list timer; /* Media selection timer. */
- int options; /* User-settable driver options (none yet). */
- unsigned int media_override:3, full_duplex:1, bus_master:1, autoselect:1;
+ int options; /* User-settable misc. driver options. */
+ int last_rx_packets; /* For media autoselection. */
+ unsigned int available_media:8, /* From Wn3_Options */
+ media_override:3, /* Passed-in media type. */
+ default_media:3, /* Read from the EEPROM. */
+ full_duplex:1, bus_master:1, autoselect:1;
};
-static char *if_names[] = {
- "10baseT", "10Mbs AUI", "undefined", "10base2",
- "100baseTX", "100baseFX", "MII", "undefined"};
+/* The action to take with a media selection timer tick.
+ Note that we deviate from the 3Com order by checking 10base2 before AUI.
+ */
+static struct media_table {
+ char *name;
+ unsigned int media_bits:16, /* Bits to set in Wn4_Media register. */
+ mask:8, /* The transceiver-present bit in Wn3_Config.*/
+ next:8; /* The media type to try next. */
+ short wait; /* Time before we check media status. */
+} media_tbl[] = {
+ { "10baseT", Media_10TP,0x08, 3 /* 10baseT->10base2 */, (14*HZ)/10},
+ { "10Mbs AUI", Media_SQE, 0x20, 8 /* AUI->default */, (1*HZ)/10},
+ { "undefined", 0, 0x80, 0 /* Undefined */, 0},
+ { "10base2", 0, 0x10, 1 /* 10base2->AUI. */, (1*HZ)/10},
+ { "100baseTX", Media_Lnk, 0x02, 5 /* 100baseTX->100baseFX */, (14*HZ)/10},
+ { "100baseFX", Media_Lnk, 0x04, 6 /* 100baseFX->MII */, (14*HZ)/10},
+ { "MII", 0, 0x40, 0 /* MII->10baseT */, (14*HZ)/10},
+ { "undefined", 0, 0x01, 0 /* Undefined/100baseT4 */, 0},
+ { "Default", 0, 0xFF, 0 /* Use default */, 0},
+};
static int vortex_scan(struct device *dev);
static int vortex_found_device(struct device *dev, int ioaddr, int irq,
@@ -237,11 +273,11 @@
static void vortex_timer(unsigned long arg);
static int vortex_start_xmit(struct sk_buff *skb, struct device *dev);
static int vortex_rx(struct device *dev);
-static void vortex_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static void vortex_interrupt IRQ(int irq, void *dev_id, struct pt_regs *regs);
static int vortex_close(struct device *dev);
static void update_stats(int addr, struct device *dev);
static struct enet_statistics *vortex_get_stats(struct device *dev);
-static void set_multicast_list(struct device *dev);
+static void set_rx_mode(struct device *dev);
/* Unlike the other PCI cards the 59x cards don't need a large contiguous
@@ -260,7 +296,7 @@
*/
/* This driver uses 'options' to pass the media type, full-duplex flag, etc. */
/* Note: this is the only limit on the number of cards supported!! */
-int options[8] = { -1, -1, -1, -1, -1, -1, -1, -1,};
+static int options[8] = { -1, -1, -1, -1, -1, -1, -1, -1,};
#ifdef MODULE
static int debug = -1;
@@ -283,7 +319,7 @@
}
#else
-unsigned long tc59x_probe(struct device *dev)
+int tc59x_probe(struct device *dev)
{
int cards_found = 0;
@@ -302,55 +338,54 @@
if (pcibios_present()) {
static int pci_index = 0;
- for (; pci_index < 8; pci_index++) {
- unsigned char pci_bus, pci_device_fn, pci_irq_line, pci_latency;
- unsigned int pci_ioaddr;
- unsigned short pci_command;
- int index;
-
- for (index = 0; product_ids[index]; index++) {
- if ( ! pcibios_find_device(TCOM_VENDOR_ID, product_ids[index],
- pci_index, &pci_bus,
- &pci_device_fn))
+ static int board_index = 0;
+ for (; product_ids[board_index]; board_index++, pci_index = 0) {
+ for (; pci_index < 16; pci_index++) {
+ unsigned char pci_bus, pci_device_fn, pci_irq_line;
+ unsigned char pci_latency;
+ unsigned int pci_ioaddr;
+ unsigned short pci_command;
+
+ if (pcibios_find_device(TCOM_VENDOR_ID,
+ product_ids[board_index], pci_index,
+ &pci_bus, &pci_device_fn))
break;
- }
- if ( ! product_ids[index])
- 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;
+ 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;
#ifdef VORTEX_BUS_MASTER
- /* Get and check the bus-master and latency values.
- Some PCI BIOSes fail to set the master-enable bit, and
- the latency timer must be set to the maximum value to avoid
- data corruption that occurs when the timer expires during
- a transfer. Yes, it's a bug. */
- pcibios_read_config_word(pci_bus, pci_device_fn,
- PCI_COMMAND, &pci_command);
- if ( ! (pci_command & PCI_COMMAND_MASTER)) {
- printk(" PCI Master Bit has not been set! Setting...\n");
- pci_command |= PCI_COMMAND_MASTER;
- pcibios_write_config_word(pci_bus, pci_device_fn,
- PCI_COMMAND, pci_command);
- }
- pcibios_read_config_byte(pci_bus, pci_device_fn,
+ /* Get and check the bus-master and latency values.
+ Some PCI BIOSes fail to set the master-enable bit, and
+ the latency timer must be set to the maximum value to avoid
+ data corruption that occurs when the timer expires during
+ a transfer. Yes, it's a bug. */
+ pcibios_read_config_word(pci_bus, pci_device_fn,
+ PCI_COMMAND, &pci_command);
+ if ( ! (pci_command & PCI_COMMAND_MASTER)) {
+ printk(" PCI Master Bit has not been set! Setting...\n");
+ pci_command |= PCI_COMMAND_MASTER;
+ pcibios_write_config_word(pci_bus, pci_device_fn,
+ PCI_COMMAND, pci_command);
+ }
+ pcibios_read_config_byte(pci_bus, pci_device_fn,
PCI_LATENCY_TIMER, &pci_latency);
- if (pci_latency != 255) {
- printk(" Overriding PCI latency timer (CFLT) setting of %d, new value is 255.\n", pci_latency);
- pcibios_write_config_byte(pci_bus, pci_device_fn,
- PCI_LATENCY_TIMER, 255);
- }
+ if (pci_latency != 255) {
+ printk(" Overriding PCI latency timer (CFLT) setting of"
+ " %d, new value is 255.\n", pci_latency);
+ pcibios_write_config_byte(pci_bus, pci_device_fn,
+ PCI_LATENCY_TIMER, 255);
+ }
#endif /* VORTEX_BUS_MASTER */
- vortex_found_device(dev, pci_ioaddr, pci_irq_line, index,
- dev && dev->mem_start ? dev->mem_start
- : options[cards_found]);
- dev = 0;
- cards_found++;
+ vortex_found_device(dev, pci_ioaddr, pci_irq_line, board_index,
+ dev && dev->mem_start ? dev->mem_start
+ : options[cards_found]);
+ dev = 0;
+ cards_found++;
+ }
}
}
@@ -361,9 +396,9 @@
/* Check the standard EISA ID register for an encoded '3Com'. */
if (inw(ioaddr + 0xC80) != 0x6d50)
continue;
- /* Check for a product that we support. */
- if ((inw(ioaddr + 0xC82) & 0xFFF0) != 0x5970
- && (inw(ioaddr + 0xC82) & 0xFFF0) != 0x5920)
+ /* Check for a product that we support, 3c59{2,7} any rev. */
+ if ((inw(ioaddr + 0xC82) & 0xF0FF) != 0x7059 /* 597 */
+ && (inw(ioaddr + 0xC82) & 0xF0FF) != 0x2059) /* 592 */
continue;
vortex_found_device(dev, ioaddr, inw(ioaddr + 0xC88) >> 12,
DEMON_INDEX, dev && dev->mem_start
@@ -452,12 +487,12 @@
int timer;
outw(EEPROM_Read + PhysAddr01 + i, ioaddr + Wn0EepromCmd);
/* Pause for at least 162 us. for the read to take place. */
- for (timer = 0; timer < 162*4 + 400; timer++) {
+ for (timer = 162*4 + 400; timer >= 0; timer--) {
SLOW_DOWN_IO;
if ((inw(ioaddr + Wn0EepromCmd) & 0x8000) == 0)
break;
}
- phys_addr[i] = htons(inw(ioaddr + 12));
+ phys_addr[i] = htons(inw(ioaddr + Wn0EepromData));
}
for (i = 0; i < 6; i++)
printk("%c%2.2x", i ? ':' : ' ', dev->dev_addr[i]);
@@ -470,6 +505,7 @@
char *ram_split[] = {"5:3", "3:1", "1:1", "invalid"};
union wn3_config config;
EL3WINDOW(3);
+ vp->available_media = inw(ioaddr + Wn3_Options);
config.i = inl(ioaddr + Wn3_Config);
if (vortex_debug > 1)
printk(" Internal config register is %4.4x, transceivers %#x.\n",
@@ -479,8 +515,9 @@
config.u.ram_width ? "word" : "byte",
ram_split[config.u.ram_split],
config.u.autoselect ? "autoselect/" : "",
- if_names[config.u.xcvr]);
+ media_tbl[config.u.xcvr].name);
dev->if_port = config.u.xcvr;
+ vp->default_media = config.u.xcvr;
vp->autoselect = config.u.autoselect;
}
@@ -492,10 +529,7 @@
dev->hard_start_xmit = &vortex_start_xmit;
dev->stop = &vortex_close;
dev->get_stats = &vortex_get_stats;
- dev->set_multicast_list = &set_multicast_list;
-#if defined (HAVE_SET_MAC_ADDR) && 0
- dev->set_mac_address = &set_mac_address;
-#endif
+ dev->set_multicast_list = &set_rx_mode;
return 0;
}
@@ -518,11 +552,29 @@
if (vp->media_override != 7) {
if (vortex_debug > 1)
printk("%s: Media override to transceiver %d (%s).\n",
- dev->name, vp->media_override, if_names[vp->media_override]);
- config.u.xcvr = vp->media_override;
+ dev->name, vp->media_override,
+ media_tbl[vp->media_override].name);
dev->if_port = vp->media_override;
- outl(config.i, ioaddr + Wn3_Config);
- }
+ } else if (vp->autoselect) {
+ /* Find first available media type, starting with 100baseTx. */
+ dev->if_port = 4;
+ while (! (vp->available_media & media_tbl[dev->if_port].mask))
+ dev->if_port = media_tbl[dev->if_port].next;
+
+ if (vortex_debug > 1)
+ printk("%s: Initial media type %s.\n",
+ dev->name, media_tbl[dev->if_port].name);
+
+ init_timer(&vp->timer);
+ vp->timer.expires = RUN_AT(media_tbl[dev->if_port].wait);
+ vp->timer.data = (unsigned long)dev;
+ vp->timer.function = &vortex_timer; /* timer handler */
+ add_timer(&vp->timer);
+ } else
+ dev->if_port = vp->default_media;
+
+ config.u.xcvr = dev->if_port;
+ outl(config.i, ioaddr + Wn3_Config);
if (vortex_debug > 1) {
printk("%s: vortex_open() InternalConfig %8.8x.\n",
@@ -542,19 +594,11 @@
outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD);
-#ifdef USE_SHARED_IRQ
- i = request_shared_irq(dev->irq, &vortex_interrupt, dev, vp->product_name);
- if (i) /* Error */
- return i;
-#else
- if (dev->irq == 0 || irq2dev_map[dev->irq] != NULL)
- return -EAGAIN;
- irq2dev_map[dev->irq] = dev;
- if (request_irq(dev->irq, &vortex_interrupt, 0, vp->product_name, NULL)) {
- irq2dev_map[dev->irq] = NULL;
+ /* Use the now-standard shared IRQ implementation. */
+ if (request_irq(dev->irq, &vortex_interrupt, SA_SHIRQ,
+ vp->product_name, dev)) {
return -EAGAIN;
}
-#endif
if (vortex_debug > 1) {
EL3WINDOW(4);
@@ -572,11 +616,9 @@
if (dev->if_port == 3)
/* Start the thinnet transceiver. We should really wait 50ms...*/
outw(StartCoax, ioaddr + EL3_CMD);
- else if (dev->if_port == 0) {
- /* 10baseT interface, enabled link beat and jabber check. */
- EL3WINDOW(4);
- outw(inw(ioaddr + Wn4_Media) | Media_TP, ioaddr + Wn4_Media);
- }
+ EL3WINDOW(4);
+ outw((inw(ioaddr + Wn4_Media) & ~(Media_10TP|Media_SQE)) |
+ media_tbl[dev->if_port].media_bits, ioaddr + Wn4_Media);
/* Switch to the stats window, and clear all stats by reading. */
outw(StatsDisable, ioaddr + EL3_CMD);
@@ -592,8 +634,8 @@
/* Switch to register set 7 for normal use. */
EL3WINDOW(7);
- /* Accept b-case and phys addr only. */
- outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD);
+ /* Set reciever mode: presumably accept b-case and phys addr only. */
+ set_rx_mode(dev);
outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */
dev->tbusy = 0;
@@ -610,26 +652,83 @@
outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull
| DMADone, ioaddr + EL3_CMD);
-#ifdef MODULE
MOD_INC_USE_COUNT;
-#endif
- if (vp->autoselect) {
- init_timer(&vp->timer);
- vp->timer.expires = (14*HZ)/10; /* 1.4 sec. */
- vp->timer.data = (unsigned long)dev;
- vp->timer.function = &vortex_timer; /* timer handler */
- add_timer(&vp->timer);
- }
return 0;
}
static void vortex_timer(unsigned long data)
{
+#ifdef AUTOMEDIA
struct device *dev = (struct device *)data;
- if (vortex_debug > 2)
- printk("%s: Media selection timer tick happened.\n", dev->name);
- /* ToDo: active media selection here! */
+ struct vortex_private *vp = (struct vortex_private *)dev->priv;
+ int ioaddr = dev->base_addr;
+ unsigned long flags;
+ int ok = 0;
+
+ if (vortex_debug > 1)
+ printk("%s: Media selection timer tick happened, %s.\n",
+ dev->name, media_tbl[dev->if_port].name);
+
+ save_flags(flags); cli(); {
+ int old_window = inw(ioaddr + EL3_CMD) >> 13;
+ int media_status;
+ EL3WINDOW(4);
+ media_status = inw(ioaddr + Wn4_Media);
+ switch (dev->if_port) {
+ case 0: case 4: case 5: /* 10baseT, 100baseTX, 100baseFX */
+ if (media_status & Media_LnkBeat) {
+ ok = 1;
+ if (vortex_debug > 1)
+ printk("%s: Media %s has link beat, %x.\n",
+ dev->name, media_tbl[dev->if_port].name, media_status);
+ } else if (vortex_debug > 1)
+ printk("%s: Media %s is has no link beat, %x.\n",
+ dev->name, media_tbl[dev->if_port].name, media_status);
+
+ break;
+ default: /* Other media types handled by Tx timeouts. */
+ if (vortex_debug > 1)
+ printk("%s: Media %s is has no indication, %x.\n",
+ dev->name, media_tbl[dev->if_port].name, media_status);
+ ok = 1;
+ }
+ if ( ! ok) {
+ union wn3_config config;
+
+ do {
+ dev->if_port = media_tbl[dev->if_port].next;
+ } while ( ! (vp->available_media & media_tbl[dev->if_port].mask));
+ if (dev->if_port == 8) { /* Go back to default. */
+ dev->if_port = vp->default_media;
+ if (vortex_debug > 1)
+ printk("%s: Media selection failing, using default %s port.\n",
+ dev->name, media_tbl[dev->if_port].name);
+ } else {
+ if (vortex_debug > 1)
+ printk("%s: Media selection failed, now trying %s port.\n",
+ dev->name, media_tbl[dev->if_port].name);
+ vp->timer.expires = RUN_AT(media_tbl[dev->if_port].wait);
+ add_timer(&vp->timer);
+ }
+ outw((media_status & ~(Media_10TP|Media_SQE)) |
+ media_tbl[dev->if_port].media_bits, ioaddr + Wn4_Media);
+
+ EL3WINDOW(3);
+ config.i = inl(ioaddr + Wn3_Config);
+ config.u.xcvr = dev->if_port;
+ outl(config.i, ioaddr + Wn3_Config);
+
+ outw(dev->if_port == 3 ? StartCoax : StopCoax, ioaddr + EL3_CMD);
+ }
+ EL3WINDOW(old_window);
+ } restore_flags(flags);
+ if (vortex_debug > 1)
+ printk("%s: Media selection timer finished, %s.\n",
+ dev->name, media_tbl[dev->if_port].name);
+
+#endif /* AUTOMEDIA*/
+ return;
}
static int
@@ -638,33 +737,49 @@
struct vortex_private *vp = (struct vortex_private *)dev->priv;
int ioaddr = dev->base_addr;
- /* Transmitter timeout, serious problems. */
- if (dev->tbusy) {
+ /* Part of the following code is inspired by code from Giuseppe Ciaccio,
+ ciaccio@disi.unige.it.
+ It works around a ?bug? in the 8K Vortex that only occurs on some
+ systems: the TxAvailable interrupt seems to be lost.
+ The ugly work-around is to busy-wait for room available in the Tx
+ buffer before deciding the transmitter is actually hung.
+ This busy-wait should never really occur, since the problem is that
+ there actually *is* room in the Tx FIFO.
+
+ This pointed out an optimization -- we can ignore dev->tbusy if
+ we actually have room for this packet.
+ */
+
+ if (inw(ioaddr + TxFree) > skb->len) /* We actually have free room. */
+ dev->tbusy = 0; /* Fake out the check below. */
+ else if (dev->tbusy) {
+ /* Transmitter timeout, serious problems. */
int tickssofar = jiffies - dev->trans_start;
- if (tickssofar < 40)
+ int i;
+
+ if (tickssofar < 2) /* We probably aren't empty. */
return 1;
- printk("%s: transmit timed out, tx_status %2.2x status %4.4x.\n",
- dev->name, inb(ioaddr + TxStatus), inw(ioaddr + EL3_STATUS));
- vp->stats.tx_errors++;
- /* Issue TX_RESET and TX_START commands. */
- outw(TxReset, ioaddr + EL3_CMD);
- {
- int i;
+ /* Wait a while to see if there really is room. */
+ for (i = WAIT_TX_AVAIL; i >= 0; i--)
+ if (inw(ioaddr + TxFree) > skb->len)
+ break;
+ if ( i < 0) {
+ if (tickssofar < TX_TIMEOUT)
+ return 1;
+ printk("%s: transmit timed out, tx_status %2.2x status %4.4x.\n",
+ dev->name, inb(ioaddr + TxStatus), inw(ioaddr + EL3_STATUS));
+ /* Issue TX_RESET and TX_START commands. */
+ outw(TxReset, ioaddr + EL3_CMD);
for (i = 20; i >= 0 ; i--)
- if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress)
- break;
+ if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress) break;
+ outw(TxEnable, ioaddr + EL3_CMD);
+ dev->trans_start = jiffies;
+ dev->tbusy = 0;
+ vp->stats.tx_errors++;
+ vp->stats.tx_dropped++;
+ return 0; /* Yes, silently *drop* the packet! */
}
- outw(TxEnable, ioaddr + EL3_CMD);
- dev->trans_start = jiffies;
dev->tbusy = 0;
- 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
@@ -684,6 +799,7 @@
outw((skb->len + 3) & ~3, ioaddr + Wn7_MasterLen);
vp->tx_skb = skb;
outw(StartDMADown, ioaddr + EL3_CMD);
+ /* dev->tbusy will be cleared at the DMADone interrupt. */
} else {
/* ... and the packet rounded to a doubleword. */
outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
@@ -736,23 +852,15 @@
/* The interrupt handler does all of the Rx thread work and cleans up
after the Tx thread. */
-static void vortex_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+static void vortex_interrupt IRQ(int irq, void *dev_id, struct pt_regs *regs)
{
-#ifdef USE_SHARED_IRQ
- struct device *dev = (struct device *)(irq == 0 ? regs : irq2dev_map[irq]);
-#else
- struct device *dev = (struct device *)(irq2dev_map[irq]);
-#endif
+ /* Use the now-standard shared IRQ implementation. */
+ struct device *dev = dev_id;
struct vortex_private *lp;
int ioaddr, status;
int latency;
int i = 0;
- if (dev == NULL) {
- printk ("vortex_interrupt(): irq %d for unknown device.\n", irq);
- return;
- }
-
if (dev->interrupt)
printk("%s: Re-entering the interrupt handler.\n", dev->name);
dev->interrupt = 1;
@@ -774,7 +882,7 @@
if (donedidthis++ > 1) {
printk("%s: Bogus interrupt, bailing. Status %4.4x, start=%d.\n",
dev->name, status, dev->start);
- free_irq(dev->irq, NULL);
+ FREE_IRQ(dev->irq, dev);
}
}
@@ -833,10 +941,7 @@
/* Adapter failure requires Rx reset and reinit. */
outw(RxReset, ioaddr + EL3_CMD);
/* Set the Rx filter to the current state. */
- outw(SetRxFilter | RxStation | RxBroadcast
- | (dev->flags & IFF_ALLMULTI ? RxMulticast : 0)
- | (dev->flags & IFF_PROMISC ? RxProm : 0),
- ioaddr + EL3_CMD);
+ set_rx_mode(dev);
outw(RxEnable, ioaddr + EL3_CMD); /* Re-enable the receiver. */
outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD);
}
@@ -890,7 +995,7 @@
short pkt_len = rx_status & 0x1fff;
struct sk_buff *skb;
- skb = dev_alloc_skb(pkt_len + 5);
+ skb = DEV_ALLOC_SKB(pkt_len + 5);
if (vortex_debug > 4)
printk("Receiving packet size %d status %4.4x.\n",
pkt_len, rx_status);
@@ -927,6 +1032,7 @@
static int
vortex_close(struct device *dev)
{
+ struct vortex_private *vp = (struct vortex_private *)dev->priv;
int ioaddr = dev->base_addr;
dev->start = 0;
@@ -936,6 +1042,8 @@
printk("%s: vortex_close() status %4.4x, Tx status %2.2x.\n",
dev->name, inw(ioaddr + EL3_STATUS), inb(ioaddr + TxStatus));
+ del_timer(&vp->timer);
+
/* Turn off statistics ASAP. We update lp->stats below. */
outw(StatsDisable, ioaddr + EL3_CMD);
@@ -946,24 +1054,11 @@
if (dev->if_port == 3)
/* Turn off thinnet power. Green! */
outw(StopCoax, ioaddr + EL3_CMD);
- else if (dev->if_port == 0) {
- /* Disable link beat and jabber, if_port may change ere next open(). */
- EL3WINDOW(4);
- outw(inw(ioaddr + Wn4_Media) & ~Media_TP, ioaddr + Wn4_Media);
- }
-#ifdef USE_SHARED_IRQ
- free_shared_irq(dev->irq, dev);
-#else
- free_irq(dev->irq, NULL);
- /* Mmmm, we should disable all interrupt sources here. */
- irq2dev_map[dev->irq] = 0;
-#endif
+ FREE_IRQ(dev->irq, dev);
update_stats(ioaddr, dev);
-#ifdef MODULE
MOD_DEC_USE_COUNT;
-#endif
return 0;
}
@@ -1019,26 +1114,27 @@
return;
}
-/* There are two version of set_multicast_list() to support both v1.2 and
- v1.4 kernels. */
+/* This new version of set_rx_mode() supports v1.4 kernels.
+ The Vortex chip has no documented multicast filter, so the only
+ multicast setting is to receive all multicast frames. At least
+ the chip has a very clean way to set the mode, unlike many others. */
static void
-set_multicast_list(struct device *dev)
+set_rx_mode(struct device *dev)
{
short ioaddr = dev->base_addr;
+ short new_mode;
- if ((dev->mc_list) || (dev->flags & IFF_ALLMULTI)) {
- outw(SetRxFilter|RxStation|RxMulticast|RxBroadcast, ioaddr + EL3_CMD);
- if (vortex_debug > 3) {
- printk("%s: Setting Rx multicast mode, %d addresses.\n",
- dev->name, dev->mc_count);
- }
- } else if (dev->flags & IFF_PROMISC) {
- outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast | RxProm,
- ioaddr + EL3_CMD);
- } else
- outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD);
-}
+ if (dev->flags & IFF_PROMISC) {
+ if (vortex_debug > 3)
+ printk("%s: Setting promiscuous mode.\n", dev->name);
+ new_mode = SetRxFilter|RxStation|RxMulticast|RxBroadcast|RxProm;
+ } else if ((dev->mc_list) || (dev->flags & IFF_ALLMULTI)) {
+ new_mode = SetRxFilter|RxStation|RxMulticast|RxBroadcast;
+ } else
+ new_mode = SetRxFilter | RxStation | RxBroadcast;
+ outw(new_mode, ioaddr + EL3_CMD);
+}
#ifdef MODULE
void
@@ -1050,6 +1146,7 @@
while (root_vortex_dev) {
next_dev = ((struct vortex_private *)root_vortex_dev->priv)->next_module;
unregister_netdev(root_vortex_dev);
+ outw(TotalReset, root_vortex_dev->base_addr + EL3_CMD);
release_region(root_vortex_dev->base_addr, VORTEX_TOTAL_SIZE);
kfree(root_vortex_dev);
root_vortex_dev = next_dev;
@@ -1059,7 +1156,7 @@
/*
* Local variables:
- * compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c 3c59x.c -o 3c59x.o"
+ * compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c 3c59x.c -o ../../modules/3c59x.o"
* 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