📄 3c589_cs.c
字号:
outw(SetTxThreshold + 1536, ioaddr + EL3_CMD); } dev_kfree_skb(skb); pop_tx_status(dev); return 0;}/* The EL3 interrupt handler. */static irqreturn_t el3_interrupt(int irq, void *dev_id, struct pt_regs *regs){ struct net_device *dev = (struct net_device *) dev_id; struct el3_private *lp = netdev_priv(dev); kio_addr_t ioaddr; __u16 status; int i = 0, handled = 1; if (!netif_device_present(dev)) return IRQ_NONE; ioaddr = dev->base_addr; DEBUG(3, "%s: interrupt, status %4.4x.\n", dev->name, inw(ioaddr + EL3_STATUS)); spin_lock(&lp->lock); while ((status = inw(ioaddr + EL3_STATUS)) & (IntLatch | RxComplete | StatsFull)) { if ((status & 0xe000) != 0x2000) { DEBUG(1, "%s: interrupt from dead card\n", dev->name); handled = 0; break; } if (status & RxComplete) el3_rx(dev); if (status & TxAvailable) { DEBUG(3, " 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 & TxComplete) pop_tx_status(dev); if (status & (AdapterFailure | RxEarly | StatsFull)) { /* 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 & AdapterFailure) { u16 fifo_diag; EL3WINDOW(4); fifo_diag = inw(ioaddr + 4); EL3WINDOW(1); printk(KERN_WARNING "%s: adapter failure, FIFO diagnostic" " register %04x.\n", dev->name, fifo_diag); if (fifo_diag & 0x0400) { /* Tx overrun */ tc589_wait_for_completion(dev, TxReset); outw(TxEnable, ioaddr + EL3_CMD); } if (fifo_diag & 0x2000) { /* Rx underrun */ tc589_wait_for_completion(dev, RxReset); set_multicast_list(dev); outw(RxEnable, ioaddr + EL3_CMD); } outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD); } } if (++i > 10) { printk(KERN_ERR "%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); } lp->last_irq = jiffies; spin_unlock(&lp->lock); DEBUG(3, "%s: exiting interrupt, status %4.4x.\n", dev->name, inw(ioaddr + EL3_STATUS)); return IRQ_RETVAL(handled);}static void media_check(unsigned long arg){ struct net_device *dev = (struct net_device *)(arg); struct el3_private *lp = netdev_priv(dev); kio_addr_t ioaddr = dev->base_addr; u16 media, errs; unsigned long flags; if (!netif_device_present(dev)) goto reschedule; EL3WINDOW(1); /* Check for pending interrupt with expired latency timer: with this, we can limp along even if the interrupt is blocked */ if ((inw(ioaddr + EL3_STATUS) & IntLatch) && (inb(ioaddr + EL3_TIMER) == 0xff)) { if (!lp->fast_poll) printk(KERN_WARNING "%s: interrupt(s) dropped!\n", dev->name); el3_interrupt(dev->irq, lp, NULL); lp->fast_poll = HZ; } if (lp->fast_poll) { lp->fast_poll--; lp->media.expires = jiffies + HZ/100; add_timer(&lp->media); return; } /* lp->lock guards the EL3 window. Window should always be 1 except when the lock is held */ spin_lock_irqsave(&lp->lock, flags); EL3WINDOW(4); media = inw(ioaddr+WN4_MEDIA) & 0xc810; /* Ignore collisions unless we've had no irq's recently */ if (jiffies - lp->last_irq < HZ) { media &= ~0x0010; } else { /* Try harder to detect carrier errors */ EL3WINDOW(6); outw(StatsDisable, ioaddr + EL3_CMD); errs = inb(ioaddr + 0); outw(StatsEnable, ioaddr + EL3_CMD); lp->stats.tx_carrier_errors += errs; if (errs || (lp->media_status & 0x0010)) media |= 0x0010; } if (media != lp->media_status) { if ((media & lp->media_status & 0x8000) && ((lp->media_status ^ media) & 0x0800)) printk(KERN_INFO "%s: %s link beat\n", dev->name, (lp->media_status & 0x0800 ? "lost" : "found")); else if ((media & lp->media_status & 0x4000) && ((lp->media_status ^ media) & 0x0010)) printk(KERN_INFO "%s: coax cable %s\n", dev->name, (lp->media_status & 0x0010 ? "ok" : "problem")); if (dev->if_port == 0) { if (media & 0x8000) { if (media & 0x0800) printk(KERN_INFO "%s: flipped to 10baseT\n", dev->name); else tc589_set_xcvr(dev, 2); } else if (media & 0x4000) { if (media & 0x0010) tc589_set_xcvr(dev, 1); else printk(KERN_INFO "%s: flipped to 10base2\n", dev->name); } } lp->media_status = media; } EL3WINDOW(1); spin_unlock_irqrestore(&lp->lock, flags); reschedule: lp->media.expires = jiffies + HZ; add_timer(&lp->media);}static struct net_device_stats *el3_get_stats(struct net_device *dev){ struct el3_private *lp = netdev_priv(dev); unsigned long flags; dev_link_t *link = &lp->link; if (DEV_OK(link)) { 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. Caller must hold the lock for this*/static void update_stats(struct net_device *dev){ struct el3_private *lp = netdev_priv(dev); kio_addr_t ioaddr = dev->base_addr; DEBUG(2, "%s: updating the statistics.\n", dev->name); /* 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); /* Rx octets */ inw(ioaddr + 10); /* Tx octets */ inw(ioaddr + 12); /* Back to window 1, and turn statistics back on. */ EL3WINDOW(1); outw(StatsEnable, ioaddr + EL3_CMD);}static int el3_rx(struct net_device *dev){ struct el3_private *lp = netdev_priv(dev); kio_addr_t ioaddr = dev->base_addr; int worklimit = 32; short rx_status; DEBUG(3, "%s: in rx_packet(), status %4.4x, rx_status %4.4x.\n", dev->name, inw(ioaddr+EL3_STATUS), inw(ioaddr+RX_STATUS)); while (!((rx_status = inw(ioaddr + RX_STATUS)) & 0x8000) && (--worklimit >= 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; } } else { short pkt_len = rx_status & 0x7ff; struct sk_buff *skb; skb = dev_alloc_skb(pkt_len+5); DEBUG(3, " Receiving packet size %d status %4.4x.\n", pkt_len, rx_status); if (skb != NULL) { skb->dev = dev; skb_reserve(skb, 2); insl(ioaddr+RX_FIFO, skb_put(skb, pkt_len), (pkt_len+3)>>2); skb->protocol = eth_type_trans(skb, dev); netif_rx(skb); dev->last_rx = jiffies; lp->stats.rx_packets++; lp->stats.rx_bytes += pkt_len; } else { DEBUG(1, "%s: couldn't allocate a sk_buff of" " size %d.\n", dev->name, pkt_len); lp->stats.rx_dropped++; } } /* Pop the top of the Rx FIFO */ tc589_wait_for_completion(dev, RxDiscard); } if (worklimit == 0) printk(KERN_WARNING "%s: too much work in el3_rx!\n", dev->name); return 0;}static void set_multicast_list(struct net_device *dev){ struct el3_private *lp = netdev_priv(dev); dev_link_t *link = &lp->link; kio_addr_t ioaddr = dev->base_addr; u16 opts = SetRxFilter | RxStation | RxBroadcast; if (!(DEV_OK(link))) return; if (dev->flags & IFF_PROMISC) opts |= RxMulticast | RxProm; else if (dev->mc_count || (dev->flags & IFF_ALLMULTI)) opts |= RxMulticast; outw(opts, ioaddr + EL3_CMD);}static int el3_close(struct net_device *dev){ struct el3_private *lp = netdev_priv(dev); dev_link_t *link = &lp->link; kio_addr_t ioaddr = dev->base_addr; DEBUG(1, "%s: shutting down ethercard.\n", dev->name); if (DEV_OK(link)) { /* 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 == 2) /* Turn off thinnet power. Green! */ outw(StopCoax, ioaddr + EL3_CMD); else if (dev->if_port == 1) { /* Disable link beat and jabber */ EL3WINDOW(4); outw(0, ioaddr + WN4_MEDIA); } /* Switching back to window 0 disables the IRQ. */ EL3WINDOW(0); /* But we explicitly zero the IRQ line select anyway. */ outw(0x0f00, ioaddr + WN0_IRQ); /* Check if the card still exists */ if ((inw(ioaddr+EL3_STATUS) & 0xe000) == 0x2000) update_stats(dev); } link->open--; netif_stop_queue(dev); del_timer_sync(&lp->media); return 0;}static struct pcmcia_driver tc589_driver = { .owner = THIS_MODULE, .drv = { .name = "3c589_cs", }, .attach = tc589_attach, .detach = tc589_detach,};static int __init init_tc589(void){ return pcmcia_register_driver(&tc589_driver);}static void __exit exit_tc589(void){ pcmcia_unregister_driver(&tc589_driver); BUG_ON(dev_list != NULL);}module_init(init_tc589);module_exit(exit_tc589);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -