⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 r8168_n.c

📁 RT8111/R8168 PCI Express 的linux驱动程序
💻 C
📖 第 1 页 / 共 5 页
字号:
/*################################################################################# # Copyright(c) Realtek Semiconductor Corp. All rights reserved.# # This program is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the Free # Software Foundation; either version 2 of the License, or (at your option) # any later version.# # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for # more details.# # You should have received a copy of the GNU General Public License along with# this program; if not, write to the Free Software Foundation, Inc., 59 # Temple Place - Suite 330, Boston, MA  02111-1307, USA.# # The full GNU General Public License is included in this distribution in the# file called LICENSE.# ################################################################################*//*This driver is modified from r8169.c in Linux kernel 2.6.18*/#include <linux/module.h>#include <linux/version.h>#include <linux/pci.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/delay.h>#include <linux/ethtool.h>#include <linux/mii.h>#include <linux/if_vlan.h>#include <linux/crc32.h>#include <linux/in.h>#include <linux/ip.h>#include <linux/tcp.h>#include <linux/init.h>#include <linux/rtnetlink.h>#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)#include <linux/dma-mapping.h>#include <linux/moduleparam.h>#endif//LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)#include <asm/io.h>#include <asm/irq.h>#include <asm/uaccess.h>#include "r8168.h"/* Maximum events (Rx packets, etc.) to handle at each interrupt. */static const int max_interrupt_work = 20;/* 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 const int multicast_filter_limit = 32;enum mcfg {	CFG_METHOD_1 = 0x01,	CFG_METHOD_2 = 0x02,	CFG_METHOD_3 = 0x03,	CFG_METHOD_4 = 0x04,	CFG_METHOD_5 = 0x05,	CFG_METHOD_6 = 0x06,	CFG_METHOD_7 = 0x07,	CFG_METHOD_8 = 0x08,	CFG_METHOD_9 = 0x09,	CFG_METHOD_10 = 0x0a,};#define _R(NAME,MAC,RCR,MASK, JumFrameSz) \	{ .name = NAME, .mcfg = MAC, .RCR_Cfg = RCR, .RxConfigMask = MASK, .jumbo_frame_sz = JumFrameSz }static const struct {	const char *name;	u8 mcfg;	u32 RCR_Cfg;	u32 RxConfigMask;	/* Clears the bits supported by this chip */	u32 jumbo_frame_sz;} rtl_chip_info[] = {	_R("RTL8168B/8111B",	   CFG_METHOD_1,	   (Reserved2_data << Reserved2_shift) | (RX_DMA_BURST << RxCfgDMAShift),	   0xff7e1880,	   Jumbo_Frame_4k),	_R("RTL8168B/8111B",	   CFG_METHOD_2,	   (Reserved2_data << Reserved2_shift) | (RX_DMA_BURST << RxCfgDMAShift),	   0xff7e1880,	   Jumbo_Frame_4k),	_R("RTL8168B/8111B",	   CFG_METHOD_3,	   (Reserved2_data << Reserved2_shift) | (RX_DMA_BURST << RxCfgDMAShift),	   0xff7e1880,	   Jumbo_Frame_4k),	_R("RTL8168C/8111C",	   CFG_METHOD_4, RxCfg_128_int_en | RxCfg_fet_multi_en | (RX_DMA_BURST << RxCfgDMAShift),	   0xff7e1880,	   Jumbo_Frame_6k),		_R("RTL8168C/8111C",	   CFG_METHOD_5,	   RxCfg_128_int_en | RxCfg_fet_multi_en | (RX_DMA_BURST << RxCfgDMAShift),	   0xff7e1880,	   Jumbo_Frame_6k),		_R("RTL8168C/8111C",	   CFG_METHOD_6,	   RxCfg_128_int_en | RxCfg_fet_multi_en | (RX_DMA_BURST << RxCfgDMAShift),	   0xff7e1880,	   Jumbo_Frame_6k),	_R("RTL8168CP/8111CP",	   CFG_METHOD_7,	   RxCfg_128_int_en | RxCfg_fet_multi_en | (RX_DMA_BURST << RxCfgDMAShift),	   0xff7e1880,	   Jumbo_Frame_6k),	_R("RTL8168CP/8111CP",	   CFG_METHOD_8,	   RxCfg_128_int_en | RxCfg_fet_multi_en | (RX_DMA_BURST << RxCfgDMAShift),	   0xff7e1880,	   Jumbo_Frame_6k),	_R("RTL8168D/8111D",	   CFG_METHOD_9,	   RxCfg_128_int_en | (RX_DMA_BURST << RxCfgDMAShift),	   0xff7e1880,	   Jumbo_Frame_9k),	_R("RTL8168D/8111D",	   CFG_METHOD_10,	   RxCfg_128_int_en | (RX_DMA_BURST << RxCfgDMAShift),	   0xff7e1880,	   Jumbo_Frame_9k),};#undef _Rstatic struct pci_device_id rtl8168_pci_tbl[] = {	{ PCI_DEVICE(PCI_VENDOR_ID_REALTEK,	0x8168), },	{0,},};MODULE_DEVICE_TABLE(pci, rtl8168_pci_tbl);static int rx_copybreak = 200;static int use_dac;static struct {	u32 msg_enable;} debug = { -1 };/* media options */#define MAX_UNITS 8static int speed[MAX_UNITS] = { -1, -1, -1, -1, -1, -1, -1, -1 };static int duplex[MAX_UNITS] = { -1, -1, -1, -1, -1, -1, -1, -1 };static int autoneg[MAX_UNITS] = { -1, -1, -1, -1, -1, -1, -1, -1 };MODULE_AUTHOR("Realtek and the Linux r8168 crew <netdev@vger.kernel.org>");MODULE_DESCRIPTION("RealTek RTL-8168 Gigabit Ethernet driver");#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)MODULE_PARM(speed, "1-" __MODULE_STRING(MAX_UNITS) "i");MODULE_PARM(duplex, "1-" __MODULE_STRING(MAX_UNITS) "i");MODULE_PARM(autoneg, "1-" __MODULE_STRING(MAX_UNITS) "i");#elsestatic int num_speed = 0;static int num_duplex = 0;static int num_autoneg = 0;module_param_array(speed, int, &num_speed, 0);module_param_array(duplex, int, &num_duplex, 0);module_param_array(autoneg, int, &num_autoneg, 0);#endifMODULE_PARM_DESC(speed, "force phy operation. Deprecated by ethtool (8).");MODULE_PARM_DESC(duplex, "force phy operation. Deprecated by ethtool (8).");MODULE_PARM_DESC(autoneg, "force phy operation. Deprecated by ethtool (8).");module_param(rx_copybreak, int, 0);MODULE_PARM_DESC(rx_copybreak, "Copy breakpoint for copy-only-tiny-frames");module_param(use_dac, int, 0);MODULE_PARM_DESC(use_dac, "Enable PCI DAC. Unsafe on 32 bit PCI slot.");#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)module_param_named(debug, debug.msg_enable, int, 0);MODULE_PARM_DESC(debug, "Debug verbosity level (0=none, ..., 16=all)");#endif//LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)MODULE_LICENSE("GPL");MODULE_VERSION(RTL8168_VERSION);static void rtl8168_dsm(struct net_device *dev, int dev_state);static void rtl8168_esd_timer(unsigned long __opaque);static void rtl8168_tx_clear(struct rtl8168_private *tp);static void rtl8168_rx_clear(struct rtl8168_private *tp);static int rtl8168_open(struct net_device *dev);static int rtl8168_start_xmit(struct sk_buff *skb, struct net_device *dev);#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)static irqreturn_t rtl8168_interrupt(int irq, void *dev_instance, struct pt_regs *regs);#elsestatic irqreturn_t rtl8168_interrupt(int irq, void *dev_instance);#endifstatic int rtl8168_init_ring(struct net_device *dev);static void rtl8168_hw_start(struct net_device *dev);static int rtl8168_close(struct net_device *dev);static void rtl8168_set_rx_mode(struct net_device *dev);static void rtl8168_tx_timeout(struct net_device *dev);static struct net_device_stats *rtl8168_get_stats(struct net_device *dev);static int rtl8168_rx_interrupt(struct net_device *, struct rtl8168_private *, void __iomem *, u32 budget);static int rtl8168_change_mtu(struct net_device *dev, int new_mtu);static void rtl8168_down(struct net_device *dev);static int rtl8168_set_mac_address(struct net_device *dev, void *p);void rtl8168_rar_set(struct rtl8168_private *tp, uint8_t *addr, uint32_t index);static void rtl8168_tx_desc_init(struct rtl8168_private *tp);static void rtl8168_rx_desc_init(struct rtl8168_private *tp);static void rtl8168_nic_reset(struct net_device *dev);static void rtl8168_phy_power_up (struct net_device *dev);static void rtl8168_phy_power_down (struct net_device *dev);#ifdef CONFIG_R8168_NAPIstatic int rtl8168_poll(napi_ptr napi, napi_budget budget);#endifstatic u16 rtl8168_intr_mask =	SYSErr | LinkChg | RxDescUnavail | TxErr | TxOK | RxErr | RxOK;static const u16 rtl8168_napi_event =	RxOK | RxDescUnavail | RxFIFOOver | TxOK | TxErr;//#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,3)#if (( LINUX_VERSION_CODE < KERNEL_VERSION(2,4,27) ) || \     (( LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ) && \      ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,3) )))/* copied from linux kernel 2.6.20 include/linux/netdev.h */#define	NETDEV_ALIGN		32#define	NETDEV_ALIGN_CONST	(NETDEV_ALIGN - 1)static inline void *netdev_priv(struct net_device *dev){	return (char *)dev + ((sizeof(struct net_device)					+ NETDEV_ALIGN_CONST)				& ~NETDEV_ALIGN_CONST);}#endif	//LINUX_VERSION_CODE < KERNEL_VERSION(2,6,3)//#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,5)#ifndef netif_msg_init#define netif_msg_init _kc_netif_msg_init/* copied from linux kernel 2.6.20 include/linux/netdevice.h */static inline u32 netif_msg_init(int debug_value, int default_msg_enable_bits){	/* use default */	if (debug_value < 0 || debug_value >= (sizeof(u32) * 8))		return default_msg_enable_bits;	if (debug_value == 0)	/* no output */		return 0;	/* set low N bits */	return (1 << debug_value) - 1;}#endif //LINUX_VERSION_CODE < KERNEL_VERSION(2,6,5)#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,22)static inline void eth_copy_and_sum (struct sk_buff *dest,				     const unsigned char *src,				     int len, int base){	memcpy (dest->data, src, len);}#endif //LINUX_VERSION_CODE > KERNEL_VERSION(2,6,22)#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,7)/* copied from linux kernel 2.6.20 /include/linux/time.h *//* Parameters used to convert the timespec values: */#define MSEC_PER_SEC	1000L/* copied from linux kernel 2.6.20 /include/linux/jiffies.h *//* * Change timeval to jiffies, trying to avoid the * most obvious overflows.. * * And some not so obvious. * * Note that we don't want to return MAX_LONG, because * for various timeout reasons we often end up having * to wait "jiffies+1" in order to guarantee that we wait * at _least_ "jiffies" - so "jiffies+1" had better still * be positive. */#define MAX_JIFFY_OFFSET ((~0UL >> 1)-1)/* * Convert jiffies to milliseconds and back. * * Avoid unnecessary multiplications/divisions in the * two most common HZ cases: */static inline unsigned int _kc_jiffies_to_msecs(const unsigned long j){#if HZ <= MSEC_PER_SEC && !(MSEC_PER_SEC % HZ)	return (MSEC_PER_SEC / HZ) * j;#elif HZ > MSEC_PER_SEC && !(HZ % MSEC_PER_SEC)	return (j + (HZ / MSEC_PER_SEC) - 1)/(HZ / MSEC_PER_SEC);#else	return (j * MSEC_PER_SEC) / HZ;#endif}static inline unsigned long _kc_msecs_to_jiffies(const unsigned int m){	if (m > _kc_jiffies_to_msecs(MAX_JIFFY_OFFSET))		return MAX_JIFFY_OFFSET;#if HZ <= MSEC_PER_SEC && !(MSEC_PER_SEC % HZ)	return (m + (MSEC_PER_SEC / HZ) - 1) / (MSEC_PER_SEC / HZ);#elif HZ > MSEC_PER_SEC && !(HZ % MSEC_PER_SEC)	return m * (HZ / MSEC_PER_SEC);#else	return (m * HZ + MSEC_PER_SEC - 1) / MSEC_PER_SEC;#endif}#endif	//LINUX_VERSION_CODE < KERNEL_VERSION(2,6,7)#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)/* copied from linux kernel 2.6.12.6 /include/linux/pm.h */typedef int __bitwise pci_power_t;/* copied from linux kernel 2.6.12.6 /include/linux/pci.h */typedef u32 __bitwise pm_message_t;#define PCI_D0	((pci_power_t __force) 0)#define PCI_D1	((pci_power_t __force) 1)#define PCI_D2	((pci_power_t __force) 2)#define PCI_D3hot	((pci_power_t __force) 3)#define PCI_D3cold	((pci_power_t __force) 4)#define PCI_POWER_ERROR	((pci_power_t __force) -1)/* copied from linux kernel 2.6.12.6 /drivers/pci/pci.c *//** * pci_choose_state - Choose the power state of a PCI device * @dev: PCI device to be suspended * @state: target sleep state for the whole system. This is the value *	that is passed to suspend() function. * * Returns PCI power state suitable for given device and given system * message. */pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state){	if (!pci_find_capability(dev, PCI_CAP_ID_PM))		return PCI_D0;	switch (state) {	case 0: return PCI_D0;	case 3: return PCI_D3hot;	default:		printk("They asked me for state %d\n", state);//		BUG();	}	return PCI_D0;}#endif	//LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,9)/** * msleep_interruptible - sleep waiting for waitqueue interruptions * @msecs: Time in milliseconds to sleep for */#define msleep_interruptible _kc_msleep_interruptibleunsigned long _kc_msleep_interruptible(unsigned int msecs){	unsigned long timeout = _kc_msecs_to_jiffies(msecs);	while (timeout && !signal_pending(current)) {		set_current_state(TASK_INTERRUPTIBLE);		timeout = schedule_timeout(timeout);	}	return _kc_jiffies_to_msecs(timeout);}#endif	//LINUX_VERSION_CODE < KERNEL_VERSION(2,6,9)#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,7)/* copied from linux kernel 2.6.20 include/linux/sched.h */#ifndef __sched#define __sched		__attribute__((__section__(".sched.text")))#endif/* copied from linux kernel 2.6.20 kernel/timer.c */signed long __sched schedule_timeout_uninterruptible(signed long timeout){	__set_current_state(TASK_UNINTERRUPTIBLE);	return schedule_timeout(timeout);}/* copied from linux kernel 2.6.20 include/linux/mii.h */#undef if_mii#define if_mii _kc_if_miistatic inline struct mii_ioctl_data *if_mii(struct ifreq *rq){	return (struct mii_ioctl_data *) &rq->ifr_ifru;}#endif	//LINUX_VERSION_CODE < KERNEL_VERSION(2,6,7)static void mdio_write(void __iomem *ioaddr, 	   int RegAddr, 	   int value){	int i;	RTL_W32(PHYAR, PHYAR_Write | 		(RegAddr & PHYAR_Reg_Mask) << PHYAR_Reg_shift | 		(value & PHYAR_Data_Mask));	for (i = 0; i < 10; i++) {		/* Check if the RTL8168 has completed writing to the specified MII register */		if (!(RTL_R32(PHYAR) & PHYAR_Flag)) 			break;		udelay(100);	}}static int mdio_read(void __iomem *ioaddr, 	  int RegAddr){	int i, value = -1;	RTL_W32(PHYAR, 		PHYAR_Read | (RegAddr & PHYAR_Reg_Mask) << PHYAR_Reg_shift);	for (i = 0; i < 10; i++) {		/* Check if the RTL8168 has completed retrieving data from the specified MII register */		if (RTL_R32(PHYAR) & PHYAR_Flag) {			value = (int) (RTL_R32(PHYAR) & PHYAR_Data_Mask);			break;		}		udelay(100);	}	return value;}static voidrtl8168_ephy_write(void __iomem *ioaddr, 		   int RegAddr, 		   int value){	int i;	RTL_W32(EPHYAR, 		EPHYAR_Write | 		(RegAddr & EPHYAR_Reg_Mask) << EPHYAR_Reg_shift | 		(value & EPHYAR_Data_Mask));	for (i = 0; i < 10; i++) {		udelay(100);		/* Check if the RTL8168 has completed EPHY write */		if (!(RTL_R32(EPHYAR) & EPHYAR_Flag)) 			break;	}	udelay(20);}static u16rtl8168_ephy_read(void __iomem *ioaddr, 		  int RegAddr){	int i;	u16 value = 0xffff;	RTL_W32(EPHYAR, 		EPHYAR_Read | (RegAddr & EPHYAR_Reg_Mask) << EPHYAR_Reg_shift);	for (i = 0; i < 10; i++) {		udelay(100);		/* Check if the RTL8168 has completed EPHY read */		if (RTL_R32(EPHYAR) & EPHYAR_Flag) {			value = (u16) (RTL_R32(EPHYAR) & EPHYAR_Data_Mask);			break;		}	}	udelay(20);	return value;}static voidrtl8168_csi_write(void __iomem *ioaddr, 		   int addr,		   int value){	int i;	RTL_W32(CSIDR, value);	RTL_W32(CSIAR, 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -