📄 pcnet32.c
字号:
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 + -