• 网卡驱动框架


    以虚拟网卡驱动为例

    框架入口源文件:vir_net.c

    (可根据入口源文件,再按着框架到内核走一遍)

    内核版本:linux_2.6.22.6 硬件平台:JZ2440

     以下是驱动框架:

    以下是驱动代码 vir_net.c :

    #include <linux/module.h>
    #include <linux/errno.h>
    #include <linux/netdevice.h>
    #include <linux/etherdevice.h>
    #include <linux/kernel.h>
    #include <linux/types.h>
    #include <linux/fcntl.h>
    #include <linux/interrupt.h>
    #include <linux/ioport.h>
    #include <linux/in.h>
    #include <linux/skbuff.h>
    #include <linux/slab.h>
    #include <linux/spinlock.h>
    #include <linux/string.h>
    #include <linux/init.h>
    #include <linux/bitops.h>
    #include <linux/delay.h>
    #include <linux/ip.h>
    
    #include <asm/system.h>
    #include <asm/io.h>
    #include <asm/irq.h>
    
    
    //定义一个 device_net结构体
    static struct net_device *s3c_vir_net;
    
    //netif_rx
    static int sent_up_sk_buff(struct sk_buff *skb,struct net_device *dev)  //copy frome LLD3
    {
            unsigned char *type;
            struct iphdr *ih;
            __be32 *saddr, *daddr, tmp;
            unsigned char    tmp_dev_addr[ETH_ALEN];
            struct ethhdr *ethhdr;
            
            struct sk_buff *rx_skb;
                
            // 从硬件读出/保存数据
            /* 对调"源/目的"的mac地址 */
            ethhdr = (struct ethhdr *)skb->data;
            memcpy(tmp_dev_addr, ethhdr->h_dest, ETH_ALEN);
            memcpy(ethhdr->h_dest, ethhdr->h_source, ETH_ALEN);
            memcpy(ethhdr->h_source, tmp_dev_addr, ETH_ALEN);
        
            /* 对调"源/目的"的ip地址 */    
            ih = (struct iphdr *)(skb->data + sizeof(struct ethhdr));
            saddr = &ih->saddr;
            daddr = &ih->daddr;
        
            tmp = *saddr;
            *saddr = *daddr;
            *daddr = tmp;
            
            //((u8 *)saddr)[2] ^= 1; /* change the third octet (class C) */
            //((u8 *)daddr)[2] ^= 1;
            type = skb->data + sizeof(struct ethhdr) + sizeof(struct iphdr);
            //printk("tx package type = %02x
    ", *type);
            
            // 修改类型, 原来0x8表示ping
            *type = 0; /* 0表示reply */
            
            ih->check = 0;           /* and rebuild the checksum (ip needs it) */
            ih->check = ip_fast_csum((unsigned char *)ih,ih->ihl);
            
            // 构造一个sk_buff
            rx_skb = dev_alloc_skb(skb->len + 2);
            skb_reserve(rx_skb, 2); /* align IP on 16B boundary */    
            memcpy(skb_put(rx_skb, skb->len), skb->data, skb->len);
        
            /* Write metadata, and then pass to the receive level */
            rx_skb->dev = dev;
            rx_skb->protocol = eth_type_trans(rx_skb, dev);
            rx_skb->ip_summed = CHECKSUM_UNNECESSARY; /* don't check it */
            dev->stats.rx_packets++;
            dev->stats.rx_bytes += skb->len;
        
            // 提交sk_buff
            netif_rx(rx_skb);
    }
    
    
    
    //hard_start_xmit
    static int receive_sk_buff(struct sk_buff *vir_sk_buff,struct net_device *vir_net_device)
    {
       //发包计数
       static int cnt=0;
       printk("the number of sent pack is : %d 
    ",++cnt);
    
       //停止队列      发送sk_buff包到网卡中
        netif_stop_queue(vir_net_device);
    
       //从网卡中直接读取数据sk_buff,通过netif_rx发送给上层网络结构,(这里自己构造sk_buff包上报)
        sent_up_sk_buff(vir_sk_buff,vir_net_device);
    
       //清除sk_buff
        dev_kfree_skb(vir_sk_buff);
    
       //恢复队列
        netif_wake_queue(vir_net_device);
    
       //设置统计信息       包个数....
        vir_net_device->stats.tx_packets++;
        vir_net_device->stats.tx_bytes+=vir_sk_buff->len;
    
      return 0;
    }
    
    
    
    static int vir_net_init(void)
    {
       //分配一个device_net结构体
       s3c_vir_net = alloc_netdev(0,"fake_net", ether_setup);
       
       //设置结构体函数         MAC地址    ping功能
       s3c_vir_net->hard_start_xmit = receive_sk_buff;
    
       s3c_vir_net->dev_addr[0] =0x08;
       s3c_vir_net->dev_addr[1] =0x89;
       s3c_vir_net->dev_addr[2] =0x89;
       s3c_vir_net->dev_addr[3] =0x89;
       s3c_vir_net->dev_addr[4] =0x89;
       s3c_vir_net->dev_addr[5] =0x11;
    
       s3c_vir_net->flags    |= IFF_NOARP;
       s3c_vir_net->features |= NETIF_F_NO_CSUM;    
    
       //注册结构体
       register_netdev(s3c_vir_net);
       return 0;
    }
    
    static void vir_net_exit(void)
    {
       unregister_netdev(s3c_vir_net);
       free_netdev(s3c_vir_net);
    }
    
    module_init(vir_net_init);
    module_exit(vir_net_exit);
    
    MODULE_LICENSE("GPL");

    以下是编译驱动的Makefile:

    KER_DIR=/work/systems/kernel/linux-2/linux-2.6.22.6
    all:
        make -C $(KER_DIR) M=`pwd` modules
    clean:
        make -C $(KER_DIR) M=`pwd` modules clean
        rm -fr modules.order
    
    obj-m += vir_net.o
  • 相关阅读:
    java8学习之Collector复合与注意事项
    动画学习之WIFI图形绘制
    java线程基础巩固---多线程死锁分析,案例介绍
    java8学习之Collector同一性与结合性分析
    java8学习之Collector源码分析与收集器核心
    java8学习之Stream分组与分区详解
    kotlin面向对象之抽象类、继承、多态
    matplotlib-曲线和折线案例
    人口、人口密度分析项目-条形图
    开机自启:bat实现一次性打开win7中的常用软件和文件夹
  • 原文地址:https://www.cnblogs.com/zsy12138/p/10392774.html
Copyright © 2020-2023  润新知