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。

 

 

 


二 正確的snull編譯方法

1)構造內核源代碼樹

     這是編寫驅動程序的必要準備,否則會無法編譯驅動程序。模塊在編譯的時候必須調用內核的函數(模塊屬於內核的一部分,無法調用glibc),故而構造源代碼樹很必要。

     請參考:debian上的詳細步驟:http://blog.csdn.net/xiebiwei/archive/2011/02/20/6196818.aspx

     debian中下載kernel比較方便,而比較通用的下載kernel方式如下:         

     用 "  uname -r " 查看系統內核版本,到 linux的kernel網站(http://www.kernel.org/pub/linux/kernel/v2.6/ )查找相應版本的內核源文件,下載並將其置於"/usr/src/"下。

     接下來便可用上述鏈接中的方法解壓縮內核源代碼並編譯了。        

 

2)修改MakeFile

I)  將CFLAGS修改爲EXTRA_CFLAGS,屬於內核升級帶來的接口變化。查看

II)修改KERNELDIR爲正確的模塊編譯目錄,即構造內核源代碼樹時生成的目錄,一般爲" /lib/modules /**/build"。查看    

 

3)修改源文件snull.c

I)頭文件中的<linux/config.h>,在新內核中已經被刪除,查看老版本內核的該文件內容,並替換。查看  

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 <linux/config.h>
+#ifdef LINUX_CONFIG_H
+#define LINUX_CONFIG_H
+#include <linux/autoconf.h>
+#endif
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/moduleparam.h>
@@ -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);
+ }
+
 }

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章