MENU

linux 内核加载驱动流程

December 13, 2023 • 阅读: 1947 • 笔记&折腾

调试环境:内核代码版本为 linux kernel 5.4。

初始化:

module_init(kbase_driver_init);
module_exit(kbase_driver_exit);

module_init指定了当驱动模块被加载到内核时应该调用的函数。使用 insmod 命令加载模块时,内核会自动调用 kbase_driver_init 函数。
module_exit这行代码指定了当驱动模块被从内核卸载时应该调用的函数。使用 rmmod 命令卸载模块时,内核会自动调用 kbase_driver_exit 函数。

static struct platform_driver kbase_platform_driver = {
    .probe = kbase_platform_device_probe,
    .remove = kbase_platform_device_remove,
    .driver = {
           .name = kbase_drv_name,
           .owner = THIS_MODULE,
           .pm = &kbase_pm_ops,
           .of_match_table = of_match_ptr(kbase_dt_ids),
    },
};

static int __init kbase_driver_init(void)
{
    int ret;
    ret = kbase_platform_register();
    if (ret)
        return ret;
    ret = platform_driver_register(&kbase_platform_driver);
    if (ret)
        kbase_platform_unregister();
    return ret;
}

device 初始化流程

在 kbase_platform_register 函数中,会尝试进行设备的初始化:

int kbase_platform_register(void)
{
    struct kbase_platform_config *config;
#ifndef CONFIG_OF
    struct resource resources[PLATFORM_CONFIG_RESOURCE_COUNT];
#endif
    int err;
    config = kbase_get_platform_config(); /* declared in midgard/mali_kbase_config.h but defined in platform folder */
    if (config == NULL) {
        pr_err("%s: couldn't get platform config\n", __func__);
        return -ENODEV;
    }
    mali_device = platform_device_alloc("mali", 0);
    if (mali_device == NULL)
        return -ENOMEM;
#ifndef CONFIG_OF
    kbasep_config_parse_io_resources(config->io_resources, resources);
    err = platform_device_add_resources(mali_device, resources, PLATFORM_CONFIG_RESOURCE_COUNT);
    if (err) {
        platform_device_put(mali_device);
        mali_device = NULL;
        return err;
    }
#endif /* CONFIG_OF */
    err = platform_device_add(mali_device);
    if (err) {
        platform_device_unregister(mali_device);
        mali_device = NULL;
        return err;
    }
    return 0;
}

以上代码用于在 Linux 内核中注册一个名为 "mali" 的平台设备。用于在内核启动时或作为模块初始化的一部分将硬件设备注册到内核。
config = kbase_get_platform_config(); 调用函数以获取平台配置。这个配置可能包含了设备的IO资源、中断等信息。如果没有获取到配置(config == NULL),函数打印错误信息并返回 -ENODEV(无此设备)。
mali_device = platform_device_alloc("mali", 0); 分配并初始化一个名为 "mali" 的平台设备。如果分配失败(mali_device == NULL),返回 -ENOMEM(内存不足)。
如果内核未使用设备树(CONFIG_OF 没有定义),则手动解析 IO 资源,并将这些资源添加到平台设备。platform_device_add_resources 用于将解析的资源添加到设备。如果出错,释放之前分配的设备并返回错误。
err = platform_device_add(mali_device); 将之前创建的平台设备添加到系统中。这个步骤使得设备成为系统的一部分,使其可以和驱动程序绑定。如果添加失败,注销并释放设备,然后返回错误。

int platform_device_add(struct platform_device *pdev)
{
    int i, ret;
    if (!pdev)
        return -EINVAL;
    if (!pdev->dev.parent)
        pdev->dev.parent = &platform_bus;
    pdev->dev.bus = &platform_bus_type;
    switch (pdev->id) {
    default:
        dev_set_name(&pdev->dev, "%s.%d", pdev->name,  pdev->id);
        break;
    case PLATFORM_DEVID_NONE:
        dev_set_name(&pdev->dev, "%s", pdev->name);
        break;
    case PLATFORM_DEVID_AUTO:
        /*
         * Automatically allocated device ID. We mark it as such so
         * that we remember it must be freed, and we append a suffix
         * to avoid namespace collision with explicit IDs.
         */
        ret = ida_simple_get(&platform_devid_ida, 0, 0, GFP_KERNEL);
        if (ret < 0)
            goto err_out;
        pdev->id = ret;
        pdev->id_auto = true;
        dev_set_name(&pdev->dev, "%s.%d.auto", pdev->name, pdev->id);
        break;
    }
    for (i = 0; i < pdev->num_resources; i++) {
        struct resource *p, *r = &pdev->resource[i];
        if (r->name == NULL)
            r->name = dev_name(&pdev->dev);
        p = r->parent;
        if (!p) {
            if (resource_type(r) == IORESOURCE_MEM)
                p = &iomem_resource;
            else if (resource_type(r) == IORESOURCE_IO)
                p = &ioport_resource;
        }
        if (p) {
            ret = insert_resource(p, r);
            if (ret) {
                dev_err(&pdev->dev, "failed to claim resource %d: %pR\n", i, r);
                goto failed;
            }
        }
    }
    pr_debug("Registering platform device '%s'. Parent at %s\n",
         dev_name(&pdev->dev), dev_name(pdev->dev.parent));
    ret = device_add(&pdev->dev);
    if (ret == 0)
        return ret;
 failed:
    if (pdev->id_auto) {
        ida_simple_remove(&platform_devid_ida, pdev->id);
        pdev->id = PLATFORM_DEVID_AUTO;
    }
    while (--i >= 0) {
        struct resource *r = &pdev->resource[i];
        if (r->parent)
            release_resource(r);
    }
 err_out:
    return ret;
}

以上代码的主要作用是负责将一个 platform_device 结构添加到系统中。会设置设备的父级和总线类型。然后根据ID设置设备名称,前面 mali_device = platform_device_alloc("mali", 0); 会设置设备的 dev->name="mali",dev->id=0,所以设备名称会被设置为 mali.0。接下来遍历设备的资源,将资源加入到系统资源中,并分辨目标资源是IO资源还是MEM资源。最后使用 device_add 将设备添加到系统中。如果添加失败,进行错误处理。

int device_add(struct device *dev)
{
    struct device *parent;
    struct kobject *kobj;
    struct class_interface *class_intf;
    int error = -EINVAL;
    struct kobject *glue_dir = NULL;
    dev = get_device(dev);
    if (!dev)
        goto done;
    if (!dev->p) {
        error = device_private_init(dev);
        if (error)
            goto done;
    }
    /*
     * for statically allocated devices, which should all be converted
     * some day, we need to initialize the name. We prevent reading back
     * the name, and force the use of dev_name()
     */
    if (dev->init_name) {
        dev_set_name(dev, "%s", dev->init_name);
        dev->init_name = NULL;
    }
    /* subsystems can specify simple device enumeration */
    if (!dev_name(dev) && dev->bus && dev->bus->dev_name)
        dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id);
    if (!dev_name(dev)) {
        error = -EINVAL;
        goto name_error;
    }
    pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
    parent = get_device(dev->parent);
    kobj = get_device_parent(dev, parent);
    if (IS_ERR(kobj)) {
        error = PTR_ERR(kobj);
        goto parent_error;
    }
    if (kobj)
        dev->kobj.parent = kobj;
    /* use parent numa_node */
    if (parent && (dev_to_node(dev) == NUMA_NO_NODE))
        set_dev_node(dev, dev_to_node(parent));
    /* first, register with generic layer. */
    /* we require the name to be set before, and pass NULL */
    error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
    if (error) {
        glue_dir = get_glue_dir(dev);
        goto Error;
    }
    /* notify platform of device entry */
    error = device_platform_notify(dev, KOBJ_ADD);
    if (error)
        goto platform_error;
    error = device_create_file(dev, &dev_attr_uevent);
    if (error)
        goto attrError;
    error = device_add_class_symlinks(dev);
    if (error)
        goto SymlinkError;
    error = device_add_attrs(dev);
    if (error)
        goto AttrsError;
    error = bus_add_device(dev);
    if (error)
        goto BusError;
    error = dpm_sysfs_add(dev);
    if (error)
        goto DPMError;
    device_pm_add(dev);
    if (MAJOR(dev->devt)) {
        error = device_create_file(dev, &dev_attr_dev);
        if (error)
            goto DevAttrError;
        error = device_create_sys_dev_entry(dev);
        if (error)
            goto SysEntryError;
        devtmpfs_create_node(dev);
    }
    /* Notify clients of device addition.  This call must come
     * after dpm_sysfs_add() and before kobject_uevent().
     */
    if (dev->bus)
        blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
                         BUS_NOTIFY_ADD_DEVICE, dev);
    kobject_uevent(&dev->kobj, KOBJ_ADD);
    bus_probe_device(dev);
    if (parent)
        klist_add_tail(&dev->p->knode_parent,
                   &parent->p->klist_children);
    if (dev->class) {
        mutex_lock(&dev->class->p->mutex);
        /* tie the class to the device */
        klist_add_tail(&dev->p->knode_class,
                   &dev->class->p->klist_devices);
        /* notify any interfaces that the device is here */
        list_for_each_entry(class_intf,
                    &dev->class->p->interfaces, node)
            if (class_intf->add_dev)
                class_intf->add_dev(dev, class_intf);
        mutex_unlock(&dev->class->p->mutex);
    }
done:
    put_device(dev);
    return error;
...
}

以上代码负责将一个设备添加到系统中。这是内核设备模型的核心部分,涉及到多个步骤来确保设备被正确注册并与系统其他部分交互。
首先要设置设备的父对象和节点,将设备的父kobject 设置为设备的父对象。然后使用 kobject_add 将设备的 kobject 添加到内核对象模型中。
使用 device_create_file 通知平台有关新设备的添加;并为设备创建 sysfs 条目,如 uevent;为设备创建类符号链接(如果它属于某个类)和其他 sysfs 属性;将设备添加到其所属的总线;在电源管理子系统中注册设备;如果设备有主设备号,创建相应的设备文件;通知总线和设备类该设备已经添加;触发设备的 kobject 事件,如 KOBJ_ADD解析。
这里有一个关键的函数调用, bus_probe_device,目的是为device 寻找可用的驱动,实现代码:

void bus_probe_device(struct device *dev)
{
    struct bus_type *bus = dev->bus;
    struct subsys_interface *sif;
    if (!bus)
        return;
    if (bus->p->drivers_autoprobe)
        device_initial_probe(dev);
    ...
}

void device_initial_probe(struct device *dev)
{
    __device_attach(dev, true);
}

static int __device_attach(struct device *dev, bool allow_async)
{
    int ret = 0;
    device_lock(dev);
    if (dev->driver) {
        if (device_is_bound(dev)) {
            ret = 1;
            goto out_unlock;
        }
        ret = device_bind_driver(dev);
        if (ret == 0)
            ret = 1;
        else {
            dev->driver = NULL;
            ret = 0;
        }
    } else {
        struct device_attach_data data = {
            .dev = dev,
            .check_async = allow_async,
            .want_async = false,
        };
        if (dev->parent)
            pm_runtime_get_sync(dev->parent);
        ret = bus_for_each_drv(dev->bus, NULL, &data,
                    __device_attach_driver);
        if (!ret && allow_async && data.have_async) {
            /*
             * If we could not find appropriate driver
             * synchronously and we are allowed to do
             * async probes and there are drivers that
             * want to probe asynchronously, we'll
             * try them.
             */
            dev_dbg(dev, "scheduling asynchronous probe\n");
            get_device(dev);
            async_schedule_dev(__device_attach_async_helper, dev);
        } else {
            pm_request_idle(dev);
        }
        if (dev->parent)
            pm_runtime_put(dev->parent);
    }
out_unlock:
    device_unlock(dev);
    return ret;
}

会先判断device是否已经绑定了驱动,如果没有则调用 bus_for_each_drv函数为device寻找驱动,并传入函数指针 __device_attach_driver:

int bus_for_each_drv(struct bus_type *bus, struct device_driver *start,
             void *data, int (*fn)(struct device_driver *, void *))
{
    struct klist_iter i;
    struct device_driver *drv;
    int error = 0;
    if (!bus)
        return -EINVAL;
    klist_iter_init_node(&bus->p->klist_drivers, &i,
                 start ? &start->p->knode_bus : NULL);
    while ((drv = next_driver(&i)) && !error)
        error = fn(drv, data);
    klist_iter_exit(&i);
    return error;
}

在该函数中,会遍历 bus 下所有可用的driver,并尝试使用传入的函数指针__device_attach_driver将驱动与 device 进行匹配绑定:

static int __device_attach_driver(struct device_driver *drv, void *_data)
{
    struct device_attach_data *data = _data;
    struct device *dev = data->dev;
    bool async_allowed;
    int ret;
    ret = driver_match_device(drv, dev);
    if (ret == 0) {
        /* no match */
        return 0;
    } else if (ret == -EPROBE_DEFER) {
        dev_dbg(dev, "Device match requests probe deferral\n");
        driver_deferred_probe_add(dev);
    } else if (ret < 0) {
        dev_dbg(dev, "Bus failed to match device: %d", ret);
        return ret;
    } /* ret > 0 means positive match */
    async_allowed = driver_allows_async_probing(drv);
    if (async_allowed)
        data->have_async = true;
    if (data->check_async && async_allowed != data->want_async)
        return 0;
    return driver_probe_device(drv, dev);
}

关于 driver_math_device 函数:

static inline int driver_match_device(struct device_driver *drv,
                      struct device *dev)
{
    return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}

drv->bus->match 函数其实就是 platform_bus_type->match ,指向 platform_match 函数:

struct bus_type platform_bus_type = {
...
.match     = platform_match,
...
};

static int platform_match(struct device *dev, struct device_driver *drv)
{
    struct platform_device *pdev = to_platform_device(dev);
    struct platform_driver *pdrv = to_platform_driver(drv);
    /* When driver_override is set, only bind to the matching driver */
    if (pdev->driver_override)
        return !strcmp(pdev->driver_override, drv->name);
    /* Attempt an OF style match first */
    if (of_driver_match_device(dev, drv))
        return 1;
    /* Then try ACPI style match */
    if (acpi_driver_match_device(dev, drv))
        return 1;
    /* Then try to match against the id table */
    if (pdrv->id_table)
        return platform_match_id(pdrv->id_table, pdev) != NULL;
    /* fall-back to driver name match */
    return (strcmp(pdev->name, drv->name) ==0;
}

首先将设备和驱动切换成平台设备和驱动的特定结构体 platform_device 和 platform_driver。
如果平台设备用 driver_override 字段,这代表该该驱动被要求只能绑定到特定的驱动,只检查设备的 driver_override 字段和驱动的 name 是否一致。
随后是设备树OF匹配。基于设备树属性进行匹配。

static inline int of_driver_match_device(struct device *dev,
                     const struct device_driver *drv)
{
    return of_match_device(drv->of_match_table, dev) != NULL;
}

其中的参数 drv->of_match_table 为最开始 kbase_platform_driver 中的 of_match_table 成员:

static const struct of_device_id kbase_dt_ids[] = {
    { .compatible = "arm,malit6xx" },
    { .compatible = "arm,mali-midgard" },
    { .compatible = "arm,mali-bifrost" },
    { /* sentinel */ }
};

static struct platform_driver kbase_platform_driver = {
    ...
    .driver = {
        ...
        .of_match_table = of_match_ptr(kbase_dt_ids),
        ...
    },
};

而of_match_device 要做的事情就是将 of_match_table 中的 .compatible 与设备树中的 .compatible 字段进行匹配:

gpu@28410000 {
    compatible = "arm,malit60x\0arm,malit6xx\0arm,mali-midgard\0arm,mali-bifrost";
    …
}

如果设备树OF匹配未成功,则调用 acpi_driver_match_device 函数匹配。

bool acpi_driver_match_device(struct device *dev,
                  const struct device_driver *drv)
{
    if (!drv->acpi_match_table)
        return acpi_of_match_device(ACPI_COMPANION(dev),
                        drv->of_match_table,
                        NULL);
    return __acpi_match_device(acpi_companion_match(dev),
                   drv->acpi_match_table, drv->of_match_table,
                   NULL, NULL);
}

acpi匹配方式要求 最开始的 kbase_platform_driver 结构体中设置有 acpi_math_table 的结构体成员,类似于 of_match_table。与 of_match_table 的匹配项 compatibles 不同,acpi 的匹配项为 acpi id。
如果 acpi 方式未能匹配到,则尝试使用 id_table 方法,这要求驱动 platform_driver 结构体对象初始化时设置它的成员 id_table:

struct platform_driver {
    …
    const struct platform_device_id *id_table;
    ...
};

然后使用 strcmp(pdev->name, id->name) 进行比较匹配。
如果 id_table 也未能匹配成功,则使用最后一种办法,strcmp(pdev->name, drv->name)。

匹配成功后将返回1回到 __device_attach_driver 函数中:

static int __device_attach_driver(struct device_driver *drv, void *_data)
{
    ...
    ret = driver_match_device(drv, dev);
    ...
    return driver_probe_device(drv, dev);
}

接下来将调用 driver_probe_device 函数尝试将驱动与设备绑定在一起

int driver_probe_device(struct device_driver *drv, struct device *dev)
{
    int ret = 0;
    if (!device_is_registered(dev))
        return -ENODEV;
    pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
         drv->bus->name, __func__, dev_name(dev), drv->name);
    pm_runtime_get_suppliers(dev);
    if (dev->parent)
        pm_runtime_get_sync(dev->parent);
    pm_runtime_barrier(dev);
    if (initcall_debug)
        ret = really_probe_debug(dev, drv);
    else
        ret = really_probe(dev, drv);
    pm_request_idle(dev);
    if (dev->parent)
        pm_runtime_put(dev->parent);
    pm_runtime_put_suppliers(dev);
    return ret;
}

该函数进入后会对目标 device 做一些准备,然后进入 really_probe 或者 really_probe_debug 函数进行实际的绑定动作。

static int really_probe(struct device *dev, struct device_driver *drv)
{
    int ret = -EPROBE_DEFER;
    int local_trigger_count = atomic_read(&deferred_trigger_count);
    bool test_remove = IS_ENABLED(CONFIG_DEBUG_TEST_DRIVER_REMOVE) &&
               !drv->suppress_bind_attrs;
    if (defer_all_probes) {
        /*
         * Value of defer_all_probes can be set only by
         * device_block_probing() which, in turn, will call
         * wait_for_device_probe() right after that to avoid any races.
         */
        dev_dbg(dev, "Driver %s force probe deferral\n", drv->name);
        driver_deferred_probe_add(dev);
        return ret;
    }
    ret = device_links_check_suppliers(dev);
    if (ret == -EPROBE_DEFER)
        driver_deferred_probe_add_trigger(dev, local_trigger_count);
    if (ret)
        return ret;
    atomic_inc(&probe_count);
    pr_debug("bus: '%s': %s: probing driver %s with device %s\n",
         drv->bus->name, __func__, drv->name, dev_name(dev));
    WARN_ON(!list_empty(&dev->devres_head));
re_probe:
    dev->driver = drv;
    /* If using pinctrl, bind pins now before probing */
    ret = pinctrl_bind_pins(dev);
    if (ret)
        goto pinctrl_bind_failed;
    if (dev->bus->dma_configure) {
        ret = dev->bus->dma_configure(dev);
        if (ret)
            goto probe_failed;
    }
    if (driver_sysfs_add(dev)) {
        printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
            __func__, dev_name(dev));
        goto probe_failed;
    }
    if (dev->pm_domain && dev->pm_domain->activate) {
        ret = dev->pm_domain->activate(dev);
        if (ret)
            goto probe_failed;
    }
    if (dev->bus->probe) {
        ret = dev->bus->probe(dev);
        if (ret)
            goto probe_failed;
    } else if (drv->probe) {
        ret = drv->probe(dev);
        if (ret)
            goto probe_failed;
    }
    if (device_add_groups(dev, drv->dev_groups)) {
        dev_err(dev, "device_add_groups() failed\n");
        goto dev_groups_failed;
    }
    if (test_remove) {
        test_remove = false;
        device_remove_groups(dev, drv->dev_groups);
        if (dev->bus->remove)
            dev->bus->remove(dev);
        else if (drv->remove)
            drv->remove(dev);
        devres_release_all(dev);
        driver_sysfs_remove(dev);
        dev->driver = NULL;
        dev_set_drvdata(dev, NULL);
        if (dev->pm_domain && dev->pm_domain->dismiss)
            dev->pm_domain->dismiss(dev);
        pm_runtime_reinit(dev);
        goto re_probe;
    }
    pinctrl_init_done(dev);
    if (dev->pm_domain && dev->pm_domain->sync)
        dev->pm_domain->sync(dev);
    driver_bound(dev);
    ret = 1;
    pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
         drv->bus->name, __func__, dev_name(dev), drv->name);
    goto done;
dev_groups_failed:
    if (dev->bus->remove)
        dev->bus->remove(dev);
    else if (drv->remove)
        drv->remove(dev);
probe_failed:
    if (dev->bus)
        blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
                         BUS_NOTIFY_DRIVER_NOT_BOUND, dev);
pinctrl_bind_failed:
    device_links_no_driver(dev);
    devres_release_all(dev);
    arch_teardown_dma_ops(dev);
    driver_sysfs_remove(dev);
    dev->driver = NULL;
    dev_set_drvdata(dev, NULL);
    if (dev->pm_domain && dev->pm_domain->dismiss)
        dev->pm_domain->dismiss(dev);
    pm_runtime_reinit(dev);
    dev_pm_set_driver_flags(dev, 0);
    switch (ret) {
    case -EPROBE_DEFER:
        /* Driver requested deferred probing */
        dev_dbg(dev, "Driver %s requests probe deferral\n", drv->name);
        driver_deferred_probe_add_trigger(dev, local_trigger_count);
        break;
    case -ENODEV:
    case -ENXIO:
        pr_debug("%s: probe of %s rejects match %d\n",
             drv->name, dev_name(dev), ret);
        break;
    default:
        /* driver matched but the probe failed */
        printk(KERN_WARNING
               "%s: probe of %s failed with error %d\n",
               drv->name, dev_name(dev), ret);
    }
    /*
     * Ignore errors returned by ->probe so that the next driver can try
     * its luck.
     */
    ret = 0;
done:
    atomic_dec(&probe_count);
    wake_up(&probe_waitqueue);
    return ret;
}

在函数开始时也需要做很多准备工作,比较关键的一步是调用 probe 方法,会先判断 bus 的 probe 方法是否存在,如果不存在则调用 drv 的 probe 方法, 而 drv 的probe 方法,由前面的 __platform_driver_register 函数指定到了 platform_drv_probe :

int __platform_driver_register(struct platform_driver *drv,
                struct module *owner)
{
    ...
    drv->driver.probe = platform_drv_probe;
    ...
}

static int platform_drv_probe(struct device *_dev)
{
    struct platform_driver *drv = to_platform_driver(_dev->driver);
    struct platform_device *dev = to_platform_device(_dev);
    int ret;
    ret = of_clk_set_defaults(_dev->of_node, false);
    if (ret < 0)
        return ret;
    ret = dev_pm_domain_attach(_dev, true);
    if (ret)
        goto out;
    if (drv->probe) {
        ret = drv->probe(dev);
        if (ret)
            dev_pm_domain_detach(_dev, true);
    }
out:
    if (drv->prevent_deferred_probe && ret == -EPROBE_DEFER) {
        dev_warn(_dev, "probe deferral not supported\n");
        ret = -ENXIO;
    }
    return ret;
}

到了 platform_drv_probe 会再次调用一次 probe 函数,注意,上一次调用的 probe 函数是 platform_driver->driver->probe,而接下来要调用的 probe 函数是 platform_driver->probe,由最开始的 kbase_platfrom_driver 决定:

static struct platform_driver kbase_platform_driver = {
    .probe = kbase_platform_device_probe,
    .remove = kbase_platform_device_remove,
    .driver = {
           .name = kbase_drv_name,
           .owner = THIS_MODULE,
           .pm = &kbase_pm_ops,
           .of_match_table = of_match_ptr(kbase_dt_ids),
    },
};

而 kbase_platform_device_probe 就是目标驱动代码中要实现的部分,通常该函数的作用是对设备做一些具体的初始化工作,并进行驱动与硬件设备之间的绑定,创建设备文件( /dev/),设置设备的数据结构等。
probe 工作完成后,really_probe 的下一个动作是 添加属性组和driver bound,

static int really_probe(struct device *dev, struct device_driver *drv)
{
    ...
    if (device_add_groups(dev, drv->dev_groups)) {
            dev_err(dev, "device_add_groups() failed\n");
            goto dev_groups_failed;
        }
    ...
    driver_bound(dev);
}

添加属性组的目的是将一组属性文件添加到特定设备的 sysfs 目录中,使得用户可以通过这些属性文件来修复并访问设备的状态。而 driver_bound 用来执行更为具体的绑定工作

static void driver_bound(struct device *dev)
{
    if (device_is_bound(dev)) {
        printk(KERN_WARNING "%s: device %s already bound\n",
            __func__, kobject_name(&dev->kobj));
        return;
    }
    pr_debug("driver: '%s': %s: bound to device '%s'\n", dev->driver->name,
         __func__, dev_name(dev));
    klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices);
    device_links_driver_bound(dev);
    device_pm_check_callbacks(dev);
    /*
     * Make sure the device is no longer in one of the deferred lists and
     * kick off retrying all pending devices
     */
    driver_deferred_probe_del(dev);
    driver_deferred_probe_trigger();
    if (dev->bus)
        blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
                         BUS_NOTIFY_BOUND_DRIVER, dev);
    kobject_uevent(&dev->kobj, KOBJ_BIND);
}

首先检测设备是否已经被绑定到其他驱动上,如果没有,则将设备添加到驱动的设备链表中,随后在 device_links_driver_bound 函数中通知该设备的所有消费者,该设备已经绑定,处于可用状态。
如果设备有关联的总线,则通过 blocking_notifier_call_chain 发送绑定驱动的通知。这通知总线上的其他部分设备已经被绑定到驱动。最后,使用 kobject_uevent 发送一个 KOBJ_BIND 事件。这个事件通知用户空间,设备已经被绑定到驱动。

driver 注册流程

在 驱动代码 kbase_driver_init 中,当设备初始化后,将会尝试进行驱动的初始化以及注册:

static int __init kbase_driver_init(void)
{
    int ret;
    ret = kbase_platform_register();
    if (ret)
        return ret;
    ret = platform_driver_register(&kbase_platform_driver);
    if (ret)
        kbase_platform_unregister();
    return ret;
}

platform_driver_register会调用 _platform_driver_register:

#define platform_driver_register(drv) \
    __platform_driver_register(drv, THIS_MODULE)

int __platform_driver_register(struct platform_driver *drv,
                struct module *owner)
{
    drv->driver.owner = owner;
    drv->driver.bus = &platform_bus_type;
    drv->driver.probe = platform_drv_probe;
    drv->driver.remove = platform_drv_remove;
    drv->driver.shutdown = platform_drv_shutdown;
    return driver_register(&drv->driver);
}

该函数会设置好 drv 结构体中的各种回调函数。设置 driver 的 bus 类型为 platform_bus_type。目的是将其类型设置为平台设备,被绑定到平台总线( platform_bus )。
然后返回 driver_register() ,该函数目的是在总线上注册驱动。

int driver_register(struct device_driver *drv)
{
    int ret;
    struct device_driver *other;
    if (!drv->bus->p) {
        pr_err("Driver '%s' was unable to register with bus_type '%s' because the bus was not initialized.\n",
               drv->name, drv->bus->name);
        return -EINVAL;
    }
    if ((drv->bus->probe && drv->probe) ||
        (drv->bus->remove && drv->remove) ||
        (drv->bus->shutdown && drv->shutdown))
        printk(KERN_WARNING "Driver '%s' needs updating - please use "
            "bus_type methods\n", drv->name);
    other = driver_find(drv->name, drv->bus);
    if (other) {
        printk(KERN_ERR "Error: Driver '%s' is already registered, "
            "aborting...\n", drv->name);
        return -EBUSY;
    }
    ret = bus_add_driver(drv);
    if (ret)
        return ret;
    ret = driver_add_groups(drv, drv->groups);
    if (ret) {
        bus_remove_driver(drv);
        return ret;
    }
    kobject_uevent(&drv->p->kobj, KOBJ_ADD);
    return ret;
}

该函数会检查driver的bus是否已经初始化,以及driver的回调函数和bus的回调函数是否有冲突。随后使用 driver_find通过查找bus上的driver name 判断驱动是否以及注册。如果没有注册则执行 bus_add_driver 函数添加新 driver 到总线。

int bus_add_driver(struct device_driver *drv)
{
    struct bus_type *bus;
    struct driver_private *priv;
    int error = 0;
    bus = bus_get(drv->bus);
    if (!bus)
        return -EINVAL;
    pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);
    priv = kzalloc(sizeof(*priv), GFP_KERNEL);
    if (!priv) {
        error = -ENOMEM;
        goto out_put_bus;
    }
    klist_init(&priv->klist_devices, NULL, NULL);
    priv->driver = drv;
    drv->p = priv;
    priv->kobj.kset = bus->p->drivers_kset;
    error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
                     "%s", drv->name);
    if (error)
        goto out_unregister;
    klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
    if (drv->bus->p->drivers_autoprobe) {
        error = driver_attach(drv);
        if (error)
            goto out_unregister;
    }
    module_add_driver(drv->owner, drv);
    error = driver_create_file(drv, &driver_attr_uevent);
    if (error) {
        printk(KERN_ERR "%s: uevent attr (%s) failed\n",
            __func__, drv->name);
    }
    error = driver_add_groups(drv, bus->drv_groups);
    if (error) {
        /* How the hell do we get out of this pickle? Give up */
        printk(KERN_ERR "%s: driver_create_groups(%s) failed\n",
            __func__, drv->name);
    }
    if (!drv->suppress_bind_attrs) {
        error = add_bind_files(drv);
        if (error) {
            /* Ditto */
            printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
                __func__, drv->name);
        }
    }
    return 0;
out_unregister:
    kobject_put(&priv->kobj);
    /* drv->p is freed in driver_release()  */
    drv->p = NULL;
out_put_bus:
    bus_put(bus);
    return error;
}

首先获取 driver 的bus 对象,然后使用 kzalloc 为 driver 的私有数据结构体 driver_private 分配内存并对其初始化,将刚分配的 priv 绑定到 driver 中。driver_private 成员:

struct driver_private {
    struct kobject kobj; // 用来表示驱动,用在 sysfs 文件系统中,可以在用户空间暴露内核数据结构和属性。
    struct klist klist_devices;// 一个链表。列表中是该驱动关联的所有设备。
    struct klist_node knode_bus;// 链表节点,会被插入到总线的驱动列表中,被总线管理。
    struct module_kobject *mkobj;// 表示与该驱动相关的内核模块
    struct device_driver *driver;// 指向driver 本身的指针
};

然后调用 kobject_init_and_add 函数初始化驱动的内核对象并将其加入到系统当中,使得 driver 在系统内部有了一个对象,可以通过sysfs访问。然后调用 klist_add_tail 函数将 driver 添加到总线的链表中,bus.p.klist_drivers 就是 总线的驱动列表。如果总线支持自动探测,则调用 driver_attach 函数尝试将驱动程序绑定到设备上。

int driver_attach(struct device_driver *drv)
{
    return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}

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;
}

在 bus_for_each_dev 函数中,通过总线的 devices 链表(klist_devices)遍历寻找 device ,对于每个 device 都执行 __driver_attach 函数:

static int __driver_attach(struct device *dev, void *data)
{
    struct device_driver *drv = data;
    int ret;
    /*
     * Lock device and try to bind to it. We drop the error
     * here and always return 0, because we need to keep trying
     * to bind to devices and some drivers will return an error
     * simply if it didn't support the device.
     *
     * driver_probe_device() will spit a warning if there
     * is an error.
     */
    ret = driver_match_device(drv, dev);
    if (ret == 0) {
        /* no match */
        return 0;
    } else if (ret == -EPROBE_DEFER) {
        dev_dbg(dev, "Device match requests probe deferral\n");
        driver_deferred_probe_add(dev);
    } else if (ret < 0) {
        dev_dbg(dev, "Bus failed to match device: %d", ret);
        return ret;
    } /* ret > 0 means positive match */
    if (driver_allows_async_probing(drv)) {
        /*
         * Instead of probing the device synchronously we will
         * probe it asynchronously to allow for more parallelism.
         *
         * We only take the device lock here in order to guarantee
         * that the dev->driver and async_driver fields are protected
         */
        dev_dbg(dev, "probing driver %s asynchronously\n", drv->name);
        device_lock(dev);
        if (!dev->driver) {
            get_device(dev);
            dev->p->async_driver = drv;
            async_schedule_dev(__driver_attach_async_helper, dev);
        }
        device_unlock(dev);
        return 0;
    }
    device_driver_attach(drv, dev);
    return 0;
}

在 __driver_attach 函数中使用 driver_match_device 函数判断是否驱动和设备是否匹配。如果不匹配,则返回错误;如果支持异步探测(driver_allows_async_probing 函数),则进行异步探测;如果不支持异步探测,则直接使用 device_driver_attach(drv, dev) 绑定驱动和设备。

当驱动与设备匹配成功后,将返回值1返回 __driver_attach 函数中。

static int __driver_attach(struct device *dev, void *data)
{
    ret = driver_match_device(drv, dev);
    …
    device_driver_attach(drv, dev);
    return 0;
}

然后调用device_driver_attach() 函数,尝试将驱动和设备绑定在一起。

int device_driver_attach(struct device_driver *drv, struct device *dev)
{
    int ret = 0;
    __device_driver_lock(dev, dev->parent);
    /*
     * If device has been removed or someone has already successfully
     * bound a driver before us just skip the driver probe call.
     */
    if (!dev->p->dead && !dev->driver)
        ret = driver_probe_device(drv, dev);
    __device_driver_unlock(dev, dev->parent);
    return ret;
}

在进行绑定之前,会判断设备是否已经被移除,以及是否已经加载了驱动。然后再调用 driver_probe_device 函数进行绑定。

接下来的步骤就和 device 注册时一致了。

通过以上分析可以发现,在驱动加载时,会先进行设备的加载,然后再进行驱动的加载,但是不管是 device 还是 driver ,双方加载的同时都会尝试去寻找对方,并且绑定在一起。

Leave a Comment

已有 1 条评论
  1. 真棒!