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

📄 8139too.c

📁 Linux内核源代码 为压缩文件 是<<Linux内核>>一书中的源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
#endif /* RTL8139_NDEBUG */	/* only wake the queue if we did work, and the queue is stopped */	if (tp->dirty_tx != dirty_tx) {		tp->dirty_tx = dirty_tx;		mb();		if (netif_queue_stopped (dev))			netif_wake_queue (dev);	}}/* TODO: clean this up!  Rx reset need not be this intensive */static void rtl8139_rx_err (u32 rx_status, struct net_device *dev,			    struct rtl8139_private *tp, void *ioaddr){	u8 tmp8;	int tmp_work = 1000;	DPRINTK ("%s: Ethernet frame had errors, status %8.8x.\n",	         dev->name, rx_status);	if (rx_status & RxTooLong) {		DPRINTK ("%s: Oversized Ethernet frame, status %4.4x!\n",			 dev->name, rx_status);		/* A.C.: The chip hangs here. */	}	tp->stats.rx_errors++;	if (rx_status & (RxBadSymbol | RxBadAlign))		tp->stats.rx_frame_errors++;	if (rx_status & (RxRunt | RxTooLong))		tp->stats.rx_length_errors++;	if (rx_status & RxCRCErr)		tp->stats.rx_crc_errors++;	/* Reset the receiver, based on RealTek recommendation. (Bug?) */	tp->cur_rx = 0;	/* disable receive */	tmp8 = RTL_R8 (ChipCmd) & ChipCmdClear;	RTL_W8_F (ChipCmd, tmp8 | CmdTxEnb);	/* A.C.: Reset the multicast list. */	rtl8139_set_rx_mode (dev);	/* XXX potentially temporary hack to	 * restart hung receiver */	while (--tmp_work > 0) {		tmp8 = RTL_R8 (ChipCmd);		if ((tmp8 & CmdRxEnb) && (tmp8 & CmdTxEnb))			break;		RTL_W8_F (ChipCmd,			  (tmp8 & ChipCmdClear) | CmdRxEnb | CmdTxEnb);	}	/* G.S.: Re-enable receiver */	/* XXX temporary hack to work around receiver hang */	rtl8139_set_rx_mode (dev);	if (tmp_work <= 0)		printk (KERN_WARNING PFX "tx/rx enable wait too long\n");}/* The data sheet doesn't describe the Rx ring at all, so I'm guessing at the   field alignments and semantics. */static void rtl8139_rx_interrupt (struct net_device *dev,				  struct rtl8139_private *tp, void *ioaddr){	unsigned char *rx_ring;	u16 cur_rx;	assert (dev != NULL);	assert (tp != NULL);	assert (ioaddr != NULL);	rx_ring = tp->rx_ring;	cur_rx = tp->cur_rx;	DPRINTK ("%s: In rtl8139_rx(), current %4.4x BufAddr %4.4x,"		 " free to %4.4x, Cmd %2.2x.\n", dev->name, cur_rx,		 RTL_R16 (RxBufAddr),		 RTL_R16 (RxBufPtr), RTL_R8 (ChipCmd));	while ((RTL_R8 (ChipCmd) & RxBufEmpty) == 0) {		int ring_offset = cur_rx % RX_BUF_LEN;		u32 rx_status;		unsigned int rx_size;		unsigned int pkt_size;		struct sk_buff *skb;		/* read size+status of next frame from DMA ring buffer */		rx_status = le32_to_cpu (*(u32 *) (rx_ring + ring_offset));		rx_size = rx_status >> 16;		pkt_size = rx_size - 4;		DPRINTK ("%s:  rtl8139_rx() status %4.4x, size %4.4x,"			 " cur %4.4x.\n", dev->name, rx_status,			 rx_size, cur_rx);#if RTL8139_DEBUG > 2		{			int i;			DPRINTK ("%s: Frame contents ", dev->name);			for (i = 0; i < 70; i++)				printk (" %2.2x",					rx_ring[ring_offset + i]);			printk (".\n");		}#endif		/* E. Gill */		/* Note from BSD driver:		 * Here's a totally undocumented fact for you. When the		 * RealTek chip is in the process of copying a packet into		 * RAM for you, the length will be 0xfff0. If you spot a		 * packet header with this value, you need to stop. The		 * datasheet makes absolutely no mention of this and		 * RealTek should be shot for this.		 */		if (rx_size == 0xfff0)			break;		/* If Rx err or invalid rx_size/rx_status received		 * (which happens if we get lost in the ring),		 * Rx process gets reset, so we abort any further		 * Rx processing.		 */		if ((rx_size > (MAX_ETH_FRAME_SIZE+4)) ||		    (!(rx_status & RxStatusOK))) {			rtl8139_rx_err (rx_status, dev, tp, ioaddr);			return;		}		/* Malloc up new buffer, compatible with net-2e. */		/* Omit the four octet CRC from the length. */		/* TODO: consider allocating skb's outside of		 * interrupt context, both to speed interrupt processing,		 * and also to reduce the chances of having to		 * drop packets here under memory pressure.		 */		skb = dev_alloc_skb (pkt_size + 2);		if (skb) {			skb->dev = dev;			skb_reserve (skb, 2);	/* 16 byte align the IP fields. */			eth_copy_and_sum (skb, &rx_ring[ring_offset + 4], pkt_size, 0);			skb_put (skb, pkt_size);			skb->protocol = eth_type_trans (skb, dev);			netif_rx (skb);			tp->stats.rx_bytes += pkt_size;			tp->stats.rx_packets++;		} else {			printk (KERN_WARNING				"%s: Memory squeeze, dropping packet.\n",				dev->name);			tp->stats.rx_dropped++;		}		cur_rx = (cur_rx + rx_size + 4 + 3) & ~3;		RTL_W16_F (RxBufPtr, cur_rx - 16);	}	DPRINTK ("%s: Done rtl8139_rx(), current %4.4x BufAddr %4.4x,"		 " free to %4.4x, Cmd %2.2x.\n", dev->name, cur_rx,		 RTL_R16 (RxBufAddr),		 RTL_R16 (RxBufPtr), RTL_R8 (ChipCmd));	tp->cur_rx = cur_rx;}static void rtl8139_weird_interrupt (struct net_device *dev,				     struct rtl8139_private *tp,				     void *ioaddr,				     int status, int link_changed){	printk (KERN_DEBUG "%s: Abnormal interrupt, status %8.8x.\n",		dev->name, status);	assert (dev != NULL);	assert (tp != NULL);	assert (ioaddr != NULL);	/* Update the error count. */	tp->stats.rx_missed_errors += RTL_R32 (RxMissed);	RTL_W32 (RxMissed, 0);	if ((status & RxUnderrun) && link_changed &&	    (tp->drv_flags & HAS_LNK_CHNG)) {		/* Really link-change on new chips. */		int lpar = RTL_R16 (NWayLPAR);		int duplex = (lpar & 0x0100) || (lpar & 0x01C0) == 0x0040				|| tp->duplex_lock;		if (tp->full_duplex != duplex) {			tp->full_duplex = duplex;			RTL_W8 (Cfg9346, Cfg9346_Unlock);			RTL_W8 (Config1, tp->full_duplex ? 0x60 : 0x20);			RTL_W8 (Cfg9346, Cfg9346_Lock);		}		status &= ~RxUnderrun;	}	/* XXX along with rtl8139_rx_err, are we double-counting errors? */	if (status &	    (RxUnderrun | RxOverflow | RxErr | RxFIFOOver))		tp->stats.rx_errors++;	if (status & (PCSTimeout))		tp->stats.rx_length_errors++;	if (status & (RxUnderrun | RxFIFOOver))		tp->stats.rx_fifo_errors++;	if (status & RxOverflow) {		tp->stats.rx_over_errors++;		tp->cur_rx = RTL_R16 (RxBufAddr) % RX_BUF_LEN;		RTL_W16_F (RxBufPtr, tp->cur_rx - 16);	}	if (status & PCIErr) {		u16 pci_cmd_status;		pci_read_config_word (tp->pci_dev, PCI_STATUS, &pci_cmd_status);		printk (KERN_ERR "%s: PCI Bus error %4.4x.\n",			dev->name, pci_cmd_status);	}}/* The interrupt handler does all of the Rx thread work and cleans up   after the Tx thread. */static void rtl8139_interrupt (int irq, void *dev_instance,			       struct pt_regs *regs){	struct net_device *dev = (struct net_device *) dev_instance;	struct rtl8139_private *tp = dev->priv;	int boguscnt = max_interrupt_work;	void *ioaddr = tp->mmio_addr;	int status = 0, link_changed = 0; /* avoid bogus "uninit" warning */	spin_lock (&tp->lock);	do {		status = RTL_R16 (IntrStatus);		/* h/w no longer present (hotplug?) or major error, bail */		if (status == 0xFFFF)			break;		/* Acknowledge all of the current interrupt sources ASAP, but		   an first get an additional status bit from CSCR. */		if (status & RxUnderrun)			link_changed = RTL_R16 (CSCR) & CSCR_LinkChangeBit;		/* E. Gill */		/* In case of an RxFIFOOver we must also clear the RxOverflow		   bit to avoid dropping frames for ever. Believe me, I got a		   lot of troubles copying huge data (approximately 2 RxFIFOOver		   errors per 1GB data transfer).		   The following is written in the 'p-guide.pdf' file (RTL8139(A/B)		   Programming guide V0.1, from 1999/1/15) on page 9 from REALTEC.		   -----------------------------------------------------------		   2. RxFIFOOvw handling:		     When RxFIFOOvw occurs, all incoming packets are discarded.		     Clear ISR(RxFIFOOvw) doesn't dismiss RxFIFOOvw event. To		     dismiss RxFIFOOvw event, the ISR(RxBufOvw) must be written		     with a '1'.		   -----------------------------------------------------------		   Unfortunately I was not able to find any reason for the		   RxFIFOOver error (I got the feeling this depends on the		   CPU speed, lower CPU speed --> more errors).		   After clearing the RxOverflow bit the transfer of the		   packet was repeated and all data are error free transfered */		RTL_W16_F (IntrStatus, (status & RxFIFOOver) ? (status | RxOverflow) : status);		DPRINTK ("%s: interrupt  status=%#4.4x new intstat=%#4.4x.\n",				dev->name, status,				RTL_R16 (IntrStatus));		if ((status &		     (PCIErr | PCSTimeout | RxUnderrun | RxOverflow |		      RxFIFOOver | TxErr | TxOK | RxErr | RxOK)) == 0)			break;		/* Check uncommon events with one test. */		if (status & (PCIErr | PCSTimeout | RxUnderrun | RxOverflow |		  	      RxFIFOOver | TxErr | RxErr))			rtl8139_weird_interrupt (dev, tp, ioaddr,						 status, link_changed);		if (status & (RxOK | RxUnderrun | RxOverflow | RxFIFOOver))	/* Rx interrupt */			rtl8139_rx_interrupt (dev, tp, ioaddr);		if (status & (TxOK | TxErr))			rtl8139_tx_interrupt (dev, tp, ioaddr);		boguscnt--;	} while (boguscnt > 0);	if (boguscnt <= 0) {		printk (KERN_WARNING			"%s: Too much work at interrupt, "			"IntrStatus=0x%4.4x.\n", dev->name,			status);		/* Clear all interrupt sources. */		RTL_W16 (IntrStatus, 0xffff);	}	spin_unlock (&tp->lock);	DPRINTK ("%s: exiting interrupt, intr_status=%#4.4x.\n",		 dev->name, RTL_R16 (IntrStatus));}static int rtl8139_close (struct net_device *dev){	struct rtl8139_private *tp = dev->priv;	void *ioaddr = tp->mmio_addr;	int ret = 0;	DPRINTK ("ENTER\n");	netif_stop_queue (dev);	if (tp->thr_pid >= 0) {		ret = kill_proc (tp->thr_pid, SIGTERM, 1);		if (ret) {			printk (KERN_ERR "%s: unable to signal thread\n", dev->name);			return ret;		}		down (&tp->thr_exited);	}	DPRINTK ("%s: Shutting down ethercard, status was 0x%4.4x.\n",			dev->name, RTL_R16 (IntrStatus));	spin_lock_irq (&tp->lock);	/* Stop the chip's Tx and Rx DMA processes. */	RTL_W8 (ChipCmd, (RTL_R8 (ChipCmd) & ChipCmdClear));	/* Disable interrupts by clearing the interrupt mask. */	RTL_W16 (IntrMask, 0x0000);	/* Update the error counts. */	tp->stats.rx_missed_errors += RTL_R32 (RxMissed);	RTL_W32 (RxMissed, 0);	spin_unlock_irq (&tp->lock);	synchronize_irq ();	free_irq (dev->irq, dev);	rtl8139_tx_clear (tp);	pci_free_consistent(tp->pci_dev, RX_BUF_TOT_LEN,			    tp->rx_ring, tp->rx_ring_dma);	pci_free_consistent(tp->pci_dev, TX_BUF_TOT_LEN,			    tp->tx_bufs, tp->tx_bufs_dma);	tp->rx_ring = NULL;	tp->tx_bufs = NULL;	/* Green! Put the chip in low-power mode. */	RTL_W8 (Cfg9346, Cfg9346_Unlock);	RTL_W8 (Config1, 0x03);	RTL_W8 (HltClk, 'H');	/* 'R' would leave the clock running. */	DPRINTK ("EXIT\n");	return 0;}static int mii_ioctl (struct net_device *dev, struct ifreq *rq, int cmd){	struct rtl8139_private *tp = dev->priv;	u16 *data = (u16 *) & rq->ifr_data;	int rc = 0;	DPRINTK ("ENTER\n");	switch (cmd) {	case SIOCDEVPRIVATE:	/* Get the address of the PHY in use. */		data[0] = tp->phys[0] & 0x3f;		/* Fall Through */	case SIOCDEVPRIVATE + 1:	/* Read the specified MII register. */		data[3] = mdio_read (dev, data[0], data[1] & 0x1f);		break;	case SIOCDEVPRIVATE + 2:	/* Write the specified MII register */		if (!capable (CAP_NET_ADMIN)) {			rc = -EPERM;			break;		}		mdio_write (dev, data[0], data[1] & 0x1f, data[2]);		break;	default:		rc = -EOPNOTSUPP;		break;	}	DPRINTK ("EXIT, returning %d\n", rc);	return rc;}static struct net_device_stats *rtl8139_get_stats (struct net_device *dev){	struct rtl8139_private *tp = dev->priv;	void *ioaddr = tp->mmio_addr;	DPRINTK ("ENTER\n");	if (netif_running(dev)) {		tp->stats.rx_missed_errors += RTL_R32 (RxMissed);		RTL_W32 (RxMissed, 0);	}	DPRINTK ("EXIT\n");	return &tp->stats;}/* Set or clear the multicast filter for this adaptor.   This routine is not state sensitive and need not be SMP locked. */static unsigned const ethernet_polynomial = 0x04c11db7U;static inline u32 ether_crc (int length, unsigned char *data){	int crc = -1;	DPRINTK ("ENTER\n");	while (--length >= 0) {		unsigned char current_octet = *data++;		int bit;		for (bit = 0; bit < 8; bit++, current_octet >>= 1)			crc = (crc << 1) ^			    ((crc < 0) ^ (current_octet & 1) ?			     ethernet_polynomial : 0);	}	DPRINTK ("EXIT\n");	return crc;}static void rtl8139_set_rx_mode (struct net_device *dev){	struct rtl8139_private *tp = dev->priv;	void *ioaddr = tp->mmio_addr;	u32 mc_filter[2];	/* Multicast hash filter */	int i, rx_mode;	u32 tmp;	DPRINTK ("ENTER\n");	DPRINTK ("%s:   rtl8139_set_rx_mode(%4.4x) done -- Rx config %8.8lx.\n",			dev->name, dev->flags, RTL_R32 (RxConfig));	/* Note: do not reorder, GCC is clever about common statements. */	if (dev->flags & IFF_PROMISC) {		/* Unconditionally log net taps. */		printk (KERN_NOTICE "%s: Promiscuous mode enabled.\n",			dev->name);		rx_mode =		    AcceptBroadcast | AcceptMulticast | AcceptMyPhys |		    AcceptAllPhys;		mc_filter[1] = mc_filter[0] = 0xffffffff;	} else if ((dev->mc_count > multicast_filter_limit)		   || (dev->flags & IFF_ALLMULTI)) {		/* Too many to filter perfectly -- accept all multicasts. */		rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys;		mc_filter[1] = mc_filter[0] = 0xffffffff;	} else {		struct dev_mc_list *mclist;		rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys;		mc_filter[1] = mc_filter[0] = 0;		for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;		     i++, mclist = mclist->next)			set_bit (ether_crc (ETH_ALEN, mclist->dmi_addr) >> 26,				 mc_filter);	}	/* if called from irq handler, lock already acquired */	if (!in_irq ())		spin_lock_irq (&tp->lock);	/* We can safely update without stopping the chip. */	tmp = rtl8139_rx_config | rx_mode |		(RTL_R32 (RxConfig) & rtl_chip_info[tp->chipset].RxConfigMask);	RTL_W32_F (RxConfig, tmp);	RTL_W32_F (MAR0 + 0, mc_filter[0]);	RTL_W32_F (MAR0 + 4, mc_filter[1]);	if (!in_irq ())		spin_unlock_irq (&tp->lock);	DPRINTK ("EXIT\n");}static void rtl8139_suspend (struct pci_dev *pdev){	struct net_device *dev = pdev->driver_data;	struct rtl8139_private *tp = dev->priv;	void *ioaddr = tp->mmio_addr;	unsigned long flags;	netif_device_detach (dev);	spin_lock_irqsave (&tp->lock, flags);	/* Disable interrupts, stop Tx and Rx. */	RTL_W16 (IntrMask, 0x0000);	RTL_W8 (ChipCmd, (RTL_R8 (ChipCmd) & ChipCmdClear));	/* Update the error counts. */	tp->stats.rx_missed_errors += RTL_R32 (RxMissed);	RTL_W32 (RxMissed, 0);	spin_unlock_irqrestore (&tp->lock, flags);}static void rtl8139_resume (struct pci_dev *pdev){	struct net_device *dev = pdev->driver_data;	netif_device_attach (dev);	rtl8139_hw_start (dev);}static struct pci_driver rtl8139_pci_driver = {	name:		MODNAME,	id_table:	rtl8139_pci_tbl,	probe:		rtl8139_init_one,	remove:		rtl8139_remove_one,	suspend:	rtl8139_suspend,	resume:		rtl8139_resume,};static int __init rtl8139_init_module (void){	return pci_module_init (&rtl8139_pci_driver);}static void __exit rtl8139_cleanup_module (void){	pci_unregister_driver (&rtl8139_pci_driver);}module_init(rtl8139_init_module);module_exit(rtl8139_cleanup_module);

⌨️ 快捷键说明

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