📄 dscc4.c
字号:
if(dscc4_do_action(dev, "IDR")) goto err_xpr; dpriv->flags &= ~NeedIDR; mb(); /* Activate receiver and misc */ writel(0x08050008, scc_offset + CCR2); } err_xpr: if (!(state &= ~Xpr)) goto try; } } else { /* ! SccEvt */ if (state & Hi) {#ifdef EXPERIMENTAL_POLLING while(!dscc4_tx_poll(dpriv, dev));#endif state &= ~Hi; } /* * FIXME: it may be avoided. Re-re-re-read the manual. */ if (state & Err) { printk(KERN_ERR "%s: Tx ERR\n", dev->name); dpriv->stats.tx_errors++; state &= ~Err; } } goto try;}static __inline__ void dscc4_rx_irq(struct dscc4_pci_priv *priv, struct net_device *dev){ struct dscc4_dev_priv *dpriv = dev->priv; u32 state; int cur;try: cur = dpriv->iqrx_current%IRQ_RING_SIZE; state = dpriv->iqrx[cur]; if (!state) return; dpriv->iqrx[cur] = 0; dpriv->iqrx_current++;#ifdef DEBUG_PARANOID if (SOURCE_ID(state) != dpriv->dev_id) { printk(KERN_DEBUG "%s (Rx): Source Id=%d, state=%08x\n", dev->name, SOURCE_ID(state), state); goto try; } if (state & 0x0df80c00) { printk(KERN_DEBUG "%s (Rx): state=%08x (UFO alert)\n", dev->name, state); goto try; }#endif if (!(state & SccEvt)){ struct RxFD *rx_fd; state &= 0x00ffffff; if (state & Err) { /* Hold or reset */ printk(KERN_DEBUG "%s (Rx): ERR\n", dev->name); cur = dpriv->rx_current; rx_fd = dpriv->rx_fd + cur; /* * Presume we're not facing a DMAC receiver reset. * As We use the rx size-filtering feature of the * DSCC4, the beginning of a new frame is waiting in * the rx fifo. I bet a Receive Data Overflow will * happen most of time but let's try and avoid it. * Btw (as for RDO) if one experiences ERR whereas * the system looks rather idle, there may be a * problem with latency. In this case, increasing * RX_RING_SIZE may help. */ while (dpriv->rx_needs_refill) { while(!(rx_fd->state1 & Hold)) { rx_fd++; cur++; if (!(cur = cur%RX_RING_SIZE)) rx_fd = dpriv->rx_fd; } dpriv->rx_needs_refill--; try_get_rx_skb(dpriv, cur, dev); if (!rx_fd->data) goto try; rx_fd->state1 &= ~Hold; rx_fd->state2 = 0x00000000; rx_fd->end = 0xbabeface; } goto try; } if (state & Fi) { cur = dpriv->rx_current%RX_RING_SIZE; rx_fd = dpriv->rx_fd + cur; dscc4_rx_skb(dpriv, cur, rx_fd, dev); dpriv->rx_current++; goto try; } if (state & Hi ) { /* HI bit */ state &= ~Hi; goto try; } } else { /* ! SccEvt */#ifdef DEBUG_PARANOIA int i; static struct { u32 mask; const char *irq_name; } evts[] = { { 0x00008000, "TIN"}, { 0x00004000, "CSC"}, { 0x00000020, "RSC"}, { 0x00000010, "PCE"}, { 0x00000008, "PLLA"}, { 0x00000004, "CDSC"}, { 0, NULL} };#endif /* DEBUG_PARANOIA */ state &= 0x00ffffff;#ifdef DEBUG_PARANOIA for (i = 0; evts[i].irq_name; i++) { if (state & evts[i].mask) { printk(KERN_DEBUG "dscc4(%s): %s\n", dev->name, evts[i].irq_name); if (!(state &= ~evts[i].mask)) goto try; } }#endif /* DEBUG_PARANOIA */ /* * Receive Data Overflow (FIXME: untested) */ if (state & Rdo) { u32 ioaddr, scc_offset, scc_addr; struct RxFD *rx_fd; int cur; //if (debug) // dscc4_rx_dump(dpriv); ioaddr = dev->base_addr; scc_addr = ioaddr + 0x0c*dpriv->dev_id; scc_offset = ioaddr + SCC_REG_START(dpriv->dev_id); writel(readl(scc_offset + CCR2) & ~RxActivate, scc_offset + CCR2); /* * This has no effect. Why ? * ORed with TxSccRes, one sees the CFG ack (for * the TX part only). */ writel(RxSccRes, scc_offset + CMDR); dpriv->flags |= RdoSet; /* * Let's try and save something in the received data. * rx_current must be incremented at least once to * avoid HOLD in the BRDA-to-be-pointed desc. */ do { cur = dpriv->rx_current++%RX_RING_SIZE; rx_fd = dpriv->rx_fd + cur; if (!(rx_fd->state2 & DataComplete)) break; if (rx_fd->state2 & FrameAborted) { dpriv->stats.rx_over_errors++; rx_fd->state1 |= Hold; rx_fd->state2 = 0x00000000; rx_fd->end = 0xbabeface; } else dscc4_rx_skb(dpriv, cur, rx_fd, dev); } while (1); if (debug) { if (dpriv->flags & RdoSet) printk(KERN_DEBUG "dscc4: no RDO in Rx data\n"); }#ifdef DSCC4_RDO_EXPERIMENTAL_RECOVERY /* * FIXME: must the reset be this violent ? */ writel(dpriv->rx_fd_dma + (dpriv->rx_current%RX_RING_SIZE)* sizeof(struct RxFD), scc_addr + CH0BRDA); writel(MTFi|Rdr|Idr, scc_addr + CH0CFG); if(dscc4_do_action(dev, "RDR")) { printk(KERN_ERR "%s: RDO recovery failed(%s)\n", dev->name, "RDR"); goto rdo_end; } writel(MTFi|Idr, scc_addr + CH0CFG); if(dscc4_do_action(dev, "IDR")) { printk(KERN_ERR "%s: RDO recovery failed(%s)\n", dev->name, "IDR"); goto rdo_end; } rdo_end:#endif writel(readl(scc_offset + CCR2) | RxActivate, scc_offset + CCR2); goto try; } /* These will be used later */ if (state & Rfs) { if (!(state &= ~Rfs)) goto try; } if (state & Rfo) { if (!(state &= ~Rfo)) goto try; } if (state & Flex) { if (!(state &= ~Flex)) goto try; } }}static int dscc4_init_ring(struct net_device *dev){ struct dscc4_dev_priv *dpriv = (struct dscc4_dev_priv *)dev->priv; struct TxFD *tx_fd; struct RxFD *rx_fd; int i; tx_fd = (struct TxFD *) pci_alloc_consistent(dpriv->pci_priv->pdev, TX_RING_SIZE*sizeof(struct TxFD), &dpriv->tx_fd_dma); if (!tx_fd) goto err_out; rx_fd = (struct RxFD *) pci_alloc_consistent(dpriv->pci_priv->pdev, RX_RING_SIZE*sizeof(struct RxFD), &dpriv->rx_fd_dma); if (!rx_fd) goto err_free_dma_tx; dpriv->tx_fd = tx_fd; dpriv->rx_fd = rx_fd; dpriv->rx_current = 0; dpriv->tx_current = 0; dpriv->tx_dirty = 0; /* the dma core of the dscc4 will be locked on the first desc */ for(i = 0; i < TX_RING_SIZE; ) { reset_TxFD(tx_fd); /* FIXME: NULL should be ok - to be tried */ tx_fd->data = dpriv->tx_fd_dma; dpriv->tx_skbuff[i] = NULL; i++; tx_fd->next = (u32)(dpriv->tx_fd_dma + i*sizeof(struct TxFD)); tx_fd++; } (--tx_fd)->next = (u32)dpriv->tx_fd_dma;{ /* * XXX: I would expect the following to work for the first descriptor * (tx_fd->state = 0xc0000000) * - Hold=1 (don't try and branch to the next descripto); * - No=0 (I want an empty data section, i.e. size=0); * - Fe=1 (required by No=0 or we got an Err irq and must reset). * Alas, it fails (and locks solid). Thus the introduction of a dummy * skb to avoid No=0 (choose one: Ugly [ ] Tasteless [ ] VMS [ ]). * TODO: fiddle the tx threshold when time permits. */ struct sk_buff *skb; skb = dev_alloc_skb(32); if (!skb) goto err_free_dma_tx; skb->len = 32; memset(skb->data, 0xaa, 16); tx_fd -= (TX_RING_SIZE - 1); tx_fd->state = 0xc0000000; tx_fd->state |= ((u32)(skb->len & TxSizeMax)) << 16; tx_fd->data = pci_map_single(dpriv->pci_priv->pdev, skb->data, skb->len, PCI_DMA_TODEVICE); dpriv->tx_skbuff[0] = skb;} for (i = 0; i < RX_RING_SIZE;) { /* size set by the host. Multiple of 4 bytes please */ rx_fd->state1 = HiDesc; /* Hi, no Hold */ rx_fd->state2 = 0x00000000; rx_fd->end = 0xbabeface; rx_fd->state1 |= ((u32)(HDLC_MAX_MRU & RxSizeMax)) << 16; try_get_rx_skb(dpriv, i, dev); i++; rx_fd->next = (u32)(dpriv->rx_fd_dma + i*sizeof(struct RxFD)); rx_fd++; } (--rx_fd)->next = (u32)dpriv->rx_fd_dma; rx_fd->state1 |= 0x40000000; /* Hold */ return 0;err_free_dma_tx: pci_free_consistent(dpriv->pci_priv->pdev, TX_RING_SIZE*sizeof(*tx_fd), tx_fd, dpriv->tx_fd_dma);err_out: return -1;}static struct net_device_stats *dscc4_get_stats(struct net_device *dev){ struct dscc4_dev_priv *priv = (struct dscc4_dev_priv *)dev->priv; return &priv->stats;}static void __exit dscc4_remove_one(struct pci_dev *pdev){ struct dscc4_pci_priv *ppriv; struct net_device *root; int i; ppriv = pci_get_drvdata(pdev); root = ppriv->root; free_irq(pdev->irq, root); pci_free_consistent(pdev, IRQ_RING_SIZE*sizeof(u32), ppriv->iqcfg, ppriv->iqcfg_dma); for (i=0; i < dev_per_card; i++) { struct dscc4_dev_priv *dpriv; struct net_device *dev; dev = ppriv->root + i; dscc4_unattach_hdlc_device(dev); dpriv = (struct dscc4_dev_priv *)dev->priv; pci_free_consistent(pdev, IRQ_RING_SIZE*sizeof(u32), dpriv->iqrx, dpriv->iqrx_dma); pci_free_consistent(pdev, IRQ_RING_SIZE*sizeof(u32), dpriv->iqtx, dpriv->iqtx_dma); unregister_netdev(dev); } kfree(root->priv); iounmap((void *)root->base_addr); kfree(root); kfree(ppriv); release_mem_region(pci_resource_start(pdev, 1), pci_resource_len(pdev, 1)); release_mem_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));}static int dscc4_hdlc_ioctl(struct hdlc_device_struct *hdlc, struct ifreq *ifr, int cmd){ struct net_device *dev = (struct net_device *)hdlc->netdev.base_addr; int result; /* FIXME: locking ? */ result = dscc4_ioctl(dev, ifr, cmd); return result;}static int dscc4_hdlc_open(struct hdlc_device_struct *hdlc){ struct net_device *dev = (struct net_device *)(hdlc->netdev.base_addr); if (netif_running(dev)) { printk(KERN_DEBUG "%s: already running\n", dev->name); // DEBUG return 0; } return dscc4_open(dev);}static int dscc4_hdlc_xmit(hdlc_device *hdlc, struct sk_buff *skb){ struct net_device *dev = (struct net_device *)hdlc->netdev.base_addr; return dscc4_start_xmit(skb, dev);}static void dscc4_hdlc_close(struct hdlc_device_struct *hdlc){ struct net_device *dev = (struct net_device *)hdlc->netdev.base_addr; struct dscc4_dev_priv *dpriv; dpriv = dev->priv; --dpriv->usecount;}/* Operated under dev lock */static int dscc4_attach_hdlc_device(struct net_device *dev) { struct dscc4_dev_priv *dpriv = dev->priv; struct hdlc_device_struct *hdlc; int result; hdlc = &dpriv->hdlc; /* XXX: Don't look at the next line */ hdlc->netdev.base_addr = (unsigned long)dev; hdlc->set_mode = NULL; hdlc->open = dscc4_hdlc_open; hdlc->close = dscc4_hdlc_close; hdlc->ioctl = dscc4_hdlc_ioctl; hdlc->xmit = dscc4_hdlc_xmit; result = register_hdlc_device(hdlc); if (!result) dpriv->usecount++; return result;}/* Operated under dev lock */static void dscc4_unattach_hdlc_device(struct net_device *dev) { struct dscc4_dev_priv *dpriv = dev->priv; unregister_hdlc_device(&dpriv->hdlc); dpriv->usecount--;}static struct pci_device_id dscc4_pci_tbl[] __devinitdata = { { PCI_VENDOR_ID_SIEMENS, PCI_DEVICE_ID_SIEMENS_DSCC4, PCI_ANY_ID, PCI_ANY_ID, }, { 0,}};MODULE_DEVICE_TABLE(pci, dscc4_pci_tbl);static struct pci_driver dscc4_driver = { name: "dscc4", id_table: dscc4_pci_tbl, probe: dscc4_init_one, remove: dscc4_remove_one,};static int __init dscc4_init_module(void){ return pci_module_init(&dscc4_driver);}static void __exit dscc4_cleanup_module(void){ pci_unregister_driver(&dscc4_driver);}module_init(dscc4_init_module);module_exit(dscc4_cleanup_module);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -