查看: 860|回复: 0

linux内核编程:网络驱动snull

[复制链接]
  • TA的每日心情
    开心
    2017-6-14 10:10
  • 签到天数: 74 天

    连续签到: 1 天

    [LV.6]常住居民II

    发表于 2015-5-5 23:42:37 | 显示全部楼层 |阅读模式
    头文件
    1. /*
    2. * snull.h -- definitions for the network module
    3. *
    4. *
    5. */

    6. #ifndef __SNULL_H__
    7. #define __SNULL_H__


    8. #define SNULL_RX_INTR 0x0001
    9. #define SNULL_TX_INTR 0x0002
    10. #define SNULL_TIMEOUT 5

    11. struct snull_priv {
    12.     struct net_device_stats stats;
    13.     int status;
    14.     int rx_packetlen;
    15.     u8 *rx_packetdata;
    16.     int tx_packetlen;
    17.     u8 *tx_packetdata;
    18.     struct sk_buff *skb;
    19.     spinlock_t lock;
    20.     struct net_device *dev;
    21. };


    22. #endif
    复制代码
    驱动源码
    1. /*
    2. * name:snull.c
    3. * function:the Simple Network Utility
    4. * time:2012-2-22
    5. *
    6. * author:txgcwm
    7. * mail:txgcwm@163.com
    8. * reference:Linux设备驱动程序(第三版)
    9. */
    10. #ifndef __KERNEL__
    11. # define __KERNEL__
    12. #endif
    13. #ifndef MODULE
    14. # define MODULE
    15. #endif

    16. #include <linux/init.h>
    17. #include <linux/module.h>
    18. #include <linux/sched.h>
    19. #include <linux/kernel.h>
    20. #include <linux/slab.h>
    21. #include <linux/errno.h>
    22. #include <linux/types.h>
    23. #include <linux/interrupt.h>
    24. #include <linux/in.h>
    25. #include <linux/netdevice.h>
    26. #include <linux/etherdevice.h>
    27. #include <linux/ip.h>
    28. #include <linux/tcp.h>
    29. #include <linux/skbuff.h>
    30. #include <linux/in6.h>
    31. #include <asm/checksum.h>
    32. #include "snull.h"


    33. static int lockup = 0;
    34. module_param(lockup,int,0);
    35. static int timeout = SNULL_TIMEOUT;
    36. module_param(timeout,int,0);
    37. struct net_device *snull_devs[2];

    38. void snull_tx_timeout(struct net_device *dev);
    39.         


    40. int snull_open(struct net_device *dev)
    41. {
    42.     /*在接口能够和外界通讯之前,要将硬件(MAC)从硬件设备复制到dev->dev_addr。硬件地址可在打开期间拷贝到设备中。snull软件接口在open时赋予硬件地址--它其实使用了一个长度为ETH_ALEN的ASCII字符串作为假的硬件地址,其中ETH_ALEN是以太网硬件地址的长度*/
    43.     memcpy(dev->dev_addr, "\0SNUL0", ETH_ALEN);
    44.     if(dev == snull_devs[1])
    45.         dev->dev_addr[ETH_ALEN-1] ++;
    46.     netif_start_queue(dev);    //一旦准备好开始发送数据后,open方法还应该启动接口的传输队列,允许接口接受传输包。

    47.     return 0;
    48. }

    49. int snull_release(struct net_device *dev)
    50. {
    51.     netif_stop_queue(dev);    //在接口被关闭时,必须调用该函数,但该函数也可以用来临时停止传输。

    52.     return 0;
    53. }

    54. int snull_config(struct net_device *dev, struct ifmap *map)
    55. {
    56.     if (dev->flags & IFF_UP)
    57.         return -EBUSY;

    58.     if (map->base_addr != dev->base_addr)
    59.     {
    60.         printk(KERN_WARNING "snull: Can't change I/O address\n");
    61.         return -EOPNOTSUPP;
    62.     }

    63.     if (map->irq != dev->irq)
    64.     {
    65.         dev->irq = map->irq;
    66.     }

    67.     return 0;
    68. }

    69. void snull_rx(struct net_device *dev, int len, unsigned char *buf)
    70. {
    71.     struct sk_buff *skb;
    72.     struct snull_priv *priv = netdev_priv(dev);

    73.     skb = dev_alloc_skb(len+2);    //分配一个保存数据包的缓冲区
    74.     if (!skb)
    75.     {
    76.         printk("snull rx: low on mem - packet dropped\n");
    77.         priv->stats.rx_dropped++;
    78.         return;
    79.     }
    80.     skb_reserve(skb, 2);
    81.     memcpy(skb_put(skb, len), buf, len);    //skb_put函数刷新缓冲区内的数据末尾指针,并且返回新创建数据区的指针

    82.     skb->dev = dev;
    83.     skb->protocol = eth_type_trans(skb, dev);
    84.     skb->ip_summed = CHECKSUM_UNNECESSARY;
    85.     priv->stats.rx_packets++;
    86.     priv->stats.rx_bytes += len;
    87.     netif_rx(skb);    //将套接字缓冲区传递给上层软件处理

    88.     return;
    89. }

    90. void snull_interrupt(int irq, void *dev_id, struct pt_regs *regs)
    91. {
    92.     int statusword;
    93.     struct net_device *dev = (struct net_device *)dev_id;
    94.     struct snull_priv *priv = netdev_priv(dev);
    95.    
    96.     if (!dev)
    97.         return;

    98.     spin_lock(&priv->lock);
    99.     statusword = priv->status;
    100.     priv->status = 0;
    101.     if (statusword & SNULL_RX_INTR)
    102.     {
    103.         snull_rx(dev, priv->rx_packetlen, priv->rx_packetdata);
    104.     }
    105.     if (statusword & SNULL_TX_INTR)
    106.     {
    107.         priv->stats.tx_packets++;
    108.         priv->stats.tx_bytes += priv->tx_packetlen;
    109.         dev_kfree_skb(priv->skb);
    110.     }
    111.     spin_unlock(&priv->lock);

    112.     return;
    113. }

    114. void snull_hw_tx(char *buf, int len, struct net_device *dev)
    115. {
    116.     struct iphdr *ih;
    117.     struct net_device *dest;
    118.     struct snull_priv *priv = netdev_priv(dev);
    119.     u32 *saddr, *daddr;

    120.     if (len < sizeof(struct ethhdr) + sizeof(struct iphdr))
    121.     {
    122.         printk("snull: Hmm... packet too short (%i octets)\n",len);
    123.         return;
    124.     }

    125.     ih = (struct iphdr *)(buf+sizeof(struct ethhdr));
    126.     saddr = &ih->saddr;
    127.     daddr = &ih->daddr;
    128.     ((u8 *)saddr)[2] ^= 1;
    129.     ((u8 *)daddr)[2] ^= 1;
    130.     ih->check = 0;
    131.     ih->check = ip_fast_csum((unsigned char *)ih,ih->ihl);

    132.     if (dev == snull_devs[0])
    133.         printk(KERN_INFO"%08x:%05i --> %08x:%05i\n",
    134.                ntohl(ih->saddr),ntohs(((struct tcphdr *)(ih+1))->source),
    135.                ntohl(ih->daddr),ntohs(((struct tcphdr *)(ih+1))->dest));
    136.     else
    137.         printk(KERN_INFO"%08x:%05i <-- %08x:%05i\n",
    138.                ntohl(ih->daddr),ntohs(((struct tcphdr *)(ih+1))->dest),
    139.                ntohl(ih->saddr),ntohs(((struct tcphdr *)(ih+1))->source));

    140.     dest = snull_devs[0] + (dev==snull_devs[0] ? 1 : 0);
    141.     priv->status = SNULL_RX_INTR;
    142.     priv->rx_packetlen = len;
    143.     priv->rx_packetdata = buf;
    144.     snull_interrupt(0, dest, NULL);

    145.     priv->status = SNULL_TX_INTR;
    146.     priv->tx_packetlen = len;
    147.     priv->tx_packetdata = buf;
    148.     if (lockup && ((priv->stats.tx_packets + 1) % lockup) == 0)
    149.     {
    150.         netif_stop_queue(dev);
    151.         printk(KERN_INFO"Simulate lockup at %ld, txp %ld\n", jiffies,(unsigned long) priv->stats.tx_packets);
    152.     }
    153.     else
    154.         snull_interrupt(0, dev, NULL);

    155.     return;
    156. }

    157. int snull_tx(struct sk_buff *skb, struct net_device *dev)
    158. {
    159.     int len;
    160.     char *data;
    161.     struct snull_priv *priv = netdev_priv(dev);

    162.     len = skb->len < ETH_ZLEN ? ETH_ZLEN : skb->len;    //当所需传输的数据包长度小于介质所支持的最小长度时,需要小心处理。
    163.     data = skb->data;
    164.     dev->trans_start = jiffies;
    165.     priv->skb = skb;

    166.     snull_hw_tx(data, len, dev);

    167.     return 0;
    168. }

    169. void snull_tx_timeout (struct net_device *dev)
    170. {
    171.     struct snull_priv *priv = netdev_priv(dev);
    172.     printk(KERN_INFO"Transmit timeout at %ld, latency %ld\n", jiffies,jiffies - dev->trans_start);

    173.     priv->status = SNULL_TX_INTR;
    174.     snull_interrupt(0, dev, NULL);
    175.     priv->stats.tx_errors++;
    176.     netif_wake_queue(dev);

    177.     return;
    178. }

    179. int snull_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
    180. {
    181.     return 0;
    182. }

    183. struct net_device_stats *snull_stats(struct net_device *dev)
    184. {
    185.     struct snull_priv *priv = netdev_priv(dev);

    186.     return &priv->stats;
    187. }

    188. int snull_rebuild_header(struct sk_buff *skb)
    189. {
    190.     struct ethhdr *eth = (struct ethhdr *) skb->data;
    191.     struct net_device *dev = skb->dev;
    192.    
    193.     memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
    194.     memcpy(eth->h_dest, dev->dev_addr, dev->addr_len);
    195.     eth->h_dest[ETH_ALEN-1] ^= 0x01;

    196.     return 0;
    197. }

    198. int snull_header(struct sk_buff *skb, struct net_device *dev,
    199.                 unsigned short type, const void *daddr, const void *saddr,
    200.                 unsigned int len)
    201. {
    202.     struct ethhdr *eth = (struct ethhdr *)skb_push(skb,ETH_HLEN);

    203.     eth->h_proto = htons(type);
    204.     memcpy(eth->h_source, saddr ? saddr : dev->dev_addr, dev->addr_len);
    205.     memcpy(eth->h_dest, daddr ? daddr : dev->dev_addr, dev->addr_len);
    206.     eth->h_dest[ETH_ALEN-1] ^= 0x01;

    207.     return (dev->hard_header_len);
    208. }

    209. int snull_change_mtu(struct net_device *dev, int new_mtu)
    210. {
    211.     unsigned long flags;
    212.     struct snull_priv *priv = netdev_priv(dev);
    213.     spinlock_t *lock = &((struct snull_priv *)priv)->lock;

    214.     if ((new_mtu < 68) || (new_mtu > 1500))    //以太网的MTU是1500个octet(ETH_DATA_LEN)
    215.         return -EINVAL;
    216.   
    217.     spin_lock_irqsave(lock, flags);
    218.     dev->mtu = new_mtu;
    219.     spin_unlock_irqrestore(lock, flags);

    220.     return 0;
    221. }

    222. //以下两个结构是相关接口函数的初始化
    223. static const struct net_device_ops snull_dev_ops = {
    224.     .ndo_open = snull_open,
    225.     .ndo_stop = snull_release,
    226.     .ndo_set_config = snull_config,
    227.     .ndo_start_xmit = snull_tx,
    228.     .ndo_do_ioctl = snull_ioctl,
    229.     .ndo_get_stats = snull_stats,
    230.     .ndo_change_mtu = snull_change_mtu,
    231.     .ndo_tx_timeout = snull_tx_timeout,
    232. };

    233. static const struct header_ops snull_header_ops= {
    234.     .create    = snull_header,
    235.     .rebuild = snull_rebuild_header,
    236.     .cache = NULL,
    237. };

    238. void snull_init(struct net_device *dev)
    239. {
    240.     struct snull_priv *priv = NULL;

    241.     ether_setup(dev); //对某些成员作一些初始化
    242.    
    243.     dev->netdev_ops = &snull_dev_ops;
    244.     dev->header_ops = &snull_header_ops;
    245.     dev->watchdog_timeo = timeout;
    246.     dev->flags |= IFF_NOARP;    //禁止ARP
    247.       dev->features |= NETIF_F_NO_CSUM;

    248.     priv = netdev_priv(dev);    //priv是net_device中的一个成员,它与net_device一起被分配,当需要访问私有成员priv时使用该函数
    249.     memset(priv, 0, sizeof(struct snull_priv));
    250.     priv->dev = dev;
    251.     spin_lock_init(&((struct snull_priv *)priv)->lock);

    252.     return;
    253. }

    254. static __init int snull_init_module(void)
    255. {
    256.     int result, i, device_present = 0;

    257.     /*alloc_netdev用来动态分配struct net_device内核数据结构,第一个参数为驱动程序的“私有数据“,第二个是接口的名字,第三个是用来设置net_device结构的初始化函数。对于设备名称,如果驱动程序设置的名称中包含%d格式化字符串,register_netdev将使用一个数字代替它,使之成为唯一的名字,分配的编号从零开始。*/
    258.     snull_devs[0] = alloc_netdev(sizeof(struct snull_priv),"sn%d",snull_init);
    259.     snull_devs[1] = alloc_netdev(sizeof(struct snull_priv),"sn%d",snull_init);
    260.     if(snull_devs[0] == NULL || snull_devs[1] == NULL)
    261.         goto fail;

    262.     for (i=0;i<2;i++)
    263.     {
    264.         //注册设备,必须在初始化一切事情后再注册
    265.         if((result = register_netdev(snull_devs[i])))
    266.             printk("snull: error %i registering device \"%s\"\n",result, snull_devs[i]->name);
    267.         else
    268.             device_present++;
    269.     }

    270.   fail:
    271.     return device_present ? 0 : -ENODEV;
    272. }

    273. static __exit void snull_cleanup(void)
    274. {
    275.     int i;
    276.    
    277.     for (i=0; i<2;i++)
    278.     {
    279.         if(snull_devs[i])
    280.         {            
    281.             unregister_netdev(snull_devs[i]);    //从系统中删除接口
    282.             free_netdev(snull_devs[i]);    //将net_device结构返还给系统
    283.         }   
    284.     }
    285.     return;
    286. }


    287. module_init(snull_init_module);
    288. module_exit(snull_cleanup);

    289. MODULE_AUTHOR("txgcwm");
    290. MODULE_VERSION("snull_v1.0");
    291. MODULE_LICENSE("GPL");
    复制代码
    Makefile
    1. ifneq ($(KERNELRELEASE),)
    2.     obj-m := snull.o
    3. else
    4.     KERNELDIR ?= /lib/modules/$(shell uname -r)/build
    5.     PWD := $(shell pwd)
    6. all:
    7.     make -C $(KERNELDIR) M=$(PWD) modules
    8. clean:
    9.     rm -f *.ko *.o *.mod.o *.mod.c *.symvers *.order
    10. endif
    复制代码
    加载文件
    1. #!/bin/sh

    2. export PATH=/sbin:/bin

    3. # Use a pathname, as new modutils don't look in the current dir by default
    4. insmod ./snull.ko $*
    5. ifconfig sn0 local0
    6. ifconfig sn1 local1

    7. # The route commands are needed for 2.0, not for later kernels
    8. case "`uname -r`" in
    9.     2.0.*)
    10.         route add -net snullnet0 sn0
    11.         route add -net snullnet1 sn1
    12.         ;;
    13. esac
    复制代码
    卸载文件
    1. #!/bin/sh

    2. /sbin/ifconfig sn0 down
    3. /sbin/ifconfig sn1 down
    4. /sbin/rmmod snull
    复制代码

    点评

    楼主写的不错!: 5.0 我很赞同!: 5.0
    楼主写的不错!: 5 我很赞同!: 5
    嵌入式大神。。。  发表于 2015-5-6 08:10

    评分

    参与人数 1黑豆 +2 收起 理由
    与你同在 + 2 支持楼主,发布更多好的帖子!

    查看全部评分

    您需要登录后才可以回帖 登录 | 注册

    本版积分规则

    站长推荐上一条 /1 下一条