Hi Alexey, On Sat, 27 Jan 2001 [EMAIL PROTECTED] wrote: > > fits the new Linux model a bit better, as it has one descriptor per > > packet, not one per fragment (like the current implementation). > > Yes. Absence of such mode with acenic is big pain in ass. And, at least for the starfire, using buffer descriptors eats up more resources than using packet descriptors. So buffer descriptors are gone, forever. > Seems, this is pretty common bug. At least, perfect checksumming > of chunks with any size and alignment is so big boast of > alteon people, that it is clearly rather exception than rule. 8)8) Oh well. Don't we wish hardware were perfect.. :-) It's not too bad for the starfire, either, as it doesn't appear to suffer from any alignment issues, and the only fragments it has problem with are 1-byte fragments (2-byte and larger are ok). And I'm still hoping that Adaptec will release updated firmware (and that they will also approve the inclusion of their firmware with the Linux driver). If they don't approve it, maybe I'll add a config option so that the user can download their own copy of the firmware from Adaptec's site, place it in /etc somewhere, give the location to the driver and have the driver open the files from kernel space and process them. It would be ugly though, and a big PITA. > I think you have to check for wrong combination of alignment/size and > to call skb_checksum_help() and to disable checksumming if combination > is bad. Good, thanks a lot for the pointer. Maybe I went a bit overboard with my fix, as I revert to CPU checksumming if *any* fragment (not just the last one in the chain) has only 1 byte. But it seems to work well now. I've attached a diff for the latest driver (and firmware) version, against 2.4.1pre10+zerocopy. Sorry about MIME, but my pine is currently broken (strips trailing spaces/tabs). And, one final question: is it worth it for me to extract just the Rx checksumming changes and send them to Linux/Alan, or should I just wait for zerocopy to be included? Thanks, Ion -- It is better to keep your mouth shut and be thought a fool, than to open it and remove all doubt.
diff -urNX diff_kernel_excludes /usr/src/local/linux-2.4.vanilla/drivers/net/starfire.c linux-2.4/drivers/net/starfire.c --- /usr/src/local/linux-2.4.vanilla/drivers/net/starfire.c Fri Aug 11 15:57:58 2000 +++ linux-2.4/drivers/net/starfire.c Sat Jan 27 12:44:35 2001 @@ -34,6 +34,10 @@ LK1.1.4 (jgarzik): - Merge Becker version 1.03 + + LK1.2.0 (Ion Badulescu <[EMAIL PROTECTED]>) + - Support hardware Rx/Tx checksumming + - Add GFP firmware taken from Adaptec's Netware driver */ /* These identify the driver base version and may not be removed. */ @@ -43,11 +47,17 @@ " Updates and info at http://www.scyld.com/network/starfire.html\n"; static const char version3[] = -" (unofficial 2.4.x kernel port, version 1.1.4, August 10, 2000)\n"; +" (unofficial 2.4.x kernel port, version 1.2.0, January 25, 2001)\n"; /* The user-configurable values. These may be modified when a driver module is loaded.*/ +/* + * The current frame processor firmware fails to checksum a fragment + * of length 1. If and when this is fixed, the #define below can be removed. + */ +#define HAS_BROKEN_FIRMWARE + /* Used for tuning interrupt latency vs. overhead. */ static int interrupt_mitigation = 0x0; @@ -117,12 +127,15 @@ #include <asm/bitops.h> #include <asm/io.h> +#include "starfire_firmware.h" + MODULE_AUTHOR("Donald Becker <[EMAIL PROTECTED]>"); MODULE_DESCRIPTION("Adaptec Starfire Ethernet driver"); MODULE_PARM(max_interrupt_work, "i"); MODULE_PARM(mtu, "i"); MODULE_PARM(debug, "i"); MODULE_PARM(rx_copybreak, "i"); +MODULE_PARM(interrupt_mitigation, "i"); MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i"); MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); @@ -256,19 +269,31 @@ TxThreshold=0x500B0, CompletionHiAddr=0x500B4, TxCompletionAddr=0x500B8, RxCompletionAddr=0x500BC, RxCompletionQ2Addr=0x500C0, - CompletionQConsumerIdx=0x500C4, + CompletionQConsumerIdx=0x500C4, RxDMACtrl=0x500D0, RxDescQCtrl=0x500D4, RxDescQHiAddr=0x500DC, RxDescQAddr=0x500E0, RxDescQIdx=0x500E8, RxDMAStatus=0x500F0, RxFilterMode=0x500F4, - TxMode=0x55000, + TxMode=0x55000, TxGfpMem=0x58000, RxGfpMem=0x5a000, }; /* Bits in the interrupt status/mask registers. */ enum intr_status_bits { - IntrNormalSummary=0x8000, IntrAbnormalSummary=0x02000000, - IntrRxDone=0x0300, IntrRxEmpty=0x10040, IntrRxPCIErr=0x80000, - IntrTxDone=0x4000, IntrTxEmpty=0x1000, IntrTxPCIErr=0x80000, - StatsMax=0x08000000, LinkChange=0xf0000000, - IntrTxDataLow=0x00040000, + IntrLinkChange=0xf0000000, IntrStatsMax=0x08000000, + IntrAbnormalSummary=0x02000000, IntrGeneralTimer=0x01000000, + IntrSoftware=0x800000, IntrRxComplQ1Low=0x400000, + IntrTxComplQLow=0x200000, IntrPCI=0x100000, + IntrDMAErr=0x080000, IntrTxDataLow=0x040000, + IntrRxComplQ2Low=0x020000, IntrRxDescQ1Low=0x010000, + IntrNormalSummary=0x8000, IntrTxDone=0x4000, + IntrTxDMADone=0x2000, IntrTxEmpty=0x1000, + IntrEarlyRxQ2=0x0800, IntrEarlyRxQ1=0x0400, + IntrRxQ2Done=0x0200, IntrRxQ1Done=0x0100, + IntrRxGFPDead=0x80, IntrRxDescQ2Low=0x40, + IntrNoTxCsum=0x20, IntrTxBadID=0x10, + IntrHiPriTxBadID=0x08, IntrRxGfp=0x04, + IntrTxGfp=0x02, IntrPCIPad=0x01, + /* not quite bits */ + IntrRxDone=IntrRxQ2Done | IntrRxQ1Done, + IntrRxEmpty=IntrRxDescQ1Low | IntrRxDescQ2Low, }; /* Bits in the RxFilterMode register. */ @@ -277,6 +302,37 @@ AcceptMulticast=0x10, AcceptMyPhys=0xE040, }; +/* Bits in the TxDescCtrl register. */ +enum tx_ctrl_bits { + TxDescSpaceUnlim=0x00, TxDescSpace32=0x10, TxDescSpace64=0x20, + TxDescSpace128=0x30, TxDescSpace256=0x40, + TxDescType0=0x00, TxDescType1=0x01, TxDescType2=0x02, + TxDescType3=0x03, TxDescType4=0x04, + TxNoDMACompletion=0x08, TxDescQ64bit=0x80, + TxHiPriFIFOThreshShift=24, TxPadLenShift=16, + TxDMABurstSizeShift=8, +}; + +/* Bits in the RxDescQCtrl register. */ +enum rx_ctrl_bits { + RxBufferLenShift=16, RxMinDescrThreshShift=0, + RxPrefetchMode=0x8000, Rx2048QEntries=0x4000, + RxVariableQ=0x2000, RxDesc64bit=0x1000, + RxDescQAddr64bit=0x0100, + RxDescSpace4=0x000, RxDescSpace8=0x100, + RxDescSpace16=0x200, RxDescSpace32=0x300, + RxDescSpace64=0x400, RxDescSpace128=0x500, + RxConsumerWrEn=0x80, +}; + +/* Bits in the RxCompletionAddr register */ +enum rx_compl_bits { + RxComplQAddr64bit=0x80, TxComplProducerWrEn=0x40, + RxComplType0=0x00, RxComplType1=0x10, + RxComplType2=0x20, RxComplType3=0x30, + RxComplThreshShift=0, +}; + /* The Rx and Tx buffer descriptors. */ struct starfire_rx_desc { u32 rxaddr; /* Optionally 64 bits. */ @@ -288,8 +344,12 @@ /* Completion queue entry. You must update the page allocation, init_ring and the shift count in rx() if using a larger format. */ +#define csum_rx_status struct rx_done_desc { u32 status; /* Low 16 bits is length. */ +#ifdef csum_rx_status + u32 status2; /* Low 16 bits is csum */ +#endif #ifdef full_rx_status u32 status2; u16 vlanid; @@ -301,14 +361,24 @@ RxOK=0x20000000, RxFIFOErr=0x10000000, RxBufQ2=0x08000000, }; -/* Type 1 Tx descriptor. */ +/* Type 0 Tx descriptor. */ +/* If more fragments are needed, don't forget to change the + descriptor spacing as well! */ struct starfire_tx_desc { - u32 status; /* Upper bits are status, lower 16 length. */ - u32 addr; + u32 status; + u32 nbufs; + u32 first_addr; + u16 first_len; + u16 total_len; + struct { + u32 addr; + u32 len; + } frag[6]; }; enum tx_desc_bits { - TxDescID=0xB1010000, /* Also marks single fragment, add CRC. */ - TxDescIntr=0x08000000, TxRingWrap=0x04000000, + TxDescID=0xB0000000, + TxCRCEn=0x01000000, TxDescIntr=0x08000000, + TxRingWrap=0x04000000, TxCalTCP=0x02000000, }; struct tx_done_report { u32 status; /* timestamp, index. */ @@ -318,10 +388,15 @@ }; #define PRIV_ALIGN 15 /* Required alignment mask */ -struct ring_info { +struct rx_ring_info { struct sk_buff *skb; dma_addr_t mapping; }; +struct tx_ring_info { + struct sk_buff *skb; + dma_addr_t first_mapping; + dma_addr_t frag_mapping[6]; +}; struct netdev_private { /* Descriptor rings first for alignment. */ @@ -330,8 +405,8 @@ dma_addr_t rx_ring_dma; dma_addr_t tx_ring_dma; /* The addresses of rx/tx-in-place skbuffs. */ - struct ring_info rx_info[RX_RING_SIZE]; - struct ring_info tx_info[TX_RING_SIZE]; + struct rx_ring_info rx_info[RX_RING_SIZE]; + struct tx_ring_info tx_info[TX_RING_SIZE]; /* Pointers to completion queues (full pages). I should cache line pad..*/ u8 pad0[100]; struct rx_done_desc *rx_done_q; @@ -390,16 +465,17 @@ static int card_idx = -1; static int printed_version = 0; long ioaddr; - int drv_flags, io_size = netdrv_tbl[chip_idx].io_size; + int drv_flags, io_size; card_idx++; option = card_idx < MAX_UNITS ? options[card_idx] : 0; - + if (!printed_version++) printk(KERN_INFO "%s" KERN_INFO "%s" KERN_INFO "%s", version1, version2, version3); ioaddr = pci_resource_start (pdev, 0); + io_size = pci_resource_len (pdev, 0); if (!ioaddr || ((pci_resource_flags (pdev, 0) & IORESOURCE_MEM) == 0)) { printk (KERN_ERR "starfire %d: no PCI MEM resources, aborting\n", card_idx); return -ENODEV; @@ -410,6 +486,7 @@ printk (KERN_ERR "starfire %d: cannot alloc etherdev, aborting\n", card_idx); return -ENOMEM; } + SET_MODULE_OWNER(dev); irq = pdev->irq; @@ -434,6 +511,9 @@ printk(KERN_INFO "%s: %s at 0x%lx, ", dev->name, netdrv_tbl[chip_idx].name, ioaddr); + /* Starfire can do SG and TCP/UDP checksumming */ + dev->features |= NETIF_F_SG | NETIF_F_IP_CSUM; + /* Serial EEPROM reads are hidden by the hardware. */ for (i = 0; i < 6; i++) dev->dev_addr[i] = readb(ioaddr + EEPROMCtrl + 20-i); @@ -546,13 +626,9 @@ /* Do we ever need to reset the chip??? */ - MOD_INC_USE_COUNT; - retval = request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev); - if (retval) { - MOD_DEC_USE_COUNT; + if (retval) return retval; - } /* Disable the Rx and Tx, and reset the chip. */ writel(0, ioaddr + GenCtrl); @@ -564,7 +640,7 @@ if (np->tx_done_q == 0) np->tx_done_q = pci_alloc_consistent(np->pci_dev, PAGE_SIZE, &np->tx_done_q_dma); if (np->rx_done_q == 0) - np->rx_done_q = pci_alloc_consistent(np->pci_dev, PAGE_SIZE, &np->rx_done_q_dma); + np->rx_done_q = pci_alloc_consistent(np->pci_dev, sizeof(struct +rx_done_desc) * DONE_Q_SIZE, &np->rx_done_q_dma); if (np->tx_ring == 0) np->tx_ring = pci_alloc_consistent(np->pci_dev, PAGE_SIZE, &np->tx_ring_dma); if (np->rx_ring == 0) @@ -575,7 +651,7 @@ 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, + pci_free_consistent(np->pci_dev, sizeof(struct rx_done_desc) * +DONE_Q_SIZE, np->rx_done_q, np->rx_done_q_dma); if (np->tx_ring) pci_free_consistent(np->pci_dev, PAGE_SIZE, @@ -583,16 +659,23 @@ if (np->rx_ring) pci_free_consistent(np->pci_dev, PAGE_SIZE, np->rx_ring, np->rx_ring_dma); - MOD_DEC_USE_COUNT; return -ENOMEM; } init_ring(dev); /* Set the size of the Rx buffers. */ - writel((np->rx_buf_sz<<16) | 0xA000, ioaddr + RxDescQCtrl); - - /* Set Tx descriptor to type 1 and padding to 0 bytes. */ - writel(0x02000401, ioaddr + TxDescCtrl); + writel((np->rx_buf_sz << RxBufferLenShift) | + (0 << RxMinDescrThreshShift) | + RxPrefetchMode | RxVariableQ | + RxDescSpace4, + ioaddr + RxDescQCtrl); + + /* Set Tx descriptor to type 0 and spacing to 64 bytes. */ + writel((2 << TxHiPriFIFOThreshShift) | + (0 << TxPadLenShift) | + (4 << TxDMABurstSizeShift) | + TxDescSpace64 | TxDescType0, + ioaddr + TxDescCtrl); #if defined(ADDR_64BITS) && defined(__alpha__) /* XXX We really need a 64-bit PCI dma interfaces too... -DaveM */ @@ -607,7 +690,24 @@ writel(np->tx_ring_dma, ioaddr + TxRingPtr); writel(np->tx_done_q_dma, ioaddr + TxCompletionAddr); - writel(np->rx_done_q_dma, ioaddr + RxCompletionAddr); +#ifdef full_rx_status + writel(np->rx_done_q_dma | + RxComplType3 | + (0 << RxComplThreshShift), + ioaddr + RxCompletionAddr); +#else +#ifdef csum_rx_status + writel(np->rx_done_q_dma | + RxComplType2 | + (0 << RxComplThreshShift), + ioaddr + RxCompletionAddr); +#else + writel(np->rx_done_q_dma | + RxComplType0 | + (0 << RxComplThreshShift), + ioaddr + RxCompletionAddr); +#endif +#endif if (debug > 1) printk(KERN_DEBUG "%s: Filling in the station address.\n", dev->name); @@ -643,15 +743,21 @@ check_duplex(dev, 1); /* Set the interrupt mask and enable PCI interrupts. */ - writel(IntrRxDone | IntrRxEmpty | IntrRxPCIErr | - IntrTxDone | IntrTxEmpty | IntrTxPCIErr | - StatsMax | LinkChange | IntrNormalSummary | IntrAbnormalSummary - | 0x0010 , ioaddr + IntrEnable); + writel(IntrRxDone | IntrRxEmpty | IntrDMAErr | + IntrTxDone | IntrStatsMax | IntrLinkChange | + IntrNormalSummary | IntrAbnormalSummary | + IntrRxGFPDead | IntrNoTxCsum | IntrTxBadID, + ioaddr + IntrEnable); writel(0x00800000 | readl(ioaddr + PCIDeviceConfig), ioaddr + PCIDeviceConfig); - /* Enable the Rx and Tx units. */ - writel(0x000F, ioaddr + GenCtrl); + /* Load Rx/Tx firmware into the frame processors */ + for (i = 0; i < FIRMWARE_RX_SIZE * 2; i++) + writel(cpu_to_le32(firmware_rx[i]), ioaddr + RxGfpMem + i * 4); + for (i = 0; i < FIRMWARE_TX_SIZE * 2; i++) + writel(cpu_to_le32(firmware_tx[i]), ioaddr + TxGfpMem + i * 4); + /* Enable the Rx and Tx units, and the Rx/Tx frame processors. */ + writel(0x003F, ioaddr + GenCtrl); if (debug > 2) printk(KERN_DEBUG "%s: Done netdev_open().\n", @@ -765,7 +871,7 @@ static void init_ring(struct net_device *dev) { struct netdev_private *np = (struct netdev_private *)dev->priv; - int i; + int i, j; np->tx_full = 0; np->cur_rx = np->cur_tx = 0; @@ -804,7 +910,9 @@ for (i = 0; i < TX_RING_SIZE; i++) { np->tx_info[i].skb = NULL; - np->tx_info[i].mapping = 0; + np->tx_info[i].first_mapping = 0; + for (j = 0; j < 6; j++) + np->tx_info[i].frag_mapping[j] = 0; np->tx_ring[i].status = 0; } return; @@ -814,6 +922,7 @@ { struct netdev_private *np = (struct netdev_private *)dev->priv; unsigned entry; + int i; /* Caution: the write order is important here, set the field with the "ownership" bits last. */ @@ -821,42 +930,87 @@ /* Calculate the next Tx descriptor entry. */ entry = np->cur_tx % TX_RING_SIZE; +#ifdef HAS_BROKEN_FIRMWARE + { + int has_bad_length = 0; + + if (skb_headlen(skb) == 1) + has_bad_length = 1; + else { + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) + if (skb_shinfo(skb)->frags[i].size == 1) { + has_bad_length = 1; + break; + } + } + + if (has_bad_length) + skb_checksum_help(skb); + } +#endif /* HAS_BROKEN_FIRMWARE */ + np->tx_info[entry].skb = skb; - np->tx_info[entry].mapping = - pci_map_single(np->pci_dev, skb->data, skb->len, PCI_DMA_TODEVICE); + np->tx_info[entry].first_mapping = + pci_map_single(np->pci_dev, skb->data, skb_headlen(skb), +PCI_DMA_TODEVICE); - np->tx_ring[entry].addr = cpu_to_le32(np->tx_info[entry].mapping); + np->tx_ring[entry].first_addr = cpu_to_le32(np->tx_info[entry].first_mapping); + np->tx_ring[entry].first_len = cpu_to_le32(skb_headlen(skb)); + np->tx_ring[entry].total_len = cpu_to_le32(skb->len); /* Add "| TxDescIntr" to generate Tx-done interrupts. */ - np->tx_ring[entry].status = cpu_to_le32(skb->len | TxDescID); + np->tx_ring[entry].status = cpu_to_le32(TxDescID | TxCRCEn); + if (entry >= TX_RING_SIZE-1) /* Wrap ring */ + np->tx_ring[entry].status |= cpu_to_le32(TxRingWrap | TxDescIntr); + np->tx_ring[entry].nbufs = cpu_to_le32(skb_shinfo(skb)->nr_frags + 1); + + if (skb->ip_summed == CHECKSUM_HW) + np->tx_ring[entry].status |= cpu_to_le32(TxCalTCP); + if (debug > 5) { - printk(KERN_DEBUG "%s: Tx #%d slot %d %8.8x %8.8x.\n", + printk(KERN_DEBUG "%s: Tx #%d slot %d status %8.8x nbufs %d len +%4.4x/%4.4x.\n", dev->name, np->cur_tx, entry, le32_to_cpu(np->tx_ring[entry].status), - le32_to_cpu(np->tx_ring[entry].addr)); + le32_to_cpu(np->tx_ring[entry].nbufs), + le32_to_cpu(np->tx_ring[entry].first_len), + le32_to_cpu(np->tx_ring[entry].total_len)); + } + + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + skb_frag_t *this_frag = &skb_shinfo(skb)->frags[i]; + + /* we already have the proper value in entry */ + np->tx_info[entry].frag_mapping[i] = + pci_map_single(np->pci_dev, page_address(this_frag->page) + +this_frag->page_offset, this_frag->size, PCI_DMA_TODEVICE); + + np->tx_ring[entry].frag[i].addr = +cpu_to_le32(np->tx_info[entry].frag_mapping[i]); + np->tx_ring[entry].frag[i].len = cpu_to_le32(this_frag->size); + if (debug > 5) { + printk(KERN_DEBUG "%s: Tx #%d frag %d len %4.4x.\n", + dev->name, np->cur_tx, i, + le32_to_cpu(np->tx_ring[entry].frag[i].len)); + } } + np->cur_tx++; -#if 1 - if (entry >= TX_RING_SIZE-1) { /* Wrap ring */ - np->tx_ring[entry].status |= cpu_to_le32(TxRingWrap | TxDescIntr); + + if (entry >= TX_RING_SIZE-1) /* Wrap ring */ entry = -1; - } -#endif + entry++; /* Non-x86: explicitly flush descriptor cache lines here. */ + /* Ensure everything is written back above before the transmit is + initiated. - Jes */ + wmb(); /* Update the producer index. */ - writel(++entry, dev->base_addr + TxProducerIdx); + writel(entry * (sizeof(struct starfire_tx_desc) / 8), dev->base_addr + +TxProducerIdx); if (np->cur_tx - np->dirty_tx >= TX_RING_SIZE - 1) { np->tx_full = 1; netif_stop_queue(dev); } + dev->trans_start = jiffies; - if (debug > 4) { - printk(KERN_DEBUG "%s: Transmit frame #%d queued in slot %d.\n", - dev->name, np->cur_tx, entry); - } return 0; } @@ -919,18 +1073,29 @@ np->stats.tx_packets++; } else if ((tx_status & 0xe0000000) == 0x80000000) { struct sk_buff *skb; + int i; u16 entry = tx_status; /* Implicit truncate */ - entry >>= 3; + entry /= sizeof(struct starfire_tx_desc); skb = np->tx_info[entry].skb; + np->tx_info[entry].skb = NULL; pci_unmap_single(np->pci_dev, - np->tx_info[entry].mapping, - skb->len, PCI_DMA_TODEVICE); + +np->tx_info[entry].first_mapping, + +skb_headlen(skb), + +PCI_DMA_TODEVICE); + np->tx_info[entry].first_mapping = 0; + + for (i = 0; i < skb_shinfo(skb)->nr_frags; +i++) { + pci_unmap_single(np->pci_dev, + +np->tx_info[entry].frag_mapping[i], + +skb_shinfo(skb)->frags[i].size, + +PCI_DMA_TODEVICE); + np->tx_info[entry].frag_mapping[i] = +0; + } /* Scavenge the descriptor. */ dev_kfree_skb_irq(skb); - np->tx_info[entry].skb = NULL; - np->tx_info[entry].mapping = 0; + np->dirty_tx++; } np->tx_done_q[np->tx_done].status = 0; @@ -1037,14 +1202,6 @@ 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. */ @@ -1060,9 +1217,23 @@ 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) +#if defined(full_rx_status) || defined(csum_rx_status) + if (le32_to_cpu(np->rx_done_q[np->rx_done].status2) & +0x01000000) { skb->ip_summed = CHECKSUM_UNNECESSARY; + } + /* + * This feature doesn't seem to be working, at least + * with the two firmware versions I have. If the GFP sees + * a fragment, it either ignores it completely, or reports + * "bad checksum" on it. + * + * Maybe I missed something -- corrections are welcome. -Ion + */ + else if (le32_to_cpu(np->rx_done_q[np->rx_done].status2) & +0x00400000) { + skb->ip_summed = CHECKSUM_HW; + skb->csum = +le32_to_cpu(np->rx_done_q[np->rx_done].status2) & 0xffff; + printk(KERN_DEBUG "%s: checksum_hw, status2 = %x\n", +dev->name, np->rx_done_q[np->rx_done].status2); + } #endif netif_rx(skb); dev->last_rx = jiffies; @@ -1109,28 +1280,26 @@ { struct netdev_private *np = (struct netdev_private *)dev->priv; - if (intr_status & LinkChange) { + if (intr_status & IntrLinkChange) { 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) { + if (intr_status & IntrStatsMax) { 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) + ~(IntrAbnormalSummary|IntrLinkChange|IntrStatsMax|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) + /* Hmmmmm, it's not clear how to recover from DMA faults. */ + if (intr_status & IntrDMAErr) 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) @@ -1280,7 +1449,7 @@ { long ioaddr = dev->base_addr; struct netdev_private *np = (struct netdev_private *)dev->priv; - int i; + int i, j; netif_stop_queue(dev); @@ -1305,7 +1474,7 @@ 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_ring[i].first_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); @@ -1333,16 +1502,23 @@ 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); + np->tx_info[i].first_mapping, + skb_headlen(skb), PCI_DMA_TODEVICE); + np->tx_info[i].first_mapping = 0; dev_kfree_skb(skb); + np->tx_info[i].skb = NULL; + for (j = 0; j < 6; j++) + if (np->tx_info[i].frag_mapping[j]) { + pci_unmap_single(np->pci_dev, + +np->tx_info[i].frag_mapping[j], + +skb_shinfo(skb)->frags[j].size, + +PCI_DMA_TODEVICE); + np->tx_info[i].frag_mapping[j] = 0; + } else + break; } - np->tx_info[i].skb = NULL; - np->tx_info[i].mapping = 0; } - MOD_DEC_USE_COUNT; - return 0; } @@ -1359,6 +1535,9 @@ unregister_netdev(dev); iounmap((char *)dev->base_addr); + + release_mem_region(pci_resource_start (pdev, 0), + pci_resource_len (pdev, 0)); if (np->tx_done_q) pci_free_consistent(np->pci_dev, PAGE_SIZE, diff -urNX diff_kernel_excludes /usr/src/local/linux-2.4.vanilla/drivers/net/starfire_firmware.h linux-2.4/drivers/net/starfire_firmware.h --- /usr/src/local/linux-2.4.vanilla/drivers/net/starfire_firmware.h Wed Dec 31 16:00:00 1969 +++ linux-2.4/drivers/net/starfire_firmware.h Sat Jan 27 13:03:06 2001 @@ -0,0 +1,215 @@ +static u32 firmware_rx[] = { + 0x010003dc, 0x00000000, + 0x04000421, 0x00000086, + 0x80000015, 0x0000180e, + 0x81000015, 0x00006664, + 0x1a0040ab, 0x00000b06, + 0x14200011, 0x00000000, + 0x14204022, 0x0000aaaa, + 0x14204022, 0x00000300, + 0x14204022, 0x00000000, + 0x1a0040ab, 0x00000b14, + 0x14200011, 0x00000000, + 0x83000015, 0x00000002, + 0x04000021, 0x00000000, + 0x00000010, 0x00000000, + 0x04000421, 0x00000087, + 0x00000010, 0x00000000, + 0x00000010, 0x00000000, + 0x00008015, 0x00000000, + 0x0000003e, 0x00000000, + 0x00000010, 0x00000000, + 0x82000015, 0x00004000, + 0x009e8050, 0x00000000, + 0x03008015, 0x00000000, + 0x86008015, 0x00000000, + 0x82000015, 0x00008000, + 0x0100001c, 0x00000000, + 0x000050a0, 0x0000010c, + 0x4e20d011, 0x00006008, + 0x1420d012, 0x00004008, + 0x0000f090, 0x00007000, + 0x0000c8b0, 0x00003000, + 0x00004040, 0x00000000, + 0x00108015, 0x00000000, + 0x00a2c150, 0x00004000, + 0x00a400b0, 0x00000014, + 0x00000020, 0x00000000, + 0x2500400d, 0x00002525, + 0x00047220, 0x00003100, + 0x00934070, 0x00000000, + 0x00000020, 0x00000000, + 0x00924460, 0x00000184, + 0x2b20c011, 0x00000000, + 0x0000c420, 0x00000540, + 0x36014018, 0x0000422d, + 0x14200011, 0x00000000, + 0x00924460, 0x00000183, + 0x3200001f, 0x00000034, + 0x02ac0015, 0x00000002, + 0x00a60110, 0x00000008, + 0x42200011, 0x00000000, + 0x00924060, 0x00000103, + 0x0000001e, 0x00000000, + 0x00000020, 0x00000100, + 0x0000001e, 0x00000000, + 0x00924460, 0x00000086, + 0x00004080, 0x00000000, + 0x0092c070, 0x00000000, + 0x00924060, 0x00000100, + 0x0000c890, 0x00005000, + 0x00a6c110, 0x00000000, + 0x00b0c090, 0x00000012, + 0x021c0015, 0x00000000, + 0x3200001f, 0x00000034, + 0x00924460, 0x00000510, + 0x44210011, 0x00000000, + 0x42000011, 0x00000000, + 0x83000015, 0x00000040, + 0x00924460, 0x00000508, + 0x45014018, 0x00004545, + 0x00808050, 0x00000000, + 0x62208012, 0x00000000, + 0x82000015, 0x00000800, + 0x15200011, 0x00000000, + 0x00000010, 0x00000000, + 0x00000010, 0x00000000, + 0x00000010, 0x00000000, + 0x00000010, 0x00000000, + 0x00000010, 0x00000000, + 0x80000015, 0x0000eea4, + 0x81000015, 0x0000005f, + 0x00000060, 0x00000000, + 0x00004120, 0x00000000, + 0x00004a00, 0x00004000, + 0x00924460, 0x00000190, + 0x5601401a, 0x00005956, + 0x14000011, 0x00000000, + 0x00934050, 0x00000018, + 0x00930050, 0x00000018, + 0x3601403a, 0x0000002d, + 0x000643a9, 0x00000000, + 0x0000c420, 0x00000140, + 0x5601401a, 0x00005956, + 0x14000011, 0x00000000, + 0x00000010, 0x00000000, + 0x00000010, 0x00000000, + 0x000642a9, 0x00000000, + 0x00024420, 0x00000183, + 0x5601401a, 0x00005956, + 0x82000015, 0x00002000, + 0x15200011, 0x00000000, + 0x82000015, 0x00000010, + 0x15200011, 0x00000000, + 0x82000015, 0x00000010, + 0x15200011, 0x00000000, +}; /* 104 Rx instructions */ +#define FIRMWARE_RX_SIZE 104 + +static u32 firmware_tx[] = { + 0x010003dc, 0x00000000, + 0x04000421, 0x00000086, + 0x80000015, 0x0000180e, + 0x81000015, 0x00006664, + 0x1a0040ab, 0x00000b06, + 0x14200011, 0x00000000, + 0x14204022, 0x0000aaaa, + 0x14204022, 0x00000300, + 0x14204022, 0x00000000, + 0x1a0040ab, 0x00000b14, + 0x14200011, 0x00000000, + 0x83000015, 0x00000002, + 0x04000021, 0x00000000, + 0x00000010, 0x00000000, + 0x04000421, 0x00000087, + 0x00000010, 0x00000000, + 0x00000010, 0x00000000, + 0x00008015, 0x00000000, + 0x0000003e, 0x00000000, + 0x00000010, 0x00000000, + 0x82000015, 0x00004000, + 0x009e8050, 0x00000000, + 0x03008015, 0x00000000, + 0x86008015, 0x00000000, + 0x82000015, 0x00008000, + 0x0100001c, 0x00000000, + 0x000050a0, 0x0000010c, + 0x4e20d011, 0x00006008, + 0x1420d012, 0x00004008, + 0x0000f090, 0x00007000, + 0x0000c8b0, 0x00003000, + 0x00004040, 0x00000000, + 0x00108015, 0x00000000, + 0x00a2c150, 0x00004000, + 0x00a400b0, 0x00000014, + 0x00000020, 0x00000000, + 0x2500400d, 0x00002525, + 0x00047220, 0x00003100, + 0x00934070, 0x00000000, + 0x00000020, 0x00000000, + 0x00924460, 0x00000184, + 0x2b20c011, 0x00000000, + 0x0000c420, 0x00000540, + 0x36014018, 0x0000422d, + 0x14200011, 0x00000000, + 0x00924460, 0x00000183, + 0x3200001f, 0x00000034, + 0x02ac0015, 0x00000002, + 0x00a60110, 0x00000008, + 0x42200011, 0x00000000, + 0x00924060, 0x00000103, + 0x0000001e, 0x00000000, + 0x00000020, 0x00000100, + 0x0000001e, 0x00000000, + 0x00924460, 0x00000086, + 0x00004080, 0x00000000, + 0x0092c070, 0x00000000, + 0x00924060, 0x00000100, + 0x0000c890, 0x00005000, + 0x00a6c110, 0x00000000, + 0x00b0c090, 0x00000012, + 0x021c0015, 0x00000000, + 0x3200001f, 0x00000034, + 0x00924460, 0x00000510, + 0x44210011, 0x00000000, + 0x42000011, 0x00000000, + 0x83000015, 0x00000040, + 0x00924460, 0x00000508, + 0x45014018, 0x00004545, + 0x00808050, 0x00000000, + 0x62208012, 0x00000000, + 0x82000015, 0x00000800, + 0x15200011, 0x00000000, + 0x00000010, 0x00000000, + 0x00000010, 0x00000000, + 0x00000010, 0x00000000, + 0x00000010, 0x00000000, + 0x00000010, 0x00000000, + 0x80000015, 0x0000eea4, + 0x81000015, 0x0000005f, + 0x00000060, 0x00000000, + 0x00004120, 0x00000000, + 0x00004a00, 0x00004000, + 0x00924460, 0x00000190, + 0x5601401a, 0x00005956, + 0x14000011, 0x00000000, + 0x00934050, 0x00000018, + 0x00930050, 0x00000018, + 0x3601403a, 0x0000002d, + 0x000643a9, 0x00000000, + 0x0000c420, 0x00000140, + 0x5601401a, 0x00005956, + 0x14000011, 0x00000000, + 0x00000010, 0x00000000, + 0x00000010, 0x00000000, + 0x000642a9, 0x00000000, + 0x00024420, 0x00000183, + 0x5601401a, 0x00005956, + 0x82000015, 0x00002000, + 0x15200011, 0x00000000, + 0x82000015, 0x00000010, + 0x15200011, 0x00000000, + 0x82000015, 0x00000010, + 0x15200011, 0x00000000, +}; /* 104 Tx instructions */ +#define FIRMWARE_TX_SIZE 104 diff -urNX diff_kernel_excludes /usr/src/local/linux-2.4.vanilla/drivers/net/starfire_firmware.pl linux-2.4/drivers/net/starfire_firmware.pl --- /usr/src/local/linux-2.4.vanilla/drivers/net/starfire_firmware.pl Wed Dec 31 16:00:00 1969 +++ linux-2.4/drivers/net/starfire_firmware.pl Thu Jan 25 21:43:27 2001 @@ -0,0 +1,31 @@ +#!/usr/bin/perl + +# This script can be used to generate a new starfire_firmware.h +# from GFP_RX.DAT and GFP_TX.DAT, files included with the DDK +# and also with the Novell drivers. + +open FW, "GFP_RX.DAT" || die; +open FWH, ">starfire_firmware.h" || die; + +printf(FWH "static u32 firmware_rx[] = {\n"); +$counter = 0; +while ($foo = <FW>) { + chomp; + printf(FWH " 0x%s, 0x0000%s,\n", substr($foo, 4, 8), substr($foo, 0, 4)); + $counter++; +} + +close FW; +open FW, "GFP_TX.DAT" || die; + +printf(FWH "};\t/* %d Rx instructions */\n#define FIRMWARE_RX_SIZE %d\n\nstatic u32 +firmware_tx[] = {\n", $counter, $counter); +$counter = 0; +while ($foo = <FW>) { + chomp; + printf(FWH " 0x%s, 0x0000%s,\n", substr($foo, 4, 8), substr($foo, 0, 4)); + $counter++; +} + +close FW; +printf(FWH "};\t/* %d Tx instructions */\n#define FIRMWARE_TX_SIZE %d\n", $counter, +$counter); +close(FWH);