📄 pci-skeleton.c
字号:
if ((tmp8 & CmdRxEnb) && (tmp8 & CmdTxEnb)) break; NETDRV_W8_F (ChipCmd, (tmp8 & ChipCmdClear) | CmdRxEnb | CmdTxEnb); } /* G.S.: Re-enable receiver */ /* XXX temporary hack to work around receiver hang */ netdrv_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 netdrv_rx_interrupt (struct net_device *dev, struct netdrv_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 netdrv_rx(), current %4.4x BufAddr %4.4x," " free to %4.4x, Cmd %2.2x.\n", dev->name, cur_rx, NETDRV_R16 (RxBufAddr), NETDRV_R16 (RxBufPtr), NETDRV_R8 (ChipCmd)); while ((NETDRV_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: netdrv_rx() status %4.4x, size %4.4x," " cur %4.4x.\n", dev->name, rx_status, rx_size, cur_rx);#if NETDRV_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 /* 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))) { netdrv_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); dev->last_rx = jiffies; 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; NETDRV_W16_F (RxBufPtr, cur_rx - 16); } DPRINTK ("%s: Done netdrv_rx(), current %4.4x BufAddr %4.4x," " free to %4.4x, Cmd %2.2x.\n", dev->name, cur_rx, NETDRV_R16 (RxBufAddr), NETDRV_R16 (RxBufPtr), NETDRV_R8 (ChipCmd)); tp->cur_rx = cur_rx;}static void netdrv_weird_interrupt (struct net_device *dev, struct netdrv_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 += NETDRV_R32 (RxMissed); NETDRV_W32 (RxMissed, 0); if ((status & RxUnderrun) && link_changed && (tp->drv_flags & HAS_LNK_CHNG)) { /* Really link-change on new chips. */ int lpar = NETDRV_R16 (NWayLPAR); int duplex = (lpar & 0x0100) || (lpar & 0x01C0) == 0x0040 || tp->duplex_lock; if (tp->full_duplex != duplex) { tp->full_duplex = duplex; NETDRV_W8 (Cfg9346, Cfg9346_Unlock); NETDRV_W8 (Config1, tp->full_duplex ? 0x60 : 0x20); NETDRV_W8 (Cfg9346, Cfg9346_Lock); } status &= ~RxUnderrun; } /* XXX along with netdrv_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 = NETDRV_R16 (RxBufAddr) % RX_BUF_LEN; NETDRV_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 irqreturn_t netdrv_interrupt (int irq, void *dev_instance, struct pt_regs *regs){ struct net_device *dev = (struct net_device *) dev_instance; struct netdrv_private *tp = dev->priv; int boguscnt = max_interrupt_work; void *ioaddr = tp->mmio_addr; int status = 0, link_changed = 0; /* avoid bogus "uninit" warning */ int handled = 0; spin_lock (&tp->lock); do { status = NETDRV_R16 (IntrStatus); /* h/w no longer present (hotplug?) or major error, bail */ if (status == 0xFFFF) break; handled = 1; /* Acknowledge all of the current interrupt sources ASAP */ NETDRV_W16_F (IntrStatus, status); DPRINTK ("%s: interrupt status=%#4.4x new intstat=%#4.4x.\n", dev->name, status, NETDRV_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)) netdrv_weird_interrupt (dev, tp, ioaddr, status, link_changed); if (status & (RxOK | RxUnderrun | RxOverflow | RxFIFOOver)) /* Rx interrupt */ netdrv_rx_interrupt (dev, tp, ioaddr); if (status & (TxOK | TxErr)) netdrv_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. */ NETDRV_W16 (IntrStatus, 0xffff); } spin_unlock (&tp->lock); DPRINTK ("%s: exiting interrupt, intr_status=%#4.4x.\n", dev->name, NETDRV_R16 (IntrStatus)); return IRQ_RETVAL(handled);}static int netdrv_close (struct net_device *dev){ struct netdrv_private *tp = dev->priv; void *ioaddr = tp->mmio_addr; unsigned long flags; DPRINTK ("ENTER\n"); netif_stop_queue (dev); DPRINTK ("%s: Shutting down ethercard, status was 0x%4.4x.\n", dev->name, NETDRV_R16 (IntrStatus)); del_timer_sync (&tp->timer); spin_lock_irqsave (&tp->lock, flags); /* Stop the chip's Tx and Rx DMA processes. */ NETDRV_W8 (ChipCmd, (NETDRV_R8 (ChipCmd) & ChipCmdClear)); /* Disable interrupts by clearing the interrupt mask. */ NETDRV_W16 (IntrMask, 0x0000); /* Update the error counts. */ tp->stats.rx_missed_errors += NETDRV_R32 (RxMissed); NETDRV_W32 (RxMissed, 0); spin_unlock_irqrestore (&tp->lock, flags); synchronize_irq (); free_irq (dev->irq, dev); netdrv_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. */ NETDRV_W8 (Cfg9346, Cfg9346_Unlock); NETDRV_W8 (Config1, 0x03); NETDRV_W8 (Cfg9346, Cfg9346_Lock); DPRINTK ("EXIT\n"); return 0;}static int netdrv_ioctl (struct net_device *dev, struct ifreq *rq, int cmd){ struct netdrv_private *tp = dev->priv; struct mii_ioctl_data *data = if_mii(rq); unsigned long flags; int rc = 0; DPRINTK ("ENTER\n"); switch (cmd) { case SIOCGMIIPHY: /* Get address of MII PHY in use. */ data->phy_id = tp->phys[0] & 0x3f; /* Fall Through */ case SIOCGMIIREG: /* Read MII PHY register. */ spin_lock_irqsave (&tp->lock, flags); data->val_out = mdio_read (dev, data->phy_id & 0x1f, data->reg_num & 0x1f); spin_unlock_irqrestore (&tp->lock, flags); break; case SIOCSMIIREG: /* Write MII PHY register. */ if (!capable (CAP_NET_ADMIN)) { rc = -EPERM; break; } spin_lock_irqsave (&tp->lock, flags); mdio_write (dev, data->phy_id & 0x1f, data->reg_num & 0x1f, data->val_in); spin_unlock_irqrestore (&tp->lock, flags); break; default: rc = -EOPNOTSUPP; break; } DPRINTK ("EXIT, returning %d\n", rc); return rc;}static struct net_device_stats *netdrv_get_stats (struct net_device *dev){ struct netdrv_private *tp = dev->priv; void *ioaddr = tp->mmio_addr; DPRINTK ("ENTER\n"); assert (tp != NULL); if (netif_running(dev)) { unsigned long flags; spin_lock_irqsave (&tp->lock, flags); tp->stats.rx_missed_errors += NETDRV_R32 (RxMissed); NETDRV_W32 (RxMissed, 0); spin_unlock_irqrestore (&tp->lock, flags); } 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 void netdrv_set_rx_mode (struct net_device *dev){ struct netdrv_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: netdrv_set_rx_mode(%4.4x) done -- Rx config %8.8x.\n", dev->name, dev->flags, NETDRV_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) { int bit_nr = ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26; mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31); } } /* 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 = netdrv_rx_config | rx_mode | (NETDRV_R32 (RxConfig) & rtl_chip_info[tp->chipset].RxConfigMask); NETDRV_W32_F (RxConfig, tmp); NETDRV_W32_F (MAR0 + 0, mc_filter[0]); NETDRV_W32_F (MAR0 + 4, mc_filter[1]); if (!in_irq ()) spin_unlock_irq (&tp->lock); DPRINTK ("EXIT\n");}#ifdef CONFIG_PMstatic int netdrv_suspend (struct pci_dev *pdev, pm_message_t state){ struct net_device *dev = pci_get_drvdata (pdev); struct netdrv_private *tp = dev->priv; void *ioaddr = tp->mmio_addr; unsigned long flags; if (!netif_running(dev)) return 0; netif_device_detach (dev); spin_lock_irqsave (&tp->lock, flags); /* Disable interrupts, stop Tx and Rx. */ NETDRV_W16 (IntrMask, 0x0000); NETDRV_W8 (ChipCmd, (NETDRV_R8 (ChipCmd) & ChipCmdClear)); /* Update the error counts. */ tp->stats.rx_missed_errors += NETDRV_R32 (RxMissed); NETDRV_W32 (RxMissed, 0); spin_unlock_irqrestore (&tp->lock, flags); pci_save_state (pdev); pci_set_power_state (pdev, PCI_D3hot); return 0;}static int netdrv_resume (struct pci_dev *pdev){ struct net_device *dev = pci_get_drvdata (pdev); struct netdrv_private *tp = dev->priv; if (!netif_running(dev)) return 0; pci_set_power_state (pdev, PCI_D0); pci_restore_state (pdev); netif_device_attach (dev); netdrv_hw_start (dev); return 0;}#endif /* CONFIG_PM */static struct pci_driver netdrv_pci_driver = { .name = MODNAME, .id_table = netdrv_pci_tbl, .probe = netdrv_init_one, .remove = __devexit_p(netdrv_remove_one),#ifdef CONFIG_PM .suspend = netdrv_suspend, .resume = netdrv_resume,#endif /* CONFIG_PM */};static int __init netdrv_init_module (void){/* when a module, this is printed whether or not devices are found in probe */#ifdef MODULE printk(version);#endif return pci_module_init (&netdrv_pci_driver);}static void __exit netdrv_cleanup_module (void){ pci_unregister_driver (&netdrv_pci_driver);}module_init(netdrv_init_module);module_exit(netdrv_cleanup_module);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -