📄 rtk8189c.c
字号:
/* RTK8189C.c: A Linux device driver for the RTK8189C Ethernet chip. */
/*
Written 1998-2001 by Donald Becker.
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 author may be reached as becker@scyld.com, or C/O
Scyld Computing Corporation
410 Severn Ave., Suite 210
Annapolis MD 21403
*/
/* These identify the driver base version and may not be removed. */
static const char version1[] =
"RTK8189C.c:v0.09 7/26/2001 Written by Donald Becker <becker@scyld.com>\n";
static const char version2[] =
" http://www.scyld.com/network/drivers.html\n";
static const char version3[] =
"RTK8189C.c:v0.21 8/27/2001 Modified\n";
/* The user-configurable values.
These may be modified when a driver module is loaded.*/
static int debug = 1; /* 1 normal messages, 0 quiet .. 7 verbose. */
/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
static int max_interrupt_work = 20;
static int mtu = 0;
/* Maximum number of multicast addresses to filter (vs. rx-all-multicast).
This chip uses a 64 element hash table based on the Ethernet CRC. */
static int multicast_filter_limit = 32;
/* Set the copy breakpoint for the copy-only-tiny-frames scheme.
Setting to > 1518 effectively disables this feature. */
static int rx_copybreak = 0;
/* Used to pass the media type, etc.
Both 'options[]' and 'full_duplex[]' should exist for driver
interoperability.
The media type is usually passed in 'options[]'.
*/
#define MAX_UNITS 8 /* More are supported, limit only on options */
static int options[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
static int full_duplex[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.
The compiler will convert <unsigned>'%'<2^N> into a bit mask.
Making the Tx ring too large decreases the effectiveness of channel
bonding and packet priority.
There are no ill effects from too-large receive rings. */
#define TX_RING_SIZE 64
//#define TX_QUEUE_LEN 6 /* Limit Tx ring entries actually used. */
#define RX_RING_SIZE 128
/* Operational parameters that usually are not changed. */
/* Time in jiffies before concluding the transmitter is hung. */
#define TX_TIMEOUT (1*HZ)
#define PKT_BUF_SZ 1520 /* Size of each temporary Rx buffer.*/
#ifndef __KERNEL__
#define __KERNEL__
#endif
#if !defined(__OPTIMIZE__)
#warning You must compile this file with the correct options!
#warning See the last lines of the source file.
#error You must compile this driver with "-O".
#endif
/* Include files, designed to support most kernel versions 2.0.0 and later. */
#include <linux/config.h>
#if defined(CONFIG_SMP) && ! defined(__SMP__)
#define __SMP__
#endif
#include <linux/version.h>
#include <linux/module.h>
#if LINUX_VERSION_CODE < 0x20300 && defined(MODVERSIONS)
#include <linux/modversions.h>
#endif
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0))
#if defined(MODULE) && defined(CONFIG_MODVERSIONS) && ! defined(MODVERSIONS)
#define MODVERSIONS
#endif
#endif
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/timer.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/malloc.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/delay.h>
#include <asm/processor.h> /* Processor type for cache alignment. */
#include <asm/bitops.h>
#include <asm/io.h>
#include <asm/unaligned.h>
#ifdef INLINE_PCISCAN
#include "k_compat.h"
#else
#include "pci-scan.h"
#include "kern_compat.h"
#endif
/* Condensed operations for readability. */
#define virt_to_le32desc(addr) cpu_to_le32(virt_to_bus(addr))
#define le32desc_to_virt(addr) bus_to_virt(le32_to_cpu(addr))
#if (LINUX_VERSION_CODE >= 0x20100) && defined(MODULE)
char kernel_version[] = UTS_RELEASE;
#endif
MODULE_AUTHOR("Donald Becker <becker@scyld.com>");
MODULE_DESCRIPTION("Myson RTK8189C Based Fast Ethernet Card driver");
MODULE_PARM(max_interrupt_work, "i");
MODULE_PARM(mtu, "i");
MODULE_PARM(debug, "i");
MODULE_PARM(rx_copybreak, "i");
MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i");
MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i");
/*
Theory of Operation
I. Board Compatibility
II. Board-specific settings
None.
III. Driver operation
IIIa. Ring buffers
This driver uses two statically allocated fixed-size descriptor lists
formed into rings by a branch from the final descriptor to the beginning of
the list. The ring sizes are set at compile time by RX/TX_RING_SIZE.
Some chips explicitly use only 2^N sized rings, while others use a
'next descriptor' pointer that the driver forms into rings.
IIIb/c. Transmit/Receive Structure
This driver uses a zero-copy receive and transmit scheme.
The driver allocates full frame size skbuffs for the Rx ring buffers at
open() time and passes the skb->data field to the chip as receive data
buffers. When an incoming frame is less than RX_COPYBREAK bytes long,
a fresh skbuff is allocated and the frame is copied to the new skbuff.
When the incoming frame is larger, the skbuff is passed directly up the
protocol stack. Buffers consumed this way are replaced by newly allocated
skbuffs in a later phase of receives.
The RX_COPYBREAK value is chosen to trade-off the memory wasted by
using a full-sized skbuff for small frames vs. the copying costs of larger
frames. New boards are typically used in generously configured machines
and the underfilled buffers have negligible impact compared to the benefit of
a single allocation size, so the default value of zero results in never
copying packets. When copying is done, the cost is usually mitigated by using
a combined copy/checksum routine. Copying also preloads the cache, which is
most useful with small frames.
A subtle aspect of the operation is that the IP header at offset 14 in an
ethernet frame isn't longword aligned for further processing.
When unaligned buffers are permitted by the hardware (and always on copies)
frames are put into the skbuff at an offset of "+2", 16-byte aligning
the IP header.
IIId. Synchronization
The driver runs as two independent, single-threaded flows of control. One
is the send-packet routine, which enforces single-threaded use by the
dev->tbusy flag. The other thread is the interrupt handler, which is single
threaded by the hardware and interrupt handling software.
The send packet thread has partial control over the Tx ring and 'dev->tbusy'
flag. It sets the tbusy flag whenever it's queuing a Tx packet. If the next
queue slot is empty, it clears the tbusy flag when finished otherwise it sets
the 'lp->tx_full' flag.
The interrupt handler has exclusive control over the Rx ring and records stats
from the Tx ring. After reaping the stats, it marks the Tx queue entry as
empty by incrementing the dirty_tx mark. Iff the 'lp->tx_full' flag is set, it
clears both the tx_full and tbusy flags.
IIId. SMP semantics
The following are serialized with respect to each other via the "xmit_lock".
dev->hard_start_xmit() Transmit a packet
dev->tx_timeout() Transmit watchdog for stuck Tx
dev->set_multicast_list() Set the recieve filter.
Note: The Tx timeout watchdog code is implemented by the timer routine in
kernels up to 2.2.*. In 2.4.* and later the timeout code is part of the
driver interface.
The following fall under the global kernel lock. The module will not be
unloaded during the call, unless a call with a potential reschedule e.g.
kmalloc() is called. No other synchronization assertion is made.
dev->open()
dev->do_ioctl()
dev->get_stats()
Caution: The lock for dev->open() is commonly broken with request_irq() or
kmalloc(). It is best to avoid any lock-breaking call in do_ioctl() and
get_stats(), or additional module locking code must be implemented.
The following is self-serialized (no simultaneous entry)
An handler registered with request_irq().
IV. Notes
IVb. References
http://www.scyld.com/expert/100mbps.html
http://scyld.com/expert/NWay.html
IVc. Errata
No undocumented errata.
*/
/* PCI probe routines. */
static void *RTK8189C_probe1(struct pci_dev *pdev, void *init_dev,
long ioaddr, int irq, int chip_idx, int find_cnt);
static int netdev_pwr_event(void *dev_instance, int event);
/* Chips prior to the 80x have an external MII transceiver. */
enum chip_capability_flags { HasMIIXcvr=1, HasChipXcvr=2 };
#ifdef USE_IO_OPS
#define PCI_IOTYPE (PCI_USES_MASTER | PCI_USES_IO | PCI_ADDR0)
#define PCI_IOSIZE 256
#else
#define PCI_IOTYPE (PCI_USES_MASTER | PCI_USES_MEM | PCI_ADDR1)
#define PCI_IOSIZE 1024
#endif
static struct pci_id_info pci_id_tbl[] = {
{"Myson RTK8189C Based Fast Ethernet Card", {0x08031516, 0xffffffff, },
PCI_IOTYPE, PCI_IOSIZE, HasChipXcvr},
{"Digitalchina EDA-100 Fast Ethernet", {0x08031516, 0xffffffff, },
PCI_IOTYPE, PCI_IOSIZE, HasChipXcvr},
{0,}, /* 0 terminated list. */
};
struct drv_id_info RTK8189C_drv_id = {
"RTK8189C", 0, PCI_CLASS_NETWORK_ETHERNET<<8, pci_id_tbl, RTK8189C_probe1,
netdev_pwr_event };
/* This driver was written to use PCI memory space, however x86-oriented
hardware sometimes works only with I/O space accesses. */
#ifdef USE_IO_OPS
#undef readb
#undef readw
#undef readl
#undef writeb
#undef writew
#undef writel
#define readb inb
#define readw inw
#define readl inl
#define writeb outb
#define writew outw
#define writel outl
#endif
/* Offsets to the various registers.
Most accesses must be longword aligned. */
enum register_offsets {
StationAddr=0x00, MulticastFilter0=0x08, MulticastFilter1=0x0C,
FlowCtrlAddr=0x10, RxConfig=0x18, TxConfig=0x1a, PCIBusCfg=0x1c,
TxStartDemand=0x20, RxStartDemand=0x24,
RxCurrentPtr=0x28, TxRingPtr=0x2c, RxRingPtr=0x30,
IntrStatus=0x34, IntrEnable=0x38,
FlowCtrlThreshold=0x3c,
MIICtrl=0x40, EECtrl=0x40, RxErrCnts=0x44, TxErrCnts=0x48,
PHYMgmt=0x4c,
};
/* Bits in the interrupt status/mask registers. */
enum intr_status_bits {
IntrRxErr=0x0002, IntrRxDone=0x0004, IntrTxDone=0x0008,
IntrTxEmpty=0x0010, IntrRxEmpty=0x0020, StatsMax=0x0040, RxEarly=0x0080,
TxEarly=0x0100, RxOverflow=0x0200, TxUnderrun=0x0400,
IntrPCIErr=0x2000, NWayDone=0x4000, LinkChange=0x8000,
};
/* Bits in the RxMode (np->txrx_config) register. */
enum rx_mode_bits {
RxEnable=0x01, RxFilter=0xfe,
AcceptErr=0x02, AcceptRunt=0x08, AcceptBroadcast=0x40,
AcceptMulticast=0x20, AcceptAllPhys=0x80, AcceptMyPhys=0x00,
RxFlowCtrl=0x2000,
TxEnable=0x40000, TxModeFDX=0x00100000, TxThreshold=0x00e00000,
};
/* Misc. bits. */
enum misc_bits {
BCR_Reset=1, /* PCIBusCfg */
};
/* The Rx and Tx buffer descriptors. */
/* Note that using only 32 bit fields simplifies conversion to big-endian
architectures. */
struct netdev_desc_tx {
u32 status;
u32 ctrl_length;
u32 buf_addr;
u32 next_desc;
};
struct netdev_desc_rx {
u32 status;
u32 ctrl_length;
u32 buf_addr;
u32 next_desc;
struct sk_buff *rxbuffer;
u32 reserved1;
u32 reserved2;
u32 reserved3;
};
/* Bits in network_desc.status */
enum desc_status_bits {
DescOwn=0x80000000,
RxDescStartPacket=0x0800, RxDescEndPacket=0x0400, RxDescWholePkt=0x0c00,
RxDescErrSum=0x80, RxErrRunt=0x40, RxErrLong=0x20, RxErrFrame=0x10,
RxErrCRC=0x08, RxErrCode=0x04,
TxErrAbort=0x2000, TxErrCarrier=0x1000, TxErrLate=0x0800,
TxErr16Colls=0x0400, TxErrDefer=0x0200, TxErrHeartbeat=0x0100,
TxColls=0x00ff,
};
/* Bits in network_desc.ctrl_length */
enum ctrl_length_bits {
TxIntrOnDone=0x80000000, TxIntrOnFIFO=0x40000000,
TxDescEndPacket=0x20000000, TxDescStartPacket=0x10000000,
TxAppendCRC=0x08000000, TxPadTo64=0x04000000, TxNormalPkt=0x3C000000,
};
#define PRIV_ALIGN 15 /* Required alignment mask */
/* Use __attribute__((aligned (L1_CACHE_BYTES))) to maintain alignment
within the structure. */
struct netdev_private {
/* Descriptor rings first for alignment. */
struct netdev_desc_rx rx_ring[RX_RING_SIZE];
struct netdev_desc_tx tx_ring[TX_RING_SIZE];
struct net_device *next_module; /* Link for devices of this type. */
void *priv_addr; /* Unaligned address for kfree */
/* The addresses of receive-in-place skbuffs. */
// struct sk_buff* rx_skbuff[RX_RING_SIZE];
/* The saved addr of a sent-in-place packet/buffer, for later free(). */
struct sk_buff* tx_skbuff[TX_RING_SIZE];
struct net_device_stats stats;
struct timer_list timer; /* Media monitoring timer. */
/* Frequently used values: keep some adjacent for cache effect. */
int chip_id, drv_flags;
struct pci_dev *pci_dev;
struct netdev_desc_rx *rx_head_desc;
unsigned int cur_rx, dirty_rx; /* Producer/consumer ring indices */
unsigned int rx_buf_sz; /* Based on MTU+slack. */
unsigned int mcast_filter[2];
unsigned int lackrxcount;
unsigned int cur_tx, dirty_tx;
// unsigned int tx_full:1; /* The Tx queue is full. */
unsigned int txrx_config;
unsigned int txfreecount;
/* These values are keep track of the transceiver/media in use. */
unsigned int full_duplex:1; /* Full-duplex operation requested. */
unsigned int duplex_lock:1;
unsigned int medialock:1; /* Do not sense media. */
unsigned int default_port:4; /* Last dev->if_port value. */
/* MII transceiver section. */
int mii_cnt; /* MII device addresses. */
u16 advertising; /* NWay media advertisement */
unsigned char phys[2]; /* MII device addresses. */
};
static int eeprom_read(long ioaddr, int location);
static int mdio_read(struct net_device *dev, int phy_id,
unsigned int location);
static void mdio_write(struct net_device *dev, int phy_id,
unsigned int location, int value);
static int netdev_open(struct net_device *dev);
static void check_duplex(struct net_device *dev);
static void netdev_timer(unsigned long data);
static void tx_timeout(struct net_device *dev);
static void init_ring(struct net_device *dev);
static int start_tx(struct sk_buff *skb, struct net_device *dev);
static void intr_handler(int irq, void *dev_instance, struct pt_regs *regs);
static void netdev_error(struct net_device *dev, int intr_status);
static int netdev_rx(struct net_device *dev);
static void netdev_error(struct net_device *dev, int intr_status);
static void set_rx_mode(struct net_device *dev);
static struct net_device_stats *get_stats(struct net_device *dev);
static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
static int netdev_close(struct net_device *dev);
/* A list of our installed devices, for removing the driver module. */
static struct net_device *root_net_dev = NULL;
#ifndef MODULE
int RTK8189C_probe(struct net_device *dev)
{
if (pci_drv_register(&RTK8189C_drv_id, dev) < 0)
return -ENODEV;
printk(KERN_INFO "%s" KERN_INFO "%s", version1, version2);
return 0;
}
#endif
static void *RTK8189C_probe1(struct pci_dev *pdev, void *init_dev,
long ioaddr, int irq, int chip_idx, int card_idx)
{
struct net_device *dev;
struct netdev_private *np;
void *priv_mem;
int i, option = card_idx < MAX_UNITS ? options[card_idx] : 0;
dev = init_etherdev(init_dev, 0);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -