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

📄 enet.c

📁 microwindows移植到S3C44B0的源码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * Ethernet driver for Motorola MPC8xx. * Copyright (c) 2000 Michael Leslie <mleslie@lineo.com> * Copyright (c) 1997 Dan Malek (dmalek@jlc.net) * * I copied the basic skeleton from the lance driver, because I did not * know how to write the Linux driver, but I did know how the LANCE worked. * * 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/config.h>#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/irq.h>/* for 2.0 compatibility: */#include <linux/compatmac.h>#define net_device device#include <asm/m68360.h>/* #include <asm/8xx_immap.h> *//* #include <asm/pgtable.h> *//* #include <asm/mpc8xx.h> */#include <asm/bitops.h>/* #include <asm/uaccess.h> */#include "commproc.h"/* *				Theory of Operation * * The MPC8xx CPM performs the Ethernet processing on SCC1.  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 send packet thread has partial control over the Tx ring and the * 'cep->tx_busy' flag.  It sets the tx_busy flag whenever it's queuing a Tx * packet. If the next queue slot is empty, it clears the tx_busy flag when * finished otherwise it sets the 'lp->tx_full' flag. * * The MBX has a control register external to the MPC8xx that has some * control of the Ethernet interface.  Information is in the manual for * your board. * * The RPX boards have an external control/status register.  Consult the * programming documents for details unique to your board. * * For the TQM8xx(L) modules, there is no control register interface. * All functions are directly controlled using I/O pins.  See commproc.h. *//* The transmitter timeout */#define TX_TIMEOUT	(2*HZ)/* The number of Tx and Rx buffers.  These are allocated statically here. * We don't need to allocate pages for the transmitter.  We just use * the skbuffer directly. */#ifdef CONFIG_ENET_BIG_BUFFERS#define RX_RING_SIZE		64#define TX_RING_SIZE		64	/* Must be power of two */#define TX_RING_MOD_MASK	63	/*   for this to work */#else#define RX_RING_SIZE		8#define TX_RING_SIZE		8	/* Must be power of two */#define TX_RING_MOD_MASK	7	/*   for this to work */#endif#define CPM_ENET_RX_FRSIZE  2048 /* overkill left over from ppc page-based allocation */static char rx_buf_pool[RX_RING_SIZE * CPM_ENET_RX_FRSIZE];/* 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.	*/	QUICC_BD	*rx_bd_base;		/* Address of Rx and Tx buffers. */	QUICC_BD	*tx_bd_base;	QUICC_BD	*cur_rx, *cur_tx;		/* The next free ring entry */	QUICC_BD	*dirty_tx;	/* The ring entries to be free()ed. */	volatile struct scc_regs	*sccp;	/* struct	net_device_stats stats; */	struct	enet_statistics stats;	uint	tx_full;	/* spinlock_t lock; */	volatile unsigned int 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(void *dev_id); */static void scc_enet_interrupt(int vec, void *dev_id, struct pt_regs *fp);static int scc_enet_close(struct net_device *dev);/* static struct net_device_stats *scc_enet_get_stats(struct net_device *dev); */static struct enet_statistics *scc_enet_get_stats(struct net_device *dev);static void set_multicast_list(struct net_device *dev);/* Get this from various configuration locations (depends on board).*//*static	ushort	my_enet_addr[] = { 0x0800, 0x3e26, 0x1559 };*//* Typically, 860(T) boards use SCC1 for Ethernet, and other 8xx boards * use SCC2.  This is easily extended if necessary. */#define CONFIG_SCC1_ENET /* by default */#ifdef CONFIG_SCC1_ENET#define CPM_CR_ENET CPM_CR_CH_SCC1#define PROFF_ENET	PROFF_SCC1#define SCC_ENET	0#define CPMVEC_ENET	CPMVEC_SCC1#endif#ifdef CONFIG_SCC2_ENET#define CPM_CR_ENET	CPM_CR_CH_SCC2#define PROFF_ENET	PROFF_SCC2#define SCC_ENET	1		/* Index, not number! */#define CPMVEC_ENET	CPMVEC_SCC2#endifstatic 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.	 * mleslie: That's no biggie. Worth doing, too.	 */	/* 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 QUICC_BD	*bdp;	/* Fill in a Tx ring entry */	bdp = cep->cur_tx;#ifndef final_version	if (bdp->status & BD_ENET_TX_READY) {		/* Ooops.  All transmit buffers are full.  Bail out.		 * This should not happen, since cep->tx_busy should be set.		 */		printk("%s: tx queue full!.\n", dev->name);		return 1;	}#endif	/* Clear all of the status flags.	 */	bdp->status &= ~BD_ENET_TX_STATS;	/* If the frame is short, tell CPM to pad it.	*/	if (skb->len <= ETH_ZLEN)		bdp->status |= BD_ENET_TX_PAD;	else		bdp->status &= ~BD_ENET_TX_PAD;	/* Set buffer length and buffer pointer.	*/	bdp->length = skb->len;	/* bdp->buf = __pa(skb->data); */	bdp->buf = skb->data;	/* Save skb pointer.	*/	cep->tx_skbuff[cep->skb_cur] = skb;	/* cep->stats.tx_bytes += skb->len; */ /* TODO: It would really be nice... */	cep->skb_cur = (cep->skb_cur+1) & TX_RING_MOD_MASK;		/* Push the data cache so the CPM does not get stale memory	 * data.	 *//* 	flush_dcache_range((unsigned long)(skb->data), *//* 					(unsigned long)(skb->data + skb->len)); */	/* spin_lock_irq(&cep->lock); */ /* TODO: SPINLOCK */	cli();	if (cep->lock > 0) {		printk ("scc_enet_start_xmit() lock == %d\n", cep->lock);	} else {		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->status |= (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->status & BD_ENET_TX_WRAP)		bdp = cep->tx_bd_base;	else		bdp++;	if (bdp->status & BD_ENET_TX_READY) {		/* netif_stop_queue(dev); */		cep->tx_full = 1;	}	cep->cur_tx = (QUICC_BD *)bdp;	/* spin_unlock_irq(&cep->lock); */ /* TODO: SPINLOCK */	cep->lock--;	sti();	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;		QUICC_BD	*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;		for (i = 0 ; i < TX_RING_SIZE; i++, bdp++)			printk("%04x %04x %08x\n",			       bdp->status,			       bdp->length,			       (int)(bdp->buf));		bdp = cep->rx_bd_base;		for (i = 0 ; i < RX_RING_SIZE; i++, bdp++)			printk("%04x %04x %08x\n",			       bdp->status,			       bdp->length,			       (int)(bdp->buf));	}#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 void scc_enet_interrupt(void *dev_id) */static void scc_enet_interrupt(int vec, void *dev_id, struct pt_regs *fp){	struct	net_device *dev = (struct net_device *)dev_id;	volatile struct	scc_enet_private *cep;	volatile QUICC_BD	*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); */ /* TODO: SPINLOCK */		/* cli(); */		if (cep->lock > 0) {			printk ("scc_enet_interrupt() lock == %d\n", cep->lock);		} else {			cep->lock++;		}	    bdp = cep->dirty_tx;	    while ((bdp->status&BD_ENET_TX_READY)==0) {		if ((bdp==cep->cur_tx) && (cep->tx_full == 0))		    break;		if (bdp->status & BD_ENET_TX_HB)	/* No heartbeat */			cep->stats.tx_heartbeat_errors++;		if (bdp->status & BD_ENET_TX_LC)	/* Late collision */			cep->stats.tx_window_errors++;		if (bdp->status & BD_ENET_TX_RL)	/* Retrans limit */			cep->stats.tx_aborted_errors++;		if (bdp->status & BD_ENET_TX_UN)	/* Underrun */			cep->stats.tx_fifo_errors++;		if (bdp->status & 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->status &		    (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->status & 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]); */		dev_kfree_skb (cep->tx_skbuff[cep->skb_dirty], FREE_WRITE);		cep->skb_dirty = (cep->skb_dirty + 1) & TX_RING_MOD_MASK;		/* Update pointer to next buffer descriptor to be transmitted.		*/		if (bdp->status & 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 = (QUICC_BD *)bdp;	    }	    if (must_restart) {			volatile QUICC *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 = pquicc;		cp->cp_cr =		    mk_cr_cmd(CPM_CR_ENET, CPM_CR_RESTART_TX) | CPM_CR_FLG;		while (cp->cp_cr & CPM_CR_FLG);	    }	    /* spin_unlock(&cep->lock); */ /* TODO: SPINLOCK */		/* sti(); */		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("CPM 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 QUICC_BD	*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.

⌨️ 快捷键说明

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