sgiseeq.c
来自「linux 内核源代码」· C语言 代码 · 共 756 行 · 第 1/2 页
C
756 行
static inline void sgiseeq_tx(struct net_device *dev, struct sgiseeq_private *sp, struct hpc3_ethregs *hregs, struct sgiseeq_regs *sregs){ struct sgiseeq_tx_desc *td; unsigned long status = hregs->tx_ctrl; int j; tx_maybe_reset_collisions(sp, sregs); if (!(status & (HPC3_ETXCTRL_ACTIVE | SEEQ_TSTAT_PTRANS))) { /* Oops, HPC detected some sort of error. */ if (status & SEEQ_TSTAT_R16) dev->stats.tx_aborted_errors++; if (status & SEEQ_TSTAT_UFLOW) dev->stats.tx_fifo_errors++; if (status & SEEQ_TSTAT_LCLS) dev->stats.collisions++; } /* Ack 'em... */ for (j = sp->tx_old; j != sp->tx_new; j = NEXT_TX(j)) { td = &sp->tx_desc[j]; if (!(td->tdma.cntinfo & (HPCDMA_XIU))) break; if (!(td->tdma.cntinfo & (HPCDMA_ETXD))) { if (!(status & HPC3_ETXCTRL_ACTIVE)) { hregs->tx_ndptr = CPHYSADDR(td); hregs->tx_ctrl = HPC3_ETXCTRL_ACTIVE; } break; } dev->stats.tx_packets++; sp->tx_old = NEXT_TX(sp->tx_old); td->tdma.cntinfo &= ~(HPCDMA_XIU | HPCDMA_XIE); td->tdma.cntinfo |= HPCDMA_EOX; }}static irqreturn_t sgiseeq_interrupt(int irq, void *dev_id){ struct net_device *dev = (struct net_device *) dev_id; struct sgiseeq_private *sp = netdev_priv(dev); struct hpc3_ethregs *hregs = sp->hregs; struct sgiseeq_regs *sregs = sp->sregs; spin_lock(&sp->tx_lock); /* Ack the IRQ and set software state. */ hregs->reset = HPC3_ERST_CLRIRQ; /* Always check for received packets. */ sgiseeq_rx(dev, sp, hregs, sregs); /* Only check for tx acks if we have something queued. */ if (sp->tx_old != sp->tx_new) sgiseeq_tx(dev, sp, hregs, sregs); if ((TX_BUFFS_AVAIL(sp) > 0) && netif_queue_stopped(dev)) { netif_wake_queue(dev); } spin_unlock(&sp->tx_lock); return IRQ_HANDLED;}static int sgiseeq_open(struct net_device *dev){ struct sgiseeq_private *sp = netdev_priv(dev); struct sgiseeq_regs *sregs = sp->sregs; unsigned int irq = dev->irq; int err; if (request_irq(irq, sgiseeq_interrupt, 0, sgiseeqstr, dev)) { printk(KERN_ERR "Seeq8003: Can't get irq %d\n", dev->irq); err = -EAGAIN; } err = init_seeq(dev, sp, sregs); if (err) goto out_free_irq; netif_start_queue(dev); return 0;out_free_irq: free_irq(irq, dev); return err;}static int sgiseeq_close(struct net_device *dev){ struct sgiseeq_private *sp = netdev_priv(dev); struct sgiseeq_regs *sregs = sp->sregs; unsigned int irq = dev->irq; netif_stop_queue(dev); /* Shutdown the Seeq. */ reset_hpc3_and_seeq(sp->hregs, sregs); free_irq(irq, dev); return 0;}static inline int sgiseeq_reset(struct net_device *dev){ struct sgiseeq_private *sp = netdev_priv(dev); struct sgiseeq_regs *sregs = sp->sregs; int err; err = init_seeq(dev, sp, sregs); if (err) return err; dev->trans_start = jiffies; netif_wake_queue(dev); return 0;}static int sgiseeq_start_xmit(struct sk_buff *skb, struct net_device *dev){ struct sgiseeq_private *sp = netdev_priv(dev); struct hpc3_ethregs *hregs = sp->hregs; unsigned long flags; struct sgiseeq_tx_desc *td; int skblen, len, entry; spin_lock_irqsave(&sp->tx_lock, flags); /* Setup... */ skblen = skb->len; len = (skblen <= ETH_ZLEN) ? ETH_ZLEN : skblen; dev->stats.tx_bytes += len; entry = sp->tx_new; td = &sp->tx_desc[entry]; /* Create entry. There are so many races with adding a new * descriptor to the chain: * 1) Assume that the HPC is off processing a DMA chain while * we are changing all of the following. * 2) Do no allow the HPC to look at a new descriptor until * we have completely set up it's state. This means, do * not clear HPCDMA_EOX in the current last descritptor * until the one we are adding looks consistent and could * be processes right now. * 3) The tx interrupt code must notice when we've added a new * entry and the HPC got to the end of the chain before we * added this new entry and restarted it. */ skb_copy_from_linear_data(skb, (char *)(long)td->buf_vaddr, skblen); if (len != skblen) memset((char *)(long)td->buf_vaddr + skb->len, 0, len-skblen); td->tdma.cntinfo = (len & HPCDMA_BCNT) | HPCDMA_XIU | HPCDMA_EOXP | HPCDMA_XIE | HPCDMA_EOX; if (sp->tx_old != sp->tx_new) { struct sgiseeq_tx_desc *backend; backend = &sp->tx_desc[PREV_TX(sp->tx_new)]; backend->tdma.cntinfo &= ~HPCDMA_EOX; } sp->tx_new = NEXT_TX(sp->tx_new); /* Advance. */ /* Maybe kick the HPC back into motion. */ if (!(hregs->tx_ctrl & HPC3_ETXCTRL_ACTIVE)) kick_tx(&sp->tx_desc[sp->tx_old], hregs); dev->trans_start = jiffies; dev_kfree_skb(skb); if (!TX_BUFFS_AVAIL(sp)) netif_stop_queue(dev); spin_unlock_irqrestore(&sp->tx_lock, flags); return 0;}static void timeout(struct net_device *dev){ printk(KERN_NOTICE "%s: transmit timed out, resetting\n", dev->name); sgiseeq_reset(dev); dev->trans_start = jiffies; netif_wake_queue(dev);}static void sgiseeq_set_multicast(struct net_device *dev){ struct sgiseeq_private *sp = (struct sgiseeq_private *) dev->priv; unsigned char oldmode = sp->mode; if(dev->flags & IFF_PROMISC) sp->mode = SEEQ_RCMD_RANY; else if ((dev->flags & IFF_ALLMULTI) || dev->mc_count) sp->mode = SEEQ_RCMD_RBMCAST; else sp->mode = SEEQ_RCMD_RBCAST; /* XXX I know this sucks, but is there a better way to reprogram * XXX the receiver? At least, this shouldn't happen too often. */ if (oldmode != sp->mode) sgiseeq_reset(dev);}static inline void setup_tx_ring(struct sgiseeq_tx_desc *buf, int nbufs){ int i = 0; while (i < (nbufs - 1)) { buf[i].tdma.pnext = CPHYSADDR(buf + i + 1); buf[i].tdma.pbuf = 0; i++; } buf[i].tdma.pnext = CPHYSADDR(buf);}static inline void setup_rx_ring(struct sgiseeq_rx_desc *buf, int nbufs){ int i = 0; while (i < (nbufs - 1)) { buf[i].rdma.pnext = CPHYSADDR(buf + i + 1); buf[i].rdma.pbuf = 0; i++; } buf[i].rdma.pbuf = 0; buf[i].rdma.pnext = CPHYSADDR(buf);}#define ALIGNED(x) ((((unsigned long)(x)) + 0xf) & ~(0xf))static int __init sgiseeq_probe(struct platform_device *pdev){ struct sgiseeq_platform_data *pd = pdev->dev.platform_data; struct hpc3_regs *hpcregs = pd->hpc; struct sgiseeq_init_block *sr; unsigned int irq = pd->irq; struct sgiseeq_private *sp; struct net_device *dev; int err, i; DECLARE_MAC_BUF(mac); dev = alloc_etherdev(sizeof (struct sgiseeq_private)); if (!dev) { printk(KERN_ERR "Sgiseeq: Etherdev alloc failed, aborting.\n"); err = -ENOMEM; goto err_out; } platform_set_drvdata(pdev, dev); sp = netdev_priv(dev); /* Make private data page aligned */ sr = dma_alloc_coherent(&pdev->dev, sizeof(*sp->srings), &sp->srings_dma, GFP_KERNEL); if (!sr) { printk(KERN_ERR "Sgiseeq: Page alloc failed, aborting.\n"); err = -ENOMEM; goto err_out_free_dev; } sp->srings = sr; sp->rx_desc = sp->srings->rxvector; sp->tx_desc = sp->srings->txvector; /* A couple calculations now, saves many cycles later. */ setup_rx_ring(sp->rx_desc, SEEQ_RX_BUFFERS); setup_tx_ring(sp->tx_desc, SEEQ_TX_BUFFERS); memcpy(dev->dev_addr, pd->mac, ETH_ALEN);#ifdef DEBUG gpriv = sp; gdev = dev;#endif sp->sregs = (struct sgiseeq_regs *) &hpcregs->eth_ext[0]; sp->hregs = &hpcregs->ethregs; sp->name = sgiseeqstr; sp->mode = SEEQ_RCMD_RBCAST; /* Setup PIO and DMA transfer timing */ sp->hregs->pconfig = 0x161; sp->hregs->dconfig = HPC3_EDCFG_FIRQ | HPC3_EDCFG_FEOP | HPC3_EDCFG_FRXDC | HPC3_EDCFG_PTO | 0x026; /* Setup PIO and DMA transfer timing */ sp->hregs->pconfig = 0x161; sp->hregs->dconfig = HPC3_EDCFG_FIRQ | HPC3_EDCFG_FEOP | HPC3_EDCFG_FRXDC | HPC3_EDCFG_PTO | 0x026; /* Reset the chip. */ hpc3_eth_reset(sp->hregs); sp->is_edlc = !(sp->sregs->rw.rregs.collision_tx[0] & 0xff); if (sp->is_edlc) sp->control = SEEQ_CTRL_XCNT | SEEQ_CTRL_ACCNT | SEEQ_CTRL_SFLAG | SEEQ_CTRL_ESHORT | SEEQ_CTRL_ENCARR; dev->open = sgiseeq_open; dev->stop = sgiseeq_close; dev->hard_start_xmit = sgiseeq_start_xmit; dev->tx_timeout = timeout; dev->watchdog_timeo = (200 * HZ) / 1000; dev->set_multicast_list = sgiseeq_set_multicast; dev->set_mac_address = sgiseeq_set_mac_address; dev->irq = irq; if (register_netdev(dev)) { printk(KERN_ERR "Sgiseeq: Cannot register net device, " "aborting.\n"); err = -ENODEV; goto err_out_free_page; } printk(KERN_INFO "%s: %s %s\n", dev->name, sgiseeqstr, print_mac(mac, dev->dev_addr)); return 0;err_out_free_page: free_page((unsigned long) sp->srings);err_out_free_dev: kfree(dev);err_out: return err;}static int __exit sgiseeq_remove(struct platform_device *pdev){ struct net_device *dev = platform_get_drvdata(pdev); struct sgiseeq_private *sp = netdev_priv(dev); unregister_netdev(dev); dma_free_coherent(&pdev->dev, sizeof(*sp->srings), sp->srings, sp->srings_dma); free_netdev(dev); platform_set_drvdata(pdev, NULL); return 0;}static struct platform_driver sgiseeq_driver = { .probe = sgiseeq_probe, .remove = __devexit_p(sgiseeq_remove), .driver = { .name = "sgiseeq" }};static int __init sgiseeq_module_init(void){ if (platform_driver_register(&sgiseeq_driver)) { printk(KERN_ERR "Driver registration failed\n"); return -ENODEV; } return 0;}static void __exit sgiseeq_module_exit(void){ platform_driver_unregister(&sgiseeq_driver);}module_init(sgiseeq_module_init);module_exit(sgiseeq_module_exit);MODULE_DESCRIPTION("SGI Seeq 8003 driver");MODULE_AUTHOR("Linux/MIPS Mailing List <linux-mips@linux-mips.org>");MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?