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

📄 pcnet32.c

📁 powerpc内核mpc8241linux系统下net驱动程序
💻 C
📖 第 1 页 / 共 3 页
字号:
/* pcnet32.c: An AMD PCnet32 ethernet driver for linux. *//* *      Copyright 1996-1999 Thomas Bogendoerfer *  * 	Derived from the lance driver written 1993,1994,1995 by Donald Becker. *  * 	Copyright 1993 United States Government as represented by the * 	Director, National Security Agency. *  * 	This software may be used and distributed according to the terms * 	of the GNU Public License, incorporated herein by reference. * * 	This driver is for PCnet32 and PCnetPCI based ethercards */static const char *version = "pcnet32.c:v1.11 17.1.99 tsbogend@alpha.franken.de\n";#include <linux/config.h>#include <linux/module.h>#ifdef MODVERSIONS#include <linux/modversions.h>#endif#include <linux/kernel.h>#include <linux/sched.h>#include <linux/string.h>#include <linux/ptrace.h>#include <linux/errno.h>#include <linux/ioport.h>#include <linux/malloc.h>#include <linux/interrupt.h>#include <linux/pci.h>#include <linux/delay.h>#include <linux/init.h>#include <asm/bitops.h>#include <asm/io.h>#include <asm/dma.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/skbuff.h>static unsigned int pcnet32_portlist[] __initdata = {0x300, 0x320, 0x340, 0x360, 0};static int pcnet32_debug = 1;#ifdef MODULEstatic struct device *pcnet32_dev = NULL;#endifstatic const int max_interrupt_work = 20;static const int rx_copybreak = 200;#define PORT_AUI      0x00#define PORT_10BT     0x01#define PORT_GPSI     0x02#define PORT_MII      0x03#define PORT_PORTSEL  0x03#define PORT_ASEL     0x04#define PORT_FD       0x80static int options = PORT_ASEL;  /* port selection *//* * 				Theory of Operation *  * This driver uses the same software structure as the normal lance * driver. So look for a verbose description in lance.c. The differences * to the normal lance driver is the use of the 32bit mode of PCnet32 * and PCnetPCI chips. Because these chips are 32bit chips, there is no * 16MB limitation and we don't need bounce buffers. */ /* * History: * v0.01:  Initial version *         only tested on Alpha Noname Board * v0.02:  changed IRQ handling for new interrupt scheme (dev_id) *         tested on a ASUS SP3G * v0.10:  fixed an odd problem with the 79C974 in a Compaq Deskpro XL *         looks like the 974 doesn't like stopping and restarting in a *         short period of time; now we do a reinit of the lance; the *         bug was triggered by doing ifconfig eth0 <ip> broadcast <addr> *         and hangs the machine (thanks to Klaus Liedl for debugging) * v0.12:  by suggestion from Donald Becker: Renamed driver to pcnet32, *         made it standalone (no need for lance.c) * v0.13:  added additional PCI detecting for special PCI devices (Compaq) * v0.14:  stripped down additional PCI probe (thanks to David C Niemi *         and sveneric@xs4all.nl for testing this on their Compaq boxes) * v0.15:  added 79C965 (VLB) probe *         added interrupt sharing for PCI chips * v0.16:  fixed set_multicast_list on Alpha machines * v0.17:  removed hack from dev.c; now pcnet32 uses ethif_probe in Space.c * v0.19:  changed setting of autoselect bit * v0.20:  removed additional Compaq PCI probe; there is now a working one *	   in arch/i386/bios32.c * v0.21:  added endian conversion for ppc, from work by cort@cs.nmt.edu * v0.22:  added printing of status to ring dump * v0.23:  changed enet_statistics to net_devive_stats * v0.90:  added multicast filter *         added module support *         changed irq probe to new style *         added PCnetFast chip id *         added fix for receive stalls with Intel saturn chipsets *         added in-place rx skbs like in the tulip driver *         minor cleanups * v0.91:  added PCnetFast+ chip id *         back port to 2.0.x * v1.00:  added some stuff from Donald Becker's 2.0.34 version *         added support for byte counters in net_dev_stats * v1.01:  do ring dumps, only when debugging the driver *         increased the transmit timeout * v1.02:  fixed memory leak in pcnet32_init_ring() * v1.10:  workaround for stopped transmitter *         added port selection for modules *         detect special T1/E1 WAN card and setup port selection * v1.11:  fixed wrong checking of Tx errors *//* * Set the number of Tx and Rx buffers, using Log_2(# buffers). * Reasonable default values are 4 Tx buffers, and 16 Rx buffers. * That translates to 2 (4 == 2^^2) and 4 (16 == 2^^4). */#ifndef PCNET32_LOG_TX_BUFFERS#define PCNET32_LOG_TX_BUFFERS 4#define PCNET32_LOG_RX_BUFFERS 4#endif#define TX_RING_SIZE			(1 << (PCNET32_LOG_TX_BUFFERS))#define TX_RING_MOD_MASK		(TX_RING_SIZE - 1)#define TX_RING_LEN_BITS		((PCNET32_LOG_TX_BUFFERS) << 12)#define RX_RING_SIZE			(1 << (PCNET32_LOG_RX_BUFFERS))#define RX_RING_MOD_MASK		(RX_RING_SIZE - 1)#define RX_RING_LEN_BITS		((PCNET32_LOG_RX_BUFFERS) << 4)#define PKT_BUF_SZ		1544/* Offsets from base I/O address. */#define PCNET32_DATA 0x10#define PCNET32_ADDR 0x12#define PCNET32_RESET 0x14#define PCNET32_BUS_IF 0x16#define PCNET32_TOTAL_SIZE 0x18#define CRC_POLYNOMIAL_LE 0xedb88320UL  /* Ethernet CRC, little endian *//* The PCNET32 Rx and Tx ring descriptors. */struct pcnet32_rx_head {	u32 base;	s16 buf_length;        s16 status;    	u32 msg_length;	u32 reserved;};	struct pcnet32_tx_head {	u32 base;	s16 length;        s16 status;	u32 misc;	u32 reserved;};/* The PCNET32 32-Bit initialization block, described in databook. */struct pcnet32_init_block {	u16 mode;	u16 tlen_rlen;	u8  phys_addr[6];	u16 reserved;	u32 filter[2];	/* Receive and transmit ring base, along with extra bits. */    	u32 rx_ring;	u32 tx_ring;};struct pcnet32_private {	/* The Tx and Rx ring entries must be aligned on 16-byte boundaries in 32bit mode. */	struct pcnet32_rx_head   rx_ring[RX_RING_SIZE];	struct pcnet32_tx_head   tx_ring[TX_RING_SIZE];	struct pcnet32_init_block	init_block;	const char *name;	/* The saved address of a sent-in-place packet/buffer, for skfree(). */	struct sk_buff *tx_skbuff[TX_RING_SIZE];        struct sk_buff *rx_skbuff[RX_RING_SIZE];	int cur_rx, cur_tx;			/* The next free ring entry */	int dirty_rx, dirty_tx;		        /* The ring entries to be free()ed. */	struct net_device_stats stats;	char tx_full;        int  options;        int  shared_irq:1,                      /* shared irq possible */             full_duplex:1;                     /* full duplex possible */#ifdef MODULE        struct device *next;#endif    };int  pcnet32_probe(struct device *dev);static int  pcnet32_probe1(struct device *dev, unsigned int ioaddr, unsigned char irq_line, int shared);static int  pcnet32_open(struct device *dev);static int  pcnet32_init_ring(struct device *dev);static int  pcnet32_start_xmit(struct sk_buff *skb, struct device *dev);static int  pcnet32_rx(struct device *dev);static void pcnet32_interrupt(int irq, void *dev_id, struct pt_regs *regs);static int  pcnet32_close(struct device *dev);static struct net_device_stats *pcnet32_get_stats(struct device *dev);static void pcnet32_set_multicast_list(struct device *dev);__initfunc(int pcnet32_probe (struct device *dev)){    unsigned int  ioaddr = dev ? dev->base_addr: 0;    unsigned int  irq_line = dev ? dev->irq : 0;    int *port;        if (ioaddr > 0x1ff)      return pcnet32_probe1(dev, ioaddr, irq_line, 0);    else if(ioaddr != 0)      return ENXIO;    #if defined(CONFIG_PCI)    if (pci_present()) {	struct pci_dev *pdev = NULL;		printk("pcnet32.c: PCI bios is present, checking for devices...\n");	while ((pdev = pci_find_device(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_LANCE, pdev))) {	    unsigned short pci_command;	    irq_line = pdev->irq;	    ioaddr = pdev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK;	    /* PCI Spec 2.1 states that it is either the driver or PCI card's	     * responsibility to set the PCI Master Enable Bit if needed.	     *	(From Mark Stockton <marks@schooner.sys.hou.compaq.com>)	     */	    pci_read_config_word(pdev, PCI_COMMAND, &pci_command);	    	    /* Avoid already found cards from previous pcnet32_probe() calls */	    if (check_region(ioaddr, PCNET32_TOTAL_SIZE))	      continue;	    if ( ! (pci_command & PCI_COMMAND_MASTER)) {		printk("PCI Master Bit has not been set. Setting...\n");		pci_command |= PCI_COMMAND_MASTER|PCI_COMMAND_IO;		pci_write_config_word(pdev, PCI_COMMAND, pci_command);	    }	    	    printk("Found PCnet/PCI at %#x, irq %d.\n",		   ioaddr, irq_line);	    	    if (pcnet32_probe1(dev, ioaddr, irq_line, 1) != 0) {	/* Shouldn't happen. */		printk(KERN_ERR "pcnet32.c: Probe of PCI card at %#x failed.\n", ioaddr);		break;	    }	    return 0;	}    } else #endif  /* defined(CONFIG_PCI) */        /* now look for PCnet32 VLB cards */    for (port = pcnet32_portlist; *port; port++) {	unsigned int ioaddr = *port;		if ( check_region(ioaddr, PCNET32_TOTAL_SIZE) == 0) {    	    /* check if there is really a pcnet chip on that ioaddr */    	    if ((inb(ioaddr + 14) == 0x57) &&		(inb(ioaddr + 15) == 0x57) &&	        (pcnet32_probe1(dev, ioaddr, 0, 0) == 0))	      return 0;	}    }    return ENODEV;}/* pcnet32_probe1 */__initfunc(static int pcnet32_probe1(struct device *dev, unsigned int ioaddr, unsigned char irq_line, int shared)){    struct pcnet32_private *lp;    int i,full_duplex = 0;    char *chipname;    inw(ioaddr+PCNET32_RESET); /* Reset the PCNET32 */    outw(0x0000, ioaddr+PCNET32_ADDR); /* Switch to window 0 */    if (inw(ioaddr+PCNET32_DATA) != 0x0004)      return ENODEV;    /* Get the version of the chip. */    outw(88, ioaddr+PCNET32_ADDR);    if (inw(ioaddr+PCNET32_ADDR) != 88) {	/* should never happen */	return ENODEV;    } else {			/* Good, it's a newer chip. */	int chip_version = inw(ioaddr+PCNET32_DATA);	outw(89, ioaddr+PCNET32_ADDR);	chip_version |= inw(ioaddr+PCNET32_DATA) << 16;	if (pcnet32_debug > 2)	  printk("  PCnet chip version is %#x.\n", chip_version);	if ((chip_version & 0xfff) != 0x003)	  return ENODEV;	chip_version = (chip_version >> 12) & 0xffff;	switch (chip_version) {	 case 0x2420:	    chipname = "PCnet/PCI 79C970";	    break;	 case 0x2430:	    if (shared)		chipname = "PCnet/PCI 79C970"; /* 970 gives the wrong chip id back */	    else		chipname = "PCnet/32 79C965";	    break;	 case 0x2621:	    chipname = "PCnet/PCI II 79C970A";	    full_duplex = 1;	    break;	 case 0x2623:	    chipname = "PCnet/FAST 79C971";	    full_duplex = 1;	    break;	 case 0x2624:	    chipname = "PCnet/FAST+ 79C972";	    full_duplex = 1;	    break;	 default:	    printk("pcnet32: PCnet version %#x, no PCnet32 chip.\n",chip_version);	    return ENODEV;	}    }        if (dev == NULL)	dev = init_etherdev(0, 0);    printk("%s: %s at %#3x,", dev->name, chipname, ioaddr);    /* There is a 16 byte station address PROM at the base address.     The first six bytes are the station address. */    for (i = 0; i < 6; i++)      printk(" %2.2x", dev->dev_addr[i] = inb(ioaddr + i));    dev->base_addr = ioaddr;    request_region(ioaddr, PCNET32_TOTAL_SIZE, chipname);        /* Make certain the data structures used by the PCnet32 are 16byte aligned and DMAble. */    lp = (struct pcnet32_private *) (((unsigned long)kmalloc(sizeof(*lp)+15, GFP_DMA | GFP_KERNEL)+15) & ~15);          memset(lp, 0, sizeof(*lp));    dev->priv = lp;    lp->name = chipname;    lp->shared_irq = shared;    lp->full_duplex = full_duplex;    lp->options = options;        /* detect special T1/E1 WAN card by checking for MAC address */    if (dev->dev_addr[0] == 0x00 && dev->dev_addr[1] == 0xe0 && dev->dev_addr[2] == 0x75)	lp->options = PORT_FD | PORT_GPSI;    lp->init_block.mode = le16_to_cpu(0x0003); 	/* Disable Rx and Tx. */    lp->init_block.tlen_rlen = le16_to_cpu(TX_RING_LEN_BITS | RX_RING_LEN_BITS);     for (i = 0; i < 6; i++)      lp->init_block.phys_addr[i] = dev->dev_addr[i];    lp->init_block.filter[0] = 0x00000000;    lp->init_block.filter[1] = 0x00000000;    lp->init_block.rx_ring = (u32)le32_to_cpu(virt_to_bus(lp->rx_ring));    lp->init_block.tx_ring = (u32)le32_to_cpu(virt_to_bus(lp->tx_ring));        /* switch pcnet32 to 32bit mode */    outw(0x0014, ioaddr+PCNET32_ADDR);

⌨️ 快捷键说明

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