歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux管理 >> Linux網絡 >> Linux網絡驅動--snull,linux網絡--snull

Linux網絡驅動--snull,linux網絡--snull

日期:2017/3/3 18:00:04   编辑:Linux網絡

Linux網絡驅動--snull,linux網絡--snull

Linux網絡驅動--snull,linux網絡--snull


snull是《Linux Device Drivers》中的一個網絡驅動的例子。這裡引用這個例子學習Linux網絡驅動。

因為snull的源碼,網上已經更新到適合最新內核,而我自己用的還是2.6.22.6比較舊的內核。而網上好像找不到舊版的snull。因此結合《Linux Device Drivers》把最新的snull例子移植到2.6.22.6內核中。移植也相對簡單,這裡也提供移植好的代碼。

估計不少網友看到《Linux Device Drivers》的網絡驅動部分,一臉懵逼,包括我自己,不理解作者設計這個例子的真正目的,盡管有配圖,仍然懵懂,甚至不知道為什麼會用到6個IP地址。如圖:

其實作者的本意是想通過虛擬網卡來模擬實際的網卡和外部的網絡設備的通信來討論網絡驅動。通過其中任何一個網絡接口(sn0或sn1)發送數據,都在另一個網絡接口(sn0或sn1)接收到。

因為sn0和sn1都不在同一個網段,所以sn0和sn1之間直接互ping是不行的,這中間必須必須做點轉換。

例子:

理論上local0和remote0只能互ping,因為他們都在同一個網段:192.168.0.0,但事實上,local0在發出數據之後,local0的第3個字節最低有效位改取反,就變成了remote1,remote1的數據才能到達local1,因為他們在同一段IP。相反,local1在發出數據之後,local1的第3個字節最低有效位改取反,就變成了remote0,remote0的數據才能到達local0.

因此,在實驗之前,需要添加一些配置:

在/etc/networks文件中添加如下網段IP:

snullnet0 192.168.2.0

snullnet1 192.168.3.0

在/etc/hosts文件中添加如下IP地址

192.168.2.8 local0

192.168.2.9 remote0

192.168.3.9 local1

192.168.3.8 remote1

注意: 1. 網段IP和IP地址的第三個字節的最低有效位是相反的

2. local0和remote1第四個字節必須一樣,remote0和local1第四個字節必須一樣

3. 如果開發板上的真正網卡用了的網段IP,就不能再用於本實驗。如:我的開發板的DM9000網卡使用網段是192.168.1.0, 因此本實驗不能再使用192.168.1.0作為網段,否則有沖突。

代碼: snull.c, 其中snull.h沒改動,因此不貼出來

  1 /*
  2  * snull.c --  the Simple Network Utility
  3  *
  4  * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet
  5  * Copyright (C) 2001 O'Reilly & Associates
  6  *
  7  * The source code in this file can be freely used, adapted,
  8  * and redistributed in source or binary form, so long as an
  9  * acknowledgment appears in derived source files.  The citation
 10  * should list that the code comes from the book "Linux Device
 11  * Drivers" by Alessandro Rubini and Jonathan Corbet, published
 12  * by O'Reilly & Associates.   No warranty is attached;
 13  * we cannot take responsibility for errors or fitness for use.
 14  *
 15  * $Id: snull.c,v 1.21 2004/11/05 02:36:03 rubini Exp $
 16  */
 17 
 18 #include <linux/module.h>
 19 #include <linux/init.h>
 20 #include <linux/moduleparam.h>
 21 
 22 #include <linux/sched.h>
 23 #include <linux/kernel.h> /* printk() */
 24 #include <linux/slab.h> /* kmalloc() */
 25 #include <linux/errno.h>  /* error codes */
 26 #include <linux/types.h>  /* size_t */
 27 #include <linux/interrupt.h> /* mark_bh */
 28 
 29 #include <linux/in.h>
 30 #include <linux/netdevice.h>   /* struct device, and other headers */
 31 #include <linux/etherdevice.h> /* eth_type_trans */
 32 #include <linux/ip.h>          /* struct iphdr */
 33 #include <linux/tcp.h>         /* struct tcphdr */
 34 #include <linux/skbuff.h>
 35 
 36 #include "snull.h"
 37 
 38 #include <linux/in6.h>
 39 #include <asm/checksum.h>
 40 
 41 MODULE_AUTHOR("Alessandro Rubini, Jonathan Corbet");
 42 MODULE_LICENSE("Dual BSD/GPL");
 43 
 44 
 45 /*
 46  * Transmitter lockup simulation, normally disabled.
 47  */
 48 static int lockup = 0;
 49 module_param(lockup, int, 0);
 50 
 51 static int timeout = SNULL_TIMEOUT;
 52 module_param(timeout, int, 0);
 53 
 54 /*
 55  * Do we run in NAPI mode?
 56  */
 57 static int use_napi = 0;
 58 module_param(use_napi, int, 0);
 59 
 60 
 61 /*
 62  * A structure representing an in-flight packet.
 63  */
 64 struct snull_packet {
 65     struct snull_packet *next;
 66     struct net_device *dev;
 67     int    datalen;
 68     u8 data[ETH_DATA_LEN];
 69 };
 70 
 71 int pool_size = 8;
 72 module_param(pool_size, int, 0);
 73 
 74 /*
 75  * This structure is private to each device. It is used to pass
 76  * packets in and out, so there is place for a packet
 77  */
 78 
 79 struct snull_priv {
 80     struct net_device_stats stats;
 81     int status;
 82     struct snull_packet *ppool;
 83     struct snull_packet *rx_queue;  /* List of incoming packets */
 84     int rx_int_enabled;
 85     int tx_packetlen;
 86     u8 *tx_packetdata;
 87     struct sk_buff *skb;
 88     spinlock_t lock;
 89     struct net_device *dev;
 90     //struct napi_struct napi;
 91 };
 92 
 93 static void snull_tx_timeout(struct net_device *dev);
 94 static void (*snull_interrupt)(int, void *, struct pt_regs *);
 95 
 96 /*
 97  * Set up a device's packet pool.
 98  */
 99 void snull_setup_pool(struct net_device *dev)
100 {
101     struct snull_priv *priv = netdev_priv(dev);
102     int i;
103     struct snull_packet *pkt;
104 
105     priv->ppool = NULL;
106     for (i = 0; i < pool_size; i++) {
107         pkt = kmalloc (sizeof (struct snull_packet), GFP_KERNEL);
108         if (pkt == NULL) {
109             printk (KERN_NOTICE "Ran out of memory allocating packet pool\n");
110             return;
111         }
112         pkt->dev = dev;
113         pkt->next = priv->ppool;
114         priv->ppool = pkt;
115     }
116 }
117 
118 void snull_teardown_pool(struct net_device *dev)
119 {
120     struct snull_priv *priv = netdev_priv(dev);
121     struct snull_packet *pkt;
122     
123     while ((pkt = priv->ppool)) {
124         priv->ppool = pkt->next;
125         kfree (pkt);
126         /* FIXME - in-flight packets ? */
127     }
128 }    
129 
130 /*
131  * Buffer/pool management.
132  */
133 struct snull_packet *snull_get_tx_buffer(struct net_device *dev)
134 {
135     struct snull_priv *priv = netdev_priv(dev);
136     unsigned long flags;
137     struct snull_packet *pkt;
138     
139     spin_lock_irqsave(&priv->lock, flags);
140     pkt = priv->ppool;
141     priv->ppool = pkt->next;
142     if (priv->ppool == NULL) {
143         printk (KERN_INFO "Pool empty\n");
144         netif_stop_queue(dev);
145     }
146     spin_unlock_irqrestore(&priv->lock, flags);
147     return pkt;
148 }
149 
150 
151 void snull_release_buffer(struct snull_packet *pkt)
152 {
153     unsigned long flags;
154     struct snull_priv *priv = netdev_priv(pkt->dev);
155     
156     spin_lock_irqsave(&priv->lock, flags);
157     pkt->next = priv->ppool;
158     priv->ppool = pkt;
159     spin_unlock_irqrestore(&priv->lock, flags);
160     if (netif_queue_stopped(pkt->dev) && pkt->next == NULL)
161         netif_wake_queue(pkt->dev);
162 
163     printk("snull_release_buffer\n");
164 }
165 
166 void snull_enqueue_buf(struct net_device *dev, struct snull_packet *pkt)
167 {
168     unsigned long flags;
169     struct snull_priv *priv = netdev_priv(dev);
170 
171     spin_lock_irqsave(&priv->lock, flags);
172     pkt->next = priv->rx_queue;  /* FIXME - misorders packets */
173     priv->rx_queue = pkt;
174     spin_unlock_irqrestore(&priv->lock, flags);
175 }
176 
177 struct snull_packet *snull_dequeue_buf(struct net_device *dev)
178 {
179     struct snull_priv *priv = netdev_priv(dev);
180     struct snull_packet *pkt;
181     unsigned long flags;
182 
183     spin_lock_irqsave(&priv->lock, flags);
184     pkt = priv->rx_queue;
185     if (pkt != NULL)
186         priv->rx_queue = pkt->next;
187     spin_unlock_irqrestore(&priv->lock, flags);
188     return pkt;
189 }
190 
191 /*
192  * Enable and disable receive interrupts.
193  */
194 static void snull_rx_ints(struct net_device *dev, int enable)
195 {
196     struct snull_priv *priv = netdev_priv(dev);
197     priv->rx_int_enabled = enable;
198 }
199 
200     
201 /*
202  * Open and close
203  */
204 
205 int snull_open(struct net_device *dev)
206 {
207     /* request_region(), request_irq(), ....  (like fops->open) */
208 
209     /* 
210      * Assign the hardware address of the board: use "\0SNULx", where
211      * x is 0 or 1. The first byte is '\0' to avoid being a multicast
212      * address (the first byte of multicast addrs is odd).
213      */
214     /* [cgw]: 分配一個假的硬件地址,真正的網卡的時候,這個地址是從網卡讀出來的 */
215     memcpy(dev->dev_addr, "\0SNUL0", ETH_ALEN);
216     /* [cgw]: 因為注冊了兩個虛擬網卡,第二個虛擬網卡的地址跟第一個的地址必須不一樣
217      * 即這兩個網卡地址分別為\0SNUL0和\0SNUL1
218      */
219     if (dev == snull_devs[1])
220         dev->dev_addr[ETH_ALEN-1]++; /* \0SNUL1 */
221     /* [cgw]: 啟動發送隊列 */
222     netif_start_queue(dev);
223 
224     printk("snull_open\n");
225     
226     return 0;
227 }
228 
229 int snull_release(struct net_device *dev)
230 {
231     /* release ports, irq and such -- like fops->close */
232 
233     netif_stop_queue(dev); /* can't transmit any more */
234     
235     printk("snull_release\n");
236     
237     return 0;
238 }
239 
240 /*
241  * Configuration changes (passed on by ifconfig)
242  */
243 int snull_config(struct net_device *dev, struct ifmap *map)
244 {
245     if (dev->flags & IFF_UP) /* can't act on a running interface */
246         return -EBUSY;
247 
248     /* Don't allow changing the I/O address */
249     if (map->base_addr != dev->base_addr) {
250         printk(KERN_WARNING "snull: Can't change I/O address\n");
251         return -EOPNOTSUPP;
252     }
253 
254     /* Allow changing the IRQ */
255     if (map->irq != dev->irq) {
256         dev->irq = map->irq;
257             /* request_irq() is delayed to open-time */
258     }
259 
260     printk("snull_config\n");
261 
262     /* ignore other fields */
263     return 0;
264 }
265 
266 /*
267  * Receive a packet: retrieve, encapsulate and pass over to upper levels
268  */
269 void snull_rx(struct net_device *dev, struct snull_packet *pkt)
270 {
271     struct sk_buff *skb;
272     struct snull_priv *priv = netdev_priv(dev);
273 
274     /*
275      * The packet has been retrieved from the transmission
276      * medium. Build an skb around it, so upper layers can handle it
277      */
278     /* [cgw]: 為接收包分配一個skb */
279     skb = dev_alloc_skb(pkt->datalen + 2);
280     if (!skb) {
281         if (printk_ratelimit())
282             printk(KERN_NOTICE "snull rx: low on mem - packet dropped\n");
283         priv->stats.rx_dropped++;
284         goto out;
285     }
286     /* [cgw]: 16字節對齊,即IP首部前是網卡硬件地址首部,其占14字節,需要為其增加2
287      * 個字節 
288      */
289     skb_reserve(skb, 2); /* align IP on 16B boundary */
290     /* [cgw]: 開辟一個數據緩沖區用於存放接收數據 */
291     memcpy(skb_put(skb, pkt->datalen), pkt->data, pkt->datalen);
292 
293     /* Write metadata, and then pass to the receive level */
294     skb->dev = dev;
295     if (skb->dev == snull_devs[0]) {
296         printk("skb->dev is snull_devs[0]\n");
297     } else {
298         printk("skb->dev is snull_devs[1]\n");
299     }
300     /* [cgw]: 確定包的協議ID */
301     skb->protocol = eth_type_trans(skb, dev);
302 
303     printk("skb->protocol = %d\n", skb->protocol);
304     
305     skb->ip_summed = CHECKSUM_UNNECESSARY; /* don't check it */
306     /* [cgw]: 統計接收包數和字節數 */
307     priv->stats.rx_packets++;
308     priv->stats.rx_bytes += pkt->datalen;
309     /* [cgw]: 上報應用層 */
310     netif_rx(skb);
311 
312     printk("snull_rx\n");
313     
314   out:
315     return;
316 }
317     
318 
319 /*
320  * The poll implementation.
321  */
322 //static int snull_poll(struct napi_struct *napi, int budget)
323 static int snull_poll(struct net_device *dev, int *budget)
324 {
325     //int npackets = 0;
326     //struct sk_buff *skb;
327     //struct snull_priv *priv = container_of(napi, struct snull_priv, napi);
328     //struct net_device *dev = priv->dev;
329     //struct snull_packet *pkt;
330 
331     int npackets = 0, quota = min(dev->quota, *budget);
332     struct sk_buff *skb;
333     struct snull_priv *priv = netdev_priv(dev);
334     struct snull_packet *pkt;
335 
336     printk("snull_poll\n");
337     
338     //while (npackets < budget && priv->rx_queue) {
339     while (npackets < quota && priv->rx_queue) {
340         pkt = snull_dequeue_buf(dev);
341         skb = dev_alloc_skb(pkt->datalen + 2);
342         if (! skb) {
343             if (printk_ratelimit())
344                 printk(KERN_NOTICE "snull: packet dropped\n");
345             priv->stats.rx_dropped++;
346             snull_release_buffer(pkt);
347             continue;
348         }
349         skb_reserve(skb, 2); /* align IP on 16B boundary */  
350         memcpy(skb_put(skb, pkt->datalen), pkt->data, pkt->datalen);
351         skb->dev = dev;
352         skb->protocol = eth_type_trans(skb, dev);
353         skb->ip_summed = CHECKSUM_UNNECESSARY; /* don't check it */
354         netif_receive_skb(skb);
355         
356             /* Maintain stats */
357         npackets++;
358         priv->stats.rx_packets++;
359         priv->stats.rx_bytes += pkt->datalen;
360         snull_release_buffer(pkt);
361     }
362     /* If we processed all packets, we're done; tell the kernel and reenable ints */
363     *budget -= npackets;
364     dev->quota -= npackets;
365     if (! priv->rx_queue) {
366         //napi_complete(napi);
367         netif_rx_complete(dev);
368         snull_rx_ints(dev, 1);
369         return 0;
370     }
371     /* We couldn't process everything. */
372     //return npackets;
373     return 1;
374 }        
375         
376 /*
377  * The typical interrupt entry point
378  */
379 static void snull_regular_interrupt(int irq, void *dev_id, struct pt_regs *regs)
380 {
381     int statusword;
382     struct snull_priv *priv;
383     struct snull_packet *pkt = NULL;
384     /*
385      * As usual, check the "device" pointer to be sure it is
386      * really interrupting.
387      * Then assign "struct device *dev"
388      */
389     struct net_device *dev = (struct net_device *)dev_id;
390     /* ... and check with hw if it's really ours */
391 
392     /* paranoid */
393     if (!dev)
394         return;
395 
396     /* Lock the device */
397     priv = netdev_priv(dev);
398     spin_lock(&priv->lock);
399 
400     /* [cgw]: 判斷產生的是什麼類型的中斷,接收還是中斷 */
401     /* retrieve statusword: real netdevices use I/O instructions */
402     statusword = priv->status;
403     
404     printk("priv->status = %d\n", priv->status);
405     
406     priv->status = 0;
407     /* [cgw]: 接收完成中斷 */
408     if (statusword & SNULL_RX_INTR) {
409         /* send it to snull_rx for handling */
410         pkt = priv->rx_queue;
411         if (pkt) {
412             priv->rx_queue = pkt->next;
413             /* [cgw]: 網卡接收到數據,上報給應用層 */
414             snull_rx(dev, pkt);
415         }
416     }
417     /* [cgw]: 發送完成中斷 */
418     if (statusword & SNULL_TX_INTR) {
419         /* [cgw]: 統計已發送的包數和總字節數,並釋放這個包的內存 */
420         /* a transmission is over: free the skb */
421         priv->stats.tx_packets++;
422         priv->stats.tx_bytes += priv->tx_packetlen;
423         dev_kfree_skb(priv->skb);
424     }
425 
426     /* Unlock the device and we are done */
427     spin_unlock(&priv->lock);
428     if (pkt) snull_release_buffer(pkt); /* Do this outside the lock! */
429 
430     printk("snull_regular_interrupt\n");
431 
432     return;
433 }
434 
435 /*
436  * A NAPI interrupt handler.
437  */
438 static void snull_napi_interrupt(int irq, void *dev_id, struct pt_regs *regs)
439 {
440     int statusword;
441     struct snull_priv *priv;
442 
443     /*
444      * As usual, check the "device" pointer for shared handlers.
445      * Then assign "struct device *dev"
446      */
447     struct net_device *dev = (struct net_device *)dev_id;
448     /* ... and check with hw if it's really ours */
449 
450     printk("snull_napi_interrupt\n");
451 
452     /* paranoid */
453     if (!dev)
454         return;
455 
456     /* Lock the device */
457     priv = netdev_priv(dev);
458     spin_lock(&priv->lock);
459 
460     /* retrieve statusword: real netdevices use I/O instructions */
461     statusword = priv->status;
462     priv->status = 0;
463     if (statusword & SNULL_RX_INTR) {
464         snull_rx_ints(dev, 0);  /* Disable further interrupts */
465         //napi_schedule(&priv->napi);
466         netif_rx_schedule(dev);
467     }
468     if (statusword & SNULL_TX_INTR) {
469             /* a transmission is over: free the skb */
470         priv->stats.tx_packets++;
471         priv->stats.tx_bytes += priv->tx_packetlen;
472         dev_kfree_skb(priv->skb);
473     }
474 
475     /* Unlock the device and we are done */
476     spin_unlock(&priv->lock);
477     return;
478 }
479 
480 
481 /*
482  * Transmit a packet (low level interface)
483  */
484 static void snull_hw_tx(char *buf, int len, struct net_device *dev)
485 {
486     /*
487      * This function deals with hw details. This interface loops
488      * back the packet to the other snull interface (if any).
489      * In other words, this function implements the snull behaviour,
490      * while all other procedures are rather device-independent
491      */
492     struct iphdr *ih;
493     struct net_device *dest;
494     struct snull_priv *priv;
495     u32 *saddr, *daddr;
496     struct snull_packet *tx_buffer;
497     
498     /* I am paranoid. Ain't I? */
499     if (len < sizeof(struct ethhdr) + sizeof(struct iphdr)) {
500         printk("snull: Hmm... packet too short (%i octets)\n",
501                 len);
502         return;
503     }
504 
505     /* [cgw]: 打印上層應用(即ping)要發的這個包的內容
506      * 這個包的格式為:
507      * 14字節以太網首部+20字節IP地址首部+20字節TCP地址首部+n字節數據
508      */
509     
510     if (1) { /* enable this conditional to look at the data */
511         int i;
512         PDEBUG("len is %i\n" KERN_DEBUG "data:",len);
513         /* [cgw]: 14字節以太網首部 */
514         for (i=0 ; i<14; i++)
515             printk(" %02x",buf[i]&0xff);
516         printk("\n");
517 
518         /* [cgw]: 20字節IP地址首部 */
519         for (i=14 ; i<34; i++)
520             printk(" %02x",buf[i]&0xff);
521         printk("\n");
522 
523         /* [cgw]: 20字節TCP地址首部 */
524         for (i=34 ; i<54; i++)
525             printk(" %02x",buf[i]&0xff);
526         printk("\n");
527 
528         /* [cgw]: n字節數據 */
529         for (i=54 ; i<len; i++)
530             printk(" %02x",buf[i]&0xff);
531         printk("\n");
532     }
533     /*
534      * Ethhdr is 14 bytes, but the kernel arranges for iphdr
535      * to be aligned (i.e., ethhdr is unaligned)
536      */
537     /* [cgw]: 提取本地和目標IP地址 */
538     ih = (struct iphdr *)(buf+sizeof(struct ethhdr));
539     saddr = &ih->saddr;
540     daddr = &ih->daddr;
541     
542     printk("ih->protocol = %d is buf[23]\n", ih->protocol);
543     printk("saddr = %d.%d.%d.%d\n", *((u8 *)saddr + 0), *((u8 *)saddr + 1), *((u8 *)saddr + 2), *((u8 *)saddr + 3));
544     printk("daddr = %d.%d.%d.%d\n", *((u8 *)daddr + 0), *((u8 *)daddr + 1), *((u8 *)daddr + 2), *((u8 *)daddr + 3));
545 
546     /* [cgw]: 改變本地和目標IP地址的第三個字節的最低位,即原來是0,則改為1,原來是1,則改為0
547      */
548     ((u8 *)saddr)[2] ^= 1; /* change the third octet (class C) */
549     ((u8 *)daddr)[2] ^= 1;
550 
551     /* [cgw]: 從新計算校驗,因為IP已改變 */
552     ih->check = 0;         /* and rebuild the checksum (ip needs it) */
553     ih->check = ip_fast_csum((unsigned char *)ih,ih->ihl);
554 
555     /* [cgw]: 打印更改後的IP地址,和TCP地址,
556      */
557     if (dev == snull_devs[0])
558         //PDEBUGG("%08x:%05i --> %08x:%05i\n",
559         printk("%08x:%05i --> %08x:%05i\n",
560                 ntohl(ih->saddr),ntohs(((struct tcphdr *)(ih+1))->source),
561                 ntohl(ih->daddr),ntohs(((struct tcphdr *)(ih+1))->dest));
562     else
563         //PDEBUGG("%08x:%05i <-- %08x:%05i\n",
564         printk("%08x:%05i <-- %08x:%05i\n",
565                 ntohl(ih->daddr),ntohs(((struct tcphdr *)(ih+1))->dest),
566                 ntohl(ih->saddr),ntohs(((struct tcphdr *)(ih+1))->source));
567 
568     /*
569      * Ok, now the packet is ready for transmission: first simulate a
570      * receive interrupt on the twin device, then  a
571      * transmission-done on the transmitting device
572      */
573     /* [cgw]: 獲得目的網卡設備 */
574     dest = snull_devs[dev == snull_devs[0] ? 1 : 0];
575     
576     if (dev == snull_devs[0]) {
577         printk("snull_devs[0]\n");
578     } else {
579         printk("snull_devs[1]\n");
580     }
581     
582     priv = netdev_priv(dest);
583     /* [cgw]: 取出一塊內存分配給本地網卡 */
584     tx_buffer = snull_get_tx_buffer(dev);
585     /* [cgw]: 設置數據包大小 */
586     tx_buffer->datalen = len;
587     
588     printk("tx_buffer->datalen = %d\n", tx_buffer->datalen);
589 
590     /* [cgw]: 填充發送網卡的數據 */
591     memcpy(tx_buffer->data, buf, len);
592     /* [cgw]: 把發送的數據直接加入到接收隊列,這裡相當於本地網卡要發送的數據
593      * 已經給目標網卡直接接收到了
594      */
595     snull_enqueue_buf(dest, tx_buffer);
596     /* [cgw]: 如果接收中斷使能,這個也是模擬的接收中斷,因為上面已經模擬接收
597      * 到數據,所以立刻產生一個中斷
598      */
599     if (priv->rx_int_enabled) {
600         priv->status |= SNULL_RX_INTR;
601         printk("priv->status = %d\n", priv->status);
602         /* [cgw]: 執行接收中斷 */
603         snull_interrupt(0, dest, NULL);
604         printk("snull_interrupt(0, dest, NULL);\n");
605     }
606 
607     /* [cgw]: 獲得本地網卡的私有數據指針 */
608     priv = netdev_priv(dev);
609     /* [cgw]: 把本地網卡要發送的數據存到私有數據緩沖區,接著產生一個發送中斷
610      */
611     priv->tx_packetlen = len;
612     priv->tx_packetdata = buf;
613     priv->status |= SNULL_TX_INTR;
614     if (lockup && ((priv->stats.tx_packets + 1) % lockup) == 0) {
615             /* Simulate a dropped transmit interrupt */
616         netif_stop_queue(dev);
617         PDEBUG("Simulate lockup at %ld, txp %ld\n", jiffies,
618                 (unsigned long) priv->stats.tx_packets);
619     }
620     else {
621         /* [cgw]: 產生一個發送中斷 */
622         snull_interrupt(0, dev, NULL);
623         printk("snull_interrupt(0, dev, NULL);\n");
624     }
625 }
626 
627 /*
628  * Transmit a packet (called by the kernel)
629  */
630 int snull_tx(struct sk_buff *skb, struct net_device *dev)
631 {
632     int len;
633     char *data, shortpkt[ETH_ZLEN];
634     struct snull_priv *priv = netdev_priv(dev);
635 
636     /* [cgw]: 獲取上層需要發送的數據和長度 */
637     data = skb->data;
638     len = skb->len;
639 
640     printk("skb->len = %d\n", skb->len);
641     
642     if (len < ETH_ZLEN) {
643         memset(shortpkt, 0, ETH_ZLEN);
644         memcpy(shortpkt, skb->data, skb->len);
645         len = ETH_ZLEN;
646         data = shortpkt;
647     }
648     /* [cgw]: 開始計算時間截,用於處理發送超時 */
649     dev->trans_start = jiffies; /* save the timestamp */
650 
651     /* Remember the skb, so we can free it at interrupt time */
652     priv->skb = skb;
653     
654     printk("snull_tx\n");
655 
656     /* actual deliver of data is device-specific, and not shown here */
657     /* [cgw]: 模擬把數據包寫入硬件,通過硬件發送出去,但實際上不是 */
658     snull_hw_tx(data, len, dev);
659 
660     //printk("snull_tx\n");
661 
662     return 0; /* Our simple device can not fail */
663 }
664 
665 /*
666  * Deal with a transmit timeout.
667  */
668 void snull_tx_timeout (struct net_device *dev)
669 {
670     struct snull_priv *priv = netdev_priv(dev);
671 
672     PDEBUG("Transmit timeout at %ld, latency %ld\n", jiffies,
673             jiffies - dev->trans_start);
674         /* Simulate a transmission interrupt to get things moving */
675     priv->status = SNULL_TX_INTR;
676     snull_interrupt(0, dev, NULL);
677     priv->stats.tx_errors++;
678     netif_wake_queue(dev);
679 
680     printk("snull_tx_timeout\n");
681     
682     return;
683 }
684 
685 
686 
687 /*
688  * Ioctl commands 
689  */
690 int snull_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
691 {
692     PDEBUG("ioctl\n");
693     printk("ioctl\n");
694     return 0;
695 }
696 
697 /*
698  * Return statistics to the caller
699  */
700 struct net_device_stats *snull_stats(struct net_device *dev)
701 {
702     struct snull_priv *priv = netdev_priv(dev);
703 
704     printk("snull_stats\n");
705     
706     return &priv->stats;
707 }
708 
709 /*
710  * This function is called to fill up an eth header, since arp is not
711  * available on the interface
712  */
713 int snull_rebuild_header(struct sk_buff *skb)
714 {
715     struct ethhdr *eth = (struct ethhdr *) skb->data;
716     struct net_device *dev = skb->dev;
717     
718     memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
719     memcpy(eth->h_dest, dev->dev_addr, dev->addr_len);
720     eth->h_dest[ETH_ALEN-1]   ^= 0x01;   /* dest is us xor 1 */
721 
722     printk("snull_rebuild_header\n");
723     
724     return 0;
725 }
726 
727 
728 //int snull_header(struct sk_buff *skb, struct net_device *dev,
729 //                unsigned short type, const void *daddr, const void *saddr,
730 //                unsigned len)
731 
732 int snull_header(struct sk_buff *skb, struct net_device *dev,
733                 unsigned short type, void *daddr, void *saddr,
734                 unsigned len)              
735 {
736     struct ethhdr *eth = (struct ethhdr *)skb_push(skb,ETH_HLEN);
737 
738     printk("len = %d\n", len);
739 
740     printk("type = %02x\n", type); //ETH_P_IP    0x0800        /* Internet Protocol packet    */
741 
742     /* htons是將整型變量從主機字節順序轉變成網絡字節順序, 
743      * 就是整數在地址空間存儲方式變為:高位字節存放在內存的低地址處
744      */
745     eth->h_proto = htons(type);
746     printk("h_proto = %d\n", eth->h_proto);
747     
748     printk("addr_len = %d\n", dev->addr_len);
749     printk("dev_addr = %02x.%02x.%02x.%02x.%02x.%02x\n", dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2], dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
750 
751     if (saddr) {
752         printk("saddr = %02x.%02x.%02x.%02x.%02x.%02x\n", *((unsigned char *)saddr + 0), *((unsigned char *)saddr + 1), *((unsigned char *)saddr + 2), *((unsigned char *)saddr + 3), *((unsigned char *)saddr + 4), *((unsigned char *)saddr + 5));
753     }
754 
755     if (daddr) {
756         printk("daddr = %02x.%02x.%02x.%02x.%02x.%02x\n", *((unsigned char *)daddr + 0), *((unsigned char *)daddr + 1), *((unsigned char *)daddr + 2), *((unsigned char *)daddr + 3), *((unsigned char *)daddr + 4), *((unsigned char *)daddr + 5));
757     }
758 
759     /* [cgw]: 上層應用要發送數據時,通過下層添加硬件地址,才能決定發送到那個目標網卡
760      */
761     memcpy(eth->h_source, saddr ? saddr : dev->dev_addr, dev->addr_len);
762     memcpy(eth->h_dest,   daddr ? daddr : dev->dev_addr, dev->addr_len);
763     printk("h_source = %02x.%02x.%02x.%02x.%02x.%02x\n", eth->h_source[0], eth->h_source[1], eth->h_source[2],eth->h_source[3], eth->h_source[4], eth->h_source[5]);
764     printk("h_dest = %02x.%02x.%02x.%02x.%02x.%02x\n", eth->h_dest[0], eth->h_dest[1], eth->h_dest[2], eth->h_dest[3], eth->h_dest[4], eth->h_dest[5]);
765 
766     /* [cgw]: 設置目標網卡硬件地址,即本地網卡和目標網卡硬件地址的最後一個字節的最低有效位
767      * 是相反關系,即本地是\0SNUL0的話,目標就是\0SNUL1,或者本地是\0SNUL1,目標就是\0SNUL0
768      */
769     eth->h_dest[ETH_ALEN-1]   ^= 0x01;   /* dest is us xor 1 */
770     printk("h_dest[ETH_ALEN-1] ^ 0x01 = %02x\n", eth->h_dest[ETH_ALEN-1]);
771     
772     printk("hard_header_len = %d\n", dev->hard_header_len);
773     
774     return (dev->hard_header_len);
775 }
776 
777 
778 
779 
780 
781 /*
782  * The "change_mtu" method is usually not needed.
783  * If you need it, it must be like this.
784  */
785 int snull_change_mtu(struct net_device *dev, int new_mtu)
786 {
787     unsigned long flags;
788     struct snull_priv *priv = netdev_priv(dev);
789     spinlock_t *lock = &priv->lock;
790     
791     /* check ranges */
792     if ((new_mtu < 68) || (new_mtu > 1500))
793         return -EINVAL;
794     /*
795      * Do anything you need, and the accept the value
796      */
797     spin_lock_irqsave(lock, flags);
798     dev->mtu = new_mtu;
799     spin_unlock_irqrestore(lock, flags);
800     return 0; /* success */
801 }
802 
803 #if 0
804 static const struct header_ops snull_header_ops = {
805         .create  = snull_header,
806     .rebuild = snull_rebuild_header
807 };
808 
809 static const struct net_device_ops snull_netdev_ops = {
810     .ndo_open            = snull_open,
811     .ndo_stop            = snull_release,
812     .ndo_start_xmit      = snull_tx,
813     .ndo_do_ioctl        = snull_ioctl,
814     .ndo_set_config      = snull_config,
815     .ndo_get_stats       = snull_stats,
816     .ndo_change_mtu      = snull_change_mtu,
817     .ndo_tx_timeout      = snull_tx_timeout
818 };
819 #endif
820 
821 /*
822  * The init function (sometimes called probe).
823  * It is invoked by register_netdev()
824  */
825 void snull_init(struct net_device *dev)
826 {
827     struct snull_priv *priv;
828 #if 0
829         /*
830      * Make the usual checks: check_region(), probe irq, ...  -ENODEV
831      * should be returned if no device found.  No resource should be
832      * grabbed: this is done on open(). 
833      */
834 #endif
835 
836         /* 
837      * Then, assign other fields in dev, using ether_setup() and some
838      * hand assignments
839      */
840     ether_setup(dev); /* assign some of the fields */
841     dev->watchdog_timeo = timeout;
842     
843     //dev->netdev_ops = &snull_netdev_ops;
844     //dev->header_ops = &snull_header_ops;
845     
846     dev->hard_header = snull_header;
847     dev->rebuild_header = snull_rebuild_header;
848     
849     dev->open = snull_open;
850     dev->stop = snull_release;
851     dev->hard_start_xmit = snull_tx;
852     dev->do_ioctl = snull_ioctl;
853     dev->set_config = snull_config;
854     dev->get_stats = snull_stats;
855     dev->change_mtu = snull_change_mtu;
856     dev->tx_timeout = snull_tx_timeout;
857     
858     /* keep the default flags, just add NOARP */
859     dev->flags           |= IFF_NOARP;
860     dev->features        |= NETIF_F_HW_CSUM;
861 
862     dev->hard_header_cache = NULL;
863 
864     /*
865      * Then, initialize the priv field. This encloses the statistics
866      * and a few private fields.
867      */
868     priv = netdev_priv(dev);
869     #if 0
870     if (use_napi) {
871         netif_napi_add(dev, &priv->napi, snull_poll,2);
872     } 
873     #else
874     if (use_napi) {
875         dev->poll = snull_poll;
876         dev->weight = 2;
877     }
878     #endif
879     memset(priv, 0, sizeof(struct snull_priv));
880     spin_lock_init(&priv->lock);
881     snull_rx_ints(dev, 1);        /* enable receive interrupts */
882     snull_setup_pool(dev);
883 
884     printk("snull_init\n");
885 }
886 
887 /*
888  * The devices
889  */
890 
891 struct net_device *snull_devs[2];
892 
893 
894 
895 /*
896  * Finally, the module stuff
897  */
898 
899 void snull_cleanup(void)
900 {
901     int i;
902     
903     for (i = 0; i < 2;  i++) {
904         if (snull_devs[i]) {
905             unregister_netdev(snull_devs[i]);
906             snull_teardown_pool(snull_devs[i]);
907             free_netdev(snull_devs[i]);
908         }
909     }
910     return;
911 }
912 
913 
914 
915 
916 int snull_init_module(void)
917 {
918     int result, i, ret = -ENOMEM;
919 
920     snull_interrupt = use_napi ? snull_napi_interrupt : snull_regular_interrupt;
921     
922     /* Allocate the devices */
923     snull_devs[0] = alloc_netdev(sizeof(struct snull_priv), "sn%d",
924             snull_init);
925     snull_devs[1] = alloc_netdev(sizeof(struct snull_priv), "sn%d",
926             snull_init);
927     if (snull_devs[0] == NULL || snull_devs[1] == NULL)
928         goto out;
929 
930     ret = -ENODEV;
931     for (i = 0; i < 2;  i++)
932         if ((result = register_netdev(snull_devs[i])))
933             printk("snull: error %i registering device \"%s\"\n",
934                     result, snull_devs[i]->name);
935         else
936             ret = 0;
937 
938     printk("snull_init_module\n");
939             
940    out:
941     if (ret) 
942         snull_cleanup();
943     return ret;
944 }
945 
946 
947 module_init(snull_init_module);
948 module_exit(snull_cleanup);


makefile:

# Comment/uncomment the following line to disable/enable debugging
#DEBUG = y


# Add your debugging flag (or not) to CFLAGS
ifeq ($(DEBUG),y)
  DEBFLAGS = -O -g -DSBULL_DEBUG # "-O" is needed to expand inlines
else
  DEBFLAGS = -O2
endif

EXTRA_CFLAGS += $(DEBFLAGS)
EXTRA_CFLAGS += -I..

ifneq ($(KERNELRELEASE),)
# call from kernel build system

obj-m    := snull.o

else

KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD       := $(shell pwd)

default:
    $(MAKE) -C $(KERNELDIR) M=$(PWD) modules

endif


運行:

# insmod snull.ko
snull_init
snull_init
snull_stats
snull_stats
snull_init_module
# ifconfig sn0 local0
snull_open
snull_stats
# ifconfig sn1 local1
snull_open
snull_stats
# ping -c 1 remote0
PING remote0 (192.168.2.9): 56 data bytes
len = 84
type = 800
h_proto = 8
addr_len = 6
dev_addr = 00.53.4e.55.4c.30
daddr = 00.53.4e.55.4c.30
h_source = 00.53.4e.55.4c.30
h_dest = 00.53.4e.55.4c.30
h_dest[ETH_ALEN-1] ^ 0x01 = 31
hard_header_len = 14
skb->len = 98
snull_tx
 00 53 4e 55 4c 31 00 53 4e 55 4c 30 08 00
 45 00 00 54 00 00 40 00 40 01 b5 47 c0 a8 02 08 c0 a8 02 09
 08 00 d0 0e 09 03 00 00 bc e8 62 05 00 00 00 00 00 00 00 00
 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
ih->protocol = 1 is buf[23]
saddr = 192.168.2.8
daddr = 192.168.2.9
c0a80308:02048 --> c0a80309:53262
snull_devs[0]
tx_buffer->datalen = 98
priv->status = 1
priv->status = 1
skb->dev is snull_devs[1]
skb->protocol = 8
snull_rx
snull_release_buffer
snull_regular_interrupt
snull_interrupt(0, dest, NULL);
priv->status = 2
snull_regular_interrupt
snull_interrupt(0, dev, NULL);
len = 84
type = 800
h_proto = 8
addr_len = 6
dev_addr = 00.53.4e.55.4c.31
daddr = 00.53.4e.55.4c.31
h_source = 00.53.4e.55.4c.31
h_dest = 00.53.4e.55.4c.31
h_dest[ETH_ALEN-1] ^ 0x01 = 30
hard_header_len = 14
skb->len = 98
snull_tx
 00 53 4e 55 4c 30 00 53 4e 55 4c 31 08 00
 45 00 00 54 a0 17 00 00 40 01 53 30 c0 a8 03 09 c0 a8 03 08
 00 00 d8 0e 09 03 00 00 bc e8 62 05 00 00 00 00 00 00 00 00
 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
ih->protocol = 1 is buf[23]
saddr = 192.168.3.9
daddr = 192.168.3.8
c0a80208:55310 <-- c0a80209:00000
snull_devs[1]
tx_buffer->datalen = 98
priv->status = 1
priv->status = 1
skb->dev is snull_devs[0]
skb->protocol = 8
snull_rx
snull_release_buffer
snull_regular_interrupt
snull_interrupt(0, dest, NULL);
priv->status = 2
snull_regular_interrupt
snull_interrupt(0, dev, NULL);
64 bytes from 192.168.2.9: seq=0 ttl=64 time=159.673 ms

--- remote0 ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 159.673/159.673/159.673 ms


分析現象:

1.當執行ping命令後,驅動首先會調用snull_header

int snull_header(struct sk_buff *skb, struct net_device *dev,
                unsigned short type, void *daddr, void *saddr,
                unsigned len)              
{
    struct ethhdr *eth = (struct ethhdr *)skb_push(skb,ETH_HLEN);

    printk("len = %d\n", len);

    printk("type = %02x\n", type); //ETH_P_IP    0x0800        /* Internet Protocol packet    */

    /* htons是將整型變量從主機字節順序轉變成網絡字節順序, 
     * 就是整數在地址空間存儲方式變為:高位字節存放在內存的低地址處
     */
    eth->h_proto = htons(type);
    printk("h_proto = %d\n", eth->h_proto);
    
    printk("addr_len = %d\n", dev->addr_len);
    printk("dev_addr = %02x.%02x.%02x.%02x.%02x.%02x\n", dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2], dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);

    if (saddr) {
        printk("saddr = %02x.%02x.%02x.%02x.%02x.%02x\n", *((unsigned char *)saddr + 0), *((unsigned char *)saddr + 1), *((unsigned char *)saddr + 2), *((unsigned char *)saddr + 3), *((unsigned char *)saddr + 4), *((unsigned char *)saddr + 5));
    }

    if (daddr) {
        printk("daddr = %02x.%02x.%02x.%02x.%02x.%02x\n", *((unsigned char *)daddr + 0), *((unsigned char *)daddr + 1), *((unsigned char *)daddr + 2), *((unsigned char *)daddr + 3), *((unsigned char *)daddr + 4), *((unsigned char *)daddr + 5));
    }

    /* [cgw]: 上層應用要發送數據時,通過下層添加硬件地址,才能決定發送到那個目標網卡
     */
    memcpy(eth->h_source, saddr ? saddr : dev->dev_addr, dev->addr_len);
    memcpy(eth->h_dest,   daddr ? daddr : dev->dev_addr, dev->addr_len);
    printk("h_source = %02x.%02x.%02x.%02x.%02x.%02x\n", eth->h_source[0], eth->h_source[1], eth->h_source[2],eth->h_source[3], eth->h_source[4], eth->h_source[5]);
    printk("h_dest = %02x.%02x.%02x.%02x.%02x.%02x\n", eth->h_dest[0], eth->h_dest[1], eth->h_dest[2], eth->h_dest[3], eth->h_dest[4], eth->h_dest[5]);

    /* [cgw]: 設置目標網卡硬件地址,即本地網卡和目標網卡硬件地址的最後一個字節的最低有效位
     * 是相反關系,即本地是\0SNUL0的話,目標就是\0SNUL1,或者本地是\0SNUL1,目標就是\0SNUL0
     */
    eth->h_dest[ETH_ALEN-1]   ^= 0x01;   /* dest is us xor 1 */
    printk("h_dest[ETH_ALEN-1] ^ 0x01 = %02x\n", eth->h_dest[ETH_ALEN-1]);
    
    printk("hard_header_len = %d\n", dev->hard_header_len);
    
    return (dev->hard_header_len);
}


因為應用層要發送數據包了,所以要為這個數據包添加硬件地址,即以太網地址首部,才能通過網卡發送出去。

2. 然後內核會通過調用snull_tx發送數據包,snull_tx調用了snull_hw_tx,在這裡更改本地IP為目標IP,並把本地要發的數據直接拷貝給目標網卡,代表目標網卡以接收到數據,並觸發接收完成中斷,向應用層上報數據,接著觸發發送完成中斷,表示數據已經發送到目標網卡。

3. 數據包分析:

static void snull_hw_tx(char *buf, int len, struct net_device *dev)

這裡的buf為應用層要發送的數據包,數據包格式為:14字節以太網首部+20字節IP地址首部+20字節TCP地址首部+n字節數據

 1 /* [cgw]: 14字節以太網首部 */
 2         for (i=0 ; i<14; i++)
 3             printk(" %02x",buf[i]&0xff);
 4         printk("\n");
 5 
 6         /* [cgw]: 20字節IP地址首部 */
 7         for (i=14 ; i<34; i++)
 8             printk(" %02x",buf[i]&0xff);
 9         printk("\n");
10 
11         /* [cgw]: 20字節TCP地址首部 */
12         for (i=34 ; i<54; i++)
13             printk(" %02x",buf[i]&0xff);
14         printk("\n");
15 
16         /* [cgw]: n字節數據 */
17         for (i=54 ; i<len; i++)
18             printk(" %02x",buf[i]&0xff);
19         printk("\n");

打印結果:

00 53 4e 55 4c 30 00 53 4e 55 4c 31 08 00                      //14字節以太網首部
 45 00 00 54 a0 17 00 00 40 01 53 30 c0 a8 03 09 c0 a8 03 08   //20字節IP地址首部
 00 00 d8 0e 09 03 00 00 bc e8 62 05 00 00 00 00 00 00 00 00   //20字節TCP地址首部
 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  //n字節數據

其中:00 53 4e 55 4c 30 就硬件地址\0SNUL0的ASCII碼,00 53 4e 55 4c 31 就硬件地址\0SNUL1的ASCII碼。
c0 a8 02 08表示本地IP地址local0:192.168.2.8, c0 a8 02 09表示本地IP地址remote0:192.168.2.9。

代表 00 53 4e 55 4c 31 00 53 4e 55 4c 30 08 00 的結構體是:

1 struct ethhdr {
2     unsigned char    h_dest[ETH_ALEN];    /* destination eth addr    */
3     unsigned char    h_source[ETH_ALEN];    /* source ether addr    */
4     __be16        h_proto;        /* packet type ID field    */
5 } __attribute__((packed));


即h_ptoto = 0x08 (0x0800,經過htons轉換為0x08)

代表45 00 00 54 00 00 40 00 40 01 b5 47 c0 a8 02 08 c0 a8 02 09的結構體是:

 1 struct iphdr {
 2 #if defined(__LITTLE_ENDIAN_BITFIELD)
 3     __u8    ihl:4,
 4         version:4;
 5 #elif defined (__BIG_ENDIAN_BITFIELD)
 6     __u8    version:4,
 7           ihl:4;
 8 #else
 9 #error    "Please fix <asm/byteorder.h>"
10 #endif
11     __u8    tos;
12     __be16    tot_len;
13     __be16    id;
14     __be16    frag_off;
15     __u8    ttl;
16     __u8    protocol;
17     __sum16    check;
18     __be32    saddr;
19     __be32    daddr;
20     /*The options start here. */
21 };


