3c589_cs.c
来自「LINUX 2.6.17.4的源码」· C语言 代码 · 共 1,010 行 · 第 1/3 页
C
1,010 行
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 (time_before(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; struct pcmcia_device *link = lp->p_dev; if (pcmcia_dev_present(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); struct pcmcia_device *link = lp->p_dev; kio_addr_t ioaddr = dev->base_addr; u16 opts = SetRxFilter | RxStation | RxBroadcast; if (!pcmcia_dev_present(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); struct pcmcia_device *link = lp->p_dev; kio_addr_t ioaddr = dev->base_addr; DEBUG(1, "%s: shutting down ethercard.\n", dev->name); if (pcmcia_dev_present(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_device_id tc589_ids[] = { PCMCIA_MFC_DEVICE_MANF_CARD(0, 0x0101, 0x0562), PCMCIA_MFC_DEVICE_PROD_ID1(0, "Motorola MARQUIS", 0xf03e4e77), PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0589), PCMCIA_DEVICE_PROD_ID12("Farallon", "ENet", 0x58d93fc4, 0x992c2202), PCMCIA_MFC_DEVICE_CIS_MANF_CARD(0, 0x0101, 0x0035, "3CXEM556.cis"), PCMCIA_MFC_DEVICE_CIS_MANF_CARD(0, 0x0101, 0x003d, "3CXEM556.cis"), PCMCIA_DEVICE_NULL,};MODULE_DEVICE_TABLE(pcmcia, tc589_ids);static struct pcmcia_driver tc589_driver = { .owner = THIS_MODULE, .drv = { .name = "3c589_cs", }, .probe = tc589_probe, .remove = tc589_detach, .id_table = tc589_ids, .suspend = tc589_suspend, .resume = tc589_resume,};static int __init init_tc589(void){ return pcmcia_register_driver(&tc589_driver);}static void __exit exit_tc589(void){ pcmcia_unregister_driver(&tc589_driver);}module_init(init_tc589);module_exit(exit_tc589);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?