分类: LINUX
2016-01-19 13:55:40
原文地址:Linux设备驱动程序(LDD)中snull的编译问题 《转》 作者:三点水兽
对LDD中snull程序,编译的时候会有许多问题,鉴于网上还没有合适的解决办法,做此总结,整理知识。本文在debian6.0上运行通过,内核版本为2.6.32。
学习LDD中网络驱动程序部分,理解snull程序的原理很有必要。snull不依赖于硬件,数据包的收发都属于内存操作,但对整个网络驱动程序原 理已经做了很好的阐述。程序并不复杂,相比e100.c;8139too.c;pci-skeleton.c,容易理解的多。作者写这本书的时候尚是 2.6.11的年代,与现在内核版本相比,有些接口发生了变化,这是snull编译失败的直接原因。本文将描述snull的修改方法,并指出发生变化的接 口。本文在debian6.0上运行通过,内核版本为2.6.32。
本文提供了修改完成后的makefile和snull.c,并生成了patch文件。
1)常见问题
2)正确的snull编译方法
3)文件下载
这是我自己遇到的,和网上看到的部分问题,现一一阐述原因,并在第二段(见下文)中阐述解决方法。 如果在这些问题之外,还有问题,清大家留言,方便讨论。至于详细的接口变化,我会另写文章,一一说明。
1)错误描述:
make -C /lib/modules/2.6.32-5-686/build M=/home/xiebiwei/dev/code/Ldd/snull modules
make: *** /lib/modules/2.6.32-5-686/build: No such file or directory. Stop.
make: *** [default] Error 2
问题原因:没有安装内核源代码树,或者是安装了内核源代码树后,没有修改makefile中的kerneldir。
2)错误描述:
make -C /lib/modules/2.6.32/build M=/home/xiebiwei/dev/code/Ldd/snull modules
make[1]: Entering directory `/usr/src/linux-source-2.6.32'
scripts/Makefile.build:49:
*** CFLAGS was changed in "/home/xiebiwei/dev/code/Ldd/snull/Makefile".
Fix it to use EXTRA_CFLAGS. Stop.
make[1]: *** [_module_/home/xiebiwei/dev/code/Ldd/snull] Error 2
make[1]: Leaving directory `/usr/src/linux-source-2.6.32'
make: *** [default] Error 2
问题原因:内核版本不同,最近版本已经把CFLAGS变为EXTRA_CFLAGS
3)错误描述:
make -C /lib/modules/2.6.32/build M=/home/xiebiwei/dev/code/Ldd/snull modules
make[1]: Entering directory `/usr/src/linux-source-2.6.32'
CC [M] /home/xiebiwei/dev/code/Ldd/snull/snull.o
/home/xiebiwei/dev/code/Ldd/snull/snull.c:18:26: error: linux/config.h: No such file or directory
/home/xiebiwei/dev/code/Ldd/snull/snull.c: In function ‘snull_poll’:
/home/xiebiwei/dev/code/Ldd/snull/snull.c:289: error: ‘struct net_device’ has no member named ‘quota’
/home/xiebiwei/dev/code/Ldd/snull/snull.c:289: warning: type defaults to ‘int’ in declaration of ‘_min1’
/home/xiebiwei/dev/code/Ldd/snull/snull.c:289: error: ‘struct net_device’ has no member named ‘quota’
/home/xiebiwei/dev/code/Ldd/snull/snull.c:319: error: ‘struct net_device’ has no member named ‘quota’
/home/xiebiwei/dev/code/Ldd/snull/snull.c:321: error: implicit declaration of function ‘netif_rx_complete’
/home/xiebiwei/dev/code/Ldd/snull/snull.c: In function ‘snull_napi_interrupt’:
/home/xiebiwei/dev/code/Ldd/snull/snull.c:406: error: implicit declaration of function ‘netif_rx_schedule’
/home/xiebiwei/dev/code/Ldd/snull/snull.c: In function ‘snull_init’:
/home/xiebiwei/dev/code/Ldd/snull/snull.c:647: error: ‘struct net_device’ has no member named ‘open’
/home/xiebiwei/dev/code/Ldd/snull/snull.c:648: error: ‘struct net_device’ has no member named ‘stop’
/home/xiebiwei/dev/code/Ldd/snull/snull.c:649: error: ‘struct net_device’ has no member named ‘set_config’
/home/xiebiwei/dev/code/Ldd/snull/snull.c:650: error: ‘struct net_device’ has no member named ‘hard_start_xmit’
/home/xiebiwei/dev/code/Ldd/snull/snull.c:651: error: ‘struct net_device’ has no member named ‘do_ioctl’
/home/xiebiwei/dev/code/Ldd/snull/snull.c:652: error: ‘struct net_device’ has no member named ‘get_stats’
/home/xiebiwei/dev/code/Ldd/snull/snull.c:653: error: ‘struct net_device’ has no member named ‘change_mtu’
/home/xiebiwei/dev/code/Ldd/snull/snull.c:654: error: ‘struct net_device’ has no member named ‘rebuild_header’
/home/xiebiwei/dev/code/Ldd/snull/snull.c:655: error: ‘struct net_device’ has no member named ‘hard_header’
/home/xiebiwei/dev/code/Ldd/snull/snull.c:656: error: ‘struct net_device’ has no member named ‘tx_timeout’
/home/xiebiwei/dev/code/Ldd/snull/snull.c:659: error: ‘struct net_device’ has no member named ‘poll’
/home/xiebiwei/dev/code/Ldd/snull/snull.c:660: error: ‘struct net_device’ has no member named ‘weight’
/home/xiebiwei/dev/code/Ldd/snull/snull.c:665: error: ‘struct net_device’ has no member named ‘hard_header_cache’
make[2]: *** [/home/xiebiwei/dev/code/Ldd/snull/snull.o] Error 1
make[1]: *** [_module_/home/xiebiwei/dev/code/Ldd/snull] Error 2
make[1]: Leaving directory `/usr/src/linux-source-2.6.32'
make: *** [default] Error 2
问题原因:
1 config.h在2.6.32内核中已经不存在,需要用相应头文件替换
2 struct net_device 的结构在新版本内核中发生了变化,删去了quota成员;将(open,stop,set_config,hard_start_xmit等)封装进了 struct net_device_ops;将(poll,weight等)封装进了 struct napi_struct;将(hard_header,hard_header_cache,rebuild等)封装进了struct header_ops。并对部分函数名和参数进行了变动。
3 netif_rx_complete和netif_rx_schedule属于poll机制中的函数,接口名称已经变为napi_complete和napi_schedule。
1)构造内核源代码树
这是编写驱动程序的必要准备,否则会无法编译驱动程序。模块在编译的时候必须调用内核的函数(模块属于内核的一部分,无法调用glibc),故而构造源代码树很必要。
请参考:debian上的详细步骤:http://blog.csdn.net/xiebiwei/archive/2011/02/20/6196818.aspx
debian中下载kernel比较方便,而比较通用的下载kernel方式如下:
用 " uname -r " 查看系统内核版本,到 linux的kernel网站( )查找相应版本的内核源文件,下载并将其置于"/usr/src/"下。
接下来便可用上述链接中的方法解压缩内核源代码并编译了。
2)修改MakeFile
I) 将CFLAGS修改为EXTRA_CFLAGS,属于内核升级带来的接口变化。查看
II)修改KERNELDIR为正确的模块编译目录,即构造内核源代码树时生成的目录,一般为" /lib/modules /**/build"。查看
3)修改源文件snull.c
I)头文件中的
II) 修改 struct snull_priv ,添加struct napi_struct、struct net_device两个结构体的成员变量。2.6.32内核将poll()、weight等封装进了napi_struct,故而想实现轮询机制必须在 private中定义napi_struct。 查看
III) 修改函数:snull_poll。2.6.32内核中,struct net_device去除了quota,quota是设备对接收数据包数量的限 制;poll函数接口参数也发生了变化,变为了 static int (*poll)(struct napi_struct,int),故而函数体也需要稍做调整,比如priv指针的获取等。查看
IV) 修改函数snull_napi_interrupt,该函数中调度poll的函数接口变为了napi_schedule(struct napi_struct *)。查看
V) 修改函数snull_init。struct net_device的定义发生了变化。查看
将open、stop、set_config等操作封装进了结构体 struct net_device_ops。查看
将harder_header、rebuild_header等操作封装进了struct header_ops;poll则封装进了struct napi_stuct。查看
(包括snull.c makefile snull_2.6.32.patch)
使用方法:可以直接覆盖snull.c和makefile。也可以只将patch文件拷贝到snull/下后运行patch -p1 < snull_2.6.32.patch
patch文件diff -uNr snull_original/Makefile snull/Makefile
--- snull_original/Makefile 2005-01-31 15:31:02.000000000 -0500
+++ snull/Makefile 2011-02-24 17:45:39.000000000 -0500
@@ -9,8 +9,8 @@
DEBFLAGS = -O2
endif
-CFLAGS += $(DEBFLAGS)
-CFLAGS += -I..
+EXTRA_CFLAGS += $(DEBFLAGS)
+EXTRA_CFLAGS += -I..
ifneq ($(KERNELRELEASE),)
# call from kernel build system
@@ -19,7 +19,7 @@
else
-KERNELDIR ?= /lib/modules/$(shell uname -r)/build
+KERNELDIR ?= /lib/modules/2.6.32/build
PWD := $(shell pwd)
default:
diff -uNr snull_original/snull.c snull/snull.c
--- snull_original/snull.c 2005-01-31 15:31:02.000000000 -0500
+++ snull/snull.c 2011-02-26 19:50:06.000000000 -0500
@@ -14,8 +14,10 @@
*
* $Id: snull.c,v 1.21 2004/11/05 02:36:03 rubini Exp $
*/
-
-#include+#ifdef LINUX_CONFIG_H
+#define LINUX_CONFIG_H
+#include#include
+#endif
#include
#include
@@ -87,6 +89,9 @@
u8 *tx_packetdata;
struct sk_buff *skb;
spinlock_t lock;
+
+ struct napi_struct napi;
+ struct net_device *dev;
};
static void snull_tx_timeout(struct net_device *dev);
@@ -284,11 +289,12 @@
/*
* The poll implementation.
*/
-static int snull_poll(struct net_device *dev, int *budget)
+static int snull_poll(struct napi_struct *napi, int budget)
{
- int npackets = 0, quota = min(dev->quota, *budget);
+ int npackets = 0, quota = budget;
struct sk_buff *skb;
- struct snull_priv *priv = netdev_priv(dev);
+ struct snull_priv *priv = container_of(napi,struct snull_priv,napi);
+ struct net_device *dev=priv->dev;
struct snull_packet *pkt;
while (npackets < quota && priv->rx_queue) {
@@ -315,10 +321,8 @@
snull_release_buffer(pkt);
}
/* If we processed all packets, we're done; tell the kernel and reenable ints */
- *budget -= npackets;
- dev->quota -= npackets;
if (! priv->rx_queue) {
- netif_rx_complete(dev);
+ napi_complete(&priv->napi);
snull_rx_ints(dev, 1);
return 0;
}
@@ -403,7 +407,7 @@
priv->status = 0;
if (statusword & SNULL_RX_INTR) {
snull_rx_ints(dev, 0); /* Disable further interrupts */
- netif_rx_schedule(dev);
+ napi_schedule(&priv->napi);
}
if (statusword & SNULL_TX_INTR) {
/* a transmission is over: free the skb */
@@ -585,8 +589,8 @@
int snull_header(struct sk_buff *skb, struct net_device *dev,
- unsigned short type, void *daddr, void *saddr,
- unsigned int len)
+ unsigned short type, const void *daddr, const void *saddr,
+ unsigned len)
{
struct ethhdr *eth = (struct ethhdr *)skb_push(skb,ETH_HLEN);
@@ -627,6 +631,25 @@
* The init function (sometimes called probe).
* It is invoked by register_netdev()
*/
+
+static const struct net_device_ops snull_dev_ops = {
+
+ .ndo_open =snull_open,
+ .ndo_stop =snull_release,
+ .ndo_set_config =snull_config,
+ .ndo_start_xmit =snull_tx,
+ .ndo_do_ioctl =snull_ioctl,
+ .ndo_get_stats =snull_stats,
+ .ndo_change_mtu =snull_change_mtu,
+ .ndo_tx_timeout =snull_tx_timeout,
+};
+
+static const struct header_ops snull_header_ops= {
+ .create =snull_header,
+ .rebuild =snull_rebuild_header,
+ .cache = NULL, /* Disable caching */
+};
+
void snull_init(struct net_device *dev)
{
struct snull_priv *priv;
@@ -644,25 +667,13 @@
*/
ether_setup(dev); /* assign some of the fields */
- dev->open = snull_open;
- dev->stop = snull_release;
- dev->set_config = snull_config;
- dev->hard_start_xmit = snull_tx;
- dev->do_ioctl = snull_ioctl;
- dev->get_stats = snull_stats;
- dev->change_mtu = snull_change_mtu;
- dev->rebuild_header = snull_rebuild_header;
- dev->hard_header = snull_header;
- dev->tx_timeout = snull_tx_timeout;
+ dev->netdev_ops = &snull_dev_ops;
+ dev->header_ops = &snull_header_ops;
dev->watchdog_timeo = timeout;
- if (use_napi) {
- dev->poll = snull_poll;
- dev->weight = 2;
- }
+
/* keep the default flags, just add NOARP */
dev->flags |= IFF_NOARP;
dev->features |= NETIF_F_NO_CSUM;
- dev->hard_header_cache = NULL; /* Disable caching */
/*
* Then, initialize the priv field. This encloses the statistics
@@ -670,9 +681,15 @@
*/
priv = netdev_priv(dev);
memset(priv, 0, sizeof(struct snull_priv));
+ priv->dev = dev;
+
spin_lock_init(&priv->lock);
snull_rx_ints(dev, 1); /* enable receive interrupts */
snull_setup_pool(dev);
+ if (use_napi) {
+ netif_napi_add(dev,&priv->napi,snull_poll,2);
+ }
+
}