add directory net

This commit is contained in:
gohigh
2024-02-19 00:25:04 -05:00
parent a778c607a4
commit 9ad4ff3d15
95 changed files with 15535 additions and 0 deletions

View File

@@ -0,0 +1,377 @@
/* 3c503.c: A shared-memory NS8390 ethernet driver for linux. */
/*
Written 1992,1993 by Donald Becker. This is alpha test code.
This is a extension to the Linux operating system, and is covered by
same Gnu Public License that covers that work.
This driver should work with the 3c503 and 3c503/16. It must be used
in shared memory mode.
The Author may be reached as becker@super.org or
C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
*/
static char *version = "3c503.c:v0.67 3/8/93 Donald Becker (becker@super.org)\n";
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <asm/io.h>
#include <asm/system.h>
#include "dev.h"
#include "8390.h"
#include "3c503reg.h"
extern void NS8390_init(struct device *dev, int startp);
extern int ei_debug;
extern struct sigaction ei_sigaction;
int el2autoprobe(int ioaddr, struct device *dev);
int el2probe(int ioaddr, struct device *dev);
static void el2_reset_8390(struct device *dev);
static void el2_init_card(struct device *dev);
static void el2_block_output(struct device *dev, int count,
const unsigned char *buf, const start_page);
static int el2_block_input(struct device *dev, int count, char *buf,
int ring_offset);
int
el2autoprobe(int ioaddr, struct device *dev)
{
int *addr, addrs[] = { 0xddffe, 0xd9ffe, 0xcdffe, 0xc9ffe, 0};
int ports[] = {0x300,0x310,0x330,0x350,0x250,0x280,0x2a0,0x2e0,0};
/* Non-autoprobe case first: */
if (ioaddr > 0)
return el2probe(ioaddr, dev);
/* We check for a memory-mapped 3c503 board by looking at the
port location bitmap at the end of the jumpered boot PROM space.
This works even if a PROM isn't there. */
for (addr = addrs; *addr; addr++) {
int i;
unsigned int base_bits = *(unsigned char *)*addr;
/* Find first set bit. */
for(i = 7; i >= 0; i--, base_bits >>= 1)
if (base_bits & 0x1)
break;
if (base_bits == 1 && el2probe(ports[i], dev))
return dev->base_addr;
}
#ifndef ignore_nonshared_memory
/* It's not memory mapped, bummer. Try all of the locations
that aren't obviously empty. */
{ int i;
for (i = 0; i < 8; i++)
if (inb_p(ports[i] + 0x403) == (0x80 >> i) /* Preliminary check */
&& el2probe(ports[i], dev))
return dev->base_addr;
}
#endif /* probe_nonshared_memory */
return 0;
}
/* Probe for the Etherlink II card at I/O port base IOADDR,
returning non-zero on sucess. If found, set the station
address and memory parameters in DEVICE. */
int
el2probe(int ioaddr, struct device *dev)
{
int i, iobase_reg, membase_reg, saved_406;
unsigned char *station_addr = dev->dev_addr;
/* We verify that it's a 3C503 board by checking the first three octets
of its ethernet address. */
printk("3c503 probe at %#3x:", ioaddr);
iobase_reg = inb(ioaddr+0x403);
membase_reg = inb(ioaddr+0x404);
/* Verify ASIC register that should be 0 or have a single bit set. */
if ( (iobase_reg & (iobase_reg - 1))
|| (membase_reg & (membase_reg - 1))) {
printk(" not found.\n");
return 0;
}
saved_406 = inb_p(ioaddr + 0x406);
outb_p(ECNTRL_RESET|ECNTRL_THIN, ioaddr + 0x406); /* Reset it... */
outb_p(ECNTRL_THIN, ioaddr + 0x406);
/* Map the station addr PROM into the lower I/O ports. */
outb(ECNTRL_SAPROM|ECNTRL_THIN, ioaddr + 0x406);
for (i = 0; i < ETHER_ADDR_LEN; i++) {
printk(" %2.2X", (station_addr[i] = inb(ioaddr + i)));
}
if ( station_addr[0] != 0x02
|| station_addr[1] != 0x60
|| station_addr[2] != 0x8c) {
printk(" 3C503 not found.\n");
/* Restore the register we frobbed. */
outb_p(saved_406, ioaddr + 0x406);
return 0;
}
/* Map the 8390 back into the window. */
outb(ECNTRL_THIN, ioaddr + 0x406);
dev->base_addr = ioaddr;
/* Probe for, turn on and clear the board's shared memory. */
if (ei_debug > 2) printk(" memory jumpers %2.2x ", membase_reg);
outb(EGACFR_NORM, ioaddr + 0x405); /* Enable RAM */
if ((membase_reg & 0xf0) == 0) {
dev->mem_start = 0;
} else {
dev->mem_start = ((membase_reg & 0xc0) ? 0xD8000 : 0xC8000) +
((membase_reg & 0xA0) ? 0x4000 : 0);
#define EL2_MEMSIZE (EL2SM_STOP_PG - EL2SM_START_PG)*256
#ifdef EL2MEMTEST
/* This has never found an error, but someone might care. */
{ /* Check the card's memory. */
int *mem_base = (int *)dev->mem_start;
int memtest_value = 0xbbadf00d;
mem_base[0] = 0xba5eba5e;
for (i = 1; i < EL2_MEMSIZE/sizeof(mem_base[0]); i++) {
mem_base[i] = memtest_value;
if (mem_base[0] != 0xba5eba5e
|| mem_base[i] != memtest_value) {
printk(" memory failure or memory address conflict.\n");
dev->mem_start = 0;
break;
}
memtest_value += 0x55555555;
mem_base[i] = 0;
}
}
#endif /* EL2MEMTEST */
/* Divide the on-board memory into a single maximum-sized transmit
(double-sized for ping-pong transmit) buffer at the base, and
use the rest as a receive ring. */
dev->mem_end = dev->rmem_end = dev->mem_start + EL2_MEMSIZE;
dev->rmem_start = TX_PAGES*256 + dev->mem_start;
}
if (ei_debug > 2)
printk("\n3c503: memory params start=%#5x rstart=%#5x end=%#5x rend=%#5x.\n",
dev->mem_start, dev->rmem_start, dev->mem_end, dev->rmem_end);
/* Finish setting the board's parameters. */
ei_status.name = "3C503";
ei_status.tx_start_page = EL2SM_START_PG;
ei_status.rx_start_page = EL2SM_START_PG + TX_PAGES;
ei_status.stop_page = EL2SM_STOP_PG;
ei_status.reset_8390 = &el2_reset_8390;
ei_status.block_input = &el2_block_input;
ei_status.block_output = &el2_block_output;
/* This should be probed for (or set via an ioctl()) at run-time someday. */
#if defined(EI8390_THICK) || defined(EL2_AUI)
ei_status.thin_bit = 0;
#else
ei_status.thin_bit = ECNTRL_THIN;
#endif
if (dev->irq < 2) {
int irqlist[] = {5, 9, 3, 4, 0};
int *irqp = irqlist;
do {
if (request_irq (dev->irq = *irqp, NULL) != -EBUSY) {
/* Twinkle the interrupt, and check if it's seen. */
autoirq_setup(0);
outb_p((0x04 << (dev->irq == 9 ? 2 : dev->irq)), E33G_IDCFR);
outb_p(0x00, E33G_IDCFR);
if (dev->irq == autoirq_report(0) /* It's a good IRQ line! */
&& request_irq (dev->irq, &ei_interrupt) == 0) {
printk(" got IRQ %d", dev->irq);
break;
} else
printk(" IRQ%d bad..", dev->irq);
}
} while (*++irqp);
if (*irqp == 0) {
printk(" unable to find an free IRQ line.\n");
return 0;
}
} else {
if (dev->irq == 2)
dev->irq = 9;
else if (dev->irq > 5 && dev->irq != 9) {
printk("\n3c503: configured interrupt number %d out of range.\n",
dev->irq);
return 0;
}
if (request_irq(dev->irq, &ei_interrupt)) {
printk (" unable to get IRQ%d.\n", dev->irq);
return 0;
}
}
dev->start = 0;
el2_init_card(dev);
if (dev->mem_start)
printk("\n%s: %s using IRQ %d with shared memory at %#6x-%#6x,\n",
dev->name, ei_status.name, dev->irq,
dev->mem_start, dev->mem_end-1);
else
printk("\n%s: %s using IRQ %d with programmed I/O.\n",
dev->name, ei_status.name, dev->irq);
if (ei_debug > 1)
printk(version);
return ioaddr;
}
/* This is called whenever we have a unrecoverable failure:
transmit timeout
Bad ring buffer packet header
*/
static void
el2_reset_8390(struct device *dev)
{
if (ei_debug > 1) {
printk("%s: Resetting the 3c503 board...", dev->name);
printk("%#x=%#02x %#x=%#02x %#x=%#02x...", E33G_IDCFR, inb(E33G_IDCFR),
E33G_CNTRL, inb(E33G_CNTRL), E33G_GACFR, inb(E33G_GACFR));
}
outb_p(ECNTRL_RESET|ECNTRL_THIN, E33G_CNTRL);
ei_status.txing = 0;
outb_p(ei_status.thin_bit, E33G_CNTRL);
el2_init_card(dev);
if (ei_debug > 1) printk("done\n");
}
/* Initialize the 3c503 GA registers after a reset. */
static void
el2_init_card(struct device *dev)
{
/* Unmap the station PROM and select the DIX or BNC connector. */
outb_p(ei_status.thin_bit, E33G_CNTRL);
/* Set ASIC copy of rx's first and last+1 buffer pages */
/* These must be the same as in the 8390. */
outb(ei_status.rx_start_page, E33G_STARTPG);
outb(ei_status.stop_page, E33G_STOPPG);
/* Point the vector pointer registers somewhere ?harmless?. */
outb(0xff, E33G_VP2); /* Point at the ROM restart location 0xffff0 */
outb(0xff, E33G_VP1);
outb(0x00, E33G_VP0);
/* Turn off all interrupts until we're opened. */
outb_p(0x00, dev->base_addr + EN0_IMR);
/* Enable IRQs iff started. */
outb(EGACFR_NORM, E33G_GACFR);
/* Set the interrupt line. */
outb_p((0x04 << (dev->irq == 9 ? 2 : dev->irq)), E33G_IDCFR);
outb_p(8, E33G_DRQCNT); /* Set burst size to 8 */
outb_p(0x20, E33G_DMAAH); /* Put a valid addr in the GA DMA */
outb_p(0x00, E33G_DMAAL);
return; /* We always succeed */
}
/* Either use the shared memory (if enabled on the board) or put the packet
out through the ASIC FIFO. The latter is probably much slower. */
static void
el2_block_output(struct device *dev, int count,
const unsigned char *buf, const start_page)
{
int i; /* Buffer index */
int boguscount = 0; /* timeout counter */
/* This should really be set with during an open(). */
outb(EGACFR_NORM, E33G_GACFR); /* Enable RAM and interrupts. */
if (dev->mem_start) { /* Shared memory transfer */
void *dest_addr = (void *)(dev->mem_start +
((start_page - ei_status.tx_start_page) << 8));
memcpy(dest_addr, buf, count);
if (ei_debug > 2 && memcmp(dest_addr, buf, count))
printk("%s: 3c503 send_packet() bad memory copy @ %#5x.\n",
dev->name, dest_addr);
else if (ei_debug > 4)
printk("%s: 3c503 send_packet() good memory copy @ %#5x.\n",
dev->name, dest_addr);
return;
}
/* No shared memory, put the packet out the slow way. */
/* Set up then start the internal memory transfer to Tx Start Page */
outb(0x00, E33G_DMAAL);
outb_p(start_page, E33G_DMAAH);
outb_p(ei_status.thin_bit | ECNTRL_OUTPUT | ECNTRL_START, E33G_CNTRL);
/* This is the byte copy loop: it should probably be tuned for
for speed once everything is working. I think it is possible
to output 8 bytes between each check of the status bit. */
for(i = 0; i < count; i++) {
if (i % 8 == 0)
while ((inb(E33G_STATUS) & ESTAT_DPRDY) == 0)
if (++boguscount > (i<<3) + 32) {
printk("%s: FIFO blocked in el2_block_output (at %d of %d, bc=%d).\n",
dev->name, i, count, boguscount);
return;
}
outb(buf[i], E33G_FIFOH);
}
outb(ei_status.thin_bit, E33G_CNTRL);
return;
}
/* Returns the new ring pointer. */
static int
el2_block_input(struct device *dev, int count, char *buf, int ring_offset)
{
int boguscount = 0;
int end_of_ring = dev->rmem_end;
unsigned int i;
/* Maybe enable shared memory just be to be safe... nahh.*/
if (dev->mem_start) { /* Use the shared memory. */
ring_offset -= (EL2SM_START_PG<<8);
if (dev->mem_start + ring_offset + count > end_of_ring) {
/* We must wrap the input move. */
int semi_count = end_of_ring - (dev->mem_start + ring_offset);
if (ei_debug > 4)
printk("%s: 3c503 block_input() @ %#5x+%x=%5x.\n",
dev->name, dev->mem_start, ring_offset,
(char *)dev->mem_start + ring_offset);
memcpy(buf, (char *)dev->mem_start + ring_offset, semi_count);
count -= semi_count;
memcpy(buf + semi_count, (char *)dev->rmem_start, count);
return dev->rmem_start + count;
}
if (ei_debug > 4)
printk("%s: 3c503 block_input() @ %#5x+%x=%5x.\n",
dev->name, dev->mem_start, ring_offset,
(char *)dev->mem_start + ring_offset);
memcpy(buf, (char *)dev->mem_start + ring_offset, count);
return ring_offset + count;
}
/* No shared memory, use programmed I/O. */
outb(ring_offset & 0xff, E33G_DMAAL);
outb_p((ring_offset >> 8) & 0xff, E33G_DMAAH);
outb_p(ei_status.thin_bit | ECNTRL_INPUT | ECNTRL_START, E33G_CNTRL);
/* This is the byte copy loop: it should probably be tuned for
for speed once everything is working. */
for(i = 0; i < count; i++) {
if (i % 8 == 0)
while ((inb(E33G_STATUS) & ESTAT_DPRDY) == 0)
if (++boguscount > (i<<3) + 32) {
printk("%s: FIFO blocked in el2_block_input() (at %d of %d, bc=%d).\n",
dev->name, i, count, boguscount);
boguscount = 0;
break;
}
buf[i] = inb_p(E33G_FIFOH);
}
outb(ei_status.thin_bit, E33G_CNTRL);
return 0;
}
/*
* Local variables:
* compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -I/usr/src/linux/net/tcp -c 3c503.c"
* version-control: t
* kept-new-versions: 5
* End:
*/

View File

@@ -0,0 +1,59 @@
/* Definitions for the 3Com 3c503 Etherlink 2. */
/* This file is part of Donald Becker's 8390 drivers.
This file is distributed under the Linux GPL.
Some of these names and comments are from the Crynwr packet drivers. */
#define EL2H (dev->base_addr + 0x400)
#define EL2L (dev->base_addr)
/* Shared memory management parameters */
#define EL2SM_START_PG (0x20) /* First page of TX buffer */
#define EL2SM_STOP_PG (0x40) /* Last page +1 of RX ring */
/* 3Com 3c503 ASIC registers */
#define E33G_STARTPG (EL2H+0) /* Start page, must match EN0_STARTPG */
#define E33G_STOPPG (EL2H+1) /* Stop page, must match EN0_STOPPG */
#define E33G_DRQCNT (EL2H+2) /* DMA burst count */
#define E33G_IOBASE (EL2H+3) /* Read of I/O base jumpers. */
/* (non-useful, but it also appears at the end of EPROM space) */
#define E33G_ROMBASE (EL2H+4) /* Read of memory base jumpers. */
#define E33G_GACFR (EL2H+5) /* Config/setup bits for the ASIC GA */
#define E33G_CNTRL (EL2H+6) /* Board's main control register */
#define E33G_STATUS (EL2H+7) /* Status on completions. */
#define E33G_IDCFR (EL2H+8) /* Interrupt/DMA config register */
/* (Which IRQ to assert, DMA chan to use) */
#define E33G_DMAAH (EL2H+9) /* High byte of DMA address reg */
#define E33G_DMAAL (EL2H+10) /* Low byte of DMA address reg */
/* "Vector pointer" - if this address matches a read, the EPROM (rather than
shared RAM) is mapped into memory space. */
#define E33G_VP2 (EL2H+11)
#define E33G_VP1 (EL2H+12)
#define E33G_VP0 (EL2H+13)
#define E33G_FIFOH (EL2H+14) /* FIFO for programmed I/O moves */
#define E33G_FIFOL (EL2H+15) /* ... low byte of above. */
/* Bits in E33G_CNTRL register: */
#define ECNTRL_RESET (0x01) /* Software reset of the ASIC and 8390 */
#define ECNTRL_THIN (0x02) /* Onboard thin-net xcvr enable */
#define ECNTRL_SAPROM (0x04) /* Map the station address prom */
#define ECNTRL_DBLBFR (0x20) /* FIFO configuration bit */
#define ECNTRL_OUTPUT (0x40) /* PC-to-3C503 direction if 1 */
#define ECNTRL_INPUT (0x00) /* 3C503-to-PC direction if 0 */
#define ECNTRL_START (0x80) /* Start the DMA logic */
/* Bits in E33G_STATUS register: */
#define ESTAT_DPRDY (0x80) /* Data port (of FIFO) ready */
#define ESTAT_UFLW (0x40) /* Tried to read FIFO when it was empty */
#define ESTAT_OFLW (0x20) /* Tried to write FIFO when it was full */
#define ESTAT_DTC (0x10) /* Terminal Count from PC bus DMA logic */
#define ESTAT_DIP (0x08) /* DMA In Progress */
/* Bits in E33G_GACFR register: */
#define EGACFR_NORM (0x49) /* Enable 8K shared mem, no DMA TC int */
#define EGACFR_IRQOFF (0xc9) /* Above, and disable 8390 IRQ line */
/* End of 3C503 parameter definitions */

View File

@@ -0,0 +1,689 @@
/* 8390.c: A general NS8390 ethernet driver core for linux. */
/*
Written 1992,1993 by Donald Becker. This is alpha test code.
This is a extension to the Linux operating system, and is covered by
same Gnu Public License that covers that work.
This driver should work with many 8390-based ethernet adaptors.
The Author may be reached as becker@super.org or
C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
*/
static char *version =
"8390.c:v0.68 3/9/93 for 0.99.6 Donald Becker (becker@super.org)\n";
#include <linux/config.h>
#if !defined(EL2) && !defined(NE2000) && !defined(WD80x3) && !defined(HPLAN)
/* They don't know what they want -- give it all to them! */
#define EL2
#define NE2000
#define WD80x3
#define HPLAN
#endif
/*
Braindamage remaining:
Ethernet devices should use a chr_drv device interface, with ioctl()s to
configure the card, bring the interface up or down, allow access to
statistics, and maybe read() and write() access to raw packets.
This won't be done until after Linux 1.00.
This driver should support multiple, diverse boards simultaneousely.
This won't be done until after Linux 1.00.
Sources:
The National Semiconductor LAN Databook, and the 3Com 3c503 databook.
The NE* programming info came from the Crynwr packet driver, and figuring
out that the those boards are similar to the NatSemi evaluation board
described in AN-729. Thanks NS, no thanks to Novell/Eagle.
Cabletron provided only info I had already gotten from other sources -- hiss.
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/tty.h>
#include <linux/types.h>
#include <linux/ptrace.h>
#include <linux/string.h>
#include <asm/system.h>
#include <asm/segment.h>
#include <asm/io.h>
#include <errno.h>
#include <linux/fcntl.h>
#include <netinet/in.h>
#include <linux/interrupt.h>
#include "dev.h"
#include "eth.h"
#include "timer.h"
#include "ip.h"
#include "tcp.h"
#include "sock.h"
#include "arp.h"
#include "8390.h"
#define ei_reset_8390 (ei_local->reset_8390)
#define ei_block_output (ei_local->block_output)
#define ei_block_input (ei_local->block_input)
/* use 0 for production, 1 for verification, >2 for debug */
#ifdef EI_DEBUG
int ei_debug = EI_DEBUG;
#else
int ei_debug = 2;
#endif
struct device *irq2dev_map[16] = {0,0,0, /* zeroed...*/};
#ifdef PINGPONG
static int lasttx = 0;
#endif
/* Max number of packets received at one Intr. */
/*static int high_water_mark = 0;*/
/* Index to functions. */
/* Put in the device structure. */
static int ei_open(struct device *dev);
/* Dispatch from interrupts. */
void ei_interrupt(int reg_ptr);
static void ei_tx_intr(struct device *dev);
static void ei_receive(struct device *dev);
static void ei_rx_overrun(struct device *dev);
int ethdev_init(struct device *dev);
/* Routines generic to NS8390-based boards. */
void NS8390_init(struct device *dev, int startp);
static void NS8390_trigger_send(struct device *dev, unsigned int length,
int start_page);
extern int el2autoprobe(int ioaddr, struct device *dev);
extern int el2probe(int ioaddr, struct device *dev);
extern int neprobe(int ioaddr, struct device *dev);
extern int wdprobe(int ioaddr, struct device *dev);
extern int hpprobe(int ioaddr, struct device *dev);
struct sigaction ei_sigaction = { ei_interrupt, 0, 0, NULL, };
/* Open/initialize the board. This routine goes all-out, setting everything
up anew at each open, even though many of these registers should only
need to be set once at boot.
*/
static int
ei_open(struct device *dev)
{
struct ei_device *ei_local = dev->private;
if ( ! ei_local) {
printk("%s: Opening a non-existent physical device\n", dev->name);
return 1; /* ENXIO would be more accurate. */
}
irq2dev_map[dev->irq] = dev;
NS8390_init(dev, 1);
ei_local->tx1 = ei_local->tx2 = 0;
/* The old local flags... */
ei_local->txing = 0;
/* ... are now global. */
dev->tbusy = 0;
dev->interrupt = 0;
dev->start = 1;
return 0;
}
static int
ei_start_xmit(struct sk_buff *skb, struct device *dev)
{
int e8390_base = dev->base_addr;
struct ei_device *ei_local = dev->private;
if (dev->tbusy) { /* Do timeouts, just like the 8003 driver. */
int txsr = inb(e8390_base+EN0_TSR), isr;
int tickssofar = jiffies - dev->trans_start;
if (tickssofar < 5 || (tickssofar < 15 && ! (txsr & ENTSR_PTX))) {
return 1;
}
isr = inb(e8390_base+EN0_ISR);
printk("%s: transmit timed out, TX status %#2x, ISR %#2x.\n",
dev->name, txsr, isr);
/* It's possible to check for an IRQ conflict here.
I may have to do that someday. */
if (isr)
printk("%s: Possible IRQ conflict on IRQ%d?", dev->name, dev->irq);
else
printk("%s: Possible network cable problem?\n", dev->name);
/* It futile, but try to restart it anyway. */
ei_reset_8390(dev);
NS8390_init(dev, 1);
printk("\n");
}
/* This is new: it means some higher layer thinks we've missed an
tx-done interrupt. Caution: dev_tint() handles the cli()/sti()
itself. */
if (skb == NULL) {
dev_tint(dev);
return 0;
}
/* Fill in the ethernet header. */
if (!skb->arp && dev->rebuild_header(skb+1, dev)) {
skb->dev = dev;
arp_queue (skb);
return 0;
}
#ifdef PINGPONG
{
int length = skb->len;
int output_page;
int send_length = ETHER_MIN_LEN < length ? length : ETHER_MIN_LEN;
if (length <= 0)
return 0;
/* Turn off interrupts so that we can put the packet out safely. */
outb(0x00, e8390_base + EN0_IMR);
if (ei_local->tx1 == 0) {
output_page = ei_local->tx_start_page;
ei_local->tx1 = send_length;
if (ei_debug && ei_local->tx2 > 0)
printk("%s: idle transmitter tx2=%d, lasttx=%d, txing=%d.\n",
dev->name, ei_local->tx2, lasttx, ei_local->txing);
} else if (ei_local->tx2 == 0) {
output_page = ei_local->tx_start_page + 6;
ei_local->tx2 = send_length;
if (ei_debug && ei_local->tx1 > 0)
printk("%s: idle transmitter, tx1=%d, lasttx=%d, txing=%d.\n",
dev->name, ei_local->tx1, lasttx, ei_local->txing);
} else {
/* We can get to here if we get an rx interrupt and queued
a tx packet just before masking 8390 irqs above. */
if (ei_debug > 2)
printk("%s: No packet buffer space for ping-pong use.\n",
dev->name);
outb_p(ENISR_ALL, e8390_base + EN0_IMR);
return 1;
}
dev->trans_start = jiffies;
ei_block_output(dev, length, (void*)(skb+1), output_page);
if (! ei_local->txing) {
NS8390_trigger_send(dev, send_length, output_page);
if (output_page == ei_local->tx_start_page)
ei_local->tx1 = -1, lasttx = -1;
else
ei_local->tx2 = -1, lasttx = -2;
ei_local->txing = 1;
} else
ei_local->txqueue++;
if (ei_local->tx1 && ei_local->tx2)
dev->tbusy = 1;
/* Turn 8390 interrupts back on. */
outb_p(ENISR_ALL, e8390_base + EN0_IMR);
}
#else
{
int length = skb->len;
int send_length = ETHER_MIN_LEN < length ? length : ETHER_MIN_LEN;
if (length <= 0)
return 0;
dev->trans_start = jiffies;
/* Turn off interrupts so that we can put the packet out safely. */
outb(0x00, e8390_base + EN0_IMR);
ei_block_output(dev, length, (void*)(skb+1), ei_local->tx_start_page);
NS8390_trigger_send(dev, send_length, ei_local->tx_start_page);
dev->tbusy = 1;
/* Turn 8390 interrupts back on. */
outb_p(ENISR_ALL, e8390_base + EN0_IMR);
}
#endif /* PINGPONG */
if (skb->free)
kfree_skb (skb, FREE_WRITE);
return 0;
}
/* The typical workload of the driver:
Handle the ether interface interrupts. */
void
ei_interrupt(int reg_ptr)
{
int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2);
struct device *dev = irq2dev_map[irq];
int e8390_base;
int interrupts, boguscount = 0;
if (dev == NULL) {
printk ("net_interrupt(): irq %d for unknown device.\n", irq);
return;
}
dev->interrupt = 1;
sti(); /* Allow other interrupts. */
e8390_base = dev->base_addr;
/* Change to page 0 and read the intr status reg. */
outb_p(E8390_NODMA+E8390_PAGE0, e8390_base + E8390_CMD);
if (ei_debug > 3)
printk("%s: interrupt(isr=%#2.2x).\n", dev->name,
inb_p(e8390_base + EN0_ISR));
/* !!Assumption!! -- we stay in page 0. Don't break this. */
while ((interrupts = inb_p(e8390_base + EN0_ISR)) != 0
&& ++boguscount < 20) {
if (interrupts & ENISR_RDC) {
/* Ack meaningless DMA complete. */
outb_p(ENISR_RDC, e8390_base + EN0_ISR);
}
if (interrupts & ENISR_OVER) {
ei_rx_overrun(dev);
} else if (interrupts & (ENISR_RX+ENISR_RX_ERR)) {
/* Got a good (?) packet. */
ei_receive(dev);
}
/* Push the next to-transmit packet through. */
if (interrupts & ENISR_TX) {
ei_tx_intr(dev);
} else if (interrupts & ENISR_COUNTERS) {
struct ei_device *ei_local = dev->private;
ei_local->soft_rx_errors += inb_p(e8390_base + EN0_COUNTER0);
ei_local->soft_rx_errors += inb_p(e8390_base + EN0_COUNTER1);
ei_local->missed_packets += inb_p(e8390_base + EN0_COUNTER2);
outb_p(ENISR_COUNTERS, e8390_base + EN0_ISR); /* Ack intr. */
}
/* Ignore the transmit errs and reset intr for now. */
if (interrupts & ENISR_TX_ERR) {
outb_p(ENISR_TX_ERR, e8390_base + EN0_ISR); /* Ack intr. */
}
outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base + E8390_CMD);
}
if (interrupts && ei_debug) {
printk("%s: unknown interrupt %#2x\n", dev->name, interrupts);
outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base + E8390_CMD);
outb_p(0xff, e8390_base + EN0_ISR); /* Ack. all intrs. */
}
dev->interrupt = 0;
return;
}
/* We have finished a transmit: check for errors and then trigger the next
packet to be sent. */
static void
ei_tx_intr(struct device *dev)
{
int e8390_base = dev->base_addr;
int status = inb(e8390_base + EN0_TSR);
struct ei_device *ei_local = dev->private;
outb_p(ENISR_TX, e8390_base + EN0_ISR); /* Ack intr. */
if ((status & ENTSR_PTX) == 0)
ei_local->tx_errors++;
else
ei_local->tx_packets++;
#ifdef PINGPONG
ei_local->txqueue--;
if (ei_local->tx1 < 0) {
if (lasttx != 1 && lasttx != -1)
printk("%s: bogus last_tx_buffer %d, tx1=%d.\n",
ei_local->name, lasttx, ei_local->tx1);
ei_local->tx1 = 0;
dev->tbusy = 0;
if (ei_local->tx2 > 0) {
NS8390_trigger_send(dev, ei_local->tx2, ei_local->tx_start_page + 6);
ei_local->txing = 1;
ei_local->tx2 = -1,
lasttx = 2;
} else
lasttx = 20, ei_local->txing = 0;
} else if (ei_local->tx2 < 0) {
if (lasttx != 2 && lasttx != -2)
printk("%s: bogus last_tx_buffer %d, tx2=%d.\n",
ei_local->name, lasttx, ei_local->tx2);
ei_local->tx2 = 0;
dev->tbusy = 0;
if (ei_local->tx1 > 0) {
NS8390_trigger_send(dev, ei_local->tx1, ei_local->tx_start_page);
ei_local->txing = 1;
ei_local->tx1 = -1;
lasttx = 1;
} else
lasttx = 10, ei_local->txing = 0;
} else
printk("%s: unexpected TX-done interrupt, lasttx=%d.\n",
dev->name, lasttx);
#else
ei_local->txing = 0;
dev->tbusy = 0;
#endif
mark_bh (INET_BH);
}
/* We have a good packet(s), get it/them out of the buffers. */
static void
ei_receive(struct device *dev)
{
int e8390_base = dev->base_addr;
struct ei_device *ei_local = dev->private;
int rxing_page, this_frame, next_frame, current_offset;
int boguscount = 0;
struct e8390_pkt_hdr rx_frame;
int num_rx_pages = ei_local->stop_page-ei_local->rx_start_page;
while (++boguscount < 10) {
int size;
/* Get the rx page (incoming packet pointer). */
outb_p(E8390_NODMA+E8390_PAGE1, e8390_base + E8390_CMD);
rxing_page = inb_p(e8390_base + EN1_CURPAG);
outb_p(E8390_NODMA+E8390_PAGE0, e8390_base + E8390_CMD);
/* Remove one frame from the ring. Boundary is alway a page behind. */
this_frame = inb_p(e8390_base + EN0_BOUNDARY) + 1;
if (this_frame >= ei_local->stop_page)
this_frame = ei_local->rx_start_page;
/* Someday we'll omit the previous step, iff we never get this message.*/
if (ei_debug > 0 && this_frame != ei_local->current_page)
printk("%s: mismatched read page pointers %2x vs %2x.\n",
dev->name, this_frame, ei_local->current_page);
if (this_frame == rxing_page) /* Read all the frames? */
break; /* Done for now */
current_offset = this_frame << 8;
ei_block_input(dev, sizeof(rx_frame), (void *)&rx_frame,
current_offset);
size = rx_frame.count - sizeof(rx_frame);
next_frame = this_frame + 1 + ((size+4)>>8);
/* Check for bogosity warned by 3c503 book: the status byte is never
written. This happened a lot during testing! This code should be
cleaned up someday, and the printk()s should be PRINTK()s. */
if ( rx_frame.next != next_frame
&& rx_frame.next != next_frame + 1
&& rx_frame.next != next_frame - num_rx_pages
&& rx_frame.next != next_frame + 1 - num_rx_pages) {
#ifndef EI_DEBUG
ei_local->current_page = rxing_page;
outb(ei_local->current_page-1, e8390_base+EN0_BOUNDARY);
continue;
#else
static int last_rx_bogosity = -1;
printk("%s: bogus packet header, status=%#2x nxpg=%#2x sz=%#x (at %#4x)\n",
dev->name, rx_frame.status, rx_frame.next, rx_frame.count,
current_offset);
if (rx_packets != last_rx_bogosity) {
/* Maybe we can avoid resetting the chip... empty the packet ring. */
ei_local->current_page = rxing_page;
printk("%s: setting next frame to %#2x (nxt=%#2x, rx_frm.nx=%#2x rx_frm.stat=%#2x).\n",
dev->name, ei_local->current_page, next_frame,
rx_frame.next, rx_frame.status);
last_rx_bogosity = rx_packets;
outb(ei_local->current_page-1, e8390_base+EN0_BOUNDARY);
continue;
} else {
/* Oh no Mr Bill! Last ditch error recovery. */
printk("%s: recovery failed, resetting at packet #%d..",
dev->name, rx_packets);
sti();
ei_reset_8390(dev);
NS8390_init(dev, 1);
printk("restarting.\n");
return;
}
#endif /* EI8390_NOCHECK */
}
if ((size < 32 || size > 1535) && ei_debug)
printk("%s: bogus packet size, status=%#2x nxpg=%#2x size=%#x\n",
dev->name, rx_frame.status, rx_frame.next, rx_frame.count);
if ((rx_frame.status & 0x0F) == ENRSR_RXOK) {
int sksize = sizeof(struct sk_buff) + size;
struct sk_buff *skb;
skb = kmalloc(sksize, GFP_ATOMIC);
if (skb != NULL) {
skb->lock = 0;
skb->mem_len = sksize;
skb->mem_addr = skb;
/* 'skb+1' points to the start of sk_buff data area. */
ei_block_input(dev, size, (void *)(skb+1),
current_offset + sizeof(rx_frame));
if (dev_rint((void *)skb, size, IN_SKBUFF, dev)) {
printk("%s: receive buffers full.\n", dev->name);
break;
}
} else if (ei_debug) {
printk("%s: Couldn't allocate a sk_buff of size %d.\n",
dev->name, sksize);
break;
}
ei_local->rx_packets++;
} else {
if (ei_debug)
printk("%s: bogus packet, status=%#2x nxpg=%#2x size=%d\n",
dev->name, rx_frame.status, rx_frame.next, rx_frame.count);
ei_local->soft_rx_err_bits |= rx_frame.status,
ei_local->soft_rx_errors++;
}
next_frame = rx_frame.next;
/* This should never happen, it's here for debugging. */
if (next_frame >= ei_local->stop_page) {
printk("%s: next frame inconsistency, %#2x..", dev->name, next_frame);
next_frame = ei_local->rx_start_page;
}
ei_local->current_page += 1 + ((size+4)>>8);
ei_local->current_page = next_frame;
outb(next_frame-1, e8390_base+EN0_BOUNDARY);
}
/* If any worth-while packets have been received, dev_rint()
has done a mark_bh(INET_BH) for us and will work on them
when we get to the bottom-half routine. */
/* Bug alert! Reset ENISR_OVER to avoid spurious overruns! */
outb_p(ENISR_RX+ENISR_RX_ERR+ENISR_OVER, e8390_base+EN0_ISR);
return;
}
/* We have a receiver overrun: we have to kick the 8390 to get it started
again.*/
static void
ei_rx_overrun(struct device *dev)
{
int e8390_base = dev->base_addr;
int reset_start_time = jiffies;
struct ei_device *ei_local = dev->private;
/* We should already be stopped and in page0. Remove after testing. */
outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base+E8390_CMD);
if (ei_debug)
printk("%s: Receiver overrun.\n", dev->name);
ei_local->rx_overruns++;
/* The we.c driver does dummy = inb_p( RBCR[01] ); at this point.
It might mean something -- magic to speed up a reset? A 8390 bug?*/
/* Wait for reset in case the NIC is doing a tx or rx. This could take up to
1.5msec, but we have no way of timing something in that range. The 'jiffies'
are just a sanity check. */
while ((inb_p(e8390_base+EN0_ISR) & ENISR_RESET) == 0)
if (jiffies - reset_start_time > 1) {
printk("%s: reset did not complete at ei_rx_overrun.\n",
dev->name);
NS8390_init(dev, 1);
return;
};
{
int old_rx_packets = ei_local->rx_packets;
/* Remove packets right away. */
ei_receive(dev);
ei_local->rx_overrun_packets +=
(ei_local->rx_packets - old_rx_packets);
}
outb_p(0xff, e8390_base+EN0_ISR);
/* Generic 8390 insns to start up again, same as in open_8390(). */
outb_p(E8390_NODMA + E8390_PAGE0 + E8390_START, e8390_base + E8390_CMD);
outb_p(E8390_TXCONFIG, e8390_base + EN0_TXCR); /* xmit on. */
}
int
ethif_init(struct device *dev)
{
if (ei_debug > 1)
printk(version);
/* The open call may be overridden by the card-specific code. */
dev->open = &ei_open;
/* Make up a ei_local structure. */
dev->private = kmalloc(sizeof(struct ei_device), GFP_KERNEL);
memset(dev->private, 0, sizeof(struct ei_device));
if (1
#ifdef WD80x3
&& ! wdprobe(dev->base_addr, dev)
#endif
#ifdef EL2
&& ! el2autoprobe(dev->base_addr, dev)
#endif
#ifdef NE2000
&& ! neprobe(dev->base_addr, dev)
#endif
#ifdef HPLAN
&& ! hpprobe(dev->base_addr, dev)
#endif
&& 1 ) {
printk("No ethernet device found.\n");
kfree(dev->private);
dev->private = NULL;
return 1; /* ENODEV or EAGAIN would be more accurate. */
}
return ethdev_init(dev);
}
/* Initialize the rest of the device structure. */
int
ethdev_init(struct device *dev)
{
int i;
for (i = 0; i < DEV_NUMBUFFS; i++)
dev->buffs[i] = NULL;
dev->hard_header = eth_hard_header;
dev->add_arp = eth_add_arp;
dev->queue_xmit = dev_queue_xmit;
dev->rebuild_header = eth_rebuild_header;
dev->type_trans = eth_type_trans;
if (dev->private == NULL) {
dev->private = kmalloc(sizeof(struct ei_device), GFP_KERNEL);
memset(dev->private, 0, sizeof(struct ei_device));
}
dev->send_packet = 0;
dev->hard_start_xmit = &ei_start_xmit;
dev->type = ETHER_TYPE;
dev->hard_header_len = sizeof (struct enet_header);
dev->mtu = 1500; /* eth_mtu */
dev->addr_len = ETHER_ADDR_LEN;
for (i = 0; i < dev->addr_len; i++) {
dev->broadcast[i]=0xff;
}
return 0;
}
/* This page of functions should be 8390 generic */
/* Follow National Semi's recommendations for initializing the "NIC". */
void NS8390_init(struct device *dev, int startp)
{
int e8390_base = dev->base_addr;
struct ei_device *ei_local = dev->private;
int i;
int endcfg = ei_local->word16 ? (0x48 | ENDCFG_WTS) : 0x48;
/* Follow National Semi's recommendations for initing the DP83902. */
outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base); /* 0x21 */
outb_p(endcfg, e8390_base + EN0_DCFG); /* 0x48 or 0x49 */
/* Clear the remote byte count registers. */
outb_p(0x00, e8390_base + EN0_RCNTLO);
outb_p(0x00, e8390_base + EN0_RCNTHI);
/* Set to monitor and loopback mode -- this is vital!. */
outb_p(E8390_RXOFF, e8390_base + EN0_RXCR); /* 0x20 */
outb_p(E8390_TXOFF, e8390_base + EN0_TXCR); /* 0x02 */
/* Set the transmit page and receive ring. */
outb_p(ei_local->tx_start_page, e8390_base + EN0_TPSR);
ei_local->tx1 = ei_local->tx2 = 0;
outb_p(ei_local->rx_start_page, e8390_base + EN0_STARTPG);
outb_p(ei_local->stop_page-1, e8390_base + EN0_BOUNDARY); /* 3c503 says 0x3f,NS0x26*/
ei_local->current_page = ei_local->rx_start_page; /* assert boundary+1 */
outb_p(ei_local->stop_page, e8390_base + EN0_STOPPG);
/* Clear the pending interrupts and mask. */
outb_p(0xFF, e8390_base + EN0_ISR);
outb_p(0x00, e8390_base + EN0_IMR);
/* Copy the station address into the DS8390 registers,
and set the multicast hash bitmap to receive all multicasts. */
cli();
outb_p(E8390_NODMA + E8390_PAGE1 + E8390_STOP, e8390_base); /* 0x61 */
for(i = 0; i < 6; i++) {
outb_p(dev->dev_addr[i], e8390_base + EN1_PHYS + i);
}
for(i = 0; i < 8; i++)
outb_p(0xff, e8390_base + EN1_MULT + i);
outb_p(ei_local->rx_start_page, e8390_base + EN1_CURPAG);
outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base);
sti();
if (startp) {
outb_p(0xff, e8390_base + EN0_ISR);
outb_p(ENISR_ALL, e8390_base + EN0_IMR);
outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base);
outb_p(E8390_TXCONFIG, e8390_base + EN0_TXCR); /* xmit on. */
/* 3c503 TechMan says rxconfig only after the NIC is started. */
outb_p(E8390_RXCONFIG, e8390_base + EN0_RXCR); /* rx on, */
}
return;
}
/* Trigger a transmit start, assuming the length is valid. */
static void NS8390_trigger_send(struct device *dev, unsigned int length,
int start_page)
{
int e8390_base = dev->base_addr;
ei_status.txing = 1;
outb_p(E8390_NODMA+E8390_PAGE0, e8390_base);
if (inb_p(e8390_base) & E8390_TRANS) {
printk("%s: trigger_send() called with the transmitter busy.\n",
dev->name);
return;
}
outb_p(length & 0xff, e8390_base + EN0_TCNTLO);
outb_p(length >> 8, e8390_base + EN0_TCNTHI);
outb_p(start_page, e8390_base + EN0_TPSR);
outb_p(E8390_NODMA+E8390_TRANS+E8390_START, e8390_base);
return;
}
/*
* Local variables:
* compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -DPINGPONG -I/usr/src/linux/net/tcp -c 8390.c"
* version-control: t
* kept-new-versions: 5
* End:
*/

View File

@@ -0,0 +1,148 @@
/* Generic NS8390 register definitions. */
/* This file is part of Donald Becker's 8390 drivers.
This file is distributed under the Linux GPL.
Some of these names and comments are from the Crynwr packet drivers. */
#ifndef e8390_h
#define e8390_h
#define PINGPONG
#ifdef PINGPONG
#define TX_PAGES 12
#else
#define TX_PAGES 6
#endif
#define ETHER_ADDR_LEN 6
/* From 8390.c */
void ei_interrupt(int reg_ptr);
/* From auto_irq.c */
extern void autoirq_setup(int waittime);
extern int autoirq_report(int waittime);
/* Most of these entries should be in 'struct device' (or most of the
things in there should be here!) */
/* You have one of these per-board */
struct ei_device {
char *name;
int open:1;
int word16:1; /* We have the 16-bit (vs 8-bit) version of the card. */
int txing:1; /* Transmit Active */
void (*reset_8390)(struct device *);
void (*block_output)(struct device *, int, const unsigned char *, int);
int (*block_input)(struct device *, int, char *, int);
unsigned char tx_start_page, rx_start_page, stop_page;
unsigned char current_page; /* Read pointer in buffer */
unsigned char thin_bit; /* Value to write to the 3c503 E33G_CNTRL */
unsigned char txqueue; /* Tx Packet buffer queue length. */
unsigned char in_interrupt;
short tx1, tx2; /* Packet lengths for ping-pong tx. */
/* The statistics: */
int tx_packets;
int tx_errors;
int rx_packets;
int soft_rx_errors;
int soft_rx_err_bits;
int missed_packets;
int rx_overruns;
int rx_overrun_packets;
};
#define ei_status (*(struct ei_device *)(dev->private))
/* Some generic ethernet register configurations. */
#define E8390_TX_IRQ_MASK 0xa /* For register EN0_ISR */
#define E8390_RX_IRQ_MASK 0x5
#define E8390_RXCONFIG 0x4 /* EN0_RXCR: broadcasts, no multicast,errors */
#define E8390_RXOFF 0x20 /* EN0_RXCR: Accept no packets */
#define E8390_TXCONFIG 0x00 /* EN0_TXCR: Normal transmit mode */
#define E8390_TXOFF 0x02 /* EN0_TXCR: Transmitter off */
/* Register accessed at EN_CMD, the 8390 base addr. */
#define E8390_STOP 0x01 /* Stop and reset the chip */
#define E8390_START 0x02 /* Start the chip, clear reset */
#define E8390_TRANS 0x04 /* Transmit a frame */
#define E8390_RREAD 0x08 /* Remote read */
#define E8390_RWRITE 0x10 /* Remote write */
#define E8390_NODMA 0x20 /* Remote DMA */
#define E8390_PAGE0 0x00 /* Select page chip registers */
#define E8390_PAGE1 0x40 /* using the two high-order bits */
#define E8390_PAGE2 0x80 /* Page 3 is invalid. */
#define E8390_CMD 0x00 /* The command register (for all pages) */
/* Page 0 register offsets. */
#define EN0_CLDALO 0x01 /* Low byte of current local dma addr RD */
#define EN0_STARTPG 0x01 /* Starting page of ring bfr WR */
#define EN0_CLDAHI 0x02 /* High byte of current local dma addr RD */
#define EN0_STOPPG 0x02 /* Ending page +1 of ring bfr WR */
#define EN0_BOUNDARY 0x03 /* Boundary page of ring bfr RD WR */
#define EN0_TSR 0x04 /* Transmit status reg RD */
#define EN0_TPSR 0x04 /* Transmit starting page WR */
#define EN0_NCR 0x05 /* Number of collision reg RD */
#define EN0_TCNTLO 0x05 /* Low byte of tx byte count WR */
#define EN0_FIFO 0x06 /* FIFO RD */
#define EN0_TCNTHI 0x06 /* High byte of tx byte count WR */
#define EN0_ISR 0x07 /* Interrupt status reg RD WR */
#define EN0_CRDALO 0x08 /* low byte of current remote dma address RD */
#define EN0_RSARLO 0x08 /* Remote start address reg 0 */
#define EN0_CRDAHI 0x09 /* high byte, current remote dma address RD */
#define EN0_RSARHI 0x09 /* Remote start address reg 1 */
#define EN0_RCNTLO 0x0a /* Remote byte count reg WR */
#define EN0_RCNTHI 0x0b /* Remote byte count reg WR */
#define EN0_RSR 0x0c /* rx status reg RD */
#define EN0_RXCR 0x0c /* RX configuration reg WR */
#define EN0_TXCR 0x0d /* TX configuration reg WR */
#define EN0_COUNTER0 0x0d /* Rcv alignment error counter RD */
#define EN0_DCFG 0x0e /* Data configuration reg WR */
#define EN0_COUNTER1 0x0e /* Rcv CRC error counter RD */
#define EN0_IMR 0x0f /* Interrupt mask reg WR */
#define EN0_COUNTER2 0x0f /* Rcv missed frame error counter RD */
/* Bits in EN0_ISR - Interrupt status register */
#define ENISR_RX 0x01 /* Receiver, no error */
#define ENISR_TX 0x02 /* Transmitter, no error */
#define ENISR_RX_ERR 0x04 /* Receiver, with error */
#define ENISR_TX_ERR 0x08 /* Transmitter, with error */
#define ENISR_OVER 0x10 /* Receiver overwrote the ring */
#define ENISR_COUNTERS 0x20 /* Counters need emptying */
#define ENISR_RDC 0x40 /* remote dma complete */
#define ENISR_RESET 0x80 /* Reset completed */
#define ENISR_ALL 0x3f /* Interrupts we will enable */
/* Bits in EN0_DCFG - Data config register */
#define ENDCFG_WTS 0x01 /* word transfer mode selection */
/* Page 1 register offsets. */
#define EN1_PHYS 0x01 /* This board's physical enet addr RD WR */
#define EN1_CURPAG 0x07 /* Current memory page RD WR */
#define EN1_MULT 0x08 /* Multicast filter mask array (8 bytes) RD WR */
/* Bits in received packet status byte and EN0_RSR*/
#define ENRSR_RXOK 0x01 /* Received a good packet */
#define ENRSR_CRC 0x02 /* CRC error */
#define ENRSR_FAE 0x04 /* frame alignment error */
#define ENRSR_FO 0x08 /* FIFO overrun */
#define ENRSR_MPA 0x10 /* missed pkt */
#define ENRSR_PHY 0x20 /* physical/multicase address */
#define ENRSR_DIS 0x40 /* receiver disable. set in monitor mode */
#define ENRSR_DEF 0x80 /* deferring */
/* Transmitted packet status, EN0_TSR. */
#define ENTSR_PTX 0x01 /* Packet transmitted without error */
/* The other bits in the TX status register mean:
0x02 The transmit wasn't deferred.
0x04 The transmit collided at least once.
0x08 The transmit collided 16 times, and was deferred.
0x10 The carrier sense was lost (from the ethernet transceiver)
0x20 A "FIFO underrun" (internal error) occured during transmit.
0x40 The collision detect "heartbeat" signal was lost.
0x80 There was an out-of-window collision.
*/
/* The per-packet-header format. */
struct e8390_pkt_hdr {
unsigned char status; /* status */
unsigned char next; /* pointer to next packet. */
unsigned short count; /* header + packet lenght in bytes */
};
#endif /* e8390_h */

View File

@@ -0,0 +1,44 @@
Subject: Enhanced Ethercard driver available for alpha test.
My "8390" Linux ethercard drivers are now available from usra.edu and
super.org in ~ftp/pub/linux/ethercards/*. They'll be at tsx-11 and
sunsite RSN.
These drivers support all common 8390-based ethernet boards. Currently
"common" is defined as:
3Com Products:
* 3Com 3c503 Board loaned by Chance Reschke, USRA.edu (thanks!)
3Com 3c503/16 and excellent documentation provided by 3Com.
Clones-n-things
NE1000 Novell and Eagle are useless for documentation,
* NE2000 but copied the designs directly from NatSemi;->.
* Alta Combo(NE2000 clone)
Aritsoft LANtastic AE-2 (NE2000 clone w/ extra memory)
D-Link Ethernet II
Cabletron products:
E1010 No ID PROM and sketchy info from Ctron means you'll
E1010-x have to compile-in information about your board.
E2010
E2010-x
WD/SMC products
WD8003
* WD8013 Board loaned by Russ Nelson, Crynwr Software. Thanks!
* I've seen it work myself!
There is support for the following boards, but since I've only been
able to borrow a thinnet of an HP ethercard I haven't been able to test it:
HP LAN adaptors
** HP27245
** HP27247
** HP27250
Thanks are due to the dozens of alpha testers, and special thanks to Chance Reschke <@usra.edu> and Russ Nelson <@crynwr.com> for loaning me ethercards.

View File

@@ -0,0 +1,33 @@
# This file must be named 'GNUmakefile'. When it has that name it
# loaded in preference to the regular 'Makefile' (which it include
# right here) so this has the effect of appending lines to the Makefile.
include Makefile
# Add a few files to tcpip.a.
newobjs = 8390.o 3c503.o ne.o wd.o hp.o auto_irq.o
OBJS := $(OBJS) $(newobjs)
tcpip.a: $(newobjs)
# Set the address and IRQ here. The ne.c and 3c503 driver will autoprobe
# if you set the address or IRQ to zero, so we do that by default.
#
# Add -DEI_NAME="eth0" if you want to be exactly compatible with the default
# driver. This will only work if you don't use the distributed 'we' driver!
#
ether_options := -DEI_NAME=\"eth0\" -DEI8390=0 -DEI8390_IRQ=0
Space.o: Space.c GNUmakefile
$(CC) $(CPPFLAGS) $(CFLAGS) $(ether_options) -c Space.c -o $@
# Change this to define the set of ethercards your kernel will support.
8390.o: 8390.c GNUmakefile
$(CC) $(CPPFLAGS) $(CFLAGS) -DNE2000 -DWD80x3 -DHPLAN -DEL2 -c 8390.c -o $@
# Change this to -DEL2_AUI if you use the AUI port.
3c503.o: 3c503.c 3c503reg.h GNUmakefile
$(CC) $(CPPFLAGS) $(CFLAGS) -UEL2_AUI -c 3c503.c -o $@
# Change this to -DSHMEM=0xd0000 and -DFORCE_8BIT if you have an old
# (non-EEPROM) wd8003.
wd.o: wd.c GNUmakefile
$(CC) $(CPPFLAGS) $(CFLAGS) -UWD_SHMEM -UFORCE_8BIT -c wd.c -o $@

View File

@@ -0,0 +1,14 @@
This is a new version of the ethercard drivers.
The new features are:
PING-PONG transmit! This can almost double the transmit rate in some
cases. 1MB/sec is now possible!!!
The 'ne' driver autoselects between the NE1000 and NE2000 modes.
There is a less-intrusive 3c503 probe.
The 3c503 can now be used in non-shared-memory mode.
The HP-LAN non-autoIRQ mode should now work.
The WD driver should now work with some older WD8003 cards, but this
is untested.

View File

@@ -0,0 +1,110 @@
/* auto_irq.c: Auto-configure IRQ lines for linux. */
/*
Written 1993 by Donald Becker. This is alpha test code.
The Author may be reached as becker@super.org or
C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
This code is a general-purpose IRQ line detector for devices with
jumpered IRQ lines. If you can make the device raise an IRQ (and
that IRQ line isn't already being used), these routines will tell
you what IRQ line it's using -- perfect for those oh-so-cool boot-time
device probes!
To use this, first call autoirq_setup(timeout). TIMEOUT is how many
'jiffies' (1/18 sec.) to detect other devices that have active IRQ lines,
and can usually be zero at boot. 'autoirq_setup()' returns the bit
vector of nominally-available IRQ lines (lines may be physically in-use,
but not yet registered to a device).
Next, set up your device to trigger an interrupt.
Finally call autoirq_report(TIMEOUT) to find out which IRQ line was
most recently active. The TIMEOUT should usually be zero, but may
be set to the number of jiffies to wait for a slow device to raise an IRQ.
The idea of using the setup timeout to filter out bogus IRQs came from
the serial driver.
*/
#ifdef version
static char *version="auto_irq.c:v0.01 1993 Donald Becker (becker@super.org)";
#endif
/*#include <linux/config.h>*/
/*#include <linux/kernel.h>*/
#include <linux/sched.h>
#include <asm/bitops.h>
#include <asm/io.h>
/*#include <asm/system.h>*/
int irqs_busy = 0x01; /* The set of fixed IRQs always enabled */
int irqs_used = 0x01; /* The set of fixed IRQs sometimes enabled. */
int irqs_reserved = 0x00; /* An advisory "reserved" table. */
int irqs_shared = 0x00; /* IRQ lines "shared" among conforming cards.*/
static volatile int irq_number; /* The latest irq number we actually found. */
static volatile int irq_bitmap; /* The irqs we actually found. */
static int irq_handled; /* The irq lines we have a handler on. */
static void autoirq_probe(int irq)
{
irq_number = irq;
set_bit(irq, (void *)&irq_bitmap); /* irq_bitmap |= 1 << irq; */
return;
}
struct sigaction autoirq_sigaction = { autoirq_probe, 0, SA_INTERRUPT, NULL};
int autoirq_setup(int waittime)
{
int i, mask;
int timeout = jiffies+waittime;
irq_number = 0;
irq_bitmap = 0;
irq_handled = 0;
for (i = 0; i < 16; i++) {
if (!irqaction(i, &autoirq_sigaction))
set_bit(i, (void *)&irq_handled); /* irq_handled |= 1 << i;*/
}
/* Update our USED lists. */
irqs_used |= ~irq_handled;
/* Hang out at least <waittime> jiffies waiting for bogus IRQ hits. */
while (timeout >= jiffies)
;
for (i = 0, mask = 0x01; i < 16; i++, mask <<= 1) {
if (irq_bitmap & irq_handled & mask) {
irq_handled &= ~mask;
printk(" Spurious interrupt on IRQ %d\n", i);
free_irq(i);
}
}
return irq_handled;
}
int autoirq_report(int waittime)
{
int i;
int timeout = jiffies+waittime;
/* Hang out at least <waittime> jiffies waiting for the IRQ. */
while (timeout >= jiffies)
if (irq_number)
break;
/* Retract the irq handlers that we installed. */
for (i = 0; i < 16; i++) {
if (test_bit(i, (void *)&irq_handled))
free_irq(i);
}
return irq_number;
}
/*
* Local variables:
* compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -I/usr/src/linux/net/tcp -c auto_irq.c"
* version-control: t
* kept-new-versions: 5
* End:
*/

View File

@@ -0,0 +1,319 @@
/* hp.c: A HP LAN ethernet driver for linux. */
/*
Written 1993 by Donald Becker. This is alpha test code.
This is a extension to the Linux operating system, and is covered by
same Gnu Public License that covers that work.
This is a driver for the HP LAN adaptors.
The Author may be reached as becker@super.org or
C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
*/
static char *version = "hp.c:v0.67 3/8/93 Donald Becker (becker@super.org)\n";
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <asm/io.h>
#include <asm/system.h>
#include "dev.h"
#include "8390.h"
/* These should be in <asm/io.h> someday, borrowed from blk_drv/hd.c. */
#define port_read(port,buf,nr) \
__asm__("cld;rep;insw"::"d" (port),"D" (buf),"c" (nr):"cx","di")
#define port_write(port,buf,nr) \
__asm__("cld;rep;outsw"::"d" (port),"S" (buf),"c" (nr):"cx","si")
#define port_read_b(port,buf,nr) \
__asm__("cld;rep;insb"::"d" (port),"D" (buf),"c" (nr):"cx","di")
#define port_write_b(port,buf,nr) \
__asm__("cld;rep;outsb"::"d" (port),"S" (buf),"c" (nr):"cx","si")
#define HP_DATAPORT 0x0c /* "Remote DMA" data port. */
#define HP_ID 0x07
#define HP_CONFIGURE 0x08 /* Configuration register. */
#define HP_RUN 0x01 /* 1 == Run, 0 == reset. */
#define HP_IRQ 0x0E /* Mask for software-configured IRQ line. */
#define HP_DATAON 0x10 /* Turn on dataport */
#define NIC_OFFSET 0x10 /* Offset the 8390 registers. */
#define HP_START_PG 0x00 /* First page of TX buffer */
#define HP_8BSTOP_PG 0x80 /* Last page +1 of RX ring */
#define HP_16BSTOP_PG 0xFF /* Last page +1 of RX ring */
extern void NS8390_init(struct device *dev, int startp);
extern int ei_debug;
extern struct sigaction ei_sigaction;
int hpprobe(int ioaddr, struct device *dev);
int hpprobe1(int ioaddr, struct device *dev);
static void hp_reset_8390(struct device *dev);
static int hp_block_input(struct device *dev, int count,
char *buf, int ring_offset);
static void hp_block_output(struct device *dev, int count,
const unsigned char *buf, const start_page);
static void hp_init_card(struct device *dev);
/* The map from IRQ number to HP_CONFIGURE register setting. */
/* My default is IRQ5 0 1 2 3 4 5 6 7 8 9 10 11 */
static char irqmap[16] = { 0, 0, 4, 6, 8,10, 0,14, 0, 4, 2,12,0,0,0,0};
/* Probe for an HP LAN adaptor.
Also initialize the card and fill in STATION_ADDR with the station
address. */
int hpprobe(int ioaddr, struct device *dev)
{
int *port, ports[] = {0x300, 0x320, 0x340, 0x280, 0x2C0, 0x200, 0x240, 0};
if (ioaddr > 0x100)
return hpprobe1(ioaddr, dev);
for (port = &ports[0]; *port; port++)
if (inb_p(*port) != 0xff && hpprobe1(*port, dev))
return dev->base_addr;
return 0;
}
int hpprobe1(int ioaddr, struct device *dev)
{
int i;
unsigned char *station_addr = dev->dev_addr;
unsigned char SA_prom[6];
int tmp;
int hplan;
printk("HP-LAN ethercard probe at %#3x:", ioaddr);
tmp = inb_p(ioaddr);
if (tmp == 0xFF) {
printk(" not found (nothing there).\n");
return 0;
}
for(i = 0; i < sizeof(SA_prom); i++) {
SA_prom[i] = inb(ioaddr + i);
if (i < ETHER_ADDR_LEN && station_addr) {
printk(" %2.2x", SA_prom[i]);
station_addr[i] = SA_prom[i];
}
}
hplan = (SA_prom[0] == 0x08 && SA_prom[1] == 0x00 && SA_prom[2] == 0x09);
if (hplan == 0) {
printk(" not found (invalid station address prefix).\n");
return 0;
}
ei_status.tx_start_page = HP_START_PG;
ei_status.rx_start_page = HP_START_PG + TX_PAGES;
/* Set up the rest of the parameters. */
if ((tmp = inb_p(HP_ID)) & 0x80) {
ei_status.name = "HP27247";
ei_status.word16 = 1;
ei_status.stop_page = HP_16BSTOP_PG; /* Safe for now */
} else {
ei_status.name = "HP27250";
ei_status.word16 = 0;
ei_status.stop_page = HP_8BSTOP_PG; /* Safe for now */
}
/* Set the base address to point to the NIC! */
dev->base_addr = ioaddr + NIC_OFFSET;
/* Snarf the interrupt now. There's no point in waiting since we cannot
share and the board will usually be enabled. */
if (dev->irq < 2) {
int irq_16list[] = { 11, 10, 5, 3, 4, 7, 9, 0};
int irq_8list[] = { 7, 5, 3, 4, 9, 0};
int *irqp = ei_status.word16 ? irq_16list : irq_8list;
do {
if (request_irq (dev->irq = *irqp, NULL) != -EBUSY) {
autoirq_setup(0);
/* Twinkle the interrupt, and check if it's seen. */
outb_p(irqmap[dev->irq] | HP_RUN, ioaddr + HP_CONFIGURE);
outb_p( 0x00 | HP_RUN, ioaddr + HP_CONFIGURE);
if (dev->irq == autoirq_report(0) /* It's a good IRQ line! */
&& request_irq (dev->irq, &ei_interrupt) == 0) {
printk(" got IRQ %d", dev->irq);
break;
} else
printk(" IRQ%d busy..", dev->irq);
}
} while (*++irqp);
if (*irqp == 0) {
printk(" unable to find an free IRQ line.\n");
return 0;
}
} else {
if (dev->irq == 2)
dev->irq = 9;
if (irqaction(dev->irq, &ei_sigaction)) {
printk (" unable to get IRQ %d.\n", dev->irq);
return 0;
}
}
printk("\n%s: %s using IRQ %d.\n", dev->name, ei_status.name, dev->irq);
if (ei_debug > 1)
printk(version);
ei_status.reset_8390 = &hp_reset_8390;
ei_status.block_input = &hp_block_input;
ei_status.block_output = &hp_block_output;
hp_init_card(dev);
return dev->base_addr;
}
static void
hp_reset_8390(struct device *dev)
{
int hp_base = dev->base_addr - NIC_OFFSET;
int saved_config = inb_p(hp_base + HP_CONFIGURE);
int reset_start_time = jiffies;
if (ei_debug > 1) printk("resetting the 8390 time=%d...", jiffies);
outb_p(0x00, hp_base + HP_CONFIGURE);
ei_status.txing = 0;
sti();
/* We shouldn't use the boguscount for timing, but this hasn't been
checked yet, and you could hang your machine if jiffies break... */
{
int boguscount = 150000;
while(jiffies - reset_start_time < 2)
if (boguscount-- < 0) {
printk("jiffy failure (t=%d)...", jiffies);
break;
}
}
outb_p(saved_config, hp_base + HP_CONFIGURE);
while ((inb_p(hp_base+NIC_OFFSET+EN0_ISR) & ENISR_RESET) == 0)
if (jiffies - reset_start_time > 2) {
printk("%s: hp_reset_8390() did not complete.\n", dev->name);
return;
}
if (ei_debug > 1) printk("8390 reset done.", jiffies);
}
/* Block input and output, similar to the Crynwr packet driver. If you
porting to a new ethercard look at the packet driver source for hints.
The HP LAN doesn't use shared memory -- we put the packet
out through the "remote DMA" dataport. */
static int
hp_block_input(struct device *dev, int count, char *buf, int ring_offset)
{
int nic_base = dev->base_addr;
int saved_config = inb_p(nic_base - NIC_OFFSET + HP_CONFIGURE);
int xfer_count = count;
outb_p(saved_config | HP_DATAON, nic_base - NIC_OFFSET + HP_CONFIGURE);
outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base);
outb_p(count & 0xff, nic_base + EN0_RCNTLO);
outb_p(count >> 8, nic_base + EN0_RCNTHI);
outb_p(ring_offset & 0xff, nic_base + EN0_RSARLO);
outb_p(ring_offset >> 8, nic_base + EN0_RSARHI);
outb_p(E8390_RREAD+E8390_START, nic_base);
if (ei_status.word16) {
port_read(nic_base - NIC_OFFSET + HP_DATAPORT,buf,count>>1);
if (count & 0x01)
buf[count-1] = inb(nic_base - NIC_OFFSET + HP_DATAPORT), xfer_count++;
} else {
port_read_b(nic_base - NIC_OFFSET + HP_DATAPORT, buf, count);
}
/* This is for the ALPHA version only, remove for later releases. */
if (ei_debug > 0) { /* DMA termination address check... */
int high = inb_p(nic_base + EN0_RSARHI);
int low = inb_p(nic_base + EN0_RSARLO);
int addr = (high << 8) + low;
/* Check only the lower 8 bits so we can ignore ring wrap. */
if (((ring_offset + xfer_count) & 0xff) != (addr & 0xff))
printk("%s: RX transfer address mismatch, %#4.4x vs. %#4.4x (actual).\n",
dev->name, ring_offset + xfer_count, addr);
}
outb_p(saved_config & (~HP_DATAON), nic_base - NIC_OFFSET + HP_CONFIGURE);
return ring_offset + count;
}
static void
hp_block_output(struct device *dev, int count,
const unsigned char *buf, const start_page)
{
int nic_base = dev->base_addr;
int saved_config = inb_p(nic_base - NIC_OFFSET + HP_CONFIGURE);
outb_p(saved_config | HP_DATAON, nic_base - NIC_OFFSET + HP_CONFIGURE);
/* Round the count up for word writes. Do we need to do this?
What effect will an odd byte count have on the 8390?
I should check someday. */
if (ei_status.word16 && (count & 0x01))
count++;
/* We should already be in page 0, but to be safe... */
outb_p(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base);
#ifdef ei8390_bug
/* Handle the read-before-write bug the same way as the
Crynwr packet driver -- the NatSemi method doesn't work. */
outb_p(0x42, nic_base + EN0_RCNTLO);
outb_p(0, nic_base + EN0_RCNTHI);
outb_p(0xff, nic_base + EN0_RSARLO);
outb_p(0x00, nic_base + EN0_RSARHI);
outb_p(E8390_RREAD+E8390_START, EN_CMD);
/* Make certain that the dummy read has occured. */
inb_p(0x61);
inb_p(0x61);
#endif
outb_p(count & 0xff, nic_base + EN0_RCNTLO);
outb_p(count >> 8, nic_base + EN0_RCNTHI);
outb_p(0x00, nic_base + EN0_RSARLO);
outb_p(start_page, nic_base + EN0_RSARHI);
outb_p(E8390_RWRITE+E8390_START, nic_base);
if (ei_status.word16) {
/* Use the 'rep' sequence for 16 bit boards. */
port_write(nic_base - NIC_OFFSET + HP_DATAPORT, buf, count>>1);
} else {
port_write_b(nic_base - NIC_OFFSET + HP_DATAPORT, buf, count);
}
/* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here -- it's broken! */
/* This is for the ALPHA version only, remove for later releases. */
if (ei_debug > 0) { /* DMA termination address check... */
int high = inb_p(nic_base + EN0_RSARHI);
int low = inb_p(nic_base + EN0_RSARLO);
int addr = (high << 8) + low;
if ((start_page << 8) + count != addr)
printk("%s: TX Transfer address mismatch, %#4.4x vs. %#4.4x.\n",
dev->name, (start_page << 8) + count, addr);
}
outb_p(saved_config & (~HP_DATAON), nic_base - NIC_OFFSET + HP_CONFIGURE);
return;
}
/* This function resets the ethercard if something screws up. */
static void
hp_init_card(struct device *dev)
{
int irq = dev->irq;
NS8390_init(dev, 0);
outb_p(irqmap[irq&0x0f] | HP_RUN,
dev->base_addr - NIC_OFFSET + HP_CONFIGURE);
return;
}
/*
* Local variables:
* compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -I/usr/src/linux/net/tcp -c hp.c"
* version-control: t
* kept-new-versions: 5
* End:
*/

View File

@@ -0,0 +1,379 @@
/* ne.c: A general non-shared-memory NS8390 ethernet driver for linux. */
/*
Written 1992,1993 by Donald Becker. This is alpha test code.
This is a extension to the Linux operating system, and is covered by
same Gnu Public License that covers that work.
This driver should work with many 8390-based ethernet boards. Currently
it support the NE1000, NE2000 (and clones), and some Cabletron products.
8-bit ethercard support is enabled with #define EI_8BIT
The Author may be reached as becker@super.org or
C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
*/
/* Routines for the NatSemi-based designs (NE[12]000). */
static char *version =
"ne.c:v0.50 2/19/93 Donald Becker (becker@super.org)\n";
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <asm/io.h>
#include <asm/system.h>
#include "dev.h"
#include "8390.h"
/* These should be in <asm/io.h> someday, borrowed from blk_drv/hd.c. */
#define port_read(port,buf,nr) \
__asm__("cld;rep;insw"::"d" (port),"D" (buf),"c" (nr):"cx","di")
#define port_write(port,buf,nr) \
__asm__("cld;rep;outsw"::"d" (port),"S" (buf),"c" (nr):"cx","si")
#define port_read_b(port,buf,nr) \
__asm__("cld;rep;insb"::"d" (port),"D" (buf),"c" (nr):"cx","di")
#define port_write_b(port,buf,nr) \
__asm__("cld;rep;outsb"::"d" (port),"S" (buf),"c" (nr):"cx","si")
#define EN_CMD (dev->base_addr)
#define NE_BASE (dev->base_addr)
#define NE_DATAPORT 0x10 /* NatSemi-defined port window offset. */
#define NE_RESET 0x1f /* Issue a read to reset, a write to clear. */
#define NE1SM_START_PG 0x20 /* First page of TX buffer */
#define NE1SM_STOP_PG 0x40 /* Last page +1 of RX ring */
#define NESM_START_PG 0x40 /* First page of TX buffer */
#define NESM_STOP_PG 0x80 /* Last page +1 of RX ring */
extern void NS8390_init(struct device *dev, int startp);
extern int ei_debug;
extern struct sigaction ei_sigaction;
int neprobe(int ioaddr, struct device *dev);
static int neprobe1(int ioaddr, struct device *dev, int verbose);
static void ne_reset_8390(struct device *dev);
static int ne_block_input(struct device *dev, int count,
char *buf, int ring_offset);
static void ne_block_output(struct device *dev, const int count,
const unsigned char *buf, const int start_page);
/* Probe for various non-shared-memory ethercards.
NEx000-clone boards have a Station Address PROM (SAPROM) in the packet
buffer memory space. 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 compansated for.
The probe is also responsible for initializing the card and filling
in the 'dev' and 'ei_status' structures.
We use the minimum memory size for some ethercard product lines, iff we can't
distinguish models. You can increase the packet buffer size by setting
PACKETBUF_MEMSIZE. Reported Cabletron packet buffer locations are:
E1010 starts at 0x100 and ends at 0x2000.
E1010-x starts at 0x100 and ends at 0x8000. ("-x" means "more memory")
E2010 starts at 0x100 and ends at 0x4000.
E2010-x starts at 0x100 and ends at 0xffff. */
int neprobe(int ioaddr, struct device *dev)
{
int *port, ports[] = {0x300, 0x280, 0x320, 0x340, 0x360, 0};
if (ioaddr > 0x100)
return neprobe1(ioaddr, dev, 1);
for (port = &ports[0]; *port; port++)
if (inb_p(*port) != 0xff && neprobe1(*port, dev, 0))
return dev->base_addr = *port;
return 0;
}
static int neprobe1(int ioaddr, struct device *dev, int verbose)
{
int i;
unsigned char SA_prom[32];
int wordlength = 2;
int neX000, ctron, dlink;
if ( inb_p(ioaddr) == 0xFF) {
if (verbose) printk("8390 ethercard probe at %#3x failed.\n", ioaddr);
return 0;
}
printk("8390 ethercard probe at %#3x:", ioaddr);
/* Read the 16 bytes of station address prom, returning 1 for
an eight-bit interface and 2 for a 16-bit interface.
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 {char value, offset; } program_seq[] = {
{E8390_NODMA+E8390_PAGE0+E8390_STOP, EN_CMD}, /* Select page 0 */
{0x48, EN0_DCFG}, /* Set byte-wide (0x48) 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, EN_CMD},
};
for (i = 0; i < sizeof(program_seq)/sizeof(program_seq[0]); i++)
outb_p(program_seq[i].value, ioaddr + program_seq[i].offset);
}
for(i = 0; i < 32 /*sizeof(SA_prom)*/; i+=2) {
SA_prom[i] = inb_p(ioaddr + NE_DATAPORT);
SA_prom[i+1] = inb_p(ioaddr + NE_DATAPORT);
if (SA_prom[i] != SA_prom[i+1])
wordlength = 1;
}
if (wordlength == 2) {
/* We must set the 8390 for word mode, AND RESET IT. */
int tmp;
outb_p(0x49, ioaddr + EN0_DCFG);
tmp = inb_p(NE_BASE + NE_RESET);
ei_status.word16 = 1;
outb(tmp, NE_BASE + NE_RESET);
/* Un-double the SA_prom values. */
for (i = 0; i < 16; i++)
SA_prom[i] = SA_prom[i+i];
} else
ei_status.word16 = 0;
#if defined(show_all_SAPROM)
/* If your ethercard isn't detected define this to see the SA_PROM. */
for(i = 0; i < sizeof(SA_prom); i++)
printk(" %2.2x", SA_prom[i]);
#else
for(i = 0; i < ETHER_ADDR_LEN; i++) {
dev->dev_addr[i] = SA_prom[i];
printk(" %2.2x", SA_prom[i]);
}
#endif
neX000 = (SA_prom[14] == 0x57 && SA_prom[15] == 0x57);
ctron = (SA_prom[0] == 0x00 && SA_prom[1] == 0x00 && SA_prom[2] == 0x1d);
dlink = (SA_prom[0] == 0x00 && SA_prom[1] == 0xDE && SA_prom[2] == 0x01);
/* Set up the rest of the parameters. */
if (neX000 && wordlength == 2) {
ei_status.name = "NE2000";
ei_status.tx_start_page = NESM_START_PG;
ei_status.stop_page = NESM_STOP_PG;
} else if (neX000 || dlink) {
ei_status.name = neX000 ? "NE1000" : "D-Link";
ei_status.tx_start_page = NE1SM_START_PG;
ei_status.stop_page = NE1SM_STOP_PG;
} else if (ctron) {
ei_status.name = "Cabletron";
ei_status.tx_start_page = 0x01;
ei_status.stop_page = (wordlength == 2) ? 0x40 : 0x20;
} else {
printk(" not found.\n");
return 0;
}
ei_status.rx_start_page = ei_status.tx_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
dev->base_addr = ioaddr;
if (dev->irq < 2) {
int nic_base = dev->base_addr;
autoirq_setup(0);
outb_p(0x50, nic_base + EN0_IMR); /* Enable one interrupt. */
outb_p(0x00, nic_base + EN0_RCNTLO);
outb_p(0x00, nic_base + EN0_RCNTHI);
outb_p(E8390_RREAD+E8390_START, nic_base); /* Trigger it... */
outb_p(0x00, nic_base + EN0_IMR); /* Mask it again. */
dev->irq = autoirq_report(0);
if (ei_debug > 2)
printk(" autoirq is %d", dev->irq);
} else if (dev->irq == 2)
/* Fixup for users that don't know that IRQ 2 is really IRQ 9,
or don't know which one to set. */
dev->irq = 9;
/* Snarf the interrupt now. There's no point in waiting since we cannot
share and the board will usually be enabled. */
{ int irqval = irqaction (dev->irq, &ei_sigaction);
if (irqval) {
printk (" unable to get IRQ %d (irqval=%d).\n", dev->irq, irqval);
return 0;
}
}
printk("\n%s: %s found, using IRQ %d.\n",
dev->name, ei_status.name, dev->irq);
if (ei_debug > 1)
printk(version);
ei_status.reset_8390 = &ne_reset_8390;
ei_status.block_input = &ne_block_input;
ei_status.block_output = &ne_block_output;
NS8390_init(dev, 0);
return dev->base_addr;
}
static void
ne_reset_8390(struct device *dev)
{
int tmp = inb_p(NE_BASE + NE_RESET);
int reset_start_time = jiffies;
if (ei_debug > 1) printk("resetting the 8390 t=%d...", jiffies);
ei_status.txing = 0;
sti();
/* We shouldn't use the boguscount for timing, but this hasn't been
checked yet, and you could hang your machine if jiffies break... */
{
int boguscount = 150000;
while(jiffies - reset_start_time < 2)
if (boguscount-- < 0) {
printk("jiffy failure (t=%d)...", jiffies);
outb_p(tmp, NE_BASE + NE_RESET);
return;
}
}
outb_p(tmp, NE_BASE + NE_RESET);
while ((inb_p(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;
}
}
/* Block input and output, similar to the Crynwr packet driver. If you
porting to a new ethercard look at the packet driver source for hints.
The NEx000 doesn't share it on-board packet memory -- you have to put
the packet out through the "remote DMA" dataport using outb. */
static int
ne_block_input(struct device *dev, int count, char *buf, int ring_offset)
{
int xfer_count = count;
int nic_base = NE_BASE;
outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, EN_CMD);
outb_p(count & 0xff, nic_base + EN0_RCNTLO);
outb_p(count >> 8, nic_base + EN0_RCNTHI);
outb_p(ring_offset & 0xff, nic_base + EN0_RSARLO);
outb_p(ring_offset >> 8, nic_base + EN0_RSARHI);
outb_p(E8390_RREAD+E8390_START, EN_CMD);
if (ei_status.word16) {
port_read(NE_BASE + NE_DATAPORT,buf,count>>1);
if (count & 0x01)
buf[count-1] = inb(NE_BASE + NE_DATAPORT), xfer_count++;
} else {
port_read_b(NE_BASE + NE_DATAPORT, buf, count);
}
/* This was for the ALPHA version only, but enough people have
encountering problems that it is still here. */
if (ei_debug > 0) { /* DMA termination address check... */
int addr, tries = 10;
do {
/* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here
-- it's broken! Check the "DMA" address instead. */
int high = inb_p(nic_base + EN0_RSARHI);
int low = inb_p(nic_base + EN0_RSARLO);
addr = (high << 8) + low;
if (((ring_offset + xfer_count) & 0xff) == low)
return ring_offset + count;
} while (--tries > 0);
printk("%s: RX transfer address mismatch, %#4.4x (should be) vs. %#4.4x (actual).\n",
dev->name, ring_offset + xfer_count, addr);
}
return ring_offset + count;
}
static void
ne_block_output(struct device *dev, int count,
const unsigned char *buf, const int start_page)
{
int retries = 0;
int nic_base = NE_BASE;
/* Round the count up for word writes. Do we need to do this?
What effect will an odd byte count have on the 8390?
I should check someday. */
if (ei_status.word16 && (count & 0x01))
count++;
/* We should already be in page 0, but to be safe... */
outb_p(E8390_PAGE0+E8390_START+E8390_NODMA, EN_CMD);
retry:
#if defined(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 aways work either, but if you have
problems with your NEx000 this is better than nothing! */
outb_p(0x42, nic_base + EN0_RCNTLO);
outb_p(0x00, nic_base + EN0_RCNTHI);
outb_p(0x42, nic_base + EN0_RSARLO);
outb_p(0x00, nic_base + EN0_RSARHI);
outb_p(E8390_RREAD+E8390_START, EN_CMD);
/* Make certain that the dummy read has occured. */
SLOW_DOWN_IO;
SLOW_DOWN_IO;
SLOW_DOWN_IO;
#endif /* rw_bugfix */
/* Now the normal output. */
outb_p(count & 0xff, nic_base + EN0_RCNTLO);
outb_p(count >> 8, nic_base + EN0_RCNTHI);
outb_p(0x00, nic_base + EN0_RSARLO);
outb_p(start_page, nic_base + EN0_RSARHI);
outb_p(E8390_RWRITE+E8390_START, EN_CMD);
if (ei_status.word16) {
port_write(NE_BASE + NE_DATAPORT, buf, count>>1);
} else {
port_write_b(NE_BASE + NE_DATAPORT, buf, count);
}
/* This was for the ALPHA version only, but enough people have
encountering problems that it is still here. */
if (ei_debug > 0) { /* DMA termination address check... */
int addr, tries = 10;
do {
/* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here
-- it's broken! Check the "DMA" address instead. */
int high = inb_p(nic_base + EN0_RSARHI);
int low = inb_p(nic_base + EN0_RSARLO);
addr = (high << 8) + low;
if ((start_page << 8) + count == addr)
return;
} while (--tries > 0);
printk("%s: Packet buffer transfer address mismatch on TX, %#4.4x vs. %#4.4x.\n",
dev->name, (start_page << 8) + count, addr);
if (retries++ == 0)
goto retry;
}
return;
}
/*
* Local variables:
* compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -I/usr/src/linux/net/tcp -c ne.c"
* version-control: t
* kept-new-versions: 5
* End:
*/

View File

@@ -0,0 +1,334 @@
/* wd.c: A WD80x3 ethernet driver for linux. */
/*
Written 1993 by Donald Becker. This is alpha test code.
This is a extension to the Linux operating system, and is covered by
same Gnu Public License that covers that work.
This is a driver for the WD8003 and WD8013 ethercards.
The Author may be reached as becker@super.org or
C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
Thanks to Russ Nelson (nelson@crnwyr.com) for loaning me a WD8013.
*/
static char *version =
"wd.c:v0.66 3/7/93 Donald Becker (becker@super.org)\n";
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <asm/io.h>
#include <asm/system.h>
#include <memory.h>
#include "dev.h"
#include "8390.h"
extern void NS8390_init(struct device *dev, int startp);
extern int ei_debug;
extern struct sigaction ei_sigaction;
int wdprobe(int ioaddr, struct device *dev);
int wdprobe1(int ioaddr, struct device *dev);
static void wd_reset_8390(struct device *dev);
static int wd_block_input(struct device *dev, int count,
char *buf, int ring_offset);
static void wd_block_output(struct device *dev, int count,
const unsigned char *buf, const start_page);
static int wd_close_card(struct device *dev);
#define WD_START_PG 0x00 /* First page of TX buffer */
#define WD03_STOP_PG 0x20 /* Last page +1 of RX ring */
#define WD13_STOP_PG 0x40 /* Last page +1 of RX ring */
#define WD_CMDREG 0 /* Offset to ASIC command register. */
#define WD_RESET 0x80 /* Board reset, in WD_CMDREG. */
#define WD_MEMENB 0x40 /* Enable the shared memory. */
#define WD_CMDREG5 5 /* Offset to 16-bit-only ASIC register 5. */
#define ISA16 0x80 /* Enable 16 bit access from the ISA bus. */
#define NIC16 0x40 /* Enable 16 bit access from the 8390. */
#define WD_NIC_OFFSET 16 /* Offset to the 8390 NIC from the base_addr. */
/* Probe for the WD8003 and WD8013. These cards have the station
address PROM at I/O ports <base>+8 to <base>+13, with a checksum
following. The routine also initializes the card and fills the
station address field. */
int wdprobe(int ioaddr, struct device *dev)
{
int *port, ports[] = {0x300, 0x280, 0};
if (ioaddr > 0x100)
return wdprobe1(ioaddr, dev);
for (port = &ports[0]; *port; port++)
if (inb(*port) != 0xff && wdprobe1(*port, dev))
return *port;
return 0;
}
int wdprobe1(int ioaddr, struct device *dev)
{
int i;
unsigned char *station_addr = dev->dev_addr;
int checksum = 0;
int ancient = 0; /* An old card without config registers. */
#if defined(EI_DEBUG) && EI_DEBUG > 2
printk("WD80x3 ethercard at %#3x:", ioaddr);
for (i = 0; i < 16; i++) {
printk(" %2.2X", inb(ioaddr+i));
}
printk("\n");
printk("WD80x3 ethercard at %#3x:", ioaddr+i);
for (;i < 33; i++) {
printk(" %2.2X", inb(ioaddr+i));
}
printk("\n");
#endif
printk("WD80x3 ethercard probe at %#3x:", ioaddr);
for (i = 0; i < 8; i++) {
int inval = inb(ioaddr + 8 + i);
checksum += inval;
if (i < 6)
printk(" %2.2X", (station_addr[i] = inval));
}
if ((checksum & 0xff) != 0xFF) {
printk(" not found (%#2.2x).\n", checksum);
return 0;
}
ei_status.name = "WD8003";
ei_status.word16 = 0;
/* This method of checking for a 16-bit board is borrowed from the
we.c driver. A simpler method is just to look in ASIC reg. 0x03.
I'm comparing the two method in alpha test to make certain they
return the same result. */
#ifndef FORCE_8BIT /* Same define as we.c. */
/* check for 16 bit board - it doesn't have register 0/8 aliasing */
for (i = 0; i < 8; i++)
if (inb(ioaddr+i) != inb(ioaddr+8+i))
break;
if (i != 8) {
int tmp = inb(ioaddr+1); /* fiddle with 16bit bit */
outb( tmp ^ 0x01, ioaddr+1 ); /* attempt to clear 16bit bit */
if (((inb( ioaddr+1) & 0x01) == 0x01) /* A 16 bit card */
&& (tmp & 0x01) == 0x01 ) { /* In a 16 slot. */
int asic_reg5 = inb(ioaddr+WD_CMDREG5);
/* Magic to set ASIC to word-wide mode. */
outb( ISA16 | NIC16 | (asic_reg5&0x1f), ioaddr+WD_CMDREG5);
outb(tmp, ioaddr+1);
ei_status.name = "WD8013";
ei_status.word16 = 1; /* We have a 16bit board here! */
}
outb(tmp, ioaddr+1); /* Restore original reg1 value. */
} else
ancient = 1;
#endif /* not FORCE_8BIT */
#ifndef final_version
if ( !ancient && (inb(ioaddr+1) & 0x01) != (ei_status.word16 & 0x01))
printk("\nWD80?3: Bus width conflict, %d (probe) != %d (reg report).",
ei_status.word16 ? 16:8, (inb(ioaddr+1) & 0x01) ? 16 : 8);
#endif
#if defined(WD_SHMEM) && WD_SHMEM > 0x80000
/* Allow an override. */
dev->mem_start = WD_SHMEM;
#else
if (dev->mem_start == 0) {
dev->mem_start = ((inb(ioaddr)&0x3f) << 13) +
(ei_status.word16 ? (inb(ioaddr+WD_CMDREG5)&0x1f)<<19 : 0x80000);
if (dev->mem_start < 0xc0000) /* Sanity and old 8003 check */
dev->mem_start = 0xd0000;
}
#endif
/* The 8390 isn't at the base address -- the ASIC regs are there! */
dev->base_addr = ioaddr+WD_NIC_OFFSET;
ei_status.tx_start_page = WD_START_PG;
ei_status.rx_start_page = WD_START_PG + TX_PAGES;
ei_status.stop_page = ei_status.word16 ? WD13_STOP_PG : WD03_STOP_PG;
dev->rmem_start = dev->mem_start + TX_PAGES*256;
dev->mem_end = dev->rmem_end
= dev->mem_start + (ei_status.stop_page - WD_START_PG)*256;
#if defined(EI_DEBUG) && EI_DEBUG > 3
memset((void*)dev->mem_start, 0x42052465,
(ei_status.stop_page - WD_START_PG)*256);
#endif
if (dev->irq < 2) {
int irqmap[] = {9,3,5,7,10,11,15,4};
dev->irq = irqmap[((inb(ioaddr+4) >> 5) & 0x03)
+ (inb(ioaddr+1) & 0x04)];
} else if (dev->irq == 2)
/* Fixup for users that don't know that IRQ 2 is really IRQ 9,
or don't know which one to set. */
dev->irq = 9;
/* Snarf the interrupt now. There's no point in waiting since we cannot
share and the board will usually be enabled. */
{ int irqval = irqaction (dev->irq, &ei_sigaction);
if (irqval) {
printk (" unable to get IRQ %d (irqval=%d).\n", dev->irq, irqval);
return 0;
}
}
printk("\n%s: %s using IRQ %d with shared memory at %#x-%#x.\n",
dev->name, ei_status.name, dev->irq, dev->mem_start, dev->mem_end-1);
if (ei_debug > 1)
printk(version);
if (ei_debug > 2)
printk("%s: Address read from register is %#x, setting address %#x\n",
ei_status.name,
((inb(ioaddr+WD_CMDREG5)&0x1f)<<19) + ((inb(ioaddr)&0x3f) << 13),
dev->mem_start);
/* Map in the shared memory. This is a little risky, since using
the stuff the user supplied is probably a bad idea. */
outb((((dev->mem_start>>13) & 0x3f)|WD_MEMENB), ioaddr); /* WD_CMDREG */
if (ei_status.word16)
outb( ISA16 | NIC16 | ((dev->mem_start>>19) & 0x1f), ioaddr+WD_CMDREG5);
ei_status.reset_8390 = &wd_reset_8390;
ei_status.block_input = &wd_block_input;
ei_status.block_output = &wd_block_output;
dev->stop = &wd_close_card;
NS8390_init(dev, 0);
return dev->base_addr;
}
static void
wd_reset_8390(struct device *dev)
{
int wd_cmd_port = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
int reset_start_time = jiffies;
outb(WD_RESET, wd_cmd_port);
if (ei_debug > 1) printk("resetting the WD80x3 t=%d...", jiffies);
ei_status.txing = 0;
sti();
/* We shouldn't use the boguscount for timing, but this hasn't been
checked yet, and you could hang your machine if jiffies break... */
{
int boguscount = 150000;
while(jiffies - reset_start_time < 2)
if (boguscount-- < 0) {
printk("jiffy failure (t=%d)...", jiffies);
break;
}
}
outb(0x00, wd_cmd_port);
while ((inb(dev->base_addr+EN0_ISR) & ENISR_RESET) == 0)
if (jiffies - reset_start_time > 2) {
printk("%s: wd_reset_8390() did not complete.\n", dev->name);
break;
}
#if defined(EI_DEBUG) && EI_DEBUG > 2
{
int i;
printk("WD80x3 ethercard at %#3x:", wd_cmd_port);
for (i = 0; i < 16; i++) {
printk(" %2.2X", inb(wd_cmd_port+i));
}
printk("\nWD80x3 ethercard at %#3x:", wd_cmd_port);
for (;i < 33; i++) {
printk(" %2.2X", inb(wd_cmd_port+i));
}
printk("\n");
}
#endif
/* Set up the ASIC registers, just in case something changed them. */
outb((((dev->mem_start>>13) & 0x3f)|WD_MEMENB), wd_cmd_port); /* WD_CMDREG */
if (ei_status.word16)
outb( ISA16 | NIC16 | ((dev->mem_start>>19) & 0x1f), wd_cmd_port+WD_CMDREG5);
}
/* Block input and output are easy on shared memory ethercards, and trivial
on the Western digital card where there is no choice of how to do it. */
static int
wd_block_input(struct device *dev, int count, char *buf, int ring_offset)
{
void *xfer_start = (void *)(dev->mem_start + ring_offset - (WD_START_PG<<8));
#ifdef mapout
int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
int mem_val = inb(wd_cmdreg);
/* Map in the shared memory. */
outb(mem_val|WD_MEMENB, wd_cmdreg);
#endif
if (xfer_start + count > (void*) dev->rmem_end) {
/* We must wrap the input move. */
int semi_count = (void*)dev->rmem_end - xfer_start;
memcpy(buf, xfer_start, semi_count);
count -= semi_count;
memcpy(buf + semi_count, (char *)dev->rmem_start, count);
return dev->rmem_start + count;
}
memcpy(buf, xfer_start, count);
if (ei_debug > 4) {
unsigned short *board = xfer_start;
printk("%s: wd8013 block_input(cnt=%d offset=%3x addr=%#x) = %2x %2x %2x...\n",
dev->name, count, ring_offset, xfer_start,
board[-1], board[0], board[1]);
}
#ifdef mapout
outb(mem_val & ~WD_MEMENB, wd_cmdreg); /* WD_CMDREG: Map out the shared memory. */
#endif
return ring_offset + count;
}
/* This could only be outputting to the transmit buffer. The
ping-pong transmit setup doesn't work with this yet. */
static void
wd_block_output(struct device *dev, int count, const unsigned char *buf, int start_page)
{
unsigned char *shmem = (void *)dev->mem_start + ((start_page - WD_START_PG)<<8);
#ifdef mapout
int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
int mem_val = inb(wd_cmdreg);
/* Map in the shared memory. */
outb(mem_val|WD_MEMENB, wd_cmdreg);
#endif
memcpy(shmem, buf, count);
if (ei_debug > 4)
printk("%s: wd80*3 block_output(addr=%#x cnt=%d) -> %2x=%2x %2x=%2x %d...\n",
shmem, count, shmem[23], buf[23], shmem[24], buf[24], memcmp(shmem,buf,count));
#ifdef mapout
outb(mem_val & ~WD_MEMENB, wd_cmdreg); /* WD_CMDREG: Map out the shared memory. */
#endif
}
/* This function resets the ethercard if something screws up. */
static int
wd_close_card(struct device *dev)
{
if (ei_debug > 1)
printk("%s: Shutting down ethercard.\n", dev->name);
NS8390_init(dev, 0);
/* Turn off the shared memory. */
outb((((dev->mem_start>>13) & 0x3f)),
dev->base_addr-WD_NIC_OFFSET); /* WD_CMDREG */
return 0;
}
/*
* Local variables:
* compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -I/usr/src/linux/net/tcp -c wd.c"
* version-control: t
* kept-new-versions: 5
* End:
*/