📄 8139too.c
字号:
#endif /* RTL8139_NDEBUG */ /* only wake the queue if we did work, and the queue is stopped */ if (tp->dirty_tx != dirty_tx) { tp->dirty_tx = dirty_tx; mb(); if (netif_queue_stopped (dev)) netif_wake_queue (dev); }}/* TODO: clean this up! Rx reset need not be this intensive */static void rtl8139_rx_err (u32 rx_status, struct net_device *dev, struct rtl8139_private *tp, void *ioaddr){ u8 tmp8; int tmp_work = 1000; DPRINTK ("%s: Ethernet frame had errors, status %8.8x.\n", dev->name, rx_status); if (rx_status & RxTooLong) { DPRINTK ("%s: Oversized Ethernet frame, status %4.4x!\n", dev->name, rx_status); /* A.C.: The chip hangs here. */ } tp->stats.rx_errors++; if (rx_status & (RxBadSymbol | RxBadAlign)) tp->stats.rx_frame_errors++; if (rx_status & (RxRunt | RxTooLong)) tp->stats.rx_length_errors++; if (rx_status & RxCRCErr) tp->stats.rx_crc_errors++; /* Reset the receiver, based on RealTek recommendation. (Bug?) */ tp->cur_rx = 0; /* disable receive */ tmp8 = RTL_R8 (ChipCmd) & ChipCmdClear; RTL_W8_F (ChipCmd, tmp8 | CmdTxEnb); /* A.C.: Reset the multicast list. */ rtl8139_set_rx_mode (dev); /* XXX potentially temporary hack to * restart hung receiver */ while (--tmp_work > 0) { tmp8 = RTL_R8 (ChipCmd); if ((tmp8 & CmdRxEnb) && (tmp8 & CmdTxEnb)) break; RTL_W8_F (ChipCmd, (tmp8 & ChipCmdClear) | CmdRxEnb | CmdTxEnb); } /* G.S.: Re-enable receiver */ /* XXX temporary hack to work around receiver hang */ rtl8139_set_rx_mode (dev); if (tmp_work <= 0) printk (KERN_WARNING PFX "tx/rx enable wait too long\n");}/* The data sheet doesn't describe the Rx ring at all, so I'm guessing at the field alignments and semantics. */static void rtl8139_rx_interrupt (struct net_device *dev, struct rtl8139_private *tp, void *ioaddr){ unsigned char *rx_ring; u16 cur_rx; assert (dev != NULL); assert (tp != NULL); assert (ioaddr != NULL); rx_ring = tp->rx_ring; cur_rx = tp->cur_rx; DPRINTK ("%s: In rtl8139_rx(), current %4.4x BufAddr %4.4x," " free to %4.4x, Cmd %2.2x.\n", dev->name, cur_rx, RTL_R16 (RxBufAddr), RTL_R16 (RxBufPtr), RTL_R8 (ChipCmd)); while ((RTL_R8 (ChipCmd) & RxBufEmpty) == 0) { int ring_offset = cur_rx % RX_BUF_LEN; u32 rx_status; unsigned int rx_size; unsigned int pkt_size; struct sk_buff *skb; /* read size+status of next frame from DMA ring buffer */ rx_status = le32_to_cpu (*(u32 *) (rx_ring + ring_offset)); rx_size = rx_status >> 16; pkt_size = rx_size - 4; DPRINTK ("%s: rtl8139_rx() status %4.4x, size %4.4x," " cur %4.4x.\n", dev->name, rx_status, rx_size, cur_rx);#if RTL8139_DEBUG > 2 { int i; DPRINTK ("%s: Frame contents ", dev->name); for (i = 0; i < 70; i++) printk (" %2.2x", rx_ring[ring_offset + i]); printk (".\n"); }#endif /* E. Gill */ /* Note from BSD driver: * Here's a totally undocumented fact for you. When the * RealTek chip is in the process of copying a packet into * RAM for you, the length will be 0xfff0. If you spot a * packet header with this value, you need to stop. The * datasheet makes absolutely no mention of this and * RealTek should be shot for this. */ if (rx_size == 0xfff0) break; /* If Rx err or invalid rx_size/rx_status received * (which happens if we get lost in the ring), * Rx process gets reset, so we abort any further * Rx processing. */ if ((rx_size > (MAX_ETH_FRAME_SIZE+4)) || (!(rx_status & RxStatusOK))) { rtl8139_rx_err (rx_status, dev, tp, ioaddr); return; } /* Malloc up new buffer, compatible with net-2e. */ /* Omit the four octet CRC from the length. */ /* TODO: consider allocating skb's outside of * interrupt context, both to speed interrupt processing, * and also to reduce the chances of having to * drop packets here under memory pressure. */ skb = dev_alloc_skb (pkt_size + 2); if (skb) { skb->dev = dev; skb_reserve (skb, 2); /* 16 byte align the IP fields. */ eth_copy_and_sum (skb, &rx_ring[ring_offset + 4], pkt_size, 0); skb_put (skb, pkt_size); skb->protocol = eth_type_trans (skb, dev); netif_rx (skb); tp->stats.rx_bytes += pkt_size; tp->stats.rx_packets++; } else { printk (KERN_WARNING "%s: Memory squeeze, dropping packet.\n", dev->name); tp->stats.rx_dropped++; } cur_rx = (cur_rx + rx_size + 4 + 3) & ~3; RTL_W16_F (RxBufPtr, cur_rx - 16); } DPRINTK ("%s: Done rtl8139_rx(), current %4.4x BufAddr %4.4x," " free to %4.4x, Cmd %2.2x.\n", dev->name, cur_rx, 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){ printk (KERN_DEBUG "%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 & 0x0100) || (lpar & 0x01C0) == 0x0040 || tp->duplex_lock; if (tp->full_duplex != duplex) { tp->full_duplex = duplex; RTL_W8 (Cfg9346, Cfg9346_Unlock); RTL_W8 (Config1, tp->full_duplex ? 0x60 : 0x20); RTL_W8 (Cfg9346, Cfg9346_Lock); } 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 & RxOverflow) { tp->stats.rx_over_errors++; tp->cur_rx = RTL_R16 (RxBufAddr) % RX_BUF_LEN; RTL_W16_F (RxBufPtr, tp->cur_rx - 16); } if (status & PCIErr) { u16 pci_cmd_status; pci_read_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 status = 0, 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; /* 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; /* E. Gill */ /* In case of an RxFIFOOver we must also clear the RxOverflow bit to avoid dropping frames for ever. Believe me, I got a lot of troubles copying huge data (approximately 2 RxFIFOOver errors per 1GB data transfer). The following is written in the 'p-guide.pdf' file (RTL8139(A/B) Programming guide V0.1, from 1999/1/15) on page 9 from REALTEC. ----------------------------------------------------------- 2. RxFIFOOvw handling: When RxFIFOOvw occurs, all incoming packets are discarded. Clear ISR(RxFIFOOvw) doesn't dismiss RxFIFOOvw event. To dismiss RxFIFOOvw event, the ISR(RxBufOvw) must be written with a '1'. ----------------------------------------------------------- Unfortunately I was not able to find any reason for the RxFIFOOver error (I got the feeling this depends on the CPU speed, lower CPU speed --> more errors). After clearing the RxOverflow bit the transfer of the packet was repeated and all data are error free transfered */ RTL_W16_F (IntrStatus, (status & RxFIFOOver) ? (status | RxOverflow) : status); DPRINTK ("%s: interrupt status=%#4.4x new intstat=%#4.4x.\n", dev->name, status, RTL_R16 (IntrStatus)); if ((status & (PCIErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver | TxErr | TxOK | RxErr | RxOK)) == 0) break; /* Check uncommon events with one test. */ if (status & (PCIErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver | TxErr | RxErr)) rtl8139_weird_interrupt (dev, tp, ioaddr, status, link_changed); if (status & (RxOK | RxUnderrun | RxOverflow | RxFIFOOver)) /* Rx interrupt */ rtl8139_rx_interrupt (dev, tp, ioaddr); if (status & (TxOK | TxErr)) rtl8139_tx_interrupt (dev, tp, ioaddr); 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; DPRINTK ("ENTER\n"); netif_stop_queue (dev); if (tp->thr_pid >= 0) { ret = kill_proc (tp->thr_pid, SIGTERM, 1); if (ret) { printk (KERN_ERR "%s: unable to signal thread\n", dev->name); return ret; } down (&tp->thr_exited); } DPRINTK ("%s: Shutting down ethercard, status was 0x%4.4x.\n", dev->name, RTL_R16 (IntrStatus)); spin_lock_irq (&tp->lock); /* Stop the chip's Tx and Rx DMA processes. */ RTL_W8 (ChipCmd, (RTL_R8 (ChipCmd) & ChipCmdClear)); /* Disable interrupts by clearing the interrupt mask. */ RTL_W16 (IntrMask, 0x0000); /* Update the error counts. */ tp->stats.rx_missed_errors += RTL_R32 (RxMissed); RTL_W32 (RxMissed, 0); spin_unlock_irq (&tp->lock); 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); RTL_W8 (Config1, 0x03); RTL_W8 (HltClk, 'H'); /* 'R' would leave the clock running. */ DPRINTK ("EXIT\n"); return 0;}static int mii_ioctl (struct net_device *dev, struct ifreq *rq, int cmd){ struct rtl8139_private *tp = dev->priv; u16 *data = (u16 *) & rq->ifr_data; int rc = 0; DPRINTK ("ENTER\n"); switch (cmd) { case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */ data[0] = tp->phys[0] & 0x3f; /* Fall Through */ case SIOCDEVPRIVATE + 1: /* Read the specified MII register. */ data[3] = mdio_read (dev, data[0], data[1] & 0x1f); break; case SIOCDEVPRIVATE + 2: /* Write the specified MII register */ if (!capable (CAP_NET_ADMIN)) { rc = -EPERM; break; } mdio_write (dev, data[0], data[1] & 0x1f, data[2]); break; default: rc = -EOPNOTSUPP; break; } DPRINTK ("EXIT, returning %d\n", rc); 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; DPRINTK ("ENTER\n"); if (netif_running(dev)) { tp->stats.rx_missed_errors += RTL_R32 (RxMissed); RTL_W32 (RxMissed, 0); } DPRINTK ("EXIT\n"); 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; DPRINTK ("ENTER\n"); 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); } DPRINTK ("EXIT\n"); return crc;}static void rtl8139_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 ("ENTER\n"); 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 | AcceptMulticast | AcceptMyPhys | AcceptAllPhys; mc_filter[1] = mc_filter[0] = 0xffffffff; } else if ((dev->mc_count > multicast_filter_limit) || (dev->flags & IFF_ALLMULTI)) { /* Too many to filter perfectly -- accept all multicasts. */ rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; mc_filter[1] = mc_filter[0] = 0xffffffff; } else { struct dev_mc_list *mclist; rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; mc_filter[1] = mc_filter[0] = 0; for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; i++, mclist = mclist->next) set_bit (ether_crc (ETH_ALEN, mclist->dmi_addr) >> 26, mc_filter); } /* if called from irq handler, lock already acquired */ if (!in_irq ()) spin_lock_irq (&tp->lock); /* We can safely update without stopping the chip. */ tmp = rtl8139_rx_config | rx_mode | (RTL_R32 (RxConfig) & rtl_chip_info[tp->chipset].RxConfigMask); RTL_W32_F (RxConfig, tmp); RTL_W32_F (MAR0 + 0, mc_filter[0]); RTL_W32_F (MAR0 + 4, mc_filter[1]); if (!in_irq ()) spin_unlock_irq (&tp->lock); DPRINTK ("EXIT\n");}static void rtl8139_suspend (struct pci_dev *pdev){ struct net_device *dev = pdev->driver_data; struct rtl8139_private *tp = dev->priv; void *ioaddr = tp->mmio_addr; unsigned long flags; netif_device_detach (dev); spin_lock_irqsave (&tp->lock, flags); /* Disable interrupts, stop Tx and Rx. */ RTL_W16 (IntrMask, 0x0000); RTL_W8 (ChipCmd, (RTL_R8 (ChipCmd) & ChipCmdClear)); /* Update the error counts. */ tp->stats.rx_missed_errors += RTL_R32 (RxMissed); RTL_W32 (RxMissed, 0); spin_unlock_irqrestore (&tp->lock, flags);}static void rtl8139_resume (struct pci_dev *pdev){ struct net_device *dev = pdev->driver_data; netif_device_attach (dev); rtl8139_hw_start (dev);}static struct pci_driver rtl8139_pci_driver = { name: MODNAME, id_table: rtl8139_pci_tbl, probe: rtl8139_init_one, remove: rtl8139_remove_one, suspend: rtl8139_suspend, resume: rtl8139_resume,};static int __init rtl8139_init_module (void){ return pci_module_init (&rtl8139_pci_driver);}static void __exit rtl8139_cleanup_module (void){ pci_unregister_driver (&rtl8139_pci_driver);}module_init(rtl8139_init_module);module_exit(rtl8139_cleanup_module);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -