📄 8139too.c
字号:
RTL_R16 (RxBufAddr), RTL_R16 (RxBufPtr), RTL_R8 (ChipCmd)); tp->cur_rx = cur_rx;}static void rtl8139_weird_interrupt (struct net_device *dev, struct rtl8139_private *tp, void *ioaddr, int status, int link_changed){ DPRINTK ("%s: Abnormal interrupt, status %8.8x.\n", dev->name, status); assert (dev != NULL); assert (tp != NULL); assert (ioaddr != NULL); /* Update the error count. */ tp->stats.rx_missed_errors += RTL_R32 (RxMissed); RTL_W32 (RxMissed, 0); if ((status & RxUnderrun) && link_changed && (tp->drv_flags & HAS_LNK_CHNG)) { /* Really link-change on new chips. */ int lpar = RTL_R16 (NWayLPAR); int duplex = (lpar & LPA_100FULL) || (lpar & 0x01C0) == 0x0040 || tp->mii.duplex_lock; if (tp->mii.full_duplex != duplex) { tp->mii.full_duplex = duplex;#if 0 RTL_W8 (Cfg9346, Cfg9346_Unlock); RTL_W8 (Config1, tp->mii.full_duplex ? 0x60 : 0x20); RTL_W8 (Cfg9346, Cfg9346_Lock);#endif } status &= ~RxUnderrun; } /* XXX along with rtl8139_rx_err, are we double-counting errors? */ if (status & (RxUnderrun | RxOverflow | RxErr | RxFIFOOver)) tp->stats.rx_errors++; if (status & PCSTimeout) tp->stats.rx_length_errors++; if (status & (RxUnderrun | RxFIFOOver)) tp->stats.rx_fifo_errors++; if (status & PCIErr) { u16 pci_cmd_status; pci_read_config_word (tp->pci_dev, PCI_STATUS, &pci_cmd_status); pci_write_config_word (tp->pci_dev, PCI_STATUS, pci_cmd_status); printk (KERN_ERR "%s: PCI Bus error %4.4x.\n", dev->name, pci_cmd_status); }}/* The interrupt handler does all of the Rx thread work and cleans up after the Tx thread. */static void rtl8139_interrupt (int irq, void *dev_instance, struct pt_regs *regs){ struct net_device *dev = (struct net_device *) dev_instance; struct rtl8139_private *tp = dev->priv; int boguscnt = max_interrupt_work; void *ioaddr = tp->mmio_addr; int ackstat, status; int link_changed = 0; /* avoid bogus "uninit" warning */ spin_lock (&tp->lock); do { status = RTL_R16 (IntrStatus); /* h/w no longer present (hotplug?) or major error, bail */ if (status == 0xFFFF) break; if ((status & (PCIErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver | TxErr | TxOK | RxErr | RxOK)) == 0) break; /* Acknowledge all of the current interrupt sources ASAP, but an first get an additional status bit from CSCR. */ if (status & RxUnderrun) link_changed = RTL_R16 (CSCR) & CSCR_LinkChangeBit; /* The chip takes special action when we clear RxAckBits, * so we clear them later in rtl8139_rx_interrupt */ ackstat = status & ~(RxAckBits | TxErr); RTL_W16 (IntrStatus, ackstat); DPRINTK ("%s: interrupt status=%#4.4x ackstat=%#4.4x new intstat=%#4.4x.\n", dev->name, ackstat, status, RTL_R16 (IntrStatus)); if (netif_running (dev) && (status & RxAckBits)) rtl8139_rx_interrupt (dev, tp, ioaddr); /* Check uncommon events with one test. */ if (status & (PCIErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver | RxErr)) rtl8139_weird_interrupt (dev, tp, ioaddr, status, link_changed); if (netif_running (dev) && (status & (TxOK | TxErr))) { rtl8139_tx_interrupt (dev, tp, ioaddr); if (status & TxErr) RTL_W16 (IntrStatus, TxErr); } boguscnt--; } while (boguscnt > 0); if (boguscnt <= 0) { printk (KERN_WARNING "%s: Too much work at interrupt, " "IntrStatus=0x%4.4x.\n", dev->name, status); /* Clear all interrupt sources. */ RTL_W16 (IntrStatus, 0xffff); } spin_unlock (&tp->lock); DPRINTK ("%s: exiting interrupt, intr_status=%#4.4x.\n", dev->name, RTL_R16 (IntrStatus));}static int rtl8139_close (struct net_device *dev){ struct rtl8139_private *tp = dev->priv; void *ioaddr = tp->mmio_addr; int ret = 0; unsigned long flags; netif_stop_queue (dev); if (tp->thr_pid >= 0) { tp->time_to_die = 1; wmb(); ret = kill_proc (tp->thr_pid, SIGTERM, 1); if (ret) { printk (KERN_ERR "%s: unable to signal thread\n", dev->name); return ret; } wait_for_completion (&tp->thr_exited); } DPRINTK ("%s: Shutting down ethercard, status was 0x%4.4x.\n", dev->name, RTL_R16 (IntrStatus)); spin_lock_irqsave (&tp->lock, flags); /* Stop the chip's Tx and Rx DMA processes. */ RTL_W8 (ChipCmd, 0); /* Disable interrupts by clearing the interrupt mask. */ RTL_W16 (IntrMask, 0); /* Update the error counts. */ tp->stats.rx_missed_errors += RTL_R32 (RxMissed); RTL_W32 (RxMissed, 0); spin_unlock_irqrestore (&tp->lock, flags); synchronize_irq (); free_irq (dev->irq, dev); rtl8139_tx_clear (tp); pci_free_consistent(tp->pci_dev, RX_BUF_TOT_LEN, tp->rx_ring, tp->rx_ring_dma); pci_free_consistent(tp->pci_dev, TX_BUF_TOT_LEN, tp->tx_bufs, tp->tx_bufs_dma); tp->rx_ring = NULL; tp->tx_bufs = NULL; /* Green! Put the chip in low-power mode. */ RTL_W8 (Cfg9346, Cfg9346_Unlock); if (rtl_chip_info[tp->chipset].flags & HasHltClk) RTL_W8 (HltClk, 'H'); /* 'R' would leave the clock running. */ return 0;}/* Get the ethtool Wake-on-LAN settings. Assumes that wol points to kernel memory, *wol has been initialized as {ETHTOOL_GWOL}, and other threads or interrupts aren't messing with the 8139. */static void netdev_get_wol (struct net_device *dev, struct ethtool_wolinfo *wol){ struct rtl8139_private *np = dev->priv; void *ioaddr = np->mmio_addr; if (rtl_chip_info[np->chipset].flags & HasLWake) { u8 cfg3 = RTL_R8 (Config3); u8 cfg5 = RTL_R8 (Config5); wol->supported = WAKE_PHY | WAKE_MAGIC | WAKE_UCAST | WAKE_MCAST | WAKE_BCAST; wol->wolopts = 0; if (cfg3 & Cfg3_LinkUp) wol->wolopts |= WAKE_PHY; if (cfg3 & Cfg3_Magic) wol->wolopts |= WAKE_MAGIC; /* (KON)FIXME: See how netdev_set_wol() handles the following constants. */ if (cfg5 & Cfg5_UWF) wol->wolopts |= WAKE_UCAST; if (cfg5 & Cfg5_MWF) wol->wolopts |= WAKE_MCAST; if (cfg5 & Cfg5_BWF) wol->wolopts |= WAKE_BCAST; }}/* Set the ethtool Wake-on-LAN settings. Return 0 or -errno. Assumes that wol points to kernel memory and other threads or interrupts aren't messing with the 8139. */static int netdev_set_wol (struct net_device *dev, const struct ethtool_wolinfo *wol){ struct rtl8139_private *np = dev->priv; void *ioaddr = np->mmio_addr; u32 support; u8 cfg3, cfg5; support = ((rtl_chip_info[np->chipset].flags & HasLWake) ? (WAKE_PHY | WAKE_MAGIC | WAKE_UCAST | WAKE_MCAST | WAKE_BCAST) : 0); if (wol->wolopts & ~support) return -EINVAL; cfg3 = RTL_R8 (Config3) & ~(Cfg3_LinkUp | Cfg3_Magic); if (wol->wolopts & WAKE_PHY) cfg3 |= Cfg3_LinkUp; if (wol->wolopts & WAKE_MAGIC) cfg3 |= Cfg3_Magic; RTL_W8 (Cfg9346, Cfg9346_Unlock); RTL_W8 (Config3, cfg3); RTL_W8 (Cfg9346, Cfg9346_Lock); cfg5 = RTL_R8 (Config5) & ~(Cfg5_UWF | Cfg5_MWF | Cfg5_BWF); /* (KON)FIXME: These are untested. We may have to set the CRC0, Wakeup0 and LSBCRC0 registers too, but I have no documentation. */ if (wol->wolopts & WAKE_UCAST) cfg5 |= Cfg5_UWF; if (wol->wolopts & WAKE_MCAST) cfg5 |= Cfg5_MWF; if (wol->wolopts & WAKE_BCAST) cfg5 |= Cfg5_BWF; RTL_W8 (Config5, cfg5); /* need not unlock via Cfg9346 */ return 0;}static int netdev_ethtool_ioctl (struct net_device *dev, void *useraddr){ struct rtl8139_private *np = dev->priv; u32 ethcmd; /* dev_ioctl() in ../../net/core/dev.c has already checked capable(CAP_NET_ADMIN), so don't bother with that here. */ if (get_user(ethcmd, (u32 *)useraddr)) return -EFAULT; switch (ethcmd) { case ETHTOOL_GDRVINFO: { struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; strcpy (info.driver, DRV_NAME); strcpy (info.version, DRV_VERSION); strcpy (info.bus_info, np->pci_dev->slot_name); if (copy_to_user (useraddr, &info, sizeof (info))) return -EFAULT; return 0; } /* get settings */ case ETHTOOL_GSET: { struct ethtool_cmd ecmd = { ETHTOOL_GSET }; spin_lock_irq(&np->lock); mii_ethtool_gset(&np->mii, &ecmd); spin_unlock_irq(&np->lock); if (copy_to_user(useraddr, &ecmd, sizeof(ecmd))) return -EFAULT; return 0; } /* set settings */ case ETHTOOL_SSET: { int r; struct ethtool_cmd ecmd; if (copy_from_user(&ecmd, useraddr, sizeof(ecmd))) return -EFAULT; spin_lock_irq(&np->lock); r = mii_ethtool_sset(&np->mii, &ecmd); spin_unlock_irq(&np->lock); return r; } /* restart autonegotiation */ case ETHTOOL_NWAY_RST: { return mii_nway_restart(&np->mii); } /* get link status */ case ETHTOOL_GLINK: { struct ethtool_value edata = {ETHTOOL_GLINK}; edata.data = mii_link_ok(&np->mii); if (copy_to_user(useraddr, &edata, sizeof(edata))) return -EFAULT; return 0; } /* get message-level */ case ETHTOOL_GMSGLVL: { struct ethtool_value edata = {ETHTOOL_GMSGLVL}; edata.data = debug; if (copy_to_user(useraddr, &edata, sizeof(edata))) return -EFAULT; return 0; } /* set message-level */ case ETHTOOL_SMSGLVL: { struct ethtool_value edata; if (copy_from_user(&edata, useraddr, sizeof(edata))) return -EFAULT; debug = edata.data; return 0; } case ETHTOOL_GWOL: { struct ethtool_wolinfo wol = { ETHTOOL_GWOL }; spin_lock_irq (&np->lock); netdev_get_wol (dev, &wol); spin_unlock_irq (&np->lock); if (copy_to_user (useraddr, &wol, sizeof (wol))) return -EFAULT; return 0; } case ETHTOOL_SWOL: { struct ethtool_wolinfo wol; int rc; if (copy_from_user (&wol, useraddr, sizeof (wol))) return -EFAULT; spin_lock_irq (&np->lock); rc = netdev_set_wol (dev, &wol); spin_unlock_irq (&np->lock); return rc; } default: break; } return -EOPNOTSUPP;}static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd){ struct rtl8139_private *tp = dev->priv; struct mii_ioctl_data *data = (struct mii_ioctl_data *)&rq->ifr_data; int rc = 0; int phy = tp->phys[0] & 0x3f; if (!netif_running(dev)) return -EINVAL; if (cmd != SIOCETHTOOL) { /* With SIOCETHTOOL, this would corrupt the pointer. */ data->phy_id &= 0x1f; data->reg_num &= 0x1f; } switch (cmd) { case SIOCETHTOOL: return netdev_ethtool_ioctl(dev, (void *) rq->ifr_data); case SIOCGMIIPHY: /* Get the address of the PHY in use. */ case SIOCDEVPRIVATE: /* binary compat, remove in 2.5 */ data->phy_id = phy; /* Fall Through */ case SIOCGMIIREG: /* Read the specified MII register. */ case SIOCDEVPRIVATE+1: /* binary compat, remove in 2.5 */ data->val_out = mdio_read (dev, data->phy_id, data->reg_num); break; case SIOCSMIIREG: /* Write the specified MII register */ case SIOCDEVPRIVATE+2: /* binary compat, remove in 2.5 */ if (!capable (CAP_NET_ADMIN)) { rc = -EPERM; break; } if (data->phy_id == phy) { u16 value = data->val_in; switch (data->reg_num) { case 0: /* Check for autonegotiation on or reset. */ tp->medialock = (value & 0x9000) ? 0 : 1; if (tp->medialock) tp->mii.full_duplex = (value & 0x0100) ? 1 : 0; break; case 4: tp->mii.advertising = value; break; } } mdio_write(dev, data->phy_id, data->reg_num, data->val_in); break; default: rc = -EOPNOTSUPP; break; } return rc;}static struct net_device_stats *rtl8139_get_stats (struct net_device *dev){ struct rtl8139_private *tp = dev->priv; void *ioaddr = tp->mmio_addr; unsigned long flags; if (netif_running(dev)) { spin_lock_irqsave (&tp->lock, flags); tp->stats.rx_missed_errors += RTL_R32 (RxMissed); RTL_W32 (RxMissed, 0); spin_unlock_irqrestore (&tp->lock, flags); } return &tp->stats;}/* Set or clear the multicast filter for this adaptor. This routine is not state sensitive and need not be SMP locked. */static unsigned const ethernet_polynomial = 0x04c11db7U;static inline u32 ether_crc (int length, unsigned char *data){ int crc = -1; while (--length >= 0) { unsigned char current_octet = *data++; int bit; for (bit = 0; bit < 8; bit++, current_octet >>= 1) crc = (crc << 1) ^ ((crc < 0) ^ (current_octet & 1) ? ethernet_polynomial : 0); } return crc;}static void __set_rx_mode (struct net_device *dev){ struct rtl8139_private *tp = dev->priv; void *ioaddr = tp->mmio_addr; u32 mc_filter[2]; /* Multicast hash filter */ int i, rx_mode; u32 tmp; DPRINTK ("%s: rtl8139_set_rx_mode(%4.4x) done -- Rx config %8.8lx.\n", dev->name, dev->flags, RTL_R32 (RxConfig)); /* Note: do not reorder, GCC is clever about common statements. */ if (dev->flags & IFF_PROMISC) { /* Unconditionally log net taps. */ printk (KERN_NOTICE "%s: Promiscuous mode enabled.\n", dev->name); rx_mode = AcceptBroadcast | A
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -