Supportandupdatesavailableat http://www.scyld.com/network/hamachi.html [linknolongerprovidesusefulinfo-jgarzik] or http://www.parl.clemson.edu/~keithu/hamachi.html
staticint debug = 1; /* 1 normal messages, 0 quiet .. 7 verbose. */ #define final_version #define hamachi_debug debug /* Maximum events (Rx packets, etc.) to handle at each interrupt. */ staticint max_interrupt_work = 40; staticint mtu; /* Default values selected by testing on a dual processor PIII-450 */ /* These six interrupt control parameters may be set directly when loading the *module,orthroughtherx_paramsandtx_paramsvariables
*/ staticint max_rx_latency = 0x11; staticint max_rx_gap = 0x05; staticint min_rx_pkt = 0x18; staticint max_tx_latency = 0x00; staticint max_tx_gap = 0x00; staticint min_tx_pkt = 0x30;
/* Set the copy breakpoint for the copy-only-tiny-frames scheme. -Settingto>1518causesallframestobecopied -Settingto0disablescopies
*/ staticint rx_copybreak;
/* An override for the hardware detection of bus width. Setto1toforce32bitPCIbusdetection.Setto4toforce64bit. Add2todisableparitydetection.
*/ staticint force32;
/* Used to pass the media type, etc. Theseexistfordriverinteroperability. Nomediatypesarecurrentlydefined. -Thelower4bitsarereservedforthemediatype. -Thenextthreebitsmaybesettooneofthefollowing: 0x00000000:AutodetectPCIbus 0x00000010:Force32bitPCIbus 0x00000020:Disableparitydetection 0x00000040:Force64bitPCIbus Defaultisautodetect -Thenextbitcanbeusedtoforcehalf-duplex.Thisisabad ideasincenoknownimplementationsimplementhalf-duplex,and, ingeneral,half-duplexforgigabitethernetisabadidea. 0x00000080:Forcehalf-duplex Defaultisfull-duplex. -Intheoriginaldriver,theninthbitcouldbeusedtoforce full-duplex.Maintainthatforcompatibility 0x00000200:Forcefull-duplex
*/ #define MAX_UNITS 8/* More are supported, limit only on options */ staticint options[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; staticint full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; /* The Hamachi chipset supports 3 parameters each for Rx and Tx *interruputmanagement.Parameterswillbeloadedasspecifiedinto *theTxIntControlandRxIntControlregisters. * *Theregistersarearrangedasfollows: *23-1615-87-0 *_________________________________ *|min_pkt|max_gap|max_latency| *--------------------------------- *min_pkt:Theminimumnumberofpacketsprocessedbetween *interrupts. *max_gap:Themaximuminter-packetgapinunitsof8.192us *max_latency:Theabsolutetimebetweeninterruptsinunitsof8.192us *
*/ staticint rx_params[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; staticint tx_params[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
/* Operational parameters that are set at compile time. */
/* Keep the ring sizes a power of two for compile efficiency. Thecompilerwillconvert<unsigned>'%'<2^N>intoabitmask. MakingtheTxringtoolargedecreasestheeffectivenessofchannel bondingandpacketpriority. Therearenoilleffectsfromtoo-largereceiverings,exceptfor
excessive memory usage */ /* Empirically it appears that the Tx ring needs to be a little bigger fortheseGbitadaptersoryougetintoanoverrunconditionreally easily.Also,thingsappeartoworkabitbetterinback-to-back configurationsiftheRxringis8timesthesizeoftheTxring
*/ #define TX_RING_SIZE 64 #define RX_RING_SIZE 512 #define TX_TOTAL_SIZE TX_RING_SIZE*sizeof(struct hamachi_desc) #define RX_TOTAL_SIZE RX_RING_SIZE*sizeof(struct hamachi_desc)
#include <linux/uaccess.h> #include <asm/processor.h> /* Processor type for cache alignment. */ #include <asm/io.h> #include <linux/unaligned.h> #include <asm/cache.h>
staticconstchar version[] =
KERN_INFO DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE " Written by Donald Becker\n" " Some modifications by Eric kasten <kasten@nscl.msu.edu>\n" " Further modifications by Keith Underwood <keithu@parl.clemson.edu>\n";
/* IP_MF appears to be only defined in <netinet/ip.h>, however, weneeditforhardwarechecksummingsupport.FYI...someof thedefinitionsin<netinet/ip.h>conflict/duplicatethosein otherlinuxheaderscausingmanycompilerwarnings.
*/ #ifndef IP_MF #define IP_MF 0x2000 /* IP more frags from <netinet/ip.h> */ #endif
/* Define IP_OFFSET to be IPOPT_OFFSET */ #ifndef IP_OFFSET #ifdef IPOPT_OFFSET #define IP_OFFSET IPOPT_OFFSET #else #define IP_OFFSET 2 #endif #endif
/* A few values that may be tweaked. */ /* Size of each temporary Rx buffer, calculated as: *1518bytes(ethernetpacket)+2bytes(toget8bytealignmentfor *thecard)+8bytesofstatusinfo+8bytesfortheRxChecksum
*/ #define PKT_BUF_SZ 1536
/* For now, this is going to be set to the maximum size of an ethernet *packet.Eventually,wemaywanttomakeitavariablethatis *relatedtotheMTU
*/ #define MAX_FRAME_SIZE 1518
/* The rest of these values should never change. */
staticint hamachi_init_one(struct pci_dev *pdev, conststruct pci_device_id *ent)
{ struct hamachi_private *hmp; int option, i, rx_int_var, tx_int_var, boguscnt; int chip_id = ent->driver_data; int irq; void __iomem *ioaddr; unsignedlong base; staticint card_idx; struct net_device *dev; void *ring_space;
dma_addr_t ring_dma; int ret = -ENOMEM;
u8 addr[ETH_ALEN];
/* when built into the kernel, we only print version if device is found */ #ifndef MODULE staticint printed_version; if (!printed_version++)
printk(version); #endif
if (pci_enable_device(pdev)) {
ret = -EIO; goto err_out;
}
base = pci_resource_start(pdev, 0); #ifdef __alpha__ /* Really "64 bit addrs" */
base |= (pci_resource_start(pdev, 1) << 32); #endif
pci_set_master(pdev);
i = pci_request_regions(pdev, DRV_NAME); if (i) return i;
/* Check for options being passed in */
option = card_idx < MAX_UNITS ? options[card_idx] : 0; if (dev->mem_start)
option = dev->mem_start;
/* If the bus size is misidentified, do the following. */
force32 = force32 ? force32 :
((option >= 0) ? ((option & 0x00000070) >> 4) : 0 ); if (force32)
writeb(force32, ioaddr + VirtualJumpers);
/* Hmmm, do we really need to reset the chip???. */
writeb(0x01, ioaddr + ChipReset);
/* After a reset, the clock speed measurement of the PCI bus will not *bevalidforamoment.Waitforalittlewhileuntilitis.If *ittakesmorethan10ms,forgetit.
*/
udelay(10);
i = readb(ioaddr + PCIClkMeas); for (boguscnt = 0; (!(i & 0x080)) && boguscnt < 1000; boguscnt++){
udelay(10);
i = readb(ioaddr + PCIClkMeas);
}
hmp->base = ioaddr;
pci_set_drvdata(pdev, dev);
hmp->chip_id = chip_id;
hmp->pci_dev = pdev;
/* The lower four bits are the media type. */ if (option > 0) {
hmp->option = option; if (option & 0x200)
hmp->mii_if.full_duplex = 1; elseif (option & 0x080)
hmp->mii_if.full_duplex = 0;
hmp->default_port = option & 15; if (hmp->default_port)
hmp->mii_if.force_media = 1;
} if (card_idx < MAX_UNITS && full_duplex[card_idx] > 0)
hmp->mii_if.full_duplex = 1;
/* lock the duplex mode if someone specified a value */ if (hmp->mii_if.full_duplex || (option & 0x080))
hmp->duplex_lock = 1;
staticint read_eeprom(void __iomem *ioaddr, int location)
{ int bogus_cnt = 1000;
/* We should check busy first - per docs -KDU */ while ((readb(ioaddr + EECmdStatus) & 0x40) && --bogus_cnt > 0);
writew(location, ioaddr + EEAddr);
writeb(0x02, ioaddr + EECmdStatus);
bogus_cnt = 1000; while ((readb(ioaddr + EECmdStatus) & 0x40) && --bogus_cnt > 0); if (hamachi_debug > 5)
printk(" EEPROM status is %2.2x after %d ticks.\n",
(int)readb(ioaddr + EECmdStatus), 1000- bogus_cnt); return readb(ioaddr + EEData);
}
/* MII Managemen Data I/O accesses. TheseroutinesassumetheMDIOcontrollerisidle,anddonotexituntil
the command is finished. */
staticint mdio_read(struct net_device *dev, int phy_id, int location)
{ struct hamachi_private *hmp = netdev_priv(dev); void __iomem *ioaddr = hmp->base; int i;
/* We should check busy first - per docs -KDU */ for (i = 10000; i >= 0; i--) if ((readw(ioaddr + MII_Status) & 1) == 0) break;
writew((phy_id<<8) + location, ioaddr + MII_Addr);
writew(0x0001, ioaddr + MII_Cmd); for (i = 10000; i >= 0; i--) if ((readw(ioaddr + MII_Status) & 1) == 0) break; return readw(ioaddr + MII_Rd_Data);
}
staticvoid mdio_write(struct net_device *dev, int phy_id, int location, int value)
{ struct hamachi_private *hmp = netdev_priv(dev); void __iomem *ioaddr = hmp->base; int i;
/* We should check busy first - per docs -KDU */ for (i = 10000; i >= 0; i--) if ((readw(ioaddr + MII_Status) & 1) == 0) break;
writew((phy_id<<8) + location, ioaddr + MII_Addr);
writew(value, ioaddr + MII_Wr_Data);
/* Wait for the command to finish. */ for (i = 10000; i >= 0; i--) if ((readw(ioaddr + MII_Status) & 1) == 0) break;
}
/* TODO: It would make sense to organize this as words since the card *documentationdoes.-KDU
*/ for (i = 0; i < 6; i++)
writeb(dev->dev_addr[i], ioaddr + StationAddr + i);
/* Initialize other registers: with so many this eventually this will
converted to an offset/value list. */
/* Configure the FIFO */
fifo_info = (readw(ioaddr + GPIO) & 0x00C0) >> 6; switch (fifo_info){ case0 : /* No FIFO */
writew(0x0000, ioaddr + FIFOcfg); break; case1 : /* Configure the FIFO for 512K external, 16K used for Tx. */
writew(0x0028, ioaddr + FIFOcfg); break; case2 : /* Configure the FIFO for 1024 external, 32K used for Tx. */
writew(0x004C, ioaddr + FIFOcfg); break; case3 : /* Configure the FIFO for 2048 external, 32K used for Tx. */
writew(0x006C, ioaddr + FIFOcfg); break; default :
printk(KERN_WARNING "%s: Unsupported external memory config!\n",
dev->name); /* Default to no FIFO */
writew(0x0000, ioaddr + FIFOcfg); break;
}
if (dev->if_port == 0)
dev->if_port = hmp->default_port;
/* Setting the Rx mode will start the Rx process. */ /* If someone didn't choose a duplex, default to full-duplex */ if (hmp->duplex_lock != 1)
hmp->mii_if.full_duplex = 1;
/* always 1, takes no more time to do it */
writew(0x0001, ioaddr + RxChecksum);
writew(0x0000, ioaddr + TxChecksum);
writew(0x8000, ioaddr + MACCnfg); /* Soft reset the MAC */
writew(0x215F, ioaddr + MACCnfg);
writew(0x000C, ioaddr + FrameGap0); /* WHAT?!?!? Why isn't this documented somewhere? -KDU */
writew(0x1018, ioaddr + FrameGap1); /* Why do we enable receives/transmits here? -KDU */
writew(0x0780, ioaddr + MACCnfg2); /* Upper 16 bits control LEDs. */ /* Enable automatic generation of flow control frames, period 0xffff. */
writel(0x0030FFFF, ioaddr + FlowCtrl);
writew(MAX_FRAME_SIZE, ioaddr + MaxFrameSize); /* dev->mtu+14 ??? */
/* Enable legacy links. */
writew(0x0400, ioaddr + ANXchngCtrl); /* Enable legacy links. */ /* Initial Link LED to blinking red. */
writeb(0x03, ioaddr + LEDCtrl);
/* Configure interrupt mitigation. This has a great effect on
performance, so systems tuning should start here!. */
/* Update the dirty pointer until we find an entry that is
still owned by the card */ 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; /* Free the original skb. */
skb = hmp->tx_skbuff[entry]; if (skb) {
dma_unmap_single(&hmp->pci_dev->dev,
leXX_to_cpu(hmp->tx_ring[entry].addr),
skb->len, DMA_TO_DEVICE);
dev_kfree_skb(skb);
hmp->tx_skbuff[entry] = NULL;
}
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);
dev->stats.tx_packets++;
}
{
printk(KERN_DEBUG " Rx ring %p: ", hmp->rx_ring); for (i = 0; i < RX_RING_SIZE; i++)
printk(KERN_CONT " %8.8x",
le32_to_cpu(hmp->rx_ring[i].status_n_length));
printk(KERN_CONT "\n");
printk(KERN_DEBUG" Tx ring %p: ", hmp->tx_ring); for (i = 0; i < TX_RING_SIZE; i++)
printk(KERN_CONT " %4.4x",
le32_to_cpu(hmp->tx_ring[i].status_n_length));
printk(KERN_CONT "\n");
}
/* Reinit the hardware and make sure the Rx and Tx processes areupandrunning.
*/
dev->if_port = 0; /* The right way to do Reset. -KDU *-ClearOWNbitinallRx/Txdescriptors *-Wait50uSforchannelstogoidle *-TurnoffMACreceiver *-IssueReset
*/
for (i = 0; i < RX_RING_SIZE; i++)
hmp->rx_ring[i].status_n_length &= cpu_to_le32(~DescOwn);
/* Presume that all packets in the Tx queue are gone if we have to *re-initthehardware.
*/ for (i = 0; i < TX_RING_SIZE; i++){ struct sk_buff *skb;
udelay(60); /* Sleep 60 us just for safety sake */
writew(0x0002, ioaddr + RxCmd); /* STOP Rx */
writeb(0x01, ioaddr + ChipReset); /* Reinit the hardware */
hmp->tx_full = 0;
hmp->cur_rx = hmp->cur_tx = 0;
hmp->dirty_rx = hmp->dirty_tx = 0; /* Rx packets are also presumed lost; however, we need to make sure a *ringofbuffersisintact.-KDU
*/ for (i = 0; i < RX_RING_SIZE; i++){ struct sk_buff *skb = hmp->rx_skbuff[i];
if (skb){
dma_unmap_single(&hmp->pci_dev->dev,
leXX_to_cpu(hmp->rx_ring[i].addr),
hmp->rx_buf_sz, DMA_FROM_DEVICE);
dev_kfree_skb(skb);
hmp->rx_skbuff[i] = NULL;
}
} /* Fill in the Rx buffers. Handle allocation failure gracefully. */ for (i = 0; i < RX_RING_SIZE; i++) { struct sk_buff *skb;
/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ staticvoid hamachi_init_ring(struct net_device *dev)
{ struct hamachi_private *hmp = netdev_priv(dev); int i;
/* +26 gets the maximum ethernet encapsulation, +7 & ~7 because the *cardneedsroomtodo8bytealignment,+2sowecanreserve *thefirst2bytes,and+16getsroomforthestatuswordfromthe *card.-KDU
*/
hmp->rx_buf_sz = (dev->mtu <= 1492 ? PKT_BUF_SZ :
(((dev->mtu+26+7) & ~7) + 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] = NULL;
} /* Fill in the Rx buffers. Handle allocation failure gracefully. */ for (i = 0; i < RX_RING_SIZE; i++) { struct sk_buff *skb = netdev_alloc_skb(dev, hmp->rx_buf_sz + 2);
hmp->rx_skbuff[i] = skb; if (skb == NULL) break;
skb_reserve(skb, 2); /* 16 byte align the IP header. */
hmp->rx_ring[i].addr = cpu_to_leXX(dma_map_single(&hmp->pci_dev->dev,
skb->data,
hmp->rx_buf_sz,
DMA_FROM_DEVICE)); /* -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 = (unsignedint)(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] = NULL;
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);
}
/* Ok, now make sure that the queue has space before trying to addanotherskbuff.ifwereturnnon-zerothescheduler shouldinterpretthisasaqueuefullandrequeuethebuffer forlater.
*/ 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(hmp->base + TxStatus); if( !(status & 0x0001) || (status & 0x0002))
writew(0x0001, hmp->base + TxCmd); return NETDEV_TX_BUSY;
}
/* 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;
/* Hmmmm, could probably put a DescIntr on these, but the way thedriveriscurrentlycodedmakesTxinterruptsunnecessary sincetheclearingoftheTxringishandledbythestart_xmit routine.Thisorganizationhelpsmitigatetheinterruptsa bitandprobablyrendersthemax_tx_latencyparamuseless.
/* Wake the potentially-idle transmit channel. */ /* If we don't need to read status, DON'T -KDU */
status=readw(hmp->base + TxStatus); if( !(status & 0x0001) || (status & 0x0002))
writew(0x0001, hmp->base + 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 *interruptswitheverypacket.i.e.realizethatGigabitethernet *cantransmitfasterthanordinarymachinescanloadpackets; *hence,anypacketthatgotputoffbecausewewereinthetransmit *routineshouldIMMEDIATELYgetachancetobere-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);
}
/* The interrupt handler does all of the Rx thread work and cleans up
after the Tx thread. */ static irqreturn_t hamachi_interrupt(int irq, void *dev_instance)
{ struct net_device *dev = dev_instance; struct hamachi_private *hmp = netdev_priv(dev); void __iomem *ioaddr = hmp->base; long boguscnt = max_interrupt_work; int handled = 0;
#ifndef final_version /* Can never occur. */ if (dev == NULL) {
printk (KERN_ERR "hamachi_interrupt(): irq %d for unknown device.\n", irq); return IRQ_NONE;
} #endif
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;
handled = 1;
if (intr_status & IntrRxDone)
hamachi_rx(dev);
if (intr_status & IntrTxDone){ /* This code should RARELY need to execute. After all, this is *agigabitlink,itshouldconsumepacketsasfastasweput *theminANDwecleartheTxringinhamachi_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){
dma_unmap_single(&hmp->pci_dev->dev,
leXX_to_cpu(hmp->tx_ring[entry].addr),
skb->len,
DMA_TO_DEVICE);
dev_consume_skb_irq(skb);
hmp->tx_skbuff[entry] = NULL;
}
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);
dev->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);
}
}
/* This routine is logically part of the interrupt handler, but separated
for clarity and better register allocation. */ staticint hamachi_rx(struct net_device *dev)
{ struct hamachi_private *hmp = netdev_priv(dev); 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;
dma_sync_single_for_cpu(&hmp->pci_dev->dev,
leXX_to_cpu(desc->addr),
hmp->rx_buf_sz, DMA_FROM_DEVICE);
buf_addr = (u8 *) hmp->rx_skbuff[entry]->data;
frame_status = get_unaligned_le32(&(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,
le32_to_cpu(hmp->rx_ring[(hmp->cur_rx+1) % RX_RING_SIZE].status_n_length) & 0xffff0000,
le32_to_cpu(hmp->rx_ring[(hmp->cur_rx+1) % RX_RING_SIZE].status_n_length) & 0x0000ffff,
le32_to_cpu(hmp->rx_ring[(hmp->cur_rx-1) % RX_RING_SIZE].status_n_length));
dev->stats.rx_length_errors++;
} /* else Omit for prototype errata??? */ if (frame_status & 0x00380000) { /* There was an error. */ if (hamachi_debug > 2)
printk(KERN_DEBUG " hamachi_rx() Rx error was %8.8x.\n",
frame_status);
dev->stats.rx_errors++; if (frame_status & 0x00600000)
dev->stats.rx_length_errors++; if (frame_status & 0x00080000)
dev->stats.rx_frame_errors++; if (frame_status & 0x00100000)
dev->stats.rx_crc_errors++; if (frame_status < 0)
dev->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 = netdev_alloc_skb(dev, 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_reserve(skb, 2); /* 16 byte align the IP header */
dma_sync_single_for_cpu(&hmp->pci_dev->dev,
leXX_to_cpu(hmp->rx_ring[entry].addr),
hmp->rx_buf_sz,
DMA_FROM_DEVICE); /* Call copy + cksum if available. */ #if1 || USE_IP_COPYSUM
skb_copy_to_linear_data(skb,
hmp->rx_skbuff[entry]->data, pkt_len);
skb_put(skb, pkt_len); #else
skb_put_data(skb, hmp->rx_ring_dma
+ entry*sizeof(*desc), pkt_len); #endif
dma_sync_single_for_device(&hmp->pci_dev->dev,
leXX_to_cpu(hmp->rx_ring[entry].addr),
hmp->rx_buf_sz,
DMA_FROM_DEVICE);
} else {
dma_unmap_single(&hmp->pci_dev->dev,
leXX_to_cpu(hmp->rx_ring[entry].addr),
hmp->rx_buf_sz,
DMA_FROM_DEVICE);
skb_put(skb = hmp->rx_skbuff[entry], pkt_len);
hmp->rx_skbuff[entry] = NULL;
}
skb->protocol = eth_type_trans(skb, dev);
#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, *theremaybepadbytesincludedinthehardwarechecksum. *Thiswouldn'thappenifeveryonepaddedwith0.
*/ if (ntohs(ih->tot_len) >= 46){ /* don't worry about frags */ if (!(ih->frag_off & cpu_to_be16(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;
/* Restart Rx engine if stopped. */ /* If we don't need to check status, don't. -KDU */ if (readw(hmp->base + RxStatus) & 0x0002)
writew(0x0001, hmp->base + RxCmd);
return0;
}
/* This is more properly named "uncommon interrupt events", as it covers more
than just errors. */ staticvoid hamachi_error(struct net_device *dev, int intr_status)
{ struct hamachi_private *hmp = netdev_priv(dev); void __iomem *ioaddr = hmp->base;
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))
dev->stats.tx_fifo_errors++; if (intr_status & (IntrRxPCIErr | IntrRxPCIFault))
dev->stats.rx_fifo_errors++;
}
/* We should lock this segment of code for SMP eventually, although thevulnerabilitywindowisverysmallandstatisticsare
non-critical. */ /* Ok, what goes here? This appears to be stuck at 21 packets accordingtoifconfig.Itdoesgetincrementedinhamachi_tx(), soIthinkI'llcommentitouthereandseeifbetterthings happen.
*/ /* dev->stats.tx_packets = readl(ioaddr + 0x000); */
if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */
writew(0x000F, ioaddr + AddrMode);
} elseif ((netdev_mc_count(dev) > 63) || (dev->flags & IFF_ALLMULTI)) { /* Too many to match, or accept all multicasts. */
writew(0x000B, ioaddr + AddrMode);
} elseif (!netdev_mc_empty(dev)) { /* Must use the CAM filter. */ struct netdev_hw_addr *ha; int i = 0;
if (cmd != SIOCDEVPRIVATE + 3) return -EOPNOTSUPP;
/* Should add this check here or an ordinary user can do nasty *things.-KDU * *TODO:ShutdowntheRxandTxengineswhiledoingthis.
*/ if (!capable(CAP_NET_ADMIN)) return -EPERM;
writel(d[0], np->base + TxIntrCtrl);
writel(d[1], np->base + RxIntrCtrl);
printk(KERN_NOTICE "%s: tx %08x, rx %08x intr\n", dev->name,
(u32)readl(np->base + TxIntrCtrl),
(u32)readl(np->base + RxIntrCtrl));
staticint __init hamachi_init (void)
{ /* when a module, this is printed whether or not devices are found in probe */ #ifdef MODULE
printk(version); #endif return pci_register_driver(&hamachi_driver);
}
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.75Bemerkung:
(vorverarbeitet am 2026-10-02)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.