Linux 网卡驱动学习(一)(分析一个虚拟硬件的网络驱动例子)

在Linux,网络分为两个层,分别是网络堆栈协议支持层,以及接收和发送网络协议的设备驱动程序层。网络堆栈是硬件中独立出来的部分,主要用来支持TCP/IP等多种协议,网络设备驱动层是连接网络堆栈协议层和网络硬件的中间层。

网络设备驱动程序的主要功能是:

(1)模块加载或内核启动相关的初始化处理

(2)清除模块时的处理

(3)网络设备的检索和探测

(4)网络设备的初始化和注册

(5)打开或关闭网络设备

(6)发送网络数据

(7)接收网络数据

(8)中断处理(在发送完数据时,硬件向内核产生一个中断,告诉内核数据已经发送完毕,在网络设备接收到数据时,也要发生一个中断,告诉内核,数据已经到达,请及时处理)

(9)超时处理

(10)多播处理

(11)网络设备的控制ioctl

而Linux网络设备驱动的主要功能就是网络设备的初始化,网络设备的配置,数据包的收发。

下面代码是关于虚拟硬件的网络驱动的例子

2、代码

  1. #undef PDEBUG             /* undef it, just in case */  
  2. #ifdef SNULL_DEBUG  
  3. #  ifdef __KERNEL__  
  4.      /* This one if debugging is on, and kernel space */  
  5. #    define PDEBUG(fmt, args...) printk( KERN_DEBUG "snull: " fmt, ## args)  
  6. #  else  
  7.      /* This one for user space */  
  8. #    define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args)  
  9. #  endif  
  10. #else  
  11. #  define PDEBUG(fmt, args...) /* not debugging: nothing */  
  12. #endif  
  13. #undef PDEBUGG  
  14. #define PDEBUGG(fmt, args...) /* nothing: it's a placeholder */  
  15.   
  16. /* These are the flags in the statusword */  
  17. #define SNULL_RX_INTR 0x0001  
  18. #define SNULL_TX_INTR 0x0002  
  19. /* Default timeout period */  
  20. #define SNULL_TIMEOUT 6   /* In jiffies */  
  21. #include <linux/module.h>  
  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. #include <linux/in.h>  
  29. #include <linux/netdevice.h>   /* struct device, and other headers */  
  30. #include <linux/etherdevice.h> /* eth_type_trans */  
  31. #include <linux/ip.h>          /* struct iphdr */  
  32. #include <linux/tcp.h>         /* struct tcphdr */  
  33. #include <linux/skbuff.h>  
  34. #include <linux/if_ether.h>  
  35. #include <linux/in6.h>  
  36. #include <asm/uaccess.h>  
  37. #include <asm/checksum.h>  
  38.   
  39. static int lockup = 0;  
  40. static int timeout = SNULL_TIMEOUT;  
  41. struct net_device snull_devs[2];//这里定义两个设备,一个是snull0,一个是snull1  
  42. //网络设备结构体,作为net_device->priv  
  43. struct snull_priv {  
  44.     struct net_device_stats stats;//有用的统计信息  
  45.     int status;//网络设备的状态信息,是发完数据包,还是接收到网络数据包  
  46.     int rx_packetlen;//接收到的数据包长度  
  47.     u8 *rx_packetdata;//接收到的数据  
  48.     int tx_packetlen;//发送的数据包长度  
  49.     u8 *tx_packetdata;//发送的数据  
  50.     struct sk_buff *skb;//socket buffer结构体,网络各层之间传送数据都是通过这个结构体来实现的  
  51.     spinlock_t lock;//自旋锁  
  52. };  
  53.   
  54. void snull_tx_timeout (struct net_device *dev);  
  55.   
  56. //网络接口的打开函数  
  57. int snull_open(struct net_device *dev)  
  58.  {  
  59.     printk("call snull_open/n");  
  60.     memcpy(dev->dev_addr, "/0SNUL0", ETH_ALEN);//分配一个硬件地址,ETH_ALEN是网络设备硬件地址的长度  
  61.      
  62.     netif_start_queue(dev);//打开传输队列,这样才能进行数据传输  
  63.   
  64.     return 0;  
  65. }  
  66. int snull_release(struct net_device *dev)  
  67. {  
  68.     printk("call snull_release/n");  
  69.     netif_stop_queue(dev); //当网络接口关闭的时候,调用stop方法,这个函数表示不能再发送数据  
  70.     return 0;  
  71. }  
  72.   
  73. //接包函数  
  74. void snull_rx(struct net_device *dev, int len, unsigned char *buf)  
  75. {  
  76.      
  77.     struct sk_buff *skb;  
  78.     struct snull_priv *priv = (struct snull_priv *) dev->priv;  
  79.    
  80.     /* 
  81.      * The packet has been retrieved from the transmission 
  82.      * medium. Build an skb around it, so upper layers can handle it 
  83.      */  
  84.   
  85.   
  86.     skb = dev_alloc_skb(len+2);//分配一个socket buffer,并且初始化skb->data,skb->tail和skb->head  
  87.     if (!skb) {  
  88.         printk("snull rx: low on mem - packet dropped/n");  
  89.         priv->stats.rx_dropped++;  
  90.         return;  
  91.     }  
  92.     skb_reserve(skb, 2); /* align IP on 16B boundary */   
  93.     memcpy(skb_put(skb, len), buf, len);//skb_put是把数据写入到socket buffer  
  94.     /* Write metadata, and then pass to the receive level */  
  95.     skb->dev = dev;  
  96.     skb->protocol = eth_type_trans(skb, dev);//返回的是协议号  
  97.     skb->ip_summed = CHECKSUM_UNNECESSARY; //此处不校验  
  98.     priv->stats.rx_packets++;//接收到包的个数+1  
  99.       
  100.     priv->stats.rx_bytes += len;//接收到包的长度  
  101.     netif_rx(skb);//通知内核已经接收到包,并且封装成socket buffer传到上层  
  102.     return;  
  103. }  
  104.      
  105.          
  106. /* 
  107.  * The typical interrupt entry point 
  108.  */  
  109. //中断处理,此程序中没有硬件,因此,没有真正的硬件中断,只是模拟中断,在发送完网络数据包之后,会产生中断  
  110. //用来通知内核已经发送完数据包,当新的数据包到达网络接口时,会发生中断,通知新的数据包已经到来了  
  111. void snull_interrupt(int irq, void *dev_id, struct pt_regs *regs)  
  112. {  
  113.     
  114.     int statusword;//用来标识是发送完毕还是接收到新的数据包  
  115.     struct snull_priv *priv;  
  116.     /* 
  117.      * As usual, check the "device" pointer for shared handlers. 
  118.      * Then assign "struct device *dev" 
  119.      */  
  120.     struct net_device *dev = (struct net_device *)dev_id;  
  121.     /* ... and check with hw if it's really ours */  
  122.     if (!dev /*paranoid*/ ) return;  
  123.     /* Lock the device */  
  124.     priv = (struct snull_priv *) dev->priv;  
  125.     spin_lock(&priv->lock);  
  126.     /* retrieve statusword: real netdevices use I/O instructions */  
  127.     statusword = priv->status;  
  128.     if (statusword & SNULL_RX_INTR) {//如果是接收  
  129.         /* send it to snull_rx for handling */  
  130.         snull_rx(dev, priv->rx_packetlen, priv->rx_packetdata);  
  131.     }  
  132.     if (statusword & SNULL_TX_INTR) {//如果发送完毕  
  133.         /* a transmission is over: free the skb */  
  134.         priv->stats.tx_packets++;  
  135.         priv->stats.tx_bytes += priv->tx_packetlen;  
  136.         dev_kfree_skb(priv->skb);//释放skb 套接字缓冲区  
  137.     }  
  138.     /* Unlock the device and we are done */  
  139.     spin_unlock(&priv->lock);  
  140.     return;  
  141. }  
  142.   
  143. /* 
  144.  * Transmit a packet (low level interface) 
  145.  */  
  146. //真正的处理的发送数据包  
  147. //模拟从一个网络向另一个网络发送数据包  
  148. void snull_hw_tx(char *buf, int len, struct net_device *dev)  
  149.   
  150. {  
  151.   
  152.    /* 
  153.      * This function deals with hw details. This interface loops 
  154.      * back the packet to the other snull interface (if any). 
  155.      * In other words, this function implements the snull behaviour, 
  156.      * while all other procedures are rather device-independent 
  157.      */  
  158.     struct iphdr *ih;//ip头部  
  159.     struct net_device *dest;//目标设备结构体,net_device存储一个网络接口的重要信息,是网络驱动程序的核心  
  160.     struct snull_priv *priv;  
  161.     u32 *saddr, *daddr;//源设备地址与目标设备地址  
  162.     /* I am paranoid. Ain't I? */  
  163.     if (len < sizeof(struct ethhdr) + sizeof(struct iphdr)) {  
  164.         printk("snull: Hmm... packet too short (%i octets)/n",  
  165.                len);  
  166.         return;  
  167.     }  
  168.   
  169.     /* 
  170.      * Ethhdr is 14 bytes, but the kernel arranges for iphdr 
  171.      * to be aligned (i.e., ethhdr is unaligned) 
  172.      */  
  173.     ih = (struct iphdr *)(buf+sizeof(struct ethhdr));  
  174.     saddr = &ih->saddr;  
  175.     daddr = &ih->daddr;  
  176.     //在同一台机器上模拟两个网络,不同的网段地址,进行发送网络数据包与接收网络数据包  
  177.     ((u8 *)saddr)[2] ^= 1; /* change the third octet (class C) ^是位异或操作符把第三个部分的网络地址与1进行异或,由于同一网络的数据不进行转发*/   
  178.     ((u8 *)daddr)[2] ^= 1;  
  179.     ih->check = 0;         /* and rebuild the checksum (ip needs it) */  
  180.     ih->check = ip_fast_csum((unsigned char *)ih,ih->ihl);  
  181.     if (dev == snull_devs)  
  182.         PDEBUGG("%08x:%05i --> %08x:%05i/n",  
  183.                ntohl(ih->saddr),ntohs(((struct tcphdr *)(ih+1))->source),  
  184.                ntohl(ih->daddr),ntohs(((struct tcphdr *)(ih+1))->dest));  
  185.     else  
  186.         PDEBUGG("%08x:%05i <-- %08x:%05i/n",  
  187.                ntohl(ih->daddr),ntohs(((struct tcphdr *)(ih+1))->dest),  
  188.                ntohl(ih->saddr),ntohs(((struct tcphdr *)(ih+1))->source));  
  189.   
  190.     /* 
  191.      * Ok, now the packet is ready for transmission: first simulate a 
  192.      * receive interrupt on the twin device, then  a 
  193.      * transmission-done on the transmitting device 
  194.      */  
  195.     dest = snull_devs + (dev==snull_devs ? 1 : 0);//如果dev是0,那么dest就是1,如果dev是1,那么dest是0  
  196.     priv = (struct snull_priv *) dest->priv;//目标dest中的priv  
  197.     priv->status = SNULL_RX_INTR;  
  198.     priv->rx_packetlen = len;  
  199.     priv->rx_packetdata = buf;  
  200.     snull_interrupt(0, dest, NULL);  
  201.     priv = (struct snull_priv *) dev->priv;  
  202.     priv->status = SNULL_TX_INTR;  
  203.     priv->tx_packetlen = len;  
  204.     priv->tx_packetdata = buf;  
  205.     if (lockup && ((priv->stats.tx_packets + 1) % lockup) == 0) {  
  206.         /* Simulate a dropped transmit interrupt */  
  207.         netif_stop_queue(dev);  
  208.   
  209.     
  210.         PDEBUG("Simulate lockup at %ld, txp %ld/n", jiffies,  
  211.                         (unsigned long) priv->stats.tx_packets);  
  212.     }  
  213.     else  
  214.         snull_interrupt(0, dev, NULL);  
  215. }  
  216.    
  217.   
  218. /* 
  219.  * Transmit a packet (called by the kernel) 
  220.  */  
  221.   
  222. //发包函数  
  223. int snull_tx(struct sk_buff *skb, struct net_device *dev)  
  224. {  
  225.     
  226.     int len;  
  227.     char *data;  
  228.     struct snull_priv *priv = (struct snull_priv *) dev->priv;  
  229.   
  230.     if ( skb == NULL) {  
  231.         PDEBUG("tint for %p,  skb %p/n", dev,  skb);  
  232.         snull_tx_timeout (dev);  
  233.         if (skb == NULL)  
  234.             return 0;  
  235.     }  
  236.   
  237.     len = skb->len < ETH_ZLEN ? ETH_ZLEN : skb->len;//ETH_ZLEN是所发的最小数据包的长度  
  238.     data = skb->data;//将要发送的数据包中数据部分  
  239.     dev->trans_start = jiffies; //保存当前的发送时间  
  240.     priv->skb = skb;  
  241.     snull_hw_tx(data, len, dev);//真正的发送函数  
  242.    return 0; /* Our simple device can not fail */  
  243. }  
  244. /* 
  245.  * Deal with a transmit timeout. 
  246.  */  
  247.   
  248. //一旦超出watchdog_timeo就会调用snull_tx_timeout  
  249. void snull_tx_timeout (struct net_device *dev)  
  250. {  
  251.    printk("call snull_tx_timeout/n");  
  252.     struct snull_priv *priv = (struct snull_priv *) dev->priv;  
  253.     PDEBUG("Transmit timeout at %ld, latency %ld/n", jiffies,  
  254.                     jiffies - dev->trans_start);  
  255.     priv->status = SNULL_TX_INTR;  
  256.     snull_interrupt(0, dev, NULL);//超时后发生中断  
  257.     priv->stats.tx_errors++;//发送的错误数  
  258.     netif_wake_queue(dev); //为了再次发送数据,调用此函数,重新启动发送队列  
  259.     return;  
  260. }  
  261.    
  262. /* 
  263.  * Ioctl commands 
  264.  */  
  265. int snull_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)  
  266. {  
  267.    
  268.     PDEBUG("ioctl/n");  
  269.     return 0;  
  270. }  
  271. /* 
  272.  * Return statistics to the caller 
  273.  */  
  274. struct net_device_stats *snull_stats(struct net_device *dev)  
  275. {  
  276.     struct snull_priv *priv = (struct snull_priv *) dev->priv;  
  277.     return &priv->stats;//得到统计资料信息  
  278. }  
  279.   
  280.   
  281. //设备初始化函数  
  282. int snull_init(struct net_device *dev)  
  283. {  
  284.    printk("call snull_init/n");  
  285.   
  286.     /* 
  287.      * Then, assign other fields in dev, using ether_setup() and some 
  288.      * hand assignments 
  289.      */  
  290.     ether_setup(dev);//填充一些以太网中的设备结构体的项  
  291.     dev->open            = snull_open;  
  292.     dev->stop            = snull_release;  
  293.     //dev->set_config      = snull_config;  
  294.     dev->hard_start_xmit = snull_tx;  
  295.     dev->do_ioctl        = snull_ioctl;  
  296.     dev->get_stats       = snull_stats;  
  297.     //dev->change_mtu      = snull_change_mtu;   
  298.    // dev->rebuild_header  = snull_rebuild_header;  
  299.     //dev->hard_header     = snull_header;  
  300.   
  301.     dev->tx_timeout     = snull_tx_timeout;//超时处理  
  302.     dev->watchdog_timeo = timeout;  
  303.   
  304.     /* keep the default flags, just add NOARP */  
  305.     dev->flags           |= IFF_NOARP;  
  306.     dev->hard_header_cache = NULL;      /* Disable caching */  
  307.     SET_MODULE_OWNER(dev);  
  308.     /* 
  309.      * Then, allocate the priv field. This encloses the statistics 
  310.      * and a few private fields. 
  311.      */  
  312. //为priv分配内存  
  313.     dev->priv = kmalloc(sizeof(struct snull_priv), GFP_KERNEL);  
  314.   if (dev->priv == NULL)  
  315.        return -ENOMEM;  
  316.     memset(dev->priv, 0, sizeof(struct snull_priv));  
  317.     spin_lock_init(& ((struct snull_priv *) dev->priv)->lock);  
  318.     return 0;  
  319. }  
  320.   
  321.   
  322. struct net_device snull_devs[2] = {  
  323.     { init: snull_init, },  /* init, nothing more */  
  324.     { init: snull_init, }  
  325. };  
  326.   
  327. int snull_init_module(void)  
  328. {  
  329.    int i,result=0;  
  330.    strcpy(snull_devs[0].name,"snull0");//net_device结构体中的name表示设备名  
  331.    strcpy(snull_devs[1].name,"snull1");//即定义了两个设备,snull0与snull1  
  332.     for (i=0; i<2;  i++)  
  333.         if ( (result = register_netdev(snull_devs+i)) )//注册设备  
  334.             printk("snull: error %i registering device /"%s/"/n",  
  335.                    result, snull_devs[i].name);  
  336.      return 0;  
  337. }  
  338. void snull_cleanup(void)  
  339. {  
  340.     int i;  
  341.    
  342.     for (i=0; i<2;  i++) {  
  343.         kfree(snull_devs[i].priv);  
  344.         unregister_netdev(snull_devs+i);  
  345.     }  
  346.     return;  
  347. }  
  348.   
  349. module_init(snull_init_module);  
  350. module_exit(snull_cleanup); 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值