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 + -
显示快捷键?