📄 3c509.c
字号:
return word;}static intel3_open(struct net_device *dev){ int ioaddr = dev->base_addr; int i; outw(TxReset, ioaddr + EL3_CMD); outw(RxReset, ioaddr + EL3_CMD); outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD); i = request_irq(dev->irq, &el3_interrupt, 0, dev->name, dev); if (i) return i; EL3WINDOW(0); if (el3_debug > 3) printk("%s: Opening, IRQ %d status@%x %4.4x.\n", dev->name, dev->irq, ioaddr + EL3_STATUS, inw(ioaddr + EL3_STATUS)); /* Activate board: this is probably unnecessary. */ outw(0x0001, ioaddr + 4); /* Set the IRQ line. */ outw((dev->irq << 12) | 0x0f00, ioaddr + WN0_IRQ); /* Set the station address in window 2 each time opened. */ EL3WINDOW(2); for (i = 0; i < 6; i++) outb(dev->dev_addr[i], ioaddr + i); if (dev->if_port == 3) /* Start the thinnet transceiver. We should really wait 50ms...*/ outw(StartCoax, ioaddr + EL3_CMD); else if (dev->if_port == 0) { /* 10baseT interface, enabled link beat and jabber check. */ EL3WINDOW(4); outw(inw(ioaddr + WN4_MEDIA) | MEDIA_TP, ioaddr + WN4_MEDIA); } /* Switch to the stats window, and clear all stats by reading. */ outw(StatsDisable, ioaddr + EL3_CMD); EL3WINDOW(6); for (i = 0; i < 9; i++) inb(ioaddr + i); inw(ioaddr + 10); inw(ioaddr + 12); /* Switch to register set 1 for normal use. */ EL3WINDOW(1); /* Accept b-case and phys addr only. */ outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD); outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */ netif_start_queue(dev); 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)); return 0;}static voidel3_tx_timeout (struct net_device *dev){ struct el3_private *lp = (struct el3_private *)dev->priv; int ioaddr = dev->base_addr; /* Transmitter timeout, serious problems. */ 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); netif_wake_queue(dev);}static intel3_start_xmit(struct sk_buff *skb, struct net_device *dev){ struct el3_private *lp = (struct el3_private *)dev->priv; int ioaddr = dev->base_addr; unsigned long flags; netif_stop_queue (dev); 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 /* * 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. */ spin_lock_irqsave(&lp->lock, flags); /* 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) netif_start_queue(dev); else /* Interrupt us when the FIFO has room for max-sized packet. */ outw(SetTxThreshold + 1536, ioaddr + EL3_CMD); spin_unlock_irqrestore(&lp->lock, flags); 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 net_device *dev = (struct net_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); 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); netif_wake_queue (dev); } 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); return;}static struct net_device_stats *el3_get_stats(struct net_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 net_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 net_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 net_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 net_device *dev){ int ioaddr = dev->base_addr; if (el3_debug > 2) printk("%s: Shutting down ethercard.\n", dev->name); netif_stop_queue(dev); /* 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); 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");MODULE_PARM(max_interrupt_work, "i");#ifdef __ISAPNP__MODULE_PARM(nopnp, "i");#endifintinit_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 net_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 + -