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

📄 enet.c

📁 Linux内核源代码 为压缩文件 是<<Linux内核>>一书中的源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * Ethernet driver for Motorola MPC8260. * Copyright (c) 1999 Dan Malek (dmalek@jlc.net) * Copyright (c) 2000 MontaVista Software Inc. (source@mvista.com) *	2.3.99 Updates * * I copied this from the 8xx CPM Ethernet driver, so follow the * credits back through that. * * This version of the driver is somewhat selectable for the different * processor/board combinations.  It works for the boards I know about * now, and should be easily modified to include others.  Some of the * configuration information is contained in "commproc.h" and the * remainder is here. * * Buffer descriptors are kept in the CPM dual port RAM, and the frame * buffers are in the host memory. * * Right now, I am very watseful with the buffers.  I allocate memory * pages and then divide them into 2K frame buffers.  This way I know I * have buffers large enough to hold one frame within one buffer descriptor. * Once I get this working, I will use 64 or 128 byte CPM buffers, which * will be much more memory efficient and will easily handle lots of * small packets. * */#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/init.h>#include <linux/delay.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/skbuff.h>#include <linux/spinlock.h>#include <asm/immap_8260.h>#include <asm/pgtable.h>#include <asm/mpc8260.h>#include <asm/bitops.h>#include <asm/uaccess.h>#include <asm/cpm_8260.h>#include <asm/irq.h>/* *				Theory of Operation * * The MPC8260 CPM performs the Ethernet processing on an SCC.  It can use * an aribtrary number of buffers on byte boundaries, but must have at * least two receive buffers to prevent constant overrun conditions. * * The buffer descriptors are allocated from the CPM dual port memory * with the data buffers allocated from host memory, just like all other * serial communication protocols.  The host memory buffers are allocated * from the free page pool, and then divided into smaller receive and * transmit buffers.  The size of the buffers should be a power of two, * since that nicely divides the page.  This creates a ring buffer * structure similar to the LANCE and other controllers. * * Like the LANCE driver: * The driver runs as two independent, single-threaded flows of control.  One * is the send-packet routine, which enforces single-threaded use by the * cep->tx_busy flag.  The other thread is the interrupt handler, which is * single threaded by the hardware and other software. *//* The transmitter timeout */#define TX_TIMEOUT	(2*HZ)/* The number of Tx and Rx buffers.  These are allocated from the page * pool.  The code may assume these are power of two, so it is best * to keep them that size. * We don't need to allocate pages for the transmitter.  We just use * the skbuffer directly. */#define CPM_ENET_RX_PAGES	4#define CPM_ENET_RX_FRSIZE	2048#define CPM_ENET_RX_FRPPG	(PAGE_SIZE / CPM_ENET_RX_FRSIZE)#define RX_RING_SIZE		(CPM_ENET_RX_FRPPG * CPM_ENET_RX_PAGES)#define TX_RING_SIZE		8	/* Must be power of two */#define TX_RING_MOD_MASK	7	/*   for this to work *//* The CPM stores dest/src/type, data, and checksum for receive packets. */#define PKT_MAXBUF_SIZE		1518#define PKT_MINBUF_SIZE		64#define PKT_MAXBLR_SIZE		1520/* The CPM buffer descriptors track the ring buffers.  The rx_bd_base and * tx_bd_base always point to the base of the buffer descriptors.  The * cur_rx and cur_tx point to the currently available buffer. * The dirty_tx tracks the current buffer that is being sent by the * controller.  The cur_tx and dirty_tx are equal under both completely * empty and completely full conditions.  The empty/ready indicator in * the buffer descriptor determines the actual condition. */struct scc_enet_private {	/* The saved address of a sent-in-place packet/buffer, for skfree(). */	struct	sk_buff* tx_skbuff[TX_RING_SIZE];	ushort	skb_cur;	ushort	skb_dirty;	/* CPM dual port RAM relative addresses.	*/	cbd_t	*rx_bd_base;		/* Address of Rx and Tx buffers. */	cbd_t	*tx_bd_base;	cbd_t	*cur_rx, *cur_tx;		/* The next free ring entry */	cbd_t	*dirty_tx;	/* The ring entries to be free()ed. */	scc_t	*sccp;	struct	net_device_stats stats;	uint	tx_full;	spinlock_t lock;};static int scc_enet_open(struct net_device *dev);static int scc_enet_start_xmit(struct sk_buff *skb, struct net_device *dev);static int scc_enet_rx(struct net_device *dev);static void scc_enet_interrupt(int irq, void * dev_id, struct pt_regs * regs);static int scc_enet_close(struct net_device *dev);static struct net_device_stats *scc_enet_get_stats(struct net_device *dev);static void set_multicast_list(struct net_device *dev);/* These will be configurable for the SCC choice.*/#define CPM_ENET_BLOCK	CPM_CR_SCC1_SBLOCK#define CPM_ENET_PAGE	CPM_CR_SCC1_PAGE#define PROFF_ENET	PROFF_SCC1#define SCC_ENET	0#define SIU_INT_ENET	SIU_INT_SCC1/* These are both board and SCC dependent....*/#define PD_ENET_RXD	((uint)0x00000001)#define PD_ENET_TXD	((uint)0x00000002)#define PD_ENET_TENA	((uint)0x00000004)#define PC_ENET_RENA	((uint)0x00020000)#define PC_ENET_CLSN	((uint)0x00000004)#define PC_ENET_TXCLK	((uint)0x00000800)#define PC_ENET_RXCLK	((uint)0x00000400)#define CMX_CLK_ROUTE	((uint)0x25000000)#define CMX_CLK_MASK	((uint)0xff000000)/* Specific to a board.*/#define PC_EST8260_ENET_LOOPBACK	((uint)0x80000000)#define PC_EST8260_ENET_SQE		((uint)0x40000000)#define PC_EST8260_ENET_NOTFD		((uint)0x20000000)static intscc_enet_open(struct net_device *dev){	/* I should reset the ring buffers here, but I don't yet know	 * a simple way to do that.	 */	netif_start_queue(dev);	return 0;					/* Always succeed */}static intscc_enet_start_xmit(struct sk_buff *skb, struct net_device *dev){	struct scc_enet_private *cep = (struct scc_enet_private *)dev->priv;	volatile cbd_t	*bdp;	/* Fill in a Tx ring entry */	bdp = cep->cur_tx;#ifndef final_version	if (bdp->cbd_sc & BD_ENET_TX_READY) {		/* Ooops.  All transmit buffers are full.  Bail out.		 * This should not happen, since cep->tx_full should be set.		 */		printk("%s: tx queue full!.\n", dev->name);		return 1;	}#endif	/* Clear all of the status flags.	 */	bdp->cbd_sc &= ~BD_ENET_TX_STATS;	/* If the frame is short, tell CPM to pad it.	*/	if (skb->len <= ETH_ZLEN)		bdp->cbd_sc |= BD_ENET_TX_PAD;	else		bdp->cbd_sc &= ~BD_ENET_TX_PAD;	/* Set buffer length and buffer pointer.	*/	bdp->cbd_datlen = skb->len;	bdp->cbd_bufaddr = __pa(skb->data);	/* Save skb pointer.	*/	cep->tx_skbuff[cep->skb_cur] = skb;	cep->stats.tx_bytes += skb->len;	cep->skb_cur = (cep->skb_cur+1) & TX_RING_MOD_MASK;		spin_lock_irq(&cep->lock);	/* Send it on its way.  Tell CPM its ready, interrupt when done,	 * its the last BD of the frame, and to put the CRC on the end.	 */	bdp->cbd_sc |= (BD_ENET_TX_READY | BD_ENET_TX_INTR | BD_ENET_TX_LAST | BD_ENET_TX_TC);	dev->trans_start = jiffies;	/* If this was the last BD in the ring, start at the beginning again.	*/	if (bdp->cbd_sc & BD_ENET_TX_WRAP)		bdp = cep->tx_bd_base;	else		bdp++;	if (bdp->cbd_sc & BD_ENET_TX_READY) {		netif_stop_queue(dev);		cep->tx_full = 1;	}	cep->cur_tx = (cbd_t *)bdp;	spin_unlock_irq(&cep->lock);	return 0;}static voidscc_enet_timeout(struct net_device *dev){	struct scc_enet_private *cep = (struct scc_enet_private *)dev->priv;	printk("%s: transmit timed out.\n", dev->name);	cep->stats.tx_errors++;#ifndef final_version	{		int	i;		cbd_t	*bdp;		printk(" Ring data dump: cur_tx %p%s cur_rx %p.\n",		       cep->cur_tx, cep->tx_full ? " (full)" : "",		       cep->cur_rx);		bdp = cep->tx_bd_base;		printk(" Tx @base %p :\n", bdp);		for (i = 0 ; i < TX_RING_SIZE; i++, bdp++)			printk("%04x %04x %08x\n",			       bdp->cbd_sc,			       bdp->cbd_datlen,			       bdp->cbd_bufaddr);		bdp = cep->rx_bd_base;		printk(" Rx @base %p :\n", bdp);		for (i = 0 ; i < RX_RING_SIZE; i++, bdp++)			printk("%04x %04x %08x\n",			       bdp->cbd_sc,			       bdp->cbd_datlen,			       bdp->cbd_bufaddr);	}#endif	if (!cep->tx_full)		netif_wake_queue(dev);}/* The interrupt handler. * This is called from the CPM handler, not the MPC core interrupt. */static voidscc_enet_interrupt(int irq, void * dev_id, struct pt_regs * regs){	struct	net_device *dev = dev_id;	volatile struct	scc_enet_private *cep;	volatile cbd_t	*bdp;	ushort	int_events;	int	must_restart;	cep = (struct scc_enet_private *)dev->priv;	/* Get the interrupt events that caused us to be here.	*/	int_events = cep->sccp->scc_scce;	cep->sccp->scc_scce = int_events;	must_restart = 0;	/* Handle receive event in its own function.	*/	if (int_events & SCCE_ENET_RXF)		scc_enet_rx(dev_id);	/* Check for a transmit error.  The manual is a little unclear	 * about this, so the debug code until I get it figured out.  It	 * appears that if TXE is set, then TXB is not set.  However,	 * if carrier sense is lost during frame transmission, the TXE	 * bit is set, "and continues the buffer transmission normally."	 * I don't know if "normally" implies TXB is set when the buffer	 * descriptor is closed.....trial and error :-).	 */	/* Transmit OK, or non-fatal error.  Update the buffer descriptors.	*/	if (int_events & (SCCE_ENET_TXE | SCCE_ENET_TXB)) {	    spin_lock(&cep->lock);	    bdp = cep->dirty_tx;	    while ((bdp->cbd_sc&BD_ENET_TX_READY)==0) {		if ((bdp==cep->cur_tx) && (cep->tx_full == 0))		    break;		if (bdp->cbd_sc & BD_ENET_TX_HB)	/* No heartbeat */			cep->stats.tx_heartbeat_errors++;		if (bdp->cbd_sc & BD_ENET_TX_LC)	/* Late collision */			cep->stats.tx_window_errors++;		if (bdp->cbd_sc & BD_ENET_TX_RL)	/* Retrans limit */			cep->stats.tx_aborted_errors++;		if (bdp->cbd_sc & BD_ENET_TX_UN)	/* Underrun */			cep->stats.tx_fifo_errors++;		if (bdp->cbd_sc & BD_ENET_TX_CSL)	/* Carrier lost */			cep->stats.tx_carrier_errors++;		/* No heartbeat or Lost carrier are not really bad errors.		 * The others require a restart transmit command.		 */		if (bdp->cbd_sc &		    (BD_ENET_TX_LC | BD_ENET_TX_RL | BD_ENET_TX_UN)) {			must_restart = 1;			cep->stats.tx_errors++;		}		cep->stats.tx_packets++;		/* Deferred means some collisions occurred during transmit,		 * but we eventually sent the packet OK.		 */		if (bdp->cbd_sc & BD_ENET_TX_DEF)			cep->stats.collisions++;		/* Free the sk buffer associated with this last transmit.		*/		dev_kfree_skb_irq(cep->tx_skbuff[cep->skb_dirty]);		cep->skb_dirty = (cep->skb_dirty + 1) & TX_RING_MOD_MASK;		/* Update pointer to next buffer descriptor to be transmitted.		*/		if (bdp->cbd_sc & BD_ENET_TX_WRAP)			bdp = cep->tx_bd_base;		else			bdp++;		/* I don't know if we can be held off from processing these		 * interrupts for more than one frame time.  I really hope		 * not.  In such a case, we would now want to check the		 * currently available BD (cur_tx) and determine if any		 * buffers between the dirty_tx and cur_tx have also been		 * sent.  We would want to process anything in between that		 * does not have BD_ENET_TX_READY set.		 */		/* Since we have freed up a buffer, the ring is no longer		 * full.		 */		if (cep->tx_full) {			cep->tx_full = 0;			if (netif_queue_stopped(dev)) {				netif_wake_queue(dev);			}		}		cep->dirty_tx = (cbd_t *)bdp;	    }	    if (must_restart) {		volatile cpm8260_t *cp;		/* Some transmit errors cause the transmitter to shut		 * down.  We now issue a restart transmit.  Since the		 * errors close the BD and update the pointers, the restart		 * _should_ pick up without having to reset any of our		 * pointers either.		 */		cp = cpmp;		cp->cp_cpcr =		    mk_cr_cmd(CPM_ENET_PAGE, CPM_ENET_BLOCK, 0,		    			CPM_CR_RESTART_TX) | CPM_CR_FLG;		while (cp->cp_cpcr & CPM_CR_FLG);	    }	    spin_unlock(&cep->lock);	}	/* Check for receive busy, i.e. packets coming but no place to	 * put them.  This "can't happen" because the receive interrupt	 * is tossing previous frames.	 */	if (int_events & SCCE_ENET_BSY) {		cep->stats.rx_dropped++;		printk("SCC ENET: BSY can't happen.\n");	}	return;}/* During a receive, the cur_rx points to the current incoming buffer. * When we update through the ring, if the next incoming buffer has * not been given to the system, we just set the empty indicator, * effectively tossing the packet. */static intscc_enet_rx(struct net_device *dev){	struct	scc_enet_private *cep;	volatile cbd_t	*bdp;	struct	sk_buff *skb;	ushort	pkt_len;	cep = (struct scc_enet_private *)dev->priv;	/* First, grab all of the stats for the incoming packet.	 * These get messed up if we get called due to a busy condition.	 */	bdp = cep->cur_rx;for (;;) {	if (bdp->cbd_sc & BD_ENET_RX_EMPTY)

⌨️ 快捷键说明

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