Linux驱动开发---网络设备驱动

本文详细介绍了Linux内核4.9.x版本下的网络设备驱动原理。涵盖了网络接口层、网络协议接口层、网络设备接口层等内容。重点讲解了sk_buff数据结构及其操作函数、net_device结构体的填充与释放、设备驱动功能层的实现细节等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

网络设备驱动(Linux kernel 4.9.x)

网络接口层

  1. 网络协议接口层:net/core/dev.c
1.1 重要函数
		int dev_queue_xmit(struct sk_buff *skb);	/*发送*/
		int netif_rx(struct sk_buff *skb);			/*接收*/
1.2 重要结构体:套接字缓冲区
		struct sk_buff{
			...
			...
			/* These elements must be at the end, see alloc_skb() for details.  */
			sk_buff_data_t		tail;		/*与data指针相对应*/
			sk_buff_data_t		end;		/*与head指针相对应*/
			unsigned char		*head,		/*指向内存中已分配的用于承载网络数据的缓冲区的起始地址*/
								*data;		/*需随着当前拥有sk_buff的协议层的变化进行相应的移动*/
			...
		}
1.3 套接字缓冲区操作函数
	1.3.1 分配
			static inline struct sk_buff *alloc_skb(unsigned int size, gfp_t priority);
			static inline struct sk_buff *netdev_alloc_skb(struct net_device *dev, unsigned int length);
			NOTE:dev_alloc_skb(unsigned int length)== netdev_alloc_skb(NULL, unsigned int length);
	1.3.2 释放
			void kfree_skb(struct sk_buff *skb);					/* 内核内部使用 */
			void dev_kfree_skb/consume_skb(struct sk_buff *skb);	/* 设备驱动程序使用 */	
			static inline void dev_kfree_skb_irq(struct sk_buff *skb);		/* when caller drops a packet from irq context, replacing kfree_skb(skb) */
			static inline void dev_kfree_skb_any(struct sk_buff *skb);		/* when caller doesn't know its current irq context, replacing kfree_skb(skb) */
			static inline void dev_consume_skb_irq(struct sk_buff *skb);	/* when caller consumes a packet from irq context. Typically used in place of consume_skb(skb) in TX completion path */
			static inline void dev_consume_skb_any(struct sk_buff *skb);	/* when caller doesn't know its current irq context, and consumed a packet. Used in place of consume_skb(skb) */
	1.3.3 移动数据缓冲区指针
			skb_put:  向下移动tail指针。
			skb_push: 向上移动data指针。
			skb_pull: 向下移动data指针。一般用于下层协议向上层协议移交数据包,使data指针指向新的一层协议的协议头。
			skb_reserve: 同时向下移动data和tail指针。主要用于在存储空间的头部预留len长度的空隙。
  1. 网络设备接口层:include/linux/netdevice.h
struct net_device{
		char			name[IFNAMSIZ];		/*网络设备名称*/
		
		unsigned long		mem_end;		/*设备使用共享内存的结束地址*/
		unsigned long		mem_start;		/*设备使用共享内存的起始地址*/
		unsigned long		base_addr;		/*网络设备的I/O基地址*/
		int			irq;					/*设备使用的中断号*/
		...
		unsigned char		if_port;		/*指定多端口设备使用哪一端口*/
		unsigned char		dma;			/*分配给设备的DMA通道*/
		
		unsigned int		mtu;			/*最大传输单元*/
		unsigned short		type;			/*接口的硬件类型*/
		unsigned short		hard_header_len;/*网络设备的硬件头长度*/
		...
		unsigned int		flags;						/*网络接口标志,以IFF_(interface flags)开头*/
		unsigned char		*dev_addr;					/*存放设备的硬件地址*/
		unsigned char		broadcast[MAX_ADDR_LEN];	/*存放设备广播地址*/
		
		const struct net_device_ops *netdev_ops;	/*指向网络设备的操作函数。详见net_device_ops结构体*/
		
		
		unsigned long		last_rx;		/*记录最后一次接收到数据包时的时间戳*/
		...
		...	
}
  1. 设备驱动功能层:驱动工程师要实现的!
	1) 实现网络设备接口层net_device下net_device_ops的一些成员函数。
		如xxx_open、xxx_stop、xxx_start_xmit、xxx_change_mtu等函数。
	2) 实现中断处理函数(负责网络数据包的接收)。
		读取硬件上接收的数据包并传送给上层协议。
	3) 对于特定的设备,还可以定义其相关私有数据和操作,并封装为一个私有信息结构体xxx_private,让其指针被赋值给net_device的priv成员。
	
  1. 网络设备与媒介层:可以是虚拟的。
	1) 直接对应于实际的硬件设备。
	2) 可以定义一组宏和一组访问设备内部寄存器的函数。

补充:
<-------------------------------------------------------------------------- 发送
物理层 数据链路层 网络层 传输层 会话层 表示层 应用层
--------------------------------------------------------------------------> 接收

(详解)设备驱动功能层

  1. net_device结构体的填充/释放
	struct net_device *alloc_netdev(int sizeof_priv, const char *name, void(*setup)(struct net_device*));	/*填充*/
	struct net_device *alloc_etherdev(int sizeof_priv);														/*填充(以太网设备)*/
		NOTE: 第一个参数为设备私有成员的大小。
		NOTE: alloc_etherdev最后是以alloc_netdev_mqs(sizeof_priv, "eth%d", NET_NAME_UNKNOWN, ether_setup, txqs, rxqs);实现的。
	void free_netdev(struct net_device *dev);																/*释放*/
  1. 网络设备驱动的注册/注销
	int register_netdev(struct net_device *dev);		/*注册*/
	void unregister_netdev(struct net_device *dev);		/*注销*/
  1. 网络设备初始化:初始化net_device下的某些成员
例如:
	struct net_device *dev;
	dev->irq = res->start;                                                                                               
	dev->netdev_ops = &xxx_netdev_ops;                                                                           
	dev->watchdog_timeo = 2 * HZ;                              
	dev->base_addr = 0;                                                                                                 
	dev->features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO;
	dev->vlan_features = dev->features;                                                                                
	dev->features |= NETIF_F_RXCSUM;                           
	dev->hw_features = dev->features;                                                                                   
	dev->priv_flags |= IFF_UNICAST_FLT;                        
	dev->gso_max_segs = MV643XX_MAX_TSO_SEGS; 
  1. 网络设备的打开/释放:.ndo_open/.ndo_stop
	打开:
		1)使能设备使用的硬件资源,申请I/O 区域、中断和DMA通道等。
		2)激活设备发送队列(netif_start_queue()等函数)。
	释放:	
		1)释放硬件资源。
		2)停止设备发送队列(netif_stop_queue()等函数)。
  1. 启动数据包发送:.ndo_start_xmit
	1)获取从上层传递过来sk_buff参数的有效数据和长度。	
			/* 对于以太网,如果有效数据的长度小于以太网冲突检测所要求数据帧的最小长度ETH_ZLEN,则给临时缓冲区的末尾填充0 */
	2)设置硬件的寄存器,驱使网络设备进行数据发送操作。
  1. 发送超时:.ndo_tx_timeout
	主要调用函数;netif_wake_queue();
  1. 数据接收:一般在中断处理函数中实现/调用。
		1)读取接收到的数据,分配sk_buffer 数据结构和数据缓冲区;
		2)复制数据到数据缓冲区;
		3)调用netif_rx将sk_buffer传递给上层协议。
	实例:某以太网设备
		static int xxx_rx(struct net_device *dev)
		{
			...
			unsigned char *data;
			struct sk_buff *skb;

			val = pfifo_pop(IND_FIFO_PORT_LO(priv->id));

			...
			len = (val & FIFO_PTR_FRAMELEN_MASK) >> FIFO_PTR_FRAMELEN_SHIFT;	

			skb = netdev_alloc_skb(ndev, len);								/*分配sk_buff*/
			...

			data = skb_put(skb, len);										/*数据缓冲区*/

			memcpy_fromio(data, priv->sram_base + frameno * 1560, len);		/*复制数据*/

			pfifo_push(EMPTY_PTR_FIFO(priv->id),
				FIFO_PTR_SEGMENT(seg) | FIFO_PTR_FRAMENO(frameno));

			skb->protocol = eth_type_trans(skb, ndev);						/*获取上层协议类型*/
			netif_rx(skb);													/*把数据交给上层*/
			ndev->stats.rx_packets++;
			ndev->stats.rx_bytes += len;
		}
		static irqreturn_t xxx_interrupt(int irq, void *dev_id)
		{
			...
			xxx_rx(dev);
			...
		}
  1. 网络连接状态
	硬件知识;
		1)网络适配器硬件电路可以检测出链路上是否有载波;
		2)载波反映了网络的连接是否正常。
	相关函数:
		void netif_carrier_on(struct net_device *dev);
		void netif_carrier_off(struct net_device *dev);
		static inline bool netif_carrier_ok(const struct net_device *dev);
	用法:多用于检查link状态。
  1. 统计数据:struct net_device_stats stats; ------net_device结构体下的成员
	NOTE: 
		1)该信息结构体适宜包含在设备的私有信息结构体中。
		2)统计信息的修改在数据的发送和接收中完成。具体函数包含中断处理函数、数据发送、发送超时和数据接收等函数。

实例分析:仅设备驱动功能层。

cs89x0网卡设备驱动(drivers/net/ethernet/cirrus/cs89x0.c)

 主要工作:
			1)填充CS8900的私有信息结构体; 						--- struct net_local *lp;
			2)填充设备初始化模板; 		   						--- 初始化net_device结构体,将其注册入内核。
			3)填充设备打开与释放函数代码(net_open/net_close);
			3)填充设备发送数据包函数(net_send_packet)模板;
			4)填充设备驱动程序的中断处理程序及具体的数据包接收函数;
代码分析(省略返回值判断部分)/*私有信息结构体net_local*/
		struct net_local {
			int chip_type;		/* one of: CS8900, CS8920, CS8920M */
			char chip_revision;	/* revision letter of the chip ('A'...) */ /*revision不同,记录的send_cmd将不同*/
			int send_cmd;		/* the proper send command: TX_NOW, TX_AFTER_381, or TX_AFTER_ALL */
			int auto_neg_cnf;	/* auto-negotiation word from EEPROM */
			int adapter_cnf;	/* adapter configuration from EEPROM */
			int isa_config;		/* ISA configuration from EEPROM */
			int irq_map;		/* IRQ map from EEPROM */
			int rx_mode;		/* what mode are we in? 0, RX_MULTCAST_ACCEPT, or RX_ALL_ACCEPT */
			int curr_rx_cfg;	/* a copy of PP_RxCFG */
			int linectl;		/* either 0 or LOW_RX_SQUELCH, depending on configuration. */
			int send_underrun;	/* keep track of how many underruns in a row we get */
			int force;		/* force various values; see FORCE* above. */
			spinlock_t lock;
			void __iomem *virt_addr;/* CS89x0 virtual address. */
		#if ALLOW_DMA
			int use_dma;		/* Flag: we're using dma */
			int dma;		/* DMA channel */
			int dmasize;		/* 16 or 64 */
			unsigned char *dma_buff;	/* points to the beginning of the buffer */
			unsigned char *end_dma_buff;	/* points to the end of the buffer */
			unsigned char *rx_dma_ptr;	/* points to the next packet  */
		#endif
		};
			
	/*数据包接收函数*/
		static void net_rx(struct net_device *dev)
		{
			struct net_local *lp = netdev_priv(dev);
			struct sk_buff *skb;
			int status, length;
	
			status = ioread16(lp->virt_addr + RX_FRAME_PORT);
			length = ioread16(lp->virt_addr + RX_FRAME_PORT);	//读取数据包接收长度
			
			...
	
			/*分配sk_buff缓冲区*/
			skb = netdev_alloc_skb(dev, length + 2);
			...
			skb_reserve(skb, 2);	/* longword align L3 header */
			
			/*将硬件中的数据包放入数据缓冲区*/
			readwords(lp, RX_FRAME_PORT, skb_put(skb, length), length >> 1);
			if (length & 1)
				skb->data[length-1] = ioread16(lp->virt_addr + RX_FRAME_PORT);
			
			...
			/*获取上层协议类型*/*/
			skb->protocol = eth_type_trans(skb, dev);
			/*将数据交给上层*/
			netif_rx(skb);
			/*更改统计信息*/
			dev->stats.rx_packets++;
			dev->stats.rx_bytes += length;
		}
			
	/*中断处理程序*/
		static irqreturn_t net_interrupt(int irq, void *dev_id)
		{
			struct net_device *dev = dev_id;
			...
			...
			while ((status = ioread16(lp->virt_addr + ISQ_PORT))) {
				...	
				switch (status & ISQ_EVENT_MASK) {
				case ISQ_RECEIVER_EVENT:
					/* Got a packet(s). */
					net_rx(dev);
					break;
				case ISQ_TRANSMITTER_EVENT:
					dev->stats.tx_packets++;
					netif_wake_queue(dev);	/* Inform upper layers. */
					...
					...
					break;
				/*其他中断事件*/
				...
				...
				}
			}
			return IRQ_RETVAL(handled);
		}
			
	/*发送超时函数*/
		static void net_timeout(struct net_device *dev)
		{
			...
			netif_wake_queue(dev);					//尝试重启适配器
		}	
	/*发送数据包函数*/
		static netdev_tx_t net_send_packet(struct sk_buff *skb, struct net_device *dev)
		{
			struct net_local *lp = netdev_priv(dev);
			unsigned long flags;
			
			...
			...
			
			spin_lock_irqsave(&lp->lock, flags);	//使用自旋锁防止多个数据包同时写入硬件
			netif_stop_queue(dev);
			
			/*启动一个传输序列*/
			iowrite16(lp->send_cmd, lp->virt_addr + TX_CMD_PORT);
			iowrite16(skb->len, lp->virt_addr + TX_LEN_PORT);
			
			/*检测芯片是否为数据包分配了内存(检测硬件是否处于发送READY状态)*/
			if ((readreg(dev, PP_BusST) & READY_FOR_TX_NOW) == 0) {
				
				spin_unlock_irqrestore(&lp->lock, flags);
				cs89_dbg(0, err, "Tx buffer not free!\n");
				return NETDEV_TX_BUSY;
			}
			/*数据包内容写入内存 */
			writewords(lp, TX_FRAME_PORT, skb->data, (skb->len + 1) >> 1);
			spin_unlock_irqrestore(&lp->lock, flags);
			dev->stats.tx_bytes += skb->len;
			dev_consume_skb_any(skb);				//释放sk_buff
			
			/*在这里既不调用netif_wake_queue(),也未调用netif_start_queue().
			 *原因是:这两种方法都会导致该包完全输出之前,通过net_send_packet()运行另一个下半部分。
			 *故只需返回等待tx完成中断处理并重启netdevice层
			 */
			
			return NETDEV_TX_OK;
		}
			
/*设备打开函数*/
	static int net_open(struct net_device *dev)
	{
		struct net_local *lp = netdev_priv(dev);
		...
	
		if (dev->irq < 2) {
			...
			writereg(dev, PP_BusCTL, ENABLE_IRQ | MEMORY_ON);
	
			for (i = 2; i < CS8920_NO_INTS; i++) {
				if ((1 << i) & lp->irq_map) {
					if (request_irq(i, net_interrupt, 0, dev->name,
							dev) == 0) {
						dev->irq = i;
						write_irq(dev, lp->chip_type, i);
						break;
					}
				}
			}
			...
		} else {
	#if !defined(CONFIG_CS89x0_PLATFORM)
			...
	#endif
			...
			write_irq(dev, lp->chip_type, dev->irq);
			ret = request_irq(dev->irq, net_interrupt, 0, dev->name, dev);
			...
		}
	
		...
	
		/*设置以太网地址*/
		for (i = 0; i < ETH_ALEN / 2; i++)
			writereg(dev, PP_IA + i * 2, (dev->dev_addr[i * 2] | (dev->dev_addr[i * 2 + 1] << 8)));
	
		/*在测试接口时,禁用中断*/
		writereg(dev, PP_BusCTL, MEMORY_ON);
	
		/* Set the LineCTL quintuplet based on adapter configuration read from EEPROM */
		if ((lp->adapter_cnf & A_CNF_EXTND_10B_2) &&
			(lp->adapter_cnf & A_CNF_LOW_RX_SQUELCH))
			lp->linectl = LOW_RX_SQUELCH;
		else
			lp->linectl = 0;
	
		/*检查以确保有可用的“正确”硬件*/
		switch (lp->adapter_cnf & A_CNF_MEDIA_TYPE) {
		case A_CNF_MEDIA_10B_T:
			result = lp->adapter_cnf & A_CNF_10B_T;
			break;
		...
		...//其他适配器配置选项
		}
		...
		...
		/*将硬件设置为配置选项*/
		switch (lp->adapter_cnf & A_CNF_MEDIA_TYPE) {
		case A_CNF_MEDIA_10B_T:
			result = detect_tp(dev);
			if (result == DETECTED_NONE) {
				pr_warn("%s: 10Base-T (RJ-45) has no cable\n",
					dev->name);
				if (lp->auto_neg_cnf & IMM_BIT) /* check "ignore missing media" bit */
					result = DETECTED_RJ45H; /* Yes! I don't care if I see a link pulse */
			}
			break;
		...
		...//其他适配器配置选项
		}
		switch (result) {
		case DETECTED_NONE:
			pr_err("%s: no network cable attached to configured media\n",
				   dev->name);
			goto release_dma;
		...
		...//其他检测结果
		}
	
		/*同时打开接收和发送操作*/
		writereg(dev, PP_LineCTL,
			 readreg(dev, PP_LineCTL) | SERIAL_RX_ON | SERIAL_TX_ON);
	
		/* Receive only error free packets addressed to this card */
		lp->rx_mode = 0;
		writereg(dev, PP_RxCTL, DEF_RX_ACCEPT);
	
		lp->curr_rx_cfg = RX_OK_ENBL | RX_CRC_ERROR_ENBL;
	
		if (lp->isa_config & STREAM_TRANSFER)
			lp->curr_rx_cfg |= RX_STREAM_ENBL;
	#if ALLOW_DMA
		set_dma_cfg(dev);
	#endif
		writereg(dev, PP_RxCFG, lp->curr_rx_cfg);
	
		writereg(dev, PP_TxCFG, (TX_LOST_CRS_ENBL |
					 TX_SQE_ERROR_ENBL |
					 TX_OK_ENBL |
					 TX_LATE_COL_ENBL |
					 TX_JBR_ENBL |
					 TX_ANY_COL_ENBL |
					 TX_16_COL_ENBL));
	
		writereg(dev, PP_BufCFG, (READY_FOR_TX_ENBL |
					  RX_MISS_COUNT_OVRFLOW_ENBL |
	#if ALLOW_DMA
					  dma_bufcfg(dev) |
	#endif
					  TX_COL_COUNT_OVRFLOW_ENBL |
					  TX_UNDERRUN_ENBL));
	
		/* now that we've got our act together, enable everything */
		writereg(dev, PP_BusCTL, (ENABLE_IRQ
					  | (dev->mem_start ? MEMORY_ON : 0) /* turn memory on */
	#if ALLOW_DMA
					  | dma_busctl(dev)
	#endif
				 ));
		
		/*激活设备发送队列*/
		netif_start_queue(dev);
	
		return 0;
	...
	}
			
/*设备释放函数*/
	static int net_close(struct net_device *dev)
	{
		#if ALLOW_DMA
			struct net_local *lp = netdev_priv(dev);
		#endif
		
		/*停止设备发送队列*/
		netif_stop_queue(dev);
	
		/*硬件寄存器清0*/
		writereg(dev, PP_RxCFG, 0);
		writereg(dev, PP_TxCFG, 0);
		writereg(dev, PP_BufCFG, 0);
		writereg(dev, PP_BusCTL, 0);
	
		/*释放中断*/
		free_irq(dev->irq, dev);
	
		#if ALLOW_DMA
		...
		#endif
	
		return 0;
	}

	/*设备操作结构体*/
	static const struct net_device_ops net_ops = {
		.ndo_open		= net_open,
		.ndo_stop		= net_close,
		.ndo_tx_timeout		= net_timeout,
		.ndo_start_xmit		= net_send_packet,
		.ndo_get_stats		= net_get_stats,
		.ndo_set_rx_mode	= set_multicast_list,
		.ndo_set_mac_address	= set_mac_address,
	#ifdef CONFIG_NET_POLL_CONTROLLER
		.ndo_poll_controller	= net_poll_controller,
	#endif
		.ndo_change_mtu		= eth_change_mtu,
		.ndo_validate_addr	= eth_validate_addr,
	};
			
/* 真正的probe函数*/
	static int __init cs89x0_probe1(struct net_device *dev, void __iomem *ioaddr, int modular)
	{
		struct net_local *lp = netdev_priv(dev);
		int i;
		int tmp;
		unsigned rev_type = 0;
		int eeprom_buff[CHKSUM_LEN];
		int retval;

		/* 初始化设备私有结构体 */
		...
		iowrite16(PP_ChipID, ioaddr + ADD_PORT);

		tmp = ioread16(ioaddr + DATA_PORT);
		...
		lp->virt_addr = ioaddr;

		/* 获取芯片类型 */
		rev_type = readreg(dev, PRODUCT_ID_ADD);
		lp->chip_type = rev_type & ~REVISON_BITS;
		lp->chip_revision = ((rev_type & REVISON_BITS) >> 8) + 'A';

		/* 检查芯片类型和版本的目的:设置正确的发送命令*/
		lp->send_cmd = TX_AFTER_381;
		if (lp->chip_type == CS8900 && lp->chip_revision >= 'F')
			lp->send_cmd = TX_NOW;
		if (lp->chip_type != CS8900 && lp->chip_revision >= 'C')
			lp->send_cmd = TX_NOW;
		...

		reset_chip(dev);

		/* 读取芯片当前配置
			...
		 */

		if ((readreg(dev, PP_SelfST) & (EEPROM_OK | EEPROM_PRESENT)) ==
			(EEPROM_OK | EEPROM_PRESENT)) {
			/* Load the MAC. */
			for (i = 0; i < ETH_ALEN / 2; i++) {
				unsigned int Addr;
				Addr = readreg(dev, PP_IA + i * 2);
				dev->dev_addr[i * 2] = Addr & 0xFF;
				dev->dev_addr[i * 2 + 1] = Addr >> 8;
			}

			/* 加载适配器配置.
			 * Note:  Barring any more specific information from some
			 * other source (ie EEPROM+Schematics), we would not know
			 * how to operate a 10Base2 interface on the AUI port.
			 * However, since we  do read the status of HCB1 and use
			 * settings that always result in calls to control_dc_dc(dev,0)
			 * a BNC interface should work if the enable pin
			 * (dc/dc converter) is on HCB1.
			 * It will be called AUI however.
			 */

			lp->adapter_cnf = 0;
			i = readreg(dev, PP_LineCTL);
			/* Preserve the setting of the HCB1 pin. */
			if ((i & (HCB1 | HCB1_ENBL)) == (HCB1 | HCB1_ENBL))
				lp->adapter_cnf |= A_CNF_DC_DC_POLARITY;
			/* Save the sqelch bit */
			if ((i & LOW_RX_SQUELCH) == LOW_RX_SQUELCH)
				lp->adapter_cnf |= A_CNF_EXTND_10B_2 | A_CNF_LOW_RX_SQUELCH;
			/* Check if the card is in 10Base-t only mode */
			if ((i & (AUI_ONLY | AUTO_AUI_10BASET)) == 0)
				lp->adapter_cnf |=  A_CNF_10B_T | A_CNF_MEDIA_10B_T;
			/* Check if the card is in AUI only mode */
			if ((i & (AUI_ONLY | AUTO_AUI_10BASET)) == AUI_ONLY)
				lp->adapter_cnf |=  A_CNF_AUI | A_CNF_MEDIA_AUI;
			/* Check if the card is in Auto mode. */
			if ((i & (AUI_ONLY | AUTO_AUI_10BASET)) == AUTO_AUI_10BASET)
				lp->adapter_cnf |=  A_CNF_AUI | A_CNF_10B_T |
					A_CNF_MEDIA_AUI | A_CNF_MEDIA_10B_T | A_CNF_MEDIA_AUTO;
			...
			/* IRQ. Other chips already probe, see below. */
			if (lp->chip_type == CS8900)
				lp->isa_config = readreg(dev, PP_CS8900_ISAINT) & INT_NO_MASK;

		}


		/* 首先检查是否有EEPROM. */
		if ((readreg(dev, PP_SelfST) & EEPROM_PRESENT) == 0)
			pr_warn("No EEPROM, relying on command line....\n");
		else if (get_eeprom_data(dev, START_EEPROM_DATA, CHKSUM_LEN, eeprom_buff) < 0) {
			pr_warn("EEPROM read failed, relying on command line\n");
		} else if (get_eeprom_cksum(START_EEPROM_DATA, CHKSUM_LEN, eeprom_buff) < 0) {
			/* Check if the chip was able to read its own configuration starting
			   at 0 in the EEPROM*/
			if ((readreg(dev, PP_SelfST) & (EEPROM_OK | EEPROM_PRESENT)) !=
				(EEPROM_OK | EEPROM_PRESENT))
				pr_warn("Extended EEPROM checksum bad and no Cirrus EEPROM, relying on command line\n");

		} else {
			/* This reads an extended EEPROM that is not documented
			 * in the CS8900 datasheet.
			 */

			/* get transmission control word  but keep the autonegotiation bits */
			if (!lp->auto_neg_cnf)
				lp->auto_neg_cnf = eeprom_buff[AUTO_NEG_CNF_OFFSET / 2];
			/* Store adapter configuration */
			if (!lp->adapter_cnf)
				lp->adapter_cnf = eeprom_buff[ADAPTER_CNF_OFFSET / 2];
			/* Store ISA configuration */
			lp->isa_config = eeprom_buff[ISA_CNF_OFFSET / 2];
			dev->mem_start = eeprom_buff[PACKET_PAGE_OFFSET / 2] << 8;

			/* eeprom_buff has 32-bit ints, so we can't just memcpy it */
			/* store the initial memory base address */
			for (i = 0; i < ETH_ALEN / 2; i++) {
				dev->dev_addr[i * 2] = eeprom_buff[i];
				dev->dev_addr[i * 2 + 1] = eeprom_buff[i] >> 8;
			}
			...
		}

		/* 允许使用多个收发器.  If they force multiple, autosense */
		{
			int count = 0;
			if (lp->force & FORCE_RJ45) {
				lp->adapter_cnf |= A_CNF_10B_T;
				count++;
			}
			if (lp->force & FORCE_AUI) {
				lp->adapter_cnf |= A_CNF_AUI;
				count++;
			}
			if (lp->force & FORCE_BNC) {
				lp->adapter_cnf |= A_CNF_10B_2;
				count++;
			}
			if (count > 1)
				lp->adapter_cnf |= A_CNF_MEDIA_AUTO;
			else if (lp->force & FORCE_RJ45)
				lp->adapter_cnf |= A_CNF_MEDIA_10B_T;
			else if (lp->force & FORCE_AUI)
				lp->adapter_cnf |= A_CNF_MEDIA_AUI;
			else if (lp->force & FORCE_BNC)
				lp->adapter_cnf |= A_CNF_MEDIA_10B_2;
		}
		...
		...

		lp->irq_map = 0xffff;

		/* If this is a CS8900 then no pnp soft */
		if (lp->chip_type != CS8900 &&
			/* 检查ISA IRQ是否已被设置 */
			(i = readreg(dev, PP_CS8920_ISAINT) & 0xff,
			 (i != 0 && i < CS8920_NO_INTS))) {
			if (!dev->irq)
				dev->irq = i;
		} else {
			i = lp->isa_config & INT_NO_MASK;
	#ifndef CONFIG_CS89x0_PLATFORM
			if (lp->chip_type == CS8900) {
				/* Translate the IRQ using the IRQ mapping table. */
				if (i >= ARRAY_SIZE(cs8900_irq_map))
					pr_err("invalid ISA interrupt number %d\n", i);
				else
					i = cs8900_irq_map[i];

				lp->irq_map = CS8900_IRQ_MAP; /* fixed IRQ map for CS8900 */
			} else {
				int irq_map_buff[IRQ_MAP_LEN/2];

				if (get_eeprom_data(dev, IRQ_MAP_EEPROM_DATA,
							IRQ_MAP_LEN / 2,
							irq_map_buff) >= 0) {
					if ((irq_map_buff[0] & 0xff) == PNP_IRQ_FRMT)
						lp->irq_map = ((irq_map_buff[0] >> 8) |
								   (irq_map_buff[1] << 8));
				}
			}
	#endif
			if (!dev->irq)
				dev->irq = i;
		}
		...
	#if ALLOW_DMA
		if (lp->use_dma) {
			/*若支持DMA, 获取dma通道*/
			get_dma_channel(dev);
			...
		} else
	#endif
		...

		/*打印以太网地址*/
		pr_cont(", MAC %pM\n", dev->dev_addr);

		dev->netdev_ops	= &net_ops;							/*设置设备操作结构体*/
		dev->watchdog_timeo = HZ;

		cs89_dbg(0, info, "cs89x0_probe1() successful\n");

		retval = register_netdev(dev);						/*注册该网络设备*//*函数内部自动调用init函数,初始化设备驱动程序*/
		...
		return 0;
		...
	}
			
/*使用平台设备*/
	#ifdef CONFIG_CS89x0_PLATFORM	
		static int __init cs89x0_platform_probe(struct platform_device *pdev)
		{
			struct net_device *dev = alloc_etherdev(sizeof(struct net_local));		/*分配以太网设备*/
			struct net_local *lp;
			struct resource *mem_res;
			void __iomem *virt_addr;
			int err;
			...
			lp = netdev_priv(dev);										/*私有信息结构体赋值*/
	
			dev->irq = platform_get_irq(pdev, 0);						/*获取设备中断号*/
			...
			mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);	/*获取设备I/O内存*/
			virt_addr = devm_ioremap_resource(&pdev->dev, mem_res);		/*映射为虚拟地址*/
			...
			
			err = cs89x0_probe1(dev, virt_addr, 0);						/*调用真正的probe函数*/	
			...
	
			platform_set_drvdata(pdev, dev);
			return 0;
			...
		}
		static int cs89x0_platform_remove(struct platform_device *pdev)
		{
			struct net_device *dev = platform_get_drvdata(pdev);
	
			unregister_netdev(dev);
			free_netdev(dev);
			return 0;
		}
	
		static const struct __maybe_unused of_device_id cs89x0_match[] = {
			{ .compatible = "cirrus,cs8900", },
			{ .compatible = "cirrus,cs8920", },
			{ },
		};
		MODULE_DEVICE_TABLE(of, cs89x0_match);
	
		static struct platform_driver cs89x0_driver = {
			.driver	= {
				.name		= DRV_NAME,
				.of_match_table	= of_match_ptr(cs89x0_match),
			},
			.remove	= cs89x0_platform_remove,
		};
	
		module_platform_driver_probe(cs89x0_driver, cs89x0_platform_probe);
	#endif
		
	NOTE: 驱动文件中还提供初始化非平台设备的各函数(较老版kernel)。
		  具体包含init_module、cs89x0_probe、cs89x0_ioport_probe、cleanup_module等函数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值