📄 hdlc.c
字号:
skb->mac.raw=skb->data; dev_to_hdlc(dev)->netif_rx(skb); } /* Clear the status flags for this buffer. */ bdp->cbd_sc &= ~BD_HDLC_RX_STATS; /* Mark the buffer empty. */ bdp->cbd_sc |= BD_HDLC_RX_EMPTY; /* Update BD pointer to next entry. */ if (bdp->cbd_sc & BD_HDLC_RX_WRAP) bdp = chp->rx_bd_base; else bdp++; } chp->cur_rx = (cbd_t *)bdp; return 0;}static intscc_hdlc_close(struct net_device *dev){ /* Don't know what to do yet. */ if (dev_to_hdlc(dev)->stop) dev_to_hdlc(dev)->stop(dev_to_hdlc(dev)); netif_stop_queue(dev); MOD_DEC_USE_COUNT; return 0;}static int scc_encoding_setting(struct net_device *dev){ /* Nothing to do here */ return 0;}#if 0static int scc_clock_setting(struct net_device *dev){ volatile struct scc_hdlc_private *chp = &scc_private_data; uint bps = chp->settings.clock_rate; if (bps < 50 || bps > 115200) return -EINVAL; m8xx_cpm_setbrg(BRG_HDLC, bps); chp->settings.clock_rate = m8xx_cpm_getbrg(BRG_HDLC); if (chp->settings.clock_rate != bps) { printk(KERN_INFO "%s: clock adjusted from %08d to %08d \n", dev->name, bps, chp->settings.clock_rate); } return 0;}#endifstatic int scc_loopback_setting(struct net_device *dev){ /* Nothing to do here */ return 0;}static int scc_crc_setting(struct net_device *dev){ /* Nothing to do here */ return 0;}static int scc_set_iface(struct net_device *dev){ struct { int (*action)(struct net_device *); } *p, do_setting[] = { { scc_encoding_setting },#if 0 { scc_clock_setting },#endif { scc_loopback_setting }, { scc_crc_setting }, { NULL } }; int ret = 0; for (p = do_setting; p->action; p++) { if ((ret = p->action(dev)) < 0) break; } return ret;}static int scc_hdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd){ struct scc_hdlc_private *chp = &scc_private_data; struct if_settings *if_s = &ifr->ifr_settings; const size_t size = sizeof(chp->settings); int ret = 0; printk("hdlc protocol: %x\n",dev_to_hdlc(dev)->proto); switch(ifr->ifr_settings.type) { case IF_GET_IFACE : { if_s->type = IF_IFACE_SYNC_SERIAL; if (if_s->data_length == 0) return 0; if (if_s->data_length < size) return -ENOMEM; if (copy_to_user(if_s->data, &chp->settings, size)) return -EFAULT; if_s->data_length = size; break; } case IF_IFACE_SYNC_SERIAL : { if (!capable(CAP_NET_ADMIN)) return -EPERM; if (if_s->data_length != size) return -ENOMEM; if (copy_from_user(&chp->settings, if_s->data, size)) return -EFAULT; ret = scc_set_iface(dev); break; } default : { ret = hdlc_ioctl(dev, ifr, cmd); } } return ret;}static int scc_hdlc_attach(hdlc_device *hdlc, unsigned short encoding, unsigned short parity){ volatile struct scc_hdlc_private *chp = &scc_private_data; if (encoding != ENCODING_NRZ && encoding != ENCODING_NRZI && encoding != ENCODING_FM_MARK && encoding != ENCODING_FM_SPACE && encoding != ENCODING_MANCHESTER) return -EINVAL; if (parity != PARITY_NONE && parity != PARITY_CRC16_PR0_CCITT && parity != PARITY_CRC16_PR1_CCITT && /* parity != PARITY_CRC32_PR0_CCITT && FIXME */ parity != PARITY_CRC32_PR1_CCITT) return -EINVAL; chp->encoding = encoding; chp->parity = parity; return 0;}/* Remove whatever the scc_hdlc_init() function requested. :-) */static void __exit scc_hdlc_exit(void){ struct scc_hdlc_private *cep = &scc_private_data; hdlc_device *hdlc = &scc_private_data.hdlc; volatile scc_hdlc_t *ep = (scc_hdlc_t *) hdlc_to_dev(hdlc)->base_addr; unsigned long mem_addr; pte_t *pte; volatile cbd_t *bdp; int i,j; if (scc_hdlc_steps&SCC_HDLC_HDLC_DEV_REG) { unregister_hdlc_device(hdlc); cpm_free_handler(CPMVEC_HDLC); } if (scc_hdlc_steps&SCC_HDLC_HDLC_PAGE_ALLOC) { bdp = cep->rx_bd_base; for (i = 0; i < CPM_HDLC_RX_PAGES; i++) { /* Get the memory address */ mem_addr = (uint) __va(bdp->cbd_bufaddr); /* Make it cached */ pte = va_to_pte(mem_addr); pte_val(*pte) &= ~_PAGE_NO_CACHE; flush_tlb_page(init_mm.mmap, mem_addr); /* Deallocate the page */ __free_page((struct page *) mem_addr); /* Jump to the next page */ for (j = 0; j < CPM_HDLC_RX_FRPPG; j++) bdp++; } } if (scc_hdlc_steps&SCC_HDLC_TBASE_DPALLOC) m8xx_cpm_dpfree(ep->shd_genscc.scc_tbase); if (scc_hdlc_steps&SCC_HDLC_RBASE_DPALLOC) m8xx_cpm_dpfree(ep->shd_genscc.scc_rbase);}/* Initialize the CPM HDLC on SCC.We don't use CTS and CD lines. * HDLC is on SCC2 */int __init scc_hdlc_init(void){ struct scc_hdlc_private *chp = &scc_private_data; hdlc_device *hdlc = &scc_private_data.hdlc; int i, j; unsigned long mem_addr; pte_t *pte; bd_t *bd; volatile cbd_t *bdp; volatile cpm8xx_t *cp; volatile scc_t *sccp; volatile scc_hdlc_t *ep; volatile immap_t *immap; cp = cpmp; /* Get pointer to Communication Processor */ immap = (immap_t *)(mfspr(IMMR)&0xFFFF0000); bd = (bd_t *)__res; spin_lock_init(&chp->lock); /* Get pointer to SCC area in parameter RAM. */ ep = (scc_hdlc_t *)(&cp->cp_dparam[PROFF_HDLC]); /* And another to the SCC register area. */ sccp = (volatile scc_t *)(&cp->cp_scc[SCC_HDLC]); chp->sccp = (scc_t *)sccp; /* Keep the pointer handy */ /* Disable receive and transmit in case EPPC-Bug started it. */ sccp->scc_gsmrl &= ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT); /* Cookbook style from the MPC860 manual..... * Not all of this is necessary if EPPC-Bug has initialized * the network. * So far we are lucky, all board configurations use the same * pins, or at least the same I/O Port for these functions..... * It can't last though...... */#if (defined(PA_HDLC_RXD) && defined(PA_HDLC_TXD)) /* Configure port A pins for Txd and Rxd. */ immap->im_ioport.iop_papar |= (PA_HDLC_RXD | PA_HDLC_TXD); immap->im_ioport.iop_padir &= ~(PA_HDLC_RXD | PA_HDLC_TXD); immap->im_ioport.iop_paodr &= ~(PA_HDLC_RXD | PA_HDLC_TXD);#elif (defined(PB_HDLC_RXD) && defined(PB_HDLC_TXD)) /* Configure port B pins for Txd and Rxd. */ immap->im_cpm.cp_pbpar |= (PB_HDLC_RXD | PB_HDLC_TXD); immap->im_cpm.cp_pbdir &= ~(PB_HDLC_RXD | PB_HDLC_TXD); immap->im_cpm.cp_pbodr &= ~(PB_HDLC_RXD | PB_HDLC_TXD);#else#error Exactly ONE pair of PA_HDLC_[RT]XD, PB_HDLC_[RT]XD must be defined#endif#if defined(PC_HDLC_LBK) /* Configure port C pins to disable External Loopback */ immap->im_ioport.iop_pcpar &= ~PC_HDLC_LBK; immap->im_ioport.iop_pcdir |= PC_HDLC_LBK; immap->im_ioport.iop_pcso &= ~PC_HDLC_LBK; immap->im_ioport.iop_pcdat &= ~PC_HDLC_LBK; /* Disable Loopback */#endif /* PC_HDLC_LBK */ /* Configure port A for TCLK and RCLK. */ immap->im_ioport.iop_papar |= PA_HDLC_TCLK|PA_HDLC_RCLK; immap->im_ioport.iop_padir &= ~(PA_HDLC_TCLK|PA_HDLC_RCLK); /* Configure Serial Interface clock routing. * First, clear all SCC bits to zero, then set the ones we want. */ cp->cp_sicr &= ~SICR_HDLC_MASK; cp->cp_sicr |= SICR_HDLC_CLKRT; cp->cp_sicr &= ~0x00004000; /* Manual says set SDDR, but I can't find anything with that * name. I think it is a misprint, and should be SDCR. This * has already been set by the communication processor initialization. */ /* Allocate space for the buffer descriptors in the DP ram. * These are relative offsets in the DP ram address space. * Initialize base addresses for the buffer descriptors. */ i = m8xx_cpm_dpalloc(sizeof(cbd_t) * RX_RING_SIZE); ep->shd_genscc.scc_rbase = i; chp->rx_bd_base = (cbd_t *)&cp->cp_dpmem[i]; i = m8xx_cpm_dpalloc(sizeof(cbd_t) * TX_RING_SIZE); ep->shd_genscc.scc_tbase = i; chp->tx_bd_base = (cbd_t *)&cp->cp_dpmem[i]; chp->dirty_tx = chp->cur_tx = chp->tx_bd_base; chp->cur_rx = chp->rx_bd_base; /* Issue init Rx BD command for SCC. * Manual says to perform an Init Rx parameters here. We have * to perform both Rx and Tx because the SCC may have been * already running. * In addition, we have to do it later because we don't yet have * all of the BD control/status set properly. cp->cp_cpcr = mk_cr_cmd(CPM_CR_HDLC, CPM_CR_INIT_RX) | CPM_CR_FLG; while (cp->cp_cpcr & CPM_CR_FLG); */ /* Initialize function code registers for big-endian. */ ep->shd_genscc.scc_rfcr = SCC_EB; ep->shd_genscc.scc_tfcr = SCC_EB; /* Set maximum bytes per receive buffer. * This appears to be an HDLC frame size, not the buffer * fragment size. It must be a multiple of four. */ ep->shd_genscc.scc_mrblr = 1600; /* Set CRC preset and mask. */ ep->shd_cpres = 0x0000ffff; ep->shd_cmask = 0x0000f0b8; ep->shd_crcec = 0; /* CRC Error counter */ ep->shd_abtsc = 0; /* Abort sequence counter */ ep->shd_nmarc = 0; /* Nonmatching address received counter */ ep->shd_disfc = 0; /* discard frame counter */ ep->shd_retrc = 0; /* Frame retransmission counter */ ep->shd_mflr = 0x0640; /* Max frame length register */ ep->shd_rfthr = 0x0001; /* Received frames threshold */ ep->shd_hmask = 0x0000; /* Mask register */ /* Clear address */ ep->shd_haddr1 = 0; ep->shd_haddr2 = 0; ep->shd_haddr3 = 0; ep->shd_haddr4 = 0; /* Now allocate the host memory pages and initialize the * buffer descriptors. */ bdp = chp->tx_bd_base; for (i=0; i<TX_RING_SIZE; i++) { /* Initialize the BD for every fragment in the page. */ bdp->cbd_sc = 0; bdp->cbd_bufaddr = 0; bdp++; } /* Set the last buffer to wrap. */ bdp--; bdp->cbd_sc |= BD_SC_WRAP; bdp = chp->rx_bd_base; for (i=0; i<CPM_HDLC_RX_PAGES; i++) { /* Allocate a page. */ mem_addr = __get_free_page(GFP_KERNEL); /* Make it uncached. */ pte = va_to_pte(mem_addr); pte_val(*pte) |= _PAGE_NO_CACHE; flush_tlb_page(init_mm.mmap, mem_addr); /* Initialize the BD for every fragment in the page. */ for (j=0; j<CPM_HDLC_RX_FRPPG; j++) { bdp->cbd_sc = BD_HDLC_RX_EMPTY | BD_HDLC_RX_INTR; bdp->cbd_bufaddr = __pa(mem_addr); mem_addr += CPM_HDLC_RX_FRSIZE; bdp++; } } /* Set the last buffer to wrap. */ bdp--; bdp->cbd_sc |= BD_SC_WRAP; /* Let's re-initialize the channel now. We have to do it later * than the manual describes because we have just now finished * the BD initialization. */ /* init tx and rx command to SCCx */ cp->cp_cpcr = mk_cr_cmd(CPM_CR_HDLC, CPM_CR_INIT_TRX) | CPM_CR_FLG; while (cp->cp_cpcr & CPM_CR_FLG); /* wait until command finished */ chp->skb_cur = chp->skb_dirty = 0; sccp->scc_scce = 0xffff; /* Clear any pending events */ /* Enable interrupts for transmit error, complete frame * received, and any transmit buffer we have also set the * interrupt flag. */ /* sccp->scc_sccm = (SCCE_HDLC_TXE | SCCE_HDLC_RXF | SCCE_HDLC_TXB); */ sccp->scc_sccm = SCCE_HDLC_RXF; /* Install our interrupt handler. */ cpm_install_handler(CPMVEC_HDLC, scc_hdlc_interrupt, NULL); /* Set GSMR_H to enable all normal operating modes. * Set GSMR_L to enable HDLC. */ sccp->scc_gsmrh = 0; sccp->scc_gsmrl = 0; /* Set processing mode. Use 16 bit CRC,one opening and one closing * flag,and prevent multiple frames in the FIFO. */ sccp->scc_pmsr = (SCC_HDLC_PMSR_NOF | SCC_HDLC_PMSR_HDCRC | SCC_HDLC_PMSR_MFF); hdlc_to_dev(hdlc)->base_addr = (unsigned long)ep; hdlc_to_dev(hdlc)->irq = 0; /* hdlc_to_dev(hdlc)->priv = &hdlc->state.ppp.syncppp_ptr; hdlc->state.ppp.syncppp_ptr = &hdlc->state.ppp.pppdev; hdlc->state.ppp.pppdev.dev = hdlc_to_dev(hdlc); */ hdlc_to_dev(hdlc)->init = NULL;#if 0 dev->name = "CPM_HDLC";#endif /* Save some pointers into device structure */ hdlc_to_dev(hdlc)->open = scc_hdlc_open; hdlc_to_dev(hdlc)->stop = scc_hdlc_close; hdlc_to_dev(hdlc)->do_ioctl = scc_hdlc_ioctl; hdlc_to_dev(hdlc)->tx_timeout = scc_hdlc_timeout; hdlc_to_dev(hdlc)->watchdog_timeo = TX_TIMEOUT; /* The CPM HDLC specific entries in the device structure */ hdlc->xmit = scc_hdlc_xmit; hdlc->attach = scc_hdlc_attach; hdlc->proto = IF_PROTO_HDLC; hdlc->netif_rx = netif_rx; /* Now we are ready to register the HDLC device */ if (register_hdlc_device(hdlc) < 0) { printk(KERN_ERR "hdlc: unable to register hdlc device\n"); scc_hdlc_exit(); return -ENODEV; } scc_hdlc_steps |= SCC_HDLC_HDLC_DEV_REG; /* And last, enable the transmit and receive processing. */ sccp->scc_gsmrl |= (SCC_GSMRL_ENR | SCC_GSMRL_ENT);#if 0 sccp->scc_gsmrl |= 0x00000040; /* local loopback test,add by YZX */#endif printk("%s: CPM HDLC Version 0.1 on SCC%d, ", hdlc_to_name(hdlc), SCC_HDLC+1); return 0;}module_init(scc_hdlc_init);module_exit(scc_hdlc_exit);MODULE_DESCRIPTION("HDLC driver for Motorola MPC8xx");EXPORT_NO_SYMBOLS;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -