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

📄 eexpress.c

📁 powerpc内核mpc8241linux系统下net驱动程序
💻 C
📖 第 1 页 / 共 3 页
字号:
/* Intel EtherExpress 16 device driver for Linux * * Written by John Sullivan, 1995 *  based on original code by Donald Becker, with changes by *  Alan Cox and Pauline Middelink. * * Support for 8-bit mode by Zoltan Szilagyi <zoltans@cs.arizona.edu> * * Many modifications, and currently maintained, by *  Philip Blundell <Philip.Blundell@pobox.com> * Added the Compaq LTE  Alan Cox <alan@redhat.com> * * Note - this driver is experimental still - it has problems on faster * machines. Someone needs to sit down and go through it line by line with * a databook... *//* The EtherExpress 16 is a fairly simple card, based on a shared-memory * design using the i82586 Ethernet coprocessor.  It bears no relationship, * as far as I know, to the similarly-named "EtherExpress Pro" range. * * Historically, Linux support for these cards has been very bad.  However, * things seem to be getting better slowly. *//* If your card is confused about what sort of interface it has (eg it * persistently reports "10baseT" when none is fitted), running 'SOFTSET /BART' * or 'SOFTSET /LISA' from DOS seems to help. *//* Here's the scoop on memory mapping. * * There are three ways to access EtherExpress card memory: either using the * shared-memory mapping, or using PIO through the dataport, or using PIO * through the "shadow memory" ports. * * The shadow memory system works by having the card map some of its memory * as follows: * * (the low five bits of the SMPTR are ignored) * *  base+0x4000..400f      memory at SMPTR+0..15 *  base+0x8000..800f      memory at SMPTR+16..31 *  base+0xc000..c007      dubious stuff (memory at SMPTR+16..23 apparently) *  base+0xc008..c00f      memory at 0x0008..0x000f * * This last set (the one at c008) is particularly handy because the SCB * lives at 0x0008.  So that set of ports gives us easy random access to data * in the SCB without having to mess around setting up pointers and the like. * We always use this method to access the SCB (via the scb_xx() functions). * * Dataport access works by aiming the appropriate (read or write) pointer * at the first address you're interested in, and then reading or writing from * the dataport.  The pointers auto-increment after each transfer.  We use * this for data transfer. * * We don't use the shared-memory system because it allegedly doesn't work on * all cards, and because it's a bit more prone to go wrong (it's one more * thing to configure...). *//* Known bugs: * * - The card seems to want to give us two interrupts every time something *   happens, where just one would be better. *//* * * Note by Zoltan Szilagyi 10-12-96: * * I've succeeded in eliminating the "CU wedged" messages, and hence the * lockups, which were only occurring with cards running in 8-bit mode ("force * 8-bit operation" in Intel's SoftSet utility). This version of the driver * sets the 82586 and the ASIC to 8-bit mode at startup; it also stops the * CU before submitting a packet for transmission, and then restarts it as soon * as the process of handing the packet is complete. This is definitely an * unnecessary slowdown if the card is running in 16-bit mode; therefore one * should detect 16-bit vs 8-bit mode from the EEPROM settings and act  * accordingly. In 8-bit mode with this bugfix I'm getting about 150 K/s for * ftp's, which is significantly better than I get in DOS, so the overhead of * stopping and restarting the CU with each transmit is not prohibitive in * practice. */  #include <linux/module.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/types.h>#include <linux/fcntl.h>#include <linux/interrupt.h>#include <linux/ptrace.h>#include <linux/ioport.h>#include <linux/string.h>#include <linux/in.h>#include <asm/system.h>#include <asm/bitops.h>#include <asm/io.h>#include <asm/irq.h>#include <linux/delay.h>#include <linux/errno.h>#include <linux/init.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/skbuff.h>#include <linux/malloc.h>#ifndef NET_DEBUG#define NET_DEBUG 4#endif#include "eexpress.h"#define EEXP_IO_EXTENT  16/* * Private data declarations */struct net_local{	struct net_device_stats stats;	unsigned long last_tx;       /* jiffies when last transmit started */	unsigned long init_time;     /* jiffies when eexp_hw_init586 called */	unsigned short rx_first;     /* first rx buf, same as RX_BUF_START */	unsigned short rx_last;      /* last rx buf */	unsigned short rx_ptr;       /* first rx buf to look at */	unsigned short tx_head;      /* next free tx buf */	unsigned short tx_reap;      /* first in-use tx buf */	unsigned short tx_tail;      /* previous tx buf to tx_head */	unsigned short tx_link;      /* last known-executing tx buf */	unsigned short last_tx_restart;   /* set to tx_link when we					     restart the CU */	unsigned char started;	unsigned short rx_buf_start;	unsigned short rx_buf_end;	unsigned short num_tx_bufs;	unsigned short num_rx_bufs;	unsigned char width;         /* 0 for 16bit, 1 for 8bit */	unsigned char was_promisc;	unsigned char old_mc_count;};/* This is the code and data that is downloaded to the EtherExpress card's * memory at boot time. */static unsigned short start_code[] = {/* 0x0000 */	0x0001,                 /* ISCP: busy - cleared after reset */	0x0008,0x0000,0x0000,   /* offset,address (lo,hi) of SCB */	0x0000,0x0000,          /* SCB: status, commands */	0x0000,0x0000,          /* links to first command block,				   first receive descriptor */	0x0000,0x0000,          /* CRC error, alignment error counts */	0x0000,0x0000,          /* out of resources, overrun error counts */	0x0000,0x0000,          /* pad */	0x0000,0x0000,/* 0x20 -- start of 82586 CU program */#define CONF_LINK 0x20	0x0000,Cmd_Config,      	0x0032,                 /* link to next command */	0x080c,                 /* 12 bytes follow : fifo threshold=8 */	0x2e40,                 /* don't rx bad frames				 * SRDY/ARDY => ext. sync. : preamble len=8	                         * take addresses from data buffers				 * 6 bytes/address				 */	0x6000,                 /* default backoff method & priority				 * interframe spacing = 0x60 */	0xf200,                 /* slot time=0x200 				 * max collision retry = 0xf */#define CONF_PROMISC  0x2e	0x0000,                 /* no HDLC : normal CRC : enable broadcast 				 * disable promiscuous/multicast modes */	0x003c,                 /* minimum frame length = 60 octets) */	0x0000,Cmd_SetAddr,	0x003e,                 /* link to next command */#define CONF_HWADDR  0x38	0x0000,0x0000,0x0000,   /* hardware address placed here */	0x0000,Cmd_MCast,	0x0076,                 /* link to next command */#define CONF_NR_MULTICAST 0x44	0x0000,                 /* number of multicast addresses */#define CONF_MULTICAST 0x46	0x0000, 0x0000, 0x0000, /* some addresses */	0x0000, 0x0000, 0x0000,	0x0000, 0x0000, 0x0000,	0x0000, 0x0000, 0x0000,	0x0000, 0x0000, 0x0000,	0x0000, 0x0000, 0x0000,	0x0000, 0x0000, 0x0000,	0x0000, 0x0000, 0x0000,#define CONF_DIAG_RESULT  0x76	0x0000, Cmd_Diag,	0x007c,                 /* link to next command */	0x0000,Cmd_TDR|Cmd_INT,	0x0084,#define CONF_TDR_RESULT  0x82	0x0000,	0x0000,Cmd_END|Cmd_Nop, /* end of configure sequence */	0x0084                  /* dummy link */};/* maps irq number to EtherExpress magic value */static char irqrmap[] = { 0,0,1,2,3,4,0,0,0,1,5,6,0,0,0,0 };/* * Prototypes for Linux interface */extern int express_probe(struct device *dev);static int eexp_open(struct device *dev);static int eexp_close(struct device *dev);static struct net_device_stats *eexp_stats(struct device *dev);static int eexp_xmit(struct sk_buff *buf, struct device *dev);static void eexp_irq(int irq, void *dev_addr, struct pt_regs *regs);static void eexp_set_multicast(struct device *dev);/* * Prototypes for hardware access functions */static void eexp_hw_rx_pio(struct device *dev);static void eexp_hw_tx_pio(struct device *dev, unsigned short *buf,		       unsigned short len);static int eexp_hw_probe(struct device *dev,unsigned short ioaddr);static unsigned short eexp_hw_readeeprom(unsigned short ioaddr,					 unsigned char location);static unsigned short eexp_hw_lasttxstat(struct device *dev);static void eexp_hw_txrestart(struct device *dev);static void eexp_hw_txinit    (struct device *dev);static void eexp_hw_rxinit    (struct device *dev);static void eexp_hw_init586   (struct device *dev);static void eexp_setup_filter (struct device *dev);static char *eexp_ifmap[]={"AUI", "BNC", "RJ45"};enum eexp_iftype {AUI=0, BNC=1, TPE=2};#define STARTED_RU      2#define STARTED_CU      1/* * Primitive hardware access functions. */static inline unsigned short scb_status(struct device *dev){	return inw(dev->base_addr + 0xc008);}static inline unsigned short scb_rdcmd(struct device *dev){	return inw(dev->base_addr + 0xc00a);}static inline void scb_command(struct device *dev, unsigned short cmd){	outw(cmd, dev->base_addr + 0xc00a);}static inline void scb_wrcbl(struct device *dev, unsigned short val){	outw(val, dev->base_addr + 0xc00c);}static inline void scb_wrrfa(struct device *dev, unsigned short val){	outw(val, dev->base_addr + 0xc00e);}static inline void set_loopback(struct device *dev){	outb(inb(dev->base_addr + Config) | 2, dev->base_addr + Config);}static inline void clear_loopback(struct device *dev){	outb(inb(dev->base_addr + Config) & ~2, dev->base_addr + Config);}static inline short int SHADOW(short int addr){	addr &= 0x1f;	if (addr > 0xf) addr += 0x3ff0;	return addr + 0x4000;}/* * Linux interface *//* * checks for presence of EtherExpress card */__initfunc(int express_probe(struct device *dev)){	unsigned short *port;	static unsigned short ports[] = { 0x300,0x310,0x270,0x320,0x340,0 };	unsigned short ioaddr = dev->base_addr;	if (ioaddr&0xfe00)		return eexp_hw_probe(dev,ioaddr);	else if (ioaddr)		return ENXIO;	for (port=&ports[0] ; *port ; port++ )	{		unsigned short sum = 0;		int i;		for ( i=0 ; i<4 ; i++ )		{			unsigned short t;			t = inb(*port + ID_PORT);			sum |= (t>>4) << ((t & 0x03)<<2);		}		if (sum==0xbaba && !eexp_hw_probe(dev,*port))			return 0;	}	return -ENODEV;}/* * open and initialize the adapter, ready for use */static int eexp_open(struct device *dev){	int irq = dev->irq;	unsigned short ioaddr = dev->base_addr;	struct net_local *lp = (struct net_local *)dev->priv;#if NET_DEBUG > 6	printk(KERN_DEBUG "%s: eexp_open()\n", dev->name);#endif	if (!irq || !irqrmap[irq])		return -ENXIO;	if (request_irq(irq,&eexp_irq,0,"EtherExpress",dev))		return -EAGAIN;	request_region(ioaddr, EEXP_IO_EXTENT, "EtherExpress");	request_region(ioaddr+0x4000, 16, "EtherExpress shadow");	request_region(ioaddr+0x8000, 16, "EtherExpress shadow");	request_region(ioaddr+0xc000, 16, "EtherExpress shadow");	dev->tbusy = 0;	dev->interrupt = 0;		if (lp->width) {		printk("%s: forcing ASIC to 8-bit mode\n", dev->name);		outb(inb(dev->base_addr+Config)&~4, dev->base_addr+Config);	}	eexp_hw_init586(dev);	dev->start = 1;	MOD_INC_USE_COUNT;#if NET_DEBUG > 6	printk(KERN_DEBUG "%s: leaving eexp_open()\n", dev->name);#endif	return 0;}/* * close and disable the interface, leaving the 586 in reset. */static int eexp_close(struct device *dev){	unsigned short ioaddr = dev->base_addr;	struct net_local *lp = dev->priv;	int irq = dev->irq;	dev->tbusy = 1;	dev->start = 0;	outb(SIRQ_dis|irqrmap[irq],ioaddr+SET_IRQ);	lp->started = 0;	scb_command(dev, SCB_CUsuspend|SCB_RUsuspend);	outb(0,ioaddr+SIGNAL_CA);	free_irq(irq,dev);	outb(i586_RST,ioaddr+EEPROM_Ctrl);	release_region(ioaddr,16);	MOD_DEC_USE_COUNT;	return 0;}/* * Return interface stats */static struct net_device_stats *eexp_stats(struct device *dev){	struct net_local *lp = (struct net_local *)dev->priv;	return &lp->stats;}/* * This gets called when a higher level thinks we are broken.  Check that * nothing has become jammed in the CU. */static void unstick_cu(struct device *dev){	struct net_local *lp = (struct net_local *)dev->priv;	unsigned short ioaddr = dev->base_addr;	if (lp->started)	{		if ((jiffies - dev->trans_start)>50)		{			if (lp->tx_link==lp->last_tx_restart)			{				unsigned short boguscount=200,rsst;				printk(KERN_WARNING "%s: Retransmit timed out, status %04x, resetting...\n",				       dev->name, scb_status(dev));				eexp_hw_txinit(dev);				lp->last_tx_restart = 0;				scb_wrcbl(dev, lp->tx_link);				scb_command(dev, SCB_CUstart);				outb(0,ioaddr+SIGNAL_CA);				while (!SCB_complete(rsst=scb_status(dev)))				{					if (!--boguscount)					{						boguscount=200;						printk(KERN_WARNING "%s: Reset timed out status %04x, retrying...\n",						       dev->name,rsst);						scb_wrcbl(dev, lp->tx_link);						scb_command(dev, SCB_CUstart);						outb(0,ioaddr+SIGNAL_CA);					}				}				dev->tbusy = 0;				mark_bh(NET_BH);			}			else			{				unsigned short status = scb_status(dev);				if (SCB_CUdead(status))				{					unsigned short txstatus = eexp_hw_lasttxstat(dev);					printk(KERN_WARNING "%s: Transmit timed out, CU not active status %04x %04x, restarting...\n",					       dev->name, status, txstatus);					eexp_hw_txrestart(dev);				}				else				{					unsigned short txstatus = eexp_hw_lasttxstat(dev);					if (dev->tbusy && !txstatus)					{						printk(KERN_WARNING "%s: CU wedged, status %04x %04x, resetting...\n",						       dev->name,status,txstatus);						eexp_hw_init586(dev);						dev->tbusy = 0;						mark_bh(NET_BH);					}					else					{						printk(KERN_WARNING "%s: transmit timed out\n", dev->name);					}				}			}		}	}	else	{		if ((jiffies-lp->init_time)>10)		{			unsigned short status = scb_status(dev);			printk(KERN_WARNING "%s: i82586 startup timed out, status %04x, resetting...\n",			       dev->name, status);			eexp_hw_init586(dev);			dev->tbusy = 0;			mark_bh(NET_BH);		}	}}/* * Called to transmit a packet, or to allow us to right ourselves * if the kernel thinks we've died. */static int eexp_xmit(struct sk_buff *buf, struct device *dev){	struct net_local *lp = (struct net_local *)dev->priv;#if NET_DEBUG > 6	printk(KERN_DEBUG "%s: eexp_xmit()\n", dev->name);#endif	disable_irq(dev->irq);	/* If dev->tbusy is set, all our tx buffers are full but the kernel	 * is calling us anyway.  Check that nothing bad is happening.	 */	if (dev->tbusy) {		int status = scb_status(dev);  		unstick_cu(dev);		if ((jiffies - lp->last_tx) < HZ)			return 1;		printk(KERN_INFO "%s: transmit timed out, %s?", dev->name,		       (SCB_complete(status)?"lost interrupt":			"board on fire"));		lp->stats.tx_errors++;		dev->tbusy = 0;		lp->last_tx = jiffies;		if (!SCB_complete(status)) {			scb_command(dev, SCB_CUabort);			outb(0,dev->base_addr+SIGNAL_CA);

⌨️ 快捷键说明

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