📄 dscc4.c
字号:
!dscc4_tx_done(dpriv)) dscc4_do_tx(dpriv, dev); return; } loop++; dpriv->iqtx[cur] = 0; dpriv->iqtx_current++; if (state_check(state, dpriv, dev, "Tx") < 0) return; if (state & SccEvt) { if (state & Alls) { struct net_device_stats *stats = hdlc_stats(dev); struct sk_buff *skb; struct TxFD *tx_fd; if (debug > 2) dscc4_tx_print(dev, dpriv, "Alls"); /* * DataComplete can't be trusted for Tx completion. * Cf errata DS5 p.8 */ cur = dpriv->tx_dirty%TX_RING_SIZE; tx_fd = dpriv->tx_fd + cur; skb = dpriv->tx_skbuff[cur]; if (skb) { pci_unmap_single(ppriv->pdev, tx_fd->data, skb->len, PCI_DMA_TODEVICE); if (tx_fd->state & FrameEnd) { stats->tx_packets++; stats->tx_bytes += skb->len; } dev_kfree_skb_irq(skb); dpriv->tx_skbuff[cur] = NULL; ++dpriv->tx_dirty; } else { if (debug > 1) printk(KERN_ERR "%s Tx: NULL skb %d\n", dev->name, cur); } /* * If the driver ends sending crap on the wire, it * will be way easier to diagnose than the (not so) * random freeze induced by null sized tx frames. */ tx_fd->data = tx_fd->next; tx_fd->state = FrameEnd | TO_STATE_TX(2*DUMMY_SKB_SIZE); tx_fd->complete = 0x00000000; tx_fd->jiffies = 0; if (!(state &= ~Alls)) goto try; } /* * Transmit Data Underrun */ if (state & Xdu) { printk(KERN_ERR "%s: XDU. Ask maintainer\n", DRV_NAME); dpriv->flags = NeedIDT; /* Tx reset */ writel(MTFi | Rdt, dpriv->base_addr + 0x0c*dpriv->dev_id + CH0CFG); writel(Action, dpriv->base_addr + GCMDR); return; } if (state & Cts) { printk(KERN_INFO "%s: CTS transition\n", dev->name); if (!(state &= ~Cts)) /* DEBUG */ goto try; } if (state & Xmr) { /* Frame needs to be sent again - FIXME */ printk(KERN_ERR "%s: Xmr. Ask maintainer\n", DRV_NAME); if (!(state &= ~Xmr)) /* DEBUG */ goto try; } if (state & Xpr) { void __iomem *scc_addr; unsigned long ring; int i; /* * - the busy condition happens (sometimes); * - it doesn't seem to make the handler unreliable. */ for (i = 1; i; i <<= 1) { if (!(scc_readl_star(dpriv, dev) & SccBusy)) break; } if (!i) printk(KERN_INFO "%s busy in irq\n", dev->name); scc_addr = dpriv->base_addr + 0x0c*dpriv->dev_id; /* Keep this order: IDT before IDR */ if (dpriv->flags & NeedIDT) { if (debug > 2) dscc4_tx_print(dev, dpriv, "Xpr"); ring = dpriv->tx_fd_dma + (dpriv->tx_dirty%TX_RING_SIZE)* sizeof(struct TxFD); writel(ring, scc_addr + CH0BTDA); dscc4_do_tx(dpriv, dev); writel(MTFi | Idt, scc_addr + CH0CFG); if (dscc4_do_action(dev, "IDT") < 0) goto err_xpr; dpriv->flags &= ~NeedIDT; } if (dpriv->flags & NeedIDR) { ring = dpriv->rx_fd_dma + (dpriv->rx_current%RX_RING_SIZE)* sizeof(struct RxFD); writel(ring, scc_addr + CH0BRDA); dscc4_rx_update(dpriv, dev); writel(MTFi | Idr, scc_addr + CH0CFG); if (dscc4_do_action(dev, "IDR") < 0) goto err_xpr; dpriv->flags &= ~NeedIDR; smp_wmb(); /* Activate receiver and misc */ scc_writel(0x08050008, dpriv, dev, CCR2); } err_xpr: if (!(state &= ~Xpr)) goto try; } if (state & Cd) { if (debug > 0) printk(KERN_INFO "%s: CD transition\n", dev->name); if (!(state &= ~Cd)) /* DEBUG */ goto try; } } else { /* ! SccEvt */ if (state & Hi) {#ifdef DSCC4_POLLING while (!dscc4_tx_poll(dpriv, dev));#endif printk(KERN_INFO "%s: Tx Hi\n", dev->name); state &= ~Hi; } if (state & Err) { printk(KERN_INFO "%s: Tx ERR\n", dev->name); hdlc_stats(dev)->tx_errors++; state &= ~Err; } } goto try;}static void dscc4_rx_irq(struct dscc4_pci_priv *priv, struct dscc4_dev_priv *dpriv){ struct net_device *dev = dscc4_to_dev(dpriv); 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++; if (state_check(state, dpriv, dev, "Rx") < 0) return; if (!(state & SccEvt)){ struct RxFD *rx_fd; if (debug > 4) printk(KERN_DEBUG "%s: Rx ISR = 0x%08x\n", dev->name, state); state &= 0x00ffffff; if (state & Err) { /* Hold or reset */ printk(KERN_DEBUG "%s: Rx ERR\n", dev->name); cur = dpriv->rx_current%RX_RING_SIZE; 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, dev); if (!rx_fd->data) goto try; rx_fd->state1 &= ~Hold; rx_fd->state2 = 0x00000000; rx_fd->end = 0xbabeface; //} goto try; } if (state & Fi) { dscc4_rx_skb(dpriv, dev); goto try; } if (state & Hi ) { /* HI bit */ printk(KERN_INFO "%s: Rx Hi\n", dev->name); state &= ~Hi; goto try; } } else { /* SccEvt */ if (debug > 1) { //FIXME: verifier la presence de tous les evenements static struct { u32 mask; const char *irq_name; } evts[] = { { 0x00008000, "TIN"}, { 0x00000020, "RSC"}, { 0x00000010, "PCE"}, { 0x00000008, "PLLA"}, { 0, NULL} }, *evt; for (evt = evts; evt->irq_name; evt++) { if (state & evt->mask) { printk(KERN_DEBUG "%s: %s\n", dev->name, evt->irq_name); if (!(state &= ~evt->mask)) goto try; } } } else { if (!(state &= ~0x0000c03c)) goto try; } if (state & Cts) { printk(KERN_INFO "%s: CTS transition\n", dev->name); if (!(state &= ~Cts)) /* DEBUG */ goto try; } /* * Receive Data Overflow (FIXME: fscked) */ if (state & Rdo) { struct RxFD *rx_fd; void __iomem *scc_addr; int cur; //if (debug) // dscc4_rx_dump(dpriv); scc_addr = dpriv->base_addr + 0x0c*dpriv->dev_id; scc_patchl(RxActivate, 0, dpriv, dev, CCR2); /* * This has no effect. Why ? * ORed with TxSccRes, one sees the CFG ack (for * the TX part only). */ scc_writel(RxSccRes, dpriv, dev, 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) { hdlc_stats(dev)->rx_over_errors++; rx_fd->state1 |= Hold; rx_fd->state2 = 0x00000000; rx_fd->end = 0xbabeface; } else dscc4_rx_skb(dpriv, dev); } while (1); if (debug > 0) { if (dpriv->flags & RdoSet) printk(KERN_DEBUG "%s: no RDO in Rx data\n", DRV_NAME); }#ifdef DSCC4_RDO_EXPERIMENTAL_RECOVERY /* * FIXME: must the reset be this violent ? */#warning "FIXME: CH0BRDA" 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") < 0) { 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") < 0) { printk(KERN_ERR "%s: RDO recovery failed(%s)\n", dev->name, "IDR"); goto rdo_end; } rdo_end:#endif scc_patchl(0, RxActivate, dpriv, dev, CCR2); goto try; } if (state & Cd) { printk(KERN_INFO "%s: CD transition\n", dev->name); if (!(state &= ~Cd)) /* DEBUG */ goto try; } if (state & Flex) { printk(KERN_DEBUG "%s: Flex. Ttttt...\n", DRV_NAME); if (!(state &= ~Flex)) goto try; } }}/* * I had expected 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). * It failed and locked solid. Thus the introduction of a dummy skb. * Problem is acknowledged in errata sheet DS5. Joy :o/ */static struct sk_buff *dscc4_init_dummy_skb(struct dscc4_dev_priv *dpriv){ struct sk_buff *skb; skb = dev_alloc_skb(DUMMY_SKB_SIZE); if (skb) { int last = dpriv->tx_dirty%TX_RING_SIZE; struct TxFD *tx_fd = dpriv->tx_fd + last; skb->len = DUMMY_SKB_SIZE; memcpy(skb->data, version, strlen(version)%DUMMY_SKB_SIZE); tx_fd->state = FrameEnd | TO_STATE_TX(DUMMY_SKB_SIZE); tx_fd->data = pci_map_single(dpriv->pci_priv->pdev, skb->data, DUMMY_SKB_SIZE, PCI_DMA_TODEVICE); dpriv->tx_skbuff[last] = skb; } return skb;}static int dscc4_init_ring(struct net_device *dev){ struct dscc4_dev_priv *dpriv = dscc4_priv(dev); struct pci_dev *pdev = dpriv->pci_priv->pdev; struct TxFD *tx_fd; struct RxFD *rx_fd; void *ring; int i; ring = pci_alloc_consistent(pdev, RX_TOTAL_SIZE, &dpriv->rx_fd_dma); if (!ring) goto err_out; dpriv->rx_fd = rx_fd = (struct RxFD *) ring; ring = pci_alloc_consistent(pdev, TX_TOTAL_SIZE, &dpriv->tx_fd_dma); if (!ring) goto err_free_dma_rx; dpriv->tx_fd = tx_fd = (struct TxFD *) ring; memset(dpriv->tx_skbuff, 0, sizeof(struct sk_buff *)*TX_RING_SIZE); dpriv->tx_dirty = 0xffffffff; i = dpriv->tx_current = 0; do { tx_fd->state = FrameEnd | TO_STATE_TX(2*DUMMY_SKB_SIZE); tx_fd->complete = 0x00000000; /* FIXME: NULL should be ok - to be tried */ tx_fd->data = dpriv->tx_fd_dma; (tx_fd++)->next = (u32)(dpriv->tx_fd_dma + (++i%TX_RING_SIZE)*sizeof(*tx_fd)); } while (i < TX_RING_SIZE); if (dscc4_init_dummy_skb(dpriv) < 0) goto err_free_dma_tx; memset(dpriv->rx_skbuff, 0, sizeof(struct sk_buff *)*RX_RING_SIZE); i = dpriv->rx_dirty = dpriv->rx_current = 0; do { /* size set by the host. Multiple of 4 bytes please */ rx_fd->state1 = HiDesc; rx_fd->state2 = 0x00000000; rx_fd->end = 0xbabeface; rx_fd->state1 |= TO_STATE_RX(HDLC_MAX_MRU); // FIXME: return value verifiee mais traitement suspect if (try_get_rx_skb(dpriv, dev) >= 0) dpriv->rx_dirty++; (rx_fd++)->next = (u32)(dpriv->rx_fd_dma + (++i%RX_RING_SIZE)*sizeof(*rx_fd)); } while (i < RX_RING_SIZE); return 0;err_free_dma_tx: pci_free_consistent(pdev, TX_TOTAL_SIZE, ring, dpriv->tx_fd_dma);err_free_dma_rx: pci_free_consistent(pdev, RX_TOTAL_SIZE, rx_fd, dpriv->rx_fd_dma);err_out: return -ENOMEM;}static void __devexit dscc4_remove_one(struct pci_dev *pdev){ struct dscc4_pci_priv *ppriv; struct dscc4_dev_priv *root; void __iomem *ioaddr; int i; ppriv = pci_get_drvdata(pdev); root = ppriv->root; ioaddr = root->base_addr; dscc4_pci_reset(pdev, ioaddr); 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 = root + i; dscc4_release_ring(dpriv); 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); } dscc4_free1(pdev); iounmap(ioaddr); pci_release_region(pdev, 1); pci_release_region(pdev, 0); pci_disable_device(pdev);}static int dscc4_hdlc_attach(struct net_device *dev, unsigned short encoding, unsigned short parity){ struct dscc4_dev_priv *dpriv = dscc4_priv(dev); 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 && parity != PARITY_CRC32_PR1_CCITT) return -EINVAL; dpriv->encoding = encoding; dpriv->parity = parity; return 0;}#ifndef MODULEstatic int __init dscc4_setup(char *str){ int *args[] = { &debug, &quartz, NULL }, **p = args; while (*p && (get_option(&str, *p) == 2)) p++; return 1;}__setup("dscc4.setup=", dscc4_setup);#endifstatic struct pci_device_id dscc4_pci_tbl[] = { { 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 = DRV_NAME, .id_table = dscc4_pci_tbl, .probe = dscc4_init_one, .remove = __devexit_p(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 + -