⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 dgrs.c

📁 powerpc内核mpc8241linux系统下net驱动程序
💻 C
📖 第 1 页 / 共 3 页
字号:
/* *	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@dgii.com, rick_richardson@dgii.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. * */static char *version = "$Id: dgrs.c,v 1.12 1996/12/21 13:43:58 rick Exp $";#include <linux/version.h>#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/malloc.h>#include <linux/interrupt.h>#include <linux/pci.h>#include <linux/init.h>#include <asm/bitops.h>#include <asm/io.h>#include <asm/byteorder.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/skbuff.h>#include <linux/types.h>/* *	API changed at linux version 2.1.0 */#if LINUX_VERSION_CODE >= 0x20100	#include <asm/uaccess.h>	#define IOREMAP(ADDR, LEN)		ioremap(ADDR, LEN)	#define IOUNMAP(ADDR)			iounmap(ADDR)	#define COPY_FROM_USER(DST,SRC,LEN)	copy_from_user(DST,SRC,LEN)	#define COPY_TO_USER(DST,SRC,LEN)	copy_to_user(DST,SRC,LEN)#else	#include <linux/bios32.h>	#define IOREMAP(ADDR, LEN)		vremap(ADDR, LEN)	#define IOUNMAP(ADDR)			vfree(ADDR)	#define COPY_FROM_USER(DST,SRC,LEN)	memcpy_fromfs(DST,SRC,LEN)	#define COPY_TO_USER(DST,SRC,LEN)	memcpy_tofs(DST,SRC,LEN)#endif/* *	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"/* *	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(). */int	dgrs_debug = 1;int	dgrs_dma = 1;int	dgrs_spantree = -1;int	dgrs_hashexpire = -1;uchar	dgrs_ipaddr[4] = { 0xff, 0xff, 0xff, 0xff};uchar	dgrs_iptrap[4] = { 0xff, 0xff, 0xff, 0xff};__u32	dgrs_ipxnet = -1;int	dgrs_nicmode = 0;/* *	Chain of device structures */#ifdef MODULE	static struct device *dgrs_root_dev = NULL;#endif/* *	Private per-board data structure (dev->priv) */typedef struct{	/*	 *	Stuff for generic ethercard I/F	 */	char			devname[8];	/* "ethN" string */	struct 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 */        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 device	*devtbl[6];	/* Ptrs to N device structs */} DGRS_PRIV;/* *	reset or un-reset the IDT processor */static voidproc_reset(struct 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 intcheck_board_dma(struct 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 regs\n", 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 intdo_plx_dma(	struct device *dev,	ulong pciaddr,	ulong lcladdr,	int len,	int to_host){        int     	i;        ulong   	csr;	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. */voiddgrs_rcv_frame(	struct device	*dev0,	DGRS_PRIV	*priv0,	I596_CB		*cbp){	int		len;	I596_TBD	*tbdp;	struct sk_buff	*skb;	uchar		*putp;	uchar		*p;	struct 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=%ld\n", 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 buffer\n", 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)

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -