?? 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 void
lance_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 int
lance_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 void
lance_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 int
lance_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 int
lance_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 void
set_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_DEVLIST
static 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 + -