ipg.c

来自「linux 内核源代码」· C语言 代码 · 共 2,341 行 · 第 1/5 页

C
2,341
字号
		rxfd = sp->rxd + entry;		if (((rxfd->rfs & __RFS_MASK) != __RFS_MASK) || !skb)			break;		/* Get received frame length. */		framelen = le64_to_cpu(rxfd->rfs) & IPG_RFS_RXFRAMELEN;		/* Check for jumbo frame arrival with too small		 * RXFRAG_SIZE.		 */		if (framelen > IPG_RXFRAG_SIZE) {			IPG_DEBUG_MSG			    ("RFS FrameLen > allocated fragment size.\n");			framelen = IPG_RXFRAG_SIZE;		}		if ((IPG_DROP_ON_RX_ETH_ERRORS && (le64_to_cpu(rxfd->rfs) &		       (IPG_RFS_RXFIFOOVERRUN | IPG_RFS_RXRUNTFRAME |			IPG_RFS_RXALIGNMENTERROR | IPG_RFS_RXFCSERROR |			IPG_RFS_RXOVERSIZEDFRAME | IPG_RFS_RXLENGTHERROR)))) {			IPG_DEBUG_MSG("Rx error, RFS = %16.16lx\n",				      (unsigned long int) rxfd->rfs);			/* Increment general receive error statistic. */			sp->stats.rx_errors++;			/* Increment detailed receive error statistics. */			if (le64_to_cpu(rxfd->rfs) & IPG_RFS_RXFIFOOVERRUN) {				IPG_DEBUG_MSG("RX FIFO overrun occured.\n");				sp->stats.rx_fifo_errors++;			}			if (le64_to_cpu(rxfd->rfs) & IPG_RFS_RXRUNTFRAME) {				IPG_DEBUG_MSG("RX runt occured.\n");				sp->stats.rx_length_errors++;			}			if (le64_to_cpu(rxfd->rfs) & IPG_RFS_RXOVERSIZEDFRAME) ;			/* Do nothing, error count handled by a IPG			 * statistic register.			 */			if (le64_to_cpu(rxfd->rfs) & IPG_RFS_RXALIGNMENTERROR) {				IPG_DEBUG_MSG("RX alignment error occured.\n");				sp->stats.rx_frame_errors++;			}			if (le64_to_cpu(rxfd->rfs) & IPG_RFS_RXFCSERROR) ;			/* Do nothing, error count handled by a IPG			 * statistic register.			 */			/* Free the memory associated with the RX			 * buffer since it is erroneous and we will			 * not pass it to higher layer processes.			 */			if (skb) {				__le64 info = rxfd->frag_info;				pci_unmap_single(sp->pdev,					le64_to_cpu(info) & ~IPG_RFI_FRAGLEN,					sp->rx_buf_sz, PCI_DMA_FROMDEVICE);				IPG_DEV_KFREE_SKB(skb);			}		} else {			/* Adjust the new buffer length to accomodate the size			 * of the received frame.			 */			skb_put(skb, framelen);			/* Set the buffer's protocol field to Ethernet. */			skb->protocol = eth_type_trans(skb, dev);			/* If the frame contains an IP/TCP/UDP frame,			 * determine if upper layer must check IP/TCP/UDP			 * checksums.			 *			 * NOTE: DO NOT RELY ON THE TCP/UDP CHECKSUM			 *       VERIFICATION FOR SILICON REVISIONS B3			 *       AND EARLIER!			 *			 if ((le64_to_cpu(rxfd->rfs &			     (IPG_RFS_TCPDETECTED | IPG_RFS_UDPDETECTED |			      IPG_RFS_IPDETECTED))) &&			    !(le64_to_cpu(rxfd->rfs &			      (IPG_RFS_TCPERROR | IPG_RFS_UDPERROR |			       IPG_RFS_IPERROR)))) {				 * Indicate IP checksums were performed				 * by the IPG.				 *				skb->ip_summed = CHECKSUM_UNNECESSARY;			 } else			 */			 {				/* The IPG encountered an error with (or				 * there were no) IP/TCP/UDP checksums.				 * This may or may not indicate an invalid				 * IP/TCP/UDP frame was received. Let the				 * upper layer decide.				 */				skb->ip_summed = CHECKSUM_NONE;			}			/* Hand off frame for higher layer processing.			 * The function netif_rx() releases the sk_buff			 * when processing completes.			 */			netif_rx(skb);			/* Record frame receive time (jiffies = Linux			 * kernel current time stamp).			 */			dev->last_rx = jiffies;		}		/* Assure RX buffer is not reused by IPG. */		sp->RxBuff[entry] = NULL;	}	/*	 * If there are more RFDs to proces and the allocated amount of RFD	 * processing time has expired, assert Interrupt Requested to make	 * sure we come back to process the remaining RFDs.	 */	if (i == IPG_MAXRFDPROCESS_COUNT)		ipg_w32(ipg_r32(ASIC_CTRL) | IPG_AC_INT_REQUEST, ASIC_CTRL);#ifdef IPG_DEBUG	/* Check if the RFD list contained no receive frame data. */	if (!i)		sp->EmptyRFDListCount++;#endif	while ((le64_to_cpu(rxfd->rfs) & IPG_RFS_RFDDONE) &&	       !((le64_to_cpu(rxfd->rfs) & IPG_RFS_FRAMESTART) &&		 (le64_to_cpu(rxfd->rfs) & IPG_RFS_FRAMEEND))) {		unsigned int entry = curr++ % IPG_RFDLIST_LENGTH;		rxfd = sp->rxd + entry;		IPG_DEBUG_MSG("Frame requires multiple RFDs.\n");		/* An unexpected event, additional code needed to handle		 * properly. So for the time being, just disregard the		 * frame.		 */		/* Free the memory associated with the RX		 * buffer since it is erroneous and we will		 * not pass it to higher layer processes.		 */		if (sp->RxBuff[entry]) {			pci_unmap_single(sp->pdev,				le64_to_cpu(rxfd->frag_info) & ~IPG_RFI_FRAGLEN,				sp->rx_buf_sz, PCI_DMA_FROMDEVICE);			IPG_DEV_KFREE_SKB(sp->RxBuff[entry]);		}		/* Assure RX buffer is not reused by IPG. */		sp->RxBuff[entry] = NULL;	}	sp->rx_current = curr;	/* Check to see if there are a minimum number of used	 * RFDs before restoring any (should improve performance.)	 */	if ((curr - sp->rx_dirty) >= IPG_MINUSEDRFDSTOFREE)		ipg_nic_rxrestore(dev);	return 0;}#endifstatic void ipg_reset_after_host_error(struct work_struct *work){	struct ipg_nic_private *sp =		container_of(work, struct ipg_nic_private, task.work);	struct net_device *dev = sp->dev;	IPG_DDEBUG_MSG("DMACtrl = %8.8x\n", ioread32(sp->ioaddr + IPG_DMACTRL));	/*	 * Acknowledge HostError interrupt by resetting	 * IPG DMA and HOST.	 */	ipg_reset(dev, IPG_AC_GLOBAL_RESET | IPG_AC_HOST | IPG_AC_DMA);	init_rfdlist(dev);	init_tfdlist(dev);	if (ipg_io_config(dev) < 0) {		printk(KERN_INFO "%s: Cannot recover from PCI error.\n",		       dev->name);		schedule_delayed_work(&sp->task, HZ);	}}static irqreturn_t ipg_interrupt_handler(int irq, void *dev_inst){	struct net_device *dev = dev_inst;	struct ipg_nic_private *sp = netdev_priv(dev);	void __iomem *ioaddr = sp->ioaddr;	unsigned int handled = 0;	u16 status;	IPG_DEBUG_MSG("_interrupt_handler\n");#ifdef JUMBO_FRAME	ipg_nic_rxrestore(dev);#endif	spin_lock(&sp->lock);	/* Get interrupt source information, and acknowledge	 * some (i.e. TxDMAComplete, RxDMAComplete, RxEarly,	 * IntRequested, MacControlFrame, LinkEvent) interrupts	 * if issued. Also, all IPG interrupts are disabled by	 * reading IntStatusAck.	 */	status = ipg_r16(INT_STATUS_ACK);	IPG_DEBUG_MSG("IntStatusAck = %4.4x\n", status);	/* Shared IRQ of remove event. */	if (!(status & IPG_IS_RSVD_MASK))		goto out_enable;	handled = 1;	if (unlikely(!netif_running(dev)))		goto out_unlock;	/* If RFDListEnd interrupt, restore all used RFDs. */	if (status & IPG_IS_RFD_LIST_END) {		IPG_DEBUG_MSG("RFDListEnd Interrupt.\n");		/* The RFD list end indicates an RFD was encountered		 * with a 0 NextPtr, or with an RFDDone bit set to 1		 * (indicating the RFD is not read for use by the		 * IPG.) Try to restore all RFDs.		 */		ipg_nic_rxrestore(dev);#ifdef IPG_DEBUG		/* Increment the RFDlistendCount counter. */		sp->RFDlistendCount++;#endif	}	/* If RFDListEnd, RxDMAPriority, RxDMAComplete, or	 * IntRequested interrupt, process received frames. */	if ((status & IPG_IS_RX_DMA_PRIORITY) ||	    (status & IPG_IS_RFD_LIST_END) ||	    (status & IPG_IS_RX_DMA_COMPLETE) ||	    (status & IPG_IS_INT_REQUESTED)) {#ifdef IPG_DEBUG		/* Increment the RFD list checked counter if interrupted		 * only to check the RFD list. */		if (status & (~(IPG_IS_RX_DMA_PRIORITY | IPG_IS_RFD_LIST_END |				IPG_IS_RX_DMA_COMPLETE | IPG_IS_INT_REQUESTED) &			       (IPG_IS_HOST_ERROR | IPG_IS_TX_DMA_COMPLETE |				IPG_IS_LINK_EVENT | IPG_IS_TX_COMPLETE |				IPG_IS_UPDATE_STATS)))			sp->RFDListCheckedCount++;#endif		ipg_nic_rx(dev);	}	/* If TxDMAComplete interrupt, free used TFDs. */	if (status & IPG_IS_TX_DMA_COMPLETE)		ipg_nic_txfree(dev);	/* TxComplete interrupts indicate one of numerous actions.	 * Determine what action to take based on TXSTATUS register.	 */	if (status & IPG_IS_TX_COMPLETE)		ipg_nic_txcleanup(dev);	/* If UpdateStats interrupt, update Linux Ethernet statistics */	if (status & IPG_IS_UPDATE_STATS)		ipg_nic_get_stats(dev);	/* If HostError interrupt, reset IPG. */	if (status & IPG_IS_HOST_ERROR) {		IPG_DDEBUG_MSG("HostError Interrupt\n");		schedule_delayed_work(&sp->task, 0);	}	/* If LinkEvent interrupt, resolve autonegotiation. */	if (status & IPG_IS_LINK_EVENT) {		if (ipg_config_autoneg(dev) < 0)			printk(KERN_INFO "%s: Auto-negotiation error.\n",			       dev->name);	}	/* If MACCtrlFrame interrupt, do nothing. */	if (status & IPG_IS_MAC_CTRL_FRAME)		IPG_DEBUG_MSG("MACCtrlFrame interrupt.\n");	/* If RxComplete interrupt, do nothing. */	if (status & IPG_IS_RX_COMPLETE)		IPG_DEBUG_MSG("RxComplete interrupt.\n");	/* If RxEarly interrupt, do nothing. */	if (status & IPG_IS_RX_EARLY)		IPG_DEBUG_MSG("RxEarly interrupt.\n");out_enable:	/* Re-enable IPG interrupts. */	ipg_w16(IPG_IE_TX_DMA_COMPLETE | IPG_IE_RX_DMA_COMPLETE |		IPG_IE_HOST_ERROR | IPG_IE_INT_REQUESTED | IPG_IE_TX_COMPLETE |		IPG_IE_LINK_EVENT | IPG_IE_UPDATE_STATS, INT_ENABLE);out_unlock:	spin_unlock(&sp->lock);	return IRQ_RETVAL(handled);}static void ipg_rx_clear(struct ipg_nic_private *sp){	unsigned int i;	for (i = 0; i < IPG_RFDLIST_LENGTH; i++) {		if (sp->RxBuff[i]) {			struct ipg_rx *rxfd = sp->rxd + i;			IPG_DEV_KFREE_SKB(sp->RxBuff[i]);			sp->RxBuff[i] = NULL;			pci_unmap_single(sp->pdev,				le64_to_cpu(rxfd->frag_info) & ~IPG_RFI_FRAGLEN,				sp->rx_buf_sz, PCI_DMA_FROMDEVICE);		}	}}static void ipg_tx_clear(struct ipg_nic_private *sp){	unsigned int i;	for (i = 0; i < IPG_TFDLIST_LENGTH; i++) {		if (sp->TxBuff[i]) {			struct ipg_tx *txfd = sp->txd + i;			pci_unmap_single(sp->pdev,				le64_to_cpu(txfd->frag_info) & ~IPG_TFI_FRAGLEN,				sp->TxBuff[i]->len, PCI_DMA_TODEVICE);			IPG_DEV_KFREE_SKB(sp->TxBuff[i]);			sp->TxBuff[i] = NULL;		}	}}static int ipg_nic_open(struct net_device *dev){	struct ipg_nic_private *sp = netdev_priv(dev);	void __iomem *ioaddr = sp->ioaddr;	struct pci_dev *pdev = sp->pdev;	int rc;	IPG_DEBUG_MSG("_nic_open\n");	sp->rx_buf_sz = IPG_RXSUPPORT_SIZE;	/* Check for interrupt line conflicts, and request interrupt	 * line for IPG.	 *	 * IMPORTANT: Disable IPG interrupts prior to registering	 *            IRQ.	 */	ipg_w16(0x0000, INT_ENABLE);	/* Register the interrupt line to be used by the IPG within	 * the Linux system.	 */	rc = request_irq(pdev->irq, &ipg_interrupt_handler, IRQF_SHARED,			 dev->name, dev);	if (rc < 0) {		printk(KERN_INFO "%s: Error when requesting interrupt.\n",		       dev->name);		goto out;	}	dev->irq = pdev->irq;	rc = -ENOMEM;	sp->rxd = dma_alloc_coherent(&pdev->dev, IPG_RX_RING_BYTES,				     &sp->rxd_map, GFP_KERNEL);	if (!sp->rxd)		goto err_free_irq_0;	sp->txd = dma_alloc_coherent(&pdev->dev, IPG_TX_RING_BYTES,				     &sp->txd_map, GFP_KERNEL);	if (!sp->txd)		goto err_free_rx_1;	rc = init_rfdlist(dev);	if (rc < 0) {		printk(KERN_INFO "%s: Error during configuration.\n",		       dev->name);		goto err_free_tx_2;	}	init_tfdlist(dev);	rc = ipg_io_config(dev);	if (rc < 0) {		printk(KERN_INFO "%s: Error during configuration.\n",		       dev->name);		goto err_release_tfdlist_3;	}	/* Resolve autonegotiation. */	if (ipg_config_autoneg(dev) < 0)		printk(KERN_INFO "%s: Auto-negotiation error.\n", dev->name);#ifdef JUMBO_FRAME	/* initialize JUMBO Frame control variable */	sp->Jumbo.FoundStart = 0;	sp->Jumbo.CurrentSize = 0;	sp->Jumbo.skb = 0;	dev->mtu = IPG_TXFRAG_SIZE;#endif	/* Enable transmit and receive operation of the IPG. */	ipg_w32((ipg_r32(MAC_CTRL) | IPG_MC_RX_ENABLE | IPG_MC_TX_ENABLE) &		 IPG_MC_RSVD_MASK, MAC_CTRL);	netif_start_queue(dev);out:	return rc;err_release_tfdlist_3:	ipg_tx_clear(sp);	ipg_rx_clear(sp);err_free_tx_2:	dma_free_coherent(&pdev->dev, IPG_TX_RING_BYTES, sp->txd, sp->txd_map);err_free_rx_1:	dma_free_coherent(&pdev->dev, IPG_RX_RING_BYTES, sp->rxd, sp->rxd_map);err_free_irq_0:	free_irq(pdev->irq, dev);	goto out;}static int ipg_nic_stop(struct net_device *dev){	struct ipg_nic_private *sp = netdev_priv(dev);	void __iomem *ioaddr = sp->ioaddr;	struct pci_dev *pdev = sp->pdev;	IPG_DEBUG_MSG("_nic_stop\n");	netif_stop_queue(dev);	IPG_DDEBUG_MSG("RFDlistendCount = %i\n", sp->RFDlistendCount);	IPG_DDEBUG_MSG("RFDListCheckedCount = %i\n", sp->rxdCheckedCount);	IPG_DDEBUG_MSG("EmptyRFDListCount = %i\n", sp->EmptyRFDListCount);	IPG_DUMPTFDLIST(dev);	do {

⌨️ 快捷键说明

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