summaryrefslogtreecommitdiff
path: root/linux/src/drivers/net/ne2k-pci.c
diff options
context:
space:
mode:
authorThomas Bushnell <thomas@gnu.org>1999-04-26 05:58:44 +0000
committerThomas Bushnell <thomas@gnu.org>1999-04-26 05:58:44 +0000
commit86297c41a26f18d924e64fc93321c59cbc4c48dd (patch)
tree376954c6b95b735d361875319a1a2a9db6a27527 /linux/src/drivers/net/ne2k-pci.c
parent851137902d3e7ad87af177487df3eea53e940a1c (diff)
1998-11-30 OKUJI Yoshinori <okuji@kuicr.kyoto-u.ac.jp>
Clean up linux emulation code to make it architecture-independent as much as possible. * linux: Renamed from linuxdev. * Makefile.in (objfiles): Add linux.o instead of linuxdev.o. (MAKE): New variable. Used for the linux.o target. * configure.in: Add AC_CHECK_TOOL(MAKE, make). * i386/i386/spl.h: Include <i386/ipl.h>, for compatibility with OSF Mach 3.0. Suggested by Elgin Lee <ehl@funghi.com>. * linux/src: Renamed from linux/linux. * linux/dev: Renamed from linux/mach. * linux/Drivers.in (AC_INIT): Use dev/include/linux/autoconf.h, instead of mach/include/linux/autoconf.h. * Makefile.in (all): Target ../linux.o instead of ../linuxdev.o. * linux/dev/drivers/block/genhd.c: Include <machine/spl.h> instead of <i386/ipl.h>. * linux/dev/drivers/net/auto_irq.c: Remove unneeded header files, <i386/ipl.h> and <i386/pic.h>. * linux/dev/init/main.c: Many i386-dependent codes moved to ... * linux/dev/arch/i386/irq.c: ... here. * linux/dev/arch/i386/setup.c: New file. * linux/dev/arch/i386/linux_emul.h: Likewise. * linux/dev/arch/i386/glue/timer.c: Merged into sched.c. * linux/dev/arch/i386/glue/sched.c: Include <machine/spl.h> instead of <i386/ipl.h>, and moved to ... * linux/dev/kernel/sched.c: ... here. * linux/dev/arch/i386/glue/block.c: Include <machine/spl.h> and <linux_emul.h>, instead of i386-dependent header files, and moved to ... * linux/dev/glue/blocl.c: ... here. * linux/dev/arch/i386/glue/net.c: Include <machine/spl.h> and <linux_emul.h>, instead of i386-dependent header files, and moved to ... * linux/dev/glue/net.c: ... here. * linux/dev/arch/i386/glue/misc.c: Remove `x86' and moved to ... * linux/dev/glue/misc.c: ... here. * linux/dev/arch/i386/glue/kmem.c: Moved to ... * linux/dev/glue/kmem.c: ... here.
Diffstat (limited to 'linux/src/drivers/net/ne2k-pci.c')
-rw-r--r--linux/src/drivers/net/ne2k-pci.c640
1 files changed, 640 insertions, 0 deletions
diff --git a/linux/src/drivers/net/ne2k-pci.c b/linux/src/drivers/net/ne2k-pci.c
new file mode 100644
index 0000000..3aa556f
--- /dev/null
+++ b/linux/src/drivers/net/ne2k-pci.c
@@ -0,0 +1,640 @@
+/* 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/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 int 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;
+
+ 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;
+
+#ifndef MODULE
+ {
+ static unsigned version_printed = 0;
+ if (version_printed++ == 0)
+ printk(KERN_INFO "%s", version);
+ }
+#endif
+
+ 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:
+ */