patch-2.0.34 linux/drivers/net/ne2k-pci.c

Next file: linux/drivers/net/new_tunnel.c
Previous file: linux/drivers/net/ne.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.0.33/linux/drivers/net/ne2k-pci.c linux/drivers/net/ne2k-pci.c
@@ -0,0 +1,641 @@
+/* ne2k-pci.c: A NE2000 clone on PCI bus driver for Linux. */
+/*
+	A Linux device driver for PCI NE2000 clones.
+
+	Authorship and other copyrights:
+	1992-1998 by Donald Becker, NE2000 core and various modifications.
+	1995-1998 by Paul Gortmaker, core modifications and PCI support.
+
+	Copyright 1993 assigned to the 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.
+
+	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
+
+	People are making PCI ne2000 clones! Oh the horror, the horror...
+
+	Issues remaining:
+	No full-duplex support.
+*/
+
+/* Our copyright info must remain in the binary. */
+static const char *version =
+"ne2k-pci.c:v0.99L 2/7/98 D. Becker/P. Gortmaker http://cesdis.gsfc.nasa.gov/linux/drivers/ne2k-pci.html\n";
+
+#ifdef MODVERSIONS
+#include <linux/modversions.h>
+#endif
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include "8390.h"
+
+/* Set statically or when loading the driver module. */
+static debug = 1;
+
+/* Some defines that people can play with if so inclined. */
+
+/* Do #define LOAD_8390_BY_KERNELD to automatically load 8390 support. */
+#ifdef LOAD_8390_BY_KERNELD
+#include <linux/kerneld.h>
+#endif
+/* Use 32 bit data-movement operations instead of 16 bit. */
+#define USE_LONGIO
+
+/* Do we implement the read before write bugfix ? */
+/* #define NE_RW_BUGFIX */
+
+/* Do we have a non std. amount of memory? (in units of 256 byte pages) */
+/* #define PACKETBUF_MEMSIZE	0x40 */
+
+static struct {
+	unsigned short vendor, dev_id;
+	char *name;
+}
+pci_clone_list[] = {
+	{0x10ec, 0x8029, "RealTek RTL-8029"},
+	{0x1050, 0x0940, "Winbond 89C940"},
+	{0x11f6, 0x1401, "Compex RL2000"},
+	{0x8e2e, 0x3000, "KTI ET32P2"},
+	{0x4a14, 0x5000, "NetVin NV5000SC"},
+	{0x1106, 0x0926, "Via 82C926"},
+	{0,}
+};
+
+/* ---- No user-serviceable parts below ---- */
+
+#define NE_BASE	 (dev->base_addr)
+#define NE_CMD	 	0x00
+#define NE_DATAPORT	0x10	/* NatSemi-defined port window offset. */
+#define NE_RESET	0x1f	/* Issue a read to reset, a write to clear. */
+#define NE_IO_EXTENT	0x20
+
+#define NESM_START_PG	0x40	/* First page of TX buffer */
+#define NESM_STOP_PG	0x80	/* Last page +1 of RX ring */
+
+int ne2k_pci_probe(struct device *dev);
+static struct device *ne2k_pci_probe1(struct device *dev, int ioaddr, int irq);
+
+static int ne_open(struct device *dev);
+static int ne_close(struct device *dev);
+
+static void ne_reset_8390(struct device *dev);
+static void ne_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
+			  int ring_page);
+static void ne_block_input(struct device *dev, int count,
+			  struct sk_buff *skb, int ring_offset);
+static void ne_block_output(struct device *dev, const int count,
+		const unsigned char *buf, const int start_page);
+
+
+
+/* No room in the standard 8390 structure for extra info we need. */
+struct ne2k_pci_card {
+	struct ne2k_pci_card *next;
+	struct device *dev;
+	unsigned char pci_bus, pci_device_fn;
+};
+/* A list of all installed devices, for removing the driver module. */
+static struct ne2k_pci_card *ne2k_card_list = NULL;
+
+#ifdef LOAD_8390_BY_KERNELD
+static int (*Lethdev_init)(struct device *dev);
+static void (*LNS8390_init)(struct device *dev, int startp);
+static int (*Lei_open)(struct device *dev);
+static int (*Lei_close)(struct device *dev);
+static void (*Lei_interrupt)(int irq, void *dev_id, struct pt_regs *regs);
+#else
+#define Lethdev_init ethdev_init
+#define LNS8390_init NS8390_init
+#define Lei_open ei_open
+#define Lei_close ei_close
+#define Lei_interrupt ei_interrupt
+#endif
+
+#ifdef MODULE
+
+int
+init_module(void)
+{
+	/* We must emit version information. */
+	if (debug)
+		printk(KERN_INFO "%s", version);
+
+	return ne2k_pci_probe(0);
+}
+
+void
+cleanup_module(void)
+{
+	struct device *dev;
+	struct ne2k_pci_card *this_card;
+
+	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */
+	while (ne2k_card_list) {
+		dev = ne2k_card_list->dev;
+		unregister_netdev(dev);
+		release_region(dev->base_addr, NE_IO_EXTENT);
+		kfree(dev);
+		this_card = ne2k_card_list;
+		ne2k_card_list = ne2k_card_list->next;
+		kfree(this_card);
+	}
+
+#ifdef LOAD_8390_BY_KERNELD
+	release_module("8390", 0);
+#endif
+}
+
+#endif  /* MODULE */
+
+/*
+  NEx000-clone boards have a Station Address (SA) PROM (SAPROM) in the packet
+  buffer memory space.  By-the-spec NE2000 clones have 0x57,0x57 in bytes
+  0x0e,0x0f of the SAPROM, while other supposed NE2000 clones must be
+  detected by their SA prefix.
+
+  Reading the SAPROM from a word-wide card with the 8390 set in byte-wide
+  mode results in doubled values, which can be detected and compensated for.
+
+  The probe is also responsible for initializing the card and filling
+  in the 'dev' and 'ei_status' structures.
+*/
+
+#ifdef HAVE_DEVLIST
+struct netdev_entry netcard_drv =
+{"ne2k_pci", ne2k_pci_probe1, NE_IO_EXTENT, 0};
+#endif
+
+int ne2k_pci_probe(struct device *dev)
+{
+	static int pci_index = 0;	/* Static, for multiple calls. */
+	int cards_found = 0;
+	int i;
+
+	if ( ! pcibios_present())
+		return -ENODEV;
+
+#ifndef MODULE
+	{
+		static unsigned version_printed = 0;
+		if (version_printed++ == 0)
+			printk(KERN_INFO "%s", version);
+	}
+#endif
+
+	for (;pci_index < 0xff; pci_index++) {
+		unsigned char pci_bus, pci_device_fn;
+		u8 pci_irq_line;
+		u16 pci_command, new_command, vendor, device;
+		u32 pci_ioaddr;
+
+		if (pcibios_find_class (PCI_CLASS_NETWORK_ETHERNET << 8, pci_index,
+								&pci_bus, &pci_device_fn)
+			!= PCIBIOS_SUCCESSFUL)
+			break;
+		pcibios_read_config_word(pci_bus, pci_device_fn,
+								 PCI_VENDOR_ID, &vendor);
+		pcibios_read_config_word(pci_bus, pci_device_fn,
+								 PCI_DEVICE_ID, &device);
+
+		/* Note: some vendor IDs (RealTek) have non-NE2k cards as well. */
+		for (i = 0; pci_clone_list[i].vendor != 0; i++)
+			if (pci_clone_list[i].vendor == vendor
+				&& pci_clone_list[i].dev_id == device)
+				break;
+		if (pci_clone_list[i].vendor == 0)
+			continue;
+
+		pcibios_read_config_dword(pci_bus, pci_device_fn,
+								  PCI_BASE_ADDRESS_0, &pci_ioaddr);
+		pcibios_read_config_byte(pci_bus, pci_device_fn,
+								 PCI_INTERRUPT_LINE, &pci_irq_line);
+		pcibios_read_config_word(pci_bus, pci_device_fn,
+								 PCI_COMMAND, &pci_command);
+
+		/* Remove I/O space marker in bit 0. */
+		pci_ioaddr &= PCI_BASE_ADDRESS_IO_MASK;
+
+		/* Avoid already found cards from previous calls */
+		if (check_region(pci_ioaddr, NE_IO_EXTENT))
+			continue;
+
+		/* Activate the card: fix for brain-damaged Win98 BIOSes. */
+		new_command = pci_command | PCI_COMMAND_IO;
+		if (pci_command != new_command) {
+			printk(KERN_INFO "  The PCI BIOS has not enabled this"
+				   " NE2k clone!  Updating PCI command %4.4x->%4.4x.\n",
+				   pci_command, new_command);
+			pcibios_write_config_word(pci_bus, pci_device_fn,
+									  PCI_COMMAND, new_command);
+		}
+
+		if (pci_irq_line <= 0 || pci_irq_line >= NR_IRQS)
+			printk(KERN_WARNING " WARNING: The PCI BIOS assigned this PCI NE2k"
+				   " card to IRQ %d, which is unlikely to work!.\n"
+				   KERN_WARNING " You should use the PCI BIOS setup to assign"
+				   " a valid IRQ line.\n", pci_irq_line);
+
+		printk("ne2k-pci.c: PCI NE2000 clone '%s' at I/O %#x, IRQ %d.\n",
+			   pci_clone_list[i].name, pci_ioaddr, pci_irq_line);
+		dev = ne2k_pci_probe1(dev, pci_ioaddr, pci_irq_line);
+		if (dev == 0) {
+			/* Should not happen. */
+			printk(KERN_ERR "ne2k-pci: Probe of PCI card at %#x failed.\n",
+				   pci_ioaddr);
+			continue;
+		} else {
+			struct ne2k_pci_card *ne2k_card =
+				kmalloc(sizeof(struct ne2k_pci_card), GFP_KERNEL);
+			ne2k_card->next = ne2k_card_list;
+			ne2k_card_list = ne2k_card;
+			ne2k_card->dev = dev;
+			ne2k_card->pci_bus = pci_bus;
+			ne2k_card->pci_device_fn = pci_device_fn;
+		}
+		dev = 0;
+
+		cards_found++;
+	}
+
+	return cards_found ? 0 : -ENODEV;
+}
+
+static struct device *ne2k_pci_probe1(struct device *dev, int ioaddr, int irq)
+{
+	int i;
+	unsigned char SA_prom[32];
+	const char *name = NULL;
+	int start_page, stop_page;
+	int reg0 = inb(ioaddr);
+
+	if (reg0 == 0xFF)
+		return 0;
+
+	/* Do a preliminary verification that we have a 8390. */
+	{
+		int regd;
+		outb(E8390_NODMA+E8390_PAGE1+E8390_STOP, ioaddr + E8390_CMD);
+		regd = inb(ioaddr + 0x0d);
+		outb(0xff, ioaddr + 0x0d);
+		outb(E8390_NODMA+E8390_PAGE0, ioaddr + E8390_CMD);
+		inb(ioaddr + EN0_COUNTER0); /* Clear the counter by reading. */
+		if (inb(ioaddr + EN0_COUNTER0) != 0) {
+			outb(reg0, ioaddr);
+			outb(regd, ioaddr + 0x0d);	/* Restore the old values. */
+			return 0;
+		}
+	}
+
+	dev = init_etherdev(dev, 0);
+
+	/* Reset card. Who knows what dain-bramaged state it was left in. */
+	{
+		unsigned long reset_start_time = jiffies;
+
+		outb(inb(ioaddr + NE_RESET), ioaddr + NE_RESET);
+
+		/* This looks like a horrible timing loop, but it should never take
+		   more than a few cycles.
+		*/
+		while ((inb(ioaddr + EN0_ISR) & ENISR_RESET) == 0)
+			/* Limit wait: '2' avoids jiffy roll-over. */
+			if (jiffies - reset_start_time > 2) {
+				printk("ne2k-pci: Card failure (no reset ack).\n");
+				return 0;
+			}
+		
+		outb(0xff, ioaddr + EN0_ISR);		/* Ack all intr. */
+	}
+
+#if defined(LOAD_8390_BY_KERNELD)
+	/* We are now certain the 8390 module is required. */
+	if (request_module("8390")) {
+		printk("ne2k-pci: Failed to load the 8390 core module.\n");
+		return 0;
+	}
+	if ((Lethdev_init = (void*)get_module_symbol(0, "ethdev_init")) == 0 ||
+		(LNS8390_init = (void*)get_module_symbol(0, "NS8390_init")) == 0 ||
+		(Lei_open = (void*)get_module_symbol(0, "ei_open")) == 0 ||
+		(Lei_close = (void*)get_module_symbol(0, "ei_close")) == 0 ||
+		(Lei_interrupt = (void*)get_module_symbol(0, "ei_interrupt")) == 0 ) {
+		printk("ne2k-pci: Failed to resolve an 8390 symbol.\n");
+		release_module("8390", 0);
+		return 0;
+	}
+#endif
+
+	/* Read the 16 bytes of station address PROM.
+	   We must first initialize registers, similar to NS8390_init(eifdev, 0).
+	   We can't reliably read the SAPROM address without this.
+	   (I learned the hard way!). */
+	{
+		struct {unsigned char value, offset; } program_seq[] = {
+			{E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD}, /* Select page 0*/
+			{0x49,	EN0_DCFG},	/* Set word-wide access. */
+			{0x00,	EN0_RCNTLO},	/* Clear the count regs. */
+			{0x00,	EN0_RCNTHI},
+			{0x00,	EN0_IMR},	/* Mask completion irq. */
+			{0xFF,	EN0_ISR},
+			{E8390_RXOFF, EN0_RXCR},	/* 0x20  Set to monitor */
+			{E8390_TXOFF, EN0_TXCR},	/* 0x02  and loopback mode. */
+			{32,	EN0_RCNTLO},
+			{0x00,	EN0_RCNTHI},
+			{0x00,	EN0_RSARLO},	/* DMA starting at 0x0000. */
+			{0x00,	EN0_RSARHI},
+			{E8390_RREAD+E8390_START, E8390_CMD},
+		};
+		for (i = 0; i < sizeof(program_seq)/sizeof(program_seq[0]); i++)
+			outb(program_seq[i].value, ioaddr + program_seq[i].offset);
+
+	}
+
+#ifdef notdef
+	/* Some broken PCI cards don't respect the byte-wide
+	   request in program_seq above, and hence don't have doubled up values.
+	*/
+	for(i = 0; i < 32 /*sizeof(SA_prom)*/; i+=2) {
+		SA_prom[i] = inb(ioaddr + NE_DATAPORT);
+		SA_prom[i+1] = inb(ioaddr + NE_DATAPORT);
+		if (SA_prom[i] != SA_prom[i+1])
+			sa_prom_doubled = 0;
+	}
+
+	if (sa_prom_doubled)
+		for (i = 0; i < 16; i++)
+			SA_prom[i] = SA_prom[i+i];
+#else
+	for(i = 0; i < 32 /*sizeof(SA_prom)*/; i++)
+		SA_prom[i] = inb(ioaddr + NE_DATAPORT);
+
+#endif
+
+	/* We always set the 8390 registers for word mode. */
+	outb(0x49, ioaddr + EN0_DCFG);
+	start_page = NESM_START_PG;
+	stop_page = NESM_STOP_PG;
+
+	/* Set up the rest of the parameters. */
+	name = "PCI NE2000";
+
+	dev->irq = irq;
+	dev->base_addr = ioaddr;
+
+	/* Allocate dev->priv and fill in 8390 specific dev fields. */
+	if (Lethdev_init(dev)) {
+		printk ("%s: unable to get memory for dev->priv.\n", dev->name);
+		return 0;
+	}
+
+	request_region(ioaddr, NE_IO_EXTENT, dev->name);
+
+	printk("%s: %s found at %#x, IRQ %d, ",
+		   dev->name, name, ioaddr, dev->irq);
+	for(i = 0; i < 6; i++) {
+		printk("%2.2X%s", SA_prom[i], i == 5 ? ".\n": ":");
+		dev->dev_addr[i] = SA_prom[i];
+	}
+
+	ei_status.name = name;
+	ei_status.tx_start_page = start_page;
+	ei_status.stop_page = stop_page;
+	ei_status.word16 = 1;
+
+	ei_status.rx_start_page = start_page + TX_PAGES;
+#ifdef PACKETBUF_MEMSIZE
+	/* Allow the packet buffer size to be overridden by know-it-alls. */
+	ei_status.stop_page = ei_status.tx_start_page + PACKETBUF_MEMSIZE;
+#endif
+
+	ei_status.reset_8390 = &ne_reset_8390;
+	ei_status.block_input = &ne_block_input;
+	ei_status.block_output = &ne_block_output;
+	ei_status.get_8390_hdr = &ne_get_8390_hdr;
+	dev->open = &ne_open;
+	dev->stop = &ne_close;
+	LNS8390_init(dev, 0);
+	return dev;
+}
+
+static int
+ne_open(struct device *dev)
+{
+	if (request_irq(dev->irq, Lei_interrupt, SA_SHIRQ, dev->name, dev))
+		return -EAGAIN;
+	Lei_open(dev);
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int
+ne_close(struct device *dev)
+{
+	Lei_close(dev);
+	free_irq(dev->irq, dev);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+/* Hard reset the card.  This used to pause for the same period that a
+   8390 reset command required, but that shouldn't be necessary. */
+static void
+ne_reset_8390(struct device *dev)
+{
+	unsigned long reset_start_time = jiffies;
+
+	if (debug > 1) printk("%s: Resetting the 8390 t=%ld...",
+						  dev->name, jiffies);
+
+	outb(inb(NE_BASE + NE_RESET), NE_BASE + NE_RESET);
+
+	ei_status.txing = 0;
+	ei_status.dmaing = 0;
+
+	/* This check _should_not_ be necessary, omit eventually. */
+	while ((inb(NE_BASE+EN0_ISR) & ENISR_RESET) == 0)
+		if (jiffies - reset_start_time > 2) {
+			printk("%s: ne_reset_8390() did not complete.\n", dev->name);
+			break;
+		}
+	outb(ENISR_RESET, NE_BASE + EN0_ISR);	/* Ack intr. */
+}
+
+/* Grab the 8390 specific header. Similar to the block_input routine, but
+   we don't need to be concerned with ring wrap as the header will be at
+   the start of a page, so we optimize accordingly. */
+
+static void
+ne_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+
+	int nic_base = dev->base_addr;
+
+	/* This *shouldn't* happen. If it does, it's the last thing you'll see */
+	if (ei_status.dmaing) {
+		printk("%s: DMAing conflict in ne_get_8390_hdr "
+			   "[DMAstat:%d][irqlock:%d][intr:%d].\n",
+			   dev->name, ei_status.dmaing, ei_status.irqlock,
+			   dev->interrupt);
+		return;
+	}
+
+	ei_status.dmaing |= 0x01;
+	outb(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD);
+	outb(sizeof(struct e8390_pkt_hdr), nic_base + EN0_RCNTLO);
+	outb(0, nic_base + EN0_RCNTHI);
+	outb(0, nic_base + EN0_RSARLO);		/* On page boundary */
+	outb(ring_page, nic_base + EN0_RSARHI);
+	outb(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+
+#if defined(USE_LONGIO)
+	*(u32*)hdr = inl(NE_BASE + NE_DATAPORT);
+#else
+	insw(NE_BASE + NE_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr)>>1);
+#endif
+
+	outb(ENISR_RDC, nic_base + EN0_ISR);	/* Ack intr. */
+	ei_status.dmaing &= ~0x01;
+}
+
+/* Block input and output, similar to the Crynwr packet driver.  If you
+   are porting to a new ethercard, look at the packet driver source for hints.
+   The NEx000 doesn't share the on-board packet memory -- you have to put
+   the packet out through the "remote DMA" dataport using outb. */
+
+static void
+ne_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset)
+{
+	int nic_base = dev->base_addr;
+	char *buf = skb->data;
+
+	/* This *shouldn't* happen. If it does, it's the last thing you'll see */
+	if (ei_status.dmaing) {
+		printk("%s: DMAing conflict in ne_block_input "
+			   "[DMAstat:%d][irqlock:%d][intr:%d].\n",
+			   dev->name, ei_status.dmaing, ei_status.irqlock,
+			   dev->interrupt);
+		return;
+	}
+	ei_status.dmaing |= 0x01;
+	outb(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD);
+	outb(count & 0xff, nic_base + EN0_RCNTLO);
+	outb(count >> 8, nic_base + EN0_RCNTHI);
+	outb(ring_offset & 0xff, nic_base + EN0_RSARLO);
+	outb(ring_offset >> 8, nic_base + EN0_RSARHI);
+	outb(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+
+#if defined(USE_LONGIO)
+	insl(NE_BASE + NE_DATAPORT, buf, count>>2);
+	if (count & 3) {
+		buf += count & ~3;
+		if (count & 2)
+			*((u16*)buf)++ = inw(NE_BASE + NE_DATAPORT);
+		if (count & 1)
+			*buf = inb(NE_BASE + NE_DATAPORT);
+	}
+#else
+	insw(NE_BASE + NE_DATAPORT,buf,count>>1);
+	if (count & 0x01) {
+		buf[count-1] = inb(NE_BASE + NE_DATAPORT);
+	}
+#endif
+
+	outb(ENISR_RDC, nic_base + EN0_ISR);	/* Ack intr. */
+	ei_status.dmaing &= ~0x01;
+}
+
+static void
+ne_block_output(struct device *dev, int count,
+		const unsigned char *buf, const int start_page)
+{
+	int nic_base = NE_BASE;
+	unsigned long dma_start;
+
+	/* On little-endian it's always safe to round the count up for
+	   word writes. */
+	if (count & 0x01)
+		count++;
+
+	/* This *shouldn't* happen. If it does, it's the last thing you'll see */
+	if (ei_status.dmaing) {
+		printk("%s: DMAing conflict in ne_block_output."
+			   "[DMAstat:%d][irqlock:%d][intr:%d]\n",
+			   dev->name, ei_status.dmaing, ei_status.irqlock,
+			   dev->interrupt);
+		return;
+	}
+	ei_status.dmaing |= 0x01;
+	/* We should already be in page 0, but to be safe... */
+	outb(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base + NE_CMD);
+
+#ifdef NE8390_RW_BUGFIX
+	/* Handle the read-before-write bug the same way as the
+	   Crynwr packet driver -- the NatSemi method doesn't work.
+	   Actually this doesn't always work either, but if you have
+	   problems with your NEx000 this is better than nothing! */
+	outb(0x42, nic_base + EN0_RCNTLO);
+	outb(0x00, nic_base + EN0_RCNTHI);
+	outb(0x42, nic_base + EN0_RSARLO);
+	outb(0x00, nic_base + EN0_RSARHI);
+	outb(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+#endif
+	outb(ENISR_RDC, nic_base + EN0_ISR);
+
+   /* Now the normal output. */
+	outb(count & 0xff, nic_base + EN0_RCNTLO);
+	outb(count >> 8,   nic_base + EN0_RCNTHI);
+	outb(0x00, nic_base + EN0_RSARLO);
+	outb(start_page, nic_base + EN0_RSARHI);
+	outb(E8390_RWRITE+E8390_START, nic_base + NE_CMD);
+#if defined(USE_LONGIO)
+	outsl(NE_BASE + NE_DATAPORT, buf, count>>2);
+	if (count & 3) {
+		buf += count & ~3;
+		if (count & 2)
+			outw(*((u16*)buf)++, NE_BASE + NE_DATAPORT);
+	}
+#else
+	outsw(NE_BASE + NE_DATAPORT, buf, count>>1);
+#endif
+
+	dma_start = jiffies;
+
+	while ((inb(nic_base + EN0_ISR) & ENISR_RDC) == 0)
+		if (jiffies - dma_start > 2) { 			/* Avoid clock roll-over. */
+			printk("%s: timeout waiting for Tx RDC.\n", dev->name);
+			ne_reset_8390(dev);
+			LNS8390_init(dev,1);
+			break;
+		}
+
+	outb(ENISR_RDC, nic_base + EN0_ISR);	/* Ack intr. */
+	ei_status.dmaing &= ~0x01;
+	return;
+}
+
+
+/*
+ * Local variables:
+ *  compile-command: "gcc -DMODVERSIONS  -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer -I/usr/src/linux/drivers/net/ -c ne2k-pci.c"
+ *  alt-compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer -I/usr/src/linux/drivers/net/ -c ne2k-pci.c"
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 4
+ *  version-control: t
+ *  kept-new-versions: 5
+ * End:
+ */

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov