📄 hamachi.c
字号:
*/ hmp->rx_buf_sz = (dev->mtu <= 1492 ? PKT_BUF_SZ : (((dev->mtu+26+7) & ~7) + 2 + 16)); /* Initialize all Rx descriptors. */ for (i = 0; i < RX_RING_SIZE; i++) { hmp->rx_ring[i].status_n_length = 0; hmp->rx_skbuff[i] = 0; } /* Fill in the Rx buffers. Handle allocation failure gracefully. */ for (i = 0; i < RX_RING_SIZE; i++) { struct sk_buff *skb = dev_alloc_skb(hmp->rx_buf_sz); hmp->rx_skbuff[i] = skb; if (skb == NULL) break; skb->dev = dev; /* Mark as being used by this device. */ skb_reserve(skb, 2); /* 16 byte align the IP header. */ hmp->rx_ring[i].addr = cpu_to_leXX(pci_map_single(hmp->pci_dev, skb->tail, hmp->rx_buf_sz, PCI_DMA_FROMDEVICE)); /* -2 because it doesn't REALLY have that first 2 bytes -KDU */ hmp->rx_ring[i].status_n_length = cpu_to_le32(DescOwn | DescEndPacket | DescIntr | (hmp->rx_buf_sz -2)); } hmp->dirty_rx = (unsigned int)(i - RX_RING_SIZE); hmp->rx_ring[RX_RING_SIZE-1].status_n_length |= cpu_to_le32(DescEndRing); for (i = 0; i < TX_RING_SIZE; i++) { hmp->tx_skbuff[i] = 0; hmp->tx_ring[i].status_n_length = 0; } /* Mark the last entry of the ring */ hmp->tx_ring[TX_RING_SIZE-1].status_n_length |= cpu_to_le32(DescEndRing); return;}#ifdef TX_CHECKSUM#define csum_add(it, val) \do { \ it += (u16) (val); \ if (it & 0xffff0000) { \ it &= 0xffff; \ ++it; \ } \} while (0) /* printk("add %04x --> %04x\n", val, it); \ *//* uh->len already network format, do not swap */#define pseudo_csum_udp(sum,ih,uh) do { \ sum = 0; \ csum_add(sum, (ih)->saddr >> 16); \ csum_add(sum, (ih)->saddr & 0xffff); \ csum_add(sum, (ih)->daddr >> 16); \ csum_add(sum, (ih)->daddr & 0xffff); \ csum_add(sum, __constant_htons(IPPROTO_UDP)); \ csum_add(sum, (uh)->len); \} while (0)/* swap len */#define pseudo_csum_tcp(sum,ih,len) do { \ sum = 0; \ csum_add(sum, (ih)->saddr >> 16); \ csum_add(sum, (ih)->saddr & 0xffff); \ csum_add(sum, (ih)->daddr >> 16); \ csum_add(sum, (ih)->daddr & 0xffff); \ csum_add(sum, __constant_htons(IPPROTO_TCP)); \ csum_add(sum, htons(len)); \} while (0)#endifstatic int hamachi_start_xmit(struct sk_buff *skb, struct net_device *dev){ struct hamachi_private *hmp = dev->priv; unsigned entry; u16 status; /* Ok, now make sure that the queue has space before trying to add another skbuff. if we return non-zero the scheduler should interpret this as a queue full and requeue the buffer for later. */ if (hmp->tx_full) { /* We should NEVER reach this point -KDU */ printk(KERN_WARNING "%s: Hamachi transmit queue full at slot %d.\n",dev->name, hmp->cur_tx); /* Wake the potentially-idle transmit channel. */ /* If we don't need to read status, DON'T -KDU */ status=readw(dev->base_addr + TxStatus); if( !(status & 0x0001) || (status & 0x0002)) writew(0x0001, dev->base_addr + TxCmd); return 1; } /* Caution: the write order is important here, set the field with the "ownership" bits last. */ /* Calculate the next Tx descriptor entry. */ entry = hmp->cur_tx % TX_RING_SIZE; hmp->tx_skbuff[entry] = skb;#ifdef TX_CHECKSUM { /* tack on checksum tag */ u32 tagval = 0; struct ethhdr *eh = (struct ethhdr *)skb->data; if (eh->h_proto == __constant_htons(ETH_P_IP)) { struct iphdr *ih = (struct iphdr *)((char *)eh + ETH_HLEN); if (ih->protocol == IPPROTO_UDP) { struct udphdr *uh = (struct udphdr *)((char *)ih + ih->ihl*4); u32 offset = ((unsigned char *)uh + 6) - skb->data; u32 pseudo; pseudo_csum_udp(pseudo, ih, uh); pseudo = htons(pseudo); printk("udp cksum was %04x, sending pseudo %04x\n", uh->check, pseudo); uh->check = 0; /* zero out uh->check before card calc */ /* * start at 14 (skip ethhdr), store at offset (uh->check), * use pseudo value given. */ tagval = (14 << 24) | (offset << 16) | pseudo; } else if (ih->protocol == IPPROTO_TCP) { printk("tcp, no auto cksum\n"); } } *(u32 *)skb_push(skb, 8) = tagval; }#endif hmp->tx_ring[entry].addr = cpu_to_leXX(pci_map_single(hmp->pci_dev, skb->data, skb->len, PCI_DMA_TODEVICE)); /* Hmmmm, could probably put a DescIntr on these, but the way the driver is currently coded makes Tx interrupts unnecessary since the clearing of the Tx ring is handled by the start_xmit routine. This organization helps mitigate the interrupts a bit and probably renders the max_tx_latency param useless. Update: Putting a DescIntr bit on all of the descriptors and mitigating interrupt frequency with the tx_min_pkt parameter. -KDU */ if (entry >= TX_RING_SIZE-1) /* Wrap ring */ hmp->tx_ring[entry].status_n_length = cpu_to_le32(DescOwn | DescEndPacket | DescEndRing | DescIntr | skb->len); else hmp->tx_ring[entry].status_n_length = cpu_to_le32(DescOwn | DescEndPacket | DescIntr | skb->len); hmp->cur_tx++; /* Non-x86 Todo: explicitly flush cache lines here. */ /* Wake the potentially-idle transmit channel. */ /* If we don't need to read status, DON'T -KDU */ status=readw(dev->base_addr + TxStatus); if( !(status & 0x0001) || (status & 0x0002)) writew(0x0001, dev->base_addr + TxCmd); /* Immediately before returning, let's clear as many entries as we can. */ hamachi_tx(dev); /* We should kick the bottom half here, since we are not accepting * interrupts with every packet. i.e. realize that Gigabit ethernet * can transmit faster than ordinary machines can load packets; * hence, any packet that got put off because we were in the transmit * routine should IMMEDIATELY get a chance to be re-queued. -KDU */ if ((hmp->cur_tx - hmp->dirty_tx) < (TX_RING_SIZE - 4)) netif_wake_queue(dev); /* Typical path */ else { hmp->tx_full = 1; netif_stop_queue(dev); } dev->trans_start = jiffies; if (hamachi_debug > 4) { printk(KERN_DEBUG "%s: Hamachi transmit frame #%d queued in slot %d.\n", dev->name, hmp->cur_tx, entry); } return 0;}/* The interrupt handler does all of the Rx thread work and cleans up after the Tx thread. */static void hamachi_interrupt(int irq, void *dev_instance, struct pt_regs *rgs){ struct net_device *dev = dev_instance; struct hamachi_private *hmp; long ioaddr, boguscnt = max_interrupt_work;#ifndef final_version /* Can never occur. */ if (dev == NULL) { printk (KERN_ERR "hamachi_interrupt(): irq %d for unknown device.\n", irq); return; }#endif ioaddr = dev->base_addr; hmp = dev->priv; spin_lock(&hmp->lock); do { u32 intr_status = readl(ioaddr + InterruptClear); if (hamachi_debug > 4) printk(KERN_DEBUG "%s: Hamachi interrupt, status %4.4x.\n", dev->name, intr_status); if (intr_status == 0) break; if (intr_status & IntrRxDone) hamachi_rx(dev); if (intr_status & IntrTxDone){ /* This code should RARELY need to execute. After all, this is * a gigabit link, it should consume packets as fast as we put * them in AND we clear the Tx ring in hamachi_start_xmit(). */ if (hmp->tx_full){ for (; hmp->cur_tx - hmp->dirty_tx > 0; hmp->dirty_tx++){ int entry = hmp->dirty_tx % TX_RING_SIZE; struct sk_buff *skb; if (hmp->tx_ring[entry].status_n_length & cpu_to_le32(DescOwn)) break; skb = hmp->tx_skbuff[entry]; /* Free the original skb. */ if (skb){ pci_unmap_single(hmp->pci_dev, hmp->tx_ring[entry].addr, skb->len, PCI_DMA_TODEVICE); dev_kfree_skb_irq(skb); hmp->tx_skbuff[entry] = 0; } hmp->tx_ring[entry].status_n_length = 0; if (entry >= TX_RING_SIZE-1) hmp->tx_ring[TX_RING_SIZE-1].status_n_length |= cpu_to_le32(DescEndRing); hmp->stats.tx_packets++; } if (hmp->cur_tx - hmp->dirty_tx < TX_RING_SIZE - 4){ /* The ring is no longer full */ hmp->tx_full = 0; netif_wake_queue(dev); } } else { netif_wake_queue(dev); } } /* Abnormal error summary/uncommon events handlers. */ if (intr_status & (IntrTxPCIFault | IntrTxPCIErr | IntrRxPCIFault | IntrRxPCIErr | LinkChange | NegotiationChange | StatsMax)) hamachi_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 (hamachi_debug > 3) printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n", dev->name, readl(ioaddr + IntrStatus));#ifndef final_version /* Code that should never be run! Perhaps remove after testing.. */ { static int stopit = 10; if (dev->start == 0 && --stopit < 0) { printk(KERN_ERR "%s: Emergency stop, looping startup interrupt.\n", dev->name); free_irq(irq, dev); } }#endif spin_unlock(&hmp->lock);}#ifdef TX_CHECKSUM/* * Copied from eth_type_trans(), with reduced header, since we don't * get it on RX, only on TX. */static unsigned short hamachi_eth_type_trans(struct sk_buff *skb, struct net_device *dev){ struct ethhdr *eth; unsigned char *rawp; skb->mac.raw=skb->data; skb_pull(skb,dev->hard_header_len-8); /* artificially enlarged on tx */ eth= skb->mac.ethernet; if(*eth->h_dest&1) { if(memcmp(eth->h_dest,dev->broadcast, ETH_ALEN)==0) skb->pkt_type=PACKET_BROADCAST; else skb->pkt_type=PACKET_MULTICAST; } /* * This ALLMULTI check should be redundant by 1.4 * so don't forget to remove it. * * Seems, you forgot to remove it. All silly devices * seems to set IFF_PROMISC. */ else if(dev->flags&(IFF_PROMISC/*|IFF_ALLMULTI*/)) { if(memcmp(eth->h_dest,dev->dev_addr, ETH_ALEN)) skb->pkt_type=PACKET_OTHERHOST; } if (ntohs(eth->h_proto) >= 1536) return eth->h_proto; rawp = skb->data; /* * This is a magic hack to spot IPX packets. Older Novell breaks * the protocol design and runs IPX over 802.3 without an 802.2 LLC * layer. We look for FFFF which isn't a used 802.2 SSAP/DSAP. This * won't work for fault tolerant netware but does for the rest. */ if (*(unsigned short *)rawp == 0xFFFF) return htons(ETH_P_802_3); /* * Real 802.2 LLC */ return htons(ETH_P_802_2);}#endif /* TX_CHECKSUM *//* This routine is logically part of the interrupt handler, but seperated for clarity and better register allocation. */static int hamachi_rx(struct net_device *dev){ struct hamachi_private *hmp = dev->priv; int entry = hmp->cur_rx % RX_RING_SIZE; int boguscnt = (hmp->dirty_rx + RX_RING_SIZE) - hmp->cur_rx; if (hamachi_debug > 4) { printk(KERN_DEBUG " In hamachi_rx(), entry %d status %4.4x.\n", entry, hmp->rx_ring[entry].status_n_length); } /* If EOP is set on the next entry, it's a new packet. Send it up. */ while (1) { struct hamachi_desc *desc = &(hmp->rx_ring[entry]); u32 desc_status = le32_to_cpu(desc->status_n_length); u16 data_size = desc_status; /* Implicit truncate */ u8 *buf_addr; s32 frame_status; if (desc_status & DescOwn) break; pci_dma_sync_single(hmp->pci_dev, desc->addr, hmp->rx_buf_sz, PCI_DMA_FROMDEVICE); buf_addr = (u8 *)hmp->rx_ring + entry*sizeof(*desc); frame_status = le32_to_cpu(get_unaligned((s32*)&(buf_addr[data_size - 12]))); if (hamachi_debug > 4) printk(KERN_DEBUG " hamachi_rx() status was %8.8x.\n", frame_status); if (--boguscnt < 0) break; if ( ! (desc_status & DescEndPacket)) { printk(KERN_WARNING "%s: Oversized Ethernet frame spanned " "multiple buffers, entry %#x length %d status %4.4x!\n", dev->name, hmp->cur_rx, data_size, desc_status); printk(KERN_WARNING "%s: Oversized Ethernet frame %p vs %p.\n", dev->name, desc, &hmp->rx_ring[hmp->cur_rx % RX_RING_SIZE]); printk(KERN_WARNING "%s: Oversized Ethernet frame -- next status %x/%x last status %x.\n", dev->name, hmp->rx_ring[(hmp->cur_rx+1) % RX_RING_SIZE].status_n_length & 0xffff0000, hmp->rx_ring[(hmp->cur_rx+1) % RX_RING_SIZE].status_n_length & 0x0000ffff, hmp->rx_ring[(hmp->cur_rx-1) % RX_RING_SIZE].status_n_length); hmp->stats.rx_length_errors++; } /* else Omit for prototype errata??? */ if (frame_status & 0x00380000) { /* There was an error. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -