diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1999-06-17 14:08:29 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1999-06-17 14:08:29 +0000 |
commit | 57d569635c05dc4ea9b9f1f8dcec69b9ddc989b2 (patch) | |
tree | 1f703abf7d95dcd50ee52da3b96eb1b4b2b4ea53 /drivers/net/sk_mca.c | |
parent | 59223edaa18759982db0a8aced0e77457d10c68e (diff) |
The rest of 2.3.6.
Diffstat (limited to 'drivers/net/sk_mca.c')
-rw-r--r-- | drivers/net/sk_mca.c | 1143 |
1 files changed, 1143 insertions, 0 deletions
diff --git a/drivers/net/sk_mca.c b/drivers/net/sk_mca.c new file mode 100644 index 000000000..720b50beb --- /dev/null +++ b/drivers/net/sk_mca.c @@ -0,0 +1,1143 @@ +/* +net-3-driver for the SKNET MCA-based cards + +This is an extension to the Linux operating system, and is covered by the +same Gnu Public License that covers that work. + +Copyright 1999 by Alfred Arnold (alfred@ccac.rwth-aachen.de, aarnold@elsa.de) + +This driver is based both on the 3C523 driver and the SK_G16 driver. + +paper sources: + 'PC Hardware: Aufbau, Funktionsweise, Programmierung' by + Hans-Peter Messmer for the basic Microchannel stuff + + 'Linux Geraetetreiber' by Allesandro Rubini, Kalle Dalheimer + for help on Ethernet driver programming + + 'Ethernet/IEEE 802.3 Family 1992 World Network Data Book/Handbook' by AMD + for documentation on the AM7990 LANCE + + 'SKNET Personal Technisches Manual', Version 1.2 by Schneider&Koch + for documentation on the Junior board + + 'SK-NET MC2+ Technical Manual", Version 1.1 by Schneider&Koch for + documentation on the MC2 bord + + A big thank you to the S&K support for providing me so quickly with + documentation! + + Also see http://www.syskonnect.com/ + + Missing things: + + -> set debug level via ioctl instead of compile-time switches + -> I didn't follow the development of the 2.1.x kernels, so my + assumptions about which things changed with which kernel version + are probably nonsense + +History: + May 16th, 1999 + startup + May 22st, 1999 + added private structure, methods + begun building data structures in RAM + May 23nd, 1999 + can receive frames, send frames + May 24th, 1999 + modularized intialization of LANCE + loadable as module + still Tx problem :-( + May 26th, 1999 + MC2 works + support for multiple devices + display media type for MC2+ + May 28th, 1999 + fixed problem in GetLANCE leaving interrupts turned off + increase TX queue to 4 packets to improve send performance + May 29th, 1999 + a few corrections in statistics, caught rcvr overruns + reinitialization of LANCE/board in critical situations + MCA info implemented + implemented LANCE multicast filter + Jun 6th, 1999 + additions for Linux 2.2 + + *************************************************************************/ + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/string.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/malloc.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/time.h> +#include <linux/mca.h> +#include <asm/processor.h> +#include <asm/bitops.h> +#include <asm/io.h> + +#ifdef MODULE +#include <linux/module.h> +#include <linux/version.h> +#endif + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> + +#define _SK_MCA_DRIVER_ +#include "sk_mca.h" + +/* ------------------------------------------------------------------------ + * global static data - not more since we can handle multiple boards and + * have to pack all state info into the device struct! + * ------------------------------------------------------------------------ */ + +static char *MediaNames[Media_Count]= + {"10Base2", "10BaseT", "10Base5", "Unknown"}; + +static unsigned char poly[] = + {1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0}; + +/* ------------------------------------------------------------------------ + * private subfunctions + * ------------------------------------------------------------------------ */ + +/* dump parts of shared memory - only needed during debugging */ + +#ifdef DEBUG +static void dumpmem(struct device *dev, u32 start, u32 len) +{ + int z; + + for (z = 0; z < len; z++) + { + if ((z & 15) == 0) + printk("%04x:", z); + printk(" %02x", readb(dev->mem_start + start + z)); + if ((z & 15) == 15) + printk("\n"); + } +} + +/* print exact time - ditto */ + +static void PrTime(void) +{ + struct timeval tv; + + do_gettimeofday(&tv); + printk("%9d:%06d: ", tv.tv_sec, tv.tv_usec); +} +#endif + +/* deduce resources out of POS registers */ + +static void getaddrs(int slot, int junior, int *base, int *irq, + skmca_medium *medium) +{ + u_char pos0, pos1, pos2; + + if (junior) + { + pos0 = mca_read_stored_pos(slot, 2); + *base = ((pos0 & 0x0e) << 13) + 0xc0000; + *irq = ((pos0 & 0x10) >> 4) + 10; + *medium = Media_Unknown; + } + else + { + /* reset POS 104 Bits 0+1 so the shared memory region goes to the + configured area between 640K and 1M. Afterwards, enable the MC2. + I really don't know what rode SK to do this... */ + + mca_write_pos(slot, 4, mca_read_stored_pos(slot, 4) & 0xfc); + mca_write_pos(slot, 2, mca_read_stored_pos(slot, 2) | 0x01); + + pos1 = mca_read_stored_pos(slot, 3); + pos2 = mca_read_stored_pos(slot, 4); + *base = ((pos1 & 0x07) << 14) + 0xc0000; + switch (pos2 & 0x0c) + { + case 0: *irq = 3; break; + case 4: *irq = 5; break; + case 8: *irq = 10; break; + case 12: *irq = 11; break; + } + *medium = (pos2 >> 6) & 3; + } +} + +/* check for both cards: + When the MC2 is turned off, it was configured for more than 15MB RAM, + is disabled and won't get detected using the standard probe. We + therefore have to scan the slots manually :-( */ + +static int dofind(int *junior, int firstslot) +{ + int slot; + unsigned int id; + + for (slot = firstslot; slot < MCA_MAX_SLOT_NR; slot++) + { + id = mca_read_stored_pos(slot, 0) + + (((unsigned int) mca_read_stored_pos(slot, 1)) << 8); + + *junior = 0; + if (id == SKNET_MCA_ID) + return slot; + *junior = 1; + if (id == SKNET_JUNIOR_MCA_ID) + return slot; + } + return MCA_NOTFOUND; +} + +/* reset the whole board */ + +static void ResetBoard(struct device *dev) +{ + skmca_priv *priv = (skmca_priv*) dev->priv; + + writeb(CTRL_RESET_ON, priv->ctrladdr); + udelay(10); + writeb(CTRL_RESET_OFF, priv->ctrladdr); +} + +/* set LANCE register - must be atomic */ + +static void SetLANCE(struct device *dev, u16 addr, u16 value) +{ + skmca_priv *priv = (skmca_priv*) dev->priv; + unsigned long flags; + + /* disable interrupts */ + + save_flags(flags); + cli(); + + /* wait until no transfer is pending */ + + while ((readb(priv->ctrladdr) & STAT_IO_BUSY) == STAT_IO_BUSY); + + /* transfer register address to RAP */ + + writeb(CTRL_RESET_OFF | CTRL_RW_WRITE | CTRL_ADR_RAP, priv->ctrladdr); + writew(addr, priv->ioregaddr); + writeb(IOCMD_GO, priv->cmdaddr); + udelay(1); + while ((readb(priv->ctrladdr) & STAT_IO_BUSY) == STAT_IO_BUSY); + + /* transfer data to register */ + + writeb(CTRL_RESET_OFF | CTRL_RW_WRITE | CTRL_ADR_DATA, priv->ctrladdr); + writew(value, priv->ioregaddr); + writeb(IOCMD_GO, priv->cmdaddr); + udelay(1); + while ((readb(priv->ctrladdr) & STAT_IO_BUSY) == STAT_IO_BUSY); + + /* reenable interrupts */ + + restore_flags(flags); +} + +/* get LANCE register */ + +static u16 GetLANCE(struct device *dev, u16 addr) +{ + skmca_priv *priv = (skmca_priv*) dev->priv; + unsigned long flags; + unsigned int res; + + /* disable interrupts */ + + save_flags(flags); + cli(); + + /* wait until no transfer is pending */ + + while ((readb(priv->ctrladdr) & STAT_IO_BUSY) == STAT_IO_BUSY); + + /* transfer register address to RAP */ + + writeb(CTRL_RESET_OFF | CTRL_RW_WRITE | CTRL_ADR_RAP, priv->ctrladdr); + writew(addr, priv->ioregaddr); + writeb(IOCMD_GO, priv->cmdaddr); + udelay(1); + while ((readb(priv->ctrladdr) & STAT_IO_BUSY) == STAT_IO_BUSY); + + /* transfer data from register */ + + writeb(CTRL_RESET_OFF | CTRL_RW_READ | CTRL_ADR_DATA, priv->ctrladdr); + writeb(IOCMD_GO, priv->cmdaddr); + udelay(1); + while ((readb(priv->ctrladdr) & STAT_IO_BUSY) == STAT_IO_BUSY); + res = readw(priv->ioregaddr); + + /* reenable interrupts */ + + restore_flags(flags); + + return res; +} + +/* build up descriptors in shared RAM */ + +static void InitDscrs(struct device *dev) +{ + u32 bufaddr; + + /* Set up Tx descriptors. The board has only 16K RAM so bits 16..23 + are always 0. */ + + bufaddr = RAM_DATABASE; + { + LANCE_TxDescr descr; + int z; + + for (z = 0; z < TXCOUNT; z++) + { + descr.LowAddr = bufaddr; + descr.Flags = 0; + descr.Len = 0xf000; + descr.Status = 0; + memcpy_toio(dev->mem_start + RAM_TXBASE + (z * sizeof(LANCE_TxDescr)), + &descr, sizeof(LANCE_TxDescr)); + memset_io(dev->mem_start + bufaddr, 0, RAM_BUFSIZE); + bufaddr += RAM_BUFSIZE; + } + } + + /* do the same for the Rx descriptors */ + + { + LANCE_RxDescr descr; + int z; + + for (z = 0; z < RXCOUNT; z++) + { + descr.LowAddr = bufaddr; + descr.Flags = RXDSCR_FLAGS_OWN; + descr.MaxLen = -RAM_BUFSIZE; + descr.Len = 0; + memcpy_toio(dev->mem_start + RAM_RXBASE + (z * sizeof(LANCE_RxDescr)), + &descr, sizeof(LANCE_RxDescr)); + memset_io(dev->mem_start + bufaddr, 0, RAM_BUFSIZE); + bufaddr += RAM_BUFSIZE; + } + } +} + +/* calculate the hash bit position for a given multicast address + taken more or less directly from the AMD datasheet... */ + +static void UpdateCRC(unsigned char *CRC, int bit) +{ + int j; + + /* shift CRC one bit */ + + memmove(CRC + 1, CRC, 32 * sizeof(unsigned char)); + CRC[0] = 0; + + /* if bit XOR controlbit = 1, set CRC = CRC XOR polynomial */ + + if (bit ^ CRC[32]) + for (j = 0; j < 32; j++) + CRC[j] ^= poly[j]; +} + +static unsigned int GetHash(char *address) +{ + unsigned char CRC[33]; + int i, byte, hashcode; + + /* a multicast address has bit 0 in the first byte set */ + + if ((address[0] & 1) == 0) + return -1; + + /* initialize CRC */ + + memset(CRC, 1, sizeof(CRC)); + + /* loop through address bits */ + + for (byte = 0; byte < 6; byte++) + for (i = 0; i < 8; i++) + UpdateCRC(CRC, (address[byte] >> i) & 1); + + /* hashcode is the 6 least significant bits of the CRC */ + + hashcode = 0; + for (i = 0; i < 6; i++) + hashcode = (hashcode << 1) + CRC[i]; + return hashcode; +} + +/* feed ready-built initialization block into LANCE */ + +static void InitLANCE(struct device *dev) +{ + skmca_priv *priv = (skmca_priv*) dev->priv; + + /* build up descriptors. */ + + InitDscrs(dev); + + /* next RX descriptor to be read is the first one. Since the LANCE + will start from the beginning after initialization, we have to + reset out pointers too. */ + + priv->nextrx = 0; + + /* no TX descriptors active */ + + priv->nexttxput = priv->nexttxdone = priv->txbusy = 0; + + /* set up the LANCE bus control register - constant for SKnet boards */ + + SetLANCE(dev, LANCE_CSR3, CSR3_BSWAP_OFF | CSR3_ALE_LOW | CSR3_BCON_HOLD); + + /* write address of initialization block into LANCE */ + + SetLANCE(dev, LANCE_CSR1, RAM_INITBASE & 0xffff); + SetLANCE(dev, LANCE_CSR2, (RAM_INITBASE >> 16) & 0xff); + + /* we don't get ready until the LANCE has read the init block */ + + dev->tbusy = 1; + + /* let LANCE read the initialization block. LANCE is ready + when we receive the corresponding interrupt. */ + + SetLANCE(dev, LANCE_CSR0, CSR0_INEA | CSR0_INIT); +} + +/* stop the LANCE so we can reinitialize it */ + +static void StopLANCE(struct device *dev) +{ + /* can't take frames any more */ + + dev->tbusy = 1; + + /* disable interrupts, stop it */ + + SetLANCE(dev, LANCE_CSR0, CSR0_STOP); +} + +/* initialize card and LANCE for proper operation */ + +static void InitBoard(struct device *dev) +{ + LANCE_InitBlock block; + + /* Lay out the shared RAM - first we create the init block for the LANCE. + We do not overwrite it later because we need it again when we switch + promiscous mode on/off. */ + + block.Mode = 0; + if (dev->flags & IFF_PROMISC) + block.Mode |= LANCE_INIT_PROM; + memcpy(block.PAdr, dev->dev_addr, 6); + memset(block.LAdrF, 0, sizeof(block.LAdrF)); + block.RdrP = (RAM_RXBASE & 0xffffff) | (LRXCOUNT << 29); + block.TdrP = (RAM_TXBASE & 0xffffff) | (LTXCOUNT << 29); + + memcpy_toio(dev->mem_start + RAM_INITBASE, &block, sizeof(block)); + + /* initialize LANCE. Implicitly sets up other structures in RAM. */ + + InitLANCE(dev); +} + +/* deinitialize card and LANCE */ + +static void DeinitBoard(struct device *dev) +{ + /* stop LANCE */ + + StopLANCE(dev); + + /* reset board */ + + ResetBoard(dev); +} + +/* ------------------------------------------------------------------------ + * interrupt handler(s) + * ------------------------------------------------------------------------ */ + +/* LANCE has read initializazion block -> start it */ + +static u16 irqstart_handler(struct device *dev, u16 oldcsr0) +{ + /* now we're ready to transmit */ + + dev->tbusy = 0; + + /* reset IDON bit, start LANCE */ + + SetLANCE(dev, LANCE_CSR0, oldcsr0 | CSR0_IDON | CSR0_STRT); + return GetLANCE(dev, LANCE_CSR0); +} + +/* receive interrupt */ + +static u16 irqrx_handler(struct device *dev, u16 oldcsr0) +{ + skmca_priv *priv = (skmca_priv*) dev->priv; + LANCE_RxDescr descr; + unsigned int descraddr; + + /* did we loose blocks due to a FIFO overrun ? */ + + if (oldcsr0 & CSR0_MISS) + priv->stat.rx_fifo_errors++; + + /* run through queue until we reach a descriptor we do not own */ + + descraddr = RAM_RXBASE + (priv->nextrx * sizeof(LANCE_RxDescr)); + while (1) + { + /* read descriptor */ + memcpy_fromio(&descr, dev->mem_start + descraddr, sizeof(LANCE_RxDescr)); + + /* if we reach a descriptor we do not own, we're done */ + if ((descr.Flags & RXDSCR_FLAGS_OWN) != 0) + break; + +#ifdef DEBUG + PrTime(); printk("Receive packet on descr %d len %d\n", priv->nextrx, descr.Len); +#endif + + /* erroneous packet ? */ + if ((descr.Flags & RXDSCR_FLAGS_ERR) != 0) + { + priv->stat.rx_errors++; + if ((descr.Flags & RXDSCR_FLAGS_CRC) != 0) + priv->stat.rx_crc_errors++; + else if ((descr.Flags & RXDSCR_FLAGS_CRC) != 0) + priv->stat.rx_frame_errors++; + else if ((descr.Flags & RXDSCR_FLAGS_OFLO) != 0) + priv->stat.rx_fifo_errors++; + } + + /* good packet ? */ + else + { + struct sk_buff *skb; + + skb = dev_alloc_skb(descr.Len + 2); + if (skb == NULL) + priv->stat.rx_dropped++; + else + { + memcpy_fromio(skb_put(skb, descr.Len), + dev->mem_start + descr.LowAddr, descr.Len); + skb->dev = dev; + skb->protocol = eth_type_trans(skb, dev); + skb->ip_summed = CHECKSUM_NONE; + priv->stat.rx_packets++; +#if LINUX_VERSION_CODE >= 0x020119 /* byte counters for >= 2.1.25 */ + priv->stat.rx_bytes += descr.Len; +#endif + netif_rx(skb); + } + } + + /* give descriptor back to LANCE */ + descr.Len = 0; + descr.Flags |= RXDSCR_FLAGS_OWN; + + /* update descriptor in shared RAM */ + memcpy_toio(dev->mem_start + descraddr, &descr, sizeof(LANCE_RxDescr)); + + /* go to next descriptor */ + priv->nextrx++; descraddr += sizeof(LANCE_RxDescr); + if (priv->nextrx >= RXCOUNT) + { + priv->nextrx = 0; + descraddr = RAM_RXBASE; + } + } + + /* reset RINT bit */ + + SetLANCE(dev, LANCE_CSR0, oldcsr0 | CSR0_RINT); + return GetLANCE(dev, LANCE_CSR0); +} + +/* transmit interrupt */ + +static u16 irqtx_handler(struct device *dev, u16 oldcsr0) +{ + skmca_priv *priv = (skmca_priv*) dev->priv; + LANCE_TxDescr descr; + unsigned int descraddr; + + /* check descriptors at most until no busy one is left */ + + descraddr = RAM_TXBASE + (priv->nexttxdone * sizeof(LANCE_TxDescr)); + while (priv->txbusy > 0) + { + /* read descriptor */ + memcpy_fromio(&descr, dev->mem_start + descraddr, sizeof(LANCE_TxDescr)); + + /* if the LANCE still owns this one, we've worked out all sent packets */ + if ((descr.Flags & TXDSCR_FLAGS_OWN) != 0) + break; + +#ifdef DEBUG + PrTime(); printk("Send packet done on descr %d\n", priv->nexttxdone); +#endif + + /* update statistics */ + if ((descr.Flags & TXDSCR_FLAGS_ERR) == 0) + { + priv->stat.tx_packets++; +#if LINUX_VERSION_CODE >= 0x020119 /* byte counters for >= 2.1.25 */ + priv->stat.tx_bytes++; +#endif + } + else + { + priv->stat.tx_errors++; + if ((descr.Status & TXDSCR_STATUS_UFLO) != 0) + { + priv->stat.tx_fifo_errors++; + InitLANCE(dev); + } + else if ((descr.Status & TXDSCR_STATUS_LCOL) != 0) + priv->stat.tx_window_errors++; + else if ((descr.Status & TXDSCR_STATUS_LCAR) != 0) + priv->stat.tx_carrier_errors++; + else if ((descr.Status & TXDSCR_STATUS_RTRY) != 0) + priv->stat.tx_aborted_errors++; + } + + /* go to next descriptor */ + priv->nexttxdone++; + descraddr += sizeof(LANCE_TxDescr); + if (priv->nexttxdone >= TXCOUNT) + { + priv->nexttxdone = 0; + descraddr = RAM_TXBASE; + } + priv->txbusy--; + } + + /* reset TX interrupt bit */ + + SetLANCE(dev, LANCE_CSR0, oldcsr0 | CSR0_TINT); + oldcsr0 = GetLANCE(dev, LANCE_CSR0); + + /* at least one descriptor is freed. Therefore we can accept + a new one */ + + dev->tbusy = 0; + + /* inform upper layers we're in business again */ + + mark_bh(NET_BH); + + return oldcsr0; +} + +/* general interrupt entry */ + +static void irq_handler(int irq, void *device, struct pt_regs *regs) +{ + struct device *dev = (struct device*) device; + u16 csr0val; + + /* read CSR0 to get interrupt cause */ + + csr0val = GetLANCE(dev, LANCE_CSR0); + + /* in case we're not meant... */ + + if ((csr0val & CSR0_INTR) == 0) + return; + + dev->interrupt = 1; + + /* loop through the interrupt bits until everything is clear */ + + do + { + if ((csr0val & CSR0_IDON) != 0) + csr0val = irqstart_handler(dev, csr0val); + if ((csr0val & CSR0_RINT) != 0) + csr0val = irqrx_handler(dev, csr0val); + if ((csr0val & CSR0_TINT) != 0) + csr0val = irqtx_handler(dev, csr0val); + } + while ((csr0val & CSR0_INTR) != 0); + + dev->interrupt = 0; +} + +/* ------------------------------------------------------------------------ + * driver methods + * ------------------------------------------------------------------------ */ + +/* MCA info */ + +static int skmca_getinfo(char *buf, int slot, void *d) +{ + int len = 0, i; + struct device *dev = (struct device*) d; + skmca_priv *priv; + + /* can't say anything about an uninitialized device... */ + + if (dev == NULL) + return len; + if (dev->priv == NULL) + return len; + priv = (skmca_priv*) dev->priv; + + /* print info */ + + len += sprintf(buf + len, "IRQ: %d\n", priv->realirq); + len += sprintf(buf + len, "Memory: %#lx-%#lx\n", dev->mem_start, + dev->mem_end - 1); + len += sprintf(buf + len, "Transceiver: %s\n", MediaNames[priv->medium]); + len += sprintf(buf + len, "Device: %s\n", dev->name); + len += sprintf(buf + len, "MAC address:"); + for (i = 0; i < 6; i ++ ) + len += sprintf( buf+len, " %02x", dev->dev_addr[i] ); + buf[len++] = '\n'; + buf[len] = 0; + + return len; +} + +/* open driver. Means also initialization and start of LANCE */ + +static int skmca_open(struct device *dev) +{ + int result; + skmca_priv *priv = (skmca_priv*) dev->priv; + + /* register resources - only necessary for IRQ */ + result = request_irq(priv->realirq, irq_handler, SA_SHIRQ | SA_SAMPLE_RANDOM, + "sk_mca", dev); + if (result != 0) + { + printk("%s: failed to register irq %d\n", dev->name, dev->irq); + return result; + } + dev->irq = priv->realirq; + + /* set up the card and LANCE */ + InitBoard(dev); + +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif + + return 0; +} + +/* close driver. Shut down board and free allocated resources */ + +static int skmca_close(struct device *dev) +{ + /* turn off board */ + DeinitBoard(dev); + + /* release resources */ + if (dev->irq != 0) + free_irq(dev->irq, dev); + dev->irq = 0; + +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif + + return 0; +} + +/* transmit a block. */ + +static int skmca_tx(struct sk_buff *skb, struct device *dev) +{ + skmca_priv *priv = (skmca_priv*) dev->priv; + LANCE_TxDescr descr; + unsigned int address; + int tmplen, retval = 0; + unsigned long flags; + + /* if we get called with a NULL descriptor, the Ethernet layer thinks + our card is stuck an we should reset it. We'll do this completely: */ + + if (skb == NULL) + { + DeinitBoard(dev); + InitBoard(dev); + return 0; /* don't try to free the block here ;-) */ + } + + /* is there space in the Tx queue ? If no, the upper layer gave us a + packet in spite of us not being ready and is really in trouble. + We'll do the dropping for him: */ + if (priv->txbusy >= TXCOUNT) + { + priv->stat.tx_dropped++; + retval = -EIO; + goto tx_done; + } + + /* get TX descriptor */ + address = RAM_TXBASE + (priv->nexttxput * sizeof(LANCE_TxDescr)); + memcpy_fromio(&descr, dev->mem_start + address, sizeof(LANCE_TxDescr)); + + /* enter packet length as 2s complement - assure minimum length */ + tmplen = skb->len; + if (tmplen < 60) + tmplen = 60; + descr.Len = 65536 - tmplen; + + /* copy filler into RAM - in case we're filling up... + we're filling a bit more than necessary, but that doesn't harm + since the buffer is far larger... */ + if (tmplen > skb->len) + { + char *fill = "NetBSD is a nice OS too! "; + unsigned int destoffs = 0, l = strlen(fill); + + while (destoffs < tmplen) + { + memcpy_toio(dev->mem_start + descr.LowAddr + destoffs, fill, l); + destoffs += l; + } + } + + /* do the real data copying */ + memcpy_toio(dev->mem_start + descr.LowAddr, skb->data, skb->len); + + /* hand descriptor over to LANCE - this is the first and last chunk */ + descr.Flags = TXDSCR_FLAGS_OWN | TXDSCR_FLAGS_STP | TXDSCR_FLAGS_ENP; + +#ifdef DEBUG + PrTime(); printk("Send packet on descr %d len %d\n", priv->nexttxput, skb->len); +#endif + + /* one more descriptor busy */ + save_flags(flags); + cli(); + priv->nexttxput++; + if (priv->nexttxput >= TXCOUNT) + priv->nexttxput = 0; + priv->txbusy++; + dev->tbusy = (priv->txbusy >= TXCOUNT); + + /* write descriptor back to RAM */ + memcpy_toio(dev->mem_start + address, &descr, sizeof(LANCE_TxDescr)); + + /* if no descriptors were active, give the LANCE a hint to read it + immediately */ + + if (priv->txbusy == 0) + SetLANCE(dev, LANCE_CSR0, CSR0_INEA | CSR0_TDMD); + + restore_flags(flags); + +tx_done: + + /* When did that change exactly ? */ + +#if LINUX_VERSION_CODE >= 0x020200 + dev_kfree_skb(skb); +#else + dev_kfree_skb(skb, FREE_WRITE); +#endif + return retval; +} + +/* return pointer to Ethernet statistics */ + +static struct enet_statistics *skmca_stats(struct device *dev) +{ + skmca_priv *priv = (skmca_priv*) dev->priv; + + return &(priv->stat); +} + +/* we don't support runtime reconfiguration, since am MCA card can + be unambigously identified by its POS registers. */ + +static int skmca_config(struct device *dev, struct ifmap *map) +{ + return 0; +} + +/* switch receiver mode. We use the LANCE's multicast filter to prefilter + multicast addresses. */ + +static void skmca_set_multicast_list(struct device *dev) +{ + LANCE_InitBlock block; + + /* first stop the LANCE... */ + StopLANCE(dev); + + /* ...then modify the initialization block... */ + memcpy_fromio(&block, dev->mem_start + RAM_INITBASE, sizeof(block)); + if (dev->flags & IFF_PROMISC) + block.Mode |= LANCE_INIT_PROM; + else + block.Mode &= ~LANCE_INIT_PROM; + + if (dev->flags & IFF_ALLMULTI) /* get all multicasts */ + { + memset(block.LAdrF, 8, 0xff); + } + else /* get selected/no multicasts */ + { + struct dev_mc_list *mptr; + int code; + + memset(block.LAdrF, 8, 0x00); + for (mptr = dev->mc_list; mptr != NULL; mptr = mptr->next) + { + code = GetHash(mptr->dmi_addr); + block.LAdrF[(code >> 3) & 7] |= 1 << (code & 7); + } + } + + memcpy_toio(dev->mem_start + RAM_INITBASE, &block, sizeof(block)); + + /* ...then reinit LANCE with the correct flags */ + InitLANCE(dev); +} + +/* ------------------------------------------------------------------------ + * hardware check + * ------------------------------------------------------------------------ */ + +#ifdef MODULE +static int startslot; /* counts through slots when probing multiple devices */ +#else +#define startslot 0 /* otherwise a dummy, since there is only eth0 in-kern*/ +#endif + +int skmca_probe(struct device *dev) +{ + int force_detect = 0; + int junior, slot, i; + int base = 0, irq = 0; + skmca_priv *priv; + skmca_medium medium; + + /* can't work without an MCA bus ;-) */ + + if (MCA_bus == 0) + return ENODEV; + + /* start address of 1 --> forced detection */ + + if (dev->mem_start == 1) + force_detect = 1; + + /* search through slots */ + + if (dev != NULL) + { + base = dev->mem_start; + irq = dev->irq; + } + slot = dofind(&junior, startslot); + + while (slot != -1) + { + /* deduce card addresses */ + + getaddrs(slot, junior, &base, &irq, &medium); + +#if 0 + /* this should work, but it doesn't with 2.2.9 :-( + somehow 'mca_is_adapter_used()' is missing in kernel syms... */ +#if LINUX_VERSION_CODE >= 0x020200 + /* slot already in use ? */ + + if (mca_is_adapter_used(slot)) + { + slot = dofind(&junior, slot + 1); + continue; + } +#endif +#endif + + /* were we looking for something different ? */ + + if ((dev->irq != 0) || (dev->mem_start != 0)) + { + if ((dev->irq != 0) && (dev->irq != irq)) + { + slot = dofind(&junior, slot + 1); + continue; + } + if ((dev->mem_start != 0) && (dev->mem_start != base)) + { + slot = dofind(&junior, slot + 1); + continue; + } + } + + /* found something that matches */ + + break; + } + + /* nothing found ? */ + + if (slot == -1) + return ((base != 0) || (irq != 0)) ? ENXIO : ENODEV; + + /* make procfs entries */ + + if (junior) + mca_set_adapter_name(slot, "SKNET junior MC2 Ethernet Adapter"); + else + mca_set_adapter_name(slot, "SKNET MC2+ Ethernet Adapter"); + mca_set_adapter_procfn(slot, (MCA_ProcFn) skmca_getinfo, dev); + +#if LINUX_VERSION_CODE >= 0x020200 + mca_mark_as_used(slot); +#endif + + /* announce success */ + printk("%s: SKNet %s adapter found in slot %d\n", dev->name, + junior ? "Junior MC2" : "MC2+", slot + 1); + + /* allocate structure */ + priv = dev->priv = (skmca_priv*) kmalloc(sizeof(skmca_priv), GFP_KERNEL); + priv->slot = slot; + priv->macbase = base + 0x3fc0; + priv->ioregaddr = base + 0x3ff0; + priv->ctrladdr = base + 0x3ff2; + priv->cmdaddr = base + 0x3ff3; + priv->realirq = irq; + priv->medium = medium; + memset(&(priv->stat), 0, sizeof(struct enet_statistics)); + + /* set base + irq for this device (irq not allocated so far) */ + dev->irq = 0; + dev->mem_start = base; + dev->mem_end = base + 0x4000; + + /* set methods */ + dev->open = skmca_open; + dev->stop = skmca_close; + dev->set_config = skmca_config; + dev->hard_start_xmit = skmca_tx; + dev->do_ioctl = NULL; + dev->get_stats = skmca_stats; + dev->set_multicast_list = skmca_set_multicast_list; + dev->flags |= IFF_MULTICAST; + + /* generic setup */ + ether_setup(dev); + dev->interrupt = 0; + dev->tbusy = 0; + dev->start = 0; + + /* copy out MAC address */ + for (i = 0; i < 6; i++) + dev->dev_addr[i] = readb(priv->macbase + (i << 1)); + + /* print config */ + printk("%s: IRQ %d, memory %#lx-%#lx, " + "MAC address %02x:%02x:%02x:%02x:%02x:%02x.\n", + dev->name, priv->realirq, dev->mem_start, dev->mem_end - 1, + dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2], + dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]); + printk("%s: %s medium\n", dev->name, MediaNames[priv->medium]); + + /* reset board */ + + ResetBoard(dev); + +#ifdef MODULE + startslot = slot + 1; +#endif + + return 0; +} + +/* ------------------------------------------------------------------------ + * modularization support + * ------------------------------------------------------------------------ */ + +#ifdef MODULE + +#define DEVMAX 5 + +static char NameSpace[8 * DEVMAX]; +static struct device moddevs[DEVMAX] = + {{NameSpace + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, skmca_probe}, + {NameSpace + 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, skmca_probe}, + {NameSpace + 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, skmca_probe}, + {NameSpace + 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, skmca_probe}, + {NameSpace + 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, skmca_probe}}; + +int irq=0; +int io=0; + +int init_module(void) +{ + int z, res; + + startslot = 0; + for (z = 0; z < DEVMAX; z++) + { + strcpy(moddevs[z].name, " "); + res = register_netdev(moddevs + z); + if (res != 0) + return (z > 0) ? 0 : -EIO; + } + + return 0; +} + +void cleanup_module(void) +{ + struct device *dev; + skmca_priv *priv; + int z; + + if (MOD_IN_USE) + { + printk("cannot unload, module in use\n"); + return; + } + + for (z = 0; z < DEVMAX; z++) + { + dev = moddevs + z; + if (dev->priv != NULL) + { + priv = (skmca_priv*) dev->priv; + DeinitBoard(dev); + if (dev->irq != 0) + free_irq(dev->irq, dev); + dev->irq = 0; + unregister_netdev(dev); +#if LINUX_VERSION_CODE >= 0x020200 + mca_mark_as_unused(priv->slot); +#endif + mca_set_adapter_procfn(priv->slot, NULL, NULL); + kfree_s(dev->priv, sizeof(skmca_priv)); + dev->priv = NULL; + } + } +} +#endif /* MODULE */ |