⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pcnet32.c

📁 powerpc内核mpc8241linux系统下net驱动程序
💻 C
📖 第 1 页 / 共 3 页
字号:
    outw(0x0002, ioaddr+PCNET32_BUS_IF);    outw(0x0001, ioaddr+PCNET32_ADDR);    inw(ioaddr+PCNET32_ADDR);    outw(virt_to_bus(&lp->init_block) & 0xffff, ioaddr+PCNET32_DATA);    outw(0x0002, ioaddr+PCNET32_ADDR);    inw(ioaddr+PCNET32_ADDR);    outw(virt_to_bus(&lp->init_block) >> 16, ioaddr+PCNET32_DATA);    outw(0x0000, ioaddr+PCNET32_ADDR);    inw(ioaddr+PCNET32_ADDR);        if (irq_line) {	dev->irq = irq_line;    }        if (dev->irq >= 2)      printk(" assigned IRQ %d.\n", dev->irq);    else {	unsigned long irq_mask = probe_irq_on();		/*	 * To auto-IRQ we enable the initialization-done and DMA error	 * interrupts. For ISA boards we get a DMA error, but VLB and PCI	 * boards will work.	 */	/* Trigger an initialization just for the interrupt. */	outw(0x0041, ioaddr+PCNET32_DATA);	mdelay (1);		dev->irq = probe_irq_off (irq_mask);	if (dev->irq)	  printk(", probed IRQ %d.\n", dev->irq);	else {	    printk(", failed to detect IRQ line.\n");	    return ENODEV;	}    }    if (pcnet32_debug > 0)      printk(version);        /* The PCNET32-specific entries in the device structure. */    dev->open = &pcnet32_open;    dev->hard_start_xmit = &pcnet32_start_xmit;    dev->stop = &pcnet32_close;    dev->get_stats = &pcnet32_get_stats;    dev->set_multicast_list = &pcnet32_set_multicast_list;    #ifdef MODULE    lp->next = pcnet32_dev;    pcnet32_dev = dev;#endif	    /* Fill in the generic fields of the device structure. */    ether_setup(dev);    return 0;}static intpcnet32_open(struct device *dev){	struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv;	unsigned int ioaddr = dev->base_addr;        unsigned short val;	int i;	if (dev->irq == 0 ||		request_irq(dev->irq, &pcnet32_interrupt,			    lp->shared_irq ? SA_SHIRQ : 0, lp->name, (void *)dev)) {		return -EAGAIN;	}	/* Reset the PCNET32 */	inw(ioaddr+PCNET32_RESET);	/* switch pcnet32 to 32bit mode */	outw(0x0014, ioaddr+PCNET32_ADDR);	outw(0x0002, ioaddr+PCNET32_BUS_IF);	if (pcnet32_debug > 1)		printk("%s: pcnet32_open() irq %d tx/rx rings %#x/%#x init %#x.\n",			   dev->name, dev->irq,		           (u32) virt_to_bus(lp->tx_ring),		           (u32) virt_to_bus(lp->rx_ring),			   (u32) virt_to_bus(&lp->init_block));            /* set/reset autoselect bit */    	outw(0x0002, ioaddr+PCNET32_ADDR);        val = inw(ioaddr+PCNET32_BUS_IF) & ~2;        if (lp->options & PORT_ASEL)	        val |= 2;        outw(val, ioaddr+PCNET32_BUS_IF);        /* handle full duplex setting */        if (lp->full_duplex) {	    outw (0x0009, ioaddr+PCNET32_ADDR);	    val = inw(ioaddr+PCNET32_BUS_IF) & ~3;	    if (lp->options & PORT_FD) {		val |= 1;		if (lp->options == (PORT_FD | PORT_AUI))		    val |= 2;	    }	    outw(val, ioaddr+PCNET32_BUS_IF);	}            /* set/reset GPSI bit in test register */        outw (0x007c, ioaddr+PCNET32_ADDR);        val = inw(ioaddr+PCNET32_DATA) & ~0x10;        if ((lp->options & PORT_PORTSEL) == PORT_GPSI)	        val |= 0x10;    	outw(val, ioaddr+PCNET32_DATA);	    	lp->init_block.mode = le16_to_cpu((lp->options & PORT_PORTSEL) << 7);	lp->init_block.filter[0] = 0x00000000;	lp->init_block.filter[1] = 0x00000000;	if (pcnet32_init_ring(dev))		return -ENOMEM;    	/* Re-initialize the PCNET32, and start it when done. */	outw(0x0001, ioaddr+PCNET32_ADDR);	outw(virt_to_bus(&lp->init_block) &0xffff, ioaddr+PCNET32_DATA);	outw(0x0002, ioaddr+PCNET32_ADDR);	outw(virt_to_bus(&lp->init_block) >> 16, ioaddr+PCNET32_DATA);	outw(0x0004, ioaddr+PCNET32_ADDR);	outw(0x0915, ioaddr+PCNET32_DATA);	outw(0x0000, ioaddr+PCNET32_ADDR);	outw(0x0001, ioaddr+PCNET32_DATA);	dev->tbusy = 0;	dev->interrupt = 0;	dev->start = 1;	i = 0;	while (i++ < 100)		if (inw(ioaddr+PCNET32_DATA) & 0x0100)			break;	/* 	 * We used to clear the InitDone bit, 0x0100, here but Mark Stockton	 * reports that doing so triggers a bug in the '974.	 */ 	outw(0x0042, ioaddr+PCNET32_DATA);	if (pcnet32_debug > 2)		printk("%s: PCNET32 open after %d ticks, init block %#x csr0 %4.4x.\n",			   dev->name, i, (u32) virt_to_bus(&lp->init_block), inw(ioaddr+PCNET32_DATA));        MOD_INC_USE_COUNT;    	return 0;					/* Always succeed */}/* * The LANCE has been halted for one reason or another (busmaster memory * arbitration error, Tx FIFO underflow, driver stopped it to reconfigure, * etc.).  Modern LANCE variants always reload their ring-buffer * configuration when restarted, so we must reinitialize our ring * context before restarting.  As part of this reinitialization, * find all packets still on the Tx ring and pretend that they had been * sent (in effect, drop the packets on the floor) - the higher-level * protocols will time out and retransmit.  It'd be better to shuffle * these skbs to a temp list and then actually re-Tx them after * restarting the chip, but I'm too lazy to do so right now.  dplatt@3do.com */static void pcnet32_purge_tx_ring(struct device *dev){	struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv;	int i;	for (i = 0; i < TX_RING_SIZE; i++) {		if (lp->tx_skbuff[i]) {			dev_kfree_skb(lp->tx_skbuff[i]);			lp->tx_skbuff[i] = NULL;		}	}}/* Initialize the PCNET32 Rx and Tx rings. */static intpcnet32_init_ring(struct device *dev){	struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv;	int i;	lp->tx_full = 0;	lp->cur_rx = lp->cur_tx = 0;	lp->dirty_rx = lp->dirty_tx = 0;	for (i = 0; i < RX_RING_SIZE; i++) {	    if (lp->rx_skbuff[i] == NULL) {		if (!(lp->rx_skbuff[i] = dev_alloc_skb (PKT_BUF_SZ))) {		    /* there is not much, we can do at this point */		    printk ("%s: pcnet32_init_ring dev_alloc_skb failed.\n",dev->name);		    return -1;		}		skb_reserve (lp->rx_skbuff[i], 2);	    }	    lp->rx_ring[i].base = (u32)le32_to_cpu(virt_to_bus(lp->rx_skbuff[i]->tail));	    lp->rx_ring[i].buf_length = le16_to_cpu(-PKT_BUF_SZ);	    lp->rx_ring[i].status = le16_to_cpu(0x8000);	}	/* The Tx buffer address is filled in as needed, but we do need to clear	   the upper ownership bit. */	for (i = 0; i < TX_RING_SIZE; i++) {	        lp->tx_ring[i].base = 0;	        lp->tx_ring[i].status = 0;	}        lp->init_block.tlen_rlen = TX_RING_LEN_BITS | RX_RING_LEN_BITS;	for (i = 0; i < 6; i++)		lp->init_block.phys_addr[i] = dev->dev_addr[i];	lp->init_block.rx_ring = (u32)le32_to_cpu(virt_to_bus(lp->rx_ring));	lp->init_block.tx_ring = (u32)le32_to_cpu(virt_to_bus(lp->tx_ring));	return 0;}static voidpcnet32_restart(struct device *dev, unsigned int csr0_bits){        int i;	unsigned int ioaddr = dev->base_addr;    	pcnet32_purge_tx_ring(dev);	if (pcnet32_init_ring(dev))		return;    	outw(0x0000, ioaddr + PCNET32_ADDR);        /* ReInit Ring */        outw(0x0001, ioaddr + PCNET32_DATA);	i = 0;	while (i++ < 100)		if (inw(ioaddr+PCNET32_DATA) & 0x0100)			break;	outw(csr0_bits, ioaddr + PCNET32_DATA);}static intpcnet32_start_xmit(struct sk_buff *skb, struct device *dev){	struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv;	unsigned int ioaddr = dev->base_addr;	int entry;	unsigned long flags;	/* Transmitter timeout, serious problems. */	if (dev->tbusy) {		int tickssofar = jiffies - dev->trans_start;		if (tickssofar < HZ/2)			return 1;		outw(0, ioaddr+PCNET32_ADDR);		printk("%s: transmit timed out, status %4.4x, resetting.\n",			   dev->name, inw(ioaddr+PCNET32_DATA));		outw(0x0004, ioaddr+PCNET32_DATA);		lp->stats.tx_errors++;		if (pcnet32_debug > 2) {			int i;			printk(" Ring data dump: dirty_tx %d cur_tx %d%s cur_rx %d.",				   lp->dirty_tx, lp->cur_tx, lp->tx_full ? " (full)" : "",				   lp->cur_rx);			for (i = 0 ; i < RX_RING_SIZE; i++)				printk("%s %08x %04x %08x %04x", i & 1 ? "" : "\n ",					   lp->rx_ring[i].base, -lp->rx_ring[i].buf_length,					   lp->rx_ring[i].msg_length, (unsigned)lp->rx_ring[i].status);			for (i = 0 ; i < TX_RING_SIZE; i++)				printk("%s %08x %04x %08x %04x", i & 1 ? "" : "\n ",					   lp->tx_ring[i].base, -lp->tx_ring[i].length,					   lp->tx_ring[i].misc, (unsigned)lp->tx_ring[i].status);			printk("\n");		}		pcnet32_restart(dev, 0x0042);		dev->tbusy = 0;		dev->trans_start = jiffies;		dev_kfree_skb(skb);		return 0;	}	if (pcnet32_debug > 3) {		outw(0x0000, ioaddr+PCNET32_ADDR);		printk("%s: pcnet32_start_xmit() called, csr0 %4.4x.\n", dev->name,			   inw(ioaddr+PCNET32_DATA));	}	/* Block a timer-based transmit from overlapping.  This could better be	   done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */	if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) {		printk("%s: Transmitter access conflict.\n", dev->name);		return 1;	}	save_flags (flags);	cli ();	/* Fill in a Tx ring entry */	/* Mask to ring buffer boundary. */	entry = lp->cur_tx & TX_RING_MOD_MASK;	/* Caution: the write order is important here, set the base address	   with the "ownership" bits last. */	lp->tx_ring[entry].length = le16_to_cpu(-skb->len);	lp->tx_ring[entry].misc = 0x00000000;	lp->tx_skbuff[entry] = skb;	lp->tx_ring[entry].base = (u32)le32_to_cpu(virt_to_bus(skb->data));        lp->tx_ring[entry].status = le16_to_cpu(0x8300);	lp->cur_tx++;        lp->stats.tx_bytes += skb->len;	/* Trigger an immediate send poll. */	outw(0x0000, ioaddr+PCNET32_ADDR);	outw(0x0048, ioaddr+PCNET32_DATA);	dev->trans_start = jiffies;	if (lp->tx_ring[(entry+1) & TX_RING_MOD_MASK].base == 0)		clear_bit (0, (void *)&dev->tbusy);	else		lp->tx_full = 1;	restore_flags(flags);	return 0;}/* The PCNET32 interrupt handler. */static voidpcnet32_interrupt(int irq, void *dev_id, struct pt_regs * regs){	struct device *dev = (struct device *)dev_id;	struct pcnet32_private *lp;	unsigned int csr0, ioaddr;	int boguscnt =  max_interrupt_work;	int must_restart;	if (dev == NULL) {		printk ("pcnet32_interrupt(): irq %d for unknown device.\n", irq);		return;	}	ioaddr = dev->base_addr;	lp = (struct pcnet32_private *)dev->priv;	if (dev->interrupt)		printk("%s: Re-entering the interrupt handler.\n", dev->name);	dev->interrupt = 1;	outw(0x00, dev->base_addr + PCNET32_ADDR);	while ((csr0 = inw(dev->base_addr + PCNET32_DATA)) & 0x8600 && --boguscnt >= 0) {		/* Acknowledge all of the current interrupt sources ASAP. */		outw(csr0 & ~0x004f, dev->base_addr + PCNET32_DATA);		must_restart = 0;		if (pcnet32_debug > 5)			printk("%s: interrupt  csr0=%#2.2x new csr=%#2.2x.\n",				   dev->name, csr0, inw(dev->base_addr + PCNET32_DATA));		if (csr0 & 0x0400)			/* Rx interrupt */			pcnet32_rx(dev);		if (csr0 & 0x0200) {		/* Tx-done interrupt */			int dirty_tx = lp->dirty_tx;			while (dirty_tx < lp->cur_tx) {				int entry = dirty_tx & TX_RING_MOD_MASK;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -