Linux网络设备驱动架构

Linux网络设备驱动程序的体系结构如下图所示:


从上到下可以划分为4层,依次为:

--- 网络协议接口层:  向网络层提供统一的数据包收发接口。

--- 设备层: 向协议接口层提供统一的用于描述具体网络设备的属性和操作的结构体 net_device.

--- 驱动层: 驱动层的各函数是net_device 数据结构的具体成员,使设备硬件完成相应动作的程序。

--- 物理层, 也可以叫媒介层: 是完成数据包发送和接收的物理实体。

在设计具体的网络设备驱动程序时,我们的主要工作是编写驱动层相关的函数,填充net_device数据结构,并将 net_device 注册进内核。

1. 协议接口层

1.1 dev_queue_xmit()

当上层协议,如ARP或IP协议,需要发送数据包时,将调用dev_queue_xmit()函数发送该数据包,

同时传递给该函数一个指向 struct sk_buff 数据结构的指针,该结构就代表了一个要传送的数据包

dev_queue_xmit()定义如下,注意这是向网络发送一个数据包。

/**
 *	dev_queue_xmit - transmit a buffer
 *	@skb: buffer to transmit
 *
 *	Queue a buffer for transmission to a network device. The caller must
 *	have set the device and priority and built the buffer before calling
 *	this function. The function can be called from an interrupt.
 *
 *	A negative errno code is returned on a failure. A success does not
 *	guarantee the frame will be transmitted as it may be dropped due
 *	to congestion or traffic shaping.
 *
 * -----------------------------------------------------------------------------------
 *      I notice this method can also return errors from the queue disciplines,
 *      including NET_XMIT_DROP, which is a positive value.  So, errors can also
 *      be positive.
 *
 *      Regardless of the return value, the skb is consumed, so it is currently
 *      difficult to retry a send to this method.  (You can bump the ref count
 *      before sending to hold a reference for retry if you are careful.)
 *
 *      When calling this method, interrupts MUST be enabled.  This is because
 *      the BH enable code must have IRQs enabled so that it will not deadlock.
 *          --BLG
 */
int dev_queue_xmit(struct sk_buff *skb)
{
	struct net_device *dev = skb->dev;
	struct netdev_queue *txq;
	struct Qdisc *q;
	int rc = -ENOMEM;

	/* Disable soft irqs for various locks below. Also
	 * stops preemption for RCU.
	 */
	rcu_read_lock_bh();

	txq = dev_pick_tx(dev, skb);
	q = rcu_dereference_bh(txq->qdisc);

#ifdef CONFIG_NET_CLS_ACT
	skb->tc_verd = SET_TC_AT(skb->tc_verd, AT_EGRESS);
#endif
	trace_net_dev_queue(skb);
	if (q->enqueue) {
		rc = __dev_xmit_skb(skb, q, dev, txq);
		goto out;
	}

	/* The device has no queue. Common case for software devices:
	   loopback, all the sorts of tunnels...

	   Really, it is unlikely that netif_tx_lock protection is necessary
	   here.  (f.e. loopback and IP tunnels are clean ignoring statistics
	   counters.)
	   However, it is possible, that they rely on protection
	   made by us here.

	   Check this and shot the lock. It is not prone from deadlocks.
	   Either shot noqueue qdisc, it is even simpler 8)
	 */
	if (dev->flags & IFF_UP) {
		int cpu = smp_processor_id(); /* ok because BHs are off */

		if (txq->xmit_lock_owner != cpu) {

			if (__this_cpu_read(xmit_recursion) > RECURSION_LIMIT)
				goto recursion_alert;

			HARD_TX_LOCK(dev, txq, cpu);

			if (!netif_tx_queue_stopped(txq)) {
				__this_cpu_inc(xmit_recursion);
				rc = dev_hard_start_xmit(skb, dev, txq);
				__this_cpu_dec(xmit_recursion);
				if (dev_xmit_complete(rc)) {
					HARD_TX_UNLOCK(dev, txq);
					goto out;
				}
			}
			HARD_TX_UNLOCK(dev, txq);
			if (net_ratelimit())
				printk(KERN_CRIT "Virtual device %s asks to "
				       "queue packet!\n", dev->name);
		} else {
			/* Recursion is detected! It is possible,
			 * unfortunately
			 */
recursion_alert:
			if (net_ratelimit())
				printk(KERN_CRIT "Dead loop on virtual device "
				       "%s, fix it urgently!\n", dev->name);
		}
	}

	rc = -ENETDOWN;
	rcu_read_unlock_bh();

	kfree_skb(skb);
	return rc;
out:
	rcu_read_unlock_bh();
	return rc;
}
EXPORT_SYMBOL(dev_queue_xmit);
1.2 netif_rx()

同样,对数据包的接收,是通过向 netif_rx() 函数 传递一个 struct sk_buff 数据结构指针来实现:

netif_rx()函数定义如下,注意这是从网络接收一个数据包到内存。

/**
 *	netif_rx	-	post buffer to the network code
 *	@skb: buffer to post
 *
 *	This function receives a packet from a device driver and queues it for
 *	the upper (protocol) levels to process.  It always succeeds. The buffer
 *	may be dropped during processing for congestion control or by the
 *	protocol layers.
 *
 *	return values:
 *	NET_RX_SUCCESS	(no congestion)
 *	NET_RX_DROP     (packet was dropped)
 *
 */

int netif_rx(struct sk_buff *skb)
{
	int ret;

	/* if netpoll wants it, pretend we never saw it */
	if (netpoll_rx(skb))
		return NET_RX_DROP;

	if (netdev_tstamp_prequeue)
		net_timestamp_check(skb);

	trace_netif_rx(skb);
#ifdef CONFIG_RPS
	{
		struct rps_dev_flow voidflow, *rflow = &voidflow;
		int cpu;

		preempt_disable();
		rcu_read_lock();

		cpu = get_rps_cpu(skb->dev, skb, &rflow);
		if (cpu < 0)
			cpu = smp_processor_id();

		ret = enqueue_to_backlog(skb, cpu, &rflow->last_qtail);

		rcu_read_unlock();
		preempt_enable();
	}
#else
	{
		unsigned int qtail;
		ret = enqueue_to_backlog(skb, get_cpu(), &qtail);
		put_cpu();
	}
#endif
	return ret;
}
EXPORT_SYMBOL(netif_rx);

1.3 sk_buff

这里sk_buff非常重要,会在另一篇sk_buff中详细介绍。

2. 设备层

设备层的主要功能是为各种网络设备定义一个统一的数据结构 net_device,实现多种硬件在软件层次上的统一。

net_device结构体在内核中代表一个网络设备。

具体在另一篇net_device中详细介绍。

3. 驱动层:

net_device中的成员(属性和函数指针),需要被驱动层的具体数值和函数赋值。

对于具体的网络设备ethx, 软件工程师需要编写设备驱动层的函数,这些函数如下:

ethx_open();

ethx_stop();

ethx_tx();

ethx_hard_header();

ethx_get_stats();

ethx_tx_timeout();

ethx_poll();

等。

网络数据包的接收可以由中断引发,所以驱动层的另一个主要部分是中断处理函数。

它负责读取硬件设备上的数据包并传给上层协议。

中断处理函数一般如下:

ethx_interrupt();  --- 完成中断类型判断等基本工作

ethx_rx();   --- 完成数据包的生成和传递给上层等复杂工作。

对于特定的设备,我们还可以定义其相关的私有数据(private_data) 和操作,并封装一个私有信息结构体 struct ethx_private,让其指针被赋值给 net_device 的 priv 成员。

struct ethx_private 结构体中可以包含设备特殊的属性,自旋锁/信号量,定时器,及统计信息等,由工程师自己定义。

4. 物理媒介层

媒介层直接对应实际的硬件设备,为了对设备的物理配置和寄存器操作进行普通操作,我们可以定义一组宏或一组访问函数,对内部寄存器进行访问。

具体的宏和函数,对特定的硬件紧密相关。

下面是设计范例:

//寄存器定义
#define DATA_REG 0x0004
#define CMD_REG 0x0008

//寄存器读写函数
static u16 xxx_readword(u32 base_addr, int portno)
{
...  //读取寄存器的值并返回
}

static void xxx_writeword(u32 base_addr, int portno, u16 value)
{
...   //想寄存器写入数值
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值