add directory net
This commit is contained in:
377
net/tcpip/newether-pingpong/3c503.c
Normal file
377
net/tcpip/newether-pingpong/3c503.c
Normal file
@@ -0,0 +1,377 @@
|
||||
/* 3c503.c: A shared-memory NS8390 ethernet driver for linux. */
|
||||
/*
|
||||
Written 1992,1993 by Donald Becker. This is alpha test code.
|
||||
This is a extension to the Linux operating system, and is covered by
|
||||
same Gnu Public License that covers that work.
|
||||
|
||||
This driver should work with the 3c503 and 3c503/16. It must be used
|
||||
in shared memory mode.
|
||||
|
||||
The Author may be reached as becker@super.org or
|
||||
C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
|
||||
*/
|
||||
|
||||
static char *version = "3c503.c:v0.67 3/8/93 Donald Becker (becker@super.org)\n";
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/errno.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/system.h>
|
||||
|
||||
#include "dev.h"
|
||||
|
||||
#include "8390.h"
|
||||
#include "3c503reg.h"
|
||||
|
||||
extern void NS8390_init(struct device *dev, int startp);
|
||||
extern int ei_debug;
|
||||
extern struct sigaction ei_sigaction;
|
||||
|
||||
int el2autoprobe(int ioaddr, struct device *dev);
|
||||
int el2probe(int ioaddr, struct device *dev);
|
||||
|
||||
static void el2_reset_8390(struct device *dev);
|
||||
static void el2_init_card(struct device *dev);
|
||||
static void el2_block_output(struct device *dev, int count,
|
||||
const unsigned char *buf, const start_page);
|
||||
static int el2_block_input(struct device *dev, int count, char *buf,
|
||||
int ring_offset);
|
||||
|
||||
|
||||
int
|
||||
el2autoprobe(int ioaddr, struct device *dev)
|
||||
{
|
||||
int *addr, addrs[] = { 0xddffe, 0xd9ffe, 0xcdffe, 0xc9ffe, 0};
|
||||
int ports[] = {0x300,0x310,0x330,0x350,0x250,0x280,0x2a0,0x2e0,0};
|
||||
|
||||
/* Non-autoprobe case first: */
|
||||
if (ioaddr > 0)
|
||||
return el2probe(ioaddr, dev);
|
||||
|
||||
/* We check for a memory-mapped 3c503 board by looking at the
|
||||
port location bitmap at the end of the jumpered boot PROM space.
|
||||
This works even if a PROM isn't there. */
|
||||
for (addr = addrs; *addr; addr++) {
|
||||
int i;
|
||||
unsigned int base_bits = *(unsigned char *)*addr;
|
||||
/* Find first set bit. */
|
||||
for(i = 7; i >= 0; i--, base_bits >>= 1)
|
||||
if (base_bits & 0x1)
|
||||
break;
|
||||
if (base_bits == 1 && el2probe(ports[i], dev))
|
||||
return dev->base_addr;
|
||||
}
|
||||
#ifndef ignore_nonshared_memory
|
||||
/* It's not memory mapped, bummer. Try all of the locations
|
||||
that aren't obviously empty. */
|
||||
{ int i;
|
||||
for (i = 0; i < 8; i++)
|
||||
if (inb_p(ports[i] + 0x403) == (0x80 >> i) /* Preliminary check */
|
||||
&& el2probe(ports[i], dev))
|
||||
return dev->base_addr;
|
||||
}
|
||||
#endif /* probe_nonshared_memory */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Probe for the Etherlink II card at I/O port base IOADDR,
|
||||
returning non-zero on sucess. If found, set the station
|
||||
address and memory parameters in DEVICE. */
|
||||
int
|
||||
el2probe(int ioaddr, struct device *dev)
|
||||
{
|
||||
int i, iobase_reg, membase_reg, saved_406;
|
||||
unsigned char *station_addr = dev->dev_addr;
|
||||
|
||||
/* We verify that it's a 3C503 board by checking the first three octets
|
||||
of its ethernet address. */
|
||||
printk("3c503 probe at %#3x:", ioaddr);
|
||||
iobase_reg = inb(ioaddr+0x403);
|
||||
membase_reg = inb(ioaddr+0x404);
|
||||
/* Verify ASIC register that should be 0 or have a single bit set. */
|
||||
if ( (iobase_reg & (iobase_reg - 1))
|
||||
|| (membase_reg & (membase_reg - 1))) {
|
||||
printk(" not found.\n");
|
||||
return 0;
|
||||
}
|
||||
saved_406 = inb_p(ioaddr + 0x406);
|
||||
outb_p(ECNTRL_RESET|ECNTRL_THIN, ioaddr + 0x406); /* Reset it... */
|
||||
outb_p(ECNTRL_THIN, ioaddr + 0x406);
|
||||
/* Map the station addr PROM into the lower I/O ports. */
|
||||
outb(ECNTRL_SAPROM|ECNTRL_THIN, ioaddr + 0x406);
|
||||
for (i = 0; i < ETHER_ADDR_LEN; i++) {
|
||||
printk(" %2.2X", (station_addr[i] = inb(ioaddr + i)));
|
||||
}
|
||||
if ( station_addr[0] != 0x02
|
||||
|| station_addr[1] != 0x60
|
||||
|| station_addr[2] != 0x8c) {
|
||||
printk(" 3C503 not found.\n");
|
||||
/* Restore the register we frobbed. */
|
||||
outb_p(saved_406, ioaddr + 0x406);
|
||||
return 0;
|
||||
}
|
||||
/* Map the 8390 back into the window. */
|
||||
outb(ECNTRL_THIN, ioaddr + 0x406);
|
||||
dev->base_addr = ioaddr;
|
||||
/* Probe for, turn on and clear the board's shared memory. */
|
||||
if (ei_debug > 2) printk(" memory jumpers %2.2x ", membase_reg);
|
||||
outb(EGACFR_NORM, ioaddr + 0x405); /* Enable RAM */
|
||||
if ((membase_reg & 0xf0) == 0) {
|
||||
dev->mem_start = 0;
|
||||
} else {
|
||||
dev->mem_start = ((membase_reg & 0xc0) ? 0xD8000 : 0xC8000) +
|
||||
((membase_reg & 0xA0) ? 0x4000 : 0);
|
||||
|
||||
#define EL2_MEMSIZE (EL2SM_STOP_PG - EL2SM_START_PG)*256
|
||||
#ifdef EL2MEMTEST
|
||||
/* This has never found an error, but someone might care. */
|
||||
{ /* Check the card's memory. */
|
||||
int *mem_base = (int *)dev->mem_start;
|
||||
int memtest_value = 0xbbadf00d;
|
||||
mem_base[0] = 0xba5eba5e;
|
||||
for (i = 1; i < EL2_MEMSIZE/sizeof(mem_base[0]); i++) {
|
||||
mem_base[i] = memtest_value;
|
||||
if (mem_base[0] != 0xba5eba5e
|
||||
|| mem_base[i] != memtest_value) {
|
||||
printk(" memory failure or memory address conflict.\n");
|
||||
dev->mem_start = 0;
|
||||
break;
|
||||
}
|
||||
memtest_value += 0x55555555;
|
||||
mem_base[i] = 0;
|
||||
}
|
||||
}
|
||||
#endif /* EL2MEMTEST */
|
||||
/* Divide the on-board memory into a single maximum-sized transmit
|
||||
(double-sized for ping-pong transmit) buffer at the base, and
|
||||
use the rest as a receive ring. */
|
||||
dev->mem_end = dev->rmem_end = dev->mem_start + EL2_MEMSIZE;
|
||||
dev->rmem_start = TX_PAGES*256 + dev->mem_start;
|
||||
}
|
||||
if (ei_debug > 2)
|
||||
printk("\n3c503: memory params start=%#5x rstart=%#5x end=%#5x rend=%#5x.\n",
|
||||
dev->mem_start, dev->rmem_start, dev->mem_end, dev->rmem_end);
|
||||
|
||||
/* Finish setting the board's parameters. */
|
||||
ei_status.name = "3C503";
|
||||
ei_status.tx_start_page = EL2SM_START_PG;
|
||||
ei_status.rx_start_page = EL2SM_START_PG + TX_PAGES;
|
||||
ei_status.stop_page = EL2SM_STOP_PG;
|
||||
ei_status.reset_8390 = &el2_reset_8390;
|
||||
ei_status.block_input = &el2_block_input;
|
||||
ei_status.block_output = &el2_block_output;
|
||||
/* This should be probed for (or set via an ioctl()) at run-time someday. */
|
||||
#if defined(EI8390_THICK) || defined(EL2_AUI)
|
||||
ei_status.thin_bit = 0;
|
||||
#else
|
||||
ei_status.thin_bit = ECNTRL_THIN;
|
||||
#endif
|
||||
|
||||
if (dev->irq < 2) {
|
||||
int irqlist[] = {5, 9, 3, 4, 0};
|
||||
int *irqp = irqlist;
|
||||
do {
|
||||
if (request_irq (dev->irq = *irqp, NULL) != -EBUSY) {
|
||||
/* Twinkle the interrupt, and check if it's seen. */
|
||||
autoirq_setup(0);
|
||||
outb_p((0x04 << (dev->irq == 9 ? 2 : dev->irq)), E33G_IDCFR);
|
||||
outb_p(0x00, E33G_IDCFR);
|
||||
if (dev->irq == autoirq_report(0) /* It's a good IRQ line! */
|
||||
&& request_irq (dev->irq, &ei_interrupt) == 0) {
|
||||
printk(" got IRQ %d", dev->irq);
|
||||
break;
|
||||
} else
|
||||
printk(" IRQ%d bad..", dev->irq);
|
||||
}
|
||||
} while (*++irqp);
|
||||
if (*irqp == 0) {
|
||||
printk(" unable to find an free IRQ line.\n");
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
if (dev->irq == 2)
|
||||
dev->irq = 9;
|
||||
else if (dev->irq > 5 && dev->irq != 9) {
|
||||
printk("\n3c503: configured interrupt number %d out of range.\n",
|
||||
dev->irq);
|
||||
return 0;
|
||||
}
|
||||
if (request_irq(dev->irq, &ei_interrupt)) {
|
||||
printk (" unable to get IRQ%d.\n", dev->irq);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
dev->start = 0;
|
||||
el2_init_card(dev);
|
||||
|
||||
if (dev->mem_start)
|
||||
printk("\n%s: %s using IRQ %d with shared memory at %#6x-%#6x,\n",
|
||||
dev->name, ei_status.name, dev->irq,
|
||||
dev->mem_start, dev->mem_end-1);
|
||||
else
|
||||
printk("\n%s: %s using IRQ %d with programmed I/O.\n",
|
||||
dev->name, ei_status.name, dev->irq);
|
||||
if (ei_debug > 1)
|
||||
printk(version);
|
||||
|
||||
return ioaddr;
|
||||
}
|
||||
|
||||
/* This is called whenever we have a unrecoverable failure:
|
||||
transmit timeout
|
||||
Bad ring buffer packet header
|
||||
*/
|
||||
static void
|
||||
el2_reset_8390(struct device *dev)
|
||||
{
|
||||
if (ei_debug > 1) {
|
||||
printk("%s: Resetting the 3c503 board...", dev->name);
|
||||
printk("%#x=%#02x %#x=%#02x %#x=%#02x...", E33G_IDCFR, inb(E33G_IDCFR),
|
||||
E33G_CNTRL, inb(E33G_CNTRL), E33G_GACFR, inb(E33G_GACFR));
|
||||
}
|
||||
|
||||
outb_p(ECNTRL_RESET|ECNTRL_THIN, E33G_CNTRL);
|
||||
ei_status.txing = 0;
|
||||
outb_p(ei_status.thin_bit, E33G_CNTRL);
|
||||
el2_init_card(dev);
|
||||
if (ei_debug > 1) printk("done\n");
|
||||
}
|
||||
|
||||
/* Initialize the 3c503 GA registers after a reset. */
|
||||
static void
|
||||
el2_init_card(struct device *dev)
|
||||
{
|
||||
/* Unmap the station PROM and select the DIX or BNC connector. */
|
||||
outb_p(ei_status.thin_bit, E33G_CNTRL);
|
||||
|
||||
/* Set ASIC copy of rx's first and last+1 buffer pages */
|
||||
/* These must be the same as in the 8390. */
|
||||
outb(ei_status.rx_start_page, E33G_STARTPG);
|
||||
outb(ei_status.stop_page, E33G_STOPPG);
|
||||
|
||||
/* Point the vector pointer registers somewhere ?harmless?. */
|
||||
outb(0xff, E33G_VP2); /* Point at the ROM restart location 0xffff0 */
|
||||
outb(0xff, E33G_VP1);
|
||||
outb(0x00, E33G_VP0);
|
||||
/* Turn off all interrupts until we're opened. */
|
||||
outb_p(0x00, dev->base_addr + EN0_IMR);
|
||||
/* Enable IRQs iff started. */
|
||||
outb(EGACFR_NORM, E33G_GACFR);
|
||||
|
||||
/* Set the interrupt line. */
|
||||
outb_p((0x04 << (dev->irq == 9 ? 2 : dev->irq)), E33G_IDCFR);
|
||||
outb_p(8, E33G_DRQCNT); /* Set burst size to 8 */
|
||||
outb_p(0x20, E33G_DMAAH); /* Put a valid addr in the GA DMA */
|
||||
outb_p(0x00, E33G_DMAAL);
|
||||
return; /* We always succeed */
|
||||
}
|
||||
|
||||
/* Either use the shared memory (if enabled on the board) or put the packet
|
||||
out through the ASIC FIFO. The latter is probably much slower. */
|
||||
static void
|
||||
el2_block_output(struct device *dev, int count,
|
||||
const unsigned char *buf, const start_page)
|
||||
{
|
||||
int i; /* Buffer index */
|
||||
int boguscount = 0; /* timeout counter */
|
||||
|
||||
/* This should really be set with during an open(). */
|
||||
outb(EGACFR_NORM, E33G_GACFR); /* Enable RAM and interrupts. */
|
||||
|
||||
if (dev->mem_start) { /* Shared memory transfer */
|
||||
void *dest_addr = (void *)(dev->mem_start +
|
||||
((start_page - ei_status.tx_start_page) << 8));
|
||||
memcpy(dest_addr, buf, count);
|
||||
if (ei_debug > 2 && memcmp(dest_addr, buf, count))
|
||||
printk("%s: 3c503 send_packet() bad memory copy @ %#5x.\n",
|
||||
dev->name, dest_addr);
|
||||
else if (ei_debug > 4)
|
||||
printk("%s: 3c503 send_packet() good memory copy @ %#5x.\n",
|
||||
dev->name, dest_addr);
|
||||
return;
|
||||
}
|
||||
/* No shared memory, put the packet out the slow way. */
|
||||
/* Set up then start the internal memory transfer to Tx Start Page */
|
||||
outb(0x00, E33G_DMAAL);
|
||||
outb_p(start_page, E33G_DMAAH);
|
||||
outb_p(ei_status.thin_bit | ECNTRL_OUTPUT | ECNTRL_START, E33G_CNTRL);
|
||||
|
||||
/* This is the byte copy loop: it should probably be tuned for
|
||||
for speed once everything is working. I think it is possible
|
||||
to output 8 bytes between each check of the status bit. */
|
||||
for(i = 0; i < count; i++) {
|
||||
if (i % 8 == 0)
|
||||
while ((inb(E33G_STATUS) & ESTAT_DPRDY) == 0)
|
||||
if (++boguscount > (i<<3) + 32) {
|
||||
printk("%s: FIFO blocked in el2_block_output (at %d of %d, bc=%d).\n",
|
||||
dev->name, i, count, boguscount);
|
||||
return;
|
||||
}
|
||||
outb(buf[i], E33G_FIFOH);
|
||||
}
|
||||
outb(ei_status.thin_bit, E33G_CNTRL);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Returns the new ring pointer. */
|
||||
static int
|
||||
el2_block_input(struct device *dev, int count, char *buf, int ring_offset)
|
||||
{
|
||||
int boguscount = 0;
|
||||
int end_of_ring = dev->rmem_end;
|
||||
unsigned int i;
|
||||
|
||||
/* Maybe enable shared memory just be to be safe... nahh.*/
|
||||
if (dev->mem_start) { /* Use the shared memory. */
|
||||
ring_offset -= (EL2SM_START_PG<<8);
|
||||
if (dev->mem_start + ring_offset + count > end_of_ring) {
|
||||
/* We must wrap the input move. */
|
||||
int semi_count = end_of_ring - (dev->mem_start + ring_offset);
|
||||
if (ei_debug > 4)
|
||||
printk("%s: 3c503 block_input() @ %#5x+%x=%5x.\n",
|
||||
dev->name, dev->mem_start, ring_offset,
|
||||
(char *)dev->mem_start + ring_offset);
|
||||
memcpy(buf, (char *)dev->mem_start + ring_offset, semi_count);
|
||||
count -= semi_count;
|
||||
memcpy(buf + semi_count, (char *)dev->rmem_start, count);
|
||||
return dev->rmem_start + count;
|
||||
}
|
||||
if (ei_debug > 4)
|
||||
printk("%s: 3c503 block_input() @ %#5x+%x=%5x.\n",
|
||||
dev->name, dev->mem_start, ring_offset,
|
||||
(char *)dev->mem_start + ring_offset);
|
||||
memcpy(buf, (char *)dev->mem_start + ring_offset, count);
|
||||
return ring_offset + count;
|
||||
}
|
||||
/* No shared memory, use programmed I/O. */
|
||||
outb(ring_offset & 0xff, E33G_DMAAL);
|
||||
outb_p((ring_offset >> 8) & 0xff, E33G_DMAAH);
|
||||
outb_p(ei_status.thin_bit | ECNTRL_INPUT | ECNTRL_START, E33G_CNTRL);
|
||||
|
||||
/* This is the byte copy loop: it should probably be tuned for
|
||||
for speed once everything is working. */
|
||||
for(i = 0; i < count; i++) {
|
||||
if (i % 8 == 0)
|
||||
while ((inb(E33G_STATUS) & ESTAT_DPRDY) == 0)
|
||||
if (++boguscount > (i<<3) + 32) {
|
||||
printk("%s: FIFO blocked in el2_block_input() (at %d of %d, bc=%d).\n",
|
||||
dev->name, i, count, boguscount);
|
||||
boguscount = 0;
|
||||
break;
|
||||
}
|
||||
buf[i] = inb_p(E33G_FIFOH);
|
||||
}
|
||||
outb(ei_status.thin_bit, E33G_CNTRL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -I/usr/src/linux/net/tcp -c 3c503.c"
|
||||
* version-control: t
|
||||
* kept-new-versions: 5
|
||||
* End:
|
||||
*/
|
||||
59
net/tcpip/newether-pingpong/3c503reg.h
Normal file
59
net/tcpip/newether-pingpong/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 */
|
||||
689
net/tcpip/newether-pingpong/8390.c
Normal file
689
net/tcpip/newether-pingpong/8390.c
Normal file
@@ -0,0 +1,689 @@
|
||||
/* 8390.c: A general NS8390 ethernet driver core for linux. */
|
||||
/*
|
||||
Written 1992,1993 by Donald Becker. This is alpha test code.
|
||||
This is a extension to the Linux operating system, and is covered by
|
||||
same Gnu Public License that covers that work.
|
||||
|
||||
This driver should work with many 8390-based ethernet adaptors.
|
||||
|
||||
The Author may be reached as becker@super.org or
|
||||
C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
|
||||
*/
|
||||
|
||||
static char *version =
|
||||
"8390.c:v0.68 3/9/93 for 0.99.6 Donald Becker (becker@super.org)\n";
|
||||
#include <linux/config.h>
|
||||
#if !defined(EL2) && !defined(NE2000) && !defined(WD80x3) && !defined(HPLAN)
|
||||
/* They don't know what they want -- give it all to them! */
|
||||
#define EL2
|
||||
#define NE2000
|
||||
#define WD80x3
|
||||
#define HPLAN
|
||||
#endif
|
||||
|
||||
/*
|
||||
Braindamage remaining:
|
||||
|
||||
Ethernet devices should use a chr_drv device interface, with ioctl()s to
|
||||
configure the card, bring the interface up or down, allow access to
|
||||
statistics, and maybe read() and write() access to raw packets.
|
||||
This won't be done until after Linux 1.00.
|
||||
|
||||
This driver should support multiple, diverse boards simultaneousely.
|
||||
This won't be done until after Linux 1.00.
|
||||
|
||||
Sources:
|
||||
The National Semiconductor LAN Databook, and the 3Com 3c503 databook.
|
||||
The NE* programming info came from the Crynwr packet driver, and figuring
|
||||
out that the those boards are similar to the NatSemi evaluation board
|
||||
described in AN-729. Thanks NS, no thanks to Novell/Eagle.
|
||||
Cabletron provided only info I had already gotten from other sources -- hiss.
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/string.h>
|
||||
#include <asm/system.h>
|
||||
#include <asm/segment.h>
|
||||
#include <asm/io.h>
|
||||
#include <errno.h>
|
||||
#include <linux/fcntl.h>
|
||||
#include <netinet/in.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#include "dev.h"
|
||||
#include "eth.h"
|
||||
#include "timer.h"
|
||||
#include "ip.h"
|
||||
#include "tcp.h"
|
||||
#include "sock.h"
|
||||
#include "arp.h"
|
||||
|
||||
#include "8390.h"
|
||||
|
||||
#define ei_reset_8390 (ei_local->reset_8390)
|
||||
#define ei_block_output (ei_local->block_output)
|
||||
#define ei_block_input (ei_local->block_input)
|
||||
|
||||
/* use 0 for production, 1 for verification, >2 for debug */
|
||||
#ifdef EI_DEBUG
|
||||
int ei_debug = EI_DEBUG;
|
||||
#else
|
||||
int ei_debug = 2;
|
||||
#endif
|
||||
|
||||
struct device *irq2dev_map[16] = {0,0,0, /* zeroed...*/};
|
||||
|
||||
#ifdef PINGPONG
|
||||
static int lasttx = 0;
|
||||
#endif
|
||||
|
||||
/* Max number of packets received at one Intr. */
|
||||
/*static int high_water_mark = 0;*/
|
||||
|
||||
/* Index to functions. */
|
||||
/* Put in the device structure. */
|
||||
static int ei_open(struct device *dev);
|
||||
/* Dispatch from interrupts. */
|
||||
void ei_interrupt(int reg_ptr);
|
||||
static void ei_tx_intr(struct device *dev);
|
||||
static void ei_receive(struct device *dev);
|
||||
static void ei_rx_overrun(struct device *dev);
|
||||
|
||||
int ethdev_init(struct device *dev);
|
||||
/* Routines generic to NS8390-based boards. */
|
||||
void NS8390_init(struct device *dev, int startp);
|
||||
static void NS8390_trigger_send(struct device *dev, unsigned int length,
|
||||
int start_page);
|
||||
|
||||
extern int el2autoprobe(int ioaddr, struct device *dev);
|
||||
extern int el2probe(int ioaddr, struct device *dev);
|
||||
extern int neprobe(int ioaddr, struct device *dev);
|
||||
extern int wdprobe(int ioaddr, struct device *dev);
|
||||
extern int hpprobe(int ioaddr, struct device *dev);
|
||||
|
||||
struct sigaction ei_sigaction = { ei_interrupt, 0, 0, NULL, };
|
||||
|
||||
/* Open/initialize the board. This routine goes all-out, setting everything
|
||||
up anew at each open, even though many of these registers should only
|
||||
need to be set once at boot.
|
||||
*/
|
||||
static int
|
||||
ei_open(struct device *dev)
|
||||
{
|
||||
struct ei_device *ei_local = dev->private;
|
||||
|
||||
if ( ! ei_local) {
|
||||
printk("%s: Opening a non-existent physical device\n", dev->name);
|
||||
return 1; /* ENXIO would be more accurate. */
|
||||
}
|
||||
|
||||
irq2dev_map[dev->irq] = dev;
|
||||
NS8390_init(dev, 1);
|
||||
ei_local->tx1 = ei_local->tx2 = 0;
|
||||
/* The old local flags... */
|
||||
ei_local->txing = 0;
|
||||
/* ... are now global. */
|
||||
dev->tbusy = 0;
|
||||
dev->interrupt = 0;
|
||||
dev->start = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ei_start_xmit(struct sk_buff *skb, struct device *dev)
|
||||
{
|
||||
int e8390_base = dev->base_addr;
|
||||
struct ei_device *ei_local = dev->private;
|
||||
|
||||
if (dev->tbusy) { /* Do timeouts, just like the 8003 driver. */
|
||||
int txsr = inb(e8390_base+EN0_TSR), isr;
|
||||
int tickssofar = jiffies - dev->trans_start;
|
||||
if (tickssofar < 5 || (tickssofar < 15 && ! (txsr & ENTSR_PTX))) {
|
||||
return 1;
|
||||
}
|
||||
isr = inb(e8390_base+EN0_ISR);
|
||||
printk("%s: transmit timed out, TX status %#2x, ISR %#2x.\n",
|
||||
dev->name, txsr, isr);
|
||||
/* It's possible to check for an IRQ conflict here.
|
||||
I may have to do that someday. */
|
||||
if (isr)
|
||||
printk("%s: Possible IRQ conflict on IRQ%d?", dev->name, dev->irq);
|
||||
else
|
||||
printk("%s: Possible network cable problem?\n", dev->name);
|
||||
/* It futile, but try to restart it anyway. */
|
||||
ei_reset_8390(dev);
|
||||
NS8390_init(dev, 1);
|
||||
printk("\n");
|
||||
}
|
||||
|
||||
/* This is new: it means some higher layer thinks we've missed an
|
||||
tx-done interrupt. Caution: dev_tint() handles the cli()/sti()
|
||||
itself. */
|
||||
if (skb == NULL) {
|
||||
dev_tint(dev);
|
||||
return 0;
|
||||
}
|
||||
/* Fill in the ethernet header. */
|
||||
if (!skb->arp && dev->rebuild_header(skb+1, dev)) {
|
||||
skb->dev = dev;
|
||||
arp_queue (skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef PINGPONG
|
||||
{
|
||||
int length = skb->len;
|
||||
int output_page;
|
||||
int send_length = ETHER_MIN_LEN < length ? length : ETHER_MIN_LEN;
|
||||
if (length <= 0)
|
||||
return 0;
|
||||
/* Turn off interrupts so that we can put the packet out safely. */
|
||||
outb(0x00, e8390_base + EN0_IMR);
|
||||
if (ei_local->tx1 == 0) {
|
||||
output_page = ei_local->tx_start_page;
|
||||
ei_local->tx1 = send_length;
|
||||
if (ei_debug && ei_local->tx2 > 0)
|
||||
printk("%s: idle transmitter tx2=%d, lasttx=%d, txing=%d.\n",
|
||||
dev->name, ei_local->tx2, lasttx, ei_local->txing);
|
||||
} else if (ei_local->tx2 == 0) {
|
||||
output_page = ei_local->tx_start_page + 6;
|
||||
ei_local->tx2 = send_length;
|
||||
if (ei_debug && ei_local->tx1 > 0)
|
||||
printk("%s: idle transmitter, tx1=%d, lasttx=%d, txing=%d.\n",
|
||||
dev->name, ei_local->tx1, lasttx, ei_local->txing);
|
||||
} else {
|
||||
/* We can get to here if we get an rx interrupt and queued
|
||||
a tx packet just before masking 8390 irqs above. */
|
||||
if (ei_debug > 2)
|
||||
printk("%s: No packet buffer space for ping-pong use.\n",
|
||||
dev->name);
|
||||
outb_p(ENISR_ALL, e8390_base + EN0_IMR);
|
||||
return 1;
|
||||
}
|
||||
dev->trans_start = jiffies;
|
||||
ei_block_output(dev, length, (void*)(skb+1), output_page);
|
||||
if (! ei_local->txing) {
|
||||
NS8390_trigger_send(dev, send_length, output_page);
|
||||
if (output_page == ei_local->tx_start_page)
|
||||
ei_local->tx1 = -1, lasttx = -1;
|
||||
else
|
||||
ei_local->tx2 = -1, lasttx = -2;
|
||||
ei_local->txing = 1;
|
||||
} else
|
||||
ei_local->txqueue++;
|
||||
if (ei_local->tx1 && ei_local->tx2)
|
||||
dev->tbusy = 1;
|
||||
/* Turn 8390 interrupts back on. */
|
||||
outb_p(ENISR_ALL, e8390_base + EN0_IMR);
|
||||
}
|
||||
#else
|
||||
{
|
||||
int length = skb->len;
|
||||
int send_length = ETHER_MIN_LEN < length ? length : ETHER_MIN_LEN;
|
||||
if (length <= 0)
|
||||
return 0;
|
||||
dev->trans_start = jiffies;
|
||||
/* Turn off interrupts so that we can put the packet out safely. */
|
||||
outb(0x00, e8390_base + EN0_IMR);
|
||||
ei_block_output(dev, length, (void*)(skb+1), ei_local->tx_start_page);
|
||||
NS8390_trigger_send(dev, send_length, ei_local->tx_start_page);
|
||||
dev->tbusy = 1;
|
||||
/* Turn 8390 interrupts back on. */
|
||||
outb_p(ENISR_ALL, e8390_base + EN0_IMR);
|
||||
}
|
||||
#endif /* PINGPONG */
|
||||
if (skb->free)
|
||||
kfree_skb (skb, FREE_WRITE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* The typical workload of the driver:
|
||||
Handle the ether interface interrupts. */
|
||||
void
|
||||
ei_interrupt(int reg_ptr)
|
||||
{
|
||||
int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2);
|
||||
struct device *dev = irq2dev_map[irq];
|
||||
int e8390_base;
|
||||
int interrupts, boguscount = 0;
|
||||
|
||||
if (dev == NULL) {
|
||||
printk ("net_interrupt(): irq %d for unknown device.\n", irq);
|
||||
return;
|
||||
}
|
||||
dev->interrupt = 1;
|
||||
sti(); /* Allow other interrupts. */
|
||||
|
||||
e8390_base = dev->base_addr;
|
||||
|
||||
/* Change to page 0 and read the intr status reg. */
|
||||
outb_p(E8390_NODMA+E8390_PAGE0, e8390_base + E8390_CMD);
|
||||
if (ei_debug > 3)
|
||||
printk("%s: interrupt(isr=%#2.2x).\n", dev->name,
|
||||
inb_p(e8390_base + EN0_ISR));
|
||||
|
||||
/* !!Assumption!! -- we stay in page 0. Don't break this. */
|
||||
while ((interrupts = inb_p(e8390_base + EN0_ISR)) != 0
|
||||
&& ++boguscount < 20) {
|
||||
if (interrupts & ENISR_RDC) {
|
||||
/* Ack meaningless DMA complete. */
|
||||
outb_p(ENISR_RDC, e8390_base + EN0_ISR);
|
||||
}
|
||||
if (interrupts & ENISR_OVER) {
|
||||
ei_rx_overrun(dev);
|
||||
} else if (interrupts & (ENISR_RX+ENISR_RX_ERR)) {
|
||||
/* Got a good (?) packet. */
|
||||
ei_receive(dev);
|
||||
}
|
||||
/* Push the next to-transmit packet through. */
|
||||
if (interrupts & ENISR_TX) {
|
||||
ei_tx_intr(dev);
|
||||
} else if (interrupts & ENISR_COUNTERS) {
|
||||
struct ei_device *ei_local = dev->private;
|
||||
ei_local->soft_rx_errors += inb_p(e8390_base + EN0_COUNTER0);
|
||||
ei_local->soft_rx_errors += inb_p(e8390_base + EN0_COUNTER1);
|
||||
ei_local->missed_packets += inb_p(e8390_base + EN0_COUNTER2);
|
||||
outb_p(ENISR_COUNTERS, e8390_base + EN0_ISR); /* Ack intr. */
|
||||
}
|
||||
|
||||
/* Ignore the transmit errs and reset intr for now. */
|
||||
if (interrupts & ENISR_TX_ERR) {
|
||||
outb_p(ENISR_TX_ERR, e8390_base + EN0_ISR); /* Ack intr. */
|
||||
}
|
||||
outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base + E8390_CMD);
|
||||
}
|
||||
|
||||
if (interrupts && ei_debug) {
|
||||
printk("%s: unknown interrupt %#2x\n", dev->name, interrupts);
|
||||
outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base + E8390_CMD);
|
||||
outb_p(0xff, e8390_base + EN0_ISR); /* Ack. all intrs. */
|
||||
}
|
||||
dev->interrupt = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/* We have finished a transmit: check for errors and then trigger the next
|
||||
packet to be sent. */
|
||||
static void
|
||||
ei_tx_intr(struct device *dev)
|
||||
{
|
||||
int e8390_base = dev->base_addr;
|
||||
int status = inb(e8390_base + EN0_TSR);
|
||||
struct ei_device *ei_local = dev->private;
|
||||
|
||||
outb_p(ENISR_TX, e8390_base + EN0_ISR); /* Ack intr. */
|
||||
if ((status & ENTSR_PTX) == 0)
|
||||
ei_local->tx_errors++;
|
||||
else
|
||||
ei_local->tx_packets++;
|
||||
|
||||
#ifdef PINGPONG
|
||||
ei_local->txqueue--;
|
||||
if (ei_local->tx1 < 0) {
|
||||
if (lasttx != 1 && lasttx != -1)
|
||||
printk("%s: bogus last_tx_buffer %d, tx1=%d.\n",
|
||||
ei_local->name, lasttx, ei_local->tx1);
|
||||
ei_local->tx1 = 0;
|
||||
dev->tbusy = 0;
|
||||
if (ei_local->tx2 > 0) {
|
||||
NS8390_trigger_send(dev, ei_local->tx2, ei_local->tx_start_page + 6);
|
||||
ei_local->txing = 1;
|
||||
ei_local->tx2 = -1,
|
||||
lasttx = 2;
|
||||
} else
|
||||
lasttx = 20, ei_local->txing = 0;
|
||||
} else if (ei_local->tx2 < 0) {
|
||||
if (lasttx != 2 && lasttx != -2)
|
||||
printk("%s: bogus last_tx_buffer %d, tx2=%d.\n",
|
||||
ei_local->name, lasttx, ei_local->tx2);
|
||||
ei_local->tx2 = 0;
|
||||
dev->tbusy = 0;
|
||||
if (ei_local->tx1 > 0) {
|
||||
NS8390_trigger_send(dev, ei_local->tx1, ei_local->tx_start_page);
|
||||
ei_local->txing = 1;
|
||||
ei_local->tx1 = -1;
|
||||
lasttx = 1;
|
||||
} else
|
||||
lasttx = 10, ei_local->txing = 0;
|
||||
} else
|
||||
printk("%s: unexpected TX-done interrupt, lasttx=%d.\n",
|
||||
dev->name, lasttx);
|
||||
#else
|
||||
ei_local->txing = 0;
|
||||
dev->tbusy = 0;
|
||||
#endif
|
||||
mark_bh (INET_BH);
|
||||
}
|
||||
|
||||
/* We have a good packet(s), get it/them out of the buffers. */
|
||||
|
||||
static void
|
||||
ei_receive(struct device *dev)
|
||||
{
|
||||
int e8390_base = dev->base_addr;
|
||||
struct ei_device *ei_local = dev->private;
|
||||
int rxing_page, this_frame, next_frame, current_offset;
|
||||
int boguscount = 0;
|
||||
struct e8390_pkt_hdr rx_frame;
|
||||
int num_rx_pages = ei_local->stop_page-ei_local->rx_start_page;
|
||||
|
||||
while (++boguscount < 10) {
|
||||
int size;
|
||||
|
||||
/* Get the rx page (incoming packet pointer). */
|
||||
outb_p(E8390_NODMA+E8390_PAGE1, e8390_base + E8390_CMD);
|
||||
rxing_page = inb_p(e8390_base + EN1_CURPAG);
|
||||
outb_p(E8390_NODMA+E8390_PAGE0, e8390_base + E8390_CMD);
|
||||
|
||||
/* Remove one frame from the ring. Boundary is alway a page behind. */
|
||||
this_frame = inb_p(e8390_base + EN0_BOUNDARY) + 1;
|
||||
if (this_frame >= ei_local->stop_page)
|
||||
this_frame = ei_local->rx_start_page;
|
||||
|
||||
/* Someday we'll omit the previous step, iff we never get this message.*/
|
||||
if (ei_debug > 0 && this_frame != ei_local->current_page)
|
||||
printk("%s: mismatched read page pointers %2x vs %2x.\n",
|
||||
dev->name, this_frame, ei_local->current_page);
|
||||
|
||||
if (this_frame == rxing_page) /* Read all the frames? */
|
||||
break; /* Done for now */
|
||||
|
||||
current_offset = this_frame << 8;
|
||||
ei_block_input(dev, sizeof(rx_frame), (void *)&rx_frame,
|
||||
current_offset);
|
||||
|
||||
size = rx_frame.count - sizeof(rx_frame);
|
||||
|
||||
next_frame = this_frame + 1 + ((size+4)>>8);
|
||||
|
||||
/* Check for bogosity warned by 3c503 book: the status byte is never
|
||||
written. This happened a lot during testing! This code should be
|
||||
cleaned up someday, and the printk()s should be PRINTK()s. */
|
||||
if ( rx_frame.next != next_frame
|
||||
&& rx_frame.next != next_frame + 1
|
||||
&& rx_frame.next != next_frame - num_rx_pages
|
||||
&& rx_frame.next != next_frame + 1 - num_rx_pages) {
|
||||
#ifndef EI_DEBUG
|
||||
ei_local->current_page = rxing_page;
|
||||
outb(ei_local->current_page-1, e8390_base+EN0_BOUNDARY);
|
||||
continue;
|
||||
#else
|
||||
static int last_rx_bogosity = -1;
|
||||
printk("%s: bogus packet header, status=%#2x nxpg=%#2x sz=%#x (at %#4x)\n",
|
||||
dev->name, rx_frame.status, rx_frame.next, rx_frame.count,
|
||||
current_offset);
|
||||
|
||||
if (rx_packets != last_rx_bogosity) {
|
||||
/* Maybe we can avoid resetting the chip... empty the packet ring. */
|
||||
ei_local->current_page = rxing_page;
|
||||
printk("%s: setting next frame to %#2x (nxt=%#2x, rx_frm.nx=%#2x rx_frm.stat=%#2x).\n",
|
||||
dev->name, ei_local->current_page, next_frame,
|
||||
rx_frame.next, rx_frame.status);
|
||||
last_rx_bogosity = rx_packets;
|
||||
outb(ei_local->current_page-1, e8390_base+EN0_BOUNDARY);
|
||||
continue;
|
||||
} else {
|
||||
/* Oh no Mr Bill! Last ditch error recovery. */
|
||||
printk("%s: recovery failed, resetting at packet #%d..",
|
||||
dev->name, rx_packets);
|
||||
sti();
|
||||
ei_reset_8390(dev);
|
||||
NS8390_init(dev, 1);
|
||||
printk("restarting.\n");
|
||||
return;
|
||||
}
|
||||
#endif /* EI8390_NOCHECK */
|
||||
}
|
||||
|
||||
if ((size < 32 || size > 1535) && ei_debug)
|
||||
printk("%s: bogus packet size, status=%#2x nxpg=%#2x size=%#x\n",
|
||||
dev->name, rx_frame.status, rx_frame.next, rx_frame.count);
|
||||
if ((rx_frame.status & 0x0F) == ENRSR_RXOK) {
|
||||
int sksize = sizeof(struct sk_buff) + size;
|
||||
struct sk_buff *skb;
|
||||
skb = kmalloc(sksize, GFP_ATOMIC);
|
||||
if (skb != NULL) {
|
||||
skb->lock = 0;
|
||||
skb->mem_len = sksize;
|
||||
skb->mem_addr = skb;
|
||||
/* 'skb+1' points to the start of sk_buff data area. */
|
||||
ei_block_input(dev, size, (void *)(skb+1),
|
||||
current_offset + sizeof(rx_frame));
|
||||
if (dev_rint((void *)skb, size, IN_SKBUFF, dev)) {
|
||||
printk("%s: receive buffers full.\n", dev->name);
|
||||
break;
|
||||
}
|
||||
} else if (ei_debug) {
|
||||
printk("%s: Couldn't allocate a sk_buff of size %d.\n",
|
||||
dev->name, sksize);
|
||||
break;
|
||||
}
|
||||
ei_local->rx_packets++;
|
||||
} else {
|
||||
if (ei_debug)
|
||||
printk("%s: bogus packet, status=%#2x nxpg=%#2x size=%d\n",
|
||||
dev->name, rx_frame.status, rx_frame.next, rx_frame.count);
|
||||
ei_local->soft_rx_err_bits |= rx_frame.status,
|
||||
ei_local->soft_rx_errors++;
|
||||
}
|
||||
next_frame = rx_frame.next;
|
||||
|
||||
/* This should never happen, it's here for debugging. */
|
||||
if (next_frame >= ei_local->stop_page) {
|
||||
printk("%s: next frame inconsistency, %#2x..", dev->name, next_frame);
|
||||
next_frame = ei_local->rx_start_page;
|
||||
}
|
||||
ei_local->current_page += 1 + ((size+4)>>8);
|
||||
ei_local->current_page = next_frame;
|
||||
outb(next_frame-1, e8390_base+EN0_BOUNDARY);
|
||||
}
|
||||
/* If any worth-while packets have been received, dev_rint()
|
||||
has done a mark_bh(INET_BH) for us and will work on them
|
||||
when we get to the bottom-half routine. */
|
||||
|
||||
/* Bug alert! Reset ENISR_OVER to avoid spurious overruns! */
|
||||
outb_p(ENISR_RX+ENISR_RX_ERR+ENISR_OVER, e8390_base+EN0_ISR);
|
||||
return;
|
||||
}
|
||||
|
||||
/* We have a receiver overrun: we have to kick the 8390 to get it started
|
||||
again.*/
|
||||
static void
|
||||
ei_rx_overrun(struct device *dev)
|
||||
{
|
||||
int e8390_base = dev->base_addr;
|
||||
int reset_start_time = jiffies;
|
||||
struct ei_device *ei_local = dev->private;
|
||||
|
||||
/* We should already be stopped and in page0. Remove after testing. */
|
||||
outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base+E8390_CMD);
|
||||
|
||||
if (ei_debug)
|
||||
printk("%s: Receiver overrun.\n", dev->name);
|
||||
ei_local->rx_overruns++;
|
||||
|
||||
/* The we.c driver does dummy = inb_p( RBCR[01] ); at this point.
|
||||
It might mean something -- magic to speed up a reset? A 8390 bug?*/
|
||||
|
||||
/* Wait for reset in case the NIC is doing a tx or rx. This could take up to
|
||||
1.5msec, but we have no way of timing something in that range. The 'jiffies'
|
||||
are just a sanity check. */
|
||||
while ((inb_p(e8390_base+EN0_ISR) & ENISR_RESET) == 0)
|
||||
if (jiffies - reset_start_time > 1) {
|
||||
printk("%s: reset did not complete at ei_rx_overrun.\n",
|
||||
dev->name);
|
||||
NS8390_init(dev, 1);
|
||||
return;
|
||||
};
|
||||
|
||||
{
|
||||
int old_rx_packets = ei_local->rx_packets;
|
||||
/* Remove packets right away. */
|
||||
ei_receive(dev);
|
||||
ei_local->rx_overrun_packets +=
|
||||
(ei_local->rx_packets - old_rx_packets);
|
||||
}
|
||||
outb_p(0xff, e8390_base+EN0_ISR);
|
||||
/* Generic 8390 insns to start up again, same as in open_8390(). */
|
||||
outb_p(E8390_NODMA + E8390_PAGE0 + E8390_START, e8390_base + E8390_CMD);
|
||||
outb_p(E8390_TXCONFIG, e8390_base + EN0_TXCR); /* xmit on. */
|
||||
}
|
||||
|
||||
int
|
||||
ethif_init(struct device *dev)
|
||||
{
|
||||
if (ei_debug > 1)
|
||||
printk(version);
|
||||
|
||||
/* The open call may be overridden by the card-specific code. */
|
||||
dev->open = &ei_open;
|
||||
|
||||
/* Make up a ei_local structure. */
|
||||
dev->private = kmalloc(sizeof(struct ei_device), GFP_KERNEL);
|
||||
memset(dev->private, 0, sizeof(struct ei_device));
|
||||
|
||||
if (1
|
||||
#ifdef WD80x3
|
||||
&& ! wdprobe(dev->base_addr, dev)
|
||||
#endif
|
||||
#ifdef EL2
|
||||
&& ! el2autoprobe(dev->base_addr, dev)
|
||||
#endif
|
||||
#ifdef NE2000
|
||||
&& ! neprobe(dev->base_addr, dev)
|
||||
#endif
|
||||
#ifdef HPLAN
|
||||
&& ! hpprobe(dev->base_addr, dev)
|
||||
#endif
|
||||
&& 1 ) {
|
||||
printk("No ethernet device found.\n");
|
||||
kfree(dev->private);
|
||||
dev->private = NULL;
|
||||
return 1; /* ENODEV or EAGAIN would be more accurate. */
|
||||
}
|
||||
|
||||
return ethdev_init(dev);
|
||||
}
|
||||
|
||||
/* Initialize the rest of the device structure. */
|
||||
int
|
||||
ethdev_init(struct device *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < DEV_NUMBUFFS; i++)
|
||||
dev->buffs[i] = NULL;
|
||||
|
||||
dev->hard_header = eth_hard_header;
|
||||
dev->add_arp = eth_add_arp;
|
||||
dev->queue_xmit = dev_queue_xmit;
|
||||
dev->rebuild_header = eth_rebuild_header;
|
||||
dev->type_trans = eth_type_trans;
|
||||
|
||||
if (dev->private == NULL) {
|
||||
dev->private = kmalloc(sizeof(struct ei_device), GFP_KERNEL);
|
||||
memset(dev->private, 0, sizeof(struct ei_device));
|
||||
}
|
||||
|
||||
dev->send_packet = 0;
|
||||
dev->hard_start_xmit = &ei_start_xmit;
|
||||
|
||||
dev->type = ETHER_TYPE;
|
||||
dev->hard_header_len = sizeof (struct enet_header);
|
||||
dev->mtu = 1500; /* eth_mtu */
|
||||
dev->addr_len = ETHER_ADDR_LEN;
|
||||
for (i = 0; i < dev->addr_len; i++) {
|
||||
dev->broadcast[i]=0xff;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* This page of functions should be 8390 generic */
|
||||
/* Follow National Semi's recommendations for initializing the "NIC". */
|
||||
void NS8390_init(struct device *dev, int startp)
|
||||
{
|
||||
int e8390_base = dev->base_addr;
|
||||
struct ei_device *ei_local = dev->private;
|
||||
int i;
|
||||
int endcfg = ei_local->word16 ? (0x48 | ENDCFG_WTS) : 0x48;
|
||||
|
||||
/* Follow National Semi's recommendations for initing the DP83902. */
|
||||
outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base); /* 0x21 */
|
||||
outb_p(endcfg, e8390_base + EN0_DCFG); /* 0x48 or 0x49 */
|
||||
/* Clear the remote byte count registers. */
|
||||
outb_p(0x00, e8390_base + EN0_RCNTLO);
|
||||
outb_p(0x00, e8390_base + EN0_RCNTHI);
|
||||
/* Set to monitor and loopback mode -- this is vital!. */
|
||||
outb_p(E8390_RXOFF, e8390_base + EN0_RXCR); /* 0x20 */
|
||||
outb_p(E8390_TXOFF, e8390_base + EN0_TXCR); /* 0x02 */
|
||||
/* Set the transmit page and receive ring. */
|
||||
outb_p(ei_local->tx_start_page, e8390_base + EN0_TPSR);
|
||||
ei_local->tx1 = ei_local->tx2 = 0;
|
||||
outb_p(ei_local->rx_start_page, e8390_base + EN0_STARTPG);
|
||||
outb_p(ei_local->stop_page-1, e8390_base + EN0_BOUNDARY); /* 3c503 says 0x3f,NS0x26*/
|
||||
ei_local->current_page = ei_local->rx_start_page; /* assert boundary+1 */
|
||||
outb_p(ei_local->stop_page, e8390_base + EN0_STOPPG);
|
||||
/* Clear the pending interrupts and mask. */
|
||||
outb_p(0xFF, e8390_base + EN0_ISR);
|
||||
outb_p(0x00, e8390_base + EN0_IMR);
|
||||
|
||||
/* Copy the station address into the DS8390 registers,
|
||||
and set the multicast hash bitmap to receive all multicasts. */
|
||||
cli();
|
||||
outb_p(E8390_NODMA + E8390_PAGE1 + E8390_STOP, e8390_base); /* 0x61 */
|
||||
for(i = 0; i < 6; i++) {
|
||||
outb_p(dev->dev_addr[i], e8390_base + EN1_PHYS + i);
|
||||
}
|
||||
for(i = 0; i < 8; i++)
|
||||
outb_p(0xff, e8390_base + EN1_MULT + i);
|
||||
|
||||
outb_p(ei_local->rx_start_page, e8390_base + EN1_CURPAG);
|
||||
outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base);
|
||||
sti();
|
||||
if (startp) {
|
||||
outb_p(0xff, e8390_base + EN0_ISR);
|
||||
outb_p(ENISR_ALL, e8390_base + EN0_IMR);
|
||||
outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base);
|
||||
outb_p(E8390_TXCONFIG, e8390_base + EN0_TXCR); /* xmit on. */
|
||||
/* 3c503 TechMan says rxconfig only after the NIC is started. */
|
||||
outb_p(E8390_RXCONFIG, e8390_base + EN0_RXCR); /* rx on, */
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* Trigger a transmit start, assuming the length is valid. */
|
||||
static void NS8390_trigger_send(struct device *dev, unsigned int length,
|
||||
int start_page)
|
||||
{
|
||||
int e8390_base = dev->base_addr;
|
||||
|
||||
ei_status.txing = 1;
|
||||
outb_p(E8390_NODMA+E8390_PAGE0, e8390_base);
|
||||
|
||||
if (inb_p(e8390_base) & E8390_TRANS) {
|
||||
printk("%s: trigger_send() called with the transmitter busy.\n",
|
||||
dev->name);
|
||||
return;
|
||||
}
|
||||
outb_p(length & 0xff, e8390_base + EN0_TCNTLO);
|
||||
outb_p(length >> 8, e8390_base + EN0_TCNTHI);
|
||||
outb_p(start_page, e8390_base + EN0_TPSR);
|
||||
outb_p(E8390_NODMA+E8390_TRANS+E8390_START, e8390_base);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -DPINGPONG -I/usr/src/linux/net/tcp -c 8390.c"
|
||||
* version-control: t
|
||||
* kept-new-versions: 5
|
||||
* End:
|
||||
*/
|
||||
148
net/tcpip/newether-pingpong/8390.h
Normal file
148
net/tcpip/newether-pingpong/8390.h
Normal file
@@ -0,0 +1,148 @@
|
||||
/* Generic NS8390 register definitions. */
|
||||
/* This file is part of Donald Becker's 8390 drivers.
|
||||
This file is distributed under the Linux GPL.
|
||||
Some of these names and comments are from the Crynwr packet drivers. */
|
||||
|
||||
#ifndef e8390_h
|
||||
#define e8390_h
|
||||
#define PINGPONG
|
||||
#ifdef PINGPONG
|
||||
#define TX_PAGES 12
|
||||
#else
|
||||
#define TX_PAGES 6
|
||||
#endif
|
||||
|
||||
#define ETHER_ADDR_LEN 6
|
||||
|
||||
/* From 8390.c */
|
||||
void ei_interrupt(int reg_ptr);
|
||||
/* From auto_irq.c */
|
||||
extern void autoirq_setup(int waittime);
|
||||
extern int autoirq_report(int waittime);
|
||||
|
||||
/* Most of these entries should be in 'struct device' (or most of the
|
||||
things in there should be here!) */
|
||||
/* You have one of these per-board */
|
||||
struct ei_device {
|
||||
char *name;
|
||||
int open:1;
|
||||
int word16:1; /* We have the 16-bit (vs 8-bit) version of the card. */
|
||||
int txing:1; /* Transmit Active */
|
||||
void (*reset_8390)(struct device *);
|
||||
void (*block_output)(struct device *, int, const unsigned char *, int);
|
||||
int (*block_input)(struct device *, int, char *, int);
|
||||
unsigned char tx_start_page, rx_start_page, stop_page;
|
||||
unsigned char current_page; /* Read pointer in buffer */
|
||||
unsigned char thin_bit; /* Value to write to the 3c503 E33G_CNTRL */
|
||||
unsigned char txqueue; /* Tx Packet buffer queue length. */
|
||||
unsigned char in_interrupt;
|
||||
short tx1, tx2; /* Packet lengths for ping-pong tx. */
|
||||
/* The statistics: */
|
||||
int tx_packets;
|
||||
int tx_errors;
|
||||
int rx_packets;
|
||||
int soft_rx_errors;
|
||||
int soft_rx_err_bits;
|
||||
int missed_packets;
|
||||
int rx_overruns;
|
||||
int rx_overrun_packets;
|
||||
};
|
||||
|
||||
#define ei_status (*(struct ei_device *)(dev->private))
|
||||
|
||||
/* Some generic ethernet register configurations. */
|
||||
#define E8390_TX_IRQ_MASK 0xa /* For register EN0_ISR */
|
||||
#define E8390_RX_IRQ_MASK 0x5
|
||||
#define E8390_RXCONFIG 0x4 /* EN0_RXCR: broadcasts, no multicast,errors */
|
||||
#define E8390_RXOFF 0x20 /* EN0_RXCR: Accept no packets */
|
||||
#define E8390_TXCONFIG 0x00 /* EN0_TXCR: Normal transmit mode */
|
||||
#define E8390_TXOFF 0x02 /* EN0_TXCR: Transmitter off */
|
||||
|
||||
/* Register accessed at EN_CMD, the 8390 base addr. */
|
||||
#define E8390_STOP 0x01 /* Stop and reset the chip */
|
||||
#define E8390_START 0x02 /* Start the chip, clear reset */
|
||||
#define E8390_TRANS 0x04 /* Transmit a frame */
|
||||
#define E8390_RREAD 0x08 /* Remote read */
|
||||
#define E8390_RWRITE 0x10 /* Remote write */
|
||||
#define E8390_NODMA 0x20 /* Remote DMA */
|
||||
#define E8390_PAGE0 0x00 /* Select page chip registers */
|
||||
#define E8390_PAGE1 0x40 /* using the two high-order bits */
|
||||
#define E8390_PAGE2 0x80 /* Page 3 is invalid. */
|
||||
|
||||
#define E8390_CMD 0x00 /* The command register (for all pages) */
|
||||
/* Page 0 register offsets. */
|
||||
#define EN0_CLDALO 0x01 /* Low byte of current local dma addr RD */
|
||||
#define EN0_STARTPG 0x01 /* Starting page of ring bfr WR */
|
||||
#define EN0_CLDAHI 0x02 /* High byte of current local dma addr RD */
|
||||
#define EN0_STOPPG 0x02 /* Ending page +1 of ring bfr WR */
|
||||
#define EN0_BOUNDARY 0x03 /* Boundary page of ring bfr RD WR */
|
||||
#define EN0_TSR 0x04 /* Transmit status reg RD */
|
||||
#define EN0_TPSR 0x04 /* Transmit starting page WR */
|
||||
#define EN0_NCR 0x05 /* Number of collision reg RD */
|
||||
#define EN0_TCNTLO 0x05 /* Low byte of tx byte count WR */
|
||||
#define EN0_FIFO 0x06 /* FIFO RD */
|
||||
#define EN0_TCNTHI 0x06 /* High byte of tx byte count WR */
|
||||
#define EN0_ISR 0x07 /* Interrupt status reg RD WR */
|
||||
#define EN0_CRDALO 0x08 /* low byte of current remote dma address RD */
|
||||
#define EN0_RSARLO 0x08 /* Remote start address reg 0 */
|
||||
#define EN0_CRDAHI 0x09 /* high byte, current remote dma address RD */
|
||||
#define EN0_RSARHI 0x09 /* Remote start address reg 1 */
|
||||
#define EN0_RCNTLO 0x0a /* Remote byte count reg WR */
|
||||
#define EN0_RCNTHI 0x0b /* Remote byte count reg WR */
|
||||
#define EN0_RSR 0x0c /* rx status reg RD */
|
||||
#define EN0_RXCR 0x0c /* RX configuration reg WR */
|
||||
#define EN0_TXCR 0x0d /* TX configuration reg WR */
|
||||
#define EN0_COUNTER0 0x0d /* Rcv alignment error counter RD */
|
||||
#define EN0_DCFG 0x0e /* Data configuration reg WR */
|
||||
#define EN0_COUNTER1 0x0e /* Rcv CRC error counter RD */
|
||||
#define EN0_IMR 0x0f /* Interrupt mask reg WR */
|
||||
#define EN0_COUNTER2 0x0f /* Rcv missed frame error counter RD */
|
||||
|
||||
/* Bits in EN0_ISR - Interrupt status register */
|
||||
#define ENISR_RX 0x01 /* Receiver, no error */
|
||||
#define ENISR_TX 0x02 /* Transmitter, no error */
|
||||
#define ENISR_RX_ERR 0x04 /* Receiver, with error */
|
||||
#define ENISR_TX_ERR 0x08 /* Transmitter, with error */
|
||||
#define ENISR_OVER 0x10 /* Receiver overwrote the ring */
|
||||
#define ENISR_COUNTERS 0x20 /* Counters need emptying */
|
||||
#define ENISR_RDC 0x40 /* remote dma complete */
|
||||
#define ENISR_RESET 0x80 /* Reset completed */
|
||||
#define ENISR_ALL 0x3f /* Interrupts we will enable */
|
||||
|
||||
/* Bits in EN0_DCFG - Data config register */
|
||||
#define ENDCFG_WTS 0x01 /* word transfer mode selection */
|
||||
|
||||
/* Page 1 register offsets. */
|
||||
#define EN1_PHYS 0x01 /* This board's physical enet addr RD WR */
|
||||
#define EN1_CURPAG 0x07 /* Current memory page RD WR */
|
||||
#define EN1_MULT 0x08 /* Multicast filter mask array (8 bytes) RD WR */
|
||||
|
||||
/* Bits in received packet status byte and EN0_RSR*/
|
||||
#define ENRSR_RXOK 0x01 /* Received a good packet */
|
||||
#define ENRSR_CRC 0x02 /* CRC error */
|
||||
#define ENRSR_FAE 0x04 /* frame alignment error */
|
||||
#define ENRSR_FO 0x08 /* FIFO overrun */
|
||||
#define ENRSR_MPA 0x10 /* missed pkt */
|
||||
#define ENRSR_PHY 0x20 /* physical/multicase address */
|
||||
#define ENRSR_DIS 0x40 /* receiver disable. set in monitor mode */
|
||||
#define ENRSR_DEF 0x80 /* deferring */
|
||||
|
||||
/* Transmitted packet status, EN0_TSR. */
|
||||
#define ENTSR_PTX 0x01 /* Packet transmitted without error */
|
||||
/* The other bits in the TX status register mean:
|
||||
0x02 The transmit wasn't deferred.
|
||||
0x04 The transmit collided at least once.
|
||||
0x08 The transmit collided 16 times, and was deferred.
|
||||
0x10 The carrier sense was lost (from the ethernet transceiver)
|
||||
0x20 A "FIFO underrun" (internal error) occured during transmit.
|
||||
0x40 The collision detect "heartbeat" signal was lost.
|
||||
0x80 There was an out-of-window collision.
|
||||
*/
|
||||
|
||||
/* The per-packet-header format. */
|
||||
struct e8390_pkt_hdr {
|
||||
unsigned char status; /* status */
|
||||
unsigned char next; /* pointer to next packet. */
|
||||
unsigned short count; /* header + packet lenght in bytes */
|
||||
};
|
||||
#endif /* e8390_h */
|
||||
44
net/tcpip/newether-pingpong/Announce
Normal file
44
net/tcpip/newether-pingpong/Announce
Normal file
@@ -0,0 +1,44 @@
|
||||
|
||||
Subject: Enhanced Ethercard driver available for alpha test.
|
||||
|
||||
My "8390" Linux ethercard drivers are now available from usra.edu and
|
||||
super.org in ~ftp/pub/linux/ethercards/*. They'll be at tsx-11 and
|
||||
sunsite RSN.
|
||||
|
||||
These drivers support all common 8390-based ethernet boards. Currently
|
||||
"common" is defined as:
|
||||
|
||||
3Com Products:
|
||||
* 3Com 3c503 Board loaned by Chance Reschke, USRA.edu (thanks!)
|
||||
3Com 3c503/16 and excellent documentation provided by 3Com.
|
||||
|
||||
Clones-n-things
|
||||
NE1000 Novell and Eagle are useless for documentation,
|
||||
* NE2000 but copied the designs directly from NatSemi;->.
|
||||
* Alta Combo(NE2000 clone)
|
||||
Aritsoft LANtastic AE-2 (NE2000 clone w/ extra memory)
|
||||
D-Link Ethernet II
|
||||
|
||||
Cabletron products:
|
||||
E1010 No ID PROM and sketchy info from Ctron means you'll
|
||||
E1010-x have to compile-in information about your board.
|
||||
E2010
|
||||
E2010-x
|
||||
|
||||
WD/SMC products
|
||||
WD8003
|
||||
* WD8013 Board loaned by Russ Nelson, Crynwr Software. Thanks!
|
||||
|
||||
* I've seen it work myself!
|
||||
|
||||
There is support for the following boards, but since I've only been
|
||||
able to borrow a thinnet of an HP ethercard I haven't been able to test it:
|
||||
|
||||
HP LAN adaptors
|
||||
** HP27245
|
||||
** HP27247
|
||||
** HP27250
|
||||
|
||||
Thanks are due to the dozens of alpha testers, and special thanks to Chance Reschke <@usra.edu> and Russ Nelson <@crynwr.com> for loaning me ethercards.
|
||||
|
||||
|
||||
33
net/tcpip/newether-pingpong/GNUmakefile
Normal file
33
net/tcpip/newether-pingpong/GNUmakefile
Normal file
@@ -0,0 +1,33 @@
|
||||
# This file must be named 'GNUmakefile'. When it has that name it
|
||||
# loaded in preference to the regular 'Makefile' (which it include
|
||||
# right here) so this has the effect of appending lines to the Makefile.
|
||||
|
||||
include Makefile
|
||||
|
||||
# Add a few files to tcpip.a.
|
||||
newobjs = 8390.o 3c503.o ne.o wd.o hp.o auto_irq.o
|
||||
OBJS := $(OBJS) $(newobjs)
|
||||
tcpip.a: $(newobjs)
|
||||
|
||||
# Set the address and IRQ here. The ne.c and 3c503 driver will autoprobe
|
||||
# if you set the address or IRQ to zero, so we do that by default.
|
||||
#
|
||||
# Add -DEI_NAME="eth0" if you want to be exactly compatible with the default
|
||||
# driver. This will only work if you don't use the distributed 'we' driver!
|
||||
#
|
||||
ether_options := -DEI_NAME=\"eth0\" -DEI8390=0 -DEI8390_IRQ=0
|
||||
Space.o: Space.c GNUmakefile
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) $(ether_options) -c Space.c -o $@
|
||||
|
||||
# Change this to define the set of ethercards your kernel will support.
|
||||
8390.o: 8390.c GNUmakefile
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) -DNE2000 -DWD80x3 -DHPLAN -DEL2 -c 8390.c -o $@
|
||||
|
||||
# Change this to -DEL2_AUI if you use the AUI port.
|
||||
3c503.o: 3c503.c 3c503reg.h GNUmakefile
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) -UEL2_AUI -c 3c503.c -o $@
|
||||
|
||||
# Change this to -DSHMEM=0xd0000 and -DFORCE_8BIT if you have an old
|
||||
# (non-EEPROM) wd8003.
|
||||
wd.o: wd.c GNUmakefile
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) -UWD_SHMEM -UFORCE_8BIT -c wd.c -o $@
|
||||
14
net/tcpip/newether-pingpong/README
Normal file
14
net/tcpip/newether-pingpong/README
Normal file
@@ -0,0 +1,14 @@
|
||||
This is a new version of the ethercard drivers.
|
||||
|
||||
The new features are:
|
||||
|
||||
PING-PONG transmit! This can almost double the transmit rate in some
|
||||
cases. 1MB/sec is now possible!!!
|
||||
|
||||
The 'ne' driver autoselects between the NE1000 and NE2000 modes.
|
||||
There is a less-intrusive 3c503 probe.
|
||||
The 3c503 can now be used in non-shared-memory mode.
|
||||
The HP-LAN non-autoIRQ mode should now work.
|
||||
The WD driver should now work with some older WD8003 cards, but this
|
||||
is untested.
|
||||
|
||||
110
net/tcpip/newether-pingpong/auto_irq.c
Normal file
110
net/tcpip/newether-pingpong/auto_irq.c
Normal file
@@ -0,0 +1,110 @@
|
||||
/* auto_irq.c: Auto-configure IRQ lines for linux. */
|
||||
/*
|
||||
Written 1993 by Donald Becker. This is alpha test code.
|
||||
|
||||
The Author may be reached as becker@super.org or
|
||||
C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
|
||||
|
||||
This code is a general-purpose IRQ line detector for devices with
|
||||
jumpered IRQ lines. If you can make the device raise an IRQ (and
|
||||
that IRQ line isn't already being used), these routines will tell
|
||||
you what IRQ line it's using -- perfect for those oh-so-cool boot-time
|
||||
device probes!
|
||||
|
||||
To use this, first call autoirq_setup(timeout). TIMEOUT is how many
|
||||
'jiffies' (1/18 sec.) to detect other devices that have active IRQ lines,
|
||||
and can usually be zero at boot. 'autoirq_setup()' returns the bit
|
||||
vector of nominally-available IRQ lines (lines may be physically in-use,
|
||||
but not yet registered to a device).
|
||||
Next, set up your device to trigger an interrupt.
|
||||
Finally call autoirq_report(TIMEOUT) to find out which IRQ line was
|
||||
most recently active. The TIMEOUT should usually be zero, but may
|
||||
be set to the number of jiffies to wait for a slow device to raise an IRQ.
|
||||
|
||||
The idea of using the setup timeout to filter out bogus IRQs came from
|
||||
the serial driver.
|
||||
*/
|
||||
|
||||
|
||||
#ifdef version
|
||||
static char *version="auto_irq.c:v0.01 1993 Donald Becker (becker@super.org)";
|
||||
#endif
|
||||
|
||||
/*#include <linux/config.h>*/
|
||||
/*#include <linux/kernel.h>*/
|
||||
#include <linux/sched.h>
|
||||
#include <asm/bitops.h>
|
||||
#include <asm/io.h>
|
||||
/*#include <asm/system.h>*/
|
||||
|
||||
int irqs_busy = 0x01; /* The set of fixed IRQs always enabled */
|
||||
int irqs_used = 0x01; /* The set of fixed IRQs sometimes enabled. */
|
||||
int irqs_reserved = 0x00; /* An advisory "reserved" table. */
|
||||
int irqs_shared = 0x00; /* IRQ lines "shared" among conforming cards.*/
|
||||
|
||||
static volatile int irq_number; /* The latest irq number we actually found. */
|
||||
static volatile int irq_bitmap; /* The irqs we actually found. */
|
||||
static int irq_handled; /* The irq lines we have a handler on. */
|
||||
|
||||
static void autoirq_probe(int irq)
|
||||
{
|
||||
irq_number = irq;
|
||||
set_bit(irq, (void *)&irq_bitmap); /* irq_bitmap |= 1 << irq; */
|
||||
return;
|
||||
}
|
||||
struct sigaction autoirq_sigaction = { autoirq_probe, 0, SA_INTERRUPT, NULL};
|
||||
|
||||
int autoirq_setup(int waittime)
|
||||
{
|
||||
int i, mask;
|
||||
int timeout = jiffies+waittime;
|
||||
|
||||
irq_number = 0;
|
||||
irq_bitmap = 0;
|
||||
irq_handled = 0;
|
||||
for (i = 0; i < 16; i++) {
|
||||
if (!irqaction(i, &autoirq_sigaction))
|
||||
set_bit(i, (void *)&irq_handled); /* irq_handled |= 1 << i;*/
|
||||
}
|
||||
/* Update our USED lists. */
|
||||
irqs_used |= ~irq_handled;
|
||||
|
||||
/* Hang out at least <waittime> jiffies waiting for bogus IRQ hits. */
|
||||
while (timeout >= jiffies)
|
||||
;
|
||||
|
||||
for (i = 0, mask = 0x01; i < 16; i++, mask <<= 1) {
|
||||
if (irq_bitmap & irq_handled & mask) {
|
||||
irq_handled &= ~mask;
|
||||
printk(" Spurious interrupt on IRQ %d\n", i);
|
||||
free_irq(i);
|
||||
}
|
||||
}
|
||||
return irq_handled;
|
||||
}
|
||||
|
||||
int autoirq_report(int waittime)
|
||||
{
|
||||
int i;
|
||||
int timeout = jiffies+waittime;
|
||||
|
||||
/* Hang out at least <waittime> jiffies waiting for the IRQ. */
|
||||
while (timeout >= jiffies)
|
||||
if (irq_number)
|
||||
break;
|
||||
|
||||
/* Retract the irq handlers that we installed. */
|
||||
for (i = 0; i < 16; i++) {
|
||||
if (test_bit(i, (void *)&irq_handled))
|
||||
free_irq(i);
|
||||
}
|
||||
return irq_number;
|
||||
}
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -I/usr/src/linux/net/tcp -c auto_irq.c"
|
||||
* version-control: t
|
||||
* kept-new-versions: 5
|
||||
* End:
|
||||
*/
|
||||
319
net/tcpip/newether-pingpong/hp.c
Normal file
319
net/tcpip/newether-pingpong/hp.c
Normal file
@@ -0,0 +1,319 @@
|
||||
/* hp.c: A HP LAN ethernet driver for linux. */
|
||||
/*
|
||||
Written 1993 by Donald Becker. This is alpha test code.
|
||||
This is a extension to the Linux operating system, and is covered by
|
||||
same Gnu Public License that covers that work.
|
||||
|
||||
This is a driver for the HP LAN adaptors.
|
||||
|
||||
The Author may be reached as becker@super.org or
|
||||
C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
|
||||
*/
|
||||
|
||||
static char *version = "hp.c:v0.67 3/8/93 Donald Becker (becker@super.org)\n";
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/errno.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/system.h>
|
||||
|
||||
#include "dev.h"
|
||||
#include "8390.h"
|
||||
|
||||
/* These should be in <asm/io.h> someday, borrowed from blk_drv/hd.c. */
|
||||
#define port_read(port,buf,nr) \
|
||||
__asm__("cld;rep;insw"::"d" (port),"D" (buf),"c" (nr):"cx","di")
|
||||
#define port_write(port,buf,nr) \
|
||||
__asm__("cld;rep;outsw"::"d" (port),"S" (buf),"c" (nr):"cx","si")
|
||||
|
||||
#define port_read_b(port,buf,nr) \
|
||||
__asm__("cld;rep;insb"::"d" (port),"D" (buf),"c" (nr):"cx","di")
|
||||
#define port_write_b(port,buf,nr) \
|
||||
__asm__("cld;rep;outsb"::"d" (port),"S" (buf),"c" (nr):"cx","si")
|
||||
|
||||
#define HP_DATAPORT 0x0c /* "Remote DMA" data port. */
|
||||
#define HP_ID 0x07
|
||||
#define HP_CONFIGURE 0x08 /* Configuration register. */
|
||||
#define HP_RUN 0x01 /* 1 == Run, 0 == reset. */
|
||||
#define HP_IRQ 0x0E /* Mask for software-configured IRQ line. */
|
||||
#define HP_DATAON 0x10 /* Turn on dataport */
|
||||
#define NIC_OFFSET 0x10 /* Offset the 8390 registers. */
|
||||
|
||||
#define HP_START_PG 0x00 /* First page of TX buffer */
|
||||
#define HP_8BSTOP_PG 0x80 /* Last page +1 of RX ring */
|
||||
#define HP_16BSTOP_PG 0xFF /* Last page +1 of RX ring */
|
||||
|
||||
extern void NS8390_init(struct device *dev, int startp);
|
||||
extern int ei_debug;
|
||||
extern struct sigaction ei_sigaction;
|
||||
|
||||
int hpprobe(int ioaddr, struct device *dev);
|
||||
int hpprobe1(int ioaddr, struct device *dev);
|
||||
|
||||
static void hp_reset_8390(struct device *dev);
|
||||
static int hp_block_input(struct device *dev, int count,
|
||||
char *buf, int ring_offset);
|
||||
static void hp_block_output(struct device *dev, int count,
|
||||
const unsigned char *buf, const start_page);
|
||||
static void hp_init_card(struct device *dev);
|
||||
|
||||
/* The map from IRQ number to HP_CONFIGURE register setting. */
|
||||
/* My default is IRQ5 0 1 2 3 4 5 6 7 8 9 10 11 */
|
||||
static char irqmap[16] = { 0, 0, 4, 6, 8,10, 0,14, 0, 4, 2,12,0,0,0,0};
|
||||
|
||||
|
||||
/* Probe for an HP LAN adaptor.
|
||||
Also initialize the card and fill in STATION_ADDR with the station
|
||||
address. */
|
||||
|
||||
int hpprobe(int ioaddr, struct device *dev)
|
||||
{
|
||||
int *port, ports[] = {0x300, 0x320, 0x340, 0x280, 0x2C0, 0x200, 0x240, 0};
|
||||
|
||||
if (ioaddr > 0x100)
|
||||
return hpprobe1(ioaddr, dev);
|
||||
|
||||
for (port = &ports[0]; *port; port++)
|
||||
if (inb_p(*port) != 0xff && hpprobe1(*port, dev))
|
||||
return dev->base_addr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hpprobe1(int ioaddr, struct device *dev)
|
||||
{
|
||||
int i;
|
||||
unsigned char *station_addr = dev->dev_addr;
|
||||
unsigned char SA_prom[6];
|
||||
int tmp;
|
||||
int hplan;
|
||||
|
||||
printk("HP-LAN ethercard probe at %#3x:", ioaddr);
|
||||
tmp = inb_p(ioaddr);
|
||||
if (tmp == 0xFF) {
|
||||
printk(" not found (nothing there).\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
for(i = 0; i < sizeof(SA_prom); i++) {
|
||||
SA_prom[i] = inb(ioaddr + i);
|
||||
if (i < ETHER_ADDR_LEN && station_addr) {
|
||||
printk(" %2.2x", SA_prom[i]);
|
||||
station_addr[i] = SA_prom[i];
|
||||
}
|
||||
}
|
||||
hplan = (SA_prom[0] == 0x08 && SA_prom[1] == 0x00 && SA_prom[2] == 0x09);
|
||||
if (hplan == 0) {
|
||||
printk(" not found (invalid station address prefix).\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ei_status.tx_start_page = HP_START_PG;
|
||||
ei_status.rx_start_page = HP_START_PG + TX_PAGES;
|
||||
/* Set up the rest of the parameters. */
|
||||
if ((tmp = inb_p(HP_ID)) & 0x80) {
|
||||
ei_status.name = "HP27247";
|
||||
ei_status.word16 = 1;
|
||||
ei_status.stop_page = HP_16BSTOP_PG; /* Safe for now */
|
||||
} else {
|
||||
ei_status.name = "HP27250";
|
||||
ei_status.word16 = 0;
|
||||
ei_status.stop_page = HP_8BSTOP_PG; /* Safe for now */
|
||||
}
|
||||
|
||||
/* Set the base address to point to the NIC! */
|
||||
dev->base_addr = ioaddr + NIC_OFFSET;
|
||||
|
||||
/* Snarf the interrupt now. There's no point in waiting since we cannot
|
||||
share and the board will usually be enabled. */
|
||||
if (dev->irq < 2) {
|
||||
int irq_16list[] = { 11, 10, 5, 3, 4, 7, 9, 0};
|
||||
int irq_8list[] = { 7, 5, 3, 4, 9, 0};
|
||||
int *irqp = ei_status.word16 ? irq_16list : irq_8list;
|
||||
do {
|
||||
if (request_irq (dev->irq = *irqp, NULL) != -EBUSY) {
|
||||
autoirq_setup(0);
|
||||
/* Twinkle the interrupt, and check if it's seen. */
|
||||
outb_p(irqmap[dev->irq] | HP_RUN, ioaddr + HP_CONFIGURE);
|
||||
outb_p( 0x00 | HP_RUN, ioaddr + HP_CONFIGURE);
|
||||
if (dev->irq == autoirq_report(0) /* It's a good IRQ line! */
|
||||
&& request_irq (dev->irq, &ei_interrupt) == 0) {
|
||||
printk(" got IRQ %d", dev->irq);
|
||||
break;
|
||||
} else
|
||||
printk(" IRQ%d busy..", dev->irq);
|
||||
}
|
||||
} while (*++irqp);
|
||||
if (*irqp == 0) {
|
||||
printk(" unable to find an free IRQ line.\n");
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
if (dev->irq == 2)
|
||||
dev->irq = 9;
|
||||
if (irqaction(dev->irq, &ei_sigaction)) {
|
||||
printk (" unable to get IRQ %d.\n", dev->irq);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
printk("\n%s: %s using IRQ %d.\n", dev->name, ei_status.name, dev->irq);
|
||||
if (ei_debug > 1)
|
||||
printk(version);
|
||||
|
||||
ei_status.reset_8390 = &hp_reset_8390;
|
||||
ei_status.block_input = &hp_block_input;
|
||||
ei_status.block_output = &hp_block_output;
|
||||
hp_init_card(dev);
|
||||
return dev->base_addr;
|
||||
}
|
||||
|
||||
static void
|
||||
hp_reset_8390(struct device *dev)
|
||||
{
|
||||
int hp_base = dev->base_addr - NIC_OFFSET;
|
||||
int saved_config = inb_p(hp_base + HP_CONFIGURE);
|
||||
int reset_start_time = jiffies;
|
||||
|
||||
if (ei_debug > 1) printk("resetting the 8390 time=%d...", jiffies);
|
||||
outb_p(0x00, hp_base + HP_CONFIGURE);
|
||||
ei_status.txing = 0;
|
||||
|
||||
sti();
|
||||
/* We shouldn't use the boguscount for timing, but this hasn't been
|
||||
checked yet, and you could hang your machine if jiffies break... */
|
||||
{
|
||||
int boguscount = 150000;
|
||||
while(jiffies - reset_start_time < 2)
|
||||
if (boguscount-- < 0) {
|
||||
printk("jiffy failure (t=%d)...", jiffies);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
outb_p(saved_config, hp_base + HP_CONFIGURE);
|
||||
while ((inb_p(hp_base+NIC_OFFSET+EN0_ISR) & ENISR_RESET) == 0)
|
||||
if (jiffies - reset_start_time > 2) {
|
||||
printk("%s: hp_reset_8390() did not complete.\n", dev->name);
|
||||
return;
|
||||
}
|
||||
if (ei_debug > 1) printk("8390 reset done.", jiffies);
|
||||
}
|
||||
|
||||
/* Block input and output, similar to the Crynwr packet driver. If you
|
||||
porting to a new ethercard look at the packet driver source for hints.
|
||||
The HP LAN doesn't use shared memory -- we put the packet
|
||||
out through the "remote DMA" dataport. */
|
||||
|
||||
static int
|
||||
hp_block_input(struct device *dev, int count, char *buf, int ring_offset)
|
||||
{
|
||||
int nic_base = dev->base_addr;
|
||||
int saved_config = inb_p(nic_base - NIC_OFFSET + HP_CONFIGURE);
|
||||
int xfer_count = count;
|
||||
|
||||
outb_p(saved_config | HP_DATAON, nic_base - NIC_OFFSET + HP_CONFIGURE);
|
||||
outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base);
|
||||
outb_p(count & 0xff, nic_base + EN0_RCNTLO);
|
||||
outb_p(count >> 8, nic_base + EN0_RCNTHI);
|
||||
outb_p(ring_offset & 0xff, nic_base + EN0_RSARLO);
|
||||
outb_p(ring_offset >> 8, nic_base + EN0_RSARHI);
|
||||
outb_p(E8390_RREAD+E8390_START, nic_base);
|
||||
if (ei_status.word16) {
|
||||
port_read(nic_base - NIC_OFFSET + HP_DATAPORT,buf,count>>1);
|
||||
if (count & 0x01)
|
||||
buf[count-1] = inb(nic_base - NIC_OFFSET + HP_DATAPORT), xfer_count++;
|
||||
} else {
|
||||
port_read_b(nic_base - NIC_OFFSET + HP_DATAPORT, buf, count);
|
||||
}
|
||||
/* This is for the ALPHA version only, remove for later releases. */
|
||||
if (ei_debug > 0) { /* DMA termination address check... */
|
||||
int high = inb_p(nic_base + EN0_RSARHI);
|
||||
int low = inb_p(nic_base + EN0_RSARLO);
|
||||
int addr = (high << 8) + low;
|
||||
/* Check only the lower 8 bits so we can ignore ring wrap. */
|
||||
if (((ring_offset + xfer_count) & 0xff) != (addr & 0xff))
|
||||
printk("%s: RX transfer address mismatch, %#4.4x vs. %#4.4x (actual).\n",
|
||||
dev->name, ring_offset + xfer_count, addr);
|
||||
}
|
||||
outb_p(saved_config & (~HP_DATAON), nic_base - NIC_OFFSET + HP_CONFIGURE);
|
||||
return ring_offset + count;
|
||||
}
|
||||
|
||||
static void
|
||||
hp_block_output(struct device *dev, int count,
|
||||
const unsigned char *buf, const start_page)
|
||||
{
|
||||
int nic_base = dev->base_addr;
|
||||
int saved_config = inb_p(nic_base - NIC_OFFSET + HP_CONFIGURE);
|
||||
|
||||
outb_p(saved_config | HP_DATAON, nic_base - NIC_OFFSET + HP_CONFIGURE);
|
||||
/* Round the count up for word writes. Do we need to do this?
|
||||
What effect will an odd byte count have on the 8390?
|
||||
I should check someday. */
|
||||
if (ei_status.word16 && (count & 0x01))
|
||||
count++;
|
||||
/* We should already be in page 0, but to be safe... */
|
||||
outb_p(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base);
|
||||
|
||||
#ifdef ei8390_bug
|
||||
/* Handle the read-before-write bug the same way as the
|
||||
Crynwr packet driver -- the NatSemi method doesn't work. */
|
||||
outb_p(0x42, nic_base + EN0_RCNTLO);
|
||||
outb_p(0, nic_base + EN0_RCNTHI);
|
||||
outb_p(0xff, nic_base + EN0_RSARLO);
|
||||
outb_p(0x00, nic_base + EN0_RSARHI);
|
||||
outb_p(E8390_RREAD+E8390_START, EN_CMD);
|
||||
/* Make certain that the dummy read has occured. */
|
||||
inb_p(0x61);
|
||||
inb_p(0x61);
|
||||
#endif
|
||||
|
||||
outb_p(count & 0xff, nic_base + EN0_RCNTLO);
|
||||
outb_p(count >> 8, nic_base + EN0_RCNTHI);
|
||||
outb_p(0x00, nic_base + EN0_RSARLO);
|
||||
outb_p(start_page, nic_base + EN0_RSARHI);
|
||||
|
||||
outb_p(E8390_RWRITE+E8390_START, nic_base);
|
||||
if (ei_status.word16) {
|
||||
/* Use the 'rep' sequence for 16 bit boards. */
|
||||
port_write(nic_base - NIC_OFFSET + HP_DATAPORT, buf, count>>1);
|
||||
} else {
|
||||
port_write_b(nic_base - NIC_OFFSET + HP_DATAPORT, buf, count);
|
||||
}
|
||||
|
||||
/* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here -- it's broken! */
|
||||
|
||||
/* This is for the ALPHA version only, remove for later releases. */
|
||||
if (ei_debug > 0) { /* DMA termination address check... */
|
||||
int high = inb_p(nic_base + EN0_RSARHI);
|
||||
int low = inb_p(nic_base + EN0_RSARLO);
|
||||
int addr = (high << 8) + low;
|
||||
if ((start_page << 8) + count != addr)
|
||||
printk("%s: TX Transfer address mismatch, %#4.4x vs. %#4.4x.\n",
|
||||
dev->name, (start_page << 8) + count, addr);
|
||||
}
|
||||
outb_p(saved_config & (~HP_DATAON), nic_base - NIC_OFFSET + HP_CONFIGURE);
|
||||
return;
|
||||
}
|
||||
|
||||
/* This function resets the ethercard if something screws up. */
|
||||
static void
|
||||
hp_init_card(struct device *dev)
|
||||
{
|
||||
int irq = dev->irq;
|
||||
NS8390_init(dev, 0);
|
||||
outb_p(irqmap[irq&0x0f] | HP_RUN,
|
||||
dev->base_addr - NIC_OFFSET + HP_CONFIGURE);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -I/usr/src/linux/net/tcp -c hp.c"
|
||||
* version-control: t
|
||||
* kept-new-versions: 5
|
||||
* End:
|
||||
*/
|
||||
379
net/tcpip/newether-pingpong/ne.c
Normal file
379
net/tcpip/newether-pingpong/ne.c
Normal file
@@ -0,0 +1,379 @@
|
||||
/* ne.c: A general non-shared-memory NS8390 ethernet driver for linux. */
|
||||
/*
|
||||
Written 1992,1993 by Donald Becker. This is alpha test code.
|
||||
This is a extension to the Linux operating system, and is covered by
|
||||
same Gnu Public License that covers that work.
|
||||
|
||||
This driver should work with many 8390-based ethernet boards. Currently
|
||||
it support the NE1000, NE2000 (and clones), and some Cabletron products.
|
||||
8-bit ethercard support is enabled with #define EI_8BIT
|
||||
|
||||
The Author may be reached as becker@super.org or
|
||||
C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
|
||||
*/
|
||||
|
||||
/* Routines for the NatSemi-based designs (NE[12]000). */
|
||||
|
||||
static char *version =
|
||||
"ne.c:v0.50 2/19/93 Donald Becker (becker@super.org)\n";
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sched.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/system.h>
|
||||
|
||||
#include "dev.h"
|
||||
#include "8390.h"
|
||||
|
||||
/* These should be in <asm/io.h> someday, borrowed from blk_drv/hd.c. */
|
||||
#define port_read(port,buf,nr) \
|
||||
__asm__("cld;rep;insw"::"d" (port),"D" (buf),"c" (nr):"cx","di")
|
||||
#define port_write(port,buf,nr) \
|
||||
__asm__("cld;rep;outsw"::"d" (port),"S" (buf),"c" (nr):"cx","si")
|
||||
|
||||
#define port_read_b(port,buf,nr) \
|
||||
__asm__("cld;rep;insb"::"d" (port),"D" (buf),"c" (nr):"cx","di")
|
||||
#define port_write_b(port,buf,nr) \
|
||||
__asm__("cld;rep;outsb"::"d" (port),"S" (buf),"c" (nr):"cx","si")
|
||||
|
||||
#define EN_CMD (dev->base_addr)
|
||||
#define NE_BASE (dev->base_addr)
|
||||
#define NE_DATAPORT 0x10 /* NatSemi-defined port window offset. */
|
||||
#define NE_RESET 0x1f /* Issue a read to reset, a write to clear. */
|
||||
|
||||
#define NE1SM_START_PG 0x20 /* First page of TX buffer */
|
||||
#define NE1SM_STOP_PG 0x40 /* Last page +1 of RX ring */
|
||||
#define NESM_START_PG 0x40 /* First page of TX buffer */
|
||||
#define NESM_STOP_PG 0x80 /* Last page +1 of RX ring */
|
||||
|
||||
extern void NS8390_init(struct device *dev, int startp);
|
||||
extern int ei_debug;
|
||||
extern struct sigaction ei_sigaction;
|
||||
|
||||
int neprobe(int ioaddr, struct device *dev);
|
||||
static int neprobe1(int ioaddr, struct device *dev, int verbose);
|
||||
|
||||
static void ne_reset_8390(struct device *dev);
|
||||
static int ne_block_input(struct device *dev, int count,
|
||||
char *buf, int ring_offset);
|
||||
static void ne_block_output(struct device *dev, const int count,
|
||||
const unsigned char *buf, const int start_page);
|
||||
|
||||
|
||||
/* Probe for various non-shared-memory ethercards.
|
||||
|
||||
NEx000-clone boards have a Station Address PROM (SAPROM) in the packet
|
||||
buffer memory space. NE2000 clones have 0x57,0x57 in bytes 0x0e,0x0f of
|
||||
the SAPROM, while other supposed NE2000 clones must be detected by their
|
||||
SA prefix.
|
||||
|
||||
Reading the SAPROM from a word-wide card with the 8390 set in byte-wide
|
||||
mode results in doubled values, which can be detected and compansated for.
|
||||
|
||||
The probe is also responsible for initializing the card and filling
|
||||
in the 'dev' and 'ei_status' structures.
|
||||
|
||||
We use the minimum memory size for some ethercard product lines, iff we can't
|
||||
distinguish models. You can increase the packet buffer size by setting
|
||||
PACKETBUF_MEMSIZE. Reported Cabletron packet buffer locations are:
|
||||
E1010 starts at 0x100 and ends at 0x2000.
|
||||
E1010-x starts at 0x100 and ends at 0x8000. ("-x" means "more memory")
|
||||
E2010 starts at 0x100 and ends at 0x4000.
|
||||
E2010-x starts at 0x100 and ends at 0xffff. */
|
||||
|
||||
int neprobe(int ioaddr, struct device *dev)
|
||||
{
|
||||
int *port, ports[] = {0x300, 0x280, 0x320, 0x340, 0x360, 0};
|
||||
|
||||
if (ioaddr > 0x100)
|
||||
return neprobe1(ioaddr, dev, 1);
|
||||
|
||||
for (port = &ports[0]; *port; port++)
|
||||
if (inb_p(*port) != 0xff && neprobe1(*port, dev, 0))
|
||||
return dev->base_addr = *port;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int neprobe1(int ioaddr, struct device *dev, int verbose)
|
||||
{
|
||||
int i;
|
||||
unsigned char SA_prom[32];
|
||||
int wordlength = 2;
|
||||
int neX000, ctron, dlink;
|
||||
|
||||
|
||||
if ( inb_p(ioaddr) == 0xFF) {
|
||||
if (verbose) printk("8390 ethercard probe at %#3x failed.\n", ioaddr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
printk("8390 ethercard probe at %#3x:", ioaddr);
|
||||
|
||||
/* Read the 16 bytes of station address prom, returning 1 for
|
||||
an eight-bit interface and 2 for a 16-bit interface.
|
||||
We must first initialize registers, similar to NS8390_init(eifdev, 0).
|
||||
We can't reliably read the SAPROM address without this.
|
||||
(I learned the hard way!). */
|
||||
{
|
||||
struct {char value, offset; } program_seq[] = {
|
||||
{E8390_NODMA+E8390_PAGE0+E8390_STOP, EN_CMD}, /* Select page 0 */
|
||||
{0x48, EN0_DCFG}, /* Set byte-wide (0x48) access. */
|
||||
{0x00, EN0_RCNTLO}, /* Clear the count regs. */
|
||||
{0x00, EN0_RCNTHI},
|
||||
{0x00, EN0_IMR}, /* Mask completion irq. */
|
||||
{0xFF, EN0_ISR},
|
||||
{E8390_RXOFF, EN0_RXCR}, /* 0x20 Set to monitor */
|
||||
{E8390_TXOFF, EN0_TXCR}, /* 0x02 and loopback mode. */
|
||||
{32, EN0_RCNTLO},
|
||||
{0x00, EN0_RCNTHI},
|
||||
{0x00, EN0_RSARLO}, /* DMA starting at 0x0000. */
|
||||
{0x00, EN0_RSARHI},
|
||||
{E8390_RREAD+E8390_START, EN_CMD},
|
||||
};
|
||||
for (i = 0; i < sizeof(program_seq)/sizeof(program_seq[0]); i++)
|
||||
outb_p(program_seq[i].value, ioaddr + program_seq[i].offset);
|
||||
}
|
||||
for(i = 0; i < 32 /*sizeof(SA_prom)*/; i+=2) {
|
||||
SA_prom[i] = inb_p(ioaddr + NE_DATAPORT);
|
||||
SA_prom[i+1] = inb_p(ioaddr + NE_DATAPORT);
|
||||
if (SA_prom[i] != SA_prom[i+1])
|
||||
wordlength = 1;
|
||||
}
|
||||
|
||||
if (wordlength == 2) {
|
||||
/* We must set the 8390 for word mode, AND RESET IT. */
|
||||
int tmp;
|
||||
outb_p(0x49, ioaddr + EN0_DCFG);
|
||||
tmp = inb_p(NE_BASE + NE_RESET);
|
||||
ei_status.word16 = 1;
|
||||
outb(tmp, NE_BASE + NE_RESET);
|
||||
/* Un-double the SA_prom values. */
|
||||
for (i = 0; i < 16; i++)
|
||||
SA_prom[i] = SA_prom[i+i];
|
||||
} else
|
||||
ei_status.word16 = 0;
|
||||
|
||||
#if defined(show_all_SAPROM)
|
||||
/* If your ethercard isn't detected define this to see the SA_PROM. */
|
||||
for(i = 0; i < sizeof(SA_prom); i++)
|
||||
printk(" %2.2x", SA_prom[i]);
|
||||
#else
|
||||
for(i = 0; i < ETHER_ADDR_LEN; i++) {
|
||||
dev->dev_addr[i] = SA_prom[i];
|
||||
printk(" %2.2x", SA_prom[i]);
|
||||
}
|
||||
#endif
|
||||
|
||||
neX000 = (SA_prom[14] == 0x57 && SA_prom[15] == 0x57);
|
||||
ctron = (SA_prom[0] == 0x00 && SA_prom[1] == 0x00 && SA_prom[2] == 0x1d);
|
||||
dlink = (SA_prom[0] == 0x00 && SA_prom[1] == 0xDE && SA_prom[2] == 0x01);
|
||||
|
||||
/* Set up the rest of the parameters. */
|
||||
if (neX000 && wordlength == 2) {
|
||||
ei_status.name = "NE2000";
|
||||
ei_status.tx_start_page = NESM_START_PG;
|
||||
ei_status.stop_page = NESM_STOP_PG;
|
||||
} else if (neX000 || dlink) {
|
||||
ei_status.name = neX000 ? "NE1000" : "D-Link";
|
||||
ei_status.tx_start_page = NE1SM_START_PG;
|
||||
ei_status.stop_page = NE1SM_STOP_PG;
|
||||
} else if (ctron) {
|
||||
ei_status.name = "Cabletron";
|
||||
ei_status.tx_start_page = 0x01;
|
||||
ei_status.stop_page = (wordlength == 2) ? 0x40 : 0x20;
|
||||
} else {
|
||||
printk(" not found.\n");
|
||||
return 0;
|
||||
}
|
||||
ei_status.rx_start_page = ei_status.tx_start_page + TX_PAGES;
|
||||
#ifdef PACKETBUF_MEMSIZE
|
||||
/* Allow the packet buffer size to be overridden by know-it-alls. */
|
||||
ei_status.stop_page = ei_status.tx_start_page + PACKETBUF_MEMSIZE;
|
||||
#endif
|
||||
dev->base_addr = ioaddr;
|
||||
|
||||
if (dev->irq < 2) {
|
||||
int nic_base = dev->base_addr;
|
||||
autoirq_setup(0);
|
||||
outb_p(0x50, nic_base + EN0_IMR); /* Enable one interrupt. */
|
||||
outb_p(0x00, nic_base + EN0_RCNTLO);
|
||||
outb_p(0x00, nic_base + EN0_RCNTHI);
|
||||
outb_p(E8390_RREAD+E8390_START, nic_base); /* Trigger it... */
|
||||
outb_p(0x00, nic_base + EN0_IMR); /* Mask it again. */
|
||||
dev->irq = autoirq_report(0);
|
||||
if (ei_debug > 2)
|
||||
printk(" autoirq is %d", dev->irq);
|
||||
} else if (dev->irq == 2)
|
||||
/* Fixup for users that don't know that IRQ 2 is really IRQ 9,
|
||||
or don't know which one to set. */
|
||||
dev->irq = 9;
|
||||
|
||||
/* Snarf the interrupt now. There's no point in waiting since we cannot
|
||||
share and the board will usually be enabled. */
|
||||
{ int irqval = irqaction (dev->irq, &ei_sigaction);
|
||||
if (irqval) {
|
||||
printk (" unable to get IRQ %d (irqval=%d).\n", dev->irq, irqval);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
printk("\n%s: %s found, using IRQ %d.\n",
|
||||
dev->name, ei_status.name, dev->irq);
|
||||
if (ei_debug > 1)
|
||||
printk(version);
|
||||
ei_status.reset_8390 = &ne_reset_8390;
|
||||
ei_status.block_input = &ne_block_input;
|
||||
ei_status.block_output = &ne_block_output;
|
||||
NS8390_init(dev, 0);
|
||||
return dev->base_addr;
|
||||
}
|
||||
|
||||
static void
|
||||
ne_reset_8390(struct device *dev)
|
||||
{
|
||||
int tmp = inb_p(NE_BASE + NE_RESET);
|
||||
int reset_start_time = jiffies;
|
||||
|
||||
if (ei_debug > 1) printk("resetting the 8390 t=%d...", jiffies);
|
||||
ei_status.txing = 0;
|
||||
|
||||
sti();
|
||||
/* We shouldn't use the boguscount for timing, but this hasn't been
|
||||
checked yet, and you could hang your machine if jiffies break... */
|
||||
{
|
||||
int boguscount = 150000;
|
||||
while(jiffies - reset_start_time < 2)
|
||||
if (boguscount-- < 0) {
|
||||
printk("jiffy failure (t=%d)...", jiffies);
|
||||
outb_p(tmp, NE_BASE + NE_RESET);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
outb_p(tmp, NE_BASE + NE_RESET);
|
||||
while ((inb_p(NE_BASE+EN0_ISR) & ENISR_RESET) == 0)
|
||||
if (jiffies - reset_start_time > 2) {
|
||||
printk("%s: ne_reset_8390() did not complete.\n", dev->name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Block input and output, similar to the Crynwr packet driver. If you
|
||||
porting to a new ethercard look at the packet driver source for hints.
|
||||
The NEx000 doesn't share it on-board packet memory -- you have to put
|
||||
the packet out through the "remote DMA" dataport using outb. */
|
||||
|
||||
static int
|
||||
ne_block_input(struct device *dev, int count, char *buf, int ring_offset)
|
||||
{
|
||||
int xfer_count = count;
|
||||
int nic_base = NE_BASE;
|
||||
|
||||
outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, EN_CMD);
|
||||
outb_p(count & 0xff, nic_base + EN0_RCNTLO);
|
||||
outb_p(count >> 8, nic_base + EN0_RCNTHI);
|
||||
outb_p(ring_offset & 0xff, nic_base + EN0_RSARLO);
|
||||
outb_p(ring_offset >> 8, nic_base + EN0_RSARHI);
|
||||
outb_p(E8390_RREAD+E8390_START, EN_CMD);
|
||||
if (ei_status.word16) {
|
||||
port_read(NE_BASE + NE_DATAPORT,buf,count>>1);
|
||||
if (count & 0x01)
|
||||
buf[count-1] = inb(NE_BASE + NE_DATAPORT), xfer_count++;
|
||||
} else {
|
||||
port_read_b(NE_BASE + NE_DATAPORT, buf, count);
|
||||
}
|
||||
|
||||
/* This was for the ALPHA version only, but enough people have
|
||||
encountering problems that it is still here. */
|
||||
if (ei_debug > 0) { /* DMA termination address check... */
|
||||
int addr, tries = 10;
|
||||
do {
|
||||
/* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here
|
||||
-- it's broken! Check the "DMA" address instead. */
|
||||
int high = inb_p(nic_base + EN0_RSARHI);
|
||||
int low = inb_p(nic_base + EN0_RSARLO);
|
||||
addr = (high << 8) + low;
|
||||
if (((ring_offset + xfer_count) & 0xff) == low)
|
||||
return ring_offset + count;
|
||||
} while (--tries > 0);
|
||||
printk("%s: RX transfer address mismatch, %#4.4x (should be) vs. %#4.4x (actual).\n",
|
||||
dev->name, ring_offset + xfer_count, addr);
|
||||
}
|
||||
return ring_offset + count;
|
||||
}
|
||||
|
||||
static void
|
||||
ne_block_output(struct device *dev, int count,
|
||||
const unsigned char *buf, const int start_page)
|
||||
{
|
||||
int retries = 0;
|
||||
int nic_base = NE_BASE;
|
||||
|
||||
/* Round the count up for word writes. Do we need to do this?
|
||||
What effect will an odd byte count have on the 8390?
|
||||
I should check someday. */
|
||||
if (ei_status.word16 && (count & 0x01))
|
||||
count++;
|
||||
/* We should already be in page 0, but to be safe... */
|
||||
outb_p(E8390_PAGE0+E8390_START+E8390_NODMA, EN_CMD);
|
||||
|
||||
retry:
|
||||
#if defined(rw_bugfix)
|
||||
/* Handle the read-before-write bug the same way as the
|
||||
Crynwr packet driver -- the NatSemi method doesn't work.
|
||||
Actually this doesn't aways work either, but if you have
|
||||
problems with your NEx000 this is better than nothing! */
|
||||
outb_p(0x42, nic_base + EN0_RCNTLO);
|
||||
outb_p(0x00, nic_base + EN0_RCNTHI);
|
||||
outb_p(0x42, nic_base + EN0_RSARLO);
|
||||
outb_p(0x00, nic_base + EN0_RSARHI);
|
||||
outb_p(E8390_RREAD+E8390_START, EN_CMD);
|
||||
/* Make certain that the dummy read has occured. */
|
||||
SLOW_DOWN_IO;
|
||||
SLOW_DOWN_IO;
|
||||
SLOW_DOWN_IO;
|
||||
#endif /* rw_bugfix */
|
||||
|
||||
/* Now the normal output. */
|
||||
outb_p(count & 0xff, nic_base + EN0_RCNTLO);
|
||||
outb_p(count >> 8, nic_base + EN0_RCNTHI);
|
||||
outb_p(0x00, nic_base + EN0_RSARLO);
|
||||
outb_p(start_page, nic_base + EN0_RSARHI);
|
||||
|
||||
outb_p(E8390_RWRITE+E8390_START, EN_CMD);
|
||||
if (ei_status.word16) {
|
||||
port_write(NE_BASE + NE_DATAPORT, buf, count>>1);
|
||||
} else {
|
||||
port_write_b(NE_BASE + NE_DATAPORT, buf, count);
|
||||
}
|
||||
|
||||
/* This was for the ALPHA version only, but enough people have
|
||||
encountering problems that it is still here. */
|
||||
if (ei_debug > 0) { /* DMA termination address check... */
|
||||
int addr, tries = 10;
|
||||
do {
|
||||
/* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here
|
||||
-- it's broken! Check the "DMA" address instead. */
|
||||
int high = inb_p(nic_base + EN0_RSARHI);
|
||||
int low = inb_p(nic_base + EN0_RSARLO);
|
||||
addr = (high << 8) + low;
|
||||
if ((start_page << 8) + count == addr)
|
||||
return;
|
||||
} while (--tries > 0);
|
||||
printk("%s: Packet buffer transfer address mismatch on TX, %#4.4x vs. %#4.4x.\n",
|
||||
dev->name, (start_page << 8) + count, addr);
|
||||
if (retries++ == 0)
|
||||
goto retry;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -I/usr/src/linux/net/tcp -c ne.c"
|
||||
* version-control: t
|
||||
* kept-new-versions: 5
|
||||
* End:
|
||||
*/
|
||||
334
net/tcpip/newether-pingpong/wd.c
Normal file
334
net/tcpip/newether-pingpong/wd.c
Normal file
@@ -0,0 +1,334 @@
|
||||
/* wd.c: A WD80x3 ethernet driver for linux. */
|
||||
/*
|
||||
Written 1993 by Donald Becker. This is alpha test code.
|
||||
This is a extension to the Linux operating system, and is covered by
|
||||
same Gnu Public License that covers that work.
|
||||
|
||||
This is a driver for the WD8003 and WD8013 ethercards.
|
||||
|
||||
The Author may be reached as becker@super.org or
|
||||
C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
|
||||
|
||||
Thanks to Russ Nelson (nelson@crnwyr.com) for loaning me a WD8013.
|
||||
*/
|
||||
|
||||
static char *version =
|
||||
"wd.c:v0.66 3/7/93 Donald Becker (becker@super.org)\n";
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sched.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/system.h>
|
||||
#include <memory.h>
|
||||
|
||||
#include "dev.h"
|
||||
#include "8390.h"
|
||||
|
||||
|
||||
extern void NS8390_init(struct device *dev, int startp);
|
||||
extern int ei_debug;
|
||||
extern struct sigaction ei_sigaction;
|
||||
|
||||
int wdprobe(int ioaddr, struct device *dev);
|
||||
int wdprobe1(int ioaddr, struct device *dev);
|
||||
|
||||
static void wd_reset_8390(struct device *dev);
|
||||
static int wd_block_input(struct device *dev, int count,
|
||||
char *buf, int ring_offset);
|
||||
static void wd_block_output(struct device *dev, int count,
|
||||
const unsigned char *buf, const start_page);
|
||||
static int wd_close_card(struct device *dev);
|
||||
|
||||
|
||||
#define WD_START_PG 0x00 /* First page of TX buffer */
|
||||
#define WD03_STOP_PG 0x20 /* Last page +1 of RX ring */
|
||||
#define WD13_STOP_PG 0x40 /* Last page +1 of RX ring */
|
||||
|
||||
#define WD_CMDREG 0 /* Offset to ASIC command register. */
|
||||
#define WD_RESET 0x80 /* Board reset, in WD_CMDREG. */
|
||||
#define WD_MEMENB 0x40 /* Enable the shared memory. */
|
||||
#define WD_CMDREG5 5 /* Offset to 16-bit-only ASIC register 5. */
|
||||
#define ISA16 0x80 /* Enable 16 bit access from the ISA bus. */
|
||||
#define NIC16 0x40 /* Enable 16 bit access from the 8390. */
|
||||
#define WD_NIC_OFFSET 16 /* Offset to the 8390 NIC from the base_addr. */
|
||||
|
||||
/* Probe for the WD8003 and WD8013. These cards have the station
|
||||
address PROM at I/O ports <base>+8 to <base>+13, with a checksum
|
||||
following. The routine also initializes the card and fills the
|
||||
station address field. */
|
||||
|
||||
int wdprobe(int ioaddr, struct device *dev)
|
||||
{
|
||||
int *port, ports[] = {0x300, 0x280, 0};
|
||||
|
||||
if (ioaddr > 0x100)
|
||||
return wdprobe1(ioaddr, dev);
|
||||
|
||||
for (port = &ports[0]; *port; port++)
|
||||
if (inb(*port) != 0xff && wdprobe1(*port, dev))
|
||||
return *port;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wdprobe1(int ioaddr, struct device *dev)
|
||||
{
|
||||
int i;
|
||||
unsigned char *station_addr = dev->dev_addr;
|
||||
int checksum = 0;
|
||||
int ancient = 0; /* An old card without config registers. */
|
||||
|
||||
#if defined(EI_DEBUG) && EI_DEBUG > 2
|
||||
printk("WD80x3 ethercard at %#3x:", ioaddr);
|
||||
for (i = 0; i < 16; i++) {
|
||||
printk(" %2.2X", inb(ioaddr+i));
|
||||
}
|
||||
printk("\n");
|
||||
printk("WD80x3 ethercard at %#3x:", ioaddr+i);
|
||||
for (;i < 33; i++) {
|
||||
printk(" %2.2X", inb(ioaddr+i));
|
||||
}
|
||||
printk("\n");
|
||||
#endif
|
||||
printk("WD80x3 ethercard probe at %#3x:", ioaddr);
|
||||
for (i = 0; i < 8; i++) {
|
||||
int inval = inb(ioaddr + 8 + i);
|
||||
checksum += inval;
|
||||
if (i < 6)
|
||||
printk(" %2.2X", (station_addr[i] = inval));
|
||||
}
|
||||
|
||||
if ((checksum & 0xff) != 0xFF) {
|
||||
printk(" not found (%#2.2x).\n", checksum);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ei_status.name = "WD8003";
|
||||
ei_status.word16 = 0;
|
||||
|
||||
/* This method of checking for a 16-bit board is borrowed from the
|
||||
we.c driver. A simpler method is just to look in ASIC reg. 0x03.
|
||||
I'm comparing the two method in alpha test to make certain they
|
||||
return the same result. */
|
||||
#ifndef FORCE_8BIT /* Same define as we.c. */
|
||||
/* check for 16 bit board - it doesn't have register 0/8 aliasing */
|
||||
for (i = 0; i < 8; i++)
|
||||
if (inb(ioaddr+i) != inb(ioaddr+8+i))
|
||||
break;
|
||||
if (i != 8) {
|
||||
int tmp = inb(ioaddr+1); /* fiddle with 16bit bit */
|
||||
outb( tmp ^ 0x01, ioaddr+1 ); /* attempt to clear 16bit bit */
|
||||
if (((inb( ioaddr+1) & 0x01) == 0x01) /* A 16 bit card */
|
||||
&& (tmp & 0x01) == 0x01 ) { /* In a 16 slot. */
|
||||
int asic_reg5 = inb(ioaddr+WD_CMDREG5);
|
||||
/* Magic to set ASIC to word-wide mode. */
|
||||
outb( ISA16 | NIC16 | (asic_reg5&0x1f), ioaddr+WD_CMDREG5);
|
||||
outb(tmp, ioaddr+1);
|
||||
ei_status.name = "WD8013";
|
||||
ei_status.word16 = 1; /* We have a 16bit board here! */
|
||||
}
|
||||
outb(tmp, ioaddr+1); /* Restore original reg1 value. */
|
||||
} else
|
||||
ancient = 1;
|
||||
#endif /* not FORCE_8BIT */
|
||||
|
||||
#ifndef final_version
|
||||
if ( !ancient && (inb(ioaddr+1) & 0x01) != (ei_status.word16 & 0x01))
|
||||
printk("\nWD80?3: Bus width conflict, %d (probe) != %d (reg report).",
|
||||
ei_status.word16 ? 16:8, (inb(ioaddr+1) & 0x01) ? 16 : 8);
|
||||
#endif
|
||||
|
||||
#if defined(WD_SHMEM) && WD_SHMEM > 0x80000
|
||||
/* Allow an override. */
|
||||
dev->mem_start = WD_SHMEM;
|
||||
#else
|
||||
if (dev->mem_start == 0) {
|
||||
dev->mem_start = ((inb(ioaddr)&0x3f) << 13) +
|
||||
(ei_status.word16 ? (inb(ioaddr+WD_CMDREG5)&0x1f)<<19 : 0x80000);
|
||||
if (dev->mem_start < 0xc0000) /* Sanity and old 8003 check */
|
||||
dev->mem_start = 0xd0000;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* The 8390 isn't at the base address -- the ASIC regs are there! */
|
||||
dev->base_addr = ioaddr+WD_NIC_OFFSET;
|
||||
|
||||
ei_status.tx_start_page = WD_START_PG;
|
||||
ei_status.rx_start_page = WD_START_PG + TX_PAGES;
|
||||
ei_status.stop_page = ei_status.word16 ? WD13_STOP_PG : WD03_STOP_PG;
|
||||
|
||||
dev->rmem_start = dev->mem_start + TX_PAGES*256;
|
||||
dev->mem_end = dev->rmem_end
|
||||
= dev->mem_start + (ei_status.stop_page - WD_START_PG)*256;
|
||||
#if defined(EI_DEBUG) && EI_DEBUG > 3
|
||||
memset((void*)dev->mem_start, 0x42052465,
|
||||
(ei_status.stop_page - WD_START_PG)*256);
|
||||
#endif
|
||||
if (dev->irq < 2) {
|
||||
int irqmap[] = {9,3,5,7,10,11,15,4};
|
||||
dev->irq = irqmap[((inb(ioaddr+4) >> 5) & 0x03)
|
||||
+ (inb(ioaddr+1) & 0x04)];
|
||||
} else if (dev->irq == 2)
|
||||
/* Fixup for users that don't know that IRQ 2 is really IRQ 9,
|
||||
or don't know which one to set. */
|
||||
dev->irq = 9;
|
||||
|
||||
/* Snarf the interrupt now. There's no point in waiting since we cannot
|
||||
share and the board will usually be enabled. */
|
||||
{ int irqval = irqaction (dev->irq, &ei_sigaction);
|
||||
if (irqval) {
|
||||
printk (" unable to get IRQ %d (irqval=%d).\n", dev->irq, irqval);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
printk("\n%s: %s using IRQ %d with shared memory at %#x-%#x.\n",
|
||||
dev->name, ei_status.name, dev->irq, dev->mem_start, dev->mem_end-1);
|
||||
if (ei_debug > 1)
|
||||
printk(version);
|
||||
|
||||
if (ei_debug > 2)
|
||||
printk("%s: Address read from register is %#x, setting address %#x\n",
|
||||
ei_status.name,
|
||||
((inb(ioaddr+WD_CMDREG5)&0x1f)<<19) + ((inb(ioaddr)&0x3f) << 13),
|
||||
dev->mem_start);
|
||||
/* Map in the shared memory. This is a little risky, since using
|
||||
the stuff the user supplied is probably a bad idea. */
|
||||
outb((((dev->mem_start>>13) & 0x3f)|WD_MEMENB), ioaddr); /* WD_CMDREG */
|
||||
if (ei_status.word16)
|
||||
outb( ISA16 | NIC16 | ((dev->mem_start>>19) & 0x1f), ioaddr+WD_CMDREG5);
|
||||
|
||||
ei_status.reset_8390 = &wd_reset_8390;
|
||||
ei_status.block_input = &wd_block_input;
|
||||
ei_status.block_output = &wd_block_output;
|
||||
dev->stop = &wd_close_card;
|
||||
NS8390_init(dev, 0);
|
||||
|
||||
return dev->base_addr;
|
||||
}
|
||||
|
||||
static void
|
||||
wd_reset_8390(struct device *dev)
|
||||
{
|
||||
int wd_cmd_port = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
|
||||
int reset_start_time = jiffies;
|
||||
|
||||
outb(WD_RESET, wd_cmd_port);
|
||||
if (ei_debug > 1) printk("resetting the WD80x3 t=%d...", jiffies);
|
||||
ei_status.txing = 0;
|
||||
|
||||
sti();
|
||||
/* We shouldn't use the boguscount for timing, but this hasn't been
|
||||
checked yet, and you could hang your machine if jiffies break... */
|
||||
{
|
||||
int boguscount = 150000;
|
||||
while(jiffies - reset_start_time < 2)
|
||||
if (boguscount-- < 0) {
|
||||
printk("jiffy failure (t=%d)...", jiffies);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
outb(0x00, wd_cmd_port);
|
||||
while ((inb(dev->base_addr+EN0_ISR) & ENISR_RESET) == 0)
|
||||
if (jiffies - reset_start_time > 2) {
|
||||
printk("%s: wd_reset_8390() did not complete.\n", dev->name);
|
||||
break;
|
||||
}
|
||||
#if defined(EI_DEBUG) && EI_DEBUG > 2
|
||||
{
|
||||
int i;
|
||||
printk("WD80x3 ethercard at %#3x:", wd_cmd_port);
|
||||
for (i = 0; i < 16; i++) {
|
||||
printk(" %2.2X", inb(wd_cmd_port+i));
|
||||
}
|
||||
printk("\nWD80x3 ethercard at %#3x:", wd_cmd_port);
|
||||
for (;i < 33; i++) {
|
||||
printk(" %2.2X", inb(wd_cmd_port+i));
|
||||
}
|
||||
printk("\n");
|
||||
}
|
||||
#endif
|
||||
/* Set up the ASIC registers, just in case something changed them. */
|
||||
outb((((dev->mem_start>>13) & 0x3f)|WD_MEMENB), wd_cmd_port); /* WD_CMDREG */
|
||||
if (ei_status.word16)
|
||||
outb( ISA16 | NIC16 | ((dev->mem_start>>19) & 0x1f), wd_cmd_port+WD_CMDREG5);
|
||||
|
||||
}
|
||||
|
||||
/* Block input and output are easy on shared memory ethercards, and trivial
|
||||
on the Western digital card where there is no choice of how to do it. */
|
||||
|
||||
static int
|
||||
wd_block_input(struct device *dev, int count, char *buf, int ring_offset)
|
||||
{
|
||||
void *xfer_start = (void *)(dev->mem_start + ring_offset - (WD_START_PG<<8));
|
||||
#ifdef mapout
|
||||
int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
|
||||
int mem_val = inb(wd_cmdreg);
|
||||
/* Map in the shared memory. */
|
||||
outb(mem_val|WD_MEMENB, wd_cmdreg);
|
||||
#endif
|
||||
if (xfer_start + count > (void*) dev->rmem_end) {
|
||||
/* We must wrap the input move. */
|
||||
int semi_count = (void*)dev->rmem_end - xfer_start;
|
||||
memcpy(buf, xfer_start, semi_count);
|
||||
count -= semi_count;
|
||||
memcpy(buf + semi_count, (char *)dev->rmem_start, count);
|
||||
return dev->rmem_start + count;
|
||||
}
|
||||
memcpy(buf, xfer_start, count);
|
||||
if (ei_debug > 4) {
|
||||
unsigned short *board = xfer_start;
|
||||
printk("%s: wd8013 block_input(cnt=%d offset=%3x addr=%#x) = %2x %2x %2x...\n",
|
||||
dev->name, count, ring_offset, xfer_start,
|
||||
board[-1], board[0], board[1]);
|
||||
}
|
||||
#ifdef mapout
|
||||
outb(mem_val & ~WD_MEMENB, wd_cmdreg); /* WD_CMDREG: Map out the shared memory. */
|
||||
#endif
|
||||
return ring_offset + count;
|
||||
}
|
||||
|
||||
/* This could only be outputting to the transmit buffer. The
|
||||
ping-pong transmit setup doesn't work with this yet. */
|
||||
static void
|
||||
wd_block_output(struct device *dev, int count, const unsigned char *buf, int start_page)
|
||||
{
|
||||
unsigned char *shmem = (void *)dev->mem_start + ((start_page - WD_START_PG)<<8);
|
||||
#ifdef mapout
|
||||
int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
|
||||
int mem_val = inb(wd_cmdreg);
|
||||
/* Map in the shared memory. */
|
||||
outb(mem_val|WD_MEMENB, wd_cmdreg);
|
||||
#endif
|
||||
memcpy(shmem, buf, count);
|
||||
if (ei_debug > 4)
|
||||
printk("%s: wd80*3 block_output(addr=%#x cnt=%d) -> %2x=%2x %2x=%2x %d...\n",
|
||||
shmem, count, shmem[23], buf[23], shmem[24], buf[24], memcmp(shmem,buf,count));
|
||||
#ifdef mapout
|
||||
outb(mem_val & ~WD_MEMENB, wd_cmdreg); /* WD_CMDREG: Map out the shared memory. */
|
||||
#endif
|
||||
}
|
||||
|
||||
/* This function resets the ethercard if something screws up. */
|
||||
static int
|
||||
wd_close_card(struct device *dev)
|
||||
{
|
||||
if (ei_debug > 1)
|
||||
printk("%s: Shutting down ethercard.\n", dev->name);
|
||||
NS8390_init(dev, 0);
|
||||
/* Turn off the shared memory. */
|
||||
outb((((dev->mem_start>>13) & 0x3f)),
|
||||
dev->base_addr-WD_NIC_OFFSET); /* WD_CMDREG */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -I/usr/src/linux/net/tcp -c wd.c"
|
||||
* version-control: t
|
||||
* kept-new-versions: 5
|
||||
* End:
|
||||
*/
|
||||
Reference in New Issue
Block a user