Linux内核移植 part2:uboot 设备驱动模型
2016-12-23 14:10
537 查看
最近在移植uboot-2015.04的时候发现,uboot的设备驱动也带驱动模型了,第一次见到的时候还真是愣了一下,特别是调试的时候没有以前那么方便直接了。而且设备模型和设备树捆绑在一起,又得花费一番功夫来了解了。不禁深深的感慨,搞技术的真真切切就是活到老学到老,而且这种一直学习的状态其实是外界不断强加给你的,有时候真的觉得挺累。人家改改模型,你就得重新去学习适应。其实这种学习都是高投入低产出的,真的希望国内搞技术的同志们能够加油加油加油,争取走到产业的上游,让人家去用我们的东西,让人家去跟随我们的步伐。希望有人看到这些东西,加快他们前进的步伐,能把更多的精力花在创造更多价值的事情上!!!
先讲几大要点:
要点1: 设备模型始于initf_dm接口
要点2: 整个过程穿插着设备树的东西,所以最好理解设备树
这里先不管这个东西,知道就行,一会儿用到的时候再解释。
dm_init
dm_scan_platdata
dm_scan_fdt (在配置了of_control的情况下)
dm_scan_other
其实我很讨厌分析代码,首先肯定免不了贴代码,容易照本宣科;其次语言难以发挥,很不爽。其实每个函数无非做了两件事,第一件事:焚香沐浴更衣,第二件事:真刀真枪开干。说多了就显得无聊,这里也是这样的,无非就是结构初始化,然后就调用一些接口做些事情。至于初始化为了做什么,其实都在代码中,tag跳一下,稍微看看就能懂了。这里做实际工作的就是两个地方
既然叫device_bind_by_name,那么肯定有这么几个点是要猜到的:
1. 这个接口是要进行搜索的
2. 搜索是根据名字来的
一开始可能会觉得自己只能马后炮分析一下,但是我觉得要做到条件反射,因为计算机编程语言本质上也是语言,对语言不就应该条件反射式的反应吗?不然怎么能够理解并且构建丰富多彩的世界呢。
再贴代码
关于list_driver_lookup_name就没必要贴了,只要知道她会根据名字返回对应的通过
好,我们得到了root_driver,其实看这个名字也要有条件反射,既然是root,那么其他driver就是node了,而且是可以通过root搜索到的。所以我们就要把这些node和root绑定起来。这是后面要做的事情,root_driver我们还没处理,先来看device_bind。再贴代码,靠,真他妈恶心。
root_driver由于是混沌初开,没有什么parent,init这些东西,所以直接返回0了。
这个函数会通过
传进去四个参数:根设备dm_root,设备树blob,offset=0,pre_reloc_only=True。
继续贴代码吧,因为这里是重点了。
也不知道是不是眼睛有缺陷,有时候我会把for和if看混淆。在公司里的时候也遇到过这样的问题,还叫同事帮忙看,看了好久都没发现。这次看的时候又把第一个for看成if了,然后觉得表达式里的语法不对,不过还好很快就发现了。
这个函数看名字就知道是干什么的,就是用来扫描设备树,提取各个有效node的。这里记录一点,对函数的理解我觉得可以分为三个层次:
1. 架构层次:放在架构里,知道她是干嘛的,可以顺利阅读框架代码。
2. 模块层次:知道这个函数输入和输出,并且输出和输入的对应关系,可以调试框架。
3. 实现层次:熟悉具体是怎么实现的,就是源代码怎么写的,可以调试模块。
心里知道当前工作所处的层次,比如现在只要知道这个函数是干嘛的,那就没必要去追究实现层次的东西,人的精力终归是有限的,什么阶段做什么事情,对不对?
那么这三个层次各需要怎么做呢?首先是架构层次,因为虽然我们是要知道函数是干嘛的,但是知道怎么实现的,不就知道是干嘛的了吗?其实不是这样的,首先我们肯定知道我们当前的框架是用来干嘛的,然后进一步细分就知道模块是用来干嘛的,在此基础上,结合函数位置和名字就很容易知道这个函数是用来干嘛的,这是一种自顶向下的分析方式,但是从实现开始则是自底向上的。这里又要多嘴一句了,其实相对于细节,把握顶层设计更容易把握方向,代码如此,社会也是如此。明确政策和社会导向能让你更加容易切入一个领域,少走好多弯路,归根到底人的生命是有限的啊,人这一生就是在用有限的生命最大化地最好自己想做的事情。
在设备树这一块上,这篇文章还处于架构层次,至于模块层次,还要另外写一篇文章分析,尽请期待。
先讲几大要点:
要点1: 设备模型始于initf_dm接口
要点2: 整个过程穿插着设备树的东西,所以最好理解设备树
1. 设备驱动入口
以前驱动入口就是一个类似init函数的东东,现在不一样了,都统一为一个宏U_BOOT_DRIVERU_BOOT_DRIVER(serial_s5p) = { .name = "serial_s5p", .id = UCLASS_SERIAL, .of_match = s5p_serial_ids, .ofdata_to_platdata = s5p_serial_ofdata_to_platdata, .platdata_auto_alloc_size = sizeof(struct s5p_serial_platdata), .probe = s5p_serial_probe, .ops = &s5p_serial_ops, .flags = DM_FLAG_PRE_RELOC, };
这里先不管这个东西,知道就行,一会儿用到的时候再解释。
2. 驱动模型
如果说设备驱动对象是一个成员,那么模型就是用来管理这些成员的规则。整个模型的入口是initf_dm, 其实就是调用了
dm_init_and_scan
static int initf_dm(void) { #if defined(CONFIG_DM) && defined(CONFIG_SYS_MALLOC_F_LEN) int ret; ret = dm_init_and_scan(true); if (ret) { return ret; } #endif return 0; }
dm_init_and_scan函数中调用了以下几个函数:
dm_init
dm_scan_platdata
dm_scan_fdt (在配置了of_control的情况下)
dm_scan_other
2.1 dm_init函数。
/* drivers/core/root.c */ int dm_init(void) { int ret; if (gd->dm_root) { dm_warn("Virtual root driver already exists!\n"); return -EINVAL; } // 初始化uclass_list INIT_LIST_HEAD(&DM_UCLASS_ROOT_NON_CONST); #if defined(CONFIG_NEEDS_MANUAL_RELOC) fix_drivers(); fix_uclass(); #endif ret = device_bind_by_name(NULL, false, &root_info, &DM_ROOT_NON_CONST); if (ret) return ret; #ifdef CONFIG_OF_CONTROL DM_ROOT_NON_CONST->of_offset = 0; #endif ret = device_probe(DM_ROOT_NON_CONST); if (ret) return ret; return 0; }
其实我很讨厌分析代码,首先肯定免不了贴代码,容易照本宣科;其次语言难以发挥,很不爽。其实每个函数无非做了两件事,第一件事:焚香沐浴更衣,第二件事:真刀真枪开干。说多了就显得无聊,这里也是这样的,无非就是结构初始化,然后就调用一些接口做些事情。至于初始化为了做什么,其实都在代码中,tag跳一下,稍微看看就能懂了。这里做实际工作的就是两个地方
device_bind_by_name(NULL, false, &root_info, &DM_ROOT_NON_CONST); device_probe(DM_ROOT_NON_CONST);
2.1.1 device_bind_by_name
原型是这样的/* drivers/core/device.c */ int device_bind_by_name(struct udevice *parent, bool pre_reloc_only, const struct driver_info *info, struct udevice **devp); int device_probe(struct udevice *dev);
既然叫device_bind_by_name,那么肯定有这么几个点是要猜到的:
1. 这个接口是要进行搜索的
2. 搜索是根据名字来的
一开始可能会觉得自己只能马后炮分析一下,但是我觉得要做到条件反射,因为计算机编程语言本质上也是语言,对语言不就应该条件反射式的反应吗?不然怎么能够理解并且构建丰富多彩的世界呢。
再贴代码
int device_bind_by_name(struct udevice *parent, bool pre_reloc_only, const struct driver_info *info, struct udevice **devp) { struct driver *drv; // 搜索driver,这里重点关注root driver drv = lists_driver_lookup_name(info->name); if (!drv) return -ENOENT; if (pre_reloc_only && !(drv->flags & DM_FLAG_PRE_RELOC)) return -EPERM; // 绑定driver和device,重点关注root driver和root device return device_bind(parent, drv, info->name, (void *)info->platdata, -1, devp); }
关于list_driver_lookup_name就没必要贴了,只要知道她会根据名字返回对应的通过
U_BOOT_DRIVER声明的驱动就行。这里返回的是root_driver,这个要贴一下,还有个uclass也贴一下,因为马上用到,注意两者id是一样的。
/* This is the root driver - all drivers are children of this */ U_BOOT_DRIVER(root_driver) = { // 这个名字和info->name是不是同一个东西?哈哈 .name = "root_driver", .id = UCLASS_ROOT, }; /* This is the root uclass */ UCLASS_DRIVER(root) = { .name = "root", .id = UCLASS_ROOT, };
好,我们得到了root_driver,其实看这个名字也要有条件反射,既然是root,那么其他driver就是node了,而且是可以通过root搜索到的。所以我们就要把这些node和root绑定起来。这是后面要做的事情,root_driver我们还没处理,先来看device_bind。再贴代码,靠,真他妈恶心。
int device_bind(struct udevice *parent, struct driver *drv, const char *name, void *platdata, int of_offset, struct udevice **devp) { struct udevice *dev; struct uclass *uc; int ret = 0; *devp = NULL; if (!name) return -EINVAL; // 和前面那个搜索driver的接口一样,这里是根据id搜索对应的uclass ret = uclass_get(drv->id, &uc); if (ret) return ret; // 创建设备udevice对象 dev = calloc(1, sizeof(struct udevice)); if (!dev) return -ENOMEM; // 构建树形结构需要的对象,放兄弟和儿子的地方 INIT_LIST_HEAD(&dev->sibling_node); INIT_LIST_HEAD(&dev->child_head); INIT_LIST_HEAD(&dev->uclass_node); // 配置设备对象 dev->platdata = platdata; dev->name = name; dev->of_offset = of_offset; dev->parent = parent; dev->driver = drv; dev->uclass = uc; dev->seq = -1; dev->req_seq = -1; #ifdef CONFIG_OF_CONTROL /* * Some devices, such as a SPI bus, I2C bus and serial ports are * numbered using aliases. * * This is just a 'requested' sequence, and will be * resolved (and ->seq updated) when the device is probed. */ /* 这里uc和uc_drv已经勾搭上了,其实她们在uclass_get的时候就好上了 * 回顾一下文章贴的第一段代码,那个sdmmc驱动里有个flags,在这里就能用上 */ if (uc->uc_drv->flags & DM_UC_FLAG_SEQ_ALIAS) { if (uc->uc_drv->name && of_offset != -1) { fdtdec_get_alias_seq(gd->fdt_blob, uc->uc_drv->name, of_offset, &dev->req_seq); } } #endif if (!dev->platdata && drv->platdata_auto_alloc_size) { dev->flags |= DM_FLAG_ALLOC_PDATA; dev->platdata = calloc(1, drv->platdata_auto_alloc_size); if (!dev->platdata) { ret = -ENOMEM; goto fail_alloc1; } } if (parent) { int size = parent->driver->per_child_platdata_auto_alloc_size; if (!size) { size = parent->uclass->uc_drv-> per_child_platdata_auto_alloc_size; } if (size) { dev->flags |= DM_FLAG_ALLOC_PARENT_PDATA; dev->parent_platdata = calloc(1, size); if (!dev->parent_platdata) { ret = -ENOMEM; goto fail_alloc2; } } } /* put dev into parent's successor list */ if (parent) list_add_tail(&dev->sibling_node, &parent->child_head); ret = uclass_bind_device(dev); if (ret) goto fail_uclass_bind; /* if we fail to bind we remove device from successors and free it */ if (drv->bind) { ret = drv->bind(dev); if (ret) goto fail_bind; } if (parent && parent->driver->child_post_bind) { ret = parent->driver->child_post_bind(dev); if (ret) goto fail_child_post_bind; } if (parent) dm_dbg("Bound device %s to %s\n", dev->name, parent->name); *devp = dev; return 0; fail_child_post_bind: if (drv->unbind && drv->unbind(dev)) { dm_warn("unbind() method failed on dev '%s' on error path\n", dev->name); } fail_bind: if (uclass_unbind_device(dev)) { dm_warn("Failed to unbind dev '%s' on error path\n", dev->name); } fail_uclass_bind: list_del(&dev->sibling_node); if (dev->flags & DM_FLAG_ALLOC_PARENT_PDATA) { free(dev->parent_platdata); dev->parent_platdata = NULL; } fail_alloc2: if (dev->flags & DM_FLAG_ALLOC_PDATA) { free(dev->platdata); dev->platdata = NULL; } fail_alloc1: free(dev); return ret; }
root_driver由于是混沌初开,没有什么parent,init这些东西,所以直接返回0了。
2.1.2 device_probe
对于root driver来讲,没有什么动作,进入下一个重要部分。2.2 dm_scan_platdata
这里先记录两个宏,其实前面已经用到过了,放这里提示一下/* Cast away any volatile pointer */ #define DM_ROOT_NON_CONST (((gd_t *)gd)->dm_root) #define DM_UCLASS_ROOT_NON_CONST (((gd_t *)gd)->uclass_root)
这个函数会通过
ll_entry_*宏去搜索
driver_info字段,但是链接时并没有,所以应该直接返回0。我有点困惑,难道目前还没用?
2.3 dm_scan_fdt
移植的这个版本中,驱动信息都是通过设备树定义的,她里面调用了/* drivers/core/root.c */ dm_scan_fdt_node(gd->dm_root, blob, 0, pre_reloc_only);
传进去四个参数:根设备dm_root,设备树blob,offset=0,pre_reloc_only=True。
继续贴代码吧,因为这里是重点了。
#ifdef CONFIG_OF_CONTROL int dm_scan_fdt_node(struct udevice *parent, const void *blob, int offset, bool pre_reloc_only) { int ret = 0, err; // 遍历node,至于fdt_first_subnode怎么做的,这里可以不用关心 for (offset = fdt_first_subnode(blob, offset); offset > 0; offset = fdt_next_subnode(blob, offset)) { // pre_reloc_only = True if (pre_reloc_only && !fdt_getprop(blob, offset, "u-boot,dm-pre-reloc", NULL)) continue; if (!fdtdec_get_is_enabled(blob, offset)) { dm_dbg(" - ignoring disabled device\n"); continue; } err = lists_bind_fdt(parent, blob, offset, NULL); if (err && !ret) ret = err; } if (ret) dm_warn("Some drivers failed to bind\n"); return ret; }
也不知道是不是眼睛有缺陷,有时候我会把for和if看混淆。在公司里的时候也遇到过这样的问题,还叫同事帮忙看,看了好久都没发现。这次看的时候又把第一个for看成if了,然后觉得表达式里的语法不对,不过还好很快就发现了。
这个函数看名字就知道是干什么的,就是用来扫描设备树,提取各个有效node的。这里记录一点,对函数的理解我觉得可以分为三个层次:
1. 架构层次:放在架构里,知道她是干嘛的,可以顺利阅读框架代码。
2. 模块层次:知道这个函数输入和输出,并且输出和输入的对应关系,可以调试框架。
3. 实现层次:熟悉具体是怎么实现的,就是源代码怎么写的,可以调试模块。
心里知道当前工作所处的层次,比如现在只要知道这个函数是干嘛的,那就没必要去追究实现层次的东西,人的精力终归是有限的,什么阶段做什么事情,对不对?
那么这三个层次各需要怎么做呢?首先是架构层次,因为虽然我们是要知道函数是干嘛的,但是知道怎么实现的,不就知道是干嘛的了吗?其实不是这样的,首先我们肯定知道我们当前的框架是用来干嘛的,然后进一步细分就知道模块是用来干嘛的,在此基础上,结合函数位置和名字就很容易知道这个函数是用来干嘛的,这是一种自顶向下的分析方式,但是从实现开始则是自底向上的。这里又要多嘴一句了,其实相对于细节,把握顶层设计更容易把握方向,代码如此,社会也是如此。明确政策和社会导向能让你更加容易切入一个领域,少走好多弯路,归根到底人的生命是有限的啊,人这一生就是在用有限的生命最大化地最好自己想做的事情。
在设备树这一块上,这篇文章还处于架构层次,至于模块层次,还要另外写一篇文章分析,尽请期待。
2.4 dm_scan_other
是个__weak,直接返回0。相关文章推荐
- Linux内核移植 part2:uboot lcd驱动
- Linux内核移植 part2:uboot 设备树--基本概念和源码介绍
- Linux内核大讲堂 (一) 设备驱动的基石驱动模型(4)
- linux内核部件分析(十)——设备驱动模型之class
- linux内核部件分析(九)——设备驱动模型之device-driver
- linux内核组件分析之--设备驱动模型之bus
- Linux内核部件分析<9> 设备驱动模型之device-driver
- Linux内核大讲堂 (一) 设备驱动的基石驱动模型(5)
- Linux内核部件分析--设备驱动模型之driver
- Linux内核部件分析<6> 设备驱动模型之device
- linux内核部件分析(六)——设备驱动模型之device
- linux内核组件分析之---设备驱动模型之driver
- Linux内核大讲堂 (一) 设备驱动的基石驱动模型(1)
- Linux内核部件分析<7> 设备驱动模型之driver
- Linux内核大讲堂 (一) 设备驱动的基石驱动模型(2)
- linux内核部件分析(九)——设备驱动模型之device-driver
- Linux内核大讲堂 (一) 设备驱动的基石驱动模型(3)
- Linux内核大讲堂 (一) 设备驱动的基石驱动模型(4)
- linux内核部件之---设备驱动模型的基石kobject
- Linux内核部件分析<8> 设备驱动模型之bus