starfire.c
来自「linux 内核源代码」· C语言 代码 · 共 2,081 行 · 第 1/5 页
C
2,081 行
/* starfire.c: Linux device driver for the Adaptec Starfire network adapter. *//* Written 1998-2000 by Donald Becker. Current maintainer is Ion Badulescu <ionut ta badula tod org>. Please send all bug reports to me, and not to Donald Becker, as this code has been heavily modified from Donald's original version. This software may be used and distributed according to the terms of the GNU General Public License (GPL), incorporated herein by reference. Drivers based on or derived from this code fall under the GPL and must retain the authorship, copyright and license notice. This file is not a complete program and may only be used when the entire operating system is licensed under the GPL. The information below comes from Donald Becker's original driver: The author may be reached as becker@scyld.com, or C/O Scyld Computing Corporation 410 Severn Ave., Suite 210 Annapolis MD 21403 Support and updates available at http://www.scyld.com/network/starfire.html [link no longer provides useful info -jgarzik]*/#define DRV_NAME "starfire"#define DRV_VERSION "2.0"#define DRV_RELDATE "June 27, 2006"#include <linux/module.h>#include <linux/kernel.h>#include <linux/pci.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/init.h>#include <linux/delay.h>#include <linux/crc32.h>#include <linux/ethtool.h>#include <linux/mii.h>#include <linux/if_vlan.h>#include <linux/mm.h>#include <asm/processor.h> /* Processor type for cache alignment. */#include <asm/uaccess.h>#include <asm/io.h>#include "starfire_firmware.h"/* * 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/* * If using the broken firmware, data must be padded to the next 32-bit boundary. */#ifdef HAS_BROKEN_FIRMWARE#define PADDING_MASK 3#endif/* * Define this if using the driver with the zero-copy patch */#define ZEROCOPY#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)#define VLAN_SUPPORT#endif#ifndef CONFIG_ADAPTEC_STARFIRE_NAPI#undef HAVE_NETDEV_POLL#endif/* The user-configurable values. These may be modified when a driver module is loaded.*//* Used for tuning interrupt latency vs. overhead. */static int intr_latency;static int small_frames;static int debug = 1; /* 1 normal messages, 0 quiet .. 7 verbose. */static int max_interrupt_work = 20;static int mtu;/* Maximum number of multicast addresses to filter (vs. rx-all-multicast). The Starfire has a 512 element hash table based on the Ethernet CRC. */static const int multicast_filter_limit = 512;/* Whether to do TCP/UDP checksums in hardware */static int enable_hw_cksum = 1;#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*//* * Set the copy breakpoint for the copy-only-tiny-frames scheme. * Setting to > 1518 effectively disables this feature. * * NOTE: * The ia64 doesn't allow for unaligned loads even of integers being * misaligned on a 2 byte boundary. Thus always force copying of * packets as the starfire doesn't allow for misaligned DMAs ;-( * 23/10/2000 - Jes * * The Alpha and the Sparc don't like unaligned loads, either. On Sparc64, * at least, having unaligned frames leads to a rather serious performance * penalty. -Ion */#if defined(__ia64__) || defined(__alpha__) || defined(__sparc__)static int rx_copybreak = PKT_BUF_SZ;#elsestatic int rx_copybreak /* = 0 */;#endif/* PCI DMA burst size -- on sparc64 we want to force it to 64 bytes, on the others the default of 128 is fine. */#ifdef __sparc__#define DMA_BURST_SIZE 64#else#define DMA_BURST_SIZE 128#endif/* Used to pass the media type, etc. Both 'options[]' and 'full_duplex[]' exist for driver interoperability. The media type is usually passed in 'options[]'. These variables are deprecated, use ethtool instead. -Ion*/#define MAX_UNITS 8 /* More are supported, limit only on options */static int options[MAX_UNITS] = {0, };static int full_duplex[MAX_UNITS] = {0, };/* Operational parameters that are set at compile time. *//* The "native" ring sizes are either 256 or 2048. However in some modes a descriptor may be marked to wrap the ring earlier.*/#define RX_RING_SIZE 256#define TX_RING_SIZE 32/* The completion queues are fixed at 1024 entries i.e. 4K or 8KB. */#define DONE_Q_SIZE 1024/* All queues must be aligned on a 256-byte boundary */#define QUEUE_ALIGN 256#if RX_RING_SIZE > 256#define RX_Q_ENTRIES Rx2048QEntries#else#define RX_Q_ENTRIES Rx256QEntries#endif/* Operational parameters that usually are not changed. *//* Time in jiffies before concluding the transmitter is hung. */#define TX_TIMEOUT (2 * HZ)/* * This SUCKS. * We need a much better method to determine if dma_addr_t is 64-bit. */#if (defined(__i386__) && defined(CONFIG_HIGHMEM64G)) || defined(__x86_64__) || defined (__ia64__) || defined(__alpha__) || defined(__mips64__) || (defined(__mips__) && defined(CONFIG_HIGHMEM) && defined(CONFIG_64BIT_PHYS_ADDR))/* 64-bit dma_addr_t */#define ADDR_64BITS /* This chip uses 64 bit addresses. */#define netdrv_addr_t __le64#define cpu_to_dma(x) cpu_to_le64(x)#define dma_to_cpu(x) le64_to_cpu(x)#define RX_DESC_Q_ADDR_SIZE RxDescQAddr64bit#define TX_DESC_Q_ADDR_SIZE TxDescQAddr64bit#define RX_COMPL_Q_ADDR_SIZE RxComplQAddr64bit#define TX_COMPL_Q_ADDR_SIZE TxComplQAddr64bit#define RX_DESC_ADDR_SIZE RxDescAddr64bit#else /* 32-bit dma_addr_t */#define netdrv_addr_t __le32#define cpu_to_dma(x) cpu_to_le32(x)#define dma_to_cpu(x) le32_to_cpu(x)#define RX_DESC_Q_ADDR_SIZE RxDescQAddr32bit#define TX_DESC_Q_ADDR_SIZE TxDescQAddr32bit#define RX_COMPL_Q_ADDR_SIZE RxComplQAddr32bit#define TX_COMPL_Q_ADDR_SIZE TxComplQAddr32bit#define RX_DESC_ADDR_SIZE RxDescAddr32bit#endif#define skb_first_frag_len(skb) skb_headlen(skb)#define skb_num_frags(skb) (skb_shinfo(skb)->nr_frags + 1)#ifdef HAVE_NETDEV_POLL#define init_poll(dev, np) \ netif_napi_add(dev, &np->napi, netdev_poll, max_interrupt_work)#define netdev_rx(dev, np, ioaddr) \do { \ u32 intr_enable; \ if (netif_rx_schedule_prep(dev, &np->napi)) { \ __netif_rx_schedule(dev, &np->napi); \ intr_enable = readl(ioaddr + IntrEnable); \ intr_enable &= ~(IntrRxDone | IntrRxEmpty); \ writel(intr_enable, ioaddr + IntrEnable); \ readl(ioaddr + IntrEnable); /* flush PCI posting buffers */ \ } else { \ /* Paranoia check */ \ intr_enable = readl(ioaddr + IntrEnable); \ if (intr_enable & (IntrRxDone | IntrRxEmpty)) { \ printk(KERN_INFO "%s: interrupt while in polling mode!\n", dev->name); \ intr_enable &= ~(IntrRxDone | IntrRxEmpty); \ writel(intr_enable, ioaddr + IntrEnable); \ } \ } \} while (0)#define netdev_receive_skb(skb) netif_receive_skb(skb)#define vlan_netdev_receive_skb(skb, vlgrp, vlid) vlan_hwaccel_receive_skb(skb, vlgrp, vlid)static int netdev_poll(struct napi_struct *napi, int budget);#else /* not HAVE_NETDEV_POLL */#define init_poll(dev, np)#define netdev_receive_skb(skb) netif_rx(skb)#define vlan_netdev_receive_skb(skb, vlgrp, vlid) vlan_hwaccel_rx(skb, vlgrp, vlid)#define netdev_rx(dev, np, ioaddr) \do { \ int quota = np->dirty_rx + RX_RING_SIZE - np->cur_rx; \ __netdev_rx(dev, "a);\} while (0)#endif /* not HAVE_NETDEV_POLL *//* end of compatibility code *//* These identify the driver base version and may not be removed. */static const char version[] __devinitdata =KERN_INFO "starfire.c:v1.03 7/26/2000 Written by Donald Becker <becker@scyld.com>\n"KERN_INFO " (unofficial 2.2/2.4 kernel port, version " DRV_VERSION ", " DRV_RELDATE ")\n";MODULE_AUTHOR("Donald Becker <becker@scyld.com>");MODULE_DESCRIPTION("Adaptec Starfire Ethernet driver");MODULE_LICENSE("GPL");MODULE_VERSION(DRV_VERSION);module_param(max_interrupt_work, int, 0);module_param(mtu, int, 0);module_param(debug, int, 0);module_param(rx_copybreak, int, 0);module_param(intr_latency, int, 0);module_param(small_frames, int, 0);module_param_array(options, int, NULL, 0);module_param_array(full_duplex, int, NULL, 0);module_param(enable_hw_cksum, int, 0);MODULE_PARM_DESC(max_interrupt_work, "Maximum events handled per interrupt");MODULE_PARM_DESC(mtu, "MTU (all boards)");MODULE_PARM_DESC(debug, "Debug level (0-6)");MODULE_PARM_DESC(rx_copybreak, "Copy breakpoint for copy-only-tiny-frames");MODULE_PARM_DESC(intr_latency, "Maximum interrupt latency, in microseconds");MODULE_PARM_DESC(small_frames, "Maximum size of receive frames that bypass interrupt latency (0,64,128,256,512)");MODULE_PARM_DESC(options, "Deprecated: Bits 0-3: media type, bit 17: full duplex");MODULE_PARM_DESC(full_duplex, "Deprecated: Forced full-duplex setting (0/1)");MODULE_PARM_DESC(enable_hw_cksum, "Enable/disable hardware cksum support (0/1)");/* Theory of OperationI. Board CompatibilityThis driver is for the Adaptec 6915 "Starfire" 64 bit PCI Ethernet adapter.II. Board-specific settingsIII. Driver operationIIIa. Ring buffersThe Starfire hardware uses multiple fixed-size descriptor queues/rings. Thering sizes are set fixed by the hardware, but may optionally be wrappedearlier by the END bit in the descriptor.This driver uses that hardware queue size for the Rx ring, where a largenumber of entries has no ill effect beyond increases the potential backlog.The Tx ring is wrapped with the END bit, since a large hardware Tx queuedisables the queue layer priority ordering and we have no mechanism toutilize the hardware two-level priority queue. When modifying theRX/TX_RING_SIZE pay close attention to page sizes and the ring-empty warninglevels.IIIb/c. Transmit/Receive StructureSee the Adaptec manual for the many possible structures, and options foreach structure. There are far too many to document all of them here.For transmit this driver uses type 0/1 transmit descriptors (dependingon the 32/64 bitness of the architecture), and relies on automaticminimum-length padding. It does not use the completion queueconsumer index, but instead checks for non-zero status entries.For receive this driver uses type 2/3 receive descriptors. The driverallocates full frame size skbuffs for the Rx ring buffers, so all framesshould fit in a single descriptor. The driver does not use the completionqueue consumer index, but instead checks for non-zero status entries.When an incoming frame is less than RX_COPYBREAK bytes long, a fresh skbuffis allocated and the frame is copied to the new skbuff. When the incomingframe is larger, the skbuff is passed directly up the protocol stack.Buffers consumed this way are replaced by newly allocated skbuffs in a laterphase of receive.A notable aspect of operation is that unaligned buffers are not permitted bythe Starfire hardware. Thus the IP header at offset 14 in an ethernet frameisn't longword aligned, which may cause problems on some machinee.g. Alphas and IA64. For these architectures, the driver is forced to copythe frame into a new skbuff unconditionally. Copied frames are put into theskbuff at an offset of "+2", thus 16-byte aligning the IP header.IIId. SynchronizationThe driver runs as two independent, single-threaded flows of control. Oneis the send-packet routine, which enforces single-threaded use by thedev->tbusy flag. The other thread is the interrupt handler, which is singlethreaded by the hardware and interrupt handling software.The send packet thread has partial control over the Tx ring and the netif_queuestatus. If the number of free Tx slots in the ring falls below a certain number(currently hardcoded to 4), it signals the upper layer to stop the queue.The interrupt handler has exclusive control over the Rx ring and records statsfrom the Tx ring. After reaping the stats, it marks the Tx queue entry asempty by incrementing the dirty_tx mark. Iff the netif_queue is stopped and thenumber of free Tx slow is above the threshold, it signals the upper layer torestart the queue.IV. NotesIVb. ReferencesThe Adaptec Starfire manuals, available only from Adaptec.http://www.scyld.com/expert/100mbps.htmlhttp://www.scyld.com/expert/NWay.htmlIVc. Errata- StopOnPerr is broken, don't enable- Hardware ethernet padding exposes random data, perform software padding instead (unverified -- works correctly for all the hardware I have)*/enum chip_capability_flags {CanHaveMII=1, };enum chipset { CH_6915 = 0,};static struct pci_device_id starfire_pci_tbl[] = { { 0x9004, 0x6915, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_6915 }, { 0, }};MODULE_DEVICE_TABLE(pci, starfire_pci_tbl);/* A chip capabilities table, matching the CH_xxx entries in xxx_pci_tbl[] above. */static const struct chip_info { const char *name; int drv_flags;} netdrv_tbl[] __devinitdata = { { "Adaptec Starfire 6915", CanHaveMII },};/* Offsets to the device registers. Unlike software-only systems, device drivers interact with complex hardware. It's not useful to define symbolic names for every register bit in the device. The name can only partially document the semantics and make the driver longer and more difficult to read. In general, only the important configuration values or bits changed multiple times should be defined symbolically.*/enum register_offsets { PCIDeviceConfig=0x50040, GenCtrl=0x50070, IntrTimerCtrl=0x50074, IntrClear=0x50080, IntrStatus=0x50084, IntrEnable=0x50088, MIICtrl=0x52000, TxStationAddr=0x50120, EEPROMCtrl=0x51000, GPIOCtrl=0x5008C, TxDescCtrl=0x50090, TxRingPtr=0x50098, HiPriTxRingPtr=0x50094, /* Low and High priority. */ TxRingHiAddr=0x5009C, /* 64 bit address extension. */ TxProducerIdx=0x500A0, TxConsumerIdx=0x500A4, TxThreshold=0x500B0, CompletionHiAddr=0x500B4, TxCompletionAddr=0x500B8, RxCompletionAddr=0x500BC, RxCompletionQ2Addr=0x500C0, CompletionQConsumerIdx=0x500C4, RxDMACtrl=0x500D0, RxDescQCtrl=0x500D4, RxDescQHiAddr=0x500DC, RxDescQAddr=0x500E0, RxDescQIdx=0x500E8, RxDMAStatus=0x500F0, RxFilterMode=0x500F4, TxMode=0x55000, VlanType=0x55064, PerfFilterTable=0x56000, HashTable=0x56100, TxGfpMem=0x58000, RxGfpMem=0x5a000,};/* * Bits in the interrupt status/mask registers. * Warning: setting Intr[Ab]NormalSummary in the IntrEnable register * enables all the interrupt sources that are or'ed into those status bits. */enum intr_status_bits { 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, IntrNormalMask=0xff00, IntrAbnormalMask=0x3ff00fe,};/* Bits in the RxFilterMode register. */enum rx_mode_bits { AcceptBroadcast=0x04, AcceptAllMulticast=0x02, AcceptAll=0x01, AcceptMulticast=0x10, PerfectFilter=0x40, HashFilter=0x30, PerfectFilterVlan=0x80, MinVLANPrio=0xE000, VlanMode=0x0200, WakeupOnGFP=0x0800,};/* Bits in the TxMode register */enum tx_mode_bits {
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?