前言
从我个人的学习习惯来说,喜欢边实践边理论总结。完成了六轴陀螺仪基于linux的i2c和spi总线的驱动后。我突然有一个问题就是probe的device参数到底是哪个函数传入的。它的值真的是从设备树中来的吗?那么是从哪个函数开始的,虽然不知道这每个函数通路,也不影响我进行驱动开发,但是出于好奇,以及技能进阶,我得需要知道更详细的内核数据流。
问题分析解决
1. device参数的来历
倒推吧,谁掉到probe的总有一个函数是第一个定义device结构体的。
这一路上的函数中找到struct device *dev;
找定义还是很容易的。然后值应该是从bus->p->klist_devices
获取的。那么第二个问题来了,把devices加入到bus应该在driver_register之前。这样的话才能在同一bus上match后进入probe函数的。我的验证下顺序。
int bus_for_each_dev(struct bus_type *bus, struct device *start,
void *data, int (*fn)(struct device *, void *))
{
struct klist_iter i;
struct device *dev;
int error = 0;
if (!bus || !bus->p)
return -EINVAL;
klist_iter_init_node(&bus->p->klist_devices, &i,
(start ? &start->p->knode_bus : NULL));
while (!error && (dev = next_device(&i)))
error = fn(dev, data);
klist_iter_exit(&i);
return error;
}
2. klist_devices什么时候加入到bus的
因为之前学习过klist相关,所以搜索关键字就能找到,通过klist_add_tail函数将klist_devices加入的。那么第3个问题来了,我要验证我的想法是否正确,就是先加devices到bus然后才会match成功。也就是会先调用bus_add_device,同时调查这里的device结构体内容是从设备树dts转换的device_node中来的吗?打断点吧,这样比较快比较直观~
int bus_add_device(struct device *dev)
{
struct bus_type *bus = bus_get(dev->bus);
int error = 0;
if (bus) {
pr_debug("bus: '%s': add device %s\n", bus->name, dev_name(dev));
error = device_add_groups(dev, bus->dev_groups);
......
klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);
}
return 0;
}
关于p指针是什么,比较容易理解,它定义了将bus同其他类型联系起来的关系,将bus同device、driver、sysfs联系起来。
subsys是kset类型,代表bus在sysfs中的类型
devices_kset代表bus目录下的device的子目录
driver_kset代表bus目录下的driver的子目录
klist_devices是bus的设备链表,klist_drivers是bus的驱动链表
struct subsys_private {
struct kset subsys;
struct kset *devices_kset;
struct kset *drivers_kset;
struct klist klist_devices;
struct klist klist_drivers;
struct blocking_notifier_head bus_notifier;
unsigned int drivers_autoprobe:1;
struct bus_type *bus;
......
};
通过打断点查看bus_add_device的调用关系
果然,rest_init中会创建内核线程kernel_init函数中会调用如下。of打头的就是设备树相关处理。并且我能通过2个断点的调用顺序发现确实是先调用bus_add_device的。至此我最开始的疑问已经解决。device的内容是从设备树解析而来的,并且device先加入的bus中,然后通过do_initialcall加载驱动的时候进行match。
而dts相关的device node tree是在start_kernel中setup_arch的unflatten_device_tree函数来加载dtb并解析。
但是随着我继续调试,发现了另外一个我不能解释的问题。就是probe函数被调用后为什么还会调用bus_add_device。难道要创建2次设备?
image.png
3.bus_add_device为什么在probe后还调用再加入一次bus
因为我当前的理解是do_initial_call中是有顺序的postcall为2,然后device初始化很后面的。postcall的初始化调用了of_add_device后,为什么probe具体函数里面还要add device,难道add的device是不同的。仔细想想之前的设备驱动若不probe成功貌似dev下还真没有i2c-0设备。那就已i2c为例,我再仔细看看源码。i2c-0等adapter设备其实是在probe具体函数中创建的呀!天哪,难道要推翻我上面3条内容的理解。of函数中不会创建设备?
好吧还是去看源码,这是看of相关源码
of_platform_device_create_pdata-> of_device_alloc。通过注释也能理解就是将device node值给到了device结构体。
/**
* of_device_alloc - Allocate and initialize an of_device
* @np: device node to assign to device
* @bus_id: Name to assign to the device. May be null to use default name.
* @parent: Parent device.
*/
struct platform_device *of_device_alloc(struct device_node *np,
const char *bus_id,
struct device *parent)
{
struct platform_device *dev;
int rc, i, num_reg = 0, num_irq;
struct resource *res, temp_res;
dev = platform_device_alloc("", PLATFORM_DEVID_NONE);
if (!dev)
return NULL;
……
}
然后就是最关键的。of_platform_device_create_pdata函数处理完成后,of_platform_bus_create被调用后则会创建设备。而此函数被调用前需要满足if (!dev || !of_match_node(matches, bus))
条件,
static int of_platform_bus_create(struct device_node *bus,
const struct of_device_id *matches,
const struct of_dev_auxdata *lookup,
struct device *parent, bool strict)
{
const struct of_dev_auxdata *auxdata;
struct device_node *child;
struct platform_device *dev;
const char *bus_id = NULL;
void *platform_data = NULL;
int rc = 0;
......
dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);
if (!dev || !of_match_node(matches, bus))
return 0;
for_each_child_of_node(bus, child) {
pr_debug(" create child: %pOF\n", child);
rc = of_platform_bus_create(child, matches, lookup, &dev->dev, strict);
if (rc) {
of_node_put(child);
break;
}
}
of_node_set_flag(bus, OF_POPULATED_BUS);
return rc;
}
然后就找到了需要和simple-bus匹配才会建立设备。也就是说device结构体值以及从设备树中获取了,但是将设备添加到bus必须按simple-bus匹配才创建。所以i2c相关设备按device_node形式存在,还没有加入总线。然后又一个问题来了,i2c的device没有加入的话,i2c总线驱动如何probe成功呢?
int of_platform_default_populate(struct device_node *root,
const struct of_dev_auxdata *lookup,
struct device *parent)
{
return of_platform_populate(root, of_default_bus_match_table, lookup,
parent);
}
const struct of_device_id of_default_bus_match_table[] = {
{ .compatible = "simple-bus", },
{ .compatible = "simple-mfd", },
{ .compatible = "isa", },
#ifdef CONFIG_ARM_AMBA
{ .compatible = "arm,amba-bus", },
#endif /* CONFIG_ARM_AMBA */
{} /* Empty terminated list */
};
4. 调查i2c的device没有加入的话,i2c总线驱动如何probe成功呢
好吧,还是用我的bb black开发板调试下吧。直接打印
先查看调用对象为of_platform_bus_create
[ 0.926547] of_platform_bus_create
[ 0.927064] device: '44e0b000.i2c': device_add
之后把match的第一个字节打印出来
[ 0.929713] match val=simple-bus
[ 0.930232] device: '44e0b000.i2c': device_add
最后打印bus_add_device内容
[ 0.929886] device: '44e0b000.i2c': device_add
[ 0.929925] bus: 'platform': add device 44e0b000.i2c
[ 0.931481] device: '44e10000.scm': device_add
另外一个在i2c_add_numbered_adapter中先添加mytrip打印,最后也会调用bus_add_device
[ 5.836588] mytrip
[ 5.838718] device: 'i2c-0': device_add
[ 5.842692] bus: 'i2c': add device i2c-0
[ 5.846828] device: 'i2c-0': device_add
[ 5.851413] device: '0-0024': device_add
[ 5.855463] bus: 'i2c': add device 0-0024
已经发现区别了。platform是树干的话,那么就是先创建i2c树枝再创建树叶i2c-0。
结合打印信息,再看看设备树
[ 0.920500] device: '44e00000.per-cm': device_add
[ 0.920544] bus: 'platform': add device 44e00000.per-cm
[ 0.928366] device: '44e09000.serial': device_add
[ 0.928409] bus: 'platform': add device 44e09000.serial
[ 0.929886] device: '44e0b000.i2c': device_add
[ 0.929925] bus: 'platform': add device 44e0b000.i2c
per_cm: per-cm@0
uart0: serial@0
i2c0: i2c@0
segment@200000 { /* 0x44e00000 */
compatible = "simple-bus";
……
target-module@b000 { /* 0x44e0b000, ap 18 48.0 */
compatible = "ti,sysc-omap2", "ti,sysc";
……
i2c0: i2c@0 {
compatible = "ti,omap4-i2c";
#address-cells = <1>;
#size-cells = <0>;
reg = <0x0 0x1000>;
interrupts = <70>;
status = "disabled";
};
};
好了规律总结下,按如上打印信息可以得出simple-bus这一层节点的名字都当做设备被添加到platform总线上。然后只要设备加入了,那么该设备的compatible就可以被查到了。说白了通过一开始的of函数i2c总线设备已经添加入平台设备,但是仅限于被sample-bus match的这一层设备。那么就具备了被driver match后probe的能力。然后i2c_add_numbered_adapter函数调用后,在i2c_register_adapter函数中,adap->dev.bus = &i2c_bus_type;
将设备类型设置为i2c,所以之后device_register是会再次调用bus_add_device,将设备添加到总线,此次是将i2c-0设备添加到i2c总线。而第一次通过of_device_add是将i2c总线设备添加到platform总线。至此,我的疑问都解决了。
网友评论