?? 3c509.c
字號:
/* Transmitter timeout, serious problems. */ if (dev->tbusy) { int tickssofar = jiffies - dev->trans_start; if (tickssofar < 10) return 1; printk("%s: transmit timed out, tx_status %2.2x status %4.4x.\n", dev->name, inb(ioaddr + TX_STATUS), inw(ioaddr + EL3_STATUS)); dev->trans_start = jiffies; /* Issue TX_RESET and TX_START commands. */ outw(0x5800, ioaddr + EL3_CMD); /* TX_RESET */ outw(0x4800, ioaddr + EL3_CMD); /* TX_START */ dev->tbusy = 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 (el3_debug > 4) { printk("%s: el3_start_xmit(lenght = %ld) called, status %4.4x.\n", dev->name, skb->len, inw(ioaddr + EL3_STATUS)); }#ifndef final_version { /* Error-checking code, delete for 1.00. */ ushort status = inw(ioaddr + EL3_STATUS); if (status & 0x0001 /* IRQ line active, missed one. */ && inw(ioaddr + EL3_STATUS) & 1) { /* Make sure. */ printk("%s: Missed interrupt, status then %04x now %04x" " Tx %2.2x Rx %4.4x.\n", dev->name, status, inw(ioaddr + EL3_STATUS), inb(ioaddr + TX_STATUS), inw(ioaddr + RX_STATUS)); outw(0x7800, ioaddr + EL3_CMD); /* Fake interrupt trigger. */ outw(0x6899, ioaddr + EL3_CMD); /* Ack IRQ */ outw(0x78ff, ioaddr + EL3_CMD); /* Set all status bits visible. */ } }#endif /* Avoid timer-based retransmission conflicts. */ if (set_bit(0, (void*)&dev->tbusy) != 0) printk("%s: Transmitter access conflict.\n", dev->name); else { /* Put out the doubleword header... */ outw(skb->len, ioaddr + TX_FIFO); outw(0x00, ioaddr + TX_FIFO); /* ... and the packet rounded to a doubleword. */ outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2); dev->trans_start = jiffies; if (inw(ioaddr + TX_FREE) > 1536) { dev->tbusy=0; } else /* Interrupt us when the FIFO has room for max-sized packet. */ outw(0x9000 + 1536, ioaddr + EL3_CMD); } if (skb->free) kfree_skb (skb, FREE_WRITE); /* Clear the Tx status stack. */ { short tx_status; int i = 4; while (--i > 0 && (tx_status = inb(ioaddr + TX_STATUS)) > 0) { if (el3_debug > 5) printk(" Tx status %4.4x.\n", tx_status); if (tx_status & 0x38) lp->stats.tx_aborted_errors++; if (tx_status & 0x30) outw(0x5800, ioaddr + EL3_CMD); if (tx_status & 0x3C) outw(0x4800, ioaddr + EL3_CMD); outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */ } } return 0;}/* The EL3 interrupt handler. */static voidel3_interrupt(int reg_ptr){ int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2); struct device *dev = (struct device *)(irq2dev_map[irq]); int ioaddr, status; int i = 0; if (dev == NULL) { printk ("el3_interrupt(): irq %d for unknown device.\n", irq); return; } if (dev->interrupt) printk("%s: Re-entering the interrupt handler.\n", dev->name); dev->interrupt = 1; ioaddr = dev->base_addr; status = inw(ioaddr + EL3_STATUS); if (el3_debug > 4) printk("%s: interrupt, status %4.4x.\n", dev->name, status); while ((status = inw(ioaddr + EL3_STATUS)) & 0x01) { if (status & 0x10) el3_rx(dev); if (status & 0x08) { if (el3_debug > 5) printk(" TX room bit was handled.\n"); /* There's room in the FIFO for a full-sized packet. */ outw(0x6808, ioaddr + EL3_CMD); /* Ack IRQ */ dev->tbusy = 0; mark_bh(INET_BH); } if (status & 0x80) /* Statistics full. */ update_stats(ioaddr, dev); if (++i > 10) { printk("%s: Infinite loop in interrupt, status %4.4x.\n", dev->name, status); /* Clear all interrupts we have handled. */ outw(0x68FF, ioaddr + EL3_CMD); break; } /* Acknowledge the IRQ. */ outw(0x6891, ioaddr + EL3_CMD); /* Ack IRQ */ } if (el3_debug > 4) { printk("%s: exiting interrupt, status %4.4x.\n", dev->name, inw(ioaddr + EL3_STATUS)); } dev->interrupt = 0; return;}static struct enet_statistics *el3_get_stats(struct device *dev){ struct el3_private *lp = (struct el3_private *)dev->priv; sti(); update_stats(dev->base_addr, dev); cli(); return &lp->stats;}/* Update statistics. We change to register window 6, so this should be run single-threaded if the device is active. This is expected to be a rare operation, and it's simpler for the rest of the driver to assume that window 1 is always valid rather than use a special window-state variable. */static void update_stats(int ioaddr, struct device *dev){ struct el3_private *lp = (struct el3_private *)dev->priv; if (el3_debug > 5) printk(" Updating the statistics.\n"); /* Turn off statistics updates while reading. */ outw(0xB000, ioaddr + EL3_CMD); /* Switch to the stats window, and read everything. */ EL3WINDOW(6); lp->stats.tx_carrier_errors += inb(ioaddr + 0); lp->stats.tx_heartbeat_errors += inb(ioaddr + 1); /* Multiple collisions. */ inb(ioaddr + 2); lp->stats.collisions += inb(ioaddr + 3); lp->stats.tx_window_errors += inb(ioaddr + 4); lp->stats.rx_fifo_errors += inb(ioaddr + 5); lp->stats.tx_packets += inb(ioaddr + 6); lp->stats.rx_packets += inb(ioaddr + 7); /* Tx deferrals */ inb(ioaddr + 8); inw(ioaddr + 10); /* Total Rx and Tx octets. */ inw(ioaddr + 12); /* Back to window 1, and turn statistics back on. */ EL3WINDOW(1); outw(0xA800, ioaddr + EL3_CMD); return;}static intel3_rx(struct device *dev){ struct el3_private *lp = (struct el3_private *)dev->priv; int ioaddr = dev->base_addr; short rx_status; if (el3_debug > 5) printk(" In rx_packet(), status %4.4x, rx_status %4.4x.\n", inw(ioaddr+EL3_STATUS), inw(ioaddr+RX_STATUS)); while ((rx_status = inw(ioaddr + RX_STATUS)) > 0) { if (rx_status & 0x4000) { /* Error, update stats. */ short error = rx_status & 0x3800; lp->stats.rx_errors++; switch (error) { case 0x0000: lp->stats.rx_over_errors++; break; case 0x0800: lp->stats.rx_length_errors++; break; case 0x1000: lp->stats.rx_frame_errors++; break; case 0x1800: lp->stats.rx_length_errors++; break; case 0x2000: lp->stats.rx_frame_errors++; break; case 0x2800: lp->stats.rx_crc_errors++; break; } } if ( (! (rx_status & 0x4000)) || ! (rx_status & 0x1000)) { /* Dribble bits are OK. */ short pkt_len = rx_status & 0x7ff; int sksize = sizeof(struct sk_buff) + pkt_len + 3; struct sk_buff *skb; skb = alloc_skb(sksize, GFP_ATOMIC); if (el3_debug > 4) printk(" Receiving packet size %d status %4.4x.\n", pkt_len, rx_status); if (skb != NULL) { skb->mem_len = sksize; skb->mem_addr = skb; skb->len = pkt_len; skb->dev = dev; /* 'skb->data' points to the start of sk_buff data area. */ insl(ioaddr+RX_FIFO, skb->data, (pkt_len + 3) >> 2);#ifdef HAVE_NETIF_RX netif_rx(skb); outw(0x4000, ioaddr + EL3_CMD); /* Rx discard */ continue;#else skb->lock = 0; if (dev_rint((unsigned char *)skb, pkt_len, IN_SKBUFF,dev)== 0){ if (el3_debug > 6) printk(" dev_rint() happy, status %4.4x.\n", inb(ioaddr + EL3_STATUS)); outw(0x4000, ioaddr + EL3_CMD); /* Rx discard */ while (inw(ioaddr + EL3_STATUS) & 0x1000) printk(" Waiting for 3c509 to discard packet, status %x.\n", inw(ioaddr + EL3_STATUS) ); if (el3_debug > 6) printk(" discarded packet, status %4.4x.\n", inb(ioaddr + EL3_STATUS)); continue; } else { printk("%s: receive buffers full.\n", dev->name); kfree_s(skb, sksize); }#endif } else if (el3_debug) printk("%s: Couldn't allocate a sk_buff of size %d.\n", dev->name, sksize); } lp->stats.rx_dropped++; outw(0x4000, ioaddr + EL3_CMD); /* Rx discard */ while (inw(ioaddr + EL3_STATUS) & 0x1000) printk(" Waiting for 3c509 to discard packet, status %x.\n", inw(ioaddr + EL3_STATUS) ); } if (el3_debug > 5) printk(" Exiting rx_packet(), status %4.4x, rx_status %4.4x.\n", inw(ioaddr+EL3_STATUS), inw(ioaddr+8)); return 0;}#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; if (num_addrs > 0) { outw(0x8007, ioaddr + EL3_CMD); } else if (num_addrs < 0) { outw(0x8008, ioaddr + EL3_CMD); } else outw(0x8005, ioaddr + EL3_CMD);}#endifstatic intel3_close(struct device *dev){ int ioaddr = dev->base_addr; if (el3_debug > 2) printk("%s: Shutting down ethercard.\n", dev->name); dev->tbusy = 1; dev->start = 0; /* Turn off statistics. We update lp->stats below. */ outw(0xB000, ioaddr + EL3_CMD); /* Disable the receiver and transmitter. */ outw(0x1800, ioaddr + EL3_CMD); outw(0x5000, ioaddr + EL3_CMD); if (dev->if_port == 3) /* Turn off thinnet power. */ outw(0xb800, ioaddr + EL3_CMD); else if (dev->if_port == 0) { /* Disable link beat and jabber, if_port may change ere next open(). */ EL3WINDOW(4); outw(inw(ioaddr + WN4_MEDIA) & ~MEDIA_TP, ioaddr + WN4_MEDIA); } free_irq(dev->irq); /* Switching back to window 0 disables the IRQ. */ EL3WINDOW(0); /* But we explicitly zero the IRQ line select anyway. */ outw(0x0f00, ioaddr + 8); irq2dev_map[dev->irq] = 0; update_stats(ioaddr, dev); return 0;}/* * Local variables: * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c 3c509.c" * version-control: t * kept-new-versions: 5 * tab-width: 4 * End: */
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -