📄 starfire.c
字号:
np->tx_full = 0; netif_wake_queue(dev); } /* Abnormal error summary/uncommon events handlers. */ if (intr_status & IntrAbnormalSummary) netdev_error(dev, intr_status); if (--boguscnt < 0) { printk(KERN_WARNING "%s: Too much work at interrupt, " "status=0x%4.4x.\n", dev->name, intr_status); break; } } while (1); if (debug > 4) printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n", dev->name, (int)readl(ioaddr + IntrStatus));#ifndef final_version /* Code that should never be run! Remove after testing.. */ { static int stopit = 10; if (!netif_running(dev) && --stopit < 0) { printk(KERN_ERR "%s: Emergency stop, looping startup interrupt.\n", dev->name); free_irq(irq, dev); } }#endif}/* This routine is logically part of the interrupt handler, but separated for clarity and better register allocation. */static int netdev_rx(struct net_device *dev){ struct netdev_private *np = (struct netdev_private *)dev->priv; int boguscnt = np->dirty_rx + RX_RING_SIZE - np->cur_rx; u32 desc_status; if (np->rx_done_q == 0) { printk(KERN_ERR "%s: rx_done_q is NULL! rx_done is %d. %p.\n", dev->name, np->rx_done, np->tx_done_q); return 0; } /* If EOP is set on the next entry, it's a new packet. Send it up. */ while ((desc_status = le32_to_cpu(np->rx_done_q[np->rx_done].status)) != 0) { if (debug > 4) printk(KERN_DEBUG " netdev_rx() status of %d was %8.8x.\n", np->rx_done, desc_status); if (--boguscnt < 0) break; if ( ! (desc_status & RxOK)) { /* There was a error. */ if (debug > 2) printk(KERN_DEBUG " netdev_rx() Rx error was %8.8x.\n", desc_status); np->stats.rx_errors++; if (desc_status & RxFIFOErr) np->stats.rx_fifo_errors++; } else { struct sk_buff *skb; u16 pkt_len = desc_status; /* Implicitly Truncate */ int entry = (desc_status >> 16) & 0x7ff;#ifndef final_version if (debug > 4) 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 */ pci_dma_sync_single(np->pci_dev, np->rx_info[entry].mapping, pkt_len, PCI_DMA_FROMDEVICE);#if HAS_IP_COPYSUM /* Call copy + cksum if available. */ eth_copy_and_sum(skb, np->rx_info[entry].skb->tail, pkt_len, 0); skb_put(skb, pkt_len);#else memcpy(skb_put(skb, pkt_len), np->rx_info[entry].skb->tail, pkt_len);#endif } else { char *temp; pci_unmap_single(np->pci_dev, np->rx_info[entry].mapping, np->rx_buf_sz, PCI_DMA_FROMDEVICE); skb = np->rx_info[entry].skb; temp = skb_put(skb, pkt_len); np->rx_info[entry].skb = NULL; np->rx_info[entry].mapping = 0;#ifndef final_version /* Remove after testing. */ if (le32_to_cpu(np->rx_ring[entry].rxaddr & ~3) != ((unsigned long) temp)) printk(KERN_ERR "%s: Internal fault: The skbuff addresses " "do not match in netdev_rx: %d vs. %p / %p.\n", dev->name, le32_to_cpu(np->rx_ring[entry].rxaddr), skb->head, temp);#endif }#ifndef final_version /* Remove after testing. */ /* You will want this info for the initial debug. */ if (debug > 5) printk(KERN_DEBUG " Rx data %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:" "%2.2x %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x %2.2x%2.2x " "%d.%d.%d.%d.\n", skb->data[0], skb->data[1], skb->data[2], skb->data[3], skb->data[4], skb->data[5], skb->data[6], skb->data[7], skb->data[8], skb->data[9], skb->data[10], skb->data[11], skb->data[12], skb->data[13], skb->data[14], skb->data[15], skb->data[16], skb->data[17]);#endif skb->protocol = eth_type_trans(skb, dev);#ifdef full_rx_status if (le32_to_cpu(np->rx_done_q[np->rx_done].status2) & 0x01000000) skb->ip_summed = CHECKSUM_UNNECESSARY;#endif netif_rx(skb); dev->last_rx = jiffies; np->stats.rx_packets++; } np->cur_rx++; np->rx_done_q[np->rx_done].status = 0; np->rx_done = (np->rx_done + 1) & (DONE_Q_SIZE-1); } writew(np->rx_done, dev->base_addr + CompletionQConsumerIdx); /* Refill the Rx ring buffers. */ for (; np->cur_rx - np->dirty_rx > 0; np->dirty_rx++) { struct sk_buff *skb; int entry = np->dirty_rx % RX_RING_SIZE; if (np->rx_info[entry].skb == NULL) { skb = dev_alloc_skb(np->rx_buf_sz); np->rx_info[entry].skb = skb; if (skb == NULL) break; /* Better luck next round. */ np->rx_info[entry].mapping = pci_map_single(np->pci_dev, skb->tail, np->rx_buf_sz, PCI_DMA_FROMDEVICE); skb->dev = dev; /* Mark as being used by this device. */ np->rx_ring[entry].rxaddr = cpu_to_le32(np->rx_info[entry].mapping | RxDescValid); } if (entry == RX_RING_SIZE - 1) np->rx_ring[entry].rxaddr |= cpu_to_le32(RxDescEndRing); /* We could defer this until later... */ writew(entry, dev->base_addr + RxDescQIdx); } if (debug > 5 || memcmp(np->pad0, np->pad0 + 1, sizeof(np->pad0) -1)) printk(KERN_DEBUG " exiting netdev_rx() status of %d was %8.8x %d.\n", np->rx_done, desc_status, memcmp(np->pad0, np->pad0 + 1, sizeof(np->pad0) -1)); /* Restart Rx engine if stopped. */ return 0;}static void netdev_error(struct net_device *dev, int intr_status){ struct netdev_private *np = (struct netdev_private *)dev->priv; if (intr_status & LinkChange) { printk(KERN_NOTICE "%s: Link changed: Autonegotiation advertising" " %4.4x partner %4.4x.\n", dev->name, mdio_read(dev, np->phys[0], 4), mdio_read(dev, np->phys[0], 5)); check_duplex(dev, 0); } if (intr_status & StatsMax) { get_stats(dev); } /* Came close to underrunning the Tx FIFO, increase threshold. */ if (intr_status & IntrTxDataLow) writel(++np->tx_threshold, dev->base_addr + TxThreshold); if ((intr_status & ~(IntrAbnormalSummary|LinkChange|StatsMax|IntrTxDataLow|1)) && debug) printk(KERN_ERR "%s: Something Wicked happened! %4.4x.\n", dev->name, intr_status); /* Hmmmmm, it's not clear how to recover from PCI faults. */ if (intr_status & IntrTxPCIErr) np->stats.tx_fifo_errors++; if (intr_status & IntrRxPCIErr) np->stats.rx_fifo_errors++;}static struct net_device_stats *get_stats(struct net_device *dev){ long ioaddr = dev->base_addr; struct netdev_private *np = (struct netdev_private *)dev->priv; /* This adapter architecture needs no SMP locks. */ np->stats.tx_bytes = readl(ioaddr + 0x57010); np->stats.rx_bytes = readl(ioaddr + 0x57044); np->stats.tx_packets = readl(ioaddr + 0x57000); np->stats.tx_aborted_errors = readl(ioaddr + 0x57024) + readl(ioaddr + 0x57028); np->stats.tx_window_errors = readl(ioaddr + 0x57018); np->stats.collisions = readl(ioaddr + 0x57004) + readl(ioaddr + 0x57008); /* The chip only need report frame silently dropped. */ np->stats.rx_dropped += readw(ioaddr + RxDMAStatus); writew(0, ioaddr + RxDMAStatus); np->stats.rx_crc_errors = readl(ioaddr + 0x5703C); np->stats.rx_frame_errors = readl(ioaddr + 0x57040); np->stats.rx_length_errors = readl(ioaddr + 0x57058); np->stats.rx_missed_errors = readl(ioaddr + 0x5707C); return &np->stats;}/* The little-endian AUTODIN II ethernet CRC calculations. A big-endian version is also available. This is slow but compact code. Do not use this routine for bulk data, use a table-based routine instead. This is common code and should be moved to net/core/crc.c. Chips may use the upper or lower CRC bits, and may reverse and/or invert them. Select the endian-ness that results in minimal calculations.*/static unsigned const ethernet_polynomial_le = 0xedb88320U;static inline unsigned ether_crc_le(int length, unsigned char *data){ unsigned int crc = 0xffffffff; /* Initial value. */ while(--length >= 0) { unsigned char current_octet = *data++; int bit; for (bit = 8; --bit >= 0; current_octet >>= 1) { if ((crc ^ current_octet) & 1) { crc >>= 1; crc ^= ethernet_polynomial_le; } else crc >>= 1; } } return crc;}static void set_rx_mode(struct net_device *dev){ long ioaddr = dev->base_addr; u32 rx_mode; struct dev_mc_list *mclist; int i; if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ /* Unconditionally log net taps. */ printk(KERN_NOTICE "%s: Promiscuous mode enabled.\n", dev->name); rx_mode = AcceptBroadcast|AcceptAllMulticast|AcceptAll|AcceptMyPhys; } else if ((dev->mc_count > multicast_filter_limit) || (dev->flags & IFF_ALLMULTI)) { /* Too many to match, or accept all multicasts. */ rx_mode = AcceptBroadcast|AcceptAllMulticast|AcceptMyPhys; } else if (dev->mc_count <= 15) { /* Use the 16 element perfect filter. */ long filter_addr = ioaddr + 0x56000 + 1*16; for (i = 1, mclist = dev->mc_list; mclist && i <= dev->mc_count; i++, mclist = mclist->next) { u16 *eaddrs = (u16 *)mclist->dmi_addr; writew(cpu_to_be16(eaddrs[2]), filter_addr); filter_addr += 4; writew(cpu_to_be16(eaddrs[1]), filter_addr); filter_addr += 4; writew(cpu_to_be16(eaddrs[0]), filter_addr); filter_addr += 8; } while (i++ < 16) { writew(0xffff, filter_addr); filter_addr += 4; writew(0xffff, filter_addr); filter_addr += 4; writew(0xffff, filter_addr); filter_addr += 8; } rx_mode = AcceptBroadcast | AcceptMyPhys; } else { /* Must use a multicast hash table. */ long filter_addr; u16 mc_filter[32] __attribute__ ((aligned(sizeof(long)))); /* Multicast hash filter */ memset(mc_filter, 0, sizeof(mc_filter)); for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; i++, mclist = mclist->next) { set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr) >> 23, mc_filter); } /* Clear the perfect filter list. */ filter_addr = ioaddr + 0x56000 + 1*16; for (i = 1; i < 16; i++) { writew(0xffff, filter_addr); filter_addr += 4; writew(0xffff, filter_addr); filter_addr += 4; writew(0xffff, filter_addr); filter_addr += 8; } for (filter_addr=ioaddr + 0x56100, i=0; i < 32; filter_addr+= 16, i++) writew(mc_filter[i], filter_addr); rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; } writel(rx_mode, ioaddr + RxFilterMode);}static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd){ struct netdev_private *np = (struct netdev_private *)dev->priv; u16 *data = (u16 *)&rq->ifr_data; switch(cmd) { case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */ data[0] = np->phys[0] & 0x1f; /* Fall Through */ case SIOCDEVPRIVATE+1: /* Read the specified MII register. */ data[3] = mdio_read(dev, data[0] & 0x1f, data[1] & 0x1f); return 0; case SIOCDEVPRIVATE+2: /* Write the specified MII register */ if (!capable(CAP_NET_ADMIN)) return -EPERM; if (data[0] == np->phys[0]) { u16 value = data[2]; switch (data[1]) { case 0: if (value & 0x9000) /* Autonegotiation. */ np->medialock = 0; else { np->full_duplex = (value & 0x0100) ? 1 : 0; np->medialock = 1; } break; case 4: np->advertising = value; break; } check_duplex(dev, 0); } mdio_write(dev, data[0] & 0x1f, data[1] & 0x1f, data[2]); return 0; default: return -EOPNOTSUPP; }}static int netdev_close(struct net_device *dev){ long ioaddr = dev->base_addr; struct netdev_private *np = (struct netdev_private *)dev->priv; int i; netif_stop_queue(dev); del_timer_sync(&np->timer); if (debug > 1) { printk(KERN_DEBUG "%s: Shutting down ethercard, Intr status %4.4x.\n", dev->name, (int)readl(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); } /* Disable interrupts by clearing the interrupt mask. */ writel(0, ioaddr + IntrEnable); /* Stop the chip's Tx and Rx processes. */#ifdef __i386__ if (debug > 2) { printk("\n"KERN_DEBUG" Tx ring at %8.8x:\n", np->tx_ring_dma); for (i = 0; i < 8 /* TX_RING_SIZE is huge! */; i++) printk(KERN_DEBUG " #%d desc. %8.8x %8.8x -> %8.8x.\n", i, le32_to_cpu(np->tx_ring[i].status), le32_to_cpu(np->tx_ring[i].addr), le32_to_cpu(np->tx_done_q[i].status)); printk(KERN_DEBUG " Rx ring at %8.8x -> %p:\n", np->rx_ring_dma, np->rx_done_q); if (np->rx_done_q) for (i = 0; i < 8 /* RX_RING_SIZE */; i++) { printk(KERN_DEBUG " #%d desc. %8.8x -> %8.8x\n", i, le32_to_cpu(np->rx_ring[i].rxaddr), le32_to_cpu(np->rx_done_q[i].status)); } }#endif /* __i386__ debugging only */ free_irq(dev->irq, dev); /* Free all the skbuffs in the Rx queue. */ for (i = 0; i < RX_RING_SIZE; i++) { np->rx_ring[i].rxaddr = cpu_to_le32(0xBADF00D0); /* An invalid address. */ if (np->rx_info[i].skb != NULL) { pci_unmap_single(np->pci_dev, np->rx_info[i].mapping, np->rx_buf_sz, PCI_DMA_FROMDEVICE); dev_kfree_skb(np->rx_info[i].skb); } np->rx_info[i].skb = NULL; np->rx_info[i].mapping = 0; } for (i = 0; i < TX_RING_SIZE; i++) { struct sk_buff *skb = np->tx_info[i].skb; if (skb != NULL) { pci_unmap_single(np->pci_dev, np->tx_info[i].mapping, skb->len, PCI_DMA_TODEVICE); dev_kfree_skb(skb); } np->tx_info[i].skb = NULL; np->tx_info[i].mapping = 0; } MOD_DEC_USE_COUNT; return 0;}static void __devexit starfire_remove_one (struct pci_dev *pdev){ struct net_device *dev = pdev->driver_data; struct netdev_private *np; if (!dev) BUG(); np = dev->priv; unregister_netdev(dev); iounmap((char *)dev->base_addr); if (np->tx_done_q) pci_free_consistent(np->pci_dev, PAGE_SIZE, np->tx_done_q, np->tx_done_q_dma); if (np->rx_done_q) pci_free_consistent(np->pci_dev, PAGE_SIZE, np->rx_done_q, np->rx_done_q_dma); if (np->tx_ring) pci_free_consistent(np->pci_dev, PAGE_SIZE, np->tx_ring, np->tx_ring_dma); if (np->rx_ring) pci_free_consistent(np->pci_dev, PAGE_SIZE, np->rx_ring, np->rx_ring_dma); kfree(dev);}static struct pci_driver starfire_driver = { name: "starfire", probe: starfire_init_one, remove: starfire_remove_one, id_table: starfire_pci_tbl,};static int __init starfire_init (void){ return pci_module_init (&starfire_driver);}static void __exit starfire_cleanup (void){ pci_unregister_driver (&starfire_driver);}module_init(starfire_init);module_exit(starfire_cleanup);/* * Local variables: * compile-command: "gcc -DMODULE -Wall -Wstrict-prototypes -O6 -c starfire.c" * simple-compile-command: "gcc -DMODULE -O6 -c starfire.c" * c-indent-level: 4 * c-basic-offset: 4 * tab-width: 4 * End: */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -