一步步寫網卡驅動(-)

        最近一直在看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, 今天就到此爲止, 明天繼續。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章