Linux网卡驱动设计
Linux网络体系结构
Linux的优点之一在于它丰富而稳定的网络协议栈。其范围从协议无关层到各种具体的网络协议实现。
协议层次对比图
网络接口层提供访问物理设备的驱动程序,对应的网络协议主要是以太网协议。
网络层协议管理离散计算机间的数据传输,如IP协议为用户和远程计算机提供了信息包的传输方法。确保了信息包能正确的到达目的机器。
传输层的功能包括:格式化信息流、提供可靠传输。
应用层位于协议栈的顶端,它的主要任务是服务于应用。
Linux网络子系统
其顶端时系统调用接口层。位于其下面的是一个协议无关层,它提供了一个通用方法来使用传输层协议。然后是具体协议的实现,然后是设备无关层,它提供了协议与设备驱动通信的通用接口,最下面是设备驱动程序。
系统调用接口:Socket系统调用。
协议无关接口:实现一组通用函数来访问各种不同的协议:通过socket实现。这个结构包含了特定socket所需要的所有状态信息,还概括了socket所使用的特定协议和socket上可以执行的一些操作。
设备无关接口将协议与各种网络设备驱动连接在一起。这一层提供一组通用函数供底层网络设备驱动程序使用,让它们可以对高层协议栈进行操作。首先,设备驱动程序可能会通过调用用register_netdevice或unregister_netdevice在内核中进行注册或注销。调用者首先填写首先填写net_device结构结构,然后传递这个结构进行注册。内核调用它的的init函数(如果定义了这种函数),然后执行一组健全性检查,并将新设备添加到设备列表中(内核中的活动设备链表)。
要从协议层向设备发送数据,需要使用dev_queue_xmit函函数,这个函数对数据进行排队,并交由底层设备驱动程序进行最终传输报文的接收通常是使用使用netif_rx执行执行的。当底层设备驱动程序接收到一个报文(包含在所分配的的sk_buff中)时,就会通过调用用netif_rx将将数据上传至设备无关层,然后,这个函数通过过netif_rx_schedule将sk_buff在上在上层协议队列中进行排队,供以后进行处理。
驱动设计
设备描述
每个网络接口都由一个net_device结构来描述,该结构可使用如下内核函数动态分配:
1、structnet_device *alloc_netdev(int sizeof_priv, const char*mask, void (*setup)(structnet_device *))
sizeof_priv 私有数据区大小;mask:设备名;setup 初始化函数
2、structnet_device *alloc_etherdev(int sizeof_priv)
alloc_etherdev是alloc_netdev针对以太网的快捷函数
structnet_device{
charname[IFNAMSIZ];
unsigned longstate;
unsigned longbase_addr;
unsigned int irq;
int(*init)(struct net_device *dev);//该函数在register_netdev时被调用来完成对net_device结构的初始化
}
基本方法
int(*open)(struct net_device *dev);打开接口。ifconfig激活时,接口将被打开。
int(*stop)(struct net_device *dev);停止接口。网卡关闭时。
int (*hard_start_xmit)(struct sk_buff *skb, struct net_device *dev) ;数据发送函数
设备注册
intregister_netdev(struct net_device *dev)
sk_buff
Linux内核中的每个网络数据包都由一个套接字缓冲区结构结构structsk_buff描述,即一个sk_buff结构结构就是一个包,指向sk_buff的的指针通常被称做skb。
该结构包含如下重要成员:
structdevice*dev;//处处理该包的设备
__u32saddr;//IP源源地址
__u32daddr;//IP目目的地址
__u32raddr;//IP路路由器地址
unsignedchar*head;//分分配空间的开始
unsignedchar*data;//有有效数据的开始
unsignedchar*tail;//有有效数据的结束
unsignedchar*end;//分分配空间的结束
unsignedlonglen;;//有有效数据的
skb操作函数
struct sk_buff *alloc_skb(unsigned int len, int priority)
分配一个sk_buff结构,供协议栈代码使用
struct sk_buff *dev_alloc_skb(unsigned int len)
分配一个sk_buff结构,供驱动代码使用
unsigned char *skb_push(struct sk_buff *skb, int len)
向后移动skb的tail指针,并返回tail移动之前的值。
unsigned char *skb_put(structsk_buff *skb, int len)
向前移动skb的head指针,并返回head移动之后的值。
kfree_skb(struct sk_buff *skb)
释放一个sk_buff结构,供协议栈代码使用。
dev_kfree_skb(struct sk_buff *skb)
释放一个sk_buff结构,供驱动代码使用
设备打开
1、 注册中断.MDA等
2、 设置寄存器,启动设备
3、 启动发送队列
代码示例
intnet_open(struct net_device *dev)
{
/*申请中断*/
request_irq(dev->irq,&net_interrupt, SA_SHIRQ,“dm9000”, dev);;
/*设置寄存器,启动设备 */
...... ............ ......
/*启动发送队列*/
netif_start_queue(dev); ;
}
数据接收
当核心需要发送一个数据包时,他调用hard_start_transmit,该函数最终将用到net_device结构中的hard_start_xmit函数指针。
网络接口驱动可以实现两种方式的报文接收:
中断和查询,Linux中驱动多采用中断方式。
接收流程
1、 分配skb
skb =dev_alloc_skb(pkt->datalen + 2)
2、 从硬件中读取数据到skb
3、 调用netif_rx将数据交到协议栈netif_rx(skb)