📄 sundance_main.c
字号:
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) pci_dma_sync_single(np->pci_dev, desc->frag[0].addr,np->rx_buf_sz, PCI_DMA_FROMDEVICE);#else pci_dma_sync_single_for_cpu(np->pci_dev, desc->frag[0].addr,np->rx_buf_sz, PCI_DMA_FROMDEVICE);#endif if (frame_status & 0x001f4000) { /* There was a error. */ if (netif_msg_rx_err(np)) printk(KERN_DEBUG " netdev_rx() Rx error was %8.8x.\n", frame_status); np->stats.rx_errors++; if (frame_status & 0x00100000) np->stats.rx_length_errors++; if (frame_status & 0x00010000) np->stats.rx_fifo_errors++; if (frame_status & 0x00060000) np->stats.rx_frame_errors++; if (frame_status & 0x00080000) np->stats.rx_crc_errors++; if (frame_status & 0x00100000) { printk(KERN_WARNING "%s: Oversized Ethernet frame," " status %8.8x.\n", dev->name, frame_status); } } else { struct sk_buff *skb;#ifndef final_version if (netif_msg_rx_status(np)) printk(KERN_DEBUG " netdev_rx() normal Rx pkt length %d" ", bogus_cnt %d.\n", pkt_len, boguscnt);#endif /* Check if the packet is long enough to accept without copying to a minimally-sized skbuff. */ if (pkt_len < rx_copybreak && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { skb->dev = dev; skb_reserve(skb, 2); /* 16 byte align the IP header */ eth_copy_and_sum(skb, np->rx_skbuff[entry]->tail, pkt_len, 0); skb_put(skb, pkt_len); } else { pci_unmap_single(np->pci_dev, desc->frag[0].addr, np->rx_buf_sz, PCI_DMA_FROMDEVICE); skb_put(skb = np->rx_skbuff[entry], pkt_len); np->rx_skbuff[entry] = NULL; } skb->protocol = eth_type_trans(skb, dev); /* Note: checksum -> skb->ip_summed = CHECKSUM_UNNECESSARY; */ netif_rx(skb); dev->last_rx = jiffies; } entry = (entry + 1) % RX_RING_SIZE; received++; } np->cur_rx = entry; refill_rx (dev); np->budget -= received; writew(DEFAULT_INTR, ioaddr + IntrEnable); return;not_done: np->cur_rx = entry; refill_rx (dev); if (!received) received = 1; np->budget -= received; if (np->budget <= 0) np->budget = RX_BUDGET; tasklet_schedule(&np->rx_tasklet); return;}static void refill_rx (struct net_device *dev){ struct netdev_private *np = dev->priv; int entry; int cnt = 0; /* Refill the Rx ring buffers. */ for (;(np->cur_rx - np->dirty_rx + RX_RING_SIZE) % RX_RING_SIZE > 0; np->dirty_rx = (np->dirty_rx + 1) % RX_RING_SIZE) { struct sk_buff *skb; entry = np->dirty_rx % RX_RING_SIZE; if (np->rx_skbuff[entry] == NULL) { skb = dev_alloc_skb(np->rx_buf_sz); np->rx_skbuff[entry] = skb; if (skb == NULL) break; /* Better luck next round. */ skb->dev = dev; /* Mark as being used by this device. */ if(rx_copybreak<=DEFAULT_COPYBREAK)skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ np->rx_ring[entry].frag[0].addr = cpu_to_le32( pci_map_single(np->pci_dev, skb->tail, np->rx_buf_sz, PCI_DMA_FROMDEVICE)); } /* Perhaps we need not reset this field. */ np->rx_ring[entry].frag[0].length = cpu_to_le32(np->rx_buf_sz | LastFrag); np->rx_ring[entry].status = 0; cnt++; } return;}static void netdev_error(struct net_device *dev, int intr_status){ long ioaddr = dev->base_addr; struct netdev_private *np = dev->priv; if (intr_status & LinkChange) { check_speed(dev); if (np->flowctrl && np->mii_if.full_duplex) { writew(readw(ioaddr + MulticastFilter1+2) | 0x0200, ioaddr + MulticastFilter1+2); writew(readw(ioaddr + MACCtrl0) | EnbFlowCtrl, ioaddr + MACCtrl0); } } if (intr_status & StatsMax) { get_stats(dev); } if (intr_status & IntrPCIErr) { printk(KERN_ERR "%s: Something Wicked happened! %4.4x.\n", dev->name, intr_status); /* We must do a global reset of DMA to continue. */ }}static struct net_device_stats *get_stats(struct net_device *dev){ struct netdev_private *np = dev->priv; long ioaddr = dev->base_addr; int i; /* We should lock this segment of code for SMP eventually, although the vulnerability window is very small and statistics are non-critical. */ /* The chip only need report frame silently dropped. */ np->stats.rx_missed_errors += readb(ioaddr + RxMissed); np->stats.tx_packets += readw(ioaddr + TxFramesOK); np->stats.rx_packets += readw(ioaddr + RxFramesOK); np->stats.collisions += readb(ioaddr + StatsLateColl); np->stats.collisions += readb(ioaddr + StatsMultiColl); np->stats.collisions += readb(ioaddr + StatsOneColl); np->stats.tx_carrier_errors += readb(ioaddr + StatsCarrierError); readb(ioaddr + StatsTxDefer); for (i = StatsTxDefer; i <= StatsMcastRx; i++) readb(ioaddr + i); np->stats.tx_bytes += readw(ioaddr + TxOctetsLow); np->stats.tx_bytes += readw(ioaddr + TxOctetsHigh) << 16; np->stats.rx_bytes += readw(ioaddr + RxOctetsLow); np->stats.rx_bytes += readw(ioaddr + RxOctetsHigh) << 16; return &np->stats;}static void set_rx_mode(struct net_device *dev){ long ioaddr = dev->base_addr; struct netdev_private *np = dev->priv; u16 mc_filter[4]; /* Multicast hash filter */ u32 rx_mode; int i; if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ /* Unconditionally log net taps. */ printk(KERN_NOTICE "%s: Promiscuous mode enabled.\n", dev->name); memset(mc_filter, 0xff, sizeof(mc_filter)); rx_mode = AcceptBroadcast | AcceptMulticast | AcceptAll | AcceptMyPhys; } else if ((dev->mc_count > multicast_filter_limit) || (dev->flags & IFF_ALLMULTI)) { /* Too many to match, or accept all multicasts. */ memset(mc_filter, 0xff, sizeof(mc_filter)); rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; } else if (dev->mc_count) { struct dev_mc_list *mclist; int bit; int index; int crc; memset (mc_filter, 0, sizeof (mc_filter)); for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; i++, mclist = mclist->next) { crc = ether_crc_le (ETH_ALEN, mclist->dmi_addr); for (index=0, bit=0; bit < 6; bit++, crc <<= 1) if (crc & 0x80000000) index |= 1 << bit; mc_filter[index/16] |= (1 << (index % 16)); } rx_mode = AcceptBroadcast | AcceptMultiHash | AcceptMyPhys; } else { writeb(AcceptBroadcast | AcceptMyPhys, ioaddr + RxMode); return; } if (np->mii_if.full_duplex && np->flowctrl) mc_filter[3] |= 0x0200; for (i = 0; i < 4; i++) writew(mc_filter[i], ioaddr + MulticastFilter0 + i*2); writeb(rx_mode, ioaddr + RxMode);}static int __set_mac_addr(struct net_device *dev){ u16 addr16; addr16 = (dev->dev_addr[0] | (dev->dev_addr[1] << 8)); writew(addr16, dev->base_addr + StationAddr); addr16 = (dev->dev_addr[2] | (dev->dev_addr[3] << 8)); writew(addr16, dev->base_addr + StationAddr+2); addr16 = (dev->dev_addr[4] | (dev->dev_addr[5] << 8)); writew(addr16, dev->base_addr + StationAddr+4); return 0;}static int sundance_set_mac_address(struct net_device *dev, void *p){ struct sockaddr *addr = p; if (!is_valid_ether_addr(addr->sa_data)) return -EADDRNOTAVAIL; memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); __set_mac_addr(dev); return 0;}static int netdev_ethtool_ioctl(struct net_device *dev, void *useraddr){ struct netdev_private *np = dev->priv; u32 ethcmd; if (copy_from_user(ðcmd, useraddr, sizeof(ethcmd))) return -EFAULT; switch (ethcmd) { /* get constant driver settings/info */ case ETHTOOL_GDRVINFO: { struct ethtool_drvinfo info = {ETHTOOL_GDRVINFO}; strcpy(info.driver, DRV_NAME); strcpy(info.version, DRV_VERSION);#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11) strcpy(info.bus_info, np->pci_dev->slot_name);#else strcpy(info.bus_info, pci_name(np->pci_dev));#endif memset(&info.fw_version, 0, sizeof(info.fw_version)); if (copy_to_user(useraddr, &info, sizeof(info))) return -EFAULT; return 0; } /* get media settings */ case ETHTOOL_GSET: { struct ethtool_cmd ecmd = { ETHTOOL_GSET }; spin_lock_irq(&np->lock); mii_ethtool_gset(&np->mii_if, &ecmd); spin_unlock_irq(&np->lock); if (copy_to_user(useraddr, &ecmd, sizeof(ecmd))) return -EFAULT; return 0; } /* set media 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_if, &ecmd); spin_unlock_irq(&np->lock); return r; } /* restart autonegotiation */ case ETHTOOL_NWAY_RST: { return mii_nway_restart(&np->mii_if); } /* get link status */ case ETHTOOL_GLINK: { struct ethtool_value edata = {ETHTOOL_GLINK}; edata.data = mii_link_ok(&np->mii_if); 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 = np->msg_enable; 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; np->msg_enable = edata.data; return 0; } default: return -EOPNOTSUPP; }}static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd){ struct netdev_private *np = dev->priv; struct mii_ioctl_data *data = (struct mii_ioctl_data *) & rq->ifr_data; int rc; int i; long ioaddr = dev->base_addr; if (!netif_running(dev)) return -EINVAL; if (cmd == SIOCETHTOOL) rc = netdev_ethtool_ioctl(dev, (void *) rq->ifr_data); else { spin_lock_irq(&np->lock); rc = generic_mii_ioctl(&np->mii_if, data, cmd, NULL); spin_unlock_irq(&np->lock); } switch (cmd) { case SIOCDEVPRIVATE: for (i=0; i<TX_RING_SIZE; i++) { printk(KERN_DEBUG "%02x %08x %08x %08x(%02x) %08x %08x\n", i, (unsigned int)np->tx_ring_dma + i*sizeof(*np->tx_ring), le32_to_cpu(np->tx_ring[i].next_desc), le32_to_cpu(np->tx_ring[i].status), (le32_to_cpu(np->tx_ring[i].status) >> 2) & 0xff, le32_to_cpu(np->tx_ring[i].frag[0].addr), le32_to_cpu(np->tx_ring[i].frag[0].length)); } printk(KERN_DEBUG "TxListPtr=%08x netif_queue_stopped=%d\n", readl(dev->base_addr + TxListPtr), netif_queue_stopped(dev)); printk(KERN_DEBUG "cur_tx=%d(%02x) dirty_tx=%d(%02x)\n", np->cur_tx, np->cur_tx % TX_RING_SIZE, np->dirty_tx, np->dirty_tx % TX_RING_SIZE); printk(KERN_DEBUG "cur_rx=%d dirty_rx=%d\n", np->cur_rx, np->dirty_rx); printk(KERN_DEBUG "cur_task=%d\n", np->cur_task); printk(KERN_DEBUG "TxStatus=%04x\n", readw(ioaddr + TxStatus)); return 0; } return rc;}static int netdev_close(struct net_device *dev){ long ioaddr = dev->base_addr; struct netdev_private *np = dev->priv; struct sk_buff *skb; int i; /* Wait and kill tasklet */ tasklet_kill(&np->rx_tasklet); tasklet_kill(&np->tx_tasklet); np->cur_tx = np->dirty_tx = 0; np->cur_task = 0; np->last_tx=0; netif_stop_queue(dev); if (netif_msg_ifdown(np)) { printk(KERN_DEBUG "%s: Shutting down ethercard, status was Tx %2.2x " "Rx %4.4x Int %2.2x.\n", dev->name, readb(ioaddr + TxStatus), readl(ioaddr + RxStatus), readw(ioaddr + IntrStatus)); printk(KERN_DEBUG "%s: Queue pointers were Tx %d / %d, Rx %d / %d.\n", dev->name, np->cur_tx, np->dirty_tx, np->cur_rx, np->dirty_rx); } /* Stop the chip's Rx processes. */ writew(RxDisable | StatsDisable, ioaddr + MACCtrl1); writew(0x100, ioaddr + DMACtrl); /* Stop the chip's Tx processes. */ writew(0x400, ioaddr + DMACtrl); writew(TxDisable, ioaddr + MACCtrl1); /* Disable interrupts by clearing the interrupt mask. */ writew(0x0000, ioaddr + IntrEnable); for(i=2000;i> 0;i--) { if((readl(ioaddr + DMACtrl)&0xC000) == 0)break; mdelay(1); } writew(GlobalReset | DMAReset | FIFOReset |NetworkReset, ioaddr +ASICCtrl + 2); for(i=2000;i >0;i--) { if((readw(ioaddr + ASICCtrl +2)&ResetBusy) == 0) break; mdelay(1); } #ifdef __i386__ if (netif_msg_hw(np)) { printk("\n"KERN_DEBUG" Tx ring at %8.8x:\n", (int)(np->tx_ring_dma)); for (i = 0; i < TX_RING_SIZE; i++) printk(" #%d desc. %4.4x %8.8x %8.8x.\n", i, np->tx_ring[i].status, np->tx_ring[i].frag[0].addr, np->tx_ring[i].frag[0].length); printk("\n"KERN_DEBUG " Rx ring %8.8x:\n", (int)(np->rx_ring_dma)); for (i = 0; i < /*RX_RING_SIZE*/4 ; i++) { printk(KERN_DEBUG " #%d desc. %4.4x %4.4x %8.8x\n", i, np->rx_ring[i].status, np->rx_ring[i].frag[0].addr, np->rx_ring[i].frag[0].length); } }#endif /* __i386__ debugging only */ free_irq(dev->irq, dev); del_timer_sync(&np->timer); /* Free all the skbuffs in the Rx queue. */ for (i = 0; i < RX_RING_SIZE; i++) { np->rx_ring[i].status = 0; np->rx_ring[i].frag[0].addr = 0xBADF00D0; /* An invalid address. */ skb = np->rx_skbuff[i]; if (skb) { pci_unmap_single(np->pci_dev, np->rx_ring[i].frag[0].addr, np->rx_buf_sz, PCI_DMA_FROMDEVICE); dev_kfree_skb(skb); np->rx_skbuff[i] = 0; } } for (i = 0; i < TX_RING_SIZE; i++) {// 2005/11/4 np->tx_ring[i].next_desc=0;// skb = np->tx_skbuff[i]; if (skb) { pci_unmap_single(np->pci_dev, np->tx_ring[i].frag[0].addr, skb->len, PCI_DMA_TODEVICE); dev_kfree_skb(skb); np->tx_skbuff[i] = 0; } } return 0;}static void __devexit sundance_remove1 (struct pci_dev *pdev){ struct net_device *dev = pci_get_drvdata(pdev); /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ if (dev) { struct netdev_private *np = dev->priv; unregister_netdev(dev); pci_free_consistent(pdev, RX_TOTAL_SIZE, np->rx_ring, np->rx_ring_dma); pci_free_consistent(pdev, TX_TOTAL_SIZE, np->tx_ring, np->tx_ring_dma); pci_release_regions(pdev);#ifndef USE_IO_OPS iounmap((char *)(dev->base_addr));#endif kfree(dev); pci_set_drvdata(pdev, NULL); }}static struct pci_driver sundance_driver = { .name = DRV_NAME, .id_table = sundance_pci_tbl, .probe = sundance_probe1, .remove = __devexit_p(sundance_remove1),};static int __init sundance_init(void){/* when a module, this is printed whether or not devices are found in probe */#ifdef MODULE printk(version);#endif return pci_module_init(&sundance_driver);}static void __exit sundance_exit(void){ pci_unregister_driver(&sundance_driver);}module_init(sundance_init);module_exit(sundance_exit);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -