• 一步步写网卡驱动()


            最近一直在看linux网络相关的东西, 做为提高准备自己动手写个网卡驱动, 手上刚好有一块mini2440, 所以准备以mini2440的DM9000下刀。当然本人也是第一次写网卡驱动, 所以希望大家看到不足的地方多海函和指教。


            今天, 我们先搭一个网卡驱动的框架, 并不设计实际硬件。


            首先先包含今天会用到的必要的头文件:

    #include <linux/module.h>
    #include <linux/init.h>
    #include <linux/platform_device.h>
    #include <linux/netdevice.h>
    #include <linux/etherdevice.h>
    

            定义调试和打印用的宏:

    #undef LYDM9K_DEBUG
    #define LYDM9K_DEBUG
    #ifdef LYDM9K_DEBUG
    #define printdbg(fmt, args...) \
        printk("lydm9k[%s]:\t" fmt, __func__, ##args)
    #else
    #define printdbg(fmt, args...) \
        do {} while(0)
    #endif /* LYDM9K_DEBUG */
    
    #define dm9kmsg(fmt, args...) \
        printk("lydm9k[%s]:\t" fmt, __func__, ##args)
    

            编写module注册函数和卸载函数:

    static int __init lydm9k_init(void)
    {
        dm9kmsg("insert module lydm9k\n");
        platform_device_register(&lydm9k_device);
        return platform_driver_register(&lydm9k_driver);
    }
    
    static void __exit lydm9k_exit(void)
    {
        platform_device_unregister(&lydm9k_device);
        platform_driver_unregister(&lydm9k_driver);
        dm9kmsg("remove module lydm9k\n");
    }
    
    MODULE_LICENSE("GPL");
    module_init(lydm9k_init);
    module_exit(lydm9k_exit);

            初始化网卡的device结构体:

    struct lydm9k_plat_data {
        unsigned char mac[6];
        unsigned int watchdog_timeo_msecs;
    };
    
    static struct lydm9k_plat_data lydm9k_plat_data = {
        .mac            = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06},
        .watchdog_timeo_msecs = 5000,
    };
    
    static struct resource lydm9k_resource[] = {
    };
    
    static void lydm9k_device_release(struct device *dev)
    {
        printdbg("invoked\n");
    }
    
    static struct platform_device lydm9k_device = {
        .name           = "lydm9k",
        .id             = -1,
        .num_resources  = ARRAY_SIZE(lydm9k_resource),
        .resource       = lydm9k_resource,
        .dev            = {
            .platform_data  = &lydm9k_plat_data,
            .release    = lydm9k_device_release,
        },
    };
    


            初始化网卡的driver结构体:

    static struct platform_driver lydm9k_driver = {
        .driver         = {
            .name       = "lydm9k",
            .owner      = THIS_MODULE,
        },
        .probe          = lydm9k_probe,
        .remove         = __devexit_p(lydm9k_remove),
    };
    

            网卡驱动的probe函数和remove函数:

    static int __devinit lydm9k_probe(struct platform_device *pdev)
    {
        int ret;
        struct net_device *ndev;
        struct lydm9k_priv *priv;
        struct lydm9k_plat_data *pdata = pdev->dev.platform_data;
    
        printdbg("detect a dm9k device\n");
    
        // 申请ndev空间
        ndev = alloc_etherdev(sizeof(struct lydm9k_priv));
        if (NULL == ndev)
        {
            dm9kmsg("fail to alloc_etherdev\n");
            return -ENOMEM;
        }
    
        // 设置ndev的福设备为pdev
        SET_NETDEV_DEV(ndev, &pdev->dev);
    
        // 设置ndev和pdev直接的关联
        platform_set_drvdata(pdev, ndev);
        priv = netdev_priv(ndev);
        priv->pdev = pdev;
    
        // 设置操作函数集/mac/watchdog_timeo
        ndev->netdev_ops = &lydm9k_ops;
        ndev->watchdog_timeo =
            msecs_to_jiffies(pdata->watchdog_timeo_msecs);
        memcpy(ndev->dev_addr, pdata->mac, 6);
    
        // 注册网卡
        ret = register_netdev(ndev);
        if(0 > ret)
        {
            dm9kmsg("fail to register netdev\n");
              return -ENOMEM;
         }
     
         // 设置ndev的父设备为pdev
         SET_NETDEV_DEV(ndev, &pdev->dev);
     
         // 设置ndev和pdev直接的关联
         platform_set_drvdata(pdev, ndev);
         priv = netdev_priv(ndev);
         priv->pdev = pdev;
     
         // 设置操作函数集/mac/watchdog_timeo
         ndev->netdev_ops = &lydm9k_ops;
         ndev->watchdog_timeo =
             msecs_to_jiffies(pdata->watchdog_timeo_msecs);
         memcpy(ndev->dev_addr, pdata->mac, 6);
     
         // 注册网卡
         ret = register_netdev(ndev);
         if(0 > ret)
         {
             dm9kmsg("fail to register netdev\n");
             goto failure_register_netdev;
         }
     
         return 0;
     
     failure_register_netdev:
         platform_set_drvdata(pdev, NULL);
         free_netdev(ndev);
     
         return ret;
     }
    
    static int __devexit lydm9k_remove(struct platform_device *pdev)
    {
        struct net_device *ndev = platform_get_drvdata(pdev);
    
        unregister_netdev(ndev);
        platform_set_drvdata(pdev, NULL);
        free_netdev(ndev);
    
        printdbg("remove ok\n");
    
        return 0;
    }
    


            其中网卡的私有数据结构需要包含的数据先不深入分析,等后续用到了再追加,为了简单先定义成如下:

    struct lydm9k_priv {
        struct platform_device *pdev;
    };
    


            以及网卡的相关操作函数也都先定义几个成空的示意用:

    static int lydm9k_open(struct net_device *ndev)
    {
        printdbg("invoked\n");
    
        return 0;
    }
    
    static int lydm9k_stop(struct net_device *ndev)
    {
        printdbg("invoked\n");
    
        return 0;
    }
    
    static int lydm9k_start_xmit(   struct sk_buff *skb,
                                    struct net_device *ndev)
    {
        printdbg("invoked\n");
    
        return 0;
    }
    
    static void lydm9k_timeout(struct net_device *ndev)
    {
        printdbg("invoked\n");
    }
    
    static struct net_device_ops lydm9k_ops = {
        .ndo_open           = lydm9k_open,
        .ndo_stop           = lydm9k_stop,
        .ndo_start_xmit     = lydm9k_start_xmit,
        .ndo_tx_timeout     = lydm9k_timeout,
    };
    


            OK,简单的网卡驱动框架就搭好了, 然后是Makgefile:

    ifneq ($(KERNELRELEASE),)
        obj-m := lydm9k.o
    else
        KERNELDIR ?= /lib/modules/$(shell uname -r)/build
        PWD := $(shell pwd)
    default:
        $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
    endif
    
    clean:
        rm -rf *.mod.c *.mod.o *.o *.order *.symvers 
        rm -rf .*.ko.cmd .*.o.cmd .tmp_versions
    


            make, 装载驱动insmod lydm9k.ko, 然后dmesg, 可以看到如下打印信息(在PC机上即可):

    [ 3949.452062] lydm9k[lydm9k_init]:	insert module lydm9k
    [ 3949.453609] lydm9k[lydm9k_probe]:	detect a dm9k device
    [ 3949.469968] lydm9k[lydm9k_open]:	invoked
    [ 3949.484108] lydm9k[lydm9k_start_xmit]:	invoked
    [ 3949.920047] lydm9k[lydm9k_start_xmit]:	invoked
    [ 3950.928047] lydm9k[lydm9k_start_xmit]:	invoked
    [ 3951.020927] lydm9k[lydm9k_start_xmit]:	invoked
    [ 3951.081567] lydm9k[lydm9k_start_xmit]:	invoked
    [ 3951.095994] lydm9k[lydm9k_start_xmit]:	invoked
    [ 3951.332173] lydm9k[lydm9k_start_xmit]:	invoked
    [ 3951.583247] lydm9k[lydm9k_start_xmit]:	invoked
    [ 3951.783516] lydm9k[lydm9k_start_xmit]:	invoked
    [ 3952.021169] lydm9k[lydm9k_start_xmit]:	invoked
    [ 3952.208043] lydm9k[lydm9k_start_xmit]:	invoked
    


            ifconfig, 可以看到系统里多出了一快网卡(MAC地址是01:02:03:04:05:06, 即我们在lydm9k_plat_data里设置的地址):

    eth1      Link encap:以太网  硬件地址 01:02:03:04:05:06  
              inet6 地址: fe80::302:3ff:fe04:506/64 Scope:Link
              UP BROADCAST RUNNING MULTICAST  MTU:1500  跃点数:1
              接收数据包:0 错误:0 丢弃:0 过载:0 帧数:0
              发送数据包:0 错误:0 丢弃:0 过载:0 载波:0
              碰撞:0 发送队列长度:1000 
              接收字节:0 (0.0 B)  发送字节:0 (0.0 B)
    



            OK, 今天就到此为止, 明天继续。

  • 相关阅读:
    Eclipse 项目导入 Android Studio 导致的乱码问题
    Android 系统服务
    Android 系统内核层与 Linux Kernel 的比较
    DPI 计算及速查表
    Android 引用文件(.db)的三种方式
    阅读记录(2017年1月)
    如何让电脑自动记录每次开关机时间
    使用VS2010编译Qt 5.6.1过程记录
    Windows无线网“无法连接到这个网络”的解决办法
    怎样在Windows资源管理器中添加右键菜单以及修改右键菜单顺序
  • 原文地址:https://www.cnblogs.com/dyllove98/p/3138795.html
Copyright © 2020-2023  润新知