📄 hamachi.c
字号:
if (hamachi_debug > 2) printk(KERN_DEBUG " hamachi_rx() Rx error was %8.8x.\n", frame_status); hmp->stats.rx_errors++; if (frame_status & 0x00600000) hmp->stats.rx_length_errors++; if (frame_status & 0x00080000) hmp->stats.rx_frame_errors++; if (frame_status & 0x00100000) hmp->stats.rx_crc_errors++; if (frame_status < 0) hmp->stats.rx_dropped++; } else { struct sk_buff *skb; /* Omit CRC */ u16 pkt_len = (frame_status & 0x07ff) - 4; #ifdef RX_CHECKSUM u32 pfck = *(u32 *) &buf_addr[data_size - 8];#endif#ifndef final_version if (hamachi_debug > 4) printk(KERN_DEBUG " hamachi_rx() normal Rx pkt length %d" " of %d, bogus_cnt %d.\n", pkt_len, data_size, boguscnt); if (hamachi_debug > 5) printk(KERN_DEBUG"%s: rx status %8.8x %8.8x %8.8x %8.8x %8.8x.\n", dev->name, *(s32*)&(buf_addr[data_size - 20]), *(s32*)&(buf_addr[data_size - 16]), *(s32*)&(buf_addr[data_size - 12]), *(s32*)&(buf_addr[data_size - 8]), *(s32*)&(buf_addr[data_size - 4]));#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) {#ifdef RX_CHECKSUM printk(KERN_ERR "%s: rx_copybreak non-zero " "not good with RX_CHECKSUM\n", dev->name);#endif skb->dev = dev; skb_reserve(skb, 2); /* 16 byte align the IP header */ /* Call copy + cksum if available. */#if 1 || USE_IP_COPYSUM eth_copy_and_sum(skb, hmp->rx_skbuff[entry]->data, pkt_len, 0); skb_put(skb, pkt_len);#else memcpy(skb_put(skb, pkt_len), hmp->rx_ring_dma + entry*sizeof(*desc), pkt_len);#endif } else { pci_unmap_single(hmp->pci_dev, hmp->rx_ring[entry].addr, hmp->rx_buf_sz, PCI_DMA_FROMDEVICE); skb_put(skb = hmp->rx_skbuff[entry], pkt_len); hmp->rx_skbuff[entry] = NULL; }#ifdef TX_CHECKSUM /* account for extra TX hard_header bytes */ skb->protocol = hamachi_eth_type_trans(skb, dev);#else skb->protocol = eth_type_trans(skb, dev);#endif#ifdef RX_CHECKSUM /* TCP or UDP on ipv4, DIX encoding */ if (pfck>>24 == 0x91 || pfck>>24 == 0x51) { struct iphdr *ih = (struct iphdr *) skb->data; /* Check that IP packet is at least 46 bytes, otherwise, * there may be pad bytes included in the hardware checksum. * This wouldn't happen if everyone padded with 0. */ if (ntohs(ih->tot_len) >= 46){ /* don't worry about frags */ if (!(ih->frag_off & __constant_htons(IP_MF|IP_OFFSET))) { u32 inv = *(u32 *) &buf_addr[data_size - 16]; u32 *p = (u32 *) &buf_addr[data_size - 20]; register u32 crc, p_r, p_r1; if (inv & 4) { inv &= ~4; --p; } p_r = *p; p_r1 = *(p-1); switch (inv) { case 0: crc = (p_r & 0xffff) + (p_r >> 16); break; case 1: crc = (p_r >> 16) + (p_r & 0xffff) + (p_r1 >> 16 & 0xff00); break; case 2: crc = p_r + (p_r1 >> 16); break; case 3: crc = p_r + (p_r1 & 0xff00) + (p_r1 >> 16); break; default: /*NOTREACHED*/ crc = 0; } if (crc & 0xffff0000) { crc &= 0xffff; ++crc; } /* tcp/udp will add in pseudo */ skb->csum = ntohs(pfck & 0xffff); if (skb->csum > crc) skb->csum -= crc; else skb->csum += (~crc & 0xffff); /* * could do the pseudo myself and return * CHECKSUM_UNNECESSARY */ skb->ip_summed = CHECKSUM_HW; } } }#endif /* RX_CHECKSUM */ netif_rx(skb); dev->last_rx = jiffies; hmp->stats.rx_packets++; } entry = (++hmp->cur_rx) % RX_RING_SIZE; } /* Refill the Rx ring buffers. */ for (; hmp->cur_rx - hmp->dirty_rx > 0; hmp->dirty_rx++) { struct hamachi_desc *desc; entry = hmp->dirty_rx % RX_RING_SIZE; desc = &(hmp->rx_ring[entry]); if (hmp->rx_skbuff[entry] == NULL) { struct sk_buff *skb = dev_alloc_skb(hmp->rx_buf_sz); hmp->rx_skbuff[entry] = skb; if (skb == NULL) break; /* Better luck next round. */ skb->dev = dev; /* Mark as being used by this device. */ skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ desc->addr = cpu_to_leXX(pci_map_single(hmp->pci_dev, skb->tail, hmp->rx_buf_sz, PCI_DMA_FROMDEVICE)); } desc->status_n_length = cpu_to_le32(hmp->rx_buf_sz); if (entry >= RX_RING_SIZE-1) desc->status_n_length |= cpu_to_le32(DescOwn | DescEndPacket | DescEndRing | DescIntr); else desc->status_n_length |= cpu_to_le32(DescOwn | DescEndPacket | DescIntr); } /* Restart Rx engine if stopped. */ /* If we don't need to check status, don't. -KDU */ if (readw(dev->base_addr + RxStatus) & 0x0002) writew(0x0001, dev->base_addr + RxCmd); return 0;}/* This is more properly named "uncommon interrupt events", as it covers more than just errors. */static void hamachi_error(struct net_device *dev, int intr_status){ long ioaddr = dev->base_addr; struct hamachi_private *hmp = dev->priv; if (intr_status & (LinkChange|NegotiationChange)) { if (hamachi_debug > 1) printk(KERN_INFO "%s: Link changed: AutoNegotiation Ctrl" " %4.4x, Status %4.4x %4.4x Intr status %4.4x.\n", dev->name, readw(ioaddr + 0x0E0), readw(ioaddr + 0x0E2), readw(ioaddr + ANLinkPartnerAbility), readl(ioaddr + IntrStatus)); if (readw(ioaddr + ANStatus) & 0x20) writeb(0x01, ioaddr + LEDCtrl); else writeb(0x03, ioaddr + LEDCtrl); } if (intr_status & StatsMax) { hamachi_get_stats(dev); /* Read the overflow bits to clear. */ readl(ioaddr + 0x370); readl(ioaddr + 0x3F0); } if ((intr_status & ~(LinkChange|StatsMax|NegotiationChange|IntrRxDone|IntrTxDone)) && hamachi_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 | IntrTxPCIFault)) hmp->stats.tx_fifo_errors++; if (intr_status & (IntrRxPCIErr | IntrRxPCIFault)) hmp->stats.rx_fifo_errors++;}static int hamachi_close(struct net_device *dev){ long ioaddr = dev->base_addr; struct hamachi_private *hmp = dev->priv; struct sk_buff *skb; int i; netif_stop_queue(dev); if (hamachi_debug > 1) { printk(KERN_DEBUG "%s: Shutting down ethercard, status was Tx %4.4x Rx %4.4x Int %2.2x.\n", dev->name, readw(ioaddr + TxStatus), readw(ioaddr + RxStatus), readl(ioaddr + IntrStatus)); printk(KERN_DEBUG "%s: Queue pointers were Tx %d / %d, Rx %d / %d.\n", dev->name, hmp->cur_tx, hmp->dirty_tx, hmp->cur_rx, hmp->dirty_rx); } /* Disable interrupts by clearing the interrupt mask. */ writel(0x0000, ioaddr + InterruptEnable); /* Stop the chip's Tx and Rx processes. */ writel(2, ioaddr + RxCmd); writew(2, ioaddr + TxCmd);#ifdef __i386__ if (hamachi_debug > 2) { printk("\n"KERN_DEBUG" Tx ring at %8.8x:\n", (int)hmp->tx_ring_dma); for (i = 0; i < TX_RING_SIZE; i++) printk(" %c #%d desc. %8.8x %8.8x.\n", readl(ioaddr + TxCurPtr) == (long)&hmp->tx_ring[i] ? '>' : ' ', i, hmp->tx_ring[i].status_n_length, hmp->tx_ring[i].addr); printk("\n"KERN_DEBUG " Rx ring %8.8x:\n", (int)hmp->rx_ring_dma); for (i = 0; i < RX_RING_SIZE; i++) { printk(KERN_DEBUG " %c #%d desc. %4.4x %8.8x\n", readl(ioaddr + RxCurPtr) == (long)&hmp->rx_ring[i] ? '>' : ' ', i, hmp->rx_ring[i].status_n_length, hmp->rx_ring[i].addr); if (hamachi_debug > 6) { if (*(u8*)hmp->rx_skbuff[i]->tail != 0x69) { u16 *addr = (u16 *) hmp->rx_skbuff[i]->tail; int j; for (j = 0; j < 0x50; j++) printk(" %4.4x", addr[j]); printk("\n"); } } } }#endif /* __i386__ debugging only */ free_irq(dev->irq, dev); del_timer_sync(&hmp->timer); /* Free all the skbuffs in the Rx queue. */ for (i = 0; i < RX_RING_SIZE; i++) { skb = hmp->rx_skbuff[i]; hmp->rx_ring[i].status_n_length = 0; hmp->rx_ring[i].addr = 0xBADF00D0; /* An invalid address. */ if (skb) { pci_unmap_single(hmp->pci_dev, hmp->rx_ring[i].addr, hmp->rx_buf_sz, PCI_DMA_FROMDEVICE); dev_kfree_skb(skb); hmp->rx_skbuff[i] = 0; } } for (i = 0; i < TX_RING_SIZE; i++) { skb = hmp->tx_skbuff[i]; if (skb) { pci_unmap_single(hmp->pci_dev, hmp->tx_ring[i].addr, skb->len, PCI_DMA_TODEVICE); dev_kfree_skb(skb); hmp->tx_skbuff[i] = 0; } } writeb(0x00, ioaddr + LEDCtrl); return 0;}static struct net_device_stats *hamachi_get_stats(struct net_device *dev){ long ioaddr = dev->base_addr; struct hamachi_private *hmp = dev->priv; /* We should lock this segment of code for SMP eventually, although the vulnerability window is very small and statistics are non-critical. */ /* Ok, what goes here? This appears to be stuck at 21 packets according to ifconfig. It does get incremented in hamachi_tx(), so I think I'll comment it out here and see if better things happen. */ /* hmp->stats.tx_packets = readl(ioaddr + 0x000); */ hmp->stats.rx_bytes = readl(ioaddr + 0x330); /* Total Uni+Brd+Multi */ hmp->stats.tx_bytes = readl(ioaddr + 0x3B0); /* Total Uni+Brd+Multi */ hmp->stats.multicast = readl(ioaddr + 0x320); /* Multicast Rx */ hmp->stats.rx_length_errors = readl(ioaddr + 0x368); /* Over+Undersized */ hmp->stats.rx_over_errors = readl(ioaddr + 0x35C); /* Jabber */ hmp->stats.rx_crc_errors = readl(ioaddr + 0x360); /* Jabber */ hmp->stats.rx_frame_errors = readl(ioaddr + 0x364); /* Symbol Errs */ hmp->stats.rx_missed_errors = readl(ioaddr + 0x36C); /* Dropped */ return &hmp->stats;}static void set_rx_mode(struct net_device *dev){ long ioaddr = dev->base_addr; if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ /* Unconditionally log net taps. */ printk(KERN_NOTICE "%s: Promiscuous mode enabled.\n", dev->name); writew(0x000F, ioaddr + AddrMode); } else if ((dev->mc_count > 63) || (dev->flags & IFF_ALLMULTI)) { /* Too many to match, or accept all multicasts. */ writew(0x000B, ioaddr + AddrMode); } else if (dev->mc_count > 0) { /* Must use the CAM filter. */ struct dev_mc_list *mclist; int i; for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; i++, mclist = mclist->next) { writel(*(u32*)(mclist->dmi_addr), ioaddr + 0x100 + i*8); writel(0x20000 | (*(u16*)&mclist->dmi_addr[4]), ioaddr + 0x104 + i*8); } /* Clear remaining entries. */ for (; i < 64; i++) writel(0, ioaddr + 0x104 + i*8); writew(0x0003, ioaddr + AddrMode); } else { /* Normal, unicast/broadcast-only mode. */ writew(0x0001, ioaddr + AddrMode); }}static int netdev_ethtool_ioctl(struct net_device *dev, void *useraddr){ struct hamachi_private *hmp = dev->priv; u32 ethcmd; if (copy_from_user(ðcmd, useraddr, sizeof(ethcmd))) return -EFAULT; switch (ethcmd) { case ETHTOOL_GDRVINFO: { struct ethtool_drvinfo info = {ETHTOOL_GDRVINFO}; strcpy(info.driver, DRV_NAME); strcpy(info.version, DRV_VERSION); strcpy(info.bus_info, hmp->pci_dev->slot_name); if (copy_to_user(useraddr, &info, sizeof(info))) return -EFAULT; return 0; } } return -EOPNOTSUPP;}static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd){ long ioaddr = dev->base_addr; struct mii_ioctl_data *data = (struct mii_ioctl_data *)&rq->ifr_data; switch(cmd) { case SIOCETHTOOL: return netdev_ethtool_ioctl(dev, (void *) rq->ifr_data); case SIOCGMIIPHY: /* Get address of MII PHY in use. */ case SIOCDEVPRIVATE: /* for binary compat, remove in 2.5 */ data->phy_id = ((struct hamachi_private *)dev->priv)->phys[0] & 0x1f; /* Fall Through */ case SIOCGMIIREG: /* Read MII PHY register. */ case SIOCDEVPRIVATE+1: /* for binary compat, remove in 2.5 */ data->val_out = mdio_read(ioaddr, data->phy_id & 0x1f, data->reg_num & 0x1f); return 0; case SIOCSMIIREG: /* Write MII PHY register. */ case SIOCDEVPRIVATE+2: /* for binary compat, remove in 2.5 */ /* TODO: Check the sequencing of this. Might need to stop and * restart Rx and Tx engines. -KDU */ if (!capable(CAP_NET_ADMIN)) return -EPERM; mdio_write(ioaddr, data->phy_id & 0x1f, data->reg_num & 0x1f, data-
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -