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

340
net/tcpip/newether/3c503.c Normal file
View File

@@ -0,0 +1,340 @@
/* 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.28 1/28/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"
#include "3c503reg.h"
#define EL2AUTOPROBE
extern void NS8390_init(struct device *dev, int startp);
extern int ei_debug;
extern struct sigaction ei_sigaction;
extern struct ei_device ei_status;
int etherlink2 = 0;
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[] = { 0xddfff, 0xd9fff, 0xcdfff, 0xc9fff, 0};
int ports[] = {0, 0x300, 0x310, 0x330, 0x350, 0x250, 0x280, 0x2a0, 0x2e0, 0};
/* Non-autoprobe case first: */
if (ioaddr > 0)
if (el2probe(ioaddr, dev))
return dev->base_addr = ioaddr;
else
return 0;
/* We check for a memory-mapped 3c503 board by looking at the
end of boot PROM space (works even if a PROM isn't there). */
for (addr = addrs; *addr; addr++) {
unsigned int base_bits = *(unsigned char *)*addr, i;
/* Find first set bit. */
for(i = 8; i; i--, base_bits >>= 1)
if (base_bits & 0x1)
break;
if (base_bits == 1 && el2probe(ports[i], dev))
return dev->base_addr = ports[i];
}
#ifdef notdef
/* If it's not memory mapped, we don't care to find it. I haven't
tested the non-memory-mapped code. */
/* It's not memory mapped -- try all of the locations that aren't
obviously empty. */
{ int *port;
for (port = &ports[1]; *port; port++)
if (inb_p(*port) != 0xff && el2probe(*port, dev))
return dev->base_addr = *port;
}
#endif
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, found, mem_jumpers;
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);
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)));
}
/* Map the 8390 back into the window. */
outb(ECNTRL_THIN, ioaddr + 0x406);
found =( station_addr[0] == 0x02
&& station_addr[1] == 0x60
&& station_addr[2] == 0x8c);
if (! found) {
printk(" 3C503 not found.\n");
return 0;
}
/* Probe for, turn on and clear the board's shared memory. */
mem_jumpers = inb(ioaddr + 0x404); /* E33G_ROMBASE */
if (ei_debug > 2) printk(" memory jumpers %2.2x ", mem_jumpers);
outb(EGACFR_IRQOFF, E33G_GACFR); /* Enable RAM */
if ((mem_jumpers & 0xf0) == 0) {
dev->mem_start = 0;
if (ei_debug > 1) printk(" no shared memory ");
} else {
dev->mem_start = ((mem_jumpers & 0xc0) ? 0xD8000 : 0xC8000) +
((mem_jumpers & 0xA0) ? 0x4000 : 0);
#define EL2_MEMSIZE (EL2SM_STOP_PG - EL2SM_START_PG)*256
#ifdef EL2MEMTEST
{ /* 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. */
etherlink2 = 1;
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. */
#ifdef EI8390_THICK
ei_status.thin_bit = 0;
#else
ei_status.thin_bit = ECNTRL_THIN;
#endif
if (dev->irq <= 0)
dev->irq = 5; /* Autoprobe guess... */
if (dev->irq == 2)
dev->irq = 9;
else if (dev->irq != 9 && (dev->irq > 5 || dev->irq < 3)) {
printk("\n3c503: configured interrupt number %d out of range.\n",
dev->irq);
return 0;
}
if (irqaction (dev->irq, &ei_sigaction)) {
printk ("\n3c503: Unable to get IRQ%d.\n", dev->irq);
/* We could look for other free interrupts here, or something... */
return 0;
}
el2_init_card(dev);
if (dev->mem_start)
printk("3c503 found, memory at %#6x, IRQ %d\n",
dev->mem_start, dev->irq);
else
printk(" 3c503 found, no shared memory, IRQ %d\n", dev->irq);
if (ei_debug > 2)
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("3c503: Resetting the board...");
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);
outb_p(EGACFR_IRQOFF, 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 */
if (dev->mem_start) { /* Shared memory transfer */
void *dest_addr = (void *)(dev->mem_start +
((start_page - ei_status.tx_start_page) << 8));
outb(EGACFR_NORM, E33G_GACFR); /* Enable RAM */
memcpy(dest_addr, buf, count);
if (ei_debug > 2 && memcmp(dest_addr, buf, count))
printk("3c503: send_packet() bad memory copy @ %#5x.\n",
dest_addr);
else if (ei_debug > 4)
printk("3c503: send_packet() good memory copy @ %#5x.\n",
dest_addr);
return;
}
/* Set up then start the internal memory transfer to Tx Start Page */
outb(0x00, E33G_DMAAL);
outb(start_page, E33G_DMAAH);
outb(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 (count % 8 == 7)
while ((inb(E33G_STATUS) & ESTAT_DPRDY) == 0)
if (++boguscount > 32) {
printk(EI_NAME": fifo blocked in el2_block_output.\n");
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;
ring_offset -= (EL2SM_START_PG<<8);
/* Maybe enable shared memory just be to be safe... nahh.*/
if (dev->mem_start) { /* Use the shared memory. */
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("3c503: block_input() @ %#5x+%x=%5x.\n",
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("3c503: block_input() @ %#5x+%x=%5x.\n",
dev->mem_start, ring_offset,
(char *)dev->mem_start + ring_offset);
memcpy(buf, (char *)dev->mem_start + ring_offset, count);
return ring_offset + count;
} else { /* No shared memory, use the fifo. */
int i;
outb(ring_offset & 0xff, E33G_DMAAL);
outb((ring_offset >> 8) & 0xff, E33G_DMAAH);
outb(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 (count % 8 == 7)
while ((inb(E33G_STATUS) & ESTAT_DPRDY) == 0)
if (++boguscount > 32) {
printk(EI_NAME": fifo blocked in el2_block_input().\n");
return 0;
}
buf[i] = inb(E33G_FIFOH);
}
outb(ei_status.thin_bit, E33G_CNTRL);
ring_offset += count;
if (ring_offset >= end_of_ring)
ring_offset = dev->rmem_start + ring_offset - end_of_ring;
return ring_offset;
}
}
/*
* 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 */

712
net/tcpip/newether/8390.c Normal file
View File

@@ -0,0 +1,712 @@
/* 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.27 1/27/93 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 3Com databooks, both companies
provided information readily. The NE* 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 "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_status.reset_8390)
#define ei_block_output (ei_status.block_output)
#define ei_block_input (ei_status.block_input)
#define EN_CMD (e8390_base)
#define E8390_BASE (e8390_base)
/* use 0 for production, 1 for verification, >2 for debug */
#ifdef EI_DEBUG
int ei_debug = EI_DEBUG;
#else
int ei_debug = 2;
#endif
static int e8390_base;
static struct device *eifdev; /* For single-board consistency checking. */
extern int etherlink2;
struct ei_device ei_status = { EI_NAME, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
/* The statistics, perhaps these should be in the above structure. */
static int tx_packets = 0;
static int rx_packets = 0;
static int tx_errors = 0;
static int soft_rx_errors = 0;
static int soft_rx_err_bits = 0;
static int missed_packets = 0;
/* Index to functions. */
/* Put in the device structure. */
static int ei_open(struct device *dev);
static void ei_send_packet(struct sk_buff *skb, struct device *dev);
/* Dispatch from interrupts. */
static 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);
/* 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)
{
if ( ! ei_status.exists) {
printk(EI_NAME ": Opening a non-existent physical device\n");
return 1; /* ENXIO would be more accurate. */
}
NS8390_init(dev, 1);
ei_status.txing = 0;
ei_status.in_interrupt = 0;
ei_status.open = 1;
return 0;
}
static int
ei_start_xmit(struct sk_buff *skb, struct device *dev)
{
if ( ! ei_status.exists)
return 0; /* We should be able to do ENODEV, but nooo. */
if (ei_status.txing) { /* Do timeouts, just like the 8003 driver. */
int txsr = inb(E8390_BASE+EN0_TSR);
int tickssofar = jiffies - dev->trans_start;
if (tickssofar < 5 || (tickssofar < 15 && ! (txsr & ENTSR_PTX))) {
return 1;
}
printk(EI_NAME": transmit timed out, TX status %#2x, ISR %#2x.\n",
txsr, inb(E8390_BASE+EN0_ISR));
/* It's possible to check for an IRQ conflict here.
I may have to do that someday. */
if ((txsr & ~0x02) == ENTSR_PTX) /* Strip an undefined bit. */
printk(EI_NAME": Possible IRQ conflict?\n");
else
printk(EI_NAME": Possible network cable problem?\n");
/* It futile, but try to restart it anyway. */
ei_reset_8390(dev);
NS8390_init(dev, 1);
}
/* 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) {
/* Alternative is ei_tx_intr(dev); */
ei_status.txing = 1;
if (dev_tint(NULL, dev) == 0)
ei_status.txing = 0;
return 0;
}
/* Fill in the ethernet header. */
if (!skb->arp && dev->rebuild_header(skb+1, dev)) {
skb->dev = dev;
arp_queue (skb);
return 0;
}
dev->trans_start = jiffies;
cli();
ei_status.txing = 1;
#ifdef PINGPONG
ei_tx_intr(dev);
#endif
ei_send_packet(skb, dev);
sti();
if (skb->free)
#ifdef notdef /* 0.98.5 and beyond use kfree_skb(). */
free_skb (skb, FREE_WRITE);
#else
kfree_skb (skb, FREE_WRITE);
#endif
return 0;
}
/* The typical workload of the driver:
Handle the ether interface interrupts. */
static void
ei_interrupt(int reg_ptr)
{
int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2);
struct device *dev;
int interrupts, boguscount = 0;
/* We do the same thing as the 8013 driver, but this is mostly bogus. */
for (dev = dev_base; dev != NULL; dev = dev->next) {
if (dev->irq == irq) break;
}
ei_status.in_interrupt++;
sti(); /* Allow other interrupts. */
#ifdef notneeded
/* If we a getting a reset-complete interrupt the 8390 might not be
mapped in for the 3c503. */
if (etherlink2)
outb_p(ei_status.thin_bit, E33G_CNTRL),
outb_p(0x00, E33G_STATUS);
#endif
/* Change to page 0 and read the intr status reg. */
outb_p(E8390_NODMA+E8390_PAGE0, EN_CMD);
if (ei_debug > 3)
printk(EI_NAME": interrupt(isr=%#2.2x).\n",
inb_p(E8390_BASE + EN0_ISR));
if (ei_status.in_interrupt > 1)
printk(EI_NAME ": Reentering the interrupt driver!\n");
if (dev == NULL) {
printk (EI_NAME ": irq %d for unknown device\n", irq);
ei_status.in_interrupt--;
return;
} else if (ei_debug > 0 && eifdev != dev) {
printk (EI_NAME": device mismatch on irq %d.\n", irq);
dev = eifdev;
}
/* !!Assumption!! -- we stay in page 0. Don't break this. */
while ((interrupts = inb_p(E8390_BASE + EN0_ISR)) != 0
&& ++boguscount < 20) {
/* The reset interrupt is the most important... */
if (interrupts & ENISR_RDC) {
outb_p(ENISR_RDC, E8390_BASE + EN0_ISR); /* Ack intr. */
}
if (interrupts & ENISR_OVER) {
ei_status.overruns++;
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) {
/* Gotta read the counter to clear the irq, even if we
don't care about their values. */
inb_p(E8390_BASE + EN0_COUNTER0);
inb_p(E8390_BASE + EN0_COUNTER1);
missed_packets += inb_p(E8390_BASE + EN0_COUNTER2);
outb_p(ENISR_COUNTERS, E8390_BASE + EN0_ISR); /* Ack intr. */
outb_p(E8390_NODMA + E8390_PAGE0 + E8390_START, EN_CMD);
}
/* 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, EN_CMD);
}
if (interrupts && ei_debug) {
printk(EI_NAME ": unknown interrupt %#2x\n", interrupts);
outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, EN_CMD);
outb_p(0xff, E8390_BASE + EN0_ISR); /* Ack. all intrs. */
}
ei_status.in_interrupt--;
return;
}
#ifdef PINGPONG
static int lasttx = 0;
#endif
/* This is stuffed into the dev struct to be called by dev.c:dev_tint().
Evenually this should be replaced by the block_output() routines. */
static void
ei_send_packet(struct sk_buff *skb, struct device *dev)
{
int length = skb->len;
int send_length = ETHER_MIN_LEN < length ? length : ETHER_MIN_LEN;
if (length <= 0)
return;
#ifdef PINGPONG
if (ei_status.tx1 == 0) { /* First buffer empty */
ei_block_output(dev, length, (void*)(skb+1),
ei_status.tx_start_page);
ei_status.tx1 = send_length;
} else if (ei_status.tx2 == 0) { /* Second buffer empty */
ei_block_output(dev, length, (void*)(skb+1),
ei_status.tx_start_page+6);
ei_status.tx2 = send_length;
} else {
printk("%s: Internal error, no transmit buffer space tx1=%d tx2=%d lasttx=%d.\n",
ei_status.name, ei_status.tx1, ei_status.tx2, lasttx);
}
ei_status.txqueue++;
/* The following should be merged with ei_tx_intr(). */
if (lasttx = 0) {
if (ei_status.tx1 > 0) {
NS8390_trigger_send(dev, ei_status.tx1,
ei_status.tx_start_page + 6),
ei_status.tx1 = -1,
lasttx = 1;
} else if (ei_status.tx2 > 0) {
NS8390_trigger_send(dev, ei_status.tx2,
ei_status.tx_start_page + 6),
ei_status.tx2 = -1,
lasttx = 2;
}
}
#else
ei_block_output(dev, length, (void*)(skb+1), ei_status.tx_start_page);
NS8390_trigger_send(dev, send_length, ei_status.tx_start_page);
#endif
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 status = inb(E8390_BASE + EN0_TSR);
outb_p(ENISR_TX, E8390_BASE + EN0_ISR); /* Ack intr. */
if ((status & ENTSR_PTX) == 0)
tx_errors++;
else
tx_packets++;
#ifdef PINGPONG
{
ei_status.txqueue--;
if (ei_status.tx1 < 0) {
if (lasttx != 1)
printk("%s: bogus last_tx_buffer %d, tx1=%d.\n",
ei_status.name, lasttx, ei_status.tx1);
ei_status.tx1 = 0;
lasttx = 0;
if (ei_status.tx2 > 0) {
NS8390_trigger_send(dev, ei_status.tx2,
ei_status.tx_start_page + 6),
ei_status.tx2 = -1,
lasttx = 2;
}
} else if (ei_status.tx2 < 0) {
if (lasttx != 2)
printk("%s: bogus last_tx_buffer %d, tx2=%d.\n",
ei_status.name, lasttx, ei_status.tx2);
ei_status.tx2 = 0;
lasttx = 0;
if (ei_status.tx1 > 0) {
NS8390_trigger_send(dev, ei_status.tx1,
ei_status.tx_start_page),
ei_status.tx1 = -1;
lasttx = 1;
}
} /*else
printk(EI_NAME": unexpected TX interrupt.\n");*/
while ((ei_status.tx1 == 0) || (ei_status.tx2 == 0))
if (dev_tint(NULL, dev) == 0) {
ei_status.txing = 0;
return;
} else if (lasttx == 0) {
if (ei_status.tx1 == 0 || ei_status.tx2 != 0)
printk(EI_NAME": Unexpected tx buffer busy tx1=%d tx2=%d.\n",
ei_status.tx1, ei_status.tx2);
NS8390_trigger_send(dev, ei_status.tx1,
ei_status.tx_start_page),
ei_status.tx1 = -1;
lasttx = 1;
}
}
#else
if (dev_tint(NULL, dev) == 0)
ei_status.txing = 0;
#endif
}
/* We have a good packet(s), get it/them out of the buffers. */
static void
ei_receive(struct device *dev)
{
int rxing_page, this_frame, next_frame, current_offset;
int boguscount = 0;
struct e8390_pkt_hdr rx_frame;
int num_rx_pages = ei_status.stop_page-ei_status.rx_start_page;
while (++boguscount < 10) {
int size;
cli();
outb_p(E8390_NODMA+E8390_PAGE1, EN_CMD); /* Get the rec. page. */
rxing_page = inb_p(E8390_BASE+EN1_CURPAG);/* (Incoming packet pointer).*/
outb_p(E8390_NODMA+E8390_PAGE0, EN_CMD);
sti();
/* 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_status.stop_page)
this_frame = ei_status.rx_start_page;
/* Someday we'll omit the previous step, iff we never get this message.*/
if (ei_debug > 0 && this_frame != ei_status.current_page)
printk(EI_NAME": mismatched read page pointers %2x vs %2x.\n",
this_frame, ei_status.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_status.current_page = rxing_page;
outb(ei_status.current_page-1, E8390_BASE+EN0_BOUNDARY);
continue;
#else
static int last_rx_bogosity = -1;
printk(EI_NAME": bogus packet header, status=%#2x nxpg=%#2x sz=%#x (at %#4x)\n",
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_status.current_page = rxing_page;
printk(EI_NAME": setting next frame to %#2x (nxt=%#2x, rx_frm.nx=%#2x rx_frm.stat=%#2x).\n",
ei_status.current_page, next_frame,
rx_frame.next, rx_frame.status);
last_rx_bogosity = rx_packets;
outb(ei_status.current_page-1, E8390_BASE+EN0_BOUNDARY);
continue;
} else {
/* Oh no Mr Bill! Last ditch error recovery. */
printk(EI_NAME": multiple sequential lossage, resetting at packet #%d.",
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(EI_NAME": bogus big packet, status=%#2x nxpg=%#2x size=%#x\n",
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(EI_NAME": receive buffers full.\n");
break;
}
} else if (ei_debug) {
printk(EI_NAME": Couldn't allocate a sk_buff of size %d.\n", sksize);
break;
}
rx_packets++;
} else {
if (ei_debug)
printk(EI_NAME": bogus packet, status=%#2x nxpg=%#2x size=%d\n",
rx_frame.status, rx_frame.next, rx_frame.count);
soft_rx_err_bits |= rx_frame.status,
soft_rx_errors++;
}
next_frame = rx_frame.next;
/* This should never happen, it's here for debugging. */
if (next_frame >= ei_status.stop_page) {
printk(EI_NAME": next frame inconsistency, %#2x..", next_frame);
next_frame = ei_status.rx_start_page;
}
ei_status.current_page += 1 + ((size+4)>>8);
#ifdef notdef
if (ei_status.current_page > ei_status.stop_page)
ei_status.current_page -= ei_status.stop_page-ei_status.rx_start_page;
if (ei_status.current_page != next_frame) {
printk(EI_NAME": inconsistency in next_frame %#2x!=%#2x.\n",
this_frame, next_frame);
/* Assume this packet frame is scrogged by the NIC, use magic to
skip to the next frame. Actually we should stop and restart.*/
next_frame = size > 1535 ? rx_frame.status : rx_frame.next;
ei_status.current_page = next_frame;
break;
}
#endif
ei_status.current_page = next_frame;
outb(next_frame-1, E8390_BASE+EN0_BOUNDARY);
}
/* Tell the upper levels we're done. */
while (dev_rint(NULL, 0, 0, dev) == 1
&& ++boguscount < 20)
;
/* Bug alert! Reset ENISR_OVER to avoid spurious overruns! */
outb_p(ENISR_RX+ENISR_RX_ERR+ENISR_OVER, E8390_BASE+EN0_ISR); /* Ack intr. */
return;
}
/* We have a receiver overrun: we have to kick the 8390 to get it started
again. Overruns are detected on a per-256byte-page basis. */
static void
ei_rx_overrun(struct device *dev)
{
int reset_start_time = jiffies;
/* We should already be stopped and in page0. Remove after testing. */
outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, EN_CMD);
if (ei_debug)
printk(EI_NAME ": Receiver overrun.\n");
/* 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(EI_NAME": reset did not complete at ei_rx_overrun.\n");
NS8390_init(dev, 1);
return;
};
/* Remove packets right away. */
ei_debug += 5;
ei_receive(dev);
ei_debug -= 5;
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, EN_CMD);
outb_p(E8390_TXCONFIG, E8390_BASE + EN0_TXCR); /* xmit on. */
#ifdef notneeded
outb_p(E8390_RXCONFIG, E8390_BASE + EN0_RXCR); /* rx on, */
#endif
}
int
ethif_init(struct device *dev)
{
int i;
eifdev = dev; /* Store for debugging. */
if (ei_debug > 3)
printk(version);
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 ) {
dev->open = &ei_open;
printk("No ethernet device found.\n");
ei_status.exists = 0;
return 1; /* ENODEV or EAGAIN would be more accurate. */
}
e8390_base = dev->base_addr;
/* Initialize the rest of the device structure. Many of these could
be in Space.c. */
for (i = 0; i < DEV_NUMBUFFS; i++)
dev->buffs[i] = NULL;
ei_status.exists = 1;
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;
dev->send_packet = &ei_send_packet;
dev->open = &ei_open;
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 i;
int endcfg = ei_status.word16 ? (0x48 | ENDCFG_WTS) : 0x48;
int e8390_base = dev->base_addr;
/* 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_status.tx_start_page, e8390_base + EN0_TPSR);
ei_status.tx1 = ei_status.tx2 = 0;
outb_p(ei_status.rx_start_page, e8390_base + EN0_STARTPG);
outb_p(ei_status.stop_page-1, e8390_base + EN0_BOUNDARY); /* 3c503 says 0x3f,NS0x26*/
ei_status.current_page = ei_status.rx_start_page; /* assert boundary+1 */
outb_p(ei_status.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_status.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;
outb_p(E8390_NODMA+E8390_PAGE0, e8390_base);
if (inb_p(EN_CMD) & E8390_TRANS) {
printk(EI_NAME": trigger_send() called with the transmitter busy.\n");
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);
outb_p(ENISR_RDC, e8390_base + EN0_ISR);
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:
*/

129
net/tcpip/newether/8390.h Normal file
View File

@@ -0,0 +1,129 @@
/* 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
#ifndef EI_NAME
#define EI_NAME "eth_if"
#endif
/*#define PINGPONG*/
#ifdef PINGPONG
#define TX_PAGES 12
#else
#define TX_PAGES 6
#endif
#define ETHER_ADDR_LEN 6
/* 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
things in there should be here!) */
struct ei_device { /* These should be stored per-board */
char *name;
int exists:1; /* perhaps in dev->private. */
int open:1;
int word16:1; /* We have the 16-bit (vs 8-bit) version of the card. */
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 txing; /* Transmit Active, don't confuse the upper levels. */
unsigned char txqueue; /* Tx Packet buffer queue length. */
unsigned char in_interrupt;
short tx1, tx2; /* Packet lengths for ping-pong tx. */
int overruns; /* Rx overruns. */
};
/* 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. */
/* 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 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,27 @@
# 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!
#
Space.o: Space.c GNUmakefile
$(CC) $(CPPFLAGS) $(CFLAGS) -DEI8390=0 -DEI8390_IRQ=0 -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 $@
# And set any special compile-time options here.
ne.o: ne.c GNUmakefile
$(CC) $(CPPFLAGS) $(CFLAGS) -UEI_8BIT -c ne.c -o $@
3c503.o: 3c503.c 3c503reg.h GNUmakefile
$(CC) $(CPPFLAGS) $(CFLAGS) -UEI8390_THICK -c 3c503.c -o $@

View File

@@ -0,0 +1,77 @@
Installation Directions:
EMail me (becker@super.org) telling me you've gotten the "experimental
drivers". I need to know how many people have tried, succeeded and
failed before this is released as part of the official Linux.
Use Linux 0.99.2 or later. Make certain you can make a working kernel
_before_ you install the ethercard driver.
Put the all of the files into linux/net/tcp/. You'll need
GNUmakefile 8390.c 8390.h Space.c wd.c ne.c 3c503.c 3c503reg.h and hp.c.
Space.c is the only tricky one -- it overwrites the old Space.c.
Stock versions of Space.c leave the "we" driver enabled and will not work.
Change the GNUmakefile to reflect your configuration. Use the guide at
the end of these instructions.
Make and install your new kernel.
To actually use this driver you must get the TCP/IP package and edit
your /usr/etc/inet/rc.net file to config "eth_if" instead of
"eth0" (the WD8003). (Alternately you can edit the GNUmakefile to use the
"eth0" name.)
If you try to 'config' an interface that doesn't exist your kernel
will report "invalid ioctl()" for anthing that tries to use the card.
Note that the ethercard devices aren't (yet) real '/dev/eth_if' devices --
they only exist in the socket namespace and thus you don't need to
'mknode' them.
________________
Important defines
For Space.c
#define EI8390 0x300 /* The base address of your ethercard. */
#define EI8390_IRQ 5 /* and the interrupt you want to use. */
For 8390.c
#define EI_DEBUG 2 /* Use '0' for no messages. */
#define EL2 /* For the 3c503 driver. */
#define NE2000 /* For the NE1000/NE2000/Ctron driver. */
#define WD80x3 /* For the WD8003/WD8013 driver. */
#define HPLAN /* For the HP27xxx driver. */
For the individual drivers
#undef EI_8BIT /* Define for ne.c iff you have an 8 bit card.*/
#undef EI8390_THICK /* Define for 3c503 AUI/DIX transceiver.*/
EI8390 Define (probably in autoconf.h or config.site.h) this to the base
address of your ethernet card.
EI8390_IRQ Define (probably in autoconf.h or config.site.h) this to the
IRQ line of your ethernet card. Most drivers convert a IRQ2 to an
IRQ9 for you, so don't be surprised.
EI_DEBUG Set to the desired numeric debugging level. Use 3 or
greater when actively debugging a problem, '1' for a
casual interest in what's going on, and '0' for normal
use. (Most of the debugging stuff has been taken out recently,
so this won't have much effect.)
EI_PINGPONG
Not included or broken the alpha version. Define this if you want
ping-pong transmit buffers.
EI_8BIT
If you are using the ne.c driver and have an 8-bit card (NE1000 or
E1xxx Cabletron) you must define this. It's not needed for the other
drivers, and I hope to find a way to clean this up in the future.
EI8390_THICK
Define for this if you are using the 3c503 and use the AUI/DIX
connector rather than the built-in thin-net transceiver.
If you have a Cabletron ethercard you might want to look at ne.c:neprobe()
for info on how to enable more packet buffer space.
ETHERLINK1_IRQ
ETHERLINK1 Define these to the base address and IRQ of a 3c501 (NOT 3c503)
card. Refer to net/tcp/Space.c.

80
net/tcpip/newether/README Normal file
View File

@@ -0,0 +1,80 @@
This is the experimental version of the 8390 ethercard driver.
You should consult the list below for the specific #define for your
ethercard. If you don't have one specific #define the driver will
probe for all supported ethercard types.
Source Header #define to get Supported cards
8390.c 8390.h EI8390 (generic, needed for all)
3c503.c 3c503reg.h EL2 3c503, 3c503/16
ne.c NE2000 NE1000, NE2000, NatSemi, Cabletron
wd.c wd.h WD80x3 WD8003, WD8013, SMC elite
hp.c HPLAN HP LAN adaptors
Notes on each package
3c503.c
o You'll need to #define EI8390_THICK if you are using the AUI port instead
of the thinnet connector. Russ Nelson <nelson@crynwr.com> sent me an
run-time method of selecting between the two, but I haven't put it in yet.
This _may_ generate a spurious error message when transmitting the first
packet, I haven't yet tracked down this bug report.
o If you want to check the shared memory, #define EL2MEMTEST. I don't think
the memory check is worth the effort -- it's missed obvious problems.
o You must jumper the board to use shared memory -- I don't test the
non-shared-memory mode and it's probably broken.
o Thanks to Chance Reschke at USRA <creschke@usra.edu> for providing the
3c503 and anonymous FTP space.
ne.c
o You'll need to #define EI_8BIT if you are using an 8-bit ethercard
such as the NE1000 or Cabletron E10xx. I might someday make a run-time
selection between the NE1000 and NE2000 (right now it screws up the
non-default bus-width) but I don't know how to distinguish the Cabletron
ethercards. I'm hoping to find a general way to identify the board's bus
width, but don't hold your breath.
wd.c
o Thanks to Russ Nelson (nelson@crynwr.com) for loaning me a WD8013.
o The 8013 doesn't work if it's probed by some other driver first.
Make it either first in the probe list or the only driver you compile in.
Maybe I should reset the board before looking at it to fix the probe problem.
o You machine may fail to do a soft reboot if the packet buffer shared memory is
mapped in -- the machine might think its a boot PROM (since it intentionally
shares the same memory space as the optional on-board boot PROM).
hp.c
o This has only been tested with a 27245A, 27247A, and 27250. It doesn't yet
work with the new 27247B!
o Thanks to Chance Reschke at USRA <creschke@usra.edu> for loaning me the
27247A ethercard.
BUGS
The "bogus packet header" is a bug in the 8390. I drop all of
the packets in the ring (usually just one) when this happens.
If you get this more than just occasionally please report it to me.
I don't yet distinguish between the problems with IRQ conflicts and
problems with the ethernet cable not being connected. If you get
a "transmit timed out" message check both! Your ethercard
probably will NOT work without proper termination.
A small number of people report continuous "RX transfer address mismatch"
errors with the NE2000. This is a bug in the 8390, and the reason
most designers use shared memory or design their own packet buffer access
circuitry rather than use what's provided by the 8390. An occasional
"mismatch" message (say, once a week) won't impact performance.
I've fixed most of the spurious "receiver overrun" messages. It
was a bug in the 8390 -- the overrun flag is sometimes set
when the boundary pointer is written. If you still get
isolated overrun errors please send me email.
The 3c501 driver isn't complete. This card is severely brain-damaged
anyway -- you won't notice the performance difference between working
and non-working versions anyway. If this source code is here, it is
only provided for a few people that wanted to write code for their 3c501
cards. Don't send me email about it unless it's a bug fix.

134
net/tcpip/newether/Space.c Normal file
View File

@@ -0,0 +1,134 @@
/* Space.c */
/* Holds initial configuration information for devices. */
#include "dev.h"
#include <linux/stddef.h>
#include <linux/config.h>
#define NEXT_DEV NULL
#ifdef ETHERLINK1
extern int etherlink_init(struct device *);
#ifndef ETHERLINK1_IRQ
#define ETHERLINK1_IRQ 9
#endif
static struct device el_dev = {
"if3c501",
0, 0, 0, 0, /* memory rx_end, rx_start, end, start are autoconfiged. */
ETHERLINK1, ETHERLINK1_IRQ, /* base i/o address, irq. */
0,0,0,0,0,
NEXT_DEV,
etherlink_init, 0, {NULL}, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, {0,}, {0,}, 0
};
#undef NEXT_DEV
#define NEXT_DEV (&el_dev)
#endif /* ETHERLINK1 */
#if defined(EI8390) || defined(EL2) || defined(NE2000) \
|| defined(WD80x3) || defined(HPLAN)
extern int ethif_init(struct device *);
#ifndef EI8390_IRQ
#define EI8390_IRQ 0
#endif
static struct device ei8390_dev = {
#ifdef EI_NAME
EI_NAME,
#else
"eth_if",
#endif
0, 0, 0, 0, /* memory rx_end, rx_start, end, start are autoconfiged. */
EI8390, EI8390_IRQ, 0,0,0,0,0, /* base i/o address, irq, and flags. */
NEXT_DEV,
ethif_init, 0, {NULL}, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, 0, 0, 0, {0,}, {0,}, 0
};
#undef NEXT_DEV
#define NEXT_DEV (&ei8390_dev)
#endif /* The EI8390 drivers. */
#ifdef WD8003
extern int wd8003_init(struct device *);
static struct device wd8003_dev =
{
"eth0",
0xd2000, /* recv memory end. */
0xd0600, /* recv memory start. */
0xd2000, /* memory end. */
0xd0000, /* memory start. */
0x280, /* base i/o address. */
5, /* irq */
0,0,0,0,0, /* flags */
NEXT_DEV,
wd8003_init,
/* wd8003_init should set up the rest. */
0, /* trans start. */
{NULL}, /* buffs */
NULL, /* backlog */
NULL, /* open */
NULL, /* stop */
NULL, /* hard_start_xmit */
NULL, /* hard_header */
NULL, /* add arp */
NULL, /* queue xmit */
NULL, /* rebuild header */
NULL, /* type_trans */
NULL, /* send_packet */
NULL, /* private */
0, /* type. */
0, /* hard_header_len */
0, /* mtu */
{0,}, /* broadcast address */
{0,}, /* device address */
0 /* addr len */
};
#undef NEXT_DEV
#define NEXT_DEV (&wd8003_dev)
#endif /* WD8003 */
extern int loopback_init(struct device *dev);
static struct device loopback_dev =
{
"loopback",
-1, /* recv memory end. */
0x0, /* recv memory start. */
-1, /* memory end. */
0, /* memory start. */
0, /* base i/o address. */
0, /* irq */
0,0,1,0,0, /* flags */
NEXT_DEV, /* next device */
loopback_init,
/* loopback_init should set up the rest. */
0, /* trans start. */
{NULL}, /* buffs */
NULL, /* backlog */
NULL, /* open */
NULL, /* stop */
NULL, /* hard_start_xmit */
NULL, /* hard_header */
NULL, /* add arp */
NULL, /* queue xmit */
NULL, /* rebuild header */
NULL, /* type_trans */
NULL, /* send_packet */
NULL, /* private */
0, /* type. */
0, /* hard_header_len */
0, /* mtu */
{0,}, /* broadcast address */
{0,}, /* device address */
0 /* addr len */
};
#undef NEXT_DEV
#define NEXT_DEV (error no next device &loopback_dev)
struct device *dev_base = &loopback_dev;

View File

@@ -0,0 +1,100 @@
/* 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.
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 an active 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>*/
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};
void 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;*/
}
/* 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);
}
}
}
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:
*/

312
net/tcpip/newether/hp.c Normal file
View File

@@ -0,0 +1,312 @@
/* 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.28 1/28/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 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;
extern struct ei_device ei_status;
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);
/* 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;
if (dev->irq == 2)
dev->irq = 9;
/* 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, irq_list[] = { 11, 10, 5, 3, 4, 7, 9, 0};
for (irq = irq_list; *irq; irq++) {
if (irqaction(*irq, &ei_sigaction) == 0)
break;
}
if (*irq == 0) {
printk (" unable to get an IRQ.\n");
return 0;
}
dev->irq = *irq;
}
printk(" %s found, using IRQ %d.\n", 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(EI_NAME": hp_reset_8390() did not complete.\n");
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 {
/* Input the bytes with a slow 8-bit loop. Someday change this to:
port_read_b(nic_base - NIC_OFFSET + HP_DATA_PORT,buf, count); */
int i;
for(i = 0; i < count; i++) {
buf[i] = inb_p(nic_base - NIC_OFFSET + HP_DATAPORT);
}
}
/* 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(EI_NAME": RX transfer address mismatch, %#4.4x vs. %#4.4x (actual).\n",
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);
int i;
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 notdef
/* 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 {
/* Output the bytes with a slow 8-bit loop. This actually is almost
as fast as possible, but it does tie up the processor. We could
also use a 'rep' sequnce here. */
for(i = 0; i < count; i++)
outb_p(buf[i], nic_base - NIC_OFFSET + HP_DATAPORT);
}
/* 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(EI_NAME": TX Transfer address mismatch, %#4.4x vs. %#4.4x.\n",
(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;
/* default is IRQ5 0 1 2 3 4 5 6 7 8 9 10 11 */
static char irqval[16] = { 0, 0, 4, 6, 8,10, 0,14, 0, 4, 2,12,0,0,0,0};
NS8390_init(dev, 0);
outb_p(irqval[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:
*/

372
net/tcpip/newether/ne.c Normal file
View File

@@ -0,0 +1,372 @@
/* 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.27 1/27/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;
extern struct ei_device ei_status;
int neprobe(int ioaddr, struct device *dev);
static int neprobe1(int ioaddr, struct device *dev);
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);
static void ne_init_card(struct device *dev);
/* Probe for the NE1000 and NE2000. NEx000-like boards have 0x57,0x57 in
bytes 0x0e,0x0f of the SAPROM, but if we read by 16 bit words the NE1000
appears to have 0x00, 0x42. */
/* Also initialize the card and fill in STATION_ADDR with the station
address. The station address (and other data) is stored in the
packet buffer memory space, 32 bytes starting at remote DMA address 0. */
int neprobe(int ioaddr, struct device *dev)
{
int *port, ports[] = {0x300, 0x320, 0x340, 0x360, 0};
if (ioaddr > 0x100)
return neprobe1(ioaddr, dev);
for (port = &ports[0]; *port; port++)
if (inb_p(*port) != 0xff && neprobe1(*port, dev))
return dev->base_addr = *port;
return 0;
}
static int neprobe1(int ioaddr, struct device *dev)
{
int i;
unsigned char *station_addr = dev->dev_addr;
unsigned char SA_prom[32];
int cmdreg;
int ne2000 = 0, ne1000 = 0, ctron = 0;
printk("8390 ethercard probe at %#3x:", ioaddr);
cmdreg = inb_p(ioaddr);
if (cmdreg == 0xFF) {
printk(" not found (%#2.2x).\n", cmdreg);
return 0;
}
/* Ooops, we must first initialize registers -- we can't just read the PROM
address right away. (Learned the hard way.) */
/* NS8390_init(eifdev, 0);*/
outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, ioaddr);
#ifdef EI_8BIT
outb_p(0x48, ioaddr + EN0_DCFG); /* Set byte-wide for probe. */
#else
outb_p(0x49, ioaddr + EN0_DCFG); /* Set word-wide for probe. */
#endif
/* Even though we'll set them soon, we must clear them! */
outb_p(0x00, ioaddr + EN0_RCNTLO);
outb_p(0x00, ioaddr + EN0_RCNTHI);
outb_p(0x00, ioaddr + EN0_IMR); /* Mask completion irq. */
outb_p(0xFF, ioaddr + EN0_ISR);
/* Set to monitor and loopback mode. */
outb_p(E8390_RXOFF, ioaddr + EN0_RXCR); /* 0x20 */
outb_p(E8390_TXOFF, ioaddr + EN0_TXCR); /* 0x02 */
#ifdef EI_8BIT
outb_p(sizeof(SA_prom), ioaddr + EN0_RCNTLO);
#else
/* Double count 0x20 words, the SA PROM is only byte wide. */
outb_p(2*sizeof(SA_prom), ioaddr + EN0_RCNTLO);
#endif
outb_p(0x00, ioaddr + EN0_RCNTHI);
outb_p(0x00, ioaddr + EN0_RSARLO); /* DMA starting at 0x0000. */
outb_p(0x00, ioaddr + EN0_RSARHI);
outb_p(E8390_RREAD+E8390_START, ioaddr);
for(i = 0; i < sizeof(SA_prom); i++) {
SA_prom[i] = inb_p(ioaddr + NE_DATAPORT);
if (i < ETHER_ADDR_LEN && station_addr) {
printk(" %2.2x", SA_prom[i]);
station_addr[i] = SA_prom[i];
}
}
#ifdef EI_8BIT
ne1000 = (SA_prom[14] == 0x57 && SA_prom[15] == 0x57);
#else
ne2000 = (SA_prom[14] == 0x57 && SA_prom[15] == 0x57);
ne1000 = (SA_prom[14] == 0x00 && SA_prom[15] == 0x42);
ctron = (SA_prom[0] == 0x00 && SA_prom[1] == 0x00 && SA_prom[2] == 0x1d);
#endif
/* Set up the rest of the parameters. */
if (ne1000) {
ei_status.name = "NE1000";
ei_status.word16 = 0;
ei_status.tx_start_page = NE1SM_START_PG;
ei_status.rx_start_page = NE1SM_START_PG + TX_PAGES;
ei_status.stop_page = NE1SM_STOP_PG;
} else if (ne2000) {
ei_status.name = "NE2000";
ei_status.word16 = 1;
ei_status.tx_start_page = NESM_START_PG;
ei_status.rx_start_page = NESM_START_PG + TX_PAGES;
ei_status.stop_page = NESM_STOP_PG;
} else if (ctron) {
/* You'll have to set these yourself, but this info might be useful.
Cabletron packet buffer locations:
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. */
ei_status.name = "Cabletron";
ei_status.word16 = 1;
ei_status.tx_start_page = 0x01;
ei_status.rx_start_page = 0x01 + TX_PAGES;
#ifndef CTRON_MEMSIZE
#define CTRON_MEMSIZE 0x20 /* Extra safe... */
#endif
ei_status.stop_page = CTRON_MEMSIZE;
} else {
printk(" not found.\n");
return 0;
}
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(" %s found, using IRQ %d.\n", 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;
ne_init_card(dev);
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);
break;
}
}
outb_p(tmp, NE_BASE + NE_RESET);
while ((inb_p(NE_BASE+EN0_ISR) & ENISR_RESET) == 0)
if (jiffies - reset_start_time > 2) {
printk(EI_NAME": ne_reset_8390() did not complete.\n");
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 have shared memory on the board -- put the packet
out through the "remote DMA" dataport. */
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 {
/* Input the bytes with a slow 8-bit loop. Someday change this to:
port_read_b(NE_BASE + NE_DATAPORT, buf, count); */
int i;
/* Input the bytes with a slow 8-bit loop. Tune this someday. */
for(i = 0; i < count; i++) {
buf[i] = inb_p(NE_BASE + NE_DATAPORT);
}
}
/* 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(EI_NAME": RX transfer address mismatch, %#4.4x (should be) vs. %#4.4x (actual).\n",
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 i, 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(no_rw_bugfix) && 0
/* 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 /* no_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) {
/* Use the 'rep' sequence for 16 bit boards. */
port_write(NE_BASE + NE_DATAPORT, buf, count>>1);
} else {
/* Output the bytes with a slow 8-bit loop. Someday change this to:
port_write_b(NE_BASE + NE_DATAPORT, buf, count); */
/* Output the bytes with a slow 8-bit loop. This actually is almost
as fast as possible, but it does tie up the processor. We could
also use a 'rep' sequnce here. */
for(i = 0; i < count; i++)
outb_p(buf[i], NE_BASE + NE_DATAPORT);
}
/* 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(EI_NAME": Packet buffer transfer address mismatch on TX, %#4.4x vs. %#4.4x.\n",
(start_page << 8) + count, addr);
if (retries++ == 0)
goto retry;
}
return;
}
/* This function resets the ethercard if something screws up. */
static void
ne_init_card(struct device *dev)
{
NS8390_init(dev, 0);
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:
*/

352
net/tcpip/newether/wd.c Normal file
View File

@@ -0,0 +1,352 @@
/* 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.28a 1/28/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;
extern struct ei_device ei_status;
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);
/* The stop page doesn't use the whole packet buffer RAM for the
16 bit versions. This could be changed for the final version. */
#define WD_START_PG 0x00 /* First page of TX buffer */
#define WD_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_p(*port) != 0xff && wdprobe1(*port, dev))
return dev->base_addr;
return 0;
}
int wdprobe1(int ioaddr, struct device *dev)
{
int i;
unsigned char *station_addr = dev->dev_addr;
int checksum = 0;
int bits16 = 0;
#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++) {
int tmp;
if( inb_p(ioaddr+8+i) != inb_p(ioaddr+i) ){
tmp = inb_p(ioaddr+1); /* fiddle with 16bit bit */
outb( tmp ^ 0x01, ioaddr+1 ); /* attempt to clear 16bit bit */
if ((tmp & 0x01) == (inb_p( ioaddr+1) & 0x01)) {
int asic_reg5 = inb(ioaddr+WD_CMDREG5);
bits16 = 1; /* use word mode of operation */
/* 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;
break; /* We have a 16bit board here! */
}
outb(tmp, ioaddr+1);
}
}
#else
bits8 = 1;
#endif /* FORCE_8BIT */
#ifndef final_version
if ((inb(ioaddr+1) & 0x01) != (ei_status.word16 & 0x01))
printk("\nWD80x3: Bus width conflict, %d (probe) != %d (reg report).\n",
ei_status.word16 ? 16:8, (inb(ioaddr+1) & 0x01) ? 16 : 8);
#endif
ei_status.tx_start_page = WD_START_PG;
ei_status.rx_start_page = WD_START_PG + TX_PAGES;
ei_status.stop_page = WD_STOP_PG;
#if defined(WD_SHMEM) && WD_SHMEM > 0x80000
/* Allow an override for alpha testing. */
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);
printk(" address %#x,", dev->mem_start);
}
#endif
dev->rmem_start = dev->mem_start + TX_PAGES*256;
dev->mem_end = dev->rmem_end
= dev->mem_start + (WD_STOP_PG - WD_START_PG)*256;
#if defined(EI_DEBUG) && EI_DEBUG > 3
memset((void*)dev->mem_start, 0x42424242, (WD_STOP_PG - WD_START_PG)*256);
#endif
/* The 8390 isn't at the base address -- the ASIC regs are there! */
dev->base_addr = ioaddr+WD_NIC_OFFSET;
if (dev->irq < 2) {
int nic_base = dev->base_addr;
outb(WD_RESET, ioaddr);
autoirq_setup(1);
outb_p((((dev->mem_start>>13) & 0x3f)|WD_MEMENB), ioaddr); /* WD_CMDREG */
if (ei_status.word16)
outb_p( ISA16 | NIC16 | ((dev->mem_start>>19) & 0x1f), ioaddr+WD_CMDREG5);
/* This doesn't reliably generate an interrupt! */
outb_p(0xff, nic_base + EN0_ISR); /* Ack. all intrs. */
outb_p(0x50, nic_base + EN0_IMR); /* Enable "DMA complete" 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(E8390_RWRITE+E8390_START, nic_base); /* Trigger it again... */
dev->irq = autoirq_report(1);
outb_p(0x00, nic_base + EN0_IMR); /* Mask it again. */
if (ei_debug > 2)
printk(" autoIRQ %d", dev->irq);
if (dev->irq == 0) {
printk(" autoIRQ failed, isr=%02x", inb(nic_base + EN0_ISR));
dev->irq = 10;
}
} 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(" %s found, using IRQ %d.\n", ei_status.name, dev->irq);
if (ei_debug > 1)
printk(version);
if (ei_debug > 1)
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_p((((dev->mem_start>>13) & 0x3f)|WD_MEMENB), ioaddr); /* WD_CMDREG */
if (ei_status.word16)
outb_p( 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_p(dev->base_addr+EN0_ISR) & ENISR_RESET) == 0)
if (jiffies - reset_start_time > 2) {
printk(EI_NAME": wd_reset_8390() did not complete.\n");
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_p((((dev->mem_start>>13) & 0x3f)|WD_MEMENB), wd_cmd_port); /* WD_CMDREG */
if (ei_status.word16)
outb_p( 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 */
/* Map in the shared memory. */
outb_p((((dev->mem_start>>13) & 0x3f)|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("wd8013: wd_block_input(cnt=%d offset=%3x addr=%#x) = %2x %2x %2x...\n",
count, ring_offset, xfer_start, board[-1], board[0], board[1]);
}
#ifdef mapout
outb(0, 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 */
/* Map in the shared memory. */
outb_p((((dev->mem_start>>13) & 0x3f)|WD_MEMENB), wd_cmdreg);
#endif
memcpy(shmem, buf, count);
if (ei_debug > 4)
printk("wd8013: wd_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(0, 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", ei_status.name);
NS8390_init(dev, 0);
/* Turn off the shared memory. */
outb_p((((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:
*/