【linux驱动分析】之dm9000驱动分析(六):dm9000_init和dm9000_probe的实现
2014-04-22 13:35
387 查看
一、dm9000_init
打印出驱动的版本号,注册dm9000_driver驱动,将驱动添加到总线上,执行match,如果匹配,将会执行probe函数。
1 static int __init 2 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 it3 */4 static int __devinit5 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;1617 /* 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);2627 dev_dbg(&pdev->dev, "dm9000_probe()\n");2829 /* setup board info structure */30 /* 设置私有数据,下面会具体分析这个函数 */31 db = netdev_priv(ndev);32 /* 给私有数据赋值 */33 db->dev = &pdev->dev;34 db->ndev = ndev;3536 /* 初始化一个自旋锁和一个互斥体 */37 spin_lock_init(&db->lock);38 mutex_init(&db->addr_lock);3940 /* 往工作队列插入一个工作,随后我们调用schedule_delayed_work就会执行传递的函数41 * 关于工作队列会有专门一篇文章来学习总结42 */43 INIT_DELAYED_WORK(&db->phy_poll, dm9000_poll_work);4445 /* 获得资源,这个函数会在下面讲解 */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);4950 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 }5657 /* 获取中断号,这个中断号是不存在的,因为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);6162 /* 为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 {6869 /* 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);8586 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);9798 if (db->io_addr == NULL) {99 dev_err(db->dev, "failed to ioremap address reg\n");100 ret = -EINVAL;101 goto out;102 }103104 iosize = resource_size(db->data_res);105 db->data_req = request_mem_region(db->data_res->start, iosize,106 pdev->name);107108 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);115116 if (db->io_data == NULL) {117 dev_err(db->dev, "failed to ioremap data reg\n");118 ret = -EINVAL;119 goto out;120 }121122 /* fill in parameters for net-dev structure */123 ndev->base_addr = (unsigned long)db->io_addr;124 ndev->irq = db->irq_res->start;125126 /* ensure at least we have a default set of IO routines */127 /* iosize是一个很大的值,这里是先保证有个默认值位宽,32位 */128 dm9000_set_io(db, iosize);129130 /* 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 */134135 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);145146 if (pdata->flags & DM9000_PLATF_32BITONLY)147 dm9000_set_io(db, 4);148149 /* check to see if there are any IO routine150 * over-rides */151152 if (pdata->inblk != NULL)153 db->inblk = pdata->inblk;154155 if (pdata->outblk != NULL)156 db->outblk = pdata->outblk;157158 if (pdata->dumpblk != NULL)159 db->dumpblk = pdata->dumpblk;160161 db->flags = pdata->flags;162 }163164 #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);174175 /* 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;183184 if (id_val == DM9000_ID)185 break;186 dev_err(db->dev, "read wrong id 0x%08x\n", id_val);187 }188189 if (id_val != DM9000_ID) {190 dev_err(db->dev, "wrong id: 0x%08x\n", id_val);191 ret = -ENODEV;192 goto out;193 }194195 /* 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);199200 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 }211212 /* 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 }218219 /* from this point we assume that we have found a DM9000 */220221 /* driver system function */222 /* 这个函数是初始化ndev的一些成员 */223 ether_setup(ndev);224225 /* 下面也是初始化ndev的一些成员 */226 ndev->netdev_ops = &dm9000_netdev_ops;227 ndev->watchdog_timeo = msecs_to_jiffies(watchdog);228 ndev->ethtool_ops = &dm9000_ethtool_ops;229230 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;238239 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);246247 /* 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);272273 /*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);284285 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;291292 out:293 dev_err(db->dev, "not found (%d).\n", ret);294295 dm9000_release_board(pdev, db);296 free_netdev(ndev);297298 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);
相关文章推荐
- 【linux驱动分析】之dm9000驱动分析(六):dm9000_init和dm9000_probe的实现
- ARM-Linux驱动--DM9000网卡驱动分析(一)
- uClinux2.6(bf561)的NorFlash驱动实现分析(6): do_map_probe
- Linux驱动late_initcall和module_init相关分析
- 【linux驱动分析】之dm9000驱动分析(四):net_device结构体
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】Linux内核抢占实现机制分析
- Linux驱动late_initcall和module_init相关分析
- Linux I2C驱动分析与实现
- ARM-Linux驱动--DM9000网卡驱动分析(一)
- uClinux2.6(bf561)的NorFlash驱动实现分析(7): cfi_probe
- linux 高级字符设备驱动 ioctl操作介绍 例程分析实现【转】
- linux触摸屏驱动开发中的s3c_ts_probe()函数的分析
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】Linux内核抢占实现机制分析
- 【linux驱动分析】之dm9000驱动分析(五):另外几个重要的结构体
- 【linux驱动分析】之dm9000驱动分析(二):定义在板文件中的资源和设备以及几个宏
- 【linux驱动分析】之dm9000驱动分析(七):dm9000的卸载挂起和恢复以及打开和停止
- ARM-Linux驱动--DM9000网卡驱动分析
- Linux2.6.32移植到MINI2440(4)移植+分析DM9000网卡驱动
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】Linux系统调用的实现机制分析
- ARM-Linux驱动--DM9000网卡驱动分析(三)