dgrs.c
上传用户:jlfgdled
上传日期:2013-04-10
资源大小:33168k
文件大小:37k
- /*
- * Digi RightSwitch SE-X loadable device driver for Linux
- *
- * The RightSwitch is a 4 (EISA) or 6 (PCI) port etherswitch and
- * a NIC on an internal board.
- *
- * Author: Rick Richardson, rick@remotepoint.com
- * Derived from the SVR4.2 (UnixWare) driver for the same card.
- *
- * Copyright 1995-1996 Digi International Inc.
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- * For information on purchasing a RightSwitch SE-4 or SE-6
- * board, please contact Digi's sales department at 1-612-912-3444
- * or 1-800-DIGIBRD. Outside the U.S., please check our Web page
- * at http://www.dgii.com for sales offices worldwide.
- *
- * OPERATION:
- * When compiled as a loadable module, this driver can operate
- * the board as either a 4/6 port switch with a 5th or 7th port
- * that is a conventional NIC interface as far as the host is
- * concerned, OR as 4/6 independent NICs. To select multi-NIC
- * mode, add "nicmode=1" on the insmod load line for the driver.
- *
- * This driver uses the "dev" common ethernet device structure
- * and a private "priv" (dev->priv) structure that contains
- * mostly DGRS-specific information and statistics. To keep
- * the code for both the switch mode and the multi-NIC mode
- * as similar as possible, I have introduced the concept of
- * "dev0"/"priv0" and "devN"/"privN" pointer pairs in subroutines
- * where needed. The first pair of pointers points to the
- * "dev" and "priv" structures of the zeroth (0th) device
- * interface associated with a board. The second pair of
- * pointers points to the current (Nth) device interface
- * for the board: the one for which we are processing data.
- *
- * In switch mode, the pairs of pointers are always the same,
- * that is, dev0 == devN and priv0 == privN. This is just
- * like previous releases of this driver which did not support
- * NIC mode.
- *
- * In multi-NIC mode, the pairs of pointers may be different.
- * We use the devN and privN pointers to reference just the
- * name, port number, and statistics for the current interface.
- * We use the dev0 and priv0 pointers to access the variables
- * that control access to the board, such as board address
- * and simulated 82596 variables. This is because there is
- * only one "fake" 82596 that serves as the interface to
- * the board. We do not want to try to keep the variables
- * associated with this 82596 in sync across all devices.
- *
- * This scheme works well. As you will see, except for
- * initialization, there is very little difference between
- * the two modes as far as this driver is concerned. On the
- * receive side in NIC mode, the interrupt *always* comes in on
- * the 0th interface (dev0/priv0). We then figure out which
- * real 82596 port it came in on from looking at the "chan"
- * member that the board firmware adds at the end of each
- * RBD (a.k.a. TBD). We get the channel number like this:
- * int chan = ((I596_RBD *) S2H(cbp->xmit.tbdp))->chan;
- *
- * On the transmit side in multi-NIC mode, we specify the
- * output 82596 port by setting the new "dstchan" structure
- * member that is at the end of the RFD, like this:
- * priv0->rfdp->dstchan = privN->chan;
- *
- * TODO:
- * - Multi-NIC mode is not yet supported when the driver is linked
- * into the kernel.
- * - Better handling of multicast addresses.
- *
- * Fixes:
- * Arnaldo Carvalho de Melo <acme@conectiva.com.br> - 11/01/2001
- * - fix dgrs_found_device wrt checking kmalloc return and
- * rollbacking the partial steps of the whole process when
- * one of the devices can't be allocated. Fix SET_MODULE_OWNER
- * on the loop to use devN instead of repeated calls to dev.
- *
- * davej <davej@suse.de> - 9/2/2001
- * - Enable PCI device before reading ioaddr/irq
- *
- */
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <linux/sched.h>
- #include <linux/string.h>
- #include <linux/delay.h>
- #include <linux/errno.h>
- #include <linux/ioport.h>
- #include <linux/slab.h>
- #include <linux/interrupt.h>
- #include <linux/pci.h>
- #include <linux/init.h>
- #include <linux/netdevice.h>
- #include <linux/etherdevice.h>
- #include <linux/skbuff.h>
- #include <asm/bitops.h>
- #include <asm/io.h>
- #include <asm/byteorder.h>
- #include <asm/uaccess.h>
- static char version[] __initdata =
- "$Id: dgrs.c,v 1.13 2000/06/06 04:07:00 rick Exp $";
- /*
- * DGRS include files
- */
- typedef unsigned char uchar;
- typedef unsigned int bool;
- #define vol volatile
- #include "dgrs.h"
- #include "dgrs_es4h.h"
- #include "dgrs_plx9060.h"
- #include "dgrs_i82596.h"
- #include "dgrs_ether.h"
- #include "dgrs_asstruct.h"
- #include "dgrs_bcomm.h"
- static struct pci_device_id dgrs_pci_tbl[] __initdata = {
- { SE6_PCI_VENDOR_ID, SE6_PCI_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, },
- { } /* Terminating entry */
- };
- MODULE_DEVICE_TABLE(pci, dgrs_pci_tbl);
- MODULE_LICENSE("GPL");
- /*
- * Firmware. Compiled separately for local compilation,
- * but #included for Linux distribution.
- */
- #ifndef NOFW
- #include "dgrs_firmware.c"
- #else
- extern int dgrs_firmnum;
- extern char dgrs_firmver[];
- extern char dgrs_firmdate[];
- extern uchar dgrs_code[];
- extern int dgrs_ncode;
- #endif
- /*
- * Linux out*() is backwards from all other operating systems
- */
- #define OUTB(ADDR, VAL) outb(VAL, ADDR)
- #define OUTW(ADDR, VAL) outw(VAL, ADDR)
- #define OUTL(ADDR, VAL) outl(VAL, ADDR)
- /*
- * Macros to convert switch to host and host to switch addresses
- * (assumes a local variable priv points to board dependent struct)
- */
- #define S2H(A) ( ((unsigned long)(A)&0x00ffffff) + priv0->vmem )
- #define S2HN(A) ( ((unsigned long)(A)&0x00ffffff) + privN->vmem )
- #define H2S(A) ( ((char *) (A) - priv0->vmem) + 0xA3000000 )
- /*
- * Convert a switch address to a "safe" address for use with the
- * PLX 9060 DMA registers and the associated HW kludge that allows
- * for host access of the DMA registers.
- */
- #define S2DMA(A) ( (unsigned long)(A) & 0x00ffffff)
- /*
- * "Space.c" variables, now settable from module interface
- * Use the name below, minus the "dgrs_" prefix. See init_module().
- */
- static int dgrs_debug = 1;
- static int dgrs_dma = 1;
- static int dgrs_spantree = -1;
- static int dgrs_hashexpire = -1;
- static uchar dgrs_ipaddr[4] = { 0xff, 0xff, 0xff, 0xff};
- static uchar dgrs_iptrap[4] = { 0xff, 0xff, 0xff, 0xff};
- static __u32 dgrs_ipxnet = -1;
- static int dgrs_nicmode;
- /*
- * Chain of device structures
- */
- static struct net_device *dgrs_root_dev;
- /*
- * Private per-board data structure (dev->priv)
- */
- typedef struct
- {
- /*
- * Stuff for generic ethercard I/F
- */
- struct net_device *next_dev;
- struct net_device_stats stats;
- /*
- * DGRS specific data
- */
- char *vmem;
- struct bios_comm *bcomm; /* Firmware BIOS comm structure */
- PORT *port; /* Ptr to PORT[0] struct in VM */
- I596_SCB *scbp; /* Ptr to SCB struct in VM */
- I596_RFD *rfdp; /* Current RFD list */
- I596_RBD *rbdp; /* Current RBD list */
- volatile int intrcnt; /* Count of interrupts */
- /*
- * SE-4 (EISA) board variables
- */
- uchar is_reg; /* EISA: Value for ES4H_IS reg */
- /*
- * SE-6 (PCI) board variables
- *
- * The PLX "expansion rom" space is used for DMA register
- * access from the host on the SE-6. These are the physical
- * and virtual addresses of that space.
- */
- ulong plxreg; /* Phys address of PLX chip */
- char *vplxreg; /* Virtual address of PLX chip */
- ulong plxdma; /* Phys addr of PLX "expansion rom" */
- ulong volatile *vplxdma; /* Virtual addr of "expansion rom" */
- int use_dma; /* Flag: use DMA */
- DMACHAIN *dmadesc_s; /* area for DMA chains (SW addr.) */
- DMACHAIN *dmadesc_h; /* area for DMA chains (Host Virtual) */
- /*
- * Multi-NIC mode variables
- *
- * All entries of the devtbl[] array are valid for the 0th
- * device (i.e. eth0, but not eth1...eth5). devtbl[0] is
- * valid for all devices (i.e. eth0, eth1, ..., eth5).
- */
- int nports; /* Number of physical ports (4 or 6) */
- int chan; /* Channel # (1-6) for this device */
- struct net_device *devtbl[6]; /* Ptrs to N device structs */
- } DGRS_PRIV;
- /*
- * reset or un-reset the IDT processor
- */
- static void
- proc_reset(struct net_device *dev0, int reset)
- {
- DGRS_PRIV *priv0 = (DGRS_PRIV *) dev0->priv;
- if (priv0->plxreg)
- {
- ulong val;
- val = inl(dev0->base_addr + PLX_MISC_CSR);
- if (reset)
- val |= SE6_RESET;
- else
- val &= ~SE6_RESET;
- OUTL(dev0->base_addr + PLX_MISC_CSR, val);
- }
- else
- {
- OUTB(dev0->base_addr + ES4H_PC, reset ? ES4H_PC_RESET : 0);
- }
- }
- /*
- * See if the board supports bus master DMA
- */
- static int
- check_board_dma(struct net_device *dev0)
- {
- DGRS_PRIV *priv0 = (DGRS_PRIV *) dev0->priv;
- ulong x;
- /*
- * If Space.c says not to use DMA, or if its not a PLX based
- * PCI board, or if the expansion ROM space is not PCI
- * configured, then return false.
- */
- if (!dgrs_dma || !priv0->plxreg || !priv0->plxdma)
- return (0);
- /*
- * Set the local address remap register of the "expansion rom"
- * area to 0x80000000 so that we can use it to access the DMA
- * registers from the host side.
- */
- OUTL(dev0->base_addr + PLX_ROM_BASE_ADDR, 0x80000000);
- /*
- * Set the PCI region descriptor to:
- * Space 0:
- * disable read-prefetch
- * enable READY
- * enable BURST
- * 0 internal wait states
- * Expansion ROM: (used for host DMA register access)
- * disable read-prefetch
- * enable READY
- * disable BURST
- * 0 internal wait states
- */
- OUTL(dev0->base_addr + PLX_BUS_REGION, 0x49430343);
- /*
- * Now map the DMA registers into our virtual space
- */
- priv0->vplxdma = (ulong *) ioremap (priv0->plxdma, 256);
- if (!priv0->vplxdma)
- {
- printk("%s: can't *remap() the DMA regsn", dev0->name);
- return (0);
- }
- /*
- * Now test to see if we can access the DMA registers
- * If we write -1 and get back 1FFF, then we accessed the
- * DMA register. Otherwise, we probably have an old board
- * and wrote into regular RAM.
- */
- priv0->vplxdma[PLX_DMA0_MODE/4] = 0xFFFFFFFF;
- x = priv0->vplxdma[PLX_DMA0_MODE/4];
- if (x != 0x00001FFF)
- return (0);
- return (1);
- }
- /*
- * Initiate DMA using PLX part on PCI board. Spin the
- * processor until completed. All addresses are physical!
- *
- * If pciaddr is NULL, then its a chaining DMA, and lcladdr is
- * the address of the first DMA descriptor in the chain.
- *
- * If pciaddr is not NULL, then its a single DMA.
- *
- * In either case, "lcladdr" must have been fixed up to make
- * sure the MSB isn't set using the S2DMA macro before passing
- * the address to this routine.
- */
- static int
- do_plx_dma(
- struct net_device *dev,
- ulong pciaddr,
- ulong lcladdr,
- int len,
- int to_host
- )
- {
- int i;
- ulong csr = 0;
- DGRS_PRIV *priv = (DGRS_PRIV *) dev->priv;
- if (pciaddr)
- {
- /*
- * Do a single, non-chain DMA
- */
- priv->vplxdma[PLX_DMA0_PCI_ADDR/4] = pciaddr;
- priv->vplxdma[PLX_DMA0_LCL_ADDR/4] = lcladdr;
- priv->vplxdma[PLX_DMA0_SIZE/4] = len;
- priv->vplxdma[PLX_DMA0_DESCRIPTOR/4] = to_host
- ? PLX_DMA_DESC_TO_HOST
- : PLX_DMA_DESC_TO_BOARD;
- priv->vplxdma[PLX_DMA0_MODE/4] =
- PLX_DMA_MODE_WIDTH32
- | PLX_DMA_MODE_WAITSTATES(0)
- | PLX_DMA_MODE_READY
- | PLX_DMA_MODE_NOBTERM
- | PLX_DMA_MODE_BURST
- | PLX_DMA_MODE_NOCHAIN;
- }
- else
- {
- /*
- * Do a chaining DMA
- */
- priv->vplxdma[PLX_DMA0_MODE/4] =
- PLX_DMA_MODE_WIDTH32
- | PLX_DMA_MODE_WAITSTATES(0)
- | PLX_DMA_MODE_READY
- | PLX_DMA_MODE_NOBTERM
- | PLX_DMA_MODE_BURST
- | PLX_DMA_MODE_CHAIN;
- priv->vplxdma[PLX_DMA0_DESCRIPTOR/4] = lcladdr;
- }
- priv->vplxdma[PLX_DMA_CSR/4] =
- PLX_DMA_CSR_0_ENABLE | PLX_DMA_CSR_0_START;
- /*
- * Wait for DMA to complete
- */
- for (i = 0; i < 1000000; ++i)
- {
- /*
- * Spin the host CPU for 1 usec, so we don't thrash
- * the PCI bus while the PLX 9060 is doing DMA.
- */
- udelay(1);
- csr = (volatile unsigned long) priv->vplxdma[PLX_DMA_CSR/4];
- if (csr & PLX_DMA_CSR_0_DONE)
- break;
- }
- if ( ! (csr & PLX_DMA_CSR_0_DONE) )
- {
- printk("%s: DMA done never occurred. DMA disabled.n",
- dev->name);
- priv->use_dma = 0;
- return 1;
- }
- return 0;
- }
- /*
- * dgrs_rcv_frame()
- *
- * Process a received frame. This is called from the interrupt
- * routine, and works for both switch mode and multi-NIC mode.
- *
- * Note that when in multi-NIC mode, we want to always access the
- * hardware using the dev and priv structures of the first port,
- * so that we are using only one set of variables to maintain
- * the board interface status, but we want to use the Nth port
- * dev and priv structures to maintain statistics and to pass
- * the packet up.
- *
- * Only the first device structure is attached to the interrupt.
- * We use the special "chan" variable at the end of the first RBD
- * to select the Nth device in multi-NIC mode.
- *
- * We currently do chained DMA on a per-packet basis when the
- * packet is "long", and we spin the CPU a short time polling
- * for DMA completion. This avoids a second interrupt overhead,
- * and gives the best performance for light traffic to the host.
- *
- * However, a better scheme that could be implemented would be
- * to see how many packets are outstanding for the host, and if
- * the number is "large", create a long chain to DMA several
- * packets into the host in one go. In this case, we would set
- * up some state variables to let the host CPU continue doing
- * other things until a DMA completion interrupt comes along.
- */
- void
- dgrs_rcv_frame(
- struct net_device *dev0,
- DGRS_PRIV *priv0,
- I596_CB *cbp
- )
- {
- int len;
- I596_TBD *tbdp;
- struct sk_buff *skb;
- uchar *putp;
- uchar *p;
- struct net_device *devN;
- DGRS_PRIV *privN;
- /*
- * Determine Nth priv and dev structure pointers
- */
- if (dgrs_nicmode)
- { /* Multi-NIC mode */
- int chan = ((I596_RBD *) S2H(cbp->xmit.tbdp))->chan;
- devN = priv0->devtbl[chan-1];
- /*
- * If devN is null, we got an interrupt before the I/F
- * has been initialized. Pitch the packet.
- */
- if (devN == NULL)
- goto out;
- privN = (DGRS_PRIV *) devN->priv;
- }
- else
- { /* Switch mode */
- devN = dev0;
- privN = priv0;
- }
- if (0) printk("%s: rcv len=%ldn", devN->name, cbp->xmit.count);
- /*
- * Allocate a message block big enough to hold the whole frame
- */
- len = cbp->xmit.count;
- if ((skb = dev_alloc_skb(len+5)) == NULL)
- {
- printk("%s: dev_alloc_skb failed for rcv buffern", devN->name);
- ++privN->stats.rx_dropped;
- /* discarding the frame */
- goto out;
- }
- skb->dev = devN;
- skb_reserve(skb, 2); /* Align IP header */
- again:
- putp = p = skb_put(skb, len);
- /*
- * There are three modes here for doing the packet copy.
- * If we have DMA, and the packet is "long", we use the
- * chaining mode of DMA. If it's shorter, we use single
- * DMA's. Otherwise, we use memcpy().
- */
- if (priv0->use_dma && priv0->dmadesc_h && len > 64)
- {
- /*
- * If we can use DMA and its a long frame, copy it using
- * DMA chaining.
- */
- DMACHAIN *ddp_h; /* Host virtual DMA desc. pointer */
- DMACHAIN *ddp_s; /* Switch physical DMA desc. pointer */
- uchar *phys_p;
- /*
- * Get the physical address of the STREAMS buffer.
- * NOTE: allocb() guarantees that the whole buffer
- * is in a single page if the length < 4096.
- */
- phys_p = (uchar *) virt_to_phys(putp);
- ddp_h = priv0->dmadesc_h;
- ddp_s = priv0->dmadesc_s;
- tbdp = (I596_TBD *) S2H(cbp->xmit.tbdp);
- for (;;)
- {
- int count;
- int amt;
- count = tbdp->count;
- amt = count & 0x3fff;
- if (amt == 0)
- break; /* For safety */
- if ( (p-putp) >= len)
- {
- printk("%s: cbp = %lxn", devN->name, (long) H2S(cbp));
- proc_reset(dev0, 1); /* Freeze IDT */
- break; /* For Safety */
- }
- ddp_h->pciaddr = (ulong) phys_p;
- ddp_h->lcladdr = S2DMA(tbdp->buf);
- ddp_h->len = amt;
- phys_p += amt;
- p += amt;
- if (count & I596_TBD_EOF)
- {
- ddp_h->next = PLX_DMA_DESC_TO_HOST
- | PLX_DMA_DESC_EOC;
- ++ddp_h;
- break;
- }
- else
- {
- ++ddp_s;
- ddp_h->next = PLX_DMA_DESC_TO_HOST
- | (ulong) ddp_s;
- tbdp = (I596_TBD *) S2H(tbdp->next);
- ++ddp_h;
- }
- }
- if (ddp_h - priv0->dmadesc_h)
- {
- int rc;
- rc = do_plx_dma(dev0,
- 0, (ulong) priv0->dmadesc_s, len, 0);
- if (rc)
- {
- printk("%s: Chained DMA failuren", devN->name);
- goto again;
- }
- }
- }
- else if (priv0->use_dma)
- {
- /*
- * If we can use DMA and its a shorter frame, copy it
- * using single DMA transfers.
- */
- uchar *phys_p;
- /*
- * Get the physical address of the STREAMS buffer.
- * NOTE: allocb() guarantees that the whole buffer
- * is in a single page if the length < 4096.
- */
- phys_p = (uchar *) virt_to_phys(putp);
- tbdp = (I596_TBD *) S2H(cbp->xmit.tbdp);
- for (;;)
- {
- int count;
- int amt;
- int rc;
- count = tbdp->count;
- amt = count & 0x3fff;
- if (amt == 0)
- break; /* For safety */
- if ( (p-putp) >= len)
- {
- printk("%s: cbp = %lxn", devN->name, (long) H2S(cbp));
- proc_reset(dev0, 1); /* Freeze IDT */
- break; /* For Safety */
- }
- rc = do_plx_dma(dev0, (ulong) phys_p,
- S2DMA(tbdp->buf), amt, 1);
- if (rc)
- {
- memcpy(p, S2H(tbdp->buf), amt);
- printk("%s: Single DMA failedn", devN->name);
- }
- phys_p += amt;
- p += amt;
- if (count & I596_TBD_EOF)
- break;
- tbdp = (I596_TBD *) S2H(tbdp->next);
- }
- }
- else
- {
- /*
- * Otherwise, copy it piece by piece using memcpy()
- */
- tbdp = (I596_TBD *) S2H(cbp->xmit.tbdp);
- for (;;)
- {
- int count;
- int amt;
- count = tbdp->count;
- amt = count & 0x3fff;
- if (amt == 0)
- break; /* For safety */
- if ( (p-putp) >= len)
- {
- printk("%s: cbp = %lxn", devN->name, (long) H2S(cbp));
- proc_reset(dev0, 1); /* Freeze IDT */
- break; /* For Safety */
- }
- memcpy(p, S2H(tbdp->buf), amt);
- p += amt;
- if (count & I596_TBD_EOF)
- break;
- tbdp = (I596_TBD *) S2H(tbdp->next);
- }
- }
- /*
- * Pass the frame to upper half
- */
- skb->protocol = eth_type_trans(skb, devN);
- netif_rx(skb);
- devN->last_rx = jiffies;
- ++privN->stats.rx_packets;
- privN->stats.rx_bytes += len;
- out:
- cbp->xmit.status = I596_CB_STATUS_C | I596_CB_STATUS_OK;
- }
- /*
- * Start transmission of a frame
- *
- * The interface to the board is simple: we pretend that we are
- * a fifth 82596 ethernet controller 'receiving' data, and copy the
- * data into the same structures that a real 82596 would. This way,
- * the board firmware handles the host 'port' the same as any other.
- *
- * NOTE: we do not use Bus master DMA for this routine. Turns out
- * that it is not needed. Slave writes over the PCI bus are about
- * as fast as DMA, due to the fact that the PLX part can do burst
- * writes. The same is not true for data being read from the board.
- *
- * For multi-NIC mode, we tell the firmware the desired 82596
- * output port by setting the special "dstchan" member at the
- * end of the traditional 82596 RFD structure.
- */
- static int dgrs_start_xmit(struct sk_buff *skb, struct net_device *devN)
- {
- DGRS_PRIV *privN = (DGRS_PRIV *) devN->priv;
- struct net_device *dev0;
- DGRS_PRIV *priv0;
- I596_RBD *rbdp;
- int count;
- int i, len, amt;
- /*
- * Determine 0th priv and dev structure pointers
- */
- if (dgrs_nicmode)
- {
- dev0 = privN->devtbl[0];
- priv0 = (DGRS_PRIV *) dev0->priv;
- }
- else
- {
- dev0 = devN;
- priv0 = privN;
- }
- if (dgrs_debug > 1)
- printk("%s: xmit len=%dn", devN->name, (int) skb->len);
- devN->trans_start = jiffies;
- netif_start_queue(devN);
- if (priv0->rfdp->cmd & I596_RFD_EL)
- { /* Out of RFD's */
- if (0) printk("%s: NO RFD'sn", devN->name);
- goto no_resources;
- }
- rbdp = priv0->rbdp;
- count = 0;
- priv0->rfdp->rbdp = (I596_RBD *) H2S(rbdp);
- i = 0; len = skb->len;
- for (;;)
- {
- if (rbdp->size & I596_RBD_EL)
- { /* Out of RBD's */
- if (0) printk("%s: NO RBD'sn", devN->name);
- goto no_resources;
- }
- amt = min_t(unsigned int, len, rbdp->size - count);
- memcpy( (char *) S2H(rbdp->buf) + count, skb->data + i, amt);
- i += amt;
- count += amt;
- len -= amt;
- if (len == 0)
- {
- if (skb->len < 60)
- rbdp->count = 60 | I596_RBD_EOF;
- else
- rbdp->count = count | I596_RBD_EOF;
- rbdp = (I596_RBD *) S2H(rbdp->next);
- goto frame_done;
- }
- else if (count < 32)
- {
- /* More data to come, but we used less than 32
- * bytes of this RBD. Keep filling this RBD.
- */
- {} /* Yes, we do nothing here */
- }
- else
- {
- rbdp->count = count;
- rbdp = (I596_RBD *) S2H(rbdp->next);
- count = 0;
- }
- }
- frame_done:
- priv0->rbdp = rbdp;
- if (dgrs_nicmode)
- priv0->rfdp->dstchan = privN->chan;
- priv0->rfdp->status = I596_RFD_C | I596_RFD_OK;
- priv0->rfdp = (I596_RFD *) S2H(priv0->rfdp->next);
- ++privN->stats.tx_packets;
- dev_kfree_skb (skb);
- return (0);
- no_resources:
- priv0->scbp->status |= I596_SCB_RNR; /* simulate I82596 */
- return (-EAGAIN);
- }
- /*
- * Open the interface
- */
- static int
- dgrs_open( struct net_device *dev )
- {
- netif_start_queue(dev);
- return (0);
- }
- /*
- * Close the interface
- */
- static int dgrs_close( struct net_device *dev )
- {
- netif_stop_queue(dev);
- return (0);
- }
- /*
- * Get statistics
- */
- static struct net_device_stats *dgrs_get_stats( struct net_device *dev )
- {
- DGRS_PRIV *priv = (DGRS_PRIV *) dev->priv;
- return (&priv->stats);
- }
- /*
- * Set multicast list and/or promiscuous mode
- */
- static void dgrs_set_multicast_list( struct net_device *dev)
- {
- DGRS_PRIV *priv = (DGRS_PRIV *) dev->priv;
- priv->port->is_promisc = (dev->flags & IFF_PROMISC) ? 1 : 0;
- }
- /*
- * Unique ioctl's
- */
- static int dgrs_ioctl(struct net_device *devN, struct ifreq *ifr, int cmd)
- {
- DGRS_PRIV *privN = (DGRS_PRIV *) devN->priv;
- DGRS_IOCTL ioc;
- int i;
- if (cmd != DGRSIOCTL)
- return -EINVAL;
- if(copy_from_user(&ioc, ifr->ifr_data, sizeof(DGRS_IOCTL)))
- return -EFAULT;
- switch (ioc.cmd)
- {
- case DGRS_GETMEM:
- if (ioc.len != sizeof(ulong))
- return -EINVAL;
- if(copy_to_user(ioc.data, &devN->mem_start, ioc.len))
- return -EFAULT;
- return (0);
- case DGRS_SETFILTER:
- if (!capable(CAP_NET_ADMIN))
- return -EPERM;
- if (ioc.port > privN->bcomm->bc_nports)
- return -EINVAL;
- if (ioc.filter >= NFILTERS)
- return -EINVAL;
- if (ioc.len > privN->bcomm->bc_filter_area_len)
- return -EINVAL;
- /* Wait for old command to finish */
- for (i = 0; i < 1000; ++i)
- {
- if ( (volatile long) privN->bcomm->bc_filter_cmd <= 0 )
- break;
- udelay(1);
- }
- if (i >= 1000)
- return -EIO;
- privN->bcomm->bc_filter_port = ioc.port;
- privN->bcomm->bc_filter_num = ioc.filter;
- privN->bcomm->bc_filter_len = ioc.len;
-
- if (ioc.len)
- {
- if(copy_from_user(S2HN(privN->bcomm->bc_filter_area),
- ioc.data, ioc.len))
- return -EFAULT;
- privN->bcomm->bc_filter_cmd = BC_FILTER_SET;
- }
- else
- privN->bcomm->bc_filter_cmd = BC_FILTER_CLR;
- return(0);
- default:
- return -EOPNOTSUPP;
- }
- }
- /*
- * Process interrupts
- *
- * dev, priv will always refer to the 0th device in Multi-NIC mode.
- */
- static void dgrs_intr(int irq, void *dev_id, struct pt_regs *regs)
- {
- struct net_device *dev0 = (struct net_device *) dev_id;
- DGRS_PRIV *priv0 = (DGRS_PRIV *) dev0->priv;
- I596_CB *cbp;
- int cmd;
- int i;
- ++priv0->intrcnt;
- if (1) ++priv0->bcomm->bc_cnt[4];
- if (0)
- {
- static int cnt = 100;
- if (--cnt > 0)
- printk("%s: interrupt: irq %dn", dev0->name, irq);
- }
- /*
- * Get 596 command
- */
- cmd = priv0->scbp->cmd;
- /*
- * See if RU has been restarted
- */
- if ( (cmd & I596_SCB_RUC) == I596_SCB_RUC_START)
- {
- if (0) printk("%s: RUC startn", dev0->name);
- priv0->rfdp = (I596_RFD *) S2H(priv0->scbp->rfdp);
- priv0->rbdp = (I596_RBD *) S2H(priv0->rfdp->rbdp);
- priv0->scbp->status &= ~(I596_SCB_RNR|I596_SCB_RUS);
- /*
- * Tell upper half (halves)
- */
- if (dgrs_nicmode)
- {
- for (i = 0; i < priv0->nports; ++i)
- netif_wake_queue (priv0->devtbl[i]);
- }
- else
- netif_wake_queue (dev0);
- /* if (bd->flags & TX_QUEUED)
- DL_sched(bd, bdd); */
- }
- /*
- * See if any CU commands to process
- */
- if ( (cmd & I596_SCB_CUC) != I596_SCB_CUC_START)
- {
- priv0->scbp->cmd = 0; /* Ignore all other commands */
- goto ack_intr;
- }
- priv0->scbp->status &= ~(I596_SCB_CNA|I596_SCB_CUS);
- /*
- * Process a command
- */
- cbp = (I596_CB *) S2H(priv0->scbp->cbp);
- priv0->scbp->cmd = 0; /* Safe to clear the command */
- for (;;)
- {
- switch (cbp->nop.cmd & I596_CB_CMD)
- {
- case I596_CB_CMD_XMIT:
- dgrs_rcv_frame(dev0, priv0, cbp);
- break;
- default:
- cbp->nop.status = I596_CB_STATUS_C | I596_CB_STATUS_OK;
- break;
- }
- if (cbp->nop.cmd & I596_CB_CMD_EL)
- break;
- cbp = (I596_CB *) S2H(cbp->nop.next);
- }
- priv0->scbp->status |= I596_SCB_CNA;
- /*
- * Ack the interrupt
- */
- ack_intr:
- if (priv0->plxreg)
- OUTL(dev0->base_addr + PLX_LCL2PCI_DOORBELL, 1);
- }
- /*
- * Download the board firmware
- */
- static int __init
- dgrs_download(struct net_device *dev0)
- {
- DGRS_PRIV *priv0 = (DGRS_PRIV *) dev0->priv;
- int is;
- int i;
- static int iv2is[16] = {
- 0, 0, 0, ES4H_IS_INT3,
- 0, ES4H_IS_INT5, 0, ES4H_IS_INT7,
- 0, 0, ES4H_IS_INT10, ES4H_IS_INT11,
- ES4H_IS_INT12, 0, 0, ES4H_IS_INT15 };
- /*
- * Map in the dual port memory
- */
- priv0->vmem = ioremap(dev0->mem_start, 2048*1024);
- if (!priv0->vmem)
- {
- printk("%s: cannot map in board memoryn", dev0->name);
- return -ENXIO;
- }
- /*
- * Hold the processor and configure the board addresses
- */
- if (priv0->plxreg)
- { /* PCI bus */
- proc_reset(dev0, 1);
- }
- else
- { /* EISA bus */
- is = iv2is[dev0->irq & 0x0f];
- if (!is)
- {
- printk("%s: Illegal IRQ %dn", dev0->name, dev0->irq);
- return -ENXIO;
- }
- OUTB(dev0->base_addr + ES4H_AS_31_24,
- (uchar) (dev0->mem_start >> 24) );
- OUTB(dev0->base_addr + ES4H_AS_23_16,
- (uchar) (dev0->mem_start >> 16) );
- priv0->is_reg = ES4H_IS_LINEAR | is |
- ((uchar) (dev0->mem_start >> 8) & ES4H_IS_AS15);
- OUTB(dev0->base_addr + ES4H_IS, priv0->is_reg);
- OUTB(dev0->base_addr + ES4H_EC, ES4H_EC_ENABLE);
- OUTB(dev0->base_addr + ES4H_PC, ES4H_PC_RESET);
- OUTB(dev0->base_addr + ES4H_MW, ES4H_MW_ENABLE | 0x00);
- }
- /*
- * See if we can do DMA on the SE-6
- */
- priv0->use_dma = check_board_dma(dev0);
- if (priv0->use_dma)
- printk("%s: Bus Master DMA is enabled.n", dev0->name);
- /*
- * Load and verify the code at the desired address
- */
- memcpy(priv0->vmem, dgrs_code, dgrs_ncode); /* Load code */
- if (memcmp(priv0->vmem, dgrs_code, dgrs_ncode))
- {
- iounmap(priv0->vmem);
- priv0->vmem = NULL;
- printk("%s: download compare failedn", dev0->name);
- return -ENXIO;
- }
- /*
- * Configurables
- */
- priv0->bcomm = (struct bios_comm *) (priv0->vmem + 0x0100);
- priv0->bcomm->bc_nowait = 1; /* Tell board to make printf not wait */
- priv0->bcomm->bc_squelch = 0; /* Flag from Space.c */
- priv0->bcomm->bc_150ohm = 0; /* Flag from Space.c */
- priv0->bcomm->bc_spew = 0; /* Debug flag from Space.c */
- priv0->bcomm->bc_maxrfd = 0; /* Debug flag from Space.c */
- priv0->bcomm->bc_maxrbd = 0; /* Debug flag from Space.c */
- /*
- * Tell board we are operating in switch mode (1) or in
- * multi-NIC mode (2).
- */
- priv0->bcomm->bc_host = dgrs_nicmode ? BC_MULTINIC : BC_SWITCH;
- /*
- * Request memory space on board for DMA chains
- */
- if (priv0->use_dma)
- priv0->bcomm->bc_hostarea_len = (2048/64) * 16;
- /*
- * NVRAM configurables from Space.c
- */
- priv0->bcomm->bc_spantree = dgrs_spantree;
- priv0->bcomm->bc_hashexpire = dgrs_hashexpire;
- memcpy(priv0->bcomm->bc_ipaddr, dgrs_ipaddr, 4);
- memcpy(priv0->bcomm->bc_iptrap, dgrs_iptrap, 4);
- memcpy(priv0->bcomm->bc_ipxnet, &dgrs_ipxnet, 4);
- /*
- * Release processor, wait 8 seconds for board to initialize
- */
- proc_reset(dev0, 0);
- for (i = jiffies + 8 * HZ; time_after(i, jiffies); )
- {
- barrier(); /* Gcc 2.95 needs this */
- if (priv0->bcomm->bc_status >= BC_RUN)
- break;
- }
- if (priv0->bcomm->bc_status < BC_RUN)
- {
- printk("%s: board not operatingn", dev0->name);
- return -ENXIO;
- }
- priv0->port = (PORT *) S2H(priv0->bcomm->bc_port);
- priv0->scbp = (I596_SCB *) S2H(priv0->port->scbp);
- priv0->rfdp = (I596_RFD *) S2H(priv0->scbp->rfdp);
- priv0->rbdp = (I596_RBD *) S2H(priv0->rfdp->rbdp);
- priv0->scbp->status = I596_SCB_CNA; /* CU is idle */
- /*
- * Get switch physical and host virtual pointers to DMA
- * chaining area. NOTE: the MSB of the switch physical
- * address *must* be turned off. Otherwise, the HW kludge
- * that allows host access of the PLX DMA registers will
- * erroneously select the PLX registers.
- */
- priv0->dmadesc_s = (DMACHAIN *) S2DMA(priv0->bcomm->bc_hostarea);
- if (priv0->dmadesc_s)
- priv0->dmadesc_h = (DMACHAIN *) S2H(priv0->dmadesc_s);
- else
- priv0->dmadesc_h = NULL;
- /*
- * Enable board interrupts
- */
- if (priv0->plxreg)
- { /* PCI bus */
- OUTL(dev0->base_addr + PLX_INT_CSR,
- inl(dev0->base_addr + PLX_INT_CSR)
- | PLX_PCI_DOORBELL_IE); /* Enable intr to host */
- OUTL(dev0->base_addr + PLX_LCL2PCI_DOORBELL, 1);
- }
- else
- { /* EISA bus */
- }
- return (0);
- }
- /*
- * Probe (init) a board
- */
- int __init
- dgrs_probe1(struct net_device *dev)
- {
- DGRS_PRIV *priv = (DGRS_PRIV *) dev->priv;
- int i;
- int rc;
- printk("%s: Digi RightSwitch io=%lx mem=%lx irq=%d plx=%lx dma=%lxn",
- dev->name, dev->base_addr, dev->mem_start, dev->irq,
- priv->plxreg, priv->plxdma);
- /*
- * Download the firmware and light the processor
- */
- rc = dgrs_download(dev);
- if (rc)
- goto err_out;
- /*
- * Get ether address of board
- */
- printk("%s: Ethernet address", dev->name);
- memcpy(dev->dev_addr, priv->port->ethaddr, 6);
- for (i = 0; i < 6; ++i)
- printk("%c%2.2x", i ? ':' : ' ', dev->dev_addr[i]);
- printk("n");
- if (dev->dev_addr[0] & 1)
- {
- printk("%s: Illegal Ethernet Addressn", dev->name);
- rc = -ENXIO;
- goto err_out;
- }
- /*
- * ACK outstanding interrupts, hook the interrupt,
- * and verify that we are getting interrupts from the board.
- */
- if (priv->plxreg)
- OUTL(dev->base_addr + PLX_LCL2PCI_DOORBELL, 1);
-
- rc = request_irq(dev->irq, &dgrs_intr, SA_SHIRQ, "RightSwitch", dev);
- if (rc)
- goto err_out;
- priv->intrcnt = 0;
- for (i = jiffies + 2*HZ + HZ/2; time_after(i, jiffies); )
- {
- barrier(); /* gcc 2.95 needs this */
- if (priv->intrcnt >= 2)
- break;
- }
- if (priv->intrcnt < 2)
- {
- printk(KERN_ERR "%s: Not interrupting on IRQ %d (%d)n",
- dev->name, dev->irq, priv->intrcnt);
- rc = -ENXIO;
- goto err_free_irq;
- }
- /*
- * Register the /proc/ioports information...
- */
- if (!request_region(dev->base_addr, 256, "RightSwitch")) {
- printk(KERN_ERR "%s: io 0x%3lX, which is busy.n", dev->name,
- dev->base_addr);
- rc = -EBUSY;
- goto err_free_irq;
- }
-
- /*
- * Entry points...
- */
- dev->open = &dgrs_open;
- dev->stop = &dgrs_close;
- dev->get_stats = &dgrs_get_stats;
- dev->hard_start_xmit = &dgrs_start_xmit;
- dev->set_multicast_list = &dgrs_set_multicast_list;
- dev->do_ioctl = &dgrs_ioctl;
- return rc;
- err_free_irq:
- free_irq(dev->irq, dev);
- err_out:
- return rc;
- }
- int __init
- dgrs_initclone(struct net_device *dev)
- {
- DGRS_PRIV *priv = (DGRS_PRIV *) dev->priv;
- int i;
- printk("%s: Digi RightSwitch port %d ",
- dev->name, priv->chan);
- for (i = 0; i < 6; ++i)
- printk("%c%2.2x", i ? ':' : ' ', dev->dev_addr[i]);
- printk("n");
- return (0);
- }
- static int __init
- dgrs_found_device(
- int io,
- ulong mem,
- int irq,
- ulong plxreg,
- ulong plxdma
- )
- {
- DGRS_PRIV *priv;
- struct net_device *dev, *aux;
- /* Allocate and fill new device structure. */
- int dev_size = sizeof(struct net_device) + sizeof(DGRS_PRIV);
- int i, ret;
- dev = (struct net_device *) kmalloc(dev_size, GFP_KERNEL);
- if (!dev)
- return -ENOMEM;
- memset(dev, 0, dev_size);
- dev->priv = ((void *)dev) + sizeof(struct net_device);
- priv = (DGRS_PRIV *)dev->priv;
- dev->base_addr = io;
- dev->mem_start = mem;
- dev->mem_end = mem + 2048 * 1024 - 1;
- dev->irq = irq;
- priv->plxreg = plxreg;
- priv->plxdma = plxdma;
- priv->vplxdma = NULL;
- priv->chan = 1;
- priv->devtbl[0] = dev;
- dev->init = dgrs_probe1;
- SET_MODULE_OWNER(dev);
- ether_setup(dev);
- if (register_netdev(dev) != 0)
- return -EIO;
- priv->next_dev = dgrs_root_dev;
- dgrs_root_dev = dev;
- if ( !dgrs_nicmode )
- return (0); /* Switch mode, we are done */
- /*
- * Operating card as N separate NICs
- */
- priv->nports = priv->bcomm->bc_nports;
- for (i = 1; i < priv->nports; ++i)
- {
- struct net_device *devN;
- DGRS_PRIV *privN;
- /* Allocate new dev and priv structures */
- devN = (struct net_device *) kmalloc(dev_size, GFP_KERNEL);
- /* Make it an exact copy of dev[0]... */
- ret = -ENOMEM;
- if (!devN)
- goto fail;
- memcpy(devN, dev, dev_size);
- memset(devN->name, 0, sizeof(devN->name));
- devN->priv = ((void *)devN) + sizeof(struct net_device);
- privN = (DGRS_PRIV *)devN->priv;
- /* ... and zero out VM areas */
- privN->vmem = 0;
- privN->vplxdma = 0;
- /* ... and zero out IRQ */
- devN->irq = 0;
- /* ... and base MAC address off address of 1st port */
- devN->dev_addr[5] += i;
- devN->init = dgrs_initclone;
- SET_MODULE_OWNER(devN);
- ether_setup(devN);
- ret = -EIO;
- if (register_netdev(devN)) {
- kfree(devN);
- goto fail;
- }
- privN->chan = i+1;
- priv->devtbl[i] = devN;
- privN->next_dev = dgrs_root_dev;
- dgrs_root_dev = devN;
- }
- return 0;
- fail: aux = priv->next_dev;
- while (dgrs_root_dev != aux) {
- struct net_device *d = dgrs_root_dev;
-
- dgrs_root_dev = ((DGRS_PRIV *)d->priv)->next_dev;
- unregister_netdev(d);
- kfree(d);
- }
- return ret;
- }
- /*
- * Scan for all boards
- */
- static int is2iv[8] __initdata = { 0, 3, 5, 7, 10, 11, 12, 15 };
- static int __init dgrs_scan(void)
- {
- int cards_found = 0;
- uint io;
- uint mem;
- uint irq;
- uint plxreg;
- uint plxdma;
- /*
- * First, check for PCI boards
- */
- if (pci_present())
- {
- struct pci_dev *pdev = NULL;
- while ((pdev = pci_find_device(SE6_PCI_VENDOR_ID, SE6_PCI_DEVICE_ID, pdev)) != NULL)
- {
- /*
- * Get and check the bus-master and latency values.
- * Some PCI BIOSes fail to set the master-enable bit,
- * and the latency timer must be set to the maximum
- * value to avoid data corruption that occurs when the
- * timer expires during a transfer. Yes, it's a bug.
- */
- if (pci_enable_device(pdev))
- continue;
- pci_set_master(pdev);
- plxreg = pci_resource_start (pdev, 0);
- io = pci_resource_start (pdev, 1);
- mem = pci_resource_start (pdev, 2);
- pci_read_config_dword(pdev, 0x30, &plxdma);
- irq = pdev->irq;
- plxdma &= ~15;
- /*
- * On some BIOSES, the PLX "expansion rom" (used for DMA)
- * address comes up as "0". This is probably because
- * the BIOS doesn't see a valid 55 AA ROM signature at
- * the "ROM" start and zeroes the address. To get
- * around this problem the SE-6 is configured to ask
- * for 4 MB of space for the dual port memory. We then
- * must set its range back to 2 MB, and use the upper
- * half for DMA register access
- */
- OUTL(io + PLX_SPACE0_RANGE, 0xFFE00000L);
- if (plxdma == 0)
- plxdma = mem + (2048L * 1024L);
- pci_write_config_dword(pdev, 0x30, plxdma + 1);
- pci_read_config_dword(pdev, 0x30, &plxdma);
- plxdma &= ~15;
- dgrs_found_device(io, mem, irq, plxreg, plxdma);
- cards_found++;
- }
- }
- /*
- * Second, check for EISA boards
- */
- if (EISA_bus)
- {
- for (io = 0x1000; io < 0x9000; io += 0x1000)
- {
- if (inb(io+ES4H_MANUFmsb) != 0x10
- || inb(io+ES4H_MANUFlsb) != 0x49
- || inb(io+ES4H_PRODUCT) != ES4H_PRODUCT_CODE)
- continue;
- if ( ! (inb(io+ES4H_EC) & ES4H_EC_ENABLE) )
- continue; /* Not EISA configured */
- mem = (inb(io+ES4H_AS_31_24) << 24)
- + (inb(io+ES4H_AS_23_16) << 16);
- irq = is2iv[ inb(io+ES4H_IS) & ES4H_IS_INTMASK ];
- dgrs_found_device(io, mem, irq, 0L, 0L);
- ++cards_found;
- }
- }
- return cards_found;
- }
- /*
- * Variables that can be overriden from module command line
- */
- static int debug = -1;
- static int dma = -1;
- static int hashexpire = -1;
- static int spantree = -1;
- static int ipaddr[4] = { -1 };
- static int iptrap[4] = { -1 };
- static __u32 ipxnet = -1;
- static int nicmode = -1;
- MODULE_PARM(debug, "i");
- MODULE_PARM(dma, "i");
- MODULE_PARM(hashexpire, "i");
- MODULE_PARM(spantree, "i");
- MODULE_PARM(ipaddr, "1-4i");
- MODULE_PARM(iptrap, "1-4i");
- MODULE_PARM(ipxnet, "i");
- MODULE_PARM(nicmode, "i");
- MODULE_PARM_DESC(debug, "Digi RightSwitch enable debugging (0-1)");
- MODULE_PARM_DESC(dma, "Digi RightSwitch enable BM DMA (0-1)");
- MODULE_PARM_DESC(nicmode, "Digi RightSwitch operating mode (1: switch, 2: multi-NIC)");
- static int __init dgrs_init_module (void)
- {
- int cards_found;
- int i;
- /*
- * Command line variable overrides
- * debug=NNN
- * dma=0/1
- * spantree=0/1
- * hashexpire=NNN
- * ipaddr=A,B,C,D
- * iptrap=A,B,C,D
- * ipxnet=NNN
- * nicmode=NNN
- */
- if (debug >= 0)
- dgrs_debug = debug;
- if (dma >= 0)
- dgrs_dma = dma;
- if (nicmode >= 0)
- dgrs_nicmode = nicmode;
- if (hashexpire >= 0)
- dgrs_hashexpire = hashexpire;
- if (spantree >= 0)
- dgrs_spantree = spantree;
- if (ipaddr[0] != -1)
- for (i = 0; i < 4; ++i)
- dgrs_ipaddr[i] = ipaddr[i];
- if (iptrap[0] != -1)
- for (i = 0; i < 4; ++i)
- dgrs_iptrap[i] = iptrap[i];
- if (ipxnet != -1)
- dgrs_ipxnet = htonl( ipxnet );
- if (dgrs_debug)
- {
- printk(KERN_INFO "dgrs: SW=%s FW=Build %d %snFW Version=%sn",
- version, dgrs_firmnum, dgrs_firmdate, dgrs_firmver);
- }
- /*
- * Find and configure all the cards
- */
- dgrs_root_dev = NULL;
- cards_found = dgrs_scan();
- return cards_found ? 0 : -ENODEV;
- }
- static void __exit dgrs_cleanup_module (void)
- {
- while (dgrs_root_dev)
- {
- struct net_device *next_dev;
- DGRS_PRIV *priv;
- priv = (DGRS_PRIV *) dgrs_root_dev->priv;
- next_dev = priv->next_dev;
- unregister_netdev(dgrs_root_dev);
- proc_reset(priv->devtbl[0], 1);
- if (priv->vmem)
- iounmap(priv->vmem);
- if (priv->vplxdma)
- iounmap((uchar *) priv->vplxdma);
- release_region(dgrs_root_dev->base_addr, 256);
- if (dgrs_root_dev->irq)
- free_irq(dgrs_root_dev->irq, dgrs_root_dev);
- kfree(dgrs_root_dev);
- dgrs_root_dev = next_dev;
- }
- }
- module_init(dgrs_init_module);
- module_exit(dgrs_cleanup_module);