Linux驱动修炼之道-DM9000A网卡驱动框架源码分析

2014-11-24 09:17:30 · 作者: · 浏览: 1

首先分析一下Linux网络设备的结构,如下图:



网络协议接口层:


这里主要进行数据包的收发,使用函数原型为:



这里使用了一个skb_buff结构体,定义于include/linux/skbuff.h中,它的含义为“套接字缓冲区”,用于在Linux网络子系统各层间传输数据。他是一个双向链表,在老的内核中会有一个list域指向sk_buff_head也就是链表头,但是在我研究的linux2.6.30.4内核中已经不存在了,如下图:



操作套接字缓冲区的函数:


1.分配



分配一个缓冲区。alloc_skb函数分配一个缓冲区并初始化skb->data和skb->tail为skb->head。参数len为数据缓冲区的空间大小,通常以L1_CACHE_BYTES字节(对ARM为32)对齐,参数priority为内存分配的优先级。dev_alloc_skb()函数以GFP_ATOMIC优先级进行skb的分配。


2.释放




Linux内核内部使用kfree_skb()函数,而网络设备驱动程序中则最好使用dev_kfree_skb()。


sk_buff中比较重要的成员是指向数据包中数据的指针,如下图所示:



用于寻址数据包中数据的指针,head指向已分配空间开头,data指向有效的octet开头,tail指向有效的octet结尾,而end指向tail可以到达的最大地址。如果不这样做而分配一个大小固定的缓冲区,如果buffer不够用,则要申请一个更大的buffer,拷贝进去再增加,这样降低了性能。


3.变更



下图分别对应了这四个函数,看了这张图应该对这4个函数的作用了然于胸。




网络设备接口层:


网络设备接口层的主要功能是为千变万化的网络设备定义了统一,抽象的数据结构net_device结构体,以不变应万变,实现多种硬件在软件层次上的统一。


首先看打开和关闭网络设备的函数:



要注意的是ifconfig是interface config的缩写,通常我们在用户空间输入:



会调用这里的open函数。


在用户空间输入:



会调用这里的stop函数。


在使用ifconfig向接口赋予地址时,要执行两个任务。首先,它通过ioctl(SIOCSIFADDR)(Socket I/O Control Set Interface Address)赋予地址,然后通过ioctl(SIOCSIFFLAGS)(Socket I/O Control Set Interface Flags)设置dev->flag中的IFF_UP标志以打开接口。这个调用会使得设备的open方法得到调用。类似的,在接口关闭时,ifconfig使用ioctl(SIOCSIFFLAGS)来清理IFF_UP标志,然后调用stop函数。



该方法根据先前检索到的源和目的硬件地址建立硬件头。



以太网的mac地址是固定的,为了高效,第一个包去询问mac地址,得到对应的mac地址后就会作为cache把mac地址保存起来。以后每次发包不用询问了,直接把包的地址拷贝出来。



如果数据包发送在超时时间内失败,这时该方法被调用,这个方法应该解决失败的问题,并重新开始发送数据。



当应用程序需要获得接口的统计信息时,这个方法被调用。



改变接口的配置,比如改变I/O端口和中断号等,现在的驱动程序通常无需该方法。



用来实现自定义的ioctl命令,如果不需要可以为NULL。



当设备的组播列表改变或设备标志改变时,该方法被调用。



如果接口支持mac地址改变,则可以实现该函数。


设备驱动接口层:


net_device结构体的成员(属性和函数指针)需要被设备驱动功能层的具体数值和函数赋予。对具体的设置xxx,工程师应该编写设备驱动功能层的函数,这些函数型如xxx_open(),xxx_stop(),xxx_tx(),xxx_hard_header(),xxx_get_stats(),xxx_tx_timeout()等。


网络设备与媒介层:


网络设备与媒介层直接对应于实际的硬件设备。