Linux 网络设备驱动(dm9000)

网络设备驱动的分层

网络协议接口层 ------------------ 数据链路层
网络接口层 ------------------------ 数据链路层
设备驱动功能层 ------------------ 数据链路层
网络媒介层 ------------------------ 物理层

网络协议接口层

网络协议接口层给上层协议提供统一的数据包收发接口,无论上层是ARP协议还是IP协议,都通过dev_queue_xmit()函数发送数据,通过netif_rx函数接收数据。此层使得上层协议独立于具体的设备。

为网络驱动提供一些列netif开头的函数:
激活设备发送队列:void netif_start_queue(struct net_device *dev)
停止设备传输包:void netif_stop_queue(struct net_device *dev)
重新启动设备发送队列:void netif_awake_queue(struct net_device *dev)

网络设备接口层

为各种各样的网络设备定义同一的、抽象的数据结构net_device结构体,实现多种硬件在软件层次上的统一。
net_device结构体在内核中指代一个网络设备,网络设备驱动只要填充其结构体就可以实现内核与具体硬件操作函数的挂接。
net_device定义的位置:include/linux/netdevice.h

 struct net_device { 	
	 /*	
	  * This is the first field of the "visible" part of this structure	
	  *  (i.e. as seen by users in the 	"Space.c" file).  It is the name	
	  *  of the interface.	 
	  */	
 	char			name[IFNAMSIZ]; //网络设备名称 	
 	struct pm_qos_request	pm_qos_req; 	
 	/* device name hash chain */	struct hlist_node	name_hlist;	/* snmp alias */	
 	char 			*ifalias; 	
 	/*	 
 	*	I/O specific fields	
 	*	FIXME: Merge these and struct ifmap into one	 
 	*/	
  	
 	//设备所用的共享内存的起始地址和结束地址 
 	unsigned long		mem_end;	/* shared mem end	*/	unsigned long		mem_start;	/* shared mem start	*/	unsigned long		
 	//base_addr 网络设备I/O基地址 
 	base_addr;	/* device I/O address	*/	
 	//irq 设备使用的中断号
 	unsigned int		irq;		/* device IRQ number	*/ 	

	/*	Some hardware also needs these fields, but they are not	 
	*	part of the usual set specified in Space.c.	 
	*/ 	
	unsigned long		state; 	
	struct list_head	dev_list;	
	struct list_head	napi_list;	
	struct list_head	unreg_list; 	
	
	/* currently active device features */
	netdev_features_t	features;	
	/* user-changeable features */	
	netdev_features_t	hw_features;	
	/* user-requested features */	
	netdev_features_t	wanted_features;
	/* mask of features inheritable by VLAN devices */	
	netdev_features_t	vlan_features; 	
	
	/* Interface index. Unique device identifier	*/	
	int			ifindex;	
	int			iflink; 	
	
	struct net_device_stats	stats;	a
	tomic_long_t		rx_dropped; /* dropped packets by core network					     * Do not use this in drivers.					     */
 #ifdef CONFIG_WIRELESS_EXT
 	/* List of functions to handle Wireless Extensions (instead of ioctl).	
 	 * See <net/iw_handler.h> for details. Jean II */
 	 const struct iw_handler_def *	wireless_handlers;	
 	 /* Instance data managed by the core of Wireless Extensions. */	
 	 struct iw_public_data *	wireless_data;
 #endif	
 	/* Management operations */	
 	const struct net_device_ops *netdev_ops;	
 	const struct ethtool_ops *ethtool_ops; 	

	/* Hardware header description */	
	const struct header_ops *header_ops;		
	
	//网络接口标志	
	unsigned int		flags;	/* interface flags (a la BSD)	*/	
	unsigned int		priv_flags; /* Like 'flags' but invisible to userspace.					     * See if.h for definitions. */
	
	unsigned short		gflags;	
	unsigned short		padded;	/* How much padding added by alloc_netdev() */ 	

	unsigned char		operstate; /* RFC2863 operstate */	
	unsigned char		link_mode; /* mapping policy to operstate */		
	//多端口设备中端口选择	
	unsigned char		if_port;	/* Selectable AUI, TP,..*/	
	//分配给设备的dma通道	
	unsigned char		dma;		/* DMA channel		*/		
	//最大传输单元	
	unsigned int		mtu;	/* interface MTU value		*/	
	//接口的硬件类型	
	unsigned short		type;	/* interface hardware type	*/	//网络设备硬件头长度,在以太网设备初始化函数中,赋值为ETH_HLEN
	unsigned short		hard_header_len;	/* hardware hdr length	*/ 	

	/* extra head- and tailroom the hardware may need, but not in all cases	 
	* can this be guaranteed, especially tailroom. Some cases also use	
	 * LL_MAX_HEADER instead to allocate the skb.	
	  */	
	  unsigned short		needed_headroom;	
	  unsigned short		needed_tailroom; 	

	/* Interface address info. */	
	unsigned char		perm_addr[MAX_ADDR_LEN]; /* permanent hw address */	
	unsigned char		addr_assign_type; /* hw address assignment type */	
	unsigned char		addr_len;	/* hardware address length	*/	
	unsigned char		neigh_priv_len;	
	unsigned short          dev_id;		/* for shared network cards */ 	
	spinlock_t		addr_list_lock;	
	struct netdev_hw_addr_list	uc;	/* Unicast mac addresses */	
	struct netdev_hw_addr_list	mc;	/* Multicast mac addresses */	
	bool			uc_promisc;	
	unsigned int		promiscuity;	
	unsigned int		allmulti;  	

	/* Protocol specific pointers */
#if IS_ENABLED(CONFIG_VLAN_8021Q)	
	struct vlan_info __rcu	*vlan_info;	/* VLAN info */
#endif
#if IS_ENABLED(CONFIG_NET_DSA)	
	struct dsa_switch_tree	*dsa_ptr;	/* dsa specific data */
#endif	
	void 			*atalk_ptr;	/* AppleTalk link 	*/	
	struct in_device __rcu	*ip_ptr;	/* IPv4 specific data	*/	
	struct dn_dev __rcu     *dn_ptr;        /* DECnet specific data */	
	struct inet6_dev __rcu	*ip6_ptr;       /* IPv6 specific data */	
	void			*ax25_ptr;	/* AX.25 specific data */	
	struct wireless_dev	*ieee80211_ptr;	/* IEEE 802.11 specific data,	assign before registering */
	 
	 /* 
	 * Cache lines mostly used on receive path (including eth_type_trans()) 
	 */	
	 unsigned long		last_rx;	/* Time of last Rx						 
	 						* This should not be set in						 
	 						* drivers, unless really needed,						 
	 						* because network stack (bonding)						 
	 						* use it if/when necessary, to						 
	 						* avoid dirtying this cache line.						 
	 						*/ 	
	struct net_device	*master; /* Pointer to master device of a group,	 which this device is member of.*/ 	
	/* Interface address info used in eth_type_trans() */	
	//设备的硬件地址 mac地址	
	unsigned char		*dev_addr;	/* hw address, (before bcast	because most packets are unicast) */		
	struct netdev_hw_addr_list	dev_addrs; /* list of device hw addresses */	
	//设备的广播地址	
	unsigned char		broadcast[MAX_ADDR_LEN];	/* hw bcast add	*/ 
#ifdef CONFIG_SYSFS	
	struct kset		*queues_kset;
#endif 
#ifdef CONFIG_RPS	
	struct netdev_rx_queue	*_rx; 	/* Number of RX queues allocated at register_netdev() time */	
	unsigned int		num_rx_queues; 	/* Number of RX queues currently active in device */	
	unsigned int		real_num_rx_queues; 
	
#ifdef CONFIG_RFS_ACCEL	
	/* CPU reverse-mapping for RX completion interrupts, indexed	 
	* by RX queue number.  Assigned by driver.  This must only be	 
	* set if the ndo_rx_flow_steer operation is defined. 
	*/	
	struct cpu_rmap		*rx_cpu_rmap;
#endif
#endif 	
	rx_handler_func_t __rcu	*rx_handler;	
	void __rcu		*rx_handler_data; 	
	struct netdev_queue __rcu *ingress_queue;

	 /* Cache lines mostly used on transmit path */	
	 struct netdev_queue	*_tx ____cacheline_aligned_in_smp; 	
	
	/* Number of TX queues allocated at alloc_netdev_mq() time  */	
	unsigned int		num_tx_queues; 
	
	/* Number of TX queues currently active in device  */	
	unsigned int		real_num_tx_queues; 

	/* root qdisc from userspace point of view */	
	struct Qdisc		*qdisc; 	

	unsigned long		tx_queue_len;	/* Max frames per queue allowed */	
	spinlock_t		tx_global_lock; 
#ifdef CONFIG_XPS	
	struct xps_dev_maps __rcu *xps_maps
;#endif 	
	/* These may be needed for future network-power-down code. */ 	

	/*	 
	* trans_start here is expensive for high speed devices on SMP,	 
	* please use netdev_queue->trans_start instead.	 
	*/	
	unsigned long		trans_start;	/* Time (in jiffies) of last Tx	*/ 	
	int			watchdog_timeo; /* used by dev_watchdog() */	
	struct timer_list	watchdog_timer; 
	
	/* Number of references to this device */	
	int __percpu		*pcpu_refcnt; 	

	/* delayed register/unregister */	
	struct list_head	todo_list;	

	/* device index hash chain */	
	struct hlist_node	index_hlist; 	
	struct list_head	link_watch_list; 	

	/* register/unregister state machine */	
	enum { NETREG_UNINITIALIZED=0,	      
		 NETREG_REGISTERED,	/* completed register_netdevice */	      
		  NETREG_UNREGISTERING,	/* called unregister_netdevice */	      
		   NETREG_UNREGISTERED,	/* completed unregister todo */	       
		   NETREG_RELEASED,		/* called free_netdev */	       
		   NETREG_DUMMY,		/* dummy device for NAPI poll */	
	} reg_state:8; 	

	bool dismantle; /* device is going do be freed */ 	

	enum {		
		RTNL_LINK_INITIALIZED,		
		RTNL_LINK_INITIALIZING,	
	} rtnl_link_state:16; 	

	/* Called from unregister, can be used to call free_netdev */
	void (*destructor)(struct net_device *dev);
 #ifdef CONFIG_NETPOLL	
	 struct netpoll_info	*npinfo;
#endif 

#ifdef CONFIG_NET_NS	
	/* Network namespace this network device is inside */	
	struct net		*nd_net;
#endif 	
	/* mid-layer private */	
	union {		
		void				*ml_priv;		
		struct pcpu_lstats __percpu	*lstats; /* loopback stats */		
		struct pcpu_tstats __percpu	*tstats; /* tunnel stats */		
		struct pcpu_dstats __percpu	*dstats; /* dummy stats */	
	};	

	/* GARP */	
	struct garp_port __rcu	*garp_port; 
	
	/* class/net/name entry */	
	struct device		dev;	
	/* space for optional device, statistics, and wireless sysfs groups */	
	const struct attribute_group *sysfs_groups[4]; 	

	/* rtnetlink link ops */	
	const struct rtnl_link_ops *rtnl_link_ops; 	

	/* for setting kernel sock attribute on TCP connection setup */
	#define GSO_MAX_SIZE		65536	
	unsigned int		gso_max_size; 
		
#ifdef CONFIG_DCB	/* Data Center Bridging netlink ops */	
	const struct dcbnl_rtnl_ops *dcbnl_ops;
#endif	
	u8 num_tc;	
	struct netdev_tc_txq tc_to_txq[TC_MAX_QUEUE];	
	u8 prio_tc_map[TC_BITMASK + 1];
	
#if IS_ENABLED(CONFIG_FCOE)	
	/* max exchange id for FCoE LRO by ddp */	
	unsigned int		fcoe_ddp_xid;
#endif

#if IS_ENABLED(CONFIG_NETPRIO_CGROUP)	
	struct netprio_map __rcu *priomap;
#endif	
	/* phy device may attach itself for hardware timestamping */	
	struct phy_device *phydev; 	

	/* group the device belongs to */	
	int group;
};

设备驱动功能层

net_device中的成员函数是一些函数指针,这些函数的实现是在设备驱动功能层实现。设备驱动功能层是Linux驱动开发要做的主要工作。

网络媒介层

主要和具体硬件大交道的一层,实现一些访问网络设备内部寄存器的操作。

重要的结构体sk_buff

sk_buff结构体是套接字缓冲区,详细记录了一个数据包的组成、时间、网络设备、各层的首部及首部的长度和数据的首尾指针。

sk_buff结构体的定义:

struct sk_buff {
	/* These two members must be first. */
	struct sk_buff                *next;s
	truct sk_buff                *prev;
	 ktime_t                        tstamp; 
	 struct sock                *sk;
	 struct net_device        *dev;

	 /* 
	 * This is the control buffer. It is free to use for every 
	 * layer. Please put your private variables there. If you 
	 * want to keep them across layers you have to do a skb_clone() 
	 * first. This is owned by whoever has the skb queued ATM. 
	 */
	 char                        cb[48] __aligned(8); 

	unsigned long                _skb_refdst;
	
#ifdef CONFIG_XFRM
	struct        sec_path        *sp;
#endif
	unsigned int                len,data_len;
	__u16                        mac_len,hdr_len;
	union {
		__wsum                csum;
		struct {
			__u16        csum_start;
			__u16        csum_offset;
		};
	};
	__u32                        priority;
	kmemcheck_bitfield_begin(flags1);
	__u8                        local_df:1,
				   cloned:1,
				   ip_summed:2,
				   nohdr:1,
				   nfctinfo:3;
	__u8                        pkt_type:3,
				   fclone:2,
				   ipvs_property:1,
				   peeked:1,
				   nf_trace:1;
   	kmemcheck_bitfield_end(flags1);
   	__be16    	 protocol; 

	void                        (*destructor)(struct sk_buff *skb
#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
	struct nf_conntrack        *nfct;
#endif

#ifdef NET_SKBUFF_NF_DEFRAG_NEEDED
	struct sk_buff                *nfct_reasm;
#endif

#ifdef CONFIG_BRIDGE_NETFILTER
	struct nf_bridge_info        *nf_bridge;
#endif 
	int                        skb_iif; 
	__u32                        rxhash;				
	__u16                        vlan_tci;
	
 #ifdef CONFIG_NET_SCHED
 	__u16                        tc_index;        /* traffic control index */
 #ifdef CONFIG_NET_CLS_ACT
 	__u16                        tc_verd;        /* traffic control verdict */
 #endif
 #endif
 	 __u16                        queue_mapping;
 	 kmemcheck_bitfield_begin(flags2);
#ifdef CONFIG_IPV6_NDISC_NODETYPE
	__u8                        ndisc_nodetype:2;
#endif
	__u8                        ooo_okay:1;
	__u8                        l4_rxhash:1;
	__u8                        wifi_acked_valid:1;
	__u8                        wifi_acked:1;
	__u8                        no_fcs:1;
	__u8                        head_frag:1;
	/* 8/10 bit hole (depending on ndisc_nodetype presence) */
	kmemcheck_bitfield_end(flags2); 
#ifdef CONFIG_NET_DMA
	dma_cookie_t                dma_cookie;
#endif
#ifdef CONFIG_NETWORK_SECMARK
	__u32                        secmark;
#endif
	union {
			__u32                mark;
			__u32                dropcount;
			__u32                avail_size;
		}; sk_buff_data_t               
	 transport_header;
	 sk_buff_data_t                network_header;
	 sk_buff_data_t                mac_header;
	 /* These elements must be at the end, see alloc_skb() for details.  */
	 sk_buff_data_t                tail;
	 sk_buff_data_t                end;
	 unsigned char                *head,*data;
	 unsigned int                truesize;
	 atomic_t                users;
};

sk_buff结构体中的4个重要成员:
sk_buff_data_t tail;
sk_buff_data_t end;
unsigned char *head,*data;

和sk_buff相关的函数:
alloc_skb()函数,该函数是在上层协议要发送数据包的时候或网络设备准备接收数据包的时候分配sk_buff结构体。

static inline struct sk_buff *alloc_skb(unsigned int size,  gfp_t priority)
{
	 return __alloc_skb(size, priority, 0, NUMA_NO_NODE);
}

struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask,			    
			int fclone, int node)
{	
	struct kmem_cache *cache;	
	struct skb_shared_info *shinfo;	
	struct sk_buff *skb;	
	u8 *data; 	

	cache = fclone ? skbuff_fclone_cache : skbuff_head_cache; 	
	/* Get the HEAD */	
	skb = kmem_cache_alloc_node(cache, gfp_mask & ~__GFP_DMA, node);	
	if (!skb)		
		goto out;	
	prefetchw(skb); 	

	/* We do our best to align skb_shared_info on a separate cache	 
	* line. It usually works because kmalloc(X > SMP_CACHE_BYTES) gives	 
	* aligned memory blocks, unless SLUB/SLAB debug is enabled.	 
	* Both skb->head and skb_shared_info are cache line aligned.	 
	*/	
	size = SKB_DATA_ALIGN(size);	
	size += SKB_DATA_ALIGN(sizeof(struct skb_shared_info));	
	data = kmalloc_node_track_caller(size, gfp_mask, node);	
	if (!data)		
		goto nodata;	

	/* kmalloc(size) might give us more room than requested.	 
	* Put skb_shared_info exactly at the end of allocated zone,	 
	* to allow max possible filling before reallocation.	 	
	*/	
	size = SKB_WITH_OVERHEAD(ksize(data));	
	prefetchw(data + size); 
	
	/*	 
	* Only clear those fields we need to clear, not those that we will	 
	* actually initialise below. Hence, don't put any more fields after	 
	* the tail pointer in struct sk_buff!	 
	*/	
	memset(skb, 0, offsetof(struct sk_buff, tail));	

	/* Account for allocated memory : skb + skb->head */	
	skb->truesize = SKB_TRUESIZE(size);	
	atomic_set(&skb->users, 1);	
	
	skb->head = data;	
	skb->data = data;	
	skb_reset_tail_pointer(skb);	
	skb->end = skb->tail + size;

#ifdef NET_SKBUFF_DATA_USES_OFFSET	
	skb->mac_header = ~0U;
#endif 	
	/* make sure we initialize shinfo sequentially */	
	shinfo = skb_shinfo(skb);	
	memset(shinfo, 0, offsetof(struct skb_shared_info, dataref));	
	atomic_set(&shinfo->dataref, 1);	
	kmemcheck_annotate_variable(shinfo->destructor_arg); 	
	if (fclone) {		
		struct sk_buff *child = skb + 1;		
		atomic_t *fclone_ref = (atomic_t *) (child + 1); 		
		kmemcheck_annotate_bitfield(child, flags1);		
		kmemcheck_annotate_bitfield(child, flags2);		
		skb->fclone = SKB_FCLONE_ORIG;		
		atomic_set(fclone_ref, 1); 		
		child->fclone = SKB_FCLONE_UNAVAILABLE;	
	}
	out:	
		return skb;
	nodata:	
		kmem_cache_free(cache, skb);	
		skb = NULL;	
		goto out;
	}

在alloc_skb中执行了关于head、data、tail、end的操作:
skb->head = data;
skb->data = data;
skb_reset_tail_pointer(skb);
skb->end = skb->tail + size;

得到如下的一张关系图 在这里插入图片描述
void kfree_skb(struct sk_buff *skb) 函数,释放不被使用的sk_buff结构体

处理skb_buff数据区的相应函数:
unsigned char *skb_put(struct sk_buff *skb, unsigned int len)函数,在数据取的末端添加某协议的尾部

unsigned char *skb_push(struct sk_buff *skb, unsigned int len)函数,在数据区的前端添加某协议的头部

unsigned char *skb_pull(struct sk_buff *skb, unsigned int len)函数,在前端去掉某协议的头部

void skb_trim(struct sk_buff *skb, unsigned int len)函数,在末端去掉某协议的尾部

skb_buff缓冲区链表的操作函数:
static inline void skb_orphan(struct sk_buff *skb)
函数,将一个缓冲结构体变成孤立的skb

static inline void skb_queue_head_init(struct sk_buff_head *list)函数,初始化sk_buff_head结构体

void skb_queue_head(struct sk_buff_head *list, struct sk_buff *newsk)函数,在链表头部添加一个sk_buff结构体

void skb_queue_tail(struct sk_buff_head *list, struct sk_buff *newsk)函数,在链表尾部添加新的skb_buff结构体

struct sk_buff *skb_dequeue(struct sk_buff_head *list)函数,在链表头部移走一个skb
skb_dequeue_tail,在链表尾部移走一个skb

void skb_queue_purge(struct sk_buff_head *list) ,清空一个由sk_buff_head管理的缓冲区链表

void skb_append(struct sk_buff *old, struct sk_buff *newsk, struct sk_buff_head *list)函数,在指定的skb后附加一个skb

网络设备DM9000驱动实例分析

内核版本号Linux3.5.0
开发板型号smdkv210
从arch/arm/mach-s5pv210/mach-smdkv210.c入手:找到关于dm9000的资源分配

#define DEFINE_RES_MEM_NAMED(_start, _size, _name)                       
	 \DEFINE_RES_NAMED((_start), (_size), (_name), IORESOURCE_MEM
 )#define DEFINE_RES_MEM(_start, _size)                                       
  	\DEFINE_RES_MEM_NAMED((_start), (_size), NULL)  

static struct resource smdkv210_dm9000_resources[] = {
	[0] = DEFINE_RES_MEM(S5PV210_PA_SROM_BANK5, 1),
	[1] = DEFINE_RES_MEM(S5PV210_PA_SROM_BANK5 + 2, 1),
	[2] = DEFINE_RES_NAMED(IRQ_EINT(9), 1, NULL, IORESOURCE_IRQ \
	| IORESOURCE_IRQ_HIGHLEVEL),
}; 

//平台设备私有数据
static struct dm9000_plat_data smdkv210_dm9000_platdata = {
	.flags                = DM9000_PLATF_16BITONLY | M9000_PLATF_NO_EEPROM,
	.dev_addr        = { 0x00, 0x09, 0xc0, 0xff, 0xec, 0x48 }, //网卡mac地址
}; 

static struct platform_device smdkv210_dm9000 = {
	.name                = "dm9000",
	.id                = -1,     //表明只有1块网卡
	.num_resources        = ARRAY_SIZE(smdkv210_dm9000_resources),
	.resource        = smdkv210_dm9000_resources,
	.dev                = {
		.platform_data        = &smdkv210_dm9000_platdata,
	},
};

dm9000平台设备被定义在smdkv210的设备列表里:

static struct platform_device *smdkv210_devices[] __initdata = {	
	&s3c_device_adc,	
	&s3c_device_cfcon,	
	&s3c_device_fb,	
	&s3c_device_hsmmc0,
	&s3c_device_hsmmc1,
	&s3c_device_hsmmc2,	
	&s3c_device_hsmmc3,	
	&s3c_device_i2c0,	
	&s3c_device_i2c1,	
	&s3c_device_i2c2,	
	&s3c_device_rtc,	
	&s3c_device_ts,	
	&s3c_device_usb_hsotg,	
	&s3c_device_wdt,	
	&s5p_device_fimc0,	
	&s5p_device_fimc1,	
	&s5p_device_fimc2,	
	&s5p_device_fimc_md,	
	&s5p_device_jpeg,	
	&s5p_device_mfc,	
	&s5p_device_mfc_l,	
	&s5p_device_mfc_r,	
	&s5pv210_device_ac97,	
	&s5pv210_device_iis0,	
	&s5pv210_device_spdif,	
	&samsung_asoc_dma,	
	&samsung_asoc_idma,	
	&samsung_device_keypad,	
	&smdkv210_dm9000,		//dm9000 platform device
	&smdkv210_lcd_lte480wv,
};

分析dm9000驱动代码,drivers/net/ethernet/davicom/dm9000.c

static int __initdm9000_init(void){
	printk(KERN_INFO "%s Ethernet Driver, V%s\n", CARDNAME, DRV_VERSION); 
	return platform_driver_register(&dm9000_driver);
} 
static struct platform_driver dm9000_driver = {
	.driver        = {
		.name    = "dm9000",
		.owner         = THIS_MODULE,
		.pm         = &dm9000_drv_pm_ops,
	},
	.probe   = dm9000_probe,
	.remove  = __devexit_p(dm9000_drv_remove),
};

dm9000_driver的重点就是dm9000_probe函数:

/* * Search DM9000 board, allocate space and register it */
static int __devinitdm9000_probe(struct platform_device *pdev){	
	struct dm9000_plat_data *pdata = pdev->dev.platform_data; /*获取dev里的platform数据*/	
	/*dm9000设备 自有的独特的信息*/	
	struct board_info *db;	/* Point a board information structure */	
	struct net_device *ndev; /*Point a net device structure */	
	const unsigned char *mac_src;	
	int ret = 0;	
	int iosize;	
	int i;	
	u32 id_val; 	

	/* Init network device */	
	ndev = alloc_etherdev(sizeof(struct board_info)); //分配以太网设备结构体	
	if (!ndev)		
		return -ENOMEM; 	

	//(net)->dev.parent = (pdev) 网络设备和平台设备的关系	
	SET_NETDEV_DEV(ndev, &pdev->dev); 	

	dev_dbg(&pdev->dev, "dm9000_probe()\n"); 	/* setup board info structure */		
	//返回net_device结构末端地址,也就是网卡私有数据结构的起始地址	
	db = netdev_priv(ndev); 	
	db->dev = &pdev->dev;	
	db->ndev = ndev;		

	spin_lock_init(&db->lock); //初始化自旋锁	
	mutex_init(&db->addr_lock); //初始化互斥锁 	

	//挂入dm9000_poll_work函数:dm9000_poll_work用于载波检测	
	INIT_DELAYED_WORK(&db->phy_poll, dm9000_poll_work);  	
	db->addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); //地址资源	db->data_res = platform_get_resource(pdev, IORESOURCE_MEM, 1); //数据资源	db->irq_res  = platform_get_resource(pdev, IORESOURCE_IRQ, 0); //中断资源 	

	//判断资源获取是否成功	
	if (db->addr_res == NULL || db->data_res == NULL ||	    
		db->irq_res == NULL) {		
		dev_err(db->dev, "insufficient resources\n");		
		ret = -ENOENT;		
		goto out;	
	} 	

	//获得中断号	
	db->irq_wake = platform_get_irq(pdev, 1); 	
	if (db->irq_wake >= 0) {		
		dev_dbg(db->dev, "wakeup irq %d\n", db->irq_wake); 		
		ret = request_irq(db->irq_wake, dm9000_wol_interrupt,				  		IRQF_SHARED, dev_name(db->dev), ndev);		
		if (ret) {			
			dev_err(db->dev, "cannot get wakeup irq (%d)\n", ret);		
		} else { 			/* test to see if irq is really wakeup capable */				ret = irq_set_irq_wake(db->irq_wake, 1);			
			if (ret) {				
				dev_err(db->dev, "irq %d cannot set wakeup (%d)\n",								db->irq_wake, ret);				
				ret = 0;			
			} else {				
				irq_set_irq_wake(db->irq_wake, 0);										db->wake_supported = 1;			
			}		
		}	
	} 	
	
	//需要分配的IO资源大小	
	iosize = resource_size(db->addr_res); //地址区资源	
	/*申请存储空间*/	
	db->addr_req = request_mem_region(db->addr_res->start, iosize,					  pdev->name); 	
	if (db->addr_req == NULL) {		
		dev_err(db->dev, "cannot claim address reg area\n");		
		ret = -EIO;		
		goto out;	
	} 	

	/*ioremap物理地址映射后才能使用*/	
	db->io_addr = ioremap(db->addr_res->start, iosize); 	
	if (db->io_addr == NULL) {		
		dev_err(db->dev, "failed to ioremap address reg\n");		
		ret = -EINVAL;		g
		oto out;	
	} 	

	iosize = resource_size(db->data_res); //数据区资源	
	db->data_req = request_mem_region(db->data_res->start, iosize,					  pdev->name); 	
	if (db->data_req == NULL) {		
		dev_err(db->dev, "cannot claim data reg area\n");		
		ret = -EIO;		
		goto out;
	} 	
	db->io_data = ioremap(db->data_res->start, iosize); 
	if (db->io_data == NULL) {		
		dev_err(db->dev, "failed to ioremap data reg\n");		
		ret = -EINVAL;		
		goto out;	
	} 	

	/* fill in parameters for net-dev structure */	
	ndev->base_addr = (unsigned long)db->io_addr;	
	ndev->irq	= db->irq_res->start; //中断号 	
	/* ensure at least we have a default set of IO routines */	
	dm9000_set_io(db, iosize); 
	//set io width 	/* check to see if anything is being over-ridden */	//pdata来自于mach-smkdv210中定义的smdkv210_dm9000_platdata	
	if (pdata != NULL) {		
		/* check to see if the driver wants to over-ride the		 
		* default IO width */ 		
		if (pdata->flags & DM9000_PLATF_8BITONLY)			
			dm9000_set_io(db, 1); 		
		if (pdata->flags & DM9000_PLATF_16BITONLY)			
			dm9000_set_io(db, 2); 		
		if (pdata->flags & DM9000_PLATF_32BITONLY)			
			dm9000_set_io(db, 4); 		
		/* check to see if there are any IO routine over-rides */ 		
		if (pdata->inblk != NULL)			
			db->inblk = pdata->inblk; 		
		if (pdata->outblk != NULL)			
			db->outblk = pdata->outblk; 		
		if (pdata->dumpblk != NULL)			
			db->dumpblk = pdata->dumpblk; 		
		db->flags = pdata->flags;	
	} 
#ifdef CONFIG_DM9000_FORCE_SIMPLE_PHY_POLL	
	db->flags |= DM9000_PLATF_SIMPLE_PHY;
#endif 
	/*dm9000 重置*/	
	dm9000_reset(db); 
	
	/* try multiple times, DM9000 sometimes gets the read wrong */	
	//多次尝试读取dm9000 ID 测试读取是否正确	
	for (i = 0; i < 8; i++) {		
		id_val  = ior(db, DM9000_VIDL);		
		id_val |= (u32)ior(db, DM9000_VIDH) << 8;		
		id_val |= (u32)ior(db, DM9000_PIDL) << 16;		
		id_val |= (u32)ior(db, DM9000_PIDH) << 24; 		
		if (id_val == DM9000_ID)			
			break;		
		dev_err(db->dev, "read wrong id 0x%08x\n", id_val);	
	} 	

	if (id_val != DM9000_ID) {		
		dev_err(db->dev, "wrong id: 0x%08x\n", id_val);		
		ret = -ENODEV;		
		goto out;	
	} 	

	/* Identify what type of DM9000 we are working on */	/*确认DM9000的具体型号*/	id_val = ior(db, DM9000_CHIPR);	
	dev_dbg(db->dev, "dm9000 revision 0x%02x\n", id_val); 	
	switch (id_val) {	
		case CHIPR_DM9000A:		db->type = TYPE_DM9000A;		
		break;	
		case CHIPR_DM9000B:		db->type = TYPE_DM9000B;		
		break;	
		default:		
		dev_dbg(db->dev, "ID %02x => defaulting to DM9000E\n", id_val);				db->type = TYPE_DM9000E;	
	} 	

	/* dm9000a/b are capable of hardware checksum offload */	
	if (db->type == TYPE_DM9000A || db->type == TYPE_DM9000B) {						ndev->hw_features = NETIF_F_RXCSUM | NETIF_F_IP_CSUM;						ndev->features |= ndev->hw_features;	
	} 	

	/* from this point we assume that we have found a DM9000 */ 	
	/* driver system function */	
	/*对ndev进行初始化*/	
	ether_setup(ndev); 	
	ndev->netdev_ops	= &dm9000_netdev_ops;	
	ndev->watchdog_timeo	= msecs_to_jiffies(watchdog);	
	ndev->ethtool_ops	= &dm9000_ethtool_ops; 	

	/*媒介层*/	
	db->msg_enable       = NETIF_MSG_LINK;	db->mii.phy_id_mask  = 0x1f;	db->mii.reg_num_mask = 0x1f;	db->mii.force_media  = 0;	db->mii.full_duplex  = 0;	db->mii.dev	     = ndev;	db->mii.mdio_read    = dm9000_phy_read;	
	db->mii.mdio_write   = dm9000_phy_write; 	
	mac_src = "eeprom"; 	

	/* try reading the node address from the attached EEPROM */	
	for (i = 0; i < 6; i += 2)		
		dm9000_read_eeprom(db, i / 2, ndev->dev_addr+i); 	
		if (!is_valid_ether_addr(ndev->dev_addr) && pdata != NULL) {						mac_src = "platform data";		
			memcpy(ndev->dev_addr, pdata->dev_addr, 6);	
		} 	
		if (!is_valid_ether_addr(ndev->dev_addr)) {		
			/* try reading from mac */				
			mac_src = "chip";		
			for (i = 0; i < 6; i++)			
			ndev->dev_addr[i] = ior(db, i+DM9000_PAR);	
		} 	
		if (!is_valid_ether_addr(ndev->dev_addr)) {		
			dev_warn(db->dev, "%s: Invalid ethernet MAC address. Please "			 			"set using ifconfig\n", ndev->name); 		
			eth_hw_addr_random(ndev);		
			mac_src = "random";
		}		

	//把ndev作为平台设备pdev的私有数据	
	platform_set_drvdata(pdev, ndev);
	/*进行注册*/	
	ret = register_netdev(ndev); 	
	if (ret == 0)		
		printk(KERN_INFO "%s: dm9000%c at %p,%p IRQ %d MAC: %pM (%s)\n",		       	ndev->name, dm9000_type_to_char(db->type, db->io_addr, 						db->io_data, ndev->irq, ndev->dev_addr, mac_src);	
		
	return 0; 
	
out:	
	dev_err(db->dev, "not found (%d).\n", ret); 	
	dm9000_release_board(pdev, db);	
	free_netdev(ndev); 	return ret;}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
NAPI 是 Linux 上采用的一种提高网络处理效率的技术,它的核心概念就是不采用中断的方式读取数据,而代之以首先采用中断唤醒数据接收的服务程序,然后 POLL 的方法来轮询数据,(类似于底半(bottom-half)处理模式);从我们在实验中所得到的数据来看,在随着网络的接收速度的增加,NIC 触发的中断能做到不断减少,目前 NAPI 技术已经在网卡驱动层和网络层得到了广泛的应用,驱动层次上已经有 E1000 系列网卡,RTL8139 系列网卡,3c50X 系列等主流的网络适配器都采用了这个技术,而在网络层次上,NAPI 技术已经完全被应用到了著名的 netif_rx 函数中间,并且提供了专门的 POLL 方法--process_backlog 来处理轮询的方法;根据实验数据表明采用NAPI技术可以大大改善短长度数据包接收的效率,减少中断触发的时间;由于 RTL8139CP 是一种应用比较广泛的网络适配器,所以本文以其为例,说明了NAPI技术在网络适配器上的应用和基本原理。 但是 NAPI 存在一些比较严重的缺陷:而对于上层的应用程序而言,系统不能在每个数据包接收到的时候都可以及时地去处理它,而且随着传输速度增加,累计的数据包将会耗费大量的内存,经过实验表明在 Linux 平台上这个问题会比在 FreeBSD 上要严重一些;另外采用 NAPI 所造成的另外一个问题是对于大的数据包处理比较困难,原因是大的数据包传送到网络层上的时候耗费的时间比短数据包长很多(即使是采用 DMA 方式),所以正如前面所说的那样,NAPI 技术适用于对高速率的短长度数据包的处理,在本文的末尾提出了 NAPI 的改善方法,和实验数据。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值