📄 eth8186.c
字号:
// fragmented packet/* 8186nic.c: A Linux Ethernet driver for the RealTek 8186 chips. */#define DRV_NAME "8186NIC"#define DRV_VERSION "0.0.7"#define DRV_RELDATE "Nov 8, 2006"#define BIT(x) (1<<(x))#define RTL8186_CHECKSUM_OFFLOAD#include <linux/config.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/compiler.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/init.h>#include <linux/delay.h>#include <linux/ethtool.h>#include <linux/mii.h>#include <linux/if_vlan.h>#include <net/pkt_sched.h>//#include <linux/crc32.h>#include <asm/io.h>#include <asm/uaccess.h>#include <linux/slab.h>#include <linux/proc_fs.h>#define BR_SHORTCUT#define VLAN_QOS//#ifdef CONFIG_RTL8186_KB// #define VLAN_QOS//#endif#ifdef DRIVER_SPEEDUP#ifndef DELAY_RX#define DELAY_RX#endif#endif#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)#define CP_VLAN_TAG_USED 1#define CP_VLAN_TX_TAG(tx_desc,vlan_tag_value) \ do { (tx_desc)->opts2 = (vlan_tag_value); } while (0)#else#define CP_VLAN_TAG_USED 0#define CP_VLAN_TX_TAG(tx_desc,vlan_tag_value) \ do { (tx_desc)->opts2 = 0; } while (0)#endif/* These identify the driver base version and may not be removed. */static char version[] __devinitdata =KERN_INFO DRV_NAME " Ethernet driver v" DRV_VERSION " (" DRV_RELDATE ")\n";MODULE_AUTHOR("Arthur Yang <ysy@realtek.com.tw>");MODULE_DESCRIPTION("RealTek RTL8186 series 10/100 Ethernet driver");MODULE_LICENSE("GPL");#if 0static int debug = -1;MODULE_PARM (debug, "i");MODULE_PARM_DESC (debug, "RTL8186NIC bitmapped message enable number");#endif/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast). The RTL chips use a 64 element hash table based on the Ethernet CRC. */static int multicast_filter_limit = 32;MODULE_PARM (multicast_filter_limit, "i");MODULE_PARM_DESC (multicast_filter_limit, "RTL8186NIC maximum number of filtered multicast addresses");#define PFX DRV_NAME ": "#define CP_DEF_MSG_ENABLE (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ NETIF_MSG_LINK)#define CP_REGS_SIZE (0xff + 1)#define RTL8186_RX_RING_SIZE 32#define RTL8186_TX_RING_SIZE 64#define DESC_ALIGN 0x100#define UNCACHE_MASK 0xa0000000#define RTL8186_RXRING_BYTES ((sizeof(struct dma_desc) * (RTL8186_RX_RING_SIZE+1)) + DESC_ALIGN)#define RTL8186_TXRING_BYTES ((sizeof(struct dma_desc) * (RTL8186_TX_RING_SIZE+1)) + DESC_ALIGN)#define NEXT_TX(N) (((N) + 1) & (RTL8186_TX_RING_SIZE - 1))#define NEXT_RX(N) (((N) + 1) & (RTL8186_RX_RING_SIZE - 1))#define TX_HQBUFFS_AVAIL(CP) \ (((CP)->tx_hqtail <= (CP)->tx_hqhead) ? \ (CP)->tx_hqtail + (RTL8186_TX_RING_SIZE - 1) - (CP)->tx_hqhead : \ (CP)->tx_hqtail - (CP)->tx_hqhead - 1)#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/#define RX_OFFSET 2/* The following settings are log_2(bytes)-4: 0 == 16 bytes .. 6==1024, 7==end of packet. *//* Time in jiffies before concluding the transmitter is hung. */#define TX_TIMEOUT (6*HZ)/* hardware minimum and maximum for a single frame's data payload */#define CP_MIN_MTU 60 /* TODO: allow lower, but pad */#define CP_MAX_MTU 4096#define RTL_W32(reg, value) (*(volatile u32*)(cp->regs+reg)) = (u32)value#define RTL_W16(reg, value) (*(volatile u16*)(cp->regs+reg)) = (u16)value#define RTL_W8(reg, value) (*(volatile u8*)(cp->regs+reg)) = (u8)value#define RTL_R32(reg) (*(volatile u32*)(cp->regs+reg))#define RTL_R16(reg) (*(volatile u16*)(cp->regs+reg))#define RTL_R8(reg) (*(volatile u8*)(cp->regs+reg))enum PHY_REGS { TXFCE = BIT(7), RXFCE = BIT(6), SP100 = BIT(3), LINK = BIT(2), TXPF = BIT(1), RXPF = BIT(0), FORCE_TX = BIT(5),};enum { NIC0, NIC1, NICEND,};enum { NIC0IRQ=4, NIC1IRQ=5,};enum { NIC0BASE=0xbd200000, NIC1BASE=0xbd300000,};enum { /* NIC register offsets */ IDR0 =0, /* Ethernet ID */ IDR1 =0x1, /* Ethernet ID */ IDR2 =0x2, /* Ethernet ID */ IDR3 =0x3, /* Ethernet ID */ IDR4 =0x4, /* Ethernet ID */ IDR5 =0x5, /* Ethernet ID */ MAR0 =0x8, /* Multicast register */ MAR1 =0x9, MAR2 =0xa, MAR3 =0xb, MAR4 =0xc, MAR5 =0xd, MAR6 =0xe, MAR7 =0xf, TXOKCNT =0x10, RXOKCNT =0x12, TXERR =0x14, RXERRR =0x16, MISSPKT =0x18, FAE =0x1A, TX1COL =0x1c, TXMCOL =0x1e, RXOKPHY =0x20, RXOKBRD =0x22, RXOKMUL =0x24, TXABT =0x26, TXUNDRN =0x28, TRSR =0x34, CMD =0x3B, IMR =0x3C, ISR =0x3E, TCR =0x40, RCR =0x44, MSR =0x58, MIIAR =0x5C, TxFDP1 =0x1300, TxCDO1 =0x1304, TXCPO1 =0x1308, TXPSA1 =0x130A, TXCPA1 =0x130C, LastTxCDO1 =0x1310, TXPAGECNT1 =0x1312, Tx1ScratchDes =0x1350, TxFDP2 =0x1380, TxCDO2 =0x1384, TXCPO2 =0x1388, TXPSA2 =0x138A, TXCPA2 =0x138C, LASTTXCDO2 =0x1390, TXPAGECNT2 =0x1392, Tx2ScratchDes =0x13A0, RxFDP =0x13F0, RxCDO =0x13F4, RxRingSize =0x13F6, RxCPO =0x13F8, RxPSA =0x13FA, RXPLEN =0x1403, //LASTRXCDO =0x1402, RXPFDP =0x1404, RXPAGECNT =0x1408, RXSCRATCHDES =0x1410, EthrntRxCPU_Des_Num =0x1430, EthrntRxCPU_Des_Wrap =0x1431, Rx_Pse_Des_Thres =0x1432, IO_CMD =0x1434, MII_RE =BIT(3), MII_TE =BIT(2), TX_FNL =BIT(1), TX_FNH =BIT(0),};enum RTL8186_STATUS_REGS { /* Tx and Rx status descriptors */ DescOwn = (1 << 31), /* Descriptor is owned by NIC */ RingEnd = (1 << 30), /* End of descriptor ring */ FirstFrag = (1 << 29), /* First segment of a packet */ LastFrag = (1 << 28), /* Final segment of a packet */ RxError = (1 << 20), /* Rx error summary */ IPCS = (1 << 18), /* Calculate IP checksum */ UDPCS = (1 << 17), /* Calculate UDP/IP checksum */ TCPCS = (1 << 16), /* Calculate TCP/IP checksum */ TxVlanTag = (1 << 16), /* Add VLAN tag */ RxVlanTagged = (1 << 16), /* Rx VLAN tag available */ IPFail = (1 << 15), /* IP checksum failed */ UDPFail = (1 << 14), /* UDP/IP checksum failed */ TCPFail = (1 << 13), /* TCP/IP checksum failed */ NormalTxPoll = (1 << 6), /* One or more normal Tx packets to send */ PID1 = (1 << 17), /* 2 protocol id bits: 0==non-IP, */ PID0 = (1 << 16), /* 1==UDP/IP, 2==TCP/IP, 3==IP */ RxProtoTCP = 1, RxProtoUDP = 2, RxProtoIP = 3, RxErrFrame = (1 << 27), /* Rx frame alignment error */ RxMcast = (1 << 26), /* Rx multicast packet rcv'd */ RxErrCRC = (1 << 18), /* Rx CRC error */ RxErrRunt = (1 << 19), /* Rx error, packet < 64 bytes */ RWT = (1 << 21), /* Rx */ E8023 = (1 << 22), /* Receive Ethernet 802.3 packet */ TxCRC = (1 << 23), PPPOE = (1 << 23), RxVlanOn = (1 << 2), /* Rx VLAN de-tagging enable */ RxChkSum = (1 << 1), /* Rx checksum offload enable */};enum RTL8186_THRESHOLD_REGS { THVAL = 2, RINGSIZE = 2, LOOPBACK = (0x3 << 8), AcceptErr = 0x20, /* Accept packets with CRC errors */ AcceptRunt = 0x10, /* Accept runt (<64 bytes) packets */ AcceptBroadcast = 0x08, /* Accept broadcast packets */ AcceptMulticast = 0x04, /* Accept multicast packets */ AcceptMyPhys = 0x02, /* Accept pkts with our MAC as dest */ AcceptAllPhys = 0x01, /* Accept all pkts w/ physical dest */ AcceptAll = AcceptBroadcast | AcceptMulticast | AcceptMyPhys | AcceptAllPhys | AcceptErr | AcceptRunt, AcceptNoBroad = AcceptMulticast |AcceptMyPhys | AcceptAllPhys | AcceptErr | AcceptRunt, AcceptNoMulti = AcceptMyPhys | AcceptAllPhys | AcceptErr | AcceptRunt, NoErrAccept = AcceptBroadcast | AcceptMulticast | AcceptMyPhys | AcceptAllPhys,};enum RTL8186_ISR_REGS { SW_INT = BIT(10), TX_EMPTY = BIT(9), LINK_CHG = BIT(8), TX_ERR = BIT(7), TX_OK = BIT(6), RX_EMPTY = BIT(5), RX_FIFOOVR = BIT(4), RX_ERR = BIT(2), RX_OK = BIT(0),};enum RTL8186_IOCMD_REG {#ifdef DRIVER_SPEEDUP RX_MIT = 1,#else RX_MIT = 3,#endif RX_TIMER = 1, RX_FIFO = 2, TX_FIFO = 1,#ifdef DRIVER_SPEEDUP TX_MIT = 1,#else TX_MIT = 3,#endif TX_POLL = 1 << 0, CMD_CONFIG = 0x3c | RX_MIT << 8 | RX_FIFO << 11 | RX_TIMER << 13 | TX_MIT << 16 | TX_FIFO<<19,};static const u32 rtl8186_intr_mask = SW_INT | LINK_CHG | RX_OK | RX_ERR | RX_EMPTY | RX_FIFOOVR | TX_OK | TX_ERR | TX_EMPTY;typedef struct dma_desc { u32 opts1; // status u32 addr; // buffer address u32 opts2; // vlan stuff u32 opts3; // partial checksum} DMA_DESC;struct ring_info { struct sk_buff *skb; dma_addr_t mapping; unsigned frag;};struct cp_extra_stats { unsigned long rx_frags; unsigned long tx_timeouts;};#ifdef VLAN_QOSstruct qos_node { unsigned char valid; unsigned char addr[6]; unsigned char vlan_tagged; unsigned char priority; unsigned char vlan_id_h; unsigned char vlan_id_l;};#endifstruct re_private { unsigned tx_hqhead; unsigned tx_hqtail; unsigned tx_lqhead; unsigned tx_lqtail; unsigned rx_tail; void *regs; struct net_device *dev;#ifdef DELAY_RX struct tasklet_struct rx_tasklet;#endif spinlock_t lock; DMA_DESC *rx_ring; DMA_DESC *tx_hqring; DMA_DESC *tx_lqring; struct ring_info tx_skb[RTL8186_TX_RING_SIZE]; struct ring_info rx_skb[RTL8186_RX_RING_SIZE]; unsigned rx_buf_sz; dma_addr_t ring_dma;#if CP_VLAN_TAG_USED struct vlan_group *vlgrp;#endif u32 msg_enable; struct net_device_stats net_stats; struct cp_extra_stats cp_stats; struct pci_dev *pdev; u32 rx_config; struct sk_buff *frag_skb; unsigned dropping_frag : 1; char* rxdesc_buf; char* txdesc_buf; struct mii_if_info mii_if;#ifdef VLAN_QOS struct qos_node qosnode_table[8][8]; unsigned char qosnode_index[8];#endif};struct re_private *reDev[2];extern int r3k_flush_dcache_range(int, int);static void __rtl8186_set_rx_mode(struct net_device *dev);static inline void rtl8186_tx(struct re_private *cp);static void rtl8186_clean_rings(struct re_private *cp);static void rtl8186_tx_timeout(struct net_device *dev);static int rtl8139_set_hwaddr(struct net_device *dev, void *addr);static int eth_flag=0; // bit0 - disable DELAY_RX, bit1 - disable BR_SHORTCUT#ifdef VLAN_QOSstatic __inline__ int mac_hash(unsigned char *mac){ unsigned long x; x = mac[0]; x = (x << 2) ^ mac[1]; x = (x << 2) ^ mac[2]; x = (x << 2) ^ mac[3]; x = (x << 2) ^ mac[4]; x = (x << 2) ^ mac[5]; x ^= x >> 8; return x & (8 - 1);}void qosnode_insert(struct re_private *cp, unsigned char *sa, unsigned char vlan_tagged, unsigned char priority, unsigned char vlan_id_h, unsigned char vlan_id_l){ unsigned int index, i; struct qos_node *pbuf; index = mac_hash(sa); for (i=0; i<8; i++) { if ((cp->qosnode_table[index][i].valid) && !(memcmp(cp->qosnode_table[index][i].addr, sa, 6))) { //printk("qosnode: entry %02x%02x%02x%02x%02x%02x found\n", // sa[0],sa[1],sa[2],sa[3],sa[4],sa[5]); cp->qosnode_table[index][i].vlan_tagged = vlan_tagged; if (vlan_tagged) { cp->qosnode_table[index][i].priority = priority; cp->qosnode_table[index][i].vlan_id_h = vlan_id_h; cp->qosnode_table[index][i].vlan_id_l = vlan_id_l; } return; } } // not found pbuf = &cp->qosnode_table[index][cp->qosnode_index[index]]; cp->qosnode_index[index] = (cp->qosnode_index[index] + 1) & (8 - 1); pbuf->valid = 1; memcpy(pbuf->addr, sa, 6); pbuf->vlan_tagged = vlan_tagged; if (vlan_tagged) { pbuf->priority = priority; pbuf->vlan_id_h = vlan_id_h; pbuf->vlan_id_l = vlan_id_l; } //printk("qosnode: entry %02x%02x%02x%02x%02x%02x insert\n", // sa[0],sa[1],sa[2],sa[3],sa[4],sa[5]);}struct qos_node *qosnode_lookup(struct re_private *cp, unsigned char *da){ unsigned int index, i; index = mac_hash(da); for (i=0; i<8; i++) { if ((cp->qosnode_table[index][i].valid) && !(memcmp(cp->qosnode_table[index][i].addr, da, 6))) { //printk("qosnode: entry %02x%02x%02x%02x%02x%02x lookup\n", // da[0],da[1],da[2],da[3],da[4],da[5]); return &cp->qosnode_table[index][i]; } } return 0;}#endif // VLAN_QOSstatic int write_proc(struct file *file, const char *buffer, unsigned long count, void *data){ unsigned char tmp; if (count < 2) return -EFAULT; if (buffer && !copy_from_user(&tmp, buffer, 1)) { eth_flag = tmp - '0'; return count; } return -EFAULT;}static inline void rtl8186_set_rxbufsize(struct re_private *cp){ unsigned int mtu = cp->dev->mtu; if (mtu > ETH_DATA_LEN) /* MTU + ethernet header + FCS + optional VLAN tag */ cp->rx_buf_sz = mtu + ETH_HLEN + 8; else cp->rx_buf_sz = PKT_BUF_SZ;}static inline void rtl8186_rx_skb(struct re_private *cp, struct sk_buff *skb, DMA_DESC *desc){#ifdef BR_SHORTCUT extern struct net_device *get_shortcut_dev(unsigned char *da); struct net_device *dev;#endif skb->protocol = eth_type_trans (skb, cp->dev); cp->net_stats.rx_packets++; cp->net_stats.rx_bytes += skb->len; cp->dev->last_rx = jiffies;#ifdef VLAN_QOS if (desc->opts2 & RxVlanTagged) { unsigned char priority = (desc->opts2 & 0x000000e0) >> 5; unsigned char vlan_id_h = (desc->opts2 & 0x0000000f); unsigned char vlan_id_l = (desc->opts2 & 0x0000ff00) >> 8; qosnode_insert(cp, skb->mac.ethernet->h_source, 1, priority, vlan_id_h, vlan_id_l); skb->cb[0] = priority; }#endif#if CP_VLAN_TAG_USED if (cp->vlgrp && (desc->opts2 & RxVlanTagged)) { vlan_hwaccel_rx(skb, cp->vlgrp, desc->opts2 & 0xffff); } else#endif {#if defined(BR_SHORTCUT) && defined(CONFIG_RTL8185) if (!(skb->mac.ethernet->h_dest[0] & 0x01) && !(eth_flag & 2) && (cp->dev->br_port) && ((dev = get_shortcut_dev(skb->mac.ethernet->h_dest)) != NULL)) { skb_push(skb, ETH_HLEN); memcpy(skb->data, skb->mac.ethernet, ETH_HLEN); dev->hard_start_xmit(skb, dev); } else#endif {#ifdef CONFIG_RTL8186_KB skb->__unused = 0;#endif netif_rx(skb); } }}static void rtl8186_rx_err_acct(struct re_private *cp, unsigned rx_tail, u32 status, u32 len){#if 0 if (netif_msg_rx_err (cp)) printk (KERN_DEBUG "%s: rx err, slot %d status 0x%x len %d\n", cp->dev->name, rx_tail, status, len);#endif cp->net_stats.rx_errors++; if (status & RxErrFrame) cp->net_stats.rx_frame_errors++; if (status & RxErrCRC) cp->net_stats.rx_crc_errors++; if (status & RxErrRunt) cp->net_stats.rx_length_errors++;}static void rtl8186_rx_frag(struct re_private *cp, unsigned rx_tail, struct sk_buff *skb, u32 status, u32 len){ struct sk_buff *copy_skb, *frag_skb = cp->frag_skb; unsigned orig_len = frag_skb ? frag_skb->len : 0; unsigned target_len = orig_len + len; unsigned first_frag = status & FirstFrag; unsigned last_frag = status & LastFrag; if (netif_msg_rx_status (cp)) printk (KERN_DEBUG "%s: rx %s%sfrag, slot %d status 0x%x len %d\n", cp->dev->name, cp->dropping_frag ? "dropping " : "", first_frag ? "first " : last_frag ? "last " : "", rx_tail, status, len); cp->cp_stats.rx_frags++; if (!frag_skb && !first_frag) cp->dropping_frag = 1; if (cp->dropping_frag) goto drop_frag; copy_skb = dev_alloc_skb (target_len + RX_OFFSET); if (!copy_skb) { printk(KERN_WARNING "%s: rx slot %d alloc failed\n", cp->dev->name, rx_tail); cp->dropping_frag = 1;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -