美文网首页BB-black开发板[Linux arm-v8]
probe函数中device参数的来历--Apple的学习笔记

probe函数中device参数的来历--Apple的学习笔记

作者: applecai | 来源:发表于2020-12-19 19:40 被阅读0次

前言

从我个人的学习习惯来说,喜欢边实践边理论总结。完成了六轴陀螺仪基于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并解析。

image.png
但是随着我继续调试,发现了另外一个我不能解释的问题。就是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总线。至此,我的疑问都解决了。

相关文章

网友评论

    本文标题:probe函数中device参数的来历--Apple的学习笔记

    本文链接:https://www.haomeiwen.com/subject/karcnktx.html