From f07a4c844da9f0ecae5bbee1ab94be56505f26f7 Mon Sep 17 00:00:00 2001 From: Thomas Bushnell Date: Tue, 25 Feb 1997 21:28:37 +0000 Subject: Initial source --- i386/i386at/gpl/linux/net/ewrk3.c | 1933 +++++++++++++++++++++++++++++++++++++ 1 file changed, 1933 insertions(+) create mode 100644 i386/i386at/gpl/linux/net/ewrk3.c (limited to 'i386/i386at/gpl/linux/net/ewrk3.c') diff --git a/i386/i386at/gpl/linux/net/ewrk3.c b/i386/i386at/gpl/linux/net/ewrk3.c new file mode 100644 index 0000000..90e3b93 --- /dev/null +++ b/i386/i386at/gpl/linux/net/ewrk3.c @@ -0,0 +1,1933 @@ +/* ewrk3.c: A DIGITAL EtherWORKS 3 ethernet driver for Linux. + + Written 1994 by David C. Davies. + + Copyright 1994 Digital Equipment Corporation. + + This software may be used and distributed according to the terms of + the GNU Public License, incorporated herein by reference. + + This driver is written for the Digital Equipment Corporation series + of EtherWORKS ethernet cards: + + DE203 Turbo (BNC) + DE204 Turbo (TP) + DE205 Turbo (TP BNC) + + The driver has been tested on a relatively busy network using the DE205 + card and benchmarked with 'ttcp': it transferred 16M of data at 975kB/s + (7.8Mb/s) to a DECstation 5000/200. + + The author may be reached at davies@wanton.lkg.dec.com or + davies@maniac.ultranet.com or Digital Equipment Corporation, 550 King + Street, Littleton MA 01460. + + ========================================================================= + This driver has been written substantially from scratch, although its + inheritance of style and stack interface from 'depca.c' and in turn from + Donald Becker's 'lance.c' should be obvious. + + The DE203/4/5 boards all use a new proprietary chip in place of the + LANCE chip used in prior cards (DEPCA, DE100, DE200/1/2, DE210, DE422). + Use the depca.c driver in the standard distribution for the LANCE based + cards from DIGITAL; this driver will not work with them. + + The DE203/4/5 cards have 2 main modes: shared memory and I/O only. I/O + only makes all the card accesses through I/O transactions and no high + (shared) memory is used. This mode provides a >48% performance penalty + and is deprecated in this driver, although allowed to provide initial + setup when hardstrapped. + + The shared memory mode comes in 3 flavours: 2kB, 32kB and 64kB. There is + no point in using any mode other than the 2kB mode - their performances + are virtually identical, although the driver has been tested in the 2kB + and 32kB modes. I would suggest you uncomment the line: + + FORCE_2K_MODE; + + to allow the driver to configure the card as a 2kB card at your current + base address, thus leaving more room to clutter your system box with + other memory hungry boards. + + As many ISA and EISA cards can be supported under this driver as you + wish, limited primarily by the available IRQ lines, rather than by the + available I/O addresses (24 ISA, 16 EISA). I have checked different + configurations of multiple depca cards and ewrk3 cards and have not + found a problem yet (provided you have at least depca.c v0.38) ... + + The board IRQ setting must be at an unused IRQ which is auto-probed + using Donald Becker's autoprobe routines. All these cards are at + {5,10,11,15}. + + No 16MB memory limitation should exist with this driver as DMA is not + used and the common memory area is in low memory on the network card (my + current system has 20MB and I've not had problems yet). + + The ability to load this driver as a loadable module has been included + and used extensively during the driver development (to save those long + reboot sequences). To utilise this ability, you have to do 8 things: + + 0) have a copy of the loadable modules code installed on your system. + 1) copy ewrk3.c from the /linux/drivers/net directory to your favourite + temporary directory. + 2) edit the source code near line 1880 to reflect the I/O address and + IRQ you're using. + 3) compile ewrk3.c, but include -DMODULE in the command line to ensure + that the correct bits are compiled (see end of source code). + 4) if you are wanting to add a new card, goto 5. Otherwise, recompile a + kernel with the ewrk3 configuration turned off and reboot. + 5) insmod ewrk3.o + [Alan Cox: Changed this so you can insmod ewrk3.o irq=x io=y] + 6) run the net startup bits for your new eth?? interface manually + (usually /etc/rc.inet[12] at boot time). + 7) enjoy! + + Note that autoprobing is not allowed in loadable modules - the system is + already up and running and you're messing with interrupts. + + To unload a module, turn off the associated interface + 'ifconfig eth?? down' then 'rmmod ewrk3'. + + Promiscuous mode has been turned off in this driver, but all the + multicast address bits have been turned on. This improved the send + performance on a busy network by about 13%. + + Ioctl's have now been provided (primarily because I wanted to grab some + packet size statistics). They are patterned after 'plipconfig.c' from a + suggestion by Alan Cox. Using these ioctls, you can enable promiscuous + mode, add/delete multicast addresses, change the hardware address, get + packet size distribution statistics and muck around with the control and + status register. I'll add others if and when the need arises. + + TO DO: + ------ + + + Revision History + ---------------- + + Version Date Description + + 0.1 26-aug-94 Initial writing. ALPHA code release. + 0.11 31-aug-94 Fixed: 2k mode memory base calc., + LeMAC version calc., + IRQ vector assignments during autoprobe. + 0.12 31-aug-94 Tested working on LeMAC2 (DE20[345]-AC) card. + Fixed up MCA hash table algorithm. + 0.20 4-sep-94 Added IOCTL functionality. + 0.21 14-sep-94 Added I/O mode. + 0.21axp 15-sep-94 Special version for ALPHA AXP Linux V1.0. + 0.22 16-sep-94 Added more IOCTLs & tidied up. + 0.23 21-sep-94 Added transmit cut through. + 0.24 31-oct-94 Added uid checks in some ioctls. + 0.30 1-nov-94 BETA code release. + 0.31 5-dec-94 Added check/allocate region code. + 0.32 16-jan-95 Broadcast packet fix. + 0.33 10-Feb-95 Fix recognition bug reported by . + 0.40 27-Dec-95 Rationalise MODULE and autoprobe code. + Rewrite for portability & updated. + ALPHA support from + Added verify_area() calls in depca_ioctl() from + suggestion by . + Add new multicasting code. + + ========================================================================= +*/ + +static const char *version = "ewrk3.c:v0.40 95/12/27 davies@wanton.lkg.dec.com\n"; + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "ewrk3.h" + +#ifdef EWRK3_DEBUG +static int ewrk3_debug = EWRK3_DEBUG; +#else +static int ewrk3_debug = 1; +#endif + +#define EWRK3_NDA 0xffe0 /* No Device Address */ + +#define PROBE_LENGTH 32 +#define ETH_PROM_SIG 0xAA5500FFUL + +#ifndef EWRK3_SIGNATURE +#define EWRK3_SIGNATURE {"DE203","DE204","DE205",""} +#define EWRK3_STRLEN 8 +#endif + +#ifndef EWRK3_RAM_BASE_ADDRESSES +#define EWRK3_RAM_BASE_ADDRESSES {0xc0000,0xd0000,0x00000} +#endif + +/* +** Sets up the I/O area for the autoprobe. +*/ +#define EWRK3_IO_BASE 0x100 /* Start address for probe search */ +#define EWRK3_IOP_INC 0x20 /* I/O address increment */ +#define EWRK3_TOTAL_SIZE 0x20 /* required I/O address length */ + +#ifndef MAX_NUM_EWRK3S +#define MAX_NUM_EWRK3S 21 +#endif + +#ifndef EWRK3_EISA_IO_PORTS +#define EWRK3_EISA_IO_PORTS 0x0c00 /* I/O port base address, slot 0 */ +#endif + +#ifndef MAX_EISA_SLOTS +#define MAX_EISA_SLOTS 16 +#define EISA_SLOT_INC 0x1000 +#endif + +#define CRC_POLYNOMIAL_BE 0x04c11db7UL /* Ethernet CRC, big endian */ +#define CRC_POLYNOMIAL_LE 0xedb88320UL /* Ethernet CRC, little endian */ + +#define QUEUE_PKT_TIMEOUT (100) /* Jiffies */ + +/* +** EtherWORKS 3 shared memory window sizes +*/ +#define IO_ONLY 0x00 +#define SHMEM_2K 0x800 +#define SHMEM_32K 0x8000 +#define SHMEM_64K 0x10000 + +/* +** EtherWORKS 3 IRQ ENABLE/DISABLE +*/ +#define ENABLE_IRQs { \ + icr |= lp->irq_mask;\ + outb(icr, EWRK3_ICR); /* Enable the IRQs */\ +} + +#define DISABLE_IRQs { \ + icr = inb(EWRK3_ICR);\ + icr &= ~lp->irq_mask;\ + outb(icr, EWRK3_ICR); /* Disable the IRQs */\ +} + +/* +** EtherWORKS 3 START/STOP +*/ +#define START_EWRK3 { \ + csr = inb(EWRK3_CSR);\ + csr &= ~(CSR_TXD|CSR_RXD);\ + outb(csr, EWRK3_CSR); /* Enable the TX and/or RX */\ +} + +#define STOP_EWRK3 { \ + csr = (CSR_TXD|CSR_RXD);\ + outb(csr, EWRK3_CSR); /* Disable the TX and/or RX */\ +} + +/* +** The EtherWORKS 3 private structure +*/ +#define EWRK3_PKT_STAT_SZ 16 +#define EWRK3_PKT_BIN_SZ 128 /* Should be >=100 unless you + increase EWRK3_PKT_STAT_SZ */ + +struct ewrk3_private { + char adapter_name[80]; /* Name exported to /proc/ioports */ + u_long shmem_base; /* Shared memory start address */ + u_long shmem_length; /* Shared memory window length */ + struct enet_statistics stats; /* Public stats */ + struct { + u32 bins[EWRK3_PKT_STAT_SZ]; /* Private stats counters */ + u32 unicast; + u32 multicast; + u32 broadcast; + u32 excessive_collisions; + u32 tx_underruns; + u32 excessive_underruns; + } pktStats; + u_char irq_mask; /* Adapter IRQ mask bits */ + u_char mPage; /* Maximum 2kB Page number */ + u_char lemac; /* Chip rev. level */ + u_char hard_strapped; /* Don't allow a full open */ + u_char lock; /* Lock the page register */ + u_char txc; /* Transmit cut through */ + u_char *mctbl; /* Pointer to the multicast table */ +}; + +/* +** Force the EtherWORKS 3 card to be in 2kB MODE +*/ +#define FORCE_2K_MODE { \ + shmem_length = SHMEM_2K;\ + outb(((mem_start - 0x80000) >> 11), EWRK3_MBR);\ +} + +/* +** Public Functions +*/ +static int ewrk3_open(struct device *dev); +static int ewrk3_queue_pkt(struct sk_buff *skb, struct device *dev); +static void ewrk3_interrupt(int irq, struct pt_regs *regs); +static int ewrk3_close(struct device *dev); +static struct enet_statistics *ewrk3_get_stats(struct device *dev); +static void set_multicast_list(struct device *dev); +static int ewrk3_ioctl(struct device *dev, struct ifreq *rq, int cmd); + +/* +** Private functions +*/ +static int ewrk3_hw_init(struct device *dev, u_long iobase); +static void ewrk3_init(struct device *dev); +static int ewrk3_rx(struct device *dev); +static int ewrk3_tx(struct device *dev); + +static void EthwrkSignature(char * name, char *eeprom_image); +static int DevicePresent(u_long iobase); +static void SetMulticastFilter(struct device *dev); +static int EISA_signature(char *name, s32 eisa_id); + +static int Read_EEPROM(u_long iobase, u_char eaddr); +static int Write_EEPROM(short data, u_long iobase, u_char eaddr); +static u_char get_hw_addr (struct device *dev, u_char *eeprom_image, char chipType); + +static void isa_probe(struct device *dev, u_long iobase); +static void eisa_probe(struct device *dev, u_long iobase); +static struct device *alloc_device(struct device *dev, u_long iobase); + + +#ifdef MODULE +int init_module(void); +void cleanup_module(void); +static int autoprobed = 1, loading_module = 1; + +# else +static u_char irq[] = {5,0,10,3,11,9,15,12}; +static int autoprobed = 0, loading_module = 0; + +#endif /* MODULE */ + +static char name[EWRK3_STRLEN + 1]; +static int num_ewrk3s = 0, num_eth = 0; + +/* +** Miscellaneous defines... +*/ +#define INIT_EWRK3 {\ + outb(EEPROM_INIT, EWRK3_IOPR);\ + udelay(1000);\ +} + + + + +int ewrk3_probe(struct device *dev) +{ + int tmp = num_ewrk3s, status = -ENODEV; + u_long iobase = dev->base_addr; + + if ((iobase == 0) && loading_module){ + printk("Autoprobing is not supported when loading a module based driver.\n"); + status = -EIO; + } else { /* First probe for the Ethernet */ + /* Address PROM pattern */ + isa_probe(dev, iobase); + eisa_probe(dev, iobase); + + if ((tmp == num_ewrk3s) && (iobase != 0) && loading_module) { + printk("%s: ewrk3_probe() cannot find device at 0x%04lx.\n", dev->name, + iobase); + } + + /* + ** Walk the device list to check that at least one device + ** initialised OK + */ + for (; (dev->priv == NULL) && (dev->next != NULL); dev = dev->next); + + if (dev->priv) status = 0; + if (iobase == 0) autoprobed = 1; + } + + return status; +} + +static int +ewrk3_hw_init(struct device *dev, u_long iobase) +{ + struct ewrk3_private *lp; + int i, status=0; + u_long mem_start, shmem_length; + u_char cr, cmr, icr, nicsr, lemac, hard_strapped = 0; + u_char eeprom_image[EEPROM_MAX], chksum, eisa_cr = 0; + + /* + ** Stop the EWRK3. Enable the DBR ROM. Disable interrupts and remote boot. + ** This also disables the EISA_ENABLE bit in the EISA Control Register. + */ + if (iobase > 0x400) eisa_cr = inb(EISA_CR); + INIT_EWRK3; + + nicsr = inb(EWRK3_CSR); + + icr = inb(EWRK3_ICR); + icr |= 0xf0; + outb(icr, EWRK3_ICR); /* Disable all the IRQs */ + + if (nicsr == CSR_TXD|CSR_RXD) { + + /* Check that the EEPROM is alive and well and not living on Pluto... */ + for (chksum=0, i=0; i>1)); + eeprom_image[i] = tmp.c[0]; + eeprom_image[i+1] = tmp.c[1]; + chksum += eeprom_image[i] + eeprom_image[i+1]; + } + + if (chksum != 0) { /* Bad EEPROM Data! */ + printk("%s: Device has a bad on-board EEPROM.\n", dev->name); + status = -ENXIO; + } else { + EthwrkSignature(name, eeprom_image); + if (*name != '\0') { /* found a EWRK3 device */ + dev->base_addr = iobase; + + if (iobase > 0x400) { + outb(eisa_cr, EISA_CR); /* Rewrite the EISA CR */ + } + + lemac = eeprom_image[EEPROM_CHIPVER]; + cmr = inb(EWRK3_CMR); + + if (((lemac == LeMAC) && ((cmr & CMR_NO_EEPROM) != CMR_NO_EEPROM)) || + ((lemac == LeMAC2) && !(cmr & CMR_HS))) { + printk("%s: %s at %#4lx", dev->name, name, iobase); + hard_strapped = 1; + } else if ((iobase&0x0fff)==EWRK3_EISA_IO_PORTS) { + /* EISA slot address */ + printk("%s: %s at %#4lx (EISA slot %ld)", + dev->name, name, iobase, ((iobase>>12)&0x0f)); + } else { /* ISA port address */ + printk("%s: %s at %#4lx", dev->name, name, iobase); + } + + if (!status) { + printk(", h/w address "); + if (lemac!=LeMAC2) DevicePresent(iobase);/* need after EWRK3_INIT */ + status = get_hw_addr(dev, eeprom_image, lemac); + for (i = 0; i < ETH_ALEN - 1; i++) { /* get the ethernet addr. */ + printk("%2.2x:", dev->dev_addr[i]); + } + printk("%2.2x,\n", dev->dev_addr[i]); + + if (status) { + printk(" which has an EEPROM CRC error.\n"); + status = -ENXIO; + } else { + if (lemac == LeMAC2) { /* Special LeMAC2 CMR things */ + cmr &= ~(CMR_RA | CMR_WB | CMR_LINK | CMR_POLARITY | CMR_0WS); + if (eeprom_image[EEPROM_MISC0] & READ_AHEAD) cmr |= CMR_RA; + if (eeprom_image[EEPROM_MISC0] & WRITE_BEHIND) cmr |= CMR_WB; + if (eeprom_image[EEPROM_NETMAN0] & NETMAN_POL) cmr |= CMR_POLARITY; + if (eeprom_image[EEPROM_NETMAN0] & NETMAN_LINK) cmr |= CMR_LINK; + if (eeprom_image[EEPROM_MISC0] & _0WS_ENA) cmr |= CMR_0WS; + } + if (eeprom_image[EEPROM_SETUP] & SETUP_DRAM) cmr |= CMR_DRAM; + outb(cmr, EWRK3_CMR); + + cr = inb(EWRK3_CR); /* Set up the Control Register */ + cr |= eeprom_image[EEPROM_SETUP] & SETUP_APD; + if (cr & SETUP_APD) cr |= eeprom_image[EEPROM_SETUP] & SETUP_PS; + cr |= eeprom_image[EEPROM_MISC0] & FAST_BUS; + cr |= eeprom_image[EEPROM_MISC0] & ENA_16; + outb(cr, EWRK3_CR); + + /* + ** Determine the base address and window length for the EWRK3 + ** RAM from the memory base register. + */ + mem_start = inb(EWRK3_MBR); + shmem_length = 0; + if (mem_start != 0) { + if ((mem_start >= 0x0a) && (mem_start <= 0x0f)) { + mem_start *= SHMEM_64K; + shmem_length = SHMEM_64K; + } else if ((mem_start >= 0x14) && (mem_start <= 0x1f)) { + mem_start *= SHMEM_32K; + shmem_length = SHMEM_32K; + } else if ((mem_start >= 0x40) && (mem_start <= 0xff)) { + mem_start = mem_start * SHMEM_2K + 0x80000; + shmem_length = SHMEM_2K; + } else { + status = -ENXIO; + } + } + + /* + ** See the top of this source code for comments about + ** uncommenting this line. + */ +/* FORCE_2K_MODE;*/ + + if (!status) { + if (hard_strapped) { + printk(" is hard strapped.\n"); + } else if (mem_start) { + printk(" has a %dk RAM window", (int)(shmem_length >> 10)); + printk(" at 0x%.5lx", mem_start); + } else { + printk(" is in I/O only mode"); + } + + /* private area & initialise */ + dev->priv = (void *) kmalloc(sizeof(struct ewrk3_private), + GFP_KERNEL); + if (dev->priv == NULL) { + return -ENOMEM; + } + lp = (struct ewrk3_private *)dev->priv; + memset(dev->priv, 0, sizeof(struct ewrk3_private)); + lp->shmem_base = mem_start; + lp->shmem_length = shmem_length; + lp->lemac = lemac; + lp->hard_strapped = hard_strapped; + + lp->mPage = 64; + if (cmr & CMR_DRAM) lp->mPage <<= 1 ;/* 2 DRAMS on module */ + + sprintf(lp->adapter_name,"%s (%s)", name, dev->name); + request_region(iobase, EWRK3_TOTAL_SIZE, lp->adapter_name); + + lp->irq_mask = ICR_TNEM|ICR_TXDM|ICR_RNEM|ICR_RXDM; + + if (!hard_strapped) { + /* + ** Enable EWRK3 board interrupts for autoprobing + */ + icr |= ICR_IE; /* Enable interrupts */ + outb(icr, EWRK3_ICR); + + /* The DMA channel may be passed in on this parameter. */ + dev->dma = 0; + + /* To auto-IRQ we enable the initialization-done and DMA err, + interrupts. For now we will always get a DMA error. */ + if (dev->irq < 2) { +#ifndef MODULE + u_char irqnum; + + autoirq_setup(0); + + /* + ** Trigger a TNE interrupt. + */ + icr |=ICR_TNEM; + outb(1,EWRK3_TDQ); /* Write to the TX done queue */ + outb(icr, EWRK3_ICR); /* Unmask the TXD interrupt */ + + irqnum = irq[((icr & IRQ_SEL) >> 4)]; + + dev->irq = autoirq_report(1); + if ((dev->irq) && (irqnum == dev->irq)) { + printk(" and uses IRQ%d.\n", dev->irq); + } else { + if (!dev->irq) { + printk(" and failed to detect IRQ line.\n"); + } else if ((irqnum == 1) && (lemac == LeMAC2)) { + printk(" and an illegal IRQ line detected.\n"); + } else { + printk(", but incorrect IRQ line detected.\n"); + } + status = -ENXIO; + } + + DISABLE_IRQs; /* Mask all interrupts */ + +#endif /* MODULE */ + } else { + printk(" and requires IRQ%d.\n", dev->irq); + } + } + if (status) release_region(iobase, EWRK3_TOTAL_SIZE); + } else { + status = -ENXIO; + } + } + } + } else { + status = -ENXIO; + } + } + + if (!status) { + if (ewrk3_debug > 0) { + printk(version); + } + + /* The EWRK3-specific entries in the device structure. */ + dev->open = &ewrk3_open; + dev->hard_start_xmit = &ewrk3_queue_pkt; + dev->stop = &ewrk3_close; + dev->get_stats = &ewrk3_get_stats; + dev->set_multicast_list = &set_multicast_list; + dev->do_ioctl = &ewrk3_ioctl; + + dev->mem_start = 0; + + /* Fill in the generic field of the device structure. */ + ether_setup(dev); + } + } else { + status = -ENXIO; + } + + return status; +} + + +static int +ewrk3_open(struct device *dev) +{ + struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv; + u_long iobase = dev->base_addr; + int i, status = 0; + u_char icr, csr; + + /* + ** Stop the TX and RX... + */ + STOP_EWRK3; + + if (!lp->hard_strapped) { + irq2dev_map[dev->irq] = dev; /* For latched interrupts */ + + if (request_irq(dev->irq, (void *)ewrk3_interrupt, 0, "ewrk3")) { + printk("ewrk3_open(): Requested IRQ%d is busy\n",dev->irq); + status = -EAGAIN; + } else { + + /* + ** Re-initialize the EWRK3... + */ + ewrk3_init(dev); + + if (ewrk3_debug > 1){ + printk("%s: ewrk3 open with irq %d\n",dev->name,dev->irq); + printk(" physical address: "); + for (i=0;i<5;i++){ + printk("%2.2x:",(u_char)dev->dev_addr[i]); + } + printk("%2.2x\n",(u_char)dev->dev_addr[i]); + if (lp->shmem_length == 0) { + printk(" no shared memory, I/O only mode\n"); + } else { + printk(" start of shared memory: 0x%08lx\n",lp->shmem_base); + printk(" window length: 0x%04lx\n",lp->shmem_length); + } + printk(" # of DRAMS: %d\n",((inb(EWRK3_CMR) & 0x02) ? 2 : 1)); + printk(" csr: 0x%02x\n", inb(EWRK3_CSR)); + printk(" cr: 0x%02x\n", inb(EWRK3_CR)); + printk(" icr: 0x%02x\n", inb(EWRK3_ICR)); + printk(" cmr: 0x%02x\n", inb(EWRK3_CMR)); + printk(" fmqc: 0x%02x\n", inb(EWRK3_FMQC)); + } + + dev->tbusy = 0; + dev->start = 1; + dev->interrupt = UNMASK_INTERRUPTS; + + /* + ** Unmask EWRK3 board interrupts + */ + icr = inb(EWRK3_ICR); + ENABLE_IRQs; + + } + } else { + dev->start = 0; + dev->tbusy = 1; + printk("%s: ewrk3 available for hard strapped set up only.\n", dev->name); + printk(" Run the 'ewrk3setup' utility or remove the hard straps.\n"); + } + + MOD_INC_USE_COUNT; + + return status; +} + +/* +** Initialize the EtherWORKS 3 operating conditions +*/ +static void +ewrk3_init(struct device *dev) +{ + struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv; + u_char csr, page; + u_long iobase = dev->base_addr; + + /* + ** Enable any multicasts + */ + set_multicast_list(dev); + + /* + ** Clean out any remaining entries in all the queues here + */ + while (inb(EWRK3_TQ)); + while (inb(EWRK3_TDQ)); + while (inb(EWRK3_RQ)); + while (inb(EWRK3_FMQ)); + + /* + ** Write a clean free memory queue + */ + for (page=1;pagemPage;page++) { /* Write the free page numbers */ + outb(page, EWRK3_FMQ); /* to the Free Memory Queue */ + } + + lp->lock = 0; /* Ensure there are no locks */ + + START_EWRK3; /* Enable the TX and/or RX */ +} + +/* +** Writes a socket buffer to the free page queue +*/ +static int +ewrk3_queue_pkt(struct sk_buff *skb, struct device *dev) +{ + struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv; + u_long iobase = dev->base_addr; + int status = 0; + u_char icr, csr; + + /* Transmitter timeout, serious problems. */ + if (dev->tbusy || lp->lock) { + int tickssofar = jiffies - dev->trans_start; + if (tickssofar < QUEUE_PKT_TIMEOUT) { + status = -1; + } else if (!lp->hard_strapped) { + printk("%s: transmit timed/locked out, status %04x, resetting.\n", + dev->name, inb(EWRK3_CSR)); + + /* + ** Mask all board interrupts + */ + DISABLE_IRQs; + + /* + ** Stop the TX and RX... + */ + STOP_EWRK3; + + ewrk3_init(dev); + + /* + ** Unmask EWRK3 board interrupts + */ + ENABLE_IRQs; + + dev->tbusy=0; + dev->trans_start = jiffies; + } + } else if (skb == NULL) { + dev_tint(dev); + } else if (skb->len > 0) { + + /* + ** Block a timer-based transmit from overlapping. This could better be + ** done with atomic_swap(1, dev->tbusy), but set_bit() works as well. + */ + if (set_bit(0, (void*)&dev->tbusy) != 0) + printk("%s: Transmitter access conflict.\n", dev->name); + + DISABLE_IRQs; /* So that the page # remains correct */ + + /* + ** Get a free page from the FMQ when resources are available + */ + if (inb(EWRK3_FMQC) > 0) { + u_long buf = 0; + u_char page; + + if ((page = inb(EWRK3_FMQ)) < lp->mPage) { + /* + ** Set up shared memory window and pointer into the window + */ + while (set_bit(0, (void *)&lp->lock) != 0); /* Wait for lock to free */ + if (lp->shmem_length == IO_ONLY) { + outb(page, EWRK3_IOPR); + } else if (lp->shmem_length == SHMEM_2K) { + buf = lp->shmem_base; + outb(page, EWRK3_MPR); + } else if (lp->shmem_length == SHMEM_32K) { + buf = ((((short)page << 11) & 0x7800) + lp->shmem_base); + outb((page >> 4), EWRK3_MPR); + } else if (lp->shmem_length == SHMEM_64K) { + buf = ((((short)page << 11) & 0xf800) + lp->shmem_base); + outb((page >> 5), EWRK3_MPR); + } else { + status = -1; + printk("%s: Oops - your private data area is hosed!\n",dev->name); + } + + if (!status) { + + /* + ** Set up the buffer control structures and copy the data from + ** the socket buffer to the shared memory . + */ + + if (lp->shmem_length == IO_ONLY) { + int i; + u_char *p = skb->data; + + outb((char)(TCR_QMODE | TCR_PAD | TCR_IFC), EWRK3_DATA); + outb((char)(skb->len & 0xff), EWRK3_DATA); + outb((char)((skb->len >> 8) & 0xff), EWRK3_DATA); + outb((char)0x04, EWRK3_DATA); + for (i=0; ilen; i++) { + outb(*p++, EWRK3_DATA); + } + outb(page, EWRK3_TQ); /* Start sending pkt */ + } else { + writeb((char)(TCR_QMODE|TCR_PAD|TCR_IFC), (char *)buf);/* ctrl byte*/ + buf+=1; + writeb((char)(skb->len & 0xff), (char *)buf);/* length (16 bit xfer)*/ + buf+=1; + if (lp->txc) { + writeb((char)(((skb->len >> 8) & 0xff) | XCT), (char *)buf); + buf+=1; + writeb(0x04, (char *)buf); /* index byte */ + buf+=1; + writeb(0x00, (char *)(buf + skb->len)); /* Write the XCT flag */ + memcpy_toio(buf, skb->data, PRELOAD);/* Write PRELOAD bytes*/ + outb(page, EWRK3_TQ); /* Start sending pkt */ + memcpy_toio(buf+PRELOAD, skb->data+PRELOAD, skb->len-PRELOAD); + writeb(0xff, (char *)(buf + skb->len)); /* Write the XCT flag */ + } else { + writeb((char)((skb->len >> 8) & 0xff), (char *)buf); + buf+=1; + writeb(0x04, (char *)buf); /* index byte */ + buf+=1; + memcpy_toio((char *)buf, skb->data, skb->len);/* Write data bytes */ + outb(page, EWRK3_TQ); /* Start sending pkt */ + } + } + + dev->trans_start = jiffies; + dev_kfree_skb (skb, FREE_WRITE); + + } else { /* return unused page to the free memory queue */ + outb(page, EWRK3_FMQ); + } + lp->lock = 0; /* unlock the page register */ + } else { + printk("ewrk3_queue_pkt(): Invalid free memory page (%d).\n", + (u_char) page); + } + } else { + printk("ewrk3_queue_pkt(): No free resources...\n"); + printk("ewrk3_queue_pkt(): CSR: %02x ICR: %02x FMQC: %02x\n",inb(EWRK3_CSR),inb(EWRK3_ICR),inb(EWRK3_FMQC)); + } + + /* Check for free resources: clear 'tbusy' if there are some */ + if (inb(EWRK3_FMQC) > 0) { + dev->tbusy = 0; + } + + ENABLE_IRQs; + } + + return status; +} + +/* +** The EWRK3 interrupt handler. +*/ +static void +ewrk3_interrupt(int irq, struct pt_regs * regs) +{ + struct device *dev = (struct device *)(irq2dev_map[irq]); + struct ewrk3_private *lp; + u_long iobase; + u_char icr, cr, csr; + + if (dev == NULL) { + printk ("ewrk3_interrupt(): irq %d for unknown device.\n", irq); + } else { + lp = (struct ewrk3_private *)dev->priv; + iobase = dev->base_addr; + + if (dev->interrupt) + printk("%s: Re-entering the interrupt handler.\n", dev->name); + + dev->interrupt = MASK_INTERRUPTS; + + /* get the interrupt information */ + csr = inb(EWRK3_CSR); + + /* + ** Mask the EWRK3 board interrupts and turn on the LED + */ + DISABLE_IRQs; + + cr = inb(EWRK3_CR); + cr |= CR_LED; + outb(cr, EWRK3_CR); + + if (csr & CSR_RNE) /* Rx interrupt (packet[s] arrived) */ + ewrk3_rx(dev); + + if (csr & CSR_TNE) /* Tx interrupt (packet sent) */ + ewrk3_tx(dev); + + /* + ** Now deal with the TX/RX disable flags. These are set when there + ** are no more resources. If resources free up then enable these + ** interrupts, otherwise mask them - failure to do this will result + ** in the system hanging in an interrupt loop. + */ + if (inb(EWRK3_FMQC)) { /* any resources available? */ + lp->irq_mask |= ICR_TXDM|ICR_RXDM;/* enable the interrupt source */ + csr &= ~(CSR_TXD|CSR_RXD);/* ensure restart of a stalled TX or RX */ + outb(csr, EWRK3_CSR); + dev->tbusy = 0; /* clear TX busy flag */ + mark_bh(NET_BH); + } else { + lp->irq_mask &= ~(ICR_TXDM|ICR_RXDM);/* disable the interrupt source */ + } + + /* Unmask the EWRK3 board interrupts and turn off the LED */ + cr &= ~CR_LED; + outb(cr, EWRK3_CR); + + dev->interrupt = UNMASK_INTERRUPTS; + ENABLE_IRQs; + } + + return; +} + +static int +ewrk3_rx(struct device *dev) +{ + struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv; + u_long iobase = dev->base_addr; + int i, status = 0; + u_char page, tmpPage = 0, tmpLock = 0; + u_long buf = 0; + + while (inb(EWRK3_RQC) && !status) { /* Whilst there's incoming data */ + if ((page = inb(EWRK3_RQ)) < lp->mPage) {/* Get next entry's buffer page */ + /* + ** Preempt any process using the current page register. Check for + ** an existing lock to reduce time taken in I/O transactions. + */ + if ((tmpLock = set_bit(0, (void *)&lp->lock)) == 1) { /* Assert lock */ + if (lp->shmem_length == IO_ONLY) { /* Get existing page */ + tmpPage = inb(EWRK3_IOPR); + } else { + tmpPage = inb(EWRK3_MPR); + } + } + + /* + ** Set up shared memory window and pointer into the window + */ + if (lp->shmem_length == IO_ONLY) { + outb(page, EWRK3_IOPR); + } else if (lp->shmem_length == SHMEM_2K) { + buf = lp->shmem_base; + outb(page, EWRK3_MPR); + } else if (lp->shmem_length == SHMEM_32K) { + buf = ((((short)page << 11) & 0x7800) + lp->shmem_base); + outb((page >> 4), EWRK3_MPR); + } else if (lp->shmem_length == SHMEM_64K) { + buf = ((((short)page << 11) & 0xf800) + lp->shmem_base); + outb((page >> 5), EWRK3_MPR); + } else { + status = -1; + printk("%s: Oops - your private data area is hosed!\n",dev->name); + } + + if (!status) { + char rx_status; + int pkt_len; + + if (lp->shmem_length == IO_ONLY) { + rx_status = inb(EWRK3_DATA); + pkt_len = inb(EWRK3_DATA); + pkt_len |= ((u_short)inb(EWRK3_DATA) << 8); + } else { + rx_status = readb(buf); + buf+=1; + pkt_len = readw(buf); + buf+=3; + } + + if (!(rx_status & R_ROK)) { /* There was an error. */ + lp->stats.rx_errors++; /* Update the error stats. */ + if (rx_status & R_DBE) lp->stats.rx_frame_errors++; + if (rx_status & R_CRC) lp->stats.rx_crc_errors++; + if (rx_status & R_PLL) lp->stats.rx_fifo_errors++; + } else { + struct sk_buff *skb; + + if ((skb = dev_alloc_skb(pkt_len+2)) != NULL) { + unsigned char *p; + skb->dev = dev; + skb_reserve(skb,2); /* Align to 16 bytes */ + p = skb_put(skb,pkt_len); + + if (lp->shmem_length == IO_ONLY) { + *p = inb(EWRK3_DATA); /* dummy read */ + for (i=0; iprotocol=eth_type_trans(skb,dev); + netif_rx(skb); + + /* + ** Update stats + */ + lp->stats.rx_packets++; + for (i=1; ipktStats.bins[i]++; + i = EWRK3_PKT_STAT_SZ; + } + } + p = skb->data; /* Look at the dest addr */ + if (p[0] & 0x01) { /* Multicast/Broadcast */ + if ((*(s32 *)&p[0] == -1) && (*(s16 *)&p[4] == -1)) { + lp->pktStats.broadcast++; + } else { + lp->pktStats.multicast++; + } + } else if ((*(s32 *)&p[0] == *(s32 *)&dev->dev_addr[0]) && + (*(s16 *)&p[4] == *(s16 *)&dev->dev_addr[4])) { + lp->pktStats.unicast++; + } + + lp->pktStats.bins[0]++; /* Duplicates stats.rx_packets */ + if (lp->pktStats.bins[0] == 0) { /* Reset counters */ + memset(&lp->pktStats, 0, sizeof(lp->pktStats)); + } + } else { + printk("%s: Insufficient memory; nuking packet.\n", dev->name); + lp->stats.rx_dropped++; /* Really, deferred. */ + break; + } + } + } + /* + ** Return the received buffer to the free memory queue + */ + outb(page, EWRK3_FMQ); + + if (tmpLock) { /* If a lock was preempted */ + if (lp->shmem_length == IO_ONLY) { /* Replace old page */ + outb(tmpPage, EWRK3_IOPR); + } else { + outb(tmpPage, EWRK3_MPR); + } + } + lp->lock = 0; /* Unlock the page register */ + } else { + printk("ewrk3_rx(): Illegal page number, page %d\n",page); + printk("ewrk3_rx(): CSR: %02x ICR: %02x FMQC: %02x\n",inb(EWRK3_CSR),inb(EWRK3_ICR),inb(EWRK3_FMQC)); + } + } + return status; +} + +/* +** Buffer sent - check for TX buffer errors. +*/ +static int +ewrk3_tx(struct device *dev) +{ + struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv; + u_long iobase = dev->base_addr; + u_char tx_status; + + while ((tx_status = inb(EWRK3_TDQ)) > 0) { /* Whilst there's old buffers */ + if (tx_status & T_VSTS) { /* The status is valid */ + if (tx_status & T_TXE) { + lp->stats.tx_errors++; + if (tx_status & T_NCL) lp->stats.tx_carrier_errors++; + if (tx_status & T_LCL) lp->stats.tx_window_errors++; + if (tx_status & T_CTU) { + if ((tx_status & T_COLL) ^ T_XUR) { + lp->pktStats.tx_underruns++; + } else { + lp->pktStats.excessive_underruns++; + } + } else if (tx_status & T_COLL) { + if ((tx_status & T_COLL) ^ T_XCOLL) { + lp->stats.collisions++; + } else { + lp->pktStats.excessive_collisions++; + } + } + } else { + lp->stats.tx_packets++; + } + } + } + + return 0; +} + +static int +ewrk3_close(struct device *dev) +{ + struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv; + u_long iobase = dev->base_addr; + u_char icr, csr; + + dev->start = 0; + dev->tbusy = 1; + + if (ewrk3_debug > 1) { + printk("%s: Shutting down ethercard, status was %2.2x.\n", + dev->name, inb(EWRK3_CSR)); + } + + /* + ** We stop the EWRK3 here... mask interrupts and stop TX & RX + */ + DISABLE_IRQs; + + STOP_EWRK3; + + /* + ** Clean out the TX and RX queues here (note that one entry + ** may get added to either the TXD or RX queues if the the TX or RX + ** just starts processing a packet before the STOP_EWRK3 command + ** is received. This will be flushed in the ewrk3_open() call). + */ + while (inb(EWRK3_TQ)); + while (inb(EWRK3_TDQ)); + while (inb(EWRK3_RQ)); + + if (!lp->hard_strapped) { + free_irq(dev->irq); + + irq2dev_map[dev->irq] = 0; + } + + MOD_DEC_USE_COUNT; + + return 0; +} + +static struct enet_statistics * +ewrk3_get_stats(struct device *dev) +{ + struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv; + + /* Null body since there is no framing error counter */ + + return &lp->stats; +} + +/* +** Set or clear the multicast filter for this adaptor. +*/ +static void +set_multicast_list(struct device *dev) +{ + struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv; + u_long iobase = dev->base_addr; + u_char csr; + + if (irq2dev_map[dev->irq] != NULL) { + csr = inb(EWRK3_CSR); + + if (lp->shmem_length == IO_ONLY) { + lp->mctbl = (char *) PAGE0_HTE; + } else { + lp->mctbl = (char *)(lp->shmem_base + PAGE0_HTE); + } + + csr &= ~(CSR_PME | CSR_MCE); + if (dev->flags & IFF_PROMISC) { /* set promiscuous mode */ + csr |= CSR_PME; + outb(csr, EWRK3_CSR); + } else { + SetMulticastFilter(dev); + csr |= CSR_MCE; + outb(csr, EWRK3_CSR); + } + } +} + +/* +** Calculate the hash code and update the logical address filter +** from a list of ethernet multicast addresses. +** Little endian crc one liner from Matt Thomas, DEC. +** +** Note that when clearing the table, the broadcast bit must remain asserted +** to receive broadcast messages. +*/ +static void SetMulticastFilter(struct device *dev) +{ + struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv; + struct dev_mc_list *dmi=dev->mc_list; + u_long iobase = dev->base_addr; + int i; + char *addrs, j, bit, byte; + short *p = (short *) lp->mctbl; + u16 hashcode; + s32 crc, poly = CRC_POLYNOMIAL_LE; + + while (set_bit(0, (void *)&lp->lock) != 0); /* Wait for lock to free */ + + if (lp->shmem_length == IO_ONLY) { + outb(0, EWRK3_IOPR); + outw(EEPROM_OFFSET(lp->mctbl), EWRK3_PIR1); + } else { + outb(0, EWRK3_MPR); + } + + if (dev->flags & IFF_ALLMULTI) { + for (i=0; i<(HASH_TABLE_LEN >> 3); i++) { + if (lp->shmem_length == IO_ONLY) { + outb(0xff, EWRK3_DATA); + } else { /* memset didn't work here */ + writew(0xffff, p); + p++; i++; + } + } + } else { + /* Clear table except for broadcast bit */ + if (lp->shmem_length == IO_ONLY) { + for (i=0; i<(HASH_TABLE_LEN >> 4) - 1; i++) { + outb(0x00, EWRK3_DATA); + } + outb(0x80, EWRK3_DATA); i++; /* insert the broadcast bit */ + for (; i<(HASH_TABLE_LEN >> 3); i++) { + outb(0x00, EWRK3_DATA); + } + } else { + memset_io(lp->mctbl, 0, (HASH_TABLE_LEN >> 3)); + writeb(0x80, (char *)(lp->mctbl + (HASH_TABLE_LEN >> 4) - 1)); + } + + /* Update table */ + for (i=0;imc_count;i++) { /* for each address in the list */ + addrs=dmi->dmi_addr; + dmi=dmi->next; + if ((*addrs & 0x01) == 1) { /* multicast address? */ + crc = 0xffffffff; /* init CRC for each address */ + for (byte=0;byte>=1) { + crc = (crc >> 1) ^ (((crc ^ bit) & 0x01) ? poly : 0); + } + } + hashcode = crc & ((1 << 9) - 1); /* hashcode is 9 LSb of CRC */ + + byte = hashcode >> 3; /* bit[3-8] -> byte in filter */ + bit = 1 << (hashcode & 0x07); /* bit[0-2] -> bit in byte */ + + if (lp->shmem_length == IO_ONLY) { + u_char tmp; + + outw((short)((long)lp->mctbl) + byte, EWRK3_PIR1); + tmp = inb(EWRK3_DATA); + tmp |= bit; + outw((short)((long)lp->mctbl) + byte, EWRK3_PIR1); + outb(tmp, EWRK3_DATA); + } else { + writeb(readb(lp->mctbl + byte) | bit, lp->mctbl + byte); + } + } + } + } + + lp->lock = 0; /* Unlock the page register */ + + return; +} + +/* +** ISA bus I/O device probe +*/ +static void isa_probe(struct device *dev, u_long ioaddr) +{ + int i = num_ewrk3s, maxSlots; + u_long iobase; + + if (!ioaddr && autoprobed) return ; /* Been here before ! */ + if (ioaddr >= 0x400) return; /* Not ISA */ + + if (ioaddr == 0) { /* Autoprobing */ + iobase = EWRK3_IO_BASE; /* Get the first slot address */ + maxSlots = 24; + } else { /* Probe a specific location */ + iobase = ioaddr; + maxSlots = i + 1; + } + + for (; (iname, iobase); + } + } + + return; +} + +/* +** EISA bus I/O device probe. Probe from slot 1 since slot 0 is usually +** the motherboard. +*/ +static void eisa_probe(struct device *dev, u_long ioaddr) +{ + int i, maxSlots; + u_long iobase; + char name[EWRK3_STRLEN]; + + if (!ioaddr && autoprobed) return ; /* Been here before ! */ + if (ioaddr < 0x1000) return; /* Not EISA */ + + if (ioaddr == 0) { /* Autoprobing */ + iobase = EISA_SLOT_INC; /* Get the first slot address */ + i = 1; + maxSlots = MAX_EISA_SLOTS; + } else { /* Probe a specific location */ + iobase = ioaddr; + i = (ioaddr >> 12); + maxSlots = i + 1; + } + + for (i=1; (iname, iobase); + } + } + } + + return; +} + +/* +** Allocate the device by pointing to the next available space in the +** device structure. Should one not be available, it is created. +*/ +static struct device *alloc_device(struct device *dev, u_long iobase) +{ + int addAutoProbe = 0; + struct device *tmp = NULL, *ret; + int (*init)(struct device *) = NULL; + + /* + ** Check the device structures for an end of list or unused device + */ + if (!loading_module) { + while (dev->next != NULL) { + if ((dev->base_addr == EWRK3_NDA) || (dev->base_addr == 0)) break; + dev = dev->next; /* walk through eth device list */ + num_eth++; /* increment eth device number */ + } + + /* + ** If an autoprobe is requested for another device, we must re-insert + ** the request later in the list. Remember the current position first. + */ + if ((dev->base_addr == 0) && (num_ewrk3s > 0)) { + addAutoProbe++; + tmp = dev->next; /* point to the next device */ + init = dev->init; /* remember the probe function */ + } + + /* + ** If at end of list and can't use current entry, malloc one up. + ** If memory could not be allocated, print an error message. + */ + if ((dev->next == NULL) && + !((dev->base_addr == EWRK3_NDA) || (dev->base_addr == 0))){ + dev->next = (struct device *)kmalloc(sizeof(struct device) + 8, + GFP_KERNEL); + + dev = dev->next; /* point to the new device */ + if (dev == NULL) { + printk("eth%d: Device not initialised, insufficient memory\n", + num_eth); + } else { + /* + ** If the memory was allocated, point to the new memory area + ** and initialize it (name, I/O address, next device (NULL) and + ** initialisation probe routine). + */ + dev->name = (char *)(dev + sizeof(struct device)); + if (num_eth > 9999) { + sprintf(dev->name,"eth????"); /* New device name */ + } else { + sprintf(dev->name,"eth%d", num_eth);/* New device name */ + } + dev->base_addr = iobase; /* assign the io address */ + dev->next = NULL; /* mark the end of list */ + dev->init = &ewrk3_probe; /* initialisation routine */ + num_ewrk3s++; + } + } + ret = dev; /* return current struct, or NULL */ + + /* + ** Now figure out what to do with the autoprobe that has to be inserted. + ** Firstly, search the (possibly altered) list for an empty space. + */ + if (ret != NULL) { + if (addAutoProbe) { + for (;(tmp->next!=NULL) && (tmp->base_addr!=EWRK3_NDA); tmp=tmp->next); + + /* + ** If no more device structures and can't use the current one, malloc + ** one up. If memory could not be allocated, print an error message. + */ + if ((tmp->next == NULL) && !(tmp->base_addr == EWRK3_NDA)) { + tmp->next = (struct device *)kmalloc(sizeof(struct device) + 8, + GFP_KERNEL); + tmp = tmp->next; /* point to the new device */ + if (tmp == NULL) { + printk("%s: Insufficient memory to extend the device list.\n", + dev->name); + } else { + /* + ** If the memory was allocated, point to the new memory area + ** and initialize it (name, I/O address, next device (NULL) and + ** initialisation probe routine). + */ + tmp->name = (char *)(tmp + sizeof(struct device)); + if (num_eth > 9999) { + sprintf(tmp->name,"eth????"); /* New device name */ + } else { + sprintf(tmp->name,"eth%d", num_eth);/* New device name */ + } + tmp->base_addr = 0; /* re-insert the io address */ + tmp->next = NULL; /* mark the end of list */ + tmp->init = init; /* initialisation routine */ + } + } else { /* structure already exists */ + tmp->base_addr = 0; /* re-insert the io address */ + } + } + } + } else { + ret = dev; + } + + return ret; +} + +/* +** Read the EWRK3 EEPROM using this routine +*/ +static int Read_EEPROM(u_long iobase, u_char eaddr) +{ + int i; + + outb((eaddr & 0x3f), EWRK3_PIR1); /* set up 6 bits of address info */ + outb(EEPROM_RD, EWRK3_IOPR); /* issue read command */ + for (i=0;i<5000;i++) inb(EWRK3_CSR); /* wait 1msec */ + + return inw(EWRK3_EPROM1); /* 16 bits data return */ +} + +/* +** Write the EWRK3 EEPROM using this routine +*/ +static int Write_EEPROM(short data, u_long iobase, u_char eaddr) +{ + int i; + + outb(EEPROM_WR_EN, EWRK3_IOPR); /* issue write enable command */ + for (i=0;i<5000;i++) inb(EWRK3_CSR); /* wait 1msec */ + outw(data, EWRK3_EPROM1); /* write data to register */ + outb((eaddr & 0x3f), EWRK3_PIR1); /* set up 6 bits of address info */ + outb(EEPROM_WR, EWRK3_IOPR); /* issue write command */ + for (i=0;i<75000;i++) inb(EWRK3_CSR); /* wait 15msec */ + outb(EEPROM_WR_DIS, EWRK3_IOPR); /* issue write disable command */ + for (i=0;i<5000;i++) inb(EWRK3_CSR); /* wait 1msec */ + + return 0; +} + +/* +** Look for a particular board name in the on-board EEPROM. +*/ +static void EthwrkSignature(char *name, char *eeprom_image) +{ + u_long i,j,k; + char *signatures[] = EWRK3_SIGNATURE; + + strcpy(name, ""); + for (i=0;*signatures[i] != '\0' && *name == '\0';i++) { + for (j=EEPROM_PNAME7,k=0;j<=EEPROM_PNAME0 && kbase_addr; + u16 tmp; + + if (chipType == LeMAC2) { + for (crc=0x6a, j=0; jdev_addr[j] = eeprom_image[EEPROM_PADDR0 + j]; + outb(dev->dev_addr[j], EWRK3_PAR0 + j); + for (k=0; k<8; k++, sd >>= 1) { + lfsr = ((((crc & 0x02) >> 1) ^ (crc & 0x01)) ^ (sd & 0x01)) << 7; + crc = (crc >> 1) + lfsr; + } + } + if (crc != eeprom_image[EEPROM_PA_CRC]) status = -1; + } else { + for (i=0,k=0;i 0xffff) k-=0xffff; + + k += (u_char) (tmp = inb(EWRK3_APROM)); + dev->dev_addr[i] = (u_char) tmp; + outb(dev->dev_addr[i], EWRK3_PAR0 + i); + i++; + k += (u_short) ((tmp = inb(EWRK3_APROM)) << 8); + dev->dev_addr[i] = (u_char) tmp; + outb(dev->dev_addr[i], EWRK3_PAR0 + i); + i++; + + if (k > 0xffff) k-=0xffff; + } + if (k == 0xffff) k=0; + chksum = inb(EWRK3_APROM); + chksum |= (inb(EWRK3_APROM)<<8); + if (k != chksum) status = -1; + } + + return status; +} + +/* +** Look for a particular board name in the EISA configuration space +*/ +static int EISA_signature(char *name, s32 eisa_id) +{ + u_long i; + char *signatures[] = EWRK3_SIGNATURE; + char ManCode[EWRK3_STRLEN]; + union { + s32 ID; + char Id[4]; + } Eisa; + int status = 0; + + *name = '\0'; + for (i=0; i<4; i++) { + Eisa.Id[i] = inb(eisa_id + i); + } + + ManCode[0]=(((Eisa.Id[0]>>2)&0x1f)+0x40); + ManCode[1]=(((Eisa.Id[1]&0xe0)>>5)+((Eisa.Id[0]&0x03)<<3)+0x40); + ManCode[2]=(((Eisa.Id[2]>>4)&0x0f)+0x30); + ManCode[3]=((Eisa.Id[2]&0x0f)+0x30); + ManCode[4]=(((Eisa.Id[3]>>4)&0x0f)+0x30); + ManCode[5]='\0'; + + for (i=0;(*signatures[i] != '\0') && (*name == '\0');i++) { + if (strstr(ManCode, signatures[i]) != NULL) { + strcpy(name,ManCode); + status = 1; + } + } + + return status; /* return the device name string */ +} + +/* +** Perform IOCTL call functions here. Some are privileged operations and the +** effective uid is checked in those cases. +*/ +static int ewrk3_ioctl(struct device *dev, struct ifreq *rq, int cmd) +{ + struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv; + struct ewrk3_ioctl *ioc = (struct ewrk3_ioctl *) &rq->ifr_data; + u_long iobase = dev->base_addr; + int i, j, status = 0; + u_char csr; + union { + u_char addr[HASH_TABLE_LEN * ETH_ALEN]; + u_short val[(HASH_TABLE_LEN * ETH_ALEN) >> 1]; + } tmp; + + switch(ioc->cmd) { + case EWRK3_GET_HWADDR: /* Get the hardware address */ + for (i=0; idev_addr[i]; + } + ioc->len = ETH_ALEN; + if (!(status = verify_area(VERIFY_WRITE, (void *)ioc->data, ioc->len))) { + memcpy_tofs(ioc->data, tmp.addr, ioc->len); + } + + break; + case EWRK3_SET_HWADDR: /* Set the hardware address */ + if (suser()) { + if (!(status = verify_area(VERIFY_READ, (void *)ioc->data, ETH_ALEN))) { + csr = inb(EWRK3_CSR); + csr |= (CSR_TXD|CSR_RXD); + outb(csr, EWRK3_CSR); /* Disable the TX and RX */ + + memcpy_fromfs(tmp.addr,ioc->data,ETH_ALEN); + for (i=0; idev_addr[i] = tmp.addr[i]; + outb(tmp.addr[i], EWRK3_PAR0 + i); + } + + csr &= ~(CSR_TXD|CSR_RXD); /* Enable the TX and RX */ + outb(csr, EWRK3_CSR); + } + } else { + status = -EPERM; + } + + break; + case EWRK3_SET_PROM: /* Set Promiscuous Mode */ + if (suser()) { + csr = inb(EWRK3_CSR); + csr |= CSR_PME; + csr &= ~CSR_MCE; + outb(csr, EWRK3_CSR); + } else { + status = -EPERM; + } + + break; + case EWRK3_CLR_PROM: /* Clear Promiscuous Mode */ + if (suser()) { + csr = inb(EWRK3_CSR); + csr &= ~CSR_PME; + outb(csr, EWRK3_CSR); + } else { + status = -EPERM; + } + + break; + case EWRK3_SAY_BOO: /* Say "Boo!" to the kernel log file */ + printk("%s: Boo!\n", dev->name); + + break; + case EWRK3_GET_MCA: /* Get the multicast address table */ + if (!(status = verify_area(VERIFY_WRITE, ioc->data, ioc->len))) { + while (set_bit(0, (void *)&lp->lock) != 0); /* Wait for lock to free */ + if (lp->shmem_length == IO_ONLY) { + outb(0, EWRK3_IOPR); + outw(PAGE0_HTE, EWRK3_PIR1); + for (i=0; i<(HASH_TABLE_LEN >> 3); i++) { + tmp.addr[i] = inb(EWRK3_DATA); + } + } else { + outb(0, EWRK3_MPR); + memcpy_fromio(tmp.addr, (char *)(lp->shmem_base + PAGE0_HTE), (HASH_TABLE_LEN >> 3)); + } + ioc->len = (HASH_TABLE_LEN >> 3); + memcpy_tofs(ioc->data, tmp.addr, ioc->len); + } + lp->lock = 0; /* Unlock the page register */ + + break; + case EWRK3_SET_MCA: /* Set a multicast address */ + if (suser()) { + if (!(status=verify_area(VERIFY_READ, ioc->data, ETH_ALEN*ioc->len))) { + memcpy_fromfs(tmp.addr, ioc->data, ETH_ALEN * ioc->len); + set_multicast_list(dev); + } + } else { + status = -EPERM; + } + + break; + case EWRK3_CLR_MCA: /* Clear all multicast addresses */ + if (suser()) { + set_multicast_list(dev); + } else { + status = -EPERM; + } + + break; + case EWRK3_MCA_EN: /* Enable multicast addressing */ + if (suser()) { + csr = inb(EWRK3_CSR); + csr |= CSR_MCE; + csr &= ~CSR_PME; + outb(csr, EWRK3_CSR); + } else { + status = -EPERM; + } + + break; + case EWRK3_GET_STATS: /* Get the driver statistics */ + cli(); + ioc->len = sizeof(lp->pktStats); + if (!(status=verify_area(VERIFY_WRITE, ioc->data, ioc->len))) { + memcpy_tofs(ioc->data, &lp->pktStats, ioc->len); + } + sti(); + + break; + case EWRK3_CLR_STATS: /* Zero out the driver statistics */ + if (suser()) { + cli(); + memset(&lp->pktStats, 0, sizeof(lp->pktStats)); + sti(); + } else { + status = -EPERM; + } + + break; + case EWRK3_GET_CSR: /* Get the CSR Register contents */ + tmp.addr[0] = inb(EWRK3_CSR); + ioc->len = 1; + if (!(status=verify_area(VERIFY_WRITE, ioc->data, ioc->len))) { + memcpy_tofs(ioc->data, tmp.addr, ioc->len); + } + + break; + case EWRK3_SET_CSR: /* Set the CSR Register contents */ + if (suser()) { + if (!(status=verify_area(VERIFY_READ, ioc->data, 1))) { + memcpy_fromfs(tmp.addr, ioc->data, 1); + outb(tmp.addr[0], EWRK3_CSR); + } + } else { + status = -EPERM; + } + + break; + case EWRK3_GET_EEPROM: /* Get the EEPROM contents */ + if (suser()) { + for (i=0; i<(EEPROM_MAX>>1); i++) { + tmp.val[i] = (short)Read_EEPROM(iobase, i); + } + i = EEPROM_MAX; + tmp.addr[i++] = inb(EWRK3_CMR); /* Config/Management Reg. */ + for (j=0;jlen = EEPROM_MAX + 1 + ETH_ALEN; + if (!(status=verify_area(VERIFY_WRITE, ioc->data, ioc->len))) { + memcpy_tofs(ioc->data, tmp.addr, ioc->len); + } + } else { + status = -EPERM; + } + + break; + case EWRK3_SET_EEPROM: /* Set the EEPROM contents */ + if (suser()) { + if (!(status=verify_area(VERIFY_READ, ioc->data, EEPROM_MAX))) { + memcpy_fromfs(tmp.addr, ioc->data, EEPROM_MAX); + for (i=0; i<(EEPROM_MAX>>1); i++) { + Write_EEPROM(tmp.val[i], iobase, i); + } + } + } else { + status = -EPERM; + } + + break; + case EWRK3_GET_CMR: /* Get the CMR Register contents */ + tmp.addr[0] = inb(EWRK3_CMR); + ioc->len = 1; + if (!(status=verify_area(VERIFY_WRITE, ioc->data, ioc->len))) { + memcpy_tofs(ioc->data, tmp.addr, ioc->len); + } + + break; + case EWRK3_SET_TX_CUT_THRU: /* Set TX cut through mode */ + if (suser()) { + lp->txc = 1; + } else { + status = -EPERM; + } + + break; + case EWRK3_CLR_TX_CUT_THRU: /* Clear TX cut through mode */ + if (suser()) { + lp->txc = 0; + } else { + status = -EPERM; + } + + break; + default: + status = -EOPNOTSUPP; + } + + return status; +} + +#ifdef MODULE +static char devicename[9] = { 0, }; +static struct device thisEthwrk = { + devicename, /* device name is inserted by /linux/drivers/net/net_init.c */ + 0, 0, 0, 0, + 0x300, 5, /* I/O address, IRQ */ + 0, 0, 0, NULL, ewrk3_probe }; + +static int io=0x300; /* <--- EDIT THESE LINES FOR YOUR CONFIGURATION */ +static int irq=5; /* or use the insmod io= irq= options */ + +int +init_module(void) +{ + thisEthwrk.base_addr=io; + thisEthwrk.irq=irq; + if (register_netdev(&thisEthwrk) != 0) + return -EIO; + return 0; +} + +void +cleanup_module(void) +{ + release_region(thisEthwrk.base_addr, EWRK3_TOTAL_SIZE); + + if (thisEthwrk.priv) { + kfree(thisEthwrk.priv); + thisEthwrk.priv = NULL; + } + thisEthwrk.irq = 0; + + unregister_netdev(&thisEthwrk); +} +#endif /* MODULE */ + + +/* + * Local variables: + * kernel-compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O2 -m486 -c ewrk3.c" + * + * module-compile-command: "gcc -D__KERNEL__ -DMODULE -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O2 -m486 -c ewrk3.c" + * End: + */ + -- cgit v1.2.3