📄 yellowfin.c
字号:
int pkt_len = data_size - (yp->chip_id ? 7 : 8 + buf_addr[data_size - 8]); /* To verify: Yellowfin Length should omit the CRC! */#ifndef final_version if (yellowfin_debug > 4) printk(KERN_DEBUG " yellowfin_rx() normal Rx pkt length %d" " of %d, bogus_cnt %d.\n", pkt_len, data_size, boguscnt);#endif /* Check if the packet is long enough to just pass up the skbuff without copying to a properly sized skbuff. */ if (pkt_len > rx_copybreak) { char *temp = skb_put(skb = yp->rx_skbuff[entry], pkt_len);#ifndef final_verison /* Remove after testing. */ if (le32desc_to_virt(yp->rx_ring[entry].addr) != temp) printk(KERN_WARNING "%s: Warning -- the skbuff addresses " "do not match in yellowfin_rx: %p vs. %p / %p.\n", dev->name, le32desc_to_virt(yp->rx_ring[entry].addr), skb->head, temp);#endif yp->rx_skbuff[entry] = NULL; } else { skb = dev_alloc_skb(pkt_len + 2); if (skb == NULL) break; skb->dev = dev; skb_reserve(skb, 2); /* 16 byte align the data fields */#if 1 || USE_IP_CSUM eth_copy_and_sum(skb, yp->rx_skbuff[entry]->tail, pkt_len, 0); skb_put(skb, pkt_len);#else memcpy(skb_put(skb, pkt_len), yp->rx_skbuff[entry]->tail, pkt_len);#endif } skb->protocol = eth_type_trans(skb, dev); netif_rx(skb); dev->last_rx = jiffies; yp->stats.rx_packets++; yp->stats.rx_bytes += pkt_len; } entry = (++yp->cur_rx) % RX_RING_SIZE; yp->rx_head_desc = &yp->rx_ring[entry]; } /* Refill the Rx ring buffers. */ for (; yp->cur_rx - yp->dirty_rx > 0; yp->dirty_rx++) { entry = yp->dirty_rx % RX_RING_SIZE; if (yp->rx_skbuff[entry] == NULL) { struct sk_buff *skb = dev_alloc_skb(yp->rx_buf_sz); if (skb == NULL) break; /* Better luck next round. */ yp->rx_skbuff[entry] = skb; skb->dev = dev; /* Mark as being used by this device. */ skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ yp->rx_ring[entry].addr = virt_to_le32desc(skb->tail); } yp->rx_ring[entry].dbdma_cmd = cpu_to_le32(CMD_STOP); yp->rx_ring[entry].result_status = 0; /* Clear complete bit. */ if (entry != 0) yp->rx_ring[entry - 1].dbdma_cmd = cpu_to_le32(CMD_RX_BUF | INTR_ALWAYS | yp->rx_buf_sz); else yp->rx_ring[RX_RING_SIZE - 1].dbdma_cmd = cpu_to_le32(CMD_RX_BUF | INTR_ALWAYS | BRANCH_ALWAYS | yp->rx_buf_sz); } return 0;}static void yellowfin_error(struct net_device *dev, int intr_status){ struct yellowfin_private *yp = (struct yellowfin_private *)dev->priv; printk(KERN_ERR "%s: Something Wicked happened! %4.4x.\n", dev->name, intr_status); /* Hmmmmm, it's not clear what to do here. */ if (intr_status & (IntrTxPCIErr | IntrTxPCIFault)) yp->stats.tx_errors++; if (intr_status & (IntrRxPCIErr | IntrRxPCIFault)) yp->stats.rx_errors++;}static int yellowfin_close(struct net_device *dev){ long ioaddr = dev->base_addr; struct yellowfin_private *yp = (struct yellowfin_private *)dev->priv; int i; netif_stop_queue (dev); if (yellowfin_debug > 1) { printk(KERN_DEBUG "%s: Shutting down ethercard, status was Tx %4.4x Rx %4.4x Int %2.2x.\n", dev->name, YF_INW(ioaddr + TxStatus), YF_INW(ioaddr + RxStatus), YF_INW(ioaddr + IntrStatus)); printk(KERN_DEBUG "%s: Queue pointers were Tx %d / %d, Rx %d / %d.\n", dev->name, yp->cur_tx, yp->dirty_tx, yp->cur_rx, yp->dirty_rx); } /* Disable interrupts by clearing the interrupt mask. */ YF_OUTW(0x0000, ioaddr + IntrEnb); /* Stop the chip's Tx and Rx processes. */ YF_OUTL(0x80000000, ioaddr + RxCtrl); YF_OUTL(0x80000000, ioaddr + TxCtrl); del_timer(&yp->timer);#if !defined(final_version) && defined(__i386__) if (yellowfin_debug > 2) { printk("\n"KERN_DEBUG" Tx ring at %8.8x:\n", (int)virt_to_bus(yp->tx_ring)); for (i = 0; i < TX_RING_SIZE*2; i++) printk(" %c #%d desc. %8.8x %8.8x %8.8x %8.8x.\n", YF_INL(ioaddr + TxPtr) == (long)&yp->tx_ring[i] ? '>' : ' ', i, yp->tx_ring[i].dbdma_cmd, yp->tx_ring[i].addr, yp->tx_ring[i].branch_addr, yp->tx_ring[i].result_status); printk(KERN_DEBUG " Tx status %p:\n", yp->tx_status); for (i = 0; i < TX_RING_SIZE; i++) printk(" #%d status %4.4x %4.4x %4.4x %4.4x.\n", i, yp->tx_status[i].tx_cnt, yp->tx_status[i].tx_errs, yp->tx_status[i].total_tx_cnt, yp->tx_status[i].paused); printk("\n"KERN_DEBUG " Rx ring %8.8x:\n", (int)virt_to_bus(yp->rx_ring)); for (i = 0; i < RX_RING_SIZE; i++) { printk(KERN_DEBUG " %c #%d desc. %8.8x %8.8x %8.8x\n", YF_INL(ioaddr + RxPtr) == (long)&yp->rx_ring[i] ? '>' : ' ', i, yp->rx_ring[i].dbdma_cmd, yp->rx_ring[i].addr, yp->rx_ring[i].result_status); if (yellowfin_debug > 6) { if (get_unaligned((u8*)yp->rx_ring[i].addr) != 0x69) { int j; for (j = 0; j < 0x50; j++) printk(" %4.4x", get_unaligned(((u16*)yp->rx_ring[i].addr) + j)); printk("\n"); } } } }#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++) { yp->rx_ring[i].dbdma_cmd = cpu_to_le32(CMD_STOP); yp->rx_ring[i].addr = 0xBADF00D0; /* An invalid address. */ if (yp->rx_skbuff[i]) { dev_kfree_skb(yp->rx_skbuff[i]); } yp->rx_skbuff[i] = 0; } for (i = 0; i < TX_RING_SIZE; i++) { if (yp->tx_skbuff[i]) dev_kfree_skb(yp->tx_skbuff[i]); yp->tx_skbuff[i] = 0; }#ifdef YF_PROTOTYPE /* Support for prototype hardware errata. */ if (yellowfin_debug > 0) { printk(KERN_DEBUG "%s: Received %d frames that we should not have.\n", dev->name, bogus_rx); }#endif return 0;}static struct net_device_stats *yellowfin_get_stats(struct net_device *dev){ struct yellowfin_private *yp = (struct yellowfin_private *)dev->priv; return &yp->stats;}/* Set or clear the multicast filter for this adaptor. *//* The little-endian AUTODIN32 ethernet CRC calculation. N.B. Do not use for bulk data, use a table-based routine instead. This is common code and should be moved to net/core/crc.c */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){ struct yellowfin_private *yp = (struct yellowfin_private *)dev->priv; long ioaddr = dev->base_addr; u16 cfg_value = YF_INW(ioaddr + Cnfg); /* Stop the Rx process to change any value. */ YF_OUTW(cfg_value & ~0x1000, ioaddr + Cnfg); if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ /* Unconditionally log net taps. */ printk(KERN_NOTICE "%s: Promiscuous mode enabled.\n", dev->name); YF_OUTW(0x000F, ioaddr + AddrMode); } else if ((dev->mc_count > 64) || (dev->flags & IFF_ALLMULTI)) { /* Too many to filter well, or accept all multicasts. */ YF_OUTW(0x000B, ioaddr + AddrMode); } else if (dev->mc_count > 0) { /* Must use the multicast hash table. */ struct dev_mc_list *mclist; u16 hash_table[4]; int i; memset(hash_table, 0, sizeof(hash_table)); for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; i++, mclist = mclist->next) { /* Due to a bug in the early chip versions, multiple filter slots must be set for each address. */ if (yp->flags & HasMulticastBug) { set_bit((ether_crc_le(3, mclist->dmi_addr) >> 3) & 0x3f, hash_table); set_bit((ether_crc_le(4, mclist->dmi_addr) >> 3) & 0x3f, hash_table); set_bit((ether_crc_le(5, mclist->dmi_addr) >> 3) & 0x3f, hash_table); } set_bit((ether_crc_le(6, mclist->dmi_addr) >> 3) & 0x3f, hash_table); } /* Copy the hash table to the chip. */ for (i = 0; i < 4; i++) YF_OUTW(hash_table[i], ioaddr + HashTbl + i*2); YF_OUTW(0x0003, ioaddr + AddrMode); } else { /* Normal, unicast/broadcast-only mode. */ YF_OUTW(0x0001, ioaddr + AddrMode); } /* Restart the Rx process. */ YF_OUTW(cfg_value | 0x1000, ioaddr + Cnfg);}#ifdef HAVE_PRIVATE_IOCTLstatic int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd){ long ioaddr = dev->base_addr; u16 *data = (u16 *)&rq->ifr_data; switch(cmd) { case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */ data[0] = ((struct yellowfin_private *)dev->priv)->phys[0] & 0x1f; /* Fall Through */ case SIOCDEVPRIVATE+1: /* Read the specified MII register. */ data[3] = mdio_read(ioaddr, data[0] & 0x1f, data[1] & 0x1f); return 0; case SIOCDEVPRIVATE+2: /* Write the specified MII register */ if (!capable(CAP_NET_ADMIN)) return -EPERM; mdio_write(ioaddr, data[0] & 0x1f, data[1] & 0x1f, data[2]); return 0; default: return -EOPNOTSUPP; }}#endif /* HAVE_PRIVATE_IOCTL */static int __devinit yellowfin_init_one(struct pci_dev *pdev, const struct pci_device_id *ent){ struct net_device *dev; struct yellowfin_private *yp; int option, i, irq; int flags, chip_idx; static int find_cnt = 0; long ioaddr, real_ioaddr; chip_idx = ent->driver_data; flags = chip_info[chip_idx].flags; dev = init_etherdev(NULL, sizeof(*yp)); if (!dev) { printk (KERN_ERR PFX "cannot allocate ethernet device\n"); return -ENOMEM; } SET_MODULE_OWNER(dev); yp = dev->priv; if (!request_region (pci_resource_start (pdev, 0), YELLOWFIN_SIZE, YELLOWFIN_MODULE_NAME)) { printk (KERN_ERR PFX "cannot obtain I/O port region\n"); goto err_out_free_netdev; } if (!request_mem_region (pci_resource_start (pdev, 1), YELLOWFIN_SIZE, YELLOWFIN_MODULE_NAME)) { printk (KERN_ERR PFX "cannot obtain MMIO region\n"); goto err_out_free_pio_region; } if (pci_enable_device (pdev)) goto err_out_free_mmio_region; pci_set_master (pdev);#ifdef USE_IO_OPS real_ioaddr = ioaddr = pci_resource_start (pdev, 0);#else real_ioaddr = ioaddr = pci_resource_start (pdev, 1); ioaddr = (long) ioremap(ioaddr, YELLOWFIN_SIZE); if (!ioaddr) goto err_out_free_mmio_region;#endif irq = pdev->irq; printk(KERN_INFO "%s: %s type %8x at 0x%lx, ", dev->name, chip_info[chip_idx].name, YF_INL(ioaddr + ChipRev), real_ioaddr); if (flags & IsGigabit) for (i = 0; i < 6; i++) dev->dev_addr[i] = YF_INB(ioaddr + StnAddr + i); else { int ee_offset = (read_eeprom(ioaddr, 6) == 0xff ? 0x100 : 0); for (i = 0; i < 6; i++) dev->dev_addr[i] = read_eeprom(ioaddr, ee_offset + i); } for (i = 0; i < 5; i++) printk("%2.2x:", dev->dev_addr[i]); printk("%2.2x, IRQ %d.\n", dev->dev_addr[i], irq); /* Reset the chip. */ YF_OUTL(0x80000000, ioaddr + DMACtrl); dev->base_addr = ioaddr; dev->irq = irq; pdev->driver_data = dev; yp->chip_id = chip_idx; yp->flags = flags; yp->lock = SPIN_LOCK_UNLOCKED; option = find_cnt < MAX_UNITS ? options[find_cnt] : 0; if (dev->mem_start) option = dev->mem_start; /* The lower four bits are the media type. */ if (option > 0) { if (option & 0x200) yp->full_duplex = 1; yp->default_port = option & 15; if (yp->default_port) yp->medialock = 1; } if (find_cnt < MAX_UNITS && full_duplex[find_cnt] > 0) yp->full_duplex = 1; if (yp->full_duplex) yp->duplex_lock = 1; /* The Yellowfin-specific entries in the device structure. */ dev->open = &yellowfin_open; dev->hard_start_xmit = &yellowfin_start_xmit; dev->stop = &yellowfin_close; dev->get_stats = &yellowfin_get_stats; dev->set_multicast_list = &set_rx_mode;#ifdef HAVE_PRIVATE_IOCTL dev->do_ioctl = &mii_ioctl;#endif dev->tx_timeout = yellowfin_tx_timeout; dev->watchdog_timeo = TX_TIMEOUT; if (mtu) dev->mtu = mtu; if (yp->flags & HasMII) { int phy, phy_idx = 0; for (phy = 0; phy < 32 && phy_idx < 4; phy++) { int mii_status = mdio_read(ioaddr, phy, 1); if (mii_status != 0xffff && mii_status != 0x0000) { yp->phys[phy_idx++] = phy; yp->advertising = mdio_read(ioaddr, phy, 4); printk(KERN_INFO "%s: MII PHY found at address %d, status " "0x%4.4x advertising %4.4x.\n", dev->name, phy, mii_status, yp->advertising); } } yp->mii_cnt = phy_idx; } find_cnt++; return 0;err_out_free_mmio_region: release_mem_region (pci_resource_start (pdev, 1), YELLOWFIN_SIZE);err_out_free_pio_region: release_region (pci_resource_start (pdev, 0), YELLOWFIN_SIZE);err_out_free_netdev: unregister_netdev (dev); kfree (dev); return -ENODEV;}static void __devexit yellowfin_remove_one (struct pci_dev *pdev){ struct net_device *dev = pdev->driver_data; struct yellowfin_private *np; if (!dev) BUG(); np = dev->priv; unregister_netdev (dev); release_region (dev->base_addr, YELLOWFIN_SIZE); release_mem_region (dev->base_addr, YELLOWFIN_SIZE);#ifndef USE_IO_OPS iounmap ((void *) dev->base_addr);#endif kfree (dev);}static struct pci_driver yellowfin_driver = { name: YELLOWFIN_MODULE_NAME, id_table: yellowfin_pci_tbl, probe: yellowfin_init_one, remove: yellowfin_remove_one,};static int __init yellowfin_init (void){ if (debug) /* Emit version even if no cards detected. */ printk(KERN_INFO "%s", version); return pci_module_init (&yellowfin_driver);}static void __exit yellowfin_cleanup (void){ pci_unregister_driver (&yellowfin_driver);}module_init(yellowfin_init);module_exit(yellowfin_cleanup);/* * Local variables: * compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c yellowfin.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`" * compile-command-alphaLX: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O2 -c yellowfin.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS` -fomit-frame-pointer -fno-strength-reduce -mno-fp-regs -Wa,-m21164a -DBWX_USABLE -DBWIO_ENABLED" * c-indent-level: 4 * c-basic-offset: 4 * tab-width: 4 * End: */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -