Linux驱动修炼之道-DM9000A网卡驱动框架源码分析

网络设备的初始化:

通过模块的加载函数看出DM9000A的驱动是以平台驱动的形式注册进内核的,下边是模块的加载函数:
1.static int __init  
2.dm9000_init(void)  
2.{  
3.    printk(KERN_INFO "%s Ethernet Driver, V%s\n", CARDNAME, DRV_VERSION);   4. 
5.    return platform_driver_register(&dm9000_driver);   6.} 

下边是平台驱动结构体:
1.static struct platform_driver dm9000_driver = {  
2.    .driver = {  
3.        .name    = "dm9000",  
2.        .owner   = THIS_MODULE,  
3.    },  
4.    .probe   = dm9000_probe,  
5.    .remove  = __devexit_p(dm9000_drv_remove),  
6.    .suspend = dm9000_drv_suspend,  
7.    .resume  = dm9000_drv_resume,  
8.}; 

下面来分析probe函数,用来执行分配的内核函数是alloc_netdev,函数原型是:
1.struct net_device *alloc_netdev(int sizeof_priv, const char *name, void (*setup)(struct net_device*)); 

这里的sizeof_priv是驱动程序私有数据区的大小;这个区成员和net_device结构一同分配给网络设备。实际上,他们都处于一大块内存中,但是驱动程序不需要知道这些。name是接口的名字,其在用户空间可见;这个名字可以使用类似printf中%d的格式,内核将用下一个可用的接口号替代%d,最后,setup是一个初始化函数,用来设置net_device结构剩余的部分。网络子系统对alloc_netdev,为不同种类的接口封装了许多函数。最常用的是alloc_etherdev,它定义在linux/etherdevice.h中:
1.struct net_device *alloc_etherdev(int sizeof_priv); 

该函数使用eth%d的形式指定分配给网络设备的名字。它提供了自己的初始化函数(ether_setup),用正确的值为以太网设备设置net_device中的许多成员。那么在DM9000A中这个私有数据成员是什么呢,看下边的结构:
1./* Structure/enum declaration ------------------------------- */ 
2.typedef struct board_info {  
2. 
3.    void __iomem    *io_addr;   /* Register I/O base address */  4.    void __iomem    *io_data;   /* Data I/O address */  5.    u16      irq;       /* IRQ */  6. 
7.    u16     tx_pkt_cnt;  
8.    u16     queue_pkt_len;  
9.    u16     queue_start_addr;  
10.    u16     dbug_cnt;  
11.    u8      io_mode;        /* 0:word, 2:byte */  12.    u8      phy_addr;  
13.    u8      imr_all;  
14. 
15.    unsigned int    flags;   16.    unsigned int    in_suspend :1;   17.    int     debug_level;   18. 
19.    enum dm9000_type type;   20. 
21.    void (*inblk)(void __iomem *port, void *data, int length);   22.    void (*outblk)(void __iomem *port, void *data, int length);   23.    void (*dumpblk)(void __iomem *port, int length);   24. 
25.    struct device   *dev;        /* parent device */  26. 
27.    struct resource *addr_res;   /* resources found */  28.    struct resource *data_res;   29.    struct resource *addr_req;   /* resources requested */  30.    struct resource *data_req;   31.    struct resource *irq_res;   32. 
33.    struct mutex     addr_lock; /* phy and eeprom access lock */  34. 
35.    struct delayed_work phy_poll;   36.    struct net_device  *ndev;   37. 
38.    spinlock_t  lock;   39. 
40.    struct mii_if_info mii;   41.    u32     msg_enable;  
42.} board_info_t; 

这个struct board_info就是那个私有数据,用来保存芯片相关的一些私有信息。

下面是probe函数的实现:
1./* 
2. * Search DM9000 board, allocate space and register it 
3. */ 
4.static int __devinit  
2.dm9000_probe(struct platform_device *pdev)   3.{  
4.    /*获得平台数据,这个应该在platform_device那边指定了*/  5.    struct dm9000_plat_data *pdata = pdev->dev.platform_data;   6.    struct board_info *db;  /* Point a board information structure */  7.    struct net_device *ndev;   8.    const unsigned char *mac_src;   9.    int ret = 0;   10.    int iosize;   11.    int i;   12.    u32 id_val;  
13. 
14.    /*分配以太网的网络设备*/  15.    ndev = alloc_etherdev(sizeof(struct board_info));   16.    if (!ndev) {   17.        dev_err(&pdev->dev, "could not allocate device.\n");   18.        return -ENOMEM;   19.    }  
20.    /*#define SET_NETDEV_DEV(net, pdev) ((net)->dev.parent = (pdev))*/  21.    SET_NETDEV_DEV(ndev, &pdev->dev);  
22. 
23.    dev_dbg(&pdev->dev, "dm9000_probe()\n");   24. 
25.    /*设置struct board_info为ndev的私有数据*/  26.    db = netdev_priv(ndev);  
27.    memset(db, 0, sizeof(*db));   28.      
29.    db->dev = &pdev->dev;  
30.    db->ndev = ndev;  
31. 
32.    spin_lock_init(&db->lock);   33.    mutex_init(&db->addr_lock);  
34.    /*提交一个任务给一个工作队列,你需要填充一个work_struct结构db->phy_poll*/  35.    INIT_DELAYED_WORK(&db->phy_poll, dm9000_poll_work);  
36.    /*获取IO内存和中断资源*/  37.    db->addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);  
38.    db->data_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);  
39.    db->irq_res  = platform_get_resource(pdev, IORESOURCE_IRQ, 0);  
40.      
41.    if (db->addr_res == NULL || db->data_res == NULL ||   42.        db->irq_res == NULL) {  
43.        dev_err(db->dev, "insufficient resources\n");   44.        ret = -ENOENT;  
45.        goto out;   46.    }  
47.    /*映射到内核,并获得IO内存的虚拟地址,ioremap完成页表的建立,不同于vmalloc,但是,它实际上不分配内存*/      48.    iosize = res_size(db->addr_res);  
49.    db->addr_req = request_mem_region(db->addr_res->start, iosize,  
50.                      pdev->name);  
51. 
52.    if (db->addr_req == NULL) {   53.        dev_err(db->dev, "cannot claim address reg area\n");   54.        ret = -EIO;  
55.        goto out;   56.    }  
57. 
58.    db->io_addr = ioremap(db->addr_res->start, iosize);  
59. 
60.    if (db->io_addr == NULL) {   61.        dev_err(db->dev, "failed to ioremap address reg\n");   62.        ret = -EINVAL;  
63.        goto out;   64.    }  
65. 
66.    iosize = res_size(db->data_res);  
67.    db->data_req = request_mem_region(db->data_res->start, iosize,  
68.                      pdev->name);  
69. 
70.    if (db->data_req == NULL) {   71.        dev_err(db->dev, "cannot claim data reg area\n");   72.        ret = -EIO;  
73.        goto out;   74.    }  
75. 
76.    db->io_data = ioremap(db->data_res->start, iosize);  
77. 
78.    if (db->io_data == NULL) {   79.        dev_err(db->dev, "failed to ioremap data reg\n");   80.        ret = -EINVAL;  
81.        goto out;   82.    }  
83. 
84.    /*获得网络设备的基地址*/  85.    ndev->base_addr = (unsigned long)db->io_addr;   86.    /*获得网络设备的中断号*/  87.    ndev->irq = db->irq_res->start;  
88. 
89.    /*设置默认的IO函数*/  90.    dm9000_set_io(db, iosize);  
91. 
92.    /*如果平台数据不为空*/  93.    if (pdata != NULL) {   94.        /* check to see if the driver wants to over-ride the  95.         * default IO width */ 
96.          
97.        if (pdata->flags & DM9000_PLATF_8BITONLY)   98.            dm9000_set_io(db, 1);  
99. 
100.        if (pdata->flags & DM9000_PLATF_16BITONLY)   101.            dm9000_set_io(db, 2);  
102. 
103.        if (pdata->flags & DM9000_PLATF_32BITONLY)   104.            dm9000_set_io(db, 4);  
105. 
106.        /* check to see if there are any IO routine  107.         * over-rides */ 
108. 
109.        if (pdata->inblk != NULL)   110.            db->inblk = pdata->inblk;  
111. 
112.        if (pdata->outblk != NULL)   113.            db->outblk = pdata->outblk;  
114. 
115.        if (pdata->dumpblk != NULL)   116.            db->dumpblk = pdata->dumpblk;  
117. 
118.        db->flags = pdata->flags;  
119.    }  120.
121.#ifdef CONFIG_DM9000_FORCE_SIMPLE_PHY_POLL  
122.    db->flags |= DM9000_PLATF_SIMPLE_PHY;  123.#endif  
124.    /*dm9000复位*/  125.    dm9000_reset(db);  
126.    /*读取Vendor ID Register,Product ID Register中的值,与0x90000A46比较,如果相等,则说明是DM9000*/  127.    /* try multiple times, DM9000 sometimes gets the read wrong */  128.    for (i = 0; i < 8; i++) {   129.        id_val  = ior(db, DM9000_VIDL);  
130.        id_val |= (u32)ior(db, DM9000_VIDH) << 8;  
131.        id_val |= (u32)ior(db, DM9000_PIDL) << 16;  
132.        id_val |= (u32)ior(db, DM9000_PIDH) << 24;  
133. 
134.        if (id_val == DM9000_ID)   135.            break;   136.        dev_err(db->dev, "read wrong id 0x%08x\n", id_val);   137.    }  
138. 
139.    if (id_val != DM9000_ID) {   140.        dev_err(db->dev, "wrong id: 0x%08x\n", id_val);   141.        ret = -ENODEV;  
142.        goto out;   143.    }  
144. 
145.    /* Identify what type of DM9000 we are working on */  146.    /*读取Chip Revision Register中的值*/  147.    id_val = ior(db, DM9000_CHIPR);  
148.    dev_dbg(db->dev, "dm9000 revision 0x%02x\n", id_val);   149. 
150.    switch (id_val) {   151.    case CHIPR_DM9000A:   152.        db->type = TYPE_DM9000A;  
153.        break;   154.    case CHIPR_DM9000B:   155.        db->type = TYPE_DM9000B;  
156.        break;   157.    default:   158.        dev_dbg(db->dev, "ID %02x => defaulting to DM9000E\n", id_val);   159.        db->type = TYPE_DM9000E;  
160.    }  
161. 
162.    /* from this point we assume that we have found a DM9000 */  163. 
164.    /* driver system function */  165.    /*设置部分net_device字段*/  166.    ether_setup(ndev);  
167. 
168.    ndev->open        = &dm9000_open;  
169.    ndev->hard_start_xmit    = &dm9000_start_xmit;  
170.    ndev->tx_timeout         = &dm9000_timeout;  
171.    ndev->watchdog_timeo = msecs_to_jiffies(watchdog);  
172.    ndev->stop        = &dm9000_stop;  
173.    ndev->set_multicast_list = &dm9000_hash_table;  
174.    /*对ethtool支持的相关声明可在<linux/ethtool.h>中找到。它的核心是一个ethtool_ops类型的结构,里边包含一个全部的24个不同的方法来支持ethtool*/  175.    ndev->ethtool_ops     = &dm9000_ethtool_ops;  
176.    ndev->do_ioctl        = &dm9000_ioctl;  177.
178.#ifdef CONFIG_NET_POLL_CONTROLLER  
179.    ndev->poll_controller     = &dm9000_poll_controller;  180.#endif  
181. 
182.    db->msg_enable       = NETIF_MSG_LINK;  
183.    db->mii.phy_id_mask  = 0x1f;  
184.    db->mii.reg_num_mask = 0x1f;  
185.    db->mii.force_media  = 0;  
186.    db->mii.full_duplex  = 0;  
187.    db->mii.dev       = ndev;  
188.    db->mii.mdio_read    = dm9000_phy_read;  
189.    db->mii.mdio_write   = dm9000_phy_write;  
190.    /*MAC地址的源是eeprom*/  191.    mac_src = "eeprom";   192. 
193.    /* try reading the node address from the attached EEPROM */  194.    for (i = 0; i < 6; i += 2)   195.        dm9000_read_eeprom(db, i / 2, ndev->dev_addr+i);  
196.    /*如果从eeprom中读取的地址无效,并且私有数据不为空,从platform_device的私有数据中获取dev_addr*/  197.    if (!is_valid_ether_addr(ndev->dev_addr) && pdata != NULL) {   198.        mac_src = "platform data";   199.        memcpy(ndev->dev_addr, pdata->dev_addr, 6);  
200.    }  
201.    /*如果地址依然无效,从PAR:物理地址(MAC)寄存器(Physical Address Register)中读取*/  202.    if (!is_valid_ether_addr(ndev->dev_addr)) {   203.        /* try reading from mac */  204.          
205.        mac_src = "chip";   206.        for (i = 0; i < 6; i++)   207.            ndev->dev_addr[i] = ior(db, i+DM9000_PAR);  
208.    }  
209.    /*查看以太网网卡设备地址是否有效*/  210.    if (!is_valid_ether_addr(ndev->dev_addr))   211.        dev_warn(db->dev, "%s: Invalid ethernet MAC address. Please "  212.             "set using ifconfig\n", ndev->name);   213.    /*将ndev保存到pdev->dev->driver_data中*/  214.    platform_set_drvdata(pdev, ndev);  
215.    /*一切都初始化好后,注册网络设备*/  216.    ret = register_netdev(ndev);  
217. 
218.    if (ret == 0)   219.        printk(KERN_INFO "%s: dm9000%c at %p,%p IRQ %d MAC: %pM (%s)\n",   220.               ndev->name, dm9000_type_to_char(db->type),  
221.               db->io_addr, db->io_data, ndev->irq,  
222.               ndev->dev_addr, mac_src);  
223.    return 0;   224. 
225.out:   226.    dev_err(db->dev, "not found (%d).\n", ret);   227. 
228.    dm9000_release_board(pdev, db);  
229.    free_netdev(ndev);  
230. 
231.    return ret;   232.} 

下边看看挂起和唤醒函数:

挂起函数完成了设置挂起标志,并没有真正把设备移除而只是设置了移除标志,复位PHY,停止PHY,禁止所有中断,禁止接受引脚。
1.static int 
2.dm9000_drv_suspend(struct platform_device *dev, pm_message_t state)  
2.{  
3.    struct net_device *ndev = platform_get_drvdata(dev);   4.    board_info_t *db;  
5. 
6.    if (ndev) {   7.        db = netdev_priv(ndev);  
8.        db->in_suspend = 1;  
9. 
10.        if (netif_running(ndev)) {   11.            netif_device_detach(ndev);  
12.            dm9000_shutdown(ndev);  
13.        }  
14.    }  
15.    return 0;   16.} 

唤醒函数完成了复位dm9000,初始化dm9000,标记设备为attached,清除挂起标志。
1.static int 
2.dm9000_drv_resume(struct platform_device *dev)  
2.{  
3.    struct net_device *ndev = platform_get_drvdata(dev);   4.    board_info_t *db = netdev_priv(ndev);  
5. 
6.    if (ndev) {   7. 
8.        if (netif_running(ndev)) {   9.            dm9000_reset(db);  
10.            dm9000_init_dm9000(ndev);  
11.            netif_device_attach(ndev);  
12.        }  
13.        db->in_suspend = 0;  
14.    }  
15.    return 0;   16.} 

网络设备的打开与释放:

首先来看这个open函数:
1.static int 
2.dm9000_open(struct net_device *dev)  
2.{  
3.    board_info_t *db = netdev_priv(dev);  
4.    unsigned long irqflags = db->irq_res->flags & IRQF_TRIGGER_MASK;  
2. 
3.    if (netif_msg_ifup(db))   4.        dev_dbg(db->dev, "enabling %s\n", dev->name);   5. 
6.    /* If there is no IRQ type specified, default to something that  7.     * may work, and tell the user that this is a problem */ 
8. 
9.    if (irqflags == IRQF_TRIGGER_NONE)   10.        dev_warn(db->dev, "WARNING: no IRQ resource flags set.\n");   11. 
12.    irqflags |= IRQF_SHARED;  
13.    /*注册中断处理函数*/  14.    if (request_irq(dev->irq, &dm9000_interrupt, irqflags, dev->name, dev))   15.        return -EAGAIN;   16. 
17.    /* Initialize DM9000 board */  18.    /*复位DM9000*/  19.    dm9000_reset(db);  
20.    /*初始化DM9000的寄存器*/  21.    dm9000_init_dm9000(dev);  
22. 
23.    /* Init driver variable */  24.    db->dbug_cnt = 0;  
25.    /*检查链路载波状况*/  26.    mii_check_media(&db->mii, netif_msg_link(db), 1);  
27.    /*启动发送队列*/  28.    netif_start_queue(dev);  
29.    /*之前在probe函数中调用 INIT_DELAYED_WORK初始化了工作队列,并关联了一个操作函数dm9000_poll_work(),此时运行dm9000_schedule_poll来调用这个函数*/  30.    dm9000_schedule_poll(db);  
31. 
32.    return 0;   33.} 

然后是stop函数:
1.static int 
2.dm9000_stop(struct net_device *ndev)  
2.{  
3.    board_info_t *db = netdev_priv(ndev);  
4. 
5.    if (netif_msg_ifdown(db))  
2.        dev_dbg(db->dev, "shutting down %s\n", ndev->name);  
2.    /*杀死延时工作队列phy_poll*/ 
2.    cancel_delayed_work_sync(&db->phy_poll);  
3.    /*停止发送队列*/  4.    netif_stop_queue(ndev);  
5.    /*通知内核链路失去连接*/  6.    netif_carrier_off(ndev);  
7.    /* free interrupt */  8.    free_irq(ndev->irq, ndev);  
9.    /*关闭DM9000*/  10.    dm9000_shutdown(ndev);  
11.    return 0;   12.} 

复位PHY,停止PHY,禁止所有中断,禁止接收引脚。
1.static void 
2.dm9000_shutdown(struct net_device *dev)  
2.{  
3.    board_info_t *db = netdev_priv(dev);  
4. 
5.    /* RESET device */ 
2.    dm9000_phy_write(dev, 0, MII_BMCR, BMCR_RESET); /* PHY RESET */ 
2.    iow(db, DM9000_GPR, 0x01);  /* Power-Down PHY */ 
2.    iow(db, DM9000_IMR, IMR_PAR);   /* Disable all interrupt */  3.    iow(db, DM9000_RCR, 0x00);  /* Disable RX */  4.} 

本篇文章来源于 Linux公社网站(www.linuxidc.com)  原文链接:http://www.linuxidc.com/Linux/2011-06/37493p2.htm

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值