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

📄 pcnet32.c

📁 GNU Mach 微内核源代码, 基于美国卡内基美隆大学的 Mach 研究项目
💻 C
📖 第 1 页 / 共 2 页
字号:
	   the upper ownership bit. */	for (i = 0; i < TX_RING_SIZE; i++) {	        lp->tx_ring[i].base = 0;	        lp->tx_ring[i].status = 0;	}	lp->init_block.tlen_rlen = 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.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));}static voidpcnet32_restart(struct device *dev, unsigned int csr0_bits, int must_reinit){        int i;	unsigned int ioaddr = dev->base_addr;	pcnet32_purge_tx_ring(dev);	pcnet32_init_ring(dev);	outw(0x0000, ioaddr + PCNET32_ADDR);        /* ReInit Ring */        outw(0x0001, ioaddr + PCNET32_DATA);	i = 0;	while (i++ < 100)		if (inw(ioaddr+PCNET32_DATA) & 0x0100)			break;	outw(csr0_bits, ioaddr + PCNET32_DATA);}static intpcnet32_start_xmit(struct sk_buff *skb, struct device *dev){	struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv;	unsigned int ioaddr = dev->base_addr;	int entry;	unsigned long flags;	/* Transmitter timeout, serious problems. */	if (dev->tbusy) {		int tickssofar = jiffies - dev->trans_start;		if (tickssofar < 20)			return 1;		outw(0, ioaddr+PCNET32_ADDR);		printk("%s: transmit timed out, status %4.4x, resetting.\n",			   dev->name, inw(ioaddr+PCNET32_DATA));		outw(0x0004, ioaddr+PCNET32_DATA);		lp->stats.tx_errors++;#ifndef final_version		{			int i;			printk(" Ring data dump: dirty_tx %d cur_tx %d%s cur_rx %d.",				   lp->dirty_tx, lp->cur_tx, lp->tx_full ? " (full)" : "",				   lp->cur_rx);			for (i = 0 ; i < RX_RING_SIZE; i++)				printk("%s %08x %04x %08x %04x", i & 1 ? "" : "\n ",					   lp->rx_ring[i].base, -lp->rx_ring[i].buf_length,					   lp->rx_ring[i].msg_length, (unsigned)lp->rx_ring[i].status);			for (i = 0 ; i < TX_RING_SIZE; i++)				printk("%s %08x %04x %08x %04x", i & 1 ? "" : "\n ",					   lp->tx_ring[i].base, -lp->tx_ring[i].length,					   lp->tx_ring[i].misc, (unsigned)lp->tx_ring[i].status);			printk("\n");		}#endif		pcnet32_restart(dev, 0x0042, 1);		dev->tbusy = 0;		dev->trans_start = jiffies;		return 0;	}	if (pcnet32_debug > 3) {		outw(0x0000, ioaddr+PCNET32_ADDR);		printk("%s: pcnet32_start_xmit() called, csr0 %4.4x.\n", dev->name,			   inw(ioaddr+PCNET32_DATA));		outw(0x0000, ioaddr+PCNET32_DATA);	}	/* Block a timer-based transmit from overlapping.  This could better be	   done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */	if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) {		printk("%s: Transmitter access conflict.\n", dev->name);		return 1;	}	if (test_and_set_bit(0, (void*)&lp->lock) != 0) {		if (pcnet32_debug > 0)			printk("%s: tx queue lock!.\n", dev->name);		/* don't clear dev->tbusy flag. */		return 1;	}	/* Fill in a Tx ring entry */	/* Mask to ring buffer boundary. */	entry = lp->cur_tx & TX_RING_MOD_MASK;	/* Caution: the write order is important here, set the base address	   with the "ownership" bits last. */	lp->tx_ring[entry].length = le16_to_cpu(-skb->len);	lp->tx_ring[entry].misc = 0x00000000;	lp->tx_skbuff[entry] = skb;	lp->tx_ring[entry].base = (u32)le32_to_cpu(virt_to_bus(skb->data));	lp->tx_ring[entry].status = le16_to_cpu(0x8300);	lp->cur_tx++;	/* Trigger an immediate send poll. */	outw(0x0000, ioaddr+PCNET32_ADDR);	outw(0x0048, ioaddr+PCNET32_DATA);	dev->trans_start = jiffies;	save_flags(flags);	cli();	lp->lock = 0;	if (lp->tx_ring[(entry+1) & TX_RING_MOD_MASK].base == 0)		clear_bit(0, (void*)&dev->tbusy);	else		lp->tx_full = 1;	restore_flags(flags);	return 0;}/* The PCNET32 interrupt handler. */static voidpcnet32_interrupt(int irq, void *dev_id, struct pt_regs * regs){	struct device *dev = (struct device *)dev_id;	struct pcnet32_private *lp;	unsigned int csr0, ioaddr;	int boguscnt = max_interrupt_work;	int must_restart;	if (dev == NULL) {		printk ("pcnet32_interrupt(): irq %d for unknown device.\n", irq);		return;	}	ioaddr = dev->base_addr;	lp = (struct pcnet32_private *)dev->priv;	if (dev->interrupt)		printk("%s: Re-entering the interrupt handler.\n", dev->name);	dev->interrupt = 1;	outw(0x00, dev->base_addr + PCNET32_ADDR);	while ((csr0 = inw(dev->base_addr + PCNET32_DATA)) & 0x8600		   && --boguscnt >= 0) {		/* Acknowledge all of the current interrupt sources ASAP. */		outw(csr0 & ~0x004f, dev->base_addr + PCNET32_DATA);		must_restart = 0;		if (pcnet32_debug > 5)			printk("%s: interrupt  csr0=%#2.2x new csr=%#2.2x.\n",				   dev->name, csr0, inw(dev->base_addr + PCNET32_DATA));		if (csr0 & 0x0400)			/* Rx interrupt */			pcnet32_rx(dev);		if (csr0 & 0x0200) {		/* Tx-done interrupt */			int dirty_tx = lp->dirty_tx;			while (dirty_tx < lp->cur_tx) {				int entry = dirty_tx & TX_RING_MOD_MASK;				int status = (short)le16_to_cpu(lp->tx_ring[entry].status);				if (status < 0)					break;			/* It still hasn't been Txed */				lp->tx_ring[entry].base = 0;				if (status & 0x4000) {					/* There was an major error, log it. */					int err_status = le16_to_cpu(lp->tx_ring[entry].misc);					lp->stats.tx_errors++;					if (err_status & 0x04000000) lp->stats.tx_aborted_errors++;					if (err_status & 0x08000000) lp->stats.tx_carrier_errors++;					if (err_status & 0x10000000) lp->stats.tx_window_errors++;					if (err_status & 0x40000000) {						/* Ackk!  On FIFO errors the Tx unit is turned off! */						lp->stats.tx_fifo_errors++;						/* Remove this verbosity later! */						printk("%s: Tx FIFO error! Status %4.4x.\n",							   dev->name, csr0);						/* Restart the chip. */						must_restart = 1;					}				} else {					if (status & 0x1800)						lp->stats.collisions++;					lp->stats.tx_packets++;				}				/* We must free the original skb */				if (lp->tx_skbuff[entry]) {					dev_kfree_skb(lp->tx_skbuff[entry], FREE_WRITE);					lp->tx_skbuff[entry] = 0;				}				dirty_tx++;			}#ifndef final_version			if (lp->cur_tx - dirty_tx >= TX_RING_SIZE) {				printk("out-of-sync dirty pointer, %d vs. %d, full=%d.\n",					   dirty_tx, lp->cur_tx, lp->tx_full);				dirty_tx += TX_RING_SIZE;			}#endif			if (lp->tx_full && dev->tbusy				&& dirty_tx > lp->cur_tx - TX_RING_SIZE + 2) {				/* The ring is no longer full, clear tbusy. */				lp->tx_full = 0;				clear_bit(0, (void*)&dev->tbusy);				mark_bh(NET_BH);			}			lp->dirty_tx = dirty_tx;		}		/* Log misc errors. */		if (csr0 & 0x4000) lp->stats.tx_errors++; /* Tx babble. */		if (csr0 & 0x1000) {			/*			 * this happens when our receive ring is full. This 			 * shouldn't be a problem as we will see normal rx 			 * interrupts for the frames in the receive ring. But 			 * there are some PCI chipsets (I can reproduce this 			 * on SP3G with Intel saturn chipset) which have some-			 * times problems and will fill up the receive ring 			 * with error descriptors. In this situation we don't 			 * get a rx interrupt, but a missed frame interrupt 			 * sooner or later. So we try to clean up our receive 			 * ring here.			 */			pcnet32_rx(dev);			lp->stats.rx_errors++; /* Missed a Rx frame. */		}		if (csr0 & 0x0800) {			printk("%s: Bus master arbitration failure, status %4.4x.\n",				   dev->name, csr0);			/* Restart the chip. */			must_restart = 1;		}		if (must_restart) {			/* stop the chip to clear the error condition, then restart */			outw(0x0000, dev->base_addr + PCNET32_ADDR);			outw(0x0004, dev->base_addr + PCNET32_DATA);			pcnet32_restart(dev, 0x0002, 0);		}	}	/* Clear any other interrupt, and set interrupt enable. */	outw(0x0000, dev->base_addr + PCNET32_ADDR);	outw(0x7940, dev->base_addr + PCNET32_DATA);	if (pcnet32_debug > 4)		printk("%s: exiting interrupt, csr%d=%#4.4x.\n",			   dev->name, inw(ioaddr + PCNET32_ADDR),			   inw(dev->base_addr + PCNET32_DATA));	dev->interrupt = 0;	return;}static intpcnet32_rx(struct device *dev){	struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv;	int entry = lp->cur_rx & RX_RING_MOD_MASK;	int i;	/* If we own the next entry, it's a new packet. Send it up. */	while ((short)le16_to_cpu(lp->rx_ring[entry].status) >= 0) {		int status = (short)le16_to_cpu(lp->rx_ring[entry].status) >> 8;		if (status != 0x03) {			/* There was an error. */			/* There is a tricky error noted by John Murphy,			   <murf@perftech.com> to Russ Nelson: Even with full-sized			   buffers it's possible for a jabber packet to use two			   buffers, with only the last correctly noting the error. */			if (status & 0x01)	/* Only count a general error at the */				lp->stats.rx_errors++; /* end of a packet.*/			if (status & 0x20) lp->stats.rx_frame_errors++;			if (status & 0x10) lp->stats.rx_over_errors++;			if (status & 0x08) lp->stats.rx_crc_errors++;			if (status & 0x04) lp->stats.rx_fifo_errors++;			lp->rx_ring[entry].status &= le16_to_cpu(0x03ff);		}		else		{			/* Malloc up new buffer, compatible with net-2e. */			short pkt_len = (le32_to_cpu(lp->rx_ring[entry].msg_length) & 0xfff)-4;			struct sk_buff *skb;			if(pkt_len < 60) {				printk("%s: Runt packet!\n",dev->name);				lp->stats.rx_errors++;			} else {				skb = dev_alloc_skb(pkt_len+2);				if (skb == NULL) {					printk("%s: Memory squeeze, deferring packet.\n",						   dev->name);					for (i=0; i < RX_RING_SIZE; i++)						if ((short)le16_to_cpu(lp->rx_ring[(entry+i) & RX_RING_MOD_MASK].status) < 0)							break;					if (i > RX_RING_SIZE -2)					{						lp->stats.rx_dropped++;						lp->rx_ring[entry].status |= le16_to_cpu(0x8000);						lp->cur_rx++;					}					break;				}				skb->dev = dev;				skb_reserve(skb,2);	/* 16 byte align */				skb_put(skb,pkt_len);	/* Make room */				eth_copy_and_sum(skb,					(unsigned char *)bus_to_virt(le32_to_cpu(lp->rx_ring[entry].base)),					pkt_len,0);				skb->protocol=eth_type_trans(skb,dev);				netif_rx(skb);				lp->stats.rx_packets++;			}		}		/* The docs say that the buffer length isn't touched, but Andrew Boyd		   of QNX reports that some revs of the 79C965 clear it. */		lp->rx_ring[entry].buf_length = le16_to_cpu(-PKT_BUF_SZ);		lp->rx_ring[entry].status |= le16_to_cpu(0x8000);		entry = (++lp->cur_rx) & RX_RING_MOD_MASK;	}	/* We should check that at least two ring entries are free.	 If not,	   we should free one and mark stats->rx_dropped++. */	return 0;}static intpcnet32_close(struct device *dev){	unsigned int ioaddr = dev->base_addr;	struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv;	dev->start = 0;	set_bit(0, (void*)&dev->tbusy);	outw(112, ioaddr+PCNET32_ADDR);	lp->stats.rx_missed_errors = inw(ioaddr+PCNET32_DATA);	outw(0, ioaddr+PCNET32_ADDR);	if (pcnet32_debug > 1)		printk("%s: Shutting down ethercard, status was %2.2x.\n",			   dev->name, inw(ioaddr+PCNET32_DATA));	/* We stop the PCNET32 here -- it occasionally polls	   memory if we don't. */	outw(0x0004, ioaddr+PCNET32_DATA);	free_irq(dev->irq, dev);	MOD_DEC_USE_COUNT;	return 0;}static struct enet_statistics *pcnet32_get_stats(struct device *dev){	struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv;	unsigned int ioaddr = dev->base_addr;	unsigned short saved_addr;	unsigned long flags;	save_flags(flags);	cli();	saved_addr = inw(ioaddr+PCNET32_ADDR);	outw(112, ioaddr+PCNET32_ADDR);	lp->stats.rx_missed_errors = inw(ioaddr+PCNET32_DATA);	outw(saved_addr, ioaddr+PCNET32_ADDR);	restore_flags(flags);	return &lp->stats;}/* Set or clear the multicast filter for this adaptor. */static void pcnet32_set_multicast_list(struct device *dev){	unsigned int ioaddr = dev->base_addr;	struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv;	if (dev->flags&IFF_PROMISC) {		/* Log any net taps. */		printk("%s: Promiscuous mode enabled.\n", dev->name);		lp->init_block.mode |= 0x8000;	} else {		int num_addrs=dev->mc_count;		if(dev->flags&IFF_ALLMULTI)			num_addrs=1;		/* FIXIT: We don't use the multicast table, but rely on upper-layer filtering. */		memset(lp->init_block.filter , (num_addrs == 0) ? 0 : -1, sizeof(lp->init_block.filter));		lp->init_block.mode &= ~0x8000;	}	outw(0, ioaddr+PCNET32_ADDR);	outw(0x0004, ioaddr+PCNET32_DATA); /* Temporarily stop the lance. */	pcnet32_restart(dev, 0x0042, 0); /*  Resume normal operation */}#ifdef MODULE#if LINUX_VERSION_CODE > 0x20118MODULE_AUTHOR("Donald Becker <becker@cesdis.gsfc.nasa.gov>");MODULE_DESCRIPTION("AMD PCnet/PCI ethernet driver");MODULE_PARM(debug, "i");MODULE_PARM(max_interrupt_work, "i");MODULE_PARM(rx_copybreak, "i");MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i");MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i");#endif/* An additional parameter that may be passed in... */static int debug = -1;intinit_module(void){	if (debug >= 0)		pcnet32_debug = debug;#ifdef CARDBUS	register_driver(&pcnet32_ops);	return 0;#else	return pcnet32_probe(NULL);#endif}voidcleanup_module(void){	struct device *next_dev;#ifdef CARDBUS	unregister_driver(&pcnet32_ops);#endif	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */	while (root_pcnet32_dev) {		next_dev = ((struct pcnet32_private *)root_pcnet32_dev->priv)->next_module;		unregister_netdev(root_pcnet32_dev);		release_region(root_pcnet32_dev->base_addr, PCNET32_TOTAL_SIZE);		kfree(root_pcnet32_dev);		root_pcnet32_dev = next_dev;	}}#endif  /* MODULE *//* * Local variables: *  compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c pcnet32.c  `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`" *  c-indent-level: 4 *  c-basic-offset: 4 *  tab-width: 4 * End: */

⌨️ 快捷键说明

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