📄 hdlc.c
字号:
/* * 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 + -