📄 3c509.c
字号:
EL3WINDOW(1); /* Accept b-case and phys addr only. */ outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD); outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */ dev->interrupt = 0; dev->tbusy = 0; dev->start = 1; outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */ outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */ /* Allow status bits to be seen. */ outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD); /* Ack all pending events, and set active indicator mask. */ outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq, ioaddr + EL3_CMD); outw(SetIntrEnb | IntLatch|TxAvailable|TxComplete|RxComplete|StatsFull, ioaddr + EL3_CMD); if (el3_debug > 3) printk("%s: Opened 3c509 IRQ %d status %4.4x.\n", dev->name, dev->irq, inw(ioaddr + EL3_STATUS)); MOD_INC_USE_COUNT; return 0; /* Always succeed */}static intel3_start_xmit(struct sk_buff *skb, struct device *dev){ struct el3_private *lp = (struct el3_private *)dev->priv; int ioaddr = dev->base_addr; /* Transmitter timeout, serious problems. */ if (dev->tbusy) { int tickssofar = jiffies - dev->trans_start; if (tickssofar < TX_TIMEOUT) return 1; printk("%s: transmit timed out, Tx_status %2.2x status %4.4x " "Tx FIFO room %d.\n", dev->name, inb(ioaddr + TX_STATUS), inw(ioaddr + EL3_STATUS), inw(ioaddr + TX_FREE)); lp->stats.tx_errors++; dev->trans_start = jiffies; /* Issue TX_RESET and TX_START commands. */ outw(TxReset, ioaddr + EL3_CMD); outw(TxEnable, ioaddr + EL3_CMD); dev->tbusy = 0; } lp->stats.tx_bytes += skb->len; if (el3_debug > 4) { printk("%s: el3_start_xmit(length = %u) called, status %4.4x.\n", dev->name, skb->len, inw(ioaddr + EL3_STATUS)); }#if 0#ifndef final_version { /* Error-checking code, delete someday. */ 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)); /* Fake interrupt trigger by masking, acknowledge interrupts. */ outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD); outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq, ioaddr + EL3_CMD); outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD); } }#endif#endif /* Avoid timer-based retransmission conflicts. */ if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) printk("%s: Transmitter access conflict.\n", dev->name); else { /* * We lock the driver against other processors. Note * we don't need to lock versus the IRQ as we suspended * that. This means that we lose the ability to take * an RX during a TX upload. That sucks a bit with SMP * on an original 3c509 (2K buffer) * * Using disable_irq stops us crapping on other * time sensitive devices. */#ifdef __SMP__ disable_irq(dev->irq); spin_lock(&lp->lock);#endif /* Put out the doubleword header... */ outw(skb->len, ioaddr + TX_FIFO); outw(0x00, ioaddr + TX_FIFO); /* ... and the packet rounded to a doubleword. */#ifdef __powerpc__ outsl_unswapped(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);#else outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);#endif 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(SetTxThreshold + 1536, ioaddr + EL3_CMD);#ifdef __SMP__ spin_unlock(&lp->lock); enable_irq(dev->irq);#endif } dev_kfree_skb (skb); /* Clear the Tx status stack. */ { short tx_status; int i = 4; while (--i > 0 && (tx_status = inb(ioaddr + TX_STATUS)) > 0) { if (tx_status & 0x38) lp->stats.tx_aborted_errors++; if (tx_status & 0x30) outw(TxReset, ioaddr + EL3_CMD); if (tx_status & 0x3C) outw(TxEnable, ioaddr + EL3_CMD); outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */ } } return 0;}/* The EL3 interrupt handler. */static voidel3_interrupt(int irq, void *dev_id, struct pt_regs *regs){ struct device *dev = (struct device *)dev_id; struct el3_private *lp; int ioaddr, status; int i = max_interrupt_work; if (dev == NULL) { printk ("el3_interrupt(): irq %d for unknown device.\n", irq); return; } lp = (struct el3_private *)dev->priv; spin_lock(&lp->lock); if (dev->interrupt) printk("%s: Re-entering the interrupt handler.\n", dev->name); dev->interrupt = 1; ioaddr = dev->base_addr; if (el3_debug > 4) { status = inw(ioaddr + EL3_STATUS); printk("%s: interrupt, status %4.4x.\n", dev->name, status); } while ((status = inw(ioaddr + EL3_STATUS)) & (IntLatch | RxComplete | StatsFull)) { if (status & RxComplete) el3_rx(dev); if (status & TxAvailable) { if (el3_debug > 5) printk(" TX room bit was handled.\n"); /* There's room in the FIFO for a full-sized packet. */ outw(AckIntr | TxAvailable, ioaddr + EL3_CMD); dev->tbusy = 0; mark_bh(NET_BH); } if (status & (AdapterFailure | RxEarly | StatsFull | TxComplete)) { /* Handle all uncommon interrupts. */ if (status & StatsFull) /* Empty statistics. */ update_stats(dev); if (status & RxEarly) { /* Rx early is unused. */ el3_rx(dev); outw(AckIntr | RxEarly, ioaddr + EL3_CMD); } if (status & TxComplete) { /* Really Tx error. */ struct el3_private *lp = (struct el3_private *)dev->priv; short tx_status; int i = 4; while (--i>0 && (tx_status = inb(ioaddr + TX_STATUS)) > 0) { if (tx_status & 0x38) lp->stats.tx_aborted_errors++; if (tx_status & 0x30) outw(TxReset, ioaddr + EL3_CMD); if (tx_status & 0x3C) outw(TxEnable, ioaddr + EL3_CMD); outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */ } } if (status & AdapterFailure) { /* Adapter failure requires Rx reset and reinit. */ outw(RxReset, ioaddr + EL3_CMD); /* Set the Rx filter to the current state. */ outw(SetRxFilter | RxStation | RxBroadcast | (dev->flags & IFF_ALLMULTI ? RxMulticast : 0) | (dev->flags & IFF_PROMISC ? RxProm : 0), ioaddr + EL3_CMD); outw(RxEnable, ioaddr + EL3_CMD); /* Re-enable the receiver. */ outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD); } } if (--i < 0) { printk("%s: Infinite loop in interrupt, status %4.4x.\n", dev->name, status); /* Clear all interrupts. */ outw(AckIntr | 0xFF, ioaddr + EL3_CMD); break; } /* Acknowledge the IRQ. */ outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD); /* Ack IRQ */ } if (el3_debug > 4) { printk("%s: exiting interrupt, status %4.4x.\n", dev->name, inw(ioaddr + EL3_STATUS)); } spin_unlock(&lp->lock); dev->interrupt = 0; return;}static struct enet_statistics *el3_get_stats(struct device *dev){ struct el3_private *lp = (struct el3_private *)dev->priv; unsigned long flags; /* * This is fast enough not to bother with disable IRQ * stuff. */ spin_lock_irqsave(&lp->lock, flags); update_stats(dev); spin_unlock_irqrestore(&lp->lock, flags); 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(struct device *dev){ struct el3_private *lp = (struct el3_private *)dev->priv; int ioaddr = dev->base_addr; if (el3_debug > 5) printk(" Updating the statistics.\n"); /* Turn off statistics updates while reading. */ outw(StatsDisable, 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); /* 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(StatsEnable, 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; outw(RxDiscard, ioaddr + EL3_CMD); 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; } } else { short pkt_len = rx_status & 0x7ff; struct sk_buff *skb; skb = dev_alloc_skb(pkt_len+5); lp->stats.rx_bytes += pkt_len; if (el3_debug > 4) printk("Receiving packet size %d status %4.4x.\n", pkt_len, rx_status); if (skb != NULL) { skb->dev = dev; skb_reserve(skb, 2); /* Align IP on 16 byte */ /* 'skb->data' points to the start of sk_buff data area. */#ifdef __powerpc__ insl_unswapped(ioaddr+RX_FIFO, skb_put(skb,pkt_len), (pkt_len + 3) >> 2);#else insl(ioaddr + RX_FIFO, skb_put(skb,pkt_len), (pkt_len + 3) >> 2);#endif outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */ skb->protocol = eth_type_trans(skb,dev); netif_rx(skb); lp->stats.rx_packets++; continue; } outw(RxDiscard, ioaddr + EL3_CMD); lp->stats.rx_dropped++; if (el3_debug) printk("%s: Couldn't allocate a sk_buff of size %d.\n", dev->name, pkt_len); } inw(ioaddr + EL3_STATUS); /* Delay. */ while (inw(ioaddr + EL3_STATUS) & 0x1000) printk(KERN_DEBUG " Waiting for 3c509 to discard packet, status %x.\n", inw(ioaddr + EL3_STATUS) ); } return 0;}/* * Set or clear the multicast filter for this adaptor. */static voidset_multicast_list(struct device *dev){ unsigned long flags; struct el3_private *lp = (struct el3_private *)dev->priv; int ioaddr = dev->base_addr; if (el3_debug > 1) { static int old = 0; if (old != dev->mc_count) { old = dev->mc_count; printk("%s: Setting Rx mode to %d addresses.\n", dev->name, dev->mc_count); } } spin_lock_irqsave(&lp->lock, flags); if (dev->flags&IFF_PROMISC) { outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast | RxProm, ioaddr + EL3_CMD); } else if (dev->mc_count || (dev->flags&IFF_ALLMULTI)) { outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast, ioaddr + EL3_CMD); } else outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD); spin_unlock_irqrestore(&lp->lock, flags);}static 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 ASAP. We update lp->stats below. */ outw(StatsDisable, ioaddr + EL3_CMD); /* Disable the receiver and transmitter. */ outw(RxDisable, ioaddr + EL3_CMD); outw(TxDisable, ioaddr + EL3_CMD); if (dev->if_port == 3) /* Turn off thinnet power. Green! */ outw(StopCoax, 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, dev); /* Switching back to window 0 disables the IRQ. */ EL3WINDOW(0); /* But we explicitly zero the IRQ line select anyway. */ outw(0x0f00, ioaddr + WN0_IRQ); update_stats(dev); MOD_DEC_USE_COUNT; return 0;}#ifdef MODULE/* Parameters that may be passed into the module. */static int debug = -1;static int irq[] = {-1, -1, -1, -1, -1, -1, -1, -1};static int xcvr[] = {-1, -1, -1, -1, -1, -1, -1, -1};MODULE_PARM(debug,"i");MODULE_PARM(irq,"1-8i");MODULE_PARM(xcvr,"1-8i");intinit_module(void){ int el3_cards = 0; if (debug >= 0) el3_debug = debug; el3_root_dev = NULL; while (el3_probe(0) == 0) { if (irq[el3_cards] > 1) el3_root_dev->irq = irq[el3_cards]; if (xcvr[el3_cards] >= 0) el3_root_dev->if_port = xcvr[el3_cards]; el3_cards++; } return el3_cards ? 0 : -ENODEV;}voidcleanup_module(void){ struct device *next_dev; /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ while (el3_root_dev) { struct el3_private *lp = (struct el3_private *)el3_root_dev->priv;#ifdef CONFIG_MCA if(lp->mca_slot!=-1) mca_mark_as_unused(lp->mca_slot);#endif next_dev = lp->next_dev; unregister_netdev(el3_root_dev); release_region(el3_root_dev->base_addr, EL3_IO_EXTENT); kfree(el3_root_dev); el3_root_dev = next_dev; }}#endif /* MODULE *//* * Local variables: * compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c509.c" * version-control: t * kept-new-versions: 5 * tab-width: 4 * End: */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -