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

📄 xircom_tulip_cb.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 4 页
字号:
/* xircom_tulip_cb.c: A Xircom CBE-100 ethernet driver for Linux. *//*	Written/copyright 1994-1999 by Donald Becker.	This software may be used and distributed according to the terms	of the GNU General Public License, incorporated herein by reference.	The author may be reached as becker@scyld.com, or C/O	Scyld Computing Corporation	410 Severn Ave., Suite 210	Annapolis MD 21403	-----------------------------------------------------------	Linux kernel-specific changes:	LK1.0 (Ion Badulescu)	- Major cleanup	- Use 2.4 PCI API	- Support ethtool	- Rewrite perfect filter/hash code	- Use interrupts for media changes	LK1.1 (Ion Badulescu)	- Disallow negotiation of unsupported full-duplex modes*/#define DRV_NAME	"xircom_tulip_cb"#define DRV_VERSION	"0.91+LK1.1"#define DRV_RELDATE	"October 11, 2001"#define CARDBUS 1/* A few user-configurable values. */#define xircom_debug debug#ifdef XIRCOM_DEBUGstatic int xircom_debug = XIRCOM_DEBUG;#elsestatic int xircom_debug = 1;#endif/* Maximum events (Rx packets, etc.) to handle at each interrupt. */static int max_interrupt_work = 25;#define MAX_UNITS 4/* Used to pass the full-duplex flag, etc. */static int full_duplex[MAX_UNITS];static int options[MAX_UNITS];static int mtu[MAX_UNITS];			/* Jumbo MTU for interfaces. *//* Keep the ring sizes a power of two for efficiency.   Making the Tx ring too large decreases the effectiveness of channel   bonding and packet priority.   There are no ill effects from too-large receive rings. */#define TX_RING_SIZE	16#define RX_RING_SIZE	32/* Set the copy breakpoint for the copy-only-tiny-buffer Rx structure. */#ifdef __alpha__static int rx_copybreak = 1518;#elsestatic int rx_copybreak = 100;#endif/*  Set the bus performance register.	Typical: Set 16 longword cache alignment, no burst limit.	Cache alignment bits 15:14	     Burst length 13:8		0000	No alignment  0x00000000 unlimited		0800 8 longwords		4000	8  longwords		0100 1 longword		1000 16 longwords		8000	16 longwords		0200 2 longwords	2000 32 longwords		C000	32  longwords		0400 4 longwords	Warning: many older 486 systems are broken and require setting 0x00A04800	   8 longword cache alignment, 8 longword burst.	ToDo: Non-Intel setting could be better.*/#if defined(__alpha__) || defined(__ia64__) || defined(__x86_64__)static int csr0 = 0x01A00000 | 0xE000;#elif defined(__powerpc__)static int csr0 = 0x01B00000 | 0x8000;#elif defined(__sparc__)static int csr0 = 0x01B00080 | 0x8000;#elif defined(__i386__)static int csr0 = 0x01A00000 | 0x8000;#else#warning Processor architecture undefined!static int csr0 = 0x00A00000 | 0x4800;#endif/* Operational parameters that usually are not changed. *//* Time in jiffies before concluding the transmitter is hung. */#define TX_TIMEOUT		(4 * HZ)#define PKT_BUF_SZ		1536			/* Size of each temporary Rx buffer.*/#define PKT_SETUP_SZ		192			/* Size of the setup frame *//* PCI registers */#define PCI_POWERMGMT 	0x40#include <linux/config.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/kernel.h>#include <linux/pci.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/delay.h>#include <linux/init.h>#include <linux/mii.h>#include <linux/ethtool.h>#include <linux/crc32.h>#include <asm/io.h>#include <asm/processor.h>	/* Processor type for cache alignment. */#include <asm/uaccess.h>/* These identify the driver base version and may not be removed. */static char version[] __devinitdata =KERN_INFO DRV_NAME ".c derived from tulip.c:v0.91 4/14/99 becker@scyld.com\n"KERN_INFO " unofficial 2.4.x kernel port, version " DRV_VERSION ", " DRV_RELDATE "\n";MODULE_AUTHOR("Donald Becker <becker@scyld.com>");MODULE_DESCRIPTION("Xircom CBE-100 ethernet driver");MODULE_LICENSE("GPL v2");MODULE_VERSION(DRV_VERSION);module_param(debug, int, 0);module_param(max_interrupt_work, int, 0);module_param(rx_copybreak, int, 0);module_param(csr0, int, 0);module_param_array(options, int, NULL, 0);module_param_array(full_duplex, int, NULL, 0);#define RUN_AT(x) (jiffies + (x))/*				Theory of OperationI. Board CompatibilityThis device driver was forked from the driver for the DECchip "Tulip",Digital's single-chip ethernet controllers for PCI.  It supports Xircom'salmost-Tulip-compatible CBE-100 CardBus adapters.II. Board-specific settingsPCI bus devices are configured by the system at boot time, so no jumpersneed to be set on the board.  The system BIOS preferably should assign thePCI INTA signal to an otherwise unused system IRQ line.III. Driver operationIIIa. Ring buffersThe Xircom can use either ring buffers or lists of Tx and Rx descriptors.This driver uses statically allocated rings of Rx and Tx descriptors, set atcompile time by RX/TX_RING_SIZE.  This version of the driver allocates skbuffsfor the Rx ring buffers at open() time and passes the skb->data field to theXircom as receive data buffers.  When an incoming frame is less thanRX_COPYBREAK bytes long, a fresh skbuff is allocated and the frame iscopied to the new skbuff.  When the incoming frame is larger, the skbuff ispassed directly up the protocol stack and replaced by a newly allocatedskbuff.The RX_COPYBREAK value is chosen to trade-off the memory wasted byusing a full-sized skbuff for small frames vs. the copying costs of largerframes.  For small frames the copying cost is negligible (esp. consideringthat we are pre-loading the cache with immediately useful headerinformation).  For large frames the copying cost is non-trivial, and thelarger copy might flush the cache of useful data.  A subtle aspect of thischoice is that the Xircom only receives into longword aligned buffers, thusthe IP header at offset 14 isn't longword aligned for further processing.Copied frames are put into the new skbuff at an offset of "+2", thus copyinghas the beneficial effect of aligning the IP header and preloading thecache.IIIC. SynchronizationThe driver runs as two independent, single-threaded flows of control.  Oneis the send-packet routine, which enforces single-threaded use by thedev->tbusy flag.  The other thread is the interrupt handler, which is singlethreaded by the hardware and other software.The send packet thread has partial control over the Tx ring and 'dev->tbusy'flag.  It sets the tbusy flag whenever it's queuing a Tx packet. If the nextqueue slot is empty, it clears the tbusy flag when finished otherwise it setsthe 'tp->tx_full' flag.The interrupt handler has exclusive control over the Rx ring and records statsfrom the Tx ring.  (The Tx-done interrupt can't be selectively turned off, sowe can't avoid the interrupt overhead by having the Tx routine reap the Txstats.)	 After reaping the stats, it marks the queue entry as empty by settingthe 'base' to zero.	 Iff the 'tp->tx_full' flag is set, it clears both thetx_full and tbusy flags.IV. NotesIVb. Referenceshttp://cesdis.gsfc.nasa.gov/linux/misc/NWay.htmlhttp://www.digital.com  (search for current 21*4* datasheets and "21X4 SROM")http://www.national.com/pf/DP/DP83840A.htmlIVc. Errata*//* A full-duplex map for media types. */enum MediaIs {	MediaIsFD = 1, MediaAlwaysFD=2, MediaIsMII=4, MediaIsFx=8,	MediaIs100=16};static const char media_cap[] ={0,0,0,16,  3,19,16,24,  27,4,7,5, 0,20,23,20 };/* Offsets to the Command and Status Registers, "CSRs".  All accesses   must be longword instructions and quadword aligned. */enum xircom_offsets {	CSR0=0,    CSR1=0x08, CSR2=0x10, CSR3=0x18, CSR4=0x20, CSR5=0x28,	CSR6=0x30, CSR7=0x38, CSR8=0x40, CSR9=0x48, CSR10=0x50, CSR11=0x58,	CSR12=0x60, CSR13=0x68, CSR14=0x70, CSR15=0x78, CSR16=0x04, };/* The bits in the CSR5 status registers, mostly interrupt sources. */enum status_bits {	LinkChange=0x08000000,	NormalIntr=0x10000, NormalIntrMask=0x00014045,	AbnormalIntr=0x8000, AbnormalIntrMask=0x0a00a5a2,	ReservedIntrMask=0xe0001a18,	EarlyRxIntr=0x4000, BusErrorIntr=0x2000,	EarlyTxIntr=0x400, RxDied=0x100, RxNoBuf=0x80, RxIntr=0x40,	TxFIFOUnderflow=0x20, TxNoBuf=0x04, TxDied=0x02, TxIntr=0x01,};enum csr0_control_bits {	EnableMWI=0x01000000, EnableMRL=0x00800000,	EnableMRM=0x00200000, EqualBusPrio=0x02,	SoftwareReset=0x01,};enum csr6_control_bits {	ReceiveAllBit=0x40000000, AllMultiBit=0x80, PromiscBit=0x40,	HashFilterBit=0x01, FullDuplexBit=0x0200,	TxThresh10=0x400000, TxStoreForw=0x200000,	TxThreshMask=0xc000, TxThreshShift=14,	EnableTx=0x2000, EnableRx=0x02,	ReservedZeroMask=0x8d930134, ReservedOneMask=0x320c0000,	EnableTxRx=(EnableTx | EnableRx),};enum tbl_flag {	HAS_MII=1, HAS_ACPI=2,};static struct xircom_chip_table {	char *chip_name;	int valid_intrs;			/* CSR7 interrupt enable settings */	int flags;} xircom_tbl[] = {  { "Xircom Cardbus Adapter",	LinkChange | NormalIntr | AbnormalIntr | BusErrorIntr |	RxDied | RxNoBuf | RxIntr | TxFIFOUnderflow | TxNoBuf | TxDied | TxIntr,	HAS_MII | HAS_ACPI, },  { NULL, },};/* This matches the table above. */enum chips {	X3201_3,};/* The Xircom Rx and Tx buffer descriptors. */struct xircom_rx_desc {	s32 status;	s32 length;	u32 buffer1, buffer2;};struct xircom_tx_desc {	s32 status;	s32 length;	u32 buffer1, buffer2;				/* We use only buffer 1.  */};enum tx_desc0_status_bits {	Tx0DescOwned=0x80000000, Tx0DescError=0x8000, Tx0NoCarrier=0x0800,	Tx0LateColl=0x0200, Tx0ManyColl=0x0100, Tx0Underflow=0x02,};enum tx_desc1_status_bits {	Tx1ComplIntr=0x80000000, Tx1LastSeg=0x40000000, Tx1FirstSeg=0x20000000,	Tx1SetupPkt=0x08000000, Tx1DisableCRC=0x04000000, Tx1RingWrap=0x02000000,	Tx1ChainDesc=0x01000000, Tx1NoPad=0x800000, Tx1HashSetup=0x400000,	Tx1WholePkt=(Tx1FirstSeg | Tx1LastSeg),};enum rx_desc0_status_bits {	Rx0DescOwned=0x80000000, Rx0DescError=0x8000, Rx0NoSpace=0x4000,	Rx0Runt=0x0800, Rx0McastPkt=0x0400, Rx0FirstSeg=0x0200, Rx0LastSeg=0x0100,	Rx0HugeFrame=0x80, Rx0CRCError=0x02,	Rx0WholePkt=(Rx0FirstSeg | Rx0LastSeg),};enum rx_desc1_status_bits {	Rx1RingWrap=0x02000000, Rx1ChainDesc=0x01000000,};struct xircom_private {	struct xircom_rx_desc rx_ring[RX_RING_SIZE];	struct xircom_tx_desc tx_ring[TX_RING_SIZE];	/* The saved address of a sent-in-place packet/buffer, for skfree(). */	struct sk_buff* tx_skbuff[TX_RING_SIZE];#ifdef CARDBUS	/* The X3201-3 requires 4-byte aligned tx bufs */	struct sk_buff* tx_aligned_skbuff[TX_RING_SIZE];#endif	/* The addresses of receive-in-place skbuffs. */	struct sk_buff* rx_skbuff[RX_RING_SIZE];	u16 setup_frame[PKT_SETUP_SZ / sizeof(u16)];	/* Pseudo-Tx frame to init address table. */	int chip_id;	struct net_device_stats stats;	unsigned int cur_rx, cur_tx;		/* The next free ring entry */	unsigned int dirty_rx, dirty_tx;	/* The ring entries to be free()ed. */	unsigned int tx_full:1;				/* The Tx queue is full. */	unsigned int speed100:1;	unsigned int full_duplex:1;			/* Full-duplex operation requested. */	unsigned int autoneg:1;	unsigned int default_port:4;		/* Last dev->if_port value. */	unsigned int open:1;	unsigned int csr0;					/* CSR0 setting. */	unsigned int csr6;					/* Current CSR6 control settings. */	u16 to_advertise;					/* NWay capabilities advertised.  */	u16 advertising[4];	signed char phys[4], mii_cnt;		/* MII device addresses. */	int saved_if_port;	struct pci_dev *pdev;	spinlock_t lock;};static int mdio_read(struct net_device *dev, int phy_id, int location);static void mdio_write(struct net_device *dev, int phy_id, int location, int value);static void xircom_up(struct net_device *dev);static void xircom_down(struct net_device *dev);static int xircom_open(struct net_device *dev);static void xircom_tx_timeout(struct net_device *dev);static void xircom_init_ring(struct net_device *dev);static int xircom_start_xmit(struct sk_buff *skb, struct net_device *dev);static int xircom_rx(struct net_device *dev);static void xircom_media_change(struct net_device *dev);static irqreturn_t xircom_interrupt(int irq, void *dev_instance, struct pt_regs *regs);static int xircom_close(struct net_device *dev);static struct net_device_stats *xircom_get_stats(struct net_device *dev);static int xircom_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);static void set_rx_mode(struct net_device *dev);static void check_duplex(struct net_device *dev);static struct ethtool_ops ops;/* The Xircom cards are picky about when certain bits in CSR6 can be   manipulated.  Keith Owens <kaos@ocs.com.au>. */static void outl_CSR6(u32 newcsr6, long ioaddr){	const int strict_bits =		TxThresh10 | TxStoreForw | TxThreshMask | EnableTxRx | FullDuplexBit;    int csr5, csr5_22_20, csr5_19_17, currcsr6, attempts = 200;    unsigned long flags;    save_flags(flags);    cli();	/* mask out the reserved bits that always read 0 on the Xircom cards */	newcsr6 &= ~ReservedZeroMask;	/* or in the reserved bits that always read 1 */	newcsr6 |= ReservedOneMask;    currcsr6 = inl(ioaddr + CSR6);    if (((newcsr6 & strict_bits) == (currcsr6 & strict_bits)) ||	((currcsr6 & ~EnableTxRx) == 0)) {		outl(newcsr6, ioaddr + CSR6);	/* safe */		restore_flags(flags);		return;    }    /* make sure the transmitter and receiver are stopped first */    currcsr6 &= ~EnableTxRx;    while (1) {		csr5 = inl(ioaddr + CSR5);		if (csr5 == 0xffffffff)			break;  /* cannot read csr5, card removed? */		csr5_22_20 = csr5 & 0x700000;		csr5_19_17 = csr5 & 0x0e0000;		if ((csr5_22_20 == 0 || csr5_22_20 == 0x600000) &&			(csr5_19_17 == 0 || csr5_19_17 == 0x80000 || csr5_19_17 == 0xc0000))			break;  /* both are stopped or suspended */		if (!--attempts) {			printk(KERN_INFO DRV_NAME ": outl_CSR6 too many attempts,"				   "csr5=0x%08x\n", csr5);			outl(newcsr6, ioaddr + CSR6);  /* unsafe but do it anyway */			restore_flags(flags);			return;		}		outl(currcsr6, ioaddr + CSR6);		udelay(1);    }    /* now it is safe to change csr6 */    outl(newcsr6, ioaddr + CSR6);    restore_flags(flags);}static void __devinit read_mac_address(struct net_device *dev){	long ioaddr = dev->base_addr;	int i, j;	unsigned char tuple, link, data_id, data_count;	/* Xircom has its address stored in the CIS;	 * we access it through the boot rom interface for now	 * this might not work, as the CIS is not parsed but I	 * (danilo) use the offset I found on my card's CIS !!!	 *	 * Doug Ledford: I changed this routine around so that it	 * walks the CIS memory space, parsing the config items, and	 * finds the proper lan_node_id tuple and uses the data	 * stored there.	 */	outl(1 << 12, ioaddr + CSR9); /* enable boot rom access */	for (i = 0x100; i < 0x1f7; i += link+2) {		outl(i, ioaddr + CSR10);		tuple = inl(ioaddr + CSR9) & 0xff;		outl(i + 1, ioaddr + CSR10);		link = inl(ioaddr + CSR9) & 0xff;		outl(i + 2, ioaddr + CSR10);		data_id = inl(ioaddr + CSR9) & 0xff;		outl(i + 3, ioaddr + CSR10);		data_count = inl(ioaddr + CSR9) & 0xff;		if ( (tuple == 0x22) &&			 (data_id == 0x04) && (data_count == 0x06) ) {			/*			 * This is it.  We have the data we want.			 */			for (j = 0; j < 6; j++) {				outl(i + j + 4, ioaddr + CSR10);				dev->dev_addr[j] = inl(ioaddr + CSR9) & 0xff;			}

⌨️ 快捷键说明

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