add directory net
This commit is contained in:
340
net/tcpip/newether/3c503.c
Normal file
340
net/tcpip/newether/3c503.c
Normal 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:
|
||||
*/
|
||||
59
net/tcpip/newether/3c503reg.h
Normal file
59
net/tcpip/newether/3c503reg.h
Normal 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
712
net/tcpip/newether/8390.c
Normal 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
129
net/tcpip/newether/8390.h
Normal 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 */
|
||||
27
net/tcpip/newether/GNUmakefile
Normal file
27
net/tcpip/newether/GNUmakefile
Normal 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 $@
|
||||
77
net/tcpip/newether/INSTALL
Normal file
77
net/tcpip/newether/INSTALL
Normal 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
80
net/tcpip/newether/README
Normal 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
134
net/tcpip/newether/Space.c
Normal 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;
|
||||
100
net/tcpip/newether/auto_irq.c
Normal file
100
net/tcpip/newether/auto_irq.c
Normal 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
312
net/tcpip/newether/hp.c
Normal 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
372
net/tcpip/newether/ne.c
Normal 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
352
net/tcpip/newether/wd.c
Normal 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:
|
||||
*/
|
||||
Reference in New Issue
Block a user