博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【linux驱动分析】之dm9000驱动分析(六):dm9000_init和dm9000_probe的实现
阅读量:7176 次
发布时间:2019-06-29

本文共 12171 字,大约阅读时间需要 40 分钟。

一、dm9000_init
打印出驱动的版本,注冊dm9000_driver驱动,将驱动加入到总线上。运行match,假设匹配,将会运行probe函数。

1 static int __init2 dm9000_init(void)3 {4     printk(KERN_INFO "%s Ethernet Driver, V%s\n", CARDNAME, DRV_VERSION);5 6     return platform_driver_register(&dm9000_driver);7 }
 
二、dm9000_probe函数
 
1 /*  2  * Search DM9000 board, allocate space and register it  3  */  4 static int __devinit  5 dm9000_probe(struct platform_device *pdev)  6 {  7     /* 把mach-mini6410.c中定义的dm9000_plat_data传递过来 */  8     struct dm9000_plat_data *pdata = pdev->dev.platform_data;  9     struct board_info *db;    /* Point a board information structure */ 10     struct net_device *ndev; 11     const unsigned char *mac_src; 12     int ret = 0; 13     int iosize; 14     int i; 15     u32 id_val; 16  17     /* Init network device */ 18     /* 分配一个名为eth%d的网络设备。同一时候分配一个私有数据区,数据区是32字节对齐 */ 19     ndev = alloc_etherdev(sizeof(struct board_info)); 20     if (!ndev) { 21         dev_err(&pdev->dev, "could not allocate device.\n"); 22         return -ENOMEM; 23     } 24     /* 把网络设备的基类dev的父指针设为平台设备的基类dev */ 25     SET_NETDEV_DEV(ndev, &pdev->dev); 26  27     dev_dbg(&pdev->dev, "dm9000_probe()\n"); 28  29     /* setup board info structure */ 30     /* 设置私有数据,以下会详细分析这个函数 */ 31     db = netdev_priv(ndev); 32     /* 给私有数据赋值 */ 33     db->dev = &pdev->dev; 34     db->ndev = ndev; 35      36     /* 初始化一个自旋锁和一个相互排斥体 */ 37     spin_lock_init(&db->lock); 38     mutex_init(&db->addr_lock); 39      40     /* 往工作队列插入一个工作,随后我们调用schedule_delayed_work就会运行传递的函数 41      * 关于工作队列会有专门一篇文章来学习总结 42      */ 43     INIT_DELAYED_WORK(&db->phy_poll, dm9000_poll_work); 44  45     /* 获得资源。这个函数会在以下解说 */ 46     db->addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 47     db->data_res = platform_get_resource(pdev, IORESOURCE_MEM, 1); 48     db->irq_res  = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 49  50     if (db->addr_res == NULL || db->data_res == NULL || 51         db->irq_res == NULL) { 52         dev_err(db->dev, "insufficient resources\n"); 53         ret = -ENOENT; 54         goto out; 55     } 56      57     /* 获取中断号,这个中断号是不存在的,因为resource里仅仅有一个中断号 */ 58     db->irq_wake = platform_get_irq(pdev, 1); 59     if (db->irq_wake >= 0) { 60         dev_dbg(db->dev, "wakeup irq %d\n", db->irq_wake); 61          62         /* 为ndev申请中断。中断服务程序为dm9000_wol_interrupt,关于中断也会有一篇文章来学习总结 */ 63         ret = request_irq(db->irq_wake, dm9000_wol_interrupt, 64                   IRQF_SHARED, dev_name(db->dev), ndev); 65         if (ret) { 66             dev_err(db->dev, "cannot get wakeup irq (%d)\n", ret); 67         } else { 68  69             /* test to see if irq is really wakeup capable */ 70             ret = set_irq_wake(db->irq_wake, 1); 71             if (ret) { 72                 dev_err(db->dev, "irq %d cannot set wakeup (%d)\n", 73                     db->irq_wake, ret); 74                 ret = 0; 75             } else { 76                 set_irq_wake(db->irq_wake, 0); 77                 db->wake_supported = 1; 78             } 79         } 80     } 81     /* 返回dm9000内存资源的大小,以下一句是申请内存,关于内存的申请和分配也会有一篇文章 */ 82     iosize = resource_size(db->addr_res); 83     db->addr_req = request_mem_region(db->addr_res->start, iosize, 84                       pdev->name); 85  86     if (db->addr_req == NULL) { 87         dev_err(db->dev, "cannot claim address reg area\n"); 88         ret = -EIO; 89         goto out; 90     } 91     /* 存放地址的内存空间開始地址,地址寄存器,一共占3个地址, 92      * 各自是0x18000000,0x18000001,0x18000002,0x18000003, 93      * 这也是一个巧妙之处。dm9000芯片的cmd引脚接的是arm11的addr2。 94      * 所以写“0地址”代表送地址。读写4地址表示读写数据 95      * */ 96     db->io_addr = ioremap(db->addr_res->start, iosize); 97  98     if (db->io_addr == NULL) { 99         dev_err(db->dev, "failed to ioremap address reg\n");100         ret = -EINVAL;101         goto out;102     }103 104     iosize = resource_size(db->data_res);105     db->data_req = request_mem_region(db->data_res->start, iosize,106                       pdev->name);107 108     if (db->data_req == NULL) {109         dev_err(db->dev, "cannot claim data reg area\n");110         ret = -EIO;111         goto out;112     }113     /* 数据寄存器的地址,1MB的空间 */114     db->io_data = ioremap(db->data_res->start, iosize);115 116     if (db->io_data == NULL) {117         dev_err(db->dev, "failed to ioremap data reg\n");118         ret = -EINVAL;119         goto out;120     }121 122     /* fill in parameters for net-dev structure */123     ndev->base_addr = (unsigned long)db->io_addr;124     ndev->irq    = db->irq_res->start;125 126     /* ensure at least we have a default set of IO routines */127     /* iosize是一个非常大的值,这里是先保证有个默认值位宽,32位 */128     dm9000_set_io(db, iosize);129 130     /* check to see if anything is being over-ridden */131     if (pdata != NULL) {132         /* check to see if the driver wants to over-ride the133          * default IO width */134 135         if (pdata->flags & DM9000_PLATF_8BITONLY)136             dm9000_set_io(db, 1);137         /*138          * 我们这里设置的是16位。他会做以下几件事:139          * db->dumpblk = dm9000_dumpblk_16bit;140          * db->outblk  = dm9000_outblk_16bit;141          * db->inblk   = dm9000_inblk_16bit;142          */143         if (pdata->flags & DM9000_PLATF_16BITONLY)144             dm9000_set_io(db, 2);145 146         if (pdata->flags & DM9000_PLATF_32BITONLY)147             dm9000_set_io(db, 4);148 149         /* check to see if there are any IO routine150          * over-rides */151 152         if (pdata->inblk != NULL)153             db->inblk = pdata->inblk;154 155         if (pdata->outblk != NULL)156             db->outblk = pdata->outblk;157 158         if (pdata->dumpblk != NULL)159             db->dumpblk = pdata->dumpblk;160 161         db->flags = pdata->flags;162     }163 164 #ifdef CONFIG_DM9000_FORCE_SIMPLE_PHY_POLL165     db->flags |= DM9000_PLATF_SIMPLE_PHY;166 #endif167     /*168      * dm9000_reset函数是运行以下两句话,中间有延时,这里省略了169      * writeb(DM9000_NCR, db->io_addr); //先写NCR寄存器的地址到“0地址”(写到0地址就代表写地址)170      * writeb(NCR_RST, db->io_data);    //再给“4地址”写NCR_RST(0x01)。即NCR = 1;171      * 读写“4地址”就相当于发送数据,cmd引脚连的是addr2,dm9000地址线数据线复用172      * */173     dm9000_reset(db);174 175     /* try multiple times, DM9000 sometimes gets the read wrong */176     /* 以下所做的是读出dm9000的供应商ID和产品ID */177     for (i = 0; i < 8; i++) {178         /* ior是从reg读出数据。类型是u8。它的原理与上面分析reset函数的原理是一样的 */179         id_val  = ior(db, DM9000_VIDL);180         id_val |= (u32)ior(db, DM9000_VIDH) << 8;181         id_val |= (u32)ior(db, DM9000_PIDL) << 16;182         id_val |= (u32)ior(db, DM9000_PIDH) << 24;183 184         if (id_val == DM9000_ID)185             break;186         dev_err(db->dev, "read wrong id 0x%08x\n", id_val);187     }188 189     if (id_val != DM9000_ID) {190         dev_err(db->dev, "wrong id: 0x%08x\n", id_val);191         ret = -ENODEV;192         goto out;193     }194 195     /* Identify what type of DM9000 we are working on */196     /* 读出芯片版本号寄存器。推断dm9000的型号,默认是dm9000E */197     id_val = ior(db, DM9000_CHIPR);198     dev_dbg(db->dev, "dm9000 revision 0x%02x\n", id_val);199 200     switch (id_val) {201     case CHIPR_DM9000A:202         db->type = TYPE_DM9000A;203         break;204     case CHIPR_DM9000B:205         db->type = TYPE_DM9000B;206         break;207     default:208         dev_dbg(db->dev, "ID %02x => defaulting to DM9000E\n", id_val);209         db->type = TYPE_DM9000E;210     }211 212     /* dm9000a/b are capable of hardware checksum offload */213     if (db->type == TYPE_DM9000A || db->type == TYPE_DM9000B) {214         db->can_csum = 1;215         db->rx_csum = 1;216         ndev->features |= NETIF_F_IP_CSUM;217     }218 219     /* from this point we assume that we have found a DM9000 */220 221     /* driver system function */222     /* 这个函数是初始化ndev的一些成员 */223     ether_setup(ndev);224 225     /* 以下也是初始化ndev的一些成员 */226     ndev->netdev_ops    = &dm9000_netdev_ops;227     ndev->watchdog_timeo    = msecs_to_jiffies(watchdog);228     ndev->ethtool_ops    = &dm9000_ethtool_ops;229 230     db->msg_enable       = NETIF_MSG_LINK;231     db->mii.phy_id_mask  = 0x1f;232     db->mii.reg_num_mask = 0x1f;233     db->mii.force_media  = 0;234     db->mii.full_duplex  = 0;235     db->mii.dev         = ndev;236     db->mii.mdio_read    = dm9000_phy_read;237     db->mii.mdio_write   = dm9000_phy_write;238 239     mac_src = "eeprom";240     /* node address是在网络中的一个电脑或终端的号码或名字。241      * 这里从eeprom读取,因为我们没有,所以它读回来的是6个FF242      * */243     /* try reading the node address from the attached EEPROM */244     for (i = 0; i < 6; i += 2)245         dm9000_read_eeprom(db, i / 2, ndev->dev_addr+i);246 247     /* try MAC address passed by kernel command line */248     /* 这个函数是友善之臂加入的,它在mach-mini6410里加入了这样一句话__setup("ethmac=", dm9000_set_mac);249      * 内核启动时,遇到"ethmac="回去运行dm9000_set_mac函数,所以就实现了mac从内核传递过来250      * 这也是一个非常巧妙的设计,须要写一篇文章学习总结一下251      * */252     if (!is_valid_ether_addr(ndev->dev_addr) && pdata != NULL) {253         mac_src = "param data";254         memcpy(ndev->dev_addr, pdata->param_addr, 6);255     }256     /* 以下是读取mac的几种方法,当前这一种是从dm9000的Physical Address Register读取 */257     if (!is_valid_ether_addr(ndev->dev_addr)) {258         /* try reading from mac */259         mac_src = "chip";260         for (i = 0; i < 6; i++)261             ndev->dev_addr[i] = ior(db, i+DM9000_PAR);262     }263     /* 从pdata里的dev_addr读取 */264     if (!is_valid_ether_addr(ndev->dev_addr) && pdata != NULL) {265         mac_src = "platform data";266         memcpy(ndev->dev_addr, pdata->dev_addr, 6);267     }268     /* 没有读到有效的mac地址。提示用ifconfig命令设置 */269     if (!is_valid_ether_addr(ndev->dev_addr))270         dev_warn(db->dev, "%s: Invalid ethernet MAC address. Please "271              "set using ifconfig\n", ndev->name);272 273     /* 274      * 这里因为ndev是我们定义的一个局部变量。所以要ndev传递给平台设备pdev275      * 即pdev->dev->p->driver_data = ndev;276      * 要使用是通过platform_get_drvdata获得277      * */278     platform_set_drvdata(pdev, ndev);279     /* net_device结构体初始化好后,剩余的工作就是把该结构传递给register_netdev函数。280      * 当调用register_netdev后就能够用驱动程序操作设备了。所以281      * 必须在初始化一切事情后再注冊282      * */283     ret = register_netdev(ndev);284 285     if (ret == 0)286         printk(KERN_INFO "%s: dm9000%c at %p,%p IRQ %d MAC: %pM (%s)\n",287                ndev->name, dm9000_type_to_char(db->type),288                db->io_addr, db->io_data, ndev->irq,289                ndev->dev_addr, mac_src);290     return 0;291 292 out:293     dev_err(db->dev, "not found (%d).\n", ret);294 295     dm9000_release_board(pdev, db);296     free_netdev(ndev);297 298     return ret;299 }300 /*********** probe函数大功告成 *************/301
 
 
三、总结probe函数分析是留下的问题
在上面用红色标记出来了要分析的东西
1、分析netdev_priv
在运行 ndev = alloc_etherdev(sizeof(struct board_info));时,先分配了一个net_device结构。又分配了一个board_info结构体。作为ndev的私有数据,然后运行了db = netdev_priv( ndev );来获得私有数据的開始地址。并在以后做初始化。
在probe函数最后,通过platform_set_drvdata函数把ndev结构传给了平台设备,以供使用,那么我猜想这里把board_info也传过去了,以后也能够用。获得它的地址的方法是通过以下的函数。
303 /**304  *    netdev_priv - access network device private data305  *    @dev: network device306  *307  * Get network device private data308  */309 static inline void *netdev_priv(const struct net_device *dev)310 {311     return (char *)dev + ALIGN(sizeof(struct net_device), NETDEV_ALIGN);312 }
 
2、platform_get_resource函数分析
 
46     db->addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);47     db->data_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);48     db->irq_res  = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
platform_data的关系是这种:
platform device->dev->platform_data
对于dm9000驱动来说是这样实现的:
 
8     struct dm9000_plat_data *pdata = pdev->dev.platform_data;
static struct dm9000_plat_data dm9000_setup = {
.flags		= DM9000_PLATF_16BITONLY | DM9000_PLATF_EXT_PHY,
.dev_addr       = { 0x08, 0x90, 0x00, 0xa0, 0x90, 0x90 },
};
 
3、以后要写文章总结的东西
(1)、关于工作队列的要总结一下
43     INIT_DELAYED_WORK(&db->phy_poll, dm9000_poll_work);
 
(2)、关于内核中断的原理和操作方法
63         ret = request_irq(db->irq_wake, dm9000_wol_interrupt,64                   IRQF_SHARED, dev_name(db->dev), ndev);
 
(3)、关于内核内存分配和操作方法
83     db->addr_req = request_mem_region(db->addr_res->start, iosize,84                       pdev->name);
(4)、关于__setup的作用
__setup("ethmac=", dm9000_set_mac);

转载地址:http://subzm.baihongyu.com/

你可能感兴趣的文章
ubuntu中root与user相互切换
查看>>
(转载)Http 请求处理流程
查看>>
GetVersion和GetVersionEx
查看>>
软工实践第一次作业
查看>>
php采集利器snoopy应用技巧
查看>>
我的友情链接
查看>>
安装虚拟机shell脚本
查看>>
去除数组中除第一个负数的所有负数
查看>>
哪些因素导致Python运行效率低?python入门编程
查看>>
[Python]第一个爬虫练习
查看>>
提高Python代码效率的方法
查看>>
zabbix使用msmtp&&mutt搭建邮件告警服务
查看>>
USB抓包工具--Bus Hound的使用方法详解
查看>>
location of android sdk has not been setup in the preference
查看>>
Centos7 二进制安装mysql5.7
查看>>
Centos7之Nginx的两种工作模式
查看>>
Java之品优购课程讲义_day18(3)
查看>>
rpm,yum,权限
查看>>
更新yum到 163
查看>>
Office 2019 & Office 2016 下载地址
查看>>