一步步写网卡驱动(-)

        最近一直在看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, 今天就到此为止, 明天继续。

 

 

你可能感兴趣的:(网卡)