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

📄 hdlc.c

📁 linux驱动源码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * HDLC driver for Motorola MPC8xx. */#include <linux/config.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/sched.h>#include <linux/string.h>#include <linux/ptrace.h>#include <linux/errno.h>#include <linux/ioport.h>#include <linux/slab.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/hdlc.h>#include <linux/skbuff.h>#include <linux/spinlock.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 HDLC processing on SCC2-SCC4.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 * chp->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 * 'chp->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 * HZ=100 ( <asm-ppc/param.h> ) */#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. */#ifdef CONFIG_HDLC_BIG_BUFFERS#define CPM_HDLC_RX_PAGES	32#define CPM_HDLC_RX_FRSIZE	2048#define CPM_HDLC_RX_FRPPG	(PAGE_SIZE / CPM_HDLC_RX_FRSIZE)#define RX_RING_SIZE		(CPM_HDLC_RX_FRPPG * CPM_HDLC_RX_PAGES)#define TX_RING_SIZE		64	/* Must be power of two */#define TX_RING_MOD_MASK	63	/*   for this to work */#else#define CPM_HDLC_RX_PAGES	4#define CPM_HDLC_RX_FRSIZE	2048#define CPM_HDLC_RX_FRPPG	(PAGE_SIZE / CPM_HDLC_RX_FRSIZE)#define RX_RING_SIZE		(CPM_HDLC_RX_FRPPG * CPM_HDLC_RX_PAGES)#define TX_RING_SIZE		8	/* Must be power of two */#define TX_RING_MOD_MASK	7	/*   for this to work */#endif/* 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		256/* 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_hdlc_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 BDs. */	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;	/* HDLC data */	hdlc_device hdlc;	sync_serial_settings settings;	unsigned short encoding;	unsigned short parity;}scc_private_data;static int scc_hdlc_open(struct net_device *dev);static int scc_hdlc_xmit(struct sk_buff *skb, struct net_device *dev);static void scc_hdlc_timeout(struct net_device *dev);static int scc_hdlc_rx(struct net_device *dev);static void scc_hdlc_interrupt(void *dev_id);static int scc_hdlc_close(struct net_device *dev);/* Get this from various configuration locations (depends on board).*//*static	ushort	my_hdlc_addr[] = { 0x0800, 0x3e26, 0x1559 };*//* * use SCC3 by default */#if defined(CONFIG_SCC3_HDLC)#define CPM_CR_HDLC	CPM_CR_CH_SCC3#define PROFF_HDLC	PROFF_SCC3#define SCC_HDLC	2		/* Index, not number! */#define CPMVEC_HDLC	CPMVEC_SCC3#elif defined(CONFIG_SCC2_HDLC)#define CPM_CR_HDLC	CPM_CR_CH_SCC2#define PROFF_HDLC	PROFF_SCC2#define SCC_HDLC	1		/* Index, not number! */#define CPMVEC_HDLC	CPMVEC_SCC2#elif defined(CONFIG_SCC1_HDLC)#define CPM_CR_HDLC	CPM_CR_CH_SCC1#define PROFF_HDLC	PROFF_SCC1#define SCC_HDLC	0		/* Index, not number! */#define CPMVEC_HDLC	CPMVEC_SCC1#else#error CONFIG_SCCx_HDLC not defined#endif#define SCC_HDLC_RBASE_DPALLOC         0x00010#define SCC_HDLC_TBASE_DPALLOC         0x00020#define SCC_HDLC_HDLC_PAGE_ALLOC       0x00100#define SCC_HDLC_HDLC_DEV_REG          0x00800static __initdata int scc_hdlc_steps;static intscc_hdlc_open(struct net_device *dev){	int ret;	/* I should reset the ring buffers here, but I don't yet know	 * a simple way to do that.	 */   if (netif_running(dev)) {      printk(KERN_WARNING "%s: already running\n", dev->name);      return 0;   }   printk("open %x\n",(unsigned int)dev_to_hdlc(dev)->open);   /* Check the open method */   if (dev_to_hdlc(dev)->open) {      ret = dev_to_hdlc(dev)->open(dev_to_hdlc(dev));      if (ret < 0)         return ret;   }   netif_start_queue(dev);   MOD_INC_USE_COUNT;   return 0;                                   /* Always succeed */}static intscc_hdlc_xmit(struct sk_buff *skb, struct net_device *dev){	struct scc_hdlc_private *chp = &scc_private_data;	volatile cbd_t	*bdp;	/* Fill in a Tx ring entry */	bdp = chp->cur_tx;#ifndef final_version	if (bdp->cbd_sc & BD_HDLC_TX_READY) {		/* Ooops.  All transmit buffers are full.  Bail out.		 * This should not happen, since chp->tx_busy should be set.		 */		printk("%s: tx queue full!.\n", dev->name);		return 1;	}#endif	/* Clear all of the status flags.	 */	bdp->cbd_sc &= ~BD_HDLC_TX_STATS;	/* Set buffer length and buffer pointer.	*/	bdp->cbd_datlen = skb->len;	bdp->cbd_bufaddr = __pa(skb->data);	/* Save skb pointer.	*/	chp->tx_skbuff[chp->skb_cur] = skb;	chp->stats.tx_bytes += skb->len;	chp->skb_cur = (chp->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(&chp->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_HDLC_TX_READY | BD_HDLC_TX_INTR | BD_HDLC_TX_LAST | BD_HDLC_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_HDLC_TX_WRAP)		bdp = chp->tx_bd_base;	else		bdp++;	if (bdp->cbd_sc & BD_HDLC_TX_READY) {		netif_stop_queue(dev);		chp->tx_full = 1;	}	chp->cur_tx = (cbd_t *)bdp;	spin_unlock_irq(&chp->lock);	return 0;}static voidscc_hdlc_timeout(struct net_device *dev){	struct scc_hdlc_private *chp = &scc_private_data;	printk("%s: transmit timed out.\n", dev->name);	chp->stats.tx_errors++;#ifndef final_version	{		int	i;		cbd_t	*bdp;		printk(" Ring data dump: cur_tx %p%s cur_rx %p.\n",		       chp->cur_tx, chp->tx_full ? " (full)" : "",		       chp->cur_rx);		bdp = chp->tx_bd_base;		for (i = 0 ; i < TX_RING_SIZE; i++, bdp++)			printk("%04x %04x %08x\n",			       bdp->cbd_sc,			       bdp->cbd_datlen,			       bdp->cbd_bufaddr);		bdp = chp->rx_bd_base;		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 (!chp->tx_full)		netif_wake_queue(dev);}/* The interrupt handler. * This is called from the CPM handler, not the MPC core interrupt. */static voidscc_hdlc_interrupt(void *dev_id){	volatile struct	scc_hdlc_private *chp=&scc_private_data;	hdlc_device *hdlc = &scc_private_data.hdlc;	volatile cbd_t	*bdp;	ushort	int_events;	int	must_restart;	/* Get the interrupt events that caused us to be here.	*/	int_events = chp->sccp->scc_scce;	chp->sccp->scc_scce = int_events;	must_restart = 0;	/* Handle receive event in its own function.	*/	if (int_events & SCCE_HDLC_RXF)		scc_hdlc_rx(hdlc_to_dev(hdlc));	/* 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_HDLC_TXE | SCCE_HDLC_TXB)) {	    spin_lock(&chp->lock);	    bdp = chp->dirty_tx;	    while ((bdp->cbd_sc&BD_HDLC_TX_READY)==0) {		if ((bdp==chp->cur_tx) && (chp->tx_full == 0))		    break;		if (bdp->cbd_sc & BD_HDLC_TX_UN) {	/* Underrun */			must_restart = 1;			chp->stats.tx_fifo_errors++;		}		chp->stats.tx_packets++;		/* Free the sk buffer associated with this last transmit.		*/		dev_kfree_skb_irq(chp->tx_skbuff[chp->skb_dirty]);		chp->skb_dirty = (chp->skb_dirty + 1) & TX_RING_MOD_MASK;		/* Update pointer to next buffer descriptor to be transmitted.		*/		if (bdp->cbd_sc & BD_HDLC_TX_WRAP)			bdp = chp->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_HDLC_TX_READY set.		 */		/* Since we have freed up a buffer, the ring is no longer		 * full.		 */		if (chp->tx_full) {			chp->tx_full = 0;			if (netif_queue_stopped(hdlc_to_dev(hdlc)))				netif_wake_queue(hdlc_to_dev(hdlc));		}		chp->dirty_tx = (cbd_t *)bdp;	    }	    if (must_restart) {		volatile cpm8xx_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_CR_HDLC, CPM_CR_RESTART_TX) | CPM_CR_FLG;		while (cp->cp_cpcr & CPM_CR_FLG);	    }	    spin_unlock(&chp->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_HDLC_BSY) {		chp->stats.rx_dropped++;		printk("CPM HDLC: 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_hdlc_rx(struct net_device *dev){	struct	scc_hdlc_private *chp = &scc_private_data;	volatile cbd_t	*bdp;	struct	sk_buff *skb;	ushort	pkt_len;	/* First, grab all of the stats for the incoming packet.	 * These get messed up if we get called due to a busy condition.	 */	bdp = chp->cur_rx;for (;;) {	if (bdp->cbd_sc & BD_HDLC_RX_EMPTY)		break;		#ifndef final_version	/* Since we have allocated space to hold a complete frame, both	 * the first and last indicators should be set.	 */	if ((bdp->cbd_sc & (BD_HDLC_RX_FIRST | BD_HDLC_RX_LAST)) !=		(BD_HDLC_RX_FIRST | BD_HDLC_RX_LAST))			printk("CPM HDLC: rcv is not first+last\n");#endif	/* Frame too long or too short.	*/	if (bdp->cbd_sc & BD_HDLC_RX_NO)	/* Frame alignment */		dev_to_hdlc(dev)->stats.rx_frame_errors++;	if (bdp->cbd_sc & BD_HDLC_RX_CR)	/* CRC Error */		dev_to_hdlc(dev)->stats.rx_crc_errors++;	if (bdp->cbd_sc & BD_HDLC_RX_OV)	/* FIFO overrun */		dev_to_hdlc(dev)->stats.rx_crc_errors++;	/* Process the incoming frame.	*/	dev_to_hdlc(dev)->stats.rx_packets++;	pkt_len = bdp->cbd_datlen;	dev_to_hdlc(dev)->stats.rx_bytes += pkt_len;	/* This does 16 byte alignment, much more than we need.	 * The packet length includes FCS, but we don't want to	 * include that when passing upstream as it messes up	 * bridging applications.	 */	/*	printk("pkt_len: %d\n",pkt_len);	printk("sc: %x\n",bdp->cbd_sc);		if(pkt_len<90)		{		int i;		for(i=0;i<pkt_len;i++)		{		unsigned char a=(*(unsigned char *)(__va(bdp->cbd_bufaddr+i)))&0xFF;		printk("%x|",a);		}		printk("\n");		}*/	/* by yzx to debug crashes */	if(pkt_len>2)		skb = dev_alloc_skb(pkt_len-2); /* we use 16 bit CRC */	else		skb=NULL;	if (skb == NULL) {		printk("%s: Memory squeeze, dropping packet.\n", dev->name);			dev_to_hdlc(dev)->stats.rx_dropped++;	}	else {		skb->dev = dev;		skb_put(skb,pkt_len-2);	/* Make room */		eth_copy_and_sum(skb,			(unsigned char *)__va(bdp->cbd_bufaddr),			pkt_len-2, 0);		/*		skb->protocol=eth_type_trans(skb,dev);		*/		/*		skb->protocol=htons(ETH_P_IP);		*/

⌨️ 快捷键说明

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