代表 08 00 d0 0e 09 03 00 00 bc e8 62 05 00 00 00 00 00 00 00 00 的結構體是:

 1 struct tcphdr {
 2     __be16    source;
 3     __be16    dest;
 4     __be32    seq;
 5     __be32    ack_seq;
 6 #if defined(__LITTLE_ENDIAN_BITFIELD)
 7     __u16    res1:4,
 8         doff:4,
 9         fin:1,
10         syn:1,
11         rst:1,
12         psh:1,
13         ack:1,
14         urg:1,
15         ece:1,
16         cwr:1;
17 #elif defined(__BIG_ENDIAN_BITFIELD)
18     __u16    doff:4,
19         res1:4,
20         cwr:1,
21         ece:1,
22         urg:1,
23         ack:1,
24         psh:1,
25         rst:1,
26         syn:1,
27         fin:1;
28 #else
29 #error    "Adjust your <asm/byteorder.h> defines"
30 #endif    
31     __be16    window;
32     __sum16    check;
33     __be16    urg_ptr;
34 };


NAPI

NAPI的全稱是“NEW API”。

要使用NAPI功能,只要在加載snull.ko的添加一句use_napi=1就行了

如:#insmod snull.ko use_napi=1

NAPI有什麼作用?

NAPI是一種使用輪詢(poll)的方式去接收數據。如當系統需要接收一大坨數據時,數據量比較大時,這個時候數據的接收就不應該在中斷中進行。即產生接收完成中斷後,立即禁止中斷,通知內核調用poll,輪詢接收數據,接收完成後,再使能接收中斷。這樣大大提高系統的性能。

在驅動初始化時:分配好poll函數

1 if (use_napi) {
2         dev->poll = snull_poll;
3         dev->weight = 2;
4     }


在接收中斷中

1 if (statusword & SNULL_RX_INTR) {
2         /* send it to snull_rx for handling */
3         pkt = priv->rx_queue;
4         if (pkt) {
5             priv->rx_queue = pkt->next;
6             /* [cgw]: 網卡接收到數據,上報給應用層 */
7             snull_rx(dev, pkt);
8         }
9     }

改為

1 if (statusword & SNULL_RX_INTR) {
2         snull_rx_ints(dev, 0);  /* Disable further interrupts */
3         //napi_schedule(&priv->napi);
4         netif_rx_schedule(dev);
5     }

在中斷中,直接通知內核調用snull_poll即可,snull_poll輪詢接收數據,並上報給應用層。

下一篇博客介紹DM9000網卡驅動。

http://xxxxxx/Linuxjc/1180963.html TechArticle

Copyright © Linux教程網 All Rights Reserved