📄 3c509.c
字号:
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_reserve(skb, 2); /* Align IP on 16 byte */ /* 'skb->data' points to the start of sk_buff data area. */ insl(ioaddr + RX_FIFO, skb_put(skb,pkt_len), (pkt_len + 3) >> 2); outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */ skb->protocol = eth_type_trans(skb,dev); netif_rx(skb); dev->last_rx = jiffies; 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 = netdev_priv(dev); int ioaddr = dev->base_addr; if (el3_debug > 1) { static int old; 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; struct el3_private *lp = netdev_priv(dev); if (el3_debug > 2) printk("%s: Shutting down ethercard.\n", dev->name); el3_down(dev); free_irq(dev->irq, dev); /* Switching back to window 0 disables the IRQ. */ EL3WINDOW(0); if (lp->type != EL3_EISA) { /* But we explicitly zero the IRQ line select anyway. Don't do * it on EISA cards, it prevents the module from getting an * IRQ after unload+reload... */ outw(0x0f00, ioaddr + WN0_IRQ); } return 0;}static intel3_link_ok(struct net_device *dev){ int ioaddr = dev->base_addr; u16 tmp; EL3WINDOW(4); tmp = inw(ioaddr + WN4_MEDIA); EL3WINDOW(1); return tmp & (1<<11);}static intel3_netdev_get_ecmd(struct net_device *dev, struct ethtool_cmd *ecmd){ u16 tmp; int ioaddr = dev->base_addr; EL3WINDOW(0); /* obtain current transceiver via WN4_MEDIA? */ tmp = inw(ioaddr + WN0_ADDR_CONF); ecmd->transceiver = XCVR_INTERNAL; switch (tmp >> 14) { case 0: ecmd->port = PORT_TP; break; case 1: ecmd->port = PORT_AUI; ecmd->transceiver = XCVR_EXTERNAL; break; case 3: ecmd->port = PORT_BNC; default: break; } ecmd->duplex = DUPLEX_HALF; ecmd->supported = 0; tmp = inw(ioaddr + WN0_CONF_CTRL); if (tmp & (1<<13)) ecmd->supported |= SUPPORTED_AUI; if (tmp & (1<<12)) ecmd->supported |= SUPPORTED_BNC; if (tmp & (1<<9)) { ecmd->supported |= SUPPORTED_TP | SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full; /* hmm... */ EL3WINDOW(4); tmp = inw(ioaddr + WN4_NETDIAG); if (tmp & FD_ENABLE) ecmd->duplex = DUPLEX_FULL; } ecmd->speed = SPEED_10; EL3WINDOW(1); return 0;}static intel3_netdev_set_ecmd(struct net_device *dev, struct ethtool_cmd *ecmd){ u16 tmp; int ioaddr = dev->base_addr; if (ecmd->speed != SPEED_10) return -EINVAL; if ((ecmd->duplex != DUPLEX_HALF) && (ecmd->duplex != DUPLEX_FULL)) return -EINVAL; if ((ecmd->transceiver != XCVR_INTERNAL) && (ecmd->transceiver != XCVR_EXTERNAL)) return -EINVAL; /* change XCVR type */ EL3WINDOW(0); tmp = inw(ioaddr + WN0_ADDR_CONF); switch (ecmd->port) { case PORT_TP: tmp &= ~(3<<14); dev->if_port = 0; break; case PORT_AUI: tmp |= (1<<14); dev->if_port = 1; break; case PORT_BNC: tmp |= (3<<14); dev->if_port = 3; break; default: return -EINVAL; } outw(tmp, ioaddr + WN0_ADDR_CONF); if (dev->if_port == 3) { /* fire up the DC-DC convertor if BNC gets enabled */ tmp = inw(ioaddr + WN0_ADDR_CONF); if (tmp & (3 << 14)) { outw(StartCoax, ioaddr + EL3_CMD); udelay(800); } else return -EIO; } EL3WINDOW(4); tmp = inw(ioaddr + WN4_NETDIAG); if (ecmd->duplex == DUPLEX_FULL) tmp |= FD_ENABLE; else tmp &= ~FD_ENABLE; outw(tmp, ioaddr + WN4_NETDIAG); EL3WINDOW(1); return 0;}static void el3_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info){ strcpy(info->driver, DRV_NAME); strcpy(info->version, DRV_VERSION);}static int el3_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd){ struct el3_private *lp = netdev_priv(dev); int ret; spin_lock_irq(&lp->lock); ret = el3_netdev_get_ecmd(dev, ecmd); spin_unlock_irq(&lp->lock); return ret;}static int el3_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd){ struct el3_private *lp = netdev_priv(dev); int ret; spin_lock_irq(&lp->lock); ret = el3_netdev_set_ecmd(dev, ecmd); spin_unlock_irq(&lp->lock); return ret;}static u32 el3_get_link(struct net_device *dev){ struct el3_private *lp = netdev_priv(dev); u32 ret; spin_lock_irq(&lp->lock); ret = el3_link_ok(dev); spin_unlock_irq(&lp->lock); return ret;}static u32 el3_get_msglevel(struct net_device *dev){ return el3_debug;}static void el3_set_msglevel(struct net_device *dev, u32 v){ el3_debug = v;}static const struct ethtool_ops ethtool_ops = { .get_drvinfo = el3_get_drvinfo, .get_settings = el3_get_settings, .set_settings = el3_set_settings, .get_link = el3_get_link, .get_msglevel = el3_get_msglevel, .set_msglevel = el3_set_msglevel,};static voidel3_down(struct net_device *dev){ int ioaddr = dev->base_addr; 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 here next open(). */ EL3WINDOW(4); outw(inw(ioaddr + WN4_MEDIA) & ~MEDIA_TP, ioaddr + WN4_MEDIA); } outw(SetIntrEnb | 0x0000, ioaddr + EL3_CMD); update_stats(dev);}static voidel3_up(struct net_device *dev){ int i, sw_info, net_diag; int ioaddr = dev->base_addr; /* Activating the board required and does no harm otherwise */ 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 & 0x03) == 3) /* BNC interface */ /* Start the thinnet transceiver. We should really wait 50ms...*/ outw(StartCoax, ioaddr + EL3_CMD); else if ((dev->if_port & 0x03) == 0) { /* 10baseT interface */ /* Combine secondary sw_info word (the adapter level) and primary sw_info word (duplex setting plus other useless bits) */ EL3WINDOW(0); sw_info = (read_eeprom(ioaddr, 0x14) & 0x400f) | (read_eeprom(ioaddr, 0x0d) & 0xBff0); EL3WINDOW(4); net_diag = inw(ioaddr + WN4_NETDIAG); net_diag = (net_diag | FD_ENABLE); /* temporarily assume full-duplex will be set */ printk("%s: ", dev->name); switch (dev->if_port & 0x0c) { case 12: /* force full-duplex mode if 3c5x9b */ if (sw_info & 0x000f) { printk("Forcing 3c5x9b full-duplex mode"); break; } case 8: /* set full-duplex mode based on eeprom config setting */ if ((sw_info & 0x000f) && (sw_info & 0x8000)) { printk("Setting 3c5x9b full-duplex mode (from EEPROM configuration bit)"); break; } default: /* xcvr=(0 || 4) OR user has an old 3c5x9 non "B" model */ printk("Setting 3c5x9/3c5x9B half-duplex mode"); net_diag = (net_diag & ~FD_ENABLE); /* disable full duplex */ } outw(net_diag, ioaddr + WN4_NETDIAG); printk(" if_port: %d, sw_info: %4.4x\n", dev->if_port, sw_info); if (el3_debug > 3) printk("%s: 3c5x9 net diag word is now: %4.4x.\n", dev->name, net_diag); /* Enable link beat and jabber check. */ 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. */ 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); netif_start_queue(dev);}/* Power Management support functions */#ifdef EL3_SUSPENDstatic intel3_suspend(struct device *pdev, pm_message_t state){ unsigned long flags; struct net_device *dev; struct el3_private *lp; int ioaddr; dev = pdev->driver_data; lp = netdev_priv(dev); ioaddr = dev->base_addr; spin_lock_irqsave(&lp->lock, flags); if (netif_running(dev)) netif_device_detach(dev); el3_down(dev); outw(PowerDown, ioaddr + EL3_CMD); spin_unlock_irqrestore(&lp->lock, flags); return 0;}static intel3_resume(struct device *pdev){ unsigned long flags; struct net_device *dev; struct el3_private *lp; int ioaddr; dev = pdev->driver_data; lp = netdev_priv(dev); ioaddr = dev->base_addr; spin_lock_irqsave(&lp->lock, flags); outw(PowerUp, ioaddr + EL3_CMD); el3_up(dev); if (netif_running(dev)) netif_device_attach(dev); spin_unlock_irqrestore(&lp->lock, flags); return 0;}#endif /* EL3_SUSPEND *//* 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, -1, -1, -1, -1};module_param(debug,int, 0);module_param_array(irq, int, NULL, 0);module_param_array(xcvr, int, NULL, 0);module_param(max_interrupt_work, int, 0);MODULE_PARM_DESC(debug, "debug level (0-6)");MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)");MODULE_PARM_DESC(xcvr,"transceiver(s) (0=internal, 1=external)");MODULE_PARM_DESC(max_interrupt_work, "maximum events handled per interrupt");#if defined(__ISAPNP__)module_param(nopnp, int, 0);MODULE_PARM_DESC(nopnp, "disable ISA PnP support (0-1)");MODULE_DEVICE_TABLE(isapnp, el3_isapnp_adapters);#endif /* __ISAPNP__ */MODULE_DESCRIPTION("3Com Etherlink III (3c509, 3c509B) ISA/PnP ethernet driver");MODULE_LICENSE("GPL");static int __init el3_init_module(void){ int ret = 0; el3_cards = 0; if (debug >= 0) el3_debug = debug; el3_root_dev = NULL; while (el3_probe(el3_cards) == 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++; }#ifdef CONFIG_EISA ret = eisa_driver_register(&el3_eisa_driver);#endif#ifdef CONFIG_MCA { int err = mca_register_driver(&el3_mca_driver); if (ret == 0) ret = err; }#endif return ret;}static void __exit el3_cleanup_module(void){ struct net_device *next_dev; while (el3_root_dev) { struct el3_private *lp = netdev_priv(el3_root_dev); next_dev = lp->next_dev; el3_common_remove (el3_root_dev); el3_root_dev = next_dev; }#ifdef CONFIG_EISA eisa_driver_unregister (&el3_eisa_driver);#endif#ifdef CONFIG_MCA mca_unregister_driver(&el3_mca_driver);#endif}module_init (el3_init_module);module_exit (el3_cleanup_module);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -