📄 lance.c
字号:
if (lance_debug > 2) printk("%s: LANCE open after %d ticks, init block %#x csr0 %4.4x.\n", dev->name, i, (int) &lp->init_block, inw(ioaddr+LANCE_DATA)); return 0; /* Always succeed */}/* Initialize the LANCE Rx and Tx rings. */static voidlance_init_ring(struct device *dev){ struct lance_private *lp = (struct lance_private *)dev->priv; int i; lp->cur_rx = lp->cur_tx = 0; lp->dirty_rx = lp->dirty_tx = 0; for (i = 0; i < RX_RING_SIZE; i++) { lp->rx_ring[i].base = (lp->rx_buffs + i*PKT_BUF_SZ) | 0x80000000; lp->rx_ring[i].buf_length = -PKT_BUF_SZ; } /* 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->init_block.mode = 0x0000; for (i = 0; i < 6; i++) lp->init_block.phys_addr[i] = dev->dev_addr[i]; lp->init_block.filter[0] = 0x00000000; lp->init_block.filter[1] = 0x00000000; lp->init_block.rx_ring = (int)lp->rx_ring | RX_RING_LEN_BITS; lp->init_block.tx_ring = (int)lp->tx_ring | TX_RING_LEN_BITS;}static intlance_start_xmit(struct sk_buff *skb, struct device *dev){ struct lance_private *lp = (struct lance_private *)dev->priv; int ioaddr = dev->base_addr; int entry; /* Transmitter timeout, serious problems. */ if (dev->tbusy) { int tickssofar = jiffies - dev->trans_start; if (tickssofar < 10) return 1; outw(0, ioaddr+LANCE_ADDR); printk("%s: transmit timed out, status %4.4x, resetting.\n", dev->name, inw(ioaddr+LANCE_DATA)); outw(0x0001, ioaddr+LANCE_DATA); lp->stats.tx_errors++;#ifndef final_version { int i; printk(" Ring data dump: dirty_tx %d cur_tx %d cur_rx %d.", lp->dirty_tx, lp->cur_tx, lp->cur_rx); for (i = 0 ; i < RX_RING_SIZE; i++) printk("%s %08x %04x %04x", i & 0x3 ? "" : "\n ", lp->rx_ring[i].base, -lp->rx_ring[i].buf_length, lp->rx_ring[i].msg_length); for (i = 0 ; i < TX_RING_SIZE; i++) printk(" %s%08x %04x %04x", i & 0x3 ? "" : "\n ", lp->tx_ring[i].base, -lp->tx_ring[i].length, lp->tx_ring[i].misc); printk("\n"); }#endif lance_init_ring(dev); outw(0x0043, ioaddr+LANCE_DATA); dev->tbusy=0; dev->trans_start = jiffies; return 0; } if (skb == NULL) { dev_tint(dev); return 0; } /* Fill in the ethernet header. */ if (!skb->arp && dev->rebuild_header(skb->data, dev)) { skb->dev = dev; arp_queue (skb); return 0; } skb->arp=1; if (skb->len <= 0) return 0; if (lance_debug > 3) { outw(0x0000, ioaddr+LANCE_ADDR); printk("%s: lance_start_xmit() called, csr0 %4.4x.\n", dev->name, inw(ioaddr+LANCE_DATA)); outw(0x0000, ioaddr+LANCE_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 (set_bit(0, (void*)&dev->tbusy) != 0) printk("%s: Transmitter access conflict.\n", dev->name); /* 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. */ /* The old LANCE chips doesn't automatically pad buffers to min. size. */ if (lp->old_lance) { lp->tx_ring[entry].length = -(ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN); } else lp->tx_ring[entry].length = -skb->len; lp->tx_ring[entry].misc = 0x0000; /* If any part of this buffer is >16M we must copy it to a low-memory buffer. */ if ((int)(skb->data) + skb->len > 0x01000000) { if (lance_debug > 5) printk("%s: bouncing a high-memory packet (%#x).\n", dev->name, (int)(skb->data)); memcpy(&lp->tx_bounce_buffs[entry], skb->data, skb->len); lp->tx_ring[entry].base = (int)(lp->tx_bounce_buffs + entry) | 0x83000000; if (skb->free) kfree_skb (skb, FREE_WRITE); } else { /* We can't free the packet yet, so we inform the memory management code that we are still using it. */ if(skb->free==0) skb_kept_by_device(skb); lp->tx_ring[entry].base = (int)(skb->data) | 0x83000000; } lp->cur_tx++; /* Trigger an immediate send poll. */ outw(0x0000, ioaddr+LANCE_ADDR); outw(0x0048, ioaddr+LANCE_DATA); dev->trans_start = jiffies; if (lp->tx_ring[(entry+1) & TX_RING_MOD_MASK].base == 0) dev->tbusy=0; return 0;}/* The LANCE interrupt handler. */static voidlance_interrupt(int reg_ptr){ int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2); struct device *dev = (struct device *)(irq2dev_map[irq]); struct lance_private *lp; int csr0, ioaddr; if (dev == NULL) { printk ("lance_interrupt(): irq %d for unknown device.\n", irq); return; } ioaddr = dev->base_addr; lp = (struct lance_private *)dev->priv; if (dev->interrupt) printk("%s: Re-entering the interrupt handler.\n", dev->name); dev->interrupt = 1; outw(0x00, dev->base_addr + LANCE_ADDR); csr0 = inw(dev->base_addr + LANCE_DATA); /* Acknowledge all of the current interrupt sources ASAP. */ outw(csr0 & ~0x004f, dev->base_addr + LANCE_DATA); if (lance_debug > 5) printk("%s: interrupt csr0=%#2.2x new csr=%#2.2x.\n", dev->name, csr0, inw(dev->base_addr + LANCE_DATA)); if (csr0 & 0x0400) /* Rx interrupt */ lance_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; int status = lp->tx_ring[entry].base; void *databuff; if (status < 0) break; /* It still hasn't been Txed */ lp->tx_ring[entry].base = 0; databuff = (void*)(status & 0x00ffffff); if (status & 0x40000000) { /* There was an major error, log it. */ int err_status = lp->tx_ring[entry].misc; lp->stats.tx_errors++; if (err_status & 0x0400) lp->stats.tx_aborted_errors++; if (err_status & 0x0800) lp->stats.tx_carrier_errors++; if (err_status & 0x1000) lp->stats.tx_window_errors++; if (err_status & 0x4000) lp->stats.tx_fifo_errors++; /* Perhaps we should re-init() after the FIFO error. */ } else { if (status & 0x18000000) lp->stats.collisions++; lp->stats.tx_packets++; } /* We don't free the skb if it's a data-only copy in the bounce buffer. The address checks here are sorted -- the first test should always work. */ if (databuff >= (void*)(&lp->tx_bounce_buffs[TX_RING_SIZE]) || databuff < (void*)(lp->tx_bounce_buffs)) { struct sk_buff *skb = ((struct sk_buff *)databuff) - 1; if (skb->free) kfree_skb(skb, FREE_WRITE); else skb_device_release(skb,FREE_WRITE); /* Warning: skb may well vanish at the point you call device_release! */ } dirty_tx++; }#ifndef final_version if (lp->cur_tx - dirty_tx >= TX_RING_SIZE) { printk("out-of-sync dirty pointer, %d vs. %d.\n", dirty_tx, lp->cur_tx); dirty_tx += TX_RING_SIZE; }#endif if (dev->tbusy && dirty_tx > lp->cur_tx - TX_RING_SIZE + 2) { /* The ring is no longer full, clear tbusy. */ dev->tbusy = 0; mark_bh(INET_BH); } lp->dirty_tx = dirty_tx; } if (csr0 & 0x8000) { if (csr0 & 0x4000) lp->stats.tx_errors++; if (csr0 & 0x1000) lp->stats.rx_errors++; } /* Clear the interrupts we've handled. */ outw(0x0000, dev->base_addr + LANCE_ADDR); outw(0x7f40, dev->base_addr + LANCE_DATA); if (lance_debug > 4) printk("%s: exiting interrupt, csr%d=%#4.4x.\n", dev->name, inw(ioaddr + LANCE_ADDR), inw(dev->base_addr + LANCE_DATA)); dev->interrupt = 0; return;}static intlance_rx(struct device *dev){ struct lance_private *lp = (struct lance_private *)dev->priv; int entry = lp->cur_rx & RX_RING_MOD_MASK; /* If we own the next entry, it's a new packet. Send it up. */ while (lp->rx_ring[entry].base >= 0) { int status = lp->rx_ring[entry].base >> 24; if (status != 0x03) { /* There was an error. */ /* There is an tricky error noted by John Murphy, <murf@perftech.com> to Russ Nelson: Even with full-sized buffers it's possible for a jabber packet to use two buffers, with only the last correctly noting the error. */ if (status & 0x01) /* Only count a general error at the */ lp->stats.rx_errors++; /* end of a packet.*/ if (status & 0x20) lp->stats.rx_frame_errors++; if (status & 0x10) lp->stats.rx_over_errors++; if (status & 0x08) lp->stats.rx_crc_errors++; if (status & 0x04) lp->stats.rx_fifo_errors++; } else { /* Malloc up new buffer, compatible with net-2e. */ short pkt_len = lp->rx_ring[entry].msg_length; int sksize = sizeof(struct sk_buff) + pkt_len; struct sk_buff *skb; skb = alloc_skb(sksize, GFP_ATOMIC); if (skb == NULL) { printk("%s: Memory squeeze, deferring packet.\n", dev->name); lp->stats.rx_dropped++; /* Really, deferred. */ break; } skb->mem_len = sksize; skb->mem_addr = skb; skb->len = pkt_len; skb->dev = dev; memcpy(skb->data, (unsigned char *)(lp->rx_ring[entry].base & 0x00ffffff), pkt_len);#ifdef HAVE_NETIF_RX netif_rx(skb);#else skb->lock = 0; if (dev_rint((unsigned char*)skb, pkt_len, IN_SKBUFF, dev) != 0) { kfree_skbmem(skb, sksize); lp->stats.rx_dropped++; break; }#endif lp->stats.rx_packets++; } lp->rx_ring[entry].base |= 0x80000000; entry = (++lp->cur_rx) & RX_RING_MOD_MASK; } /* We should check that at least two ring entries are free. If not, we should free one and mark stats->rx_dropped++. */ return 0;}static intlance_close(struct device *dev){ int ioaddr = dev->base_addr; struct lance_private *lp = (struct lance_private *)dev->priv; dev->start = 0; dev->tbusy = 1; outw(112, ioaddr+LANCE_ADDR); lp->stats.rx_missed_errors = inw(ioaddr+LANCE_DATA); outw(0, ioaddr+LANCE_ADDR); if (lance_debug > 1) printk("%s: Shutting down ethercard, status was %2.2x.\n", dev->name, inw(ioaddr+LANCE_DATA)); /* We stop the LANCE here -- it occasionally polls memory if we don't. */ outw(0x0004, ioaddr+LANCE_DATA); disable_dma(dev->dma); free_irq(dev->irq); free_dma(dev->dma); irq2dev_map[dev->irq] = 0; return 0;}static struct enet_statistics *lance_get_stats(struct device *dev){ struct lance_private *lp = (struct lance_private *)dev->priv; short ioaddr = dev->base_addr; short saved_addr; cli(); saved_addr = inw(ioaddr+LANCE_ADDR); outw(112, ioaddr+LANCE_ADDR); lp->stats.rx_missed_errors = inw(ioaddr+LANCE_DATA); outw(saved_addr, ioaddr+LANCE_ADDR); sti(); return &lp->stats;}#ifdef HAVE_MULTICAST/* Set or clear the multicast filter for this adaptor. num_addrs == -1 Promiscuous mode, receive all packets num_addrs == 0 Normal mode, clear multicast list num_addrs > 0 Multicast mode, receive normal and MC packets, and do best-effort filtering. */static voidset_multicast_list(struct device *dev, int num_addrs, void *addrs){ short ioaddr = dev->base_addr; /* We take the simple way out and always enable promiscuous mode. */ outw(0, ioaddr+LANCE_ADDR); outw(0x0004, ioaddr+LANCE_DATA); /* Temporarily stop the lance. */ outw(15, ioaddr+LANCE_ADDR); if (num_addrs >= 0) { short multicast_table[4]; int i; /* We don't use the multicast table, but rely on upper-layer filtering. */ memset(multicast_table, (num_addrs == 0) ? 0 : -1, sizeof(multicast_table)); for (i = 0; i < 4; i++) { outw(8 + i, ioaddr+LANCE_ADDR); outw(multicast_table[i], ioaddr+LANCE_DATA); } outw(0x0000, ioaddr+LANCE_DATA); /* Unset promiscuous mode */ } else { outw(0x8000, ioaddr+LANCE_DATA); /* Set promiscuous mode */ } outw(0, ioaddr+LANCE_ADDR); outw(0x0142, ioaddr+LANCE_DATA); /* Resume normal operation. */}#endif#ifdef HAVE_DEVLISTstatic unsigned int lance_portlist[] = {0x300, 0x320, 0x340, 0x360, 0};struct netdev_entry lance_drv ={"lance", lance_probe1, LANCE_TOTAL_SIZE, lance_portlist};#endif/* * Local variables: * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c lance.c" * End: */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -