底层软件 | 设备树、设备注册、驱动注册与驱动匹配

前面我们学习了设备如何和驱动匹配,也学习了设备树。

一直觉得,需要有一篇文章系统、项目般的结合讲解一下,加深一下影响,正当我准备动笔之时,发现了一篇很不错的文章!

就分享给大家一起来看看设备树、设备驱动模型是如何相互结合的。

文章以背光调整设备为例,记录设备描述,设备注册,驱动注册,驱动与设备匹配的过程。

1 设备描述

在设备树dts文件中对设备信息进行描述,使用dts将设备与驱动分离,在不同的平台或目标机上,如果硬件设备资源不通,则只需要变更设备树文件即可,驱动可以保持一致。例如背光设备的dts描述为:

{
    lvds_backlight0: lvds_backlight {                           // 标签:设备名[@设备地址]
        compatible = "pwm-backlight";                           // 设备与驱动匹配的关键字
        pwms = <&lvds0_pwm 0 100000 0>;                         // pwm设备描述 <引用PWM设备结点 极性 周期>

        brightness-levels = < 0  1  2  3  4  5  6  7  8  9      // 背光调整等级
                        10 11 12 13 14 15 16 17 18 19
                        20 21 22 23 24 25 26 27 28 29
                        30 31 32 33 34 35 36 37 38 39
                        40 41 42 43 44 45 46 47 48 49
                        50 51 52 53 54 55 56 57 58 59
                        60 61 62 63 64 65 66 67 68 69
                        70 71 72 73 74 75 76 77 78 79
                        80 81 82 83 84 85 86 87 88 89
                        90 91 92 93 94 95 96 97 98 99
                    100>;
        default-brightness-level = <80>;                        // 默认背光等级
        status = "okay";                                        // 设备结点状态,如果是okay则向kernel注册设备,否则为disable,则不向kernel注册设备信息
    };
}

以上为设备树文件描述lvds_backlight设备节点的节点信息。

  • compatible:字符串属性,用于匹配设备与设备驱动。一般形式为,,前者一般为芯片供应商,后者为模块名。
  • pwms:pwm设备属性,其引用了一个pwm设备节点&lvds0_pwm,节点信息由drivers/pwm/core.c解析成设备。在pwm_bl.c驱动中可以直接get pwm资源及对pwm资源进行操作。
  • brightness-levels:背光调整等级,由pwm_bl.c中的probe进行解析。
  • default-brightness-level:默认背光等级,由pwm_bl.c中的probe进行解析。
  • status:设备状态如果设置成okay,则在解析设备树文件时会将此设备注册到对应的总线上,否则不会自动注册,需要手动注册。

2 设备注册

  • dts文件在编译时,编译成dtb文件,启动kernel时,将dtb的地址传递给kernel,对dtb文件进行解析。开始解析的函数为setup.c文件中的setup_arch()函数中的unflatten_device_tree()对设备树文件进行解析,解析结果放在of_root(of/base.c)的一个链表中。
  • start_kernel()–>rest_init()–>kernel_init()–>kernel_init_freeable()–>driver_init()–>driver_init()–>of_core_init(),将设备树中配置的节点暴露到用户空间的/sys/firmware/devicetree/base目录。其中kset kobj等概念还未完全建立,后续补充
  • 多数设备均注册到platform bus上,如何总线进行注册目前还没有理清楚,在系统目录/sys/bus/platform/devices中列出系统已经注册的设备。

经过kernel的一系列设置arch,初始化操作,kernel将bl_pwm设备注册到platform上,可在文件系统中查看到以下文件:/sys/bus/platform/devices/lvds_backlight,当status属性设置成"disable"时,设备便不会注册,/sys/bus/platform/devices/目录下也不会有相关的设备信息。

3 驱动注册

由于设备注册到platform bus上,因此驱动也注册到platform bus上,platform bus对设备与驱动进行匹配。将驱动注册到platform bus的方法需要使用platform提供的api,具体注册方法如下:

// match table
static const struct of_device_id pwm_backlight_of_match[] = {
    { .compatible = "pwm-backlight" },                                  // 匹配设备中的compatible属性
    { }
};
MODULE_DEVICE_TABLE(of, pwm_backlight_of_match);

...

// platform driver 结构定义
static struct platform_driver pwm_backlight_driver = {
    .driver     = {
    .name       = "lvds_backlight",                                     // 驱动名称
    .pm         = &pwm_backlight_pm_ops,
    .of_match_table = of_match_ptr(pwm_backlight_of_match),             // 匹配设备的配置表,同一个驱动,可以匹配不同的设备
    },
    .probe      = pwm_backlight_probe,                                  // 探针函数,当设备与驱动匹配时调用
    .remove     = pwm_backlight_remove,                                 // 设备离线时调用
    .shutdown   = pwm_backlight_shutdown,                               // shutdown时调用
};

module_platform_driver(pwm_backlight_driver);                           // 宏,展开后调用init 和 exit函数。注册驱动

pwm_backlight_driver和pwm_backlight_of_match均为配置表,设置驱动名,回调函数等,确保能被kernel调用,注册驱动的是platform提供的宏module_platform_driver,具体如下:

// platform_device.h
/* module_platform_driver() - Helper macro for drivers that don't do
 * anything special in module init/exit.  This eliminates a lot of
 * boilerplate.  Each module may only use this macro once, and
 * calling it replaces module_init() and module_exit()
 */
#define module_platform_driver(__platform_driver) \
    module_driver(__platform_driver, platform_driver_register, \
        platform_driver_unregister)

// device.h
/**
 * module_driver() - Helper macro for drivers that don't do anything
 * special in module init/exit. This eliminates a lot of boilerplate.
 * Each module may only use this macro once, and calling it replaces
 * module_init() and module_exit().
 *
 * @__driver: driver name
 * @__register: register function for this driver type
 * @__unregister: unregister function for this driver type
 * @...: Additional arguments to be passed to __register and __unregister.
 *
 * Use this macro to construct bus specific macros for registering
 * drivers, and do not use it on its own.
 */
#define module_driver(__driver, __register, __unregister, ...) \
static int __init __driver##_init(void) \
{ \
    return __register(&(__driver) , ##__VA_ARGS__); \
} \
module_init(__driver##_init); \
static void __exit __driver##_exit(void) \
{ \
    __unregister(&(__driver) , ##__VA_ARGS__); \
} \
module_exit(__driver##_exit);

因此在驱动中只要包含module_platform_driver(pwm_backlight_driver);代码,kernel就会适当时候将此驱动注册到platform bus上。注册成功后,可以在/sys/bus/platform/drivers路径下查看已经注册的驱动。

4 驱动与设备匹配

由于设备与驱动均注册到platform bus上,因此设备的匹配由platform bus完成,具体匹配函数为platform.c中的platform_match(struct device *dev, struct device_driver *drv)函数,其参数为设备结构指针和驱动结构指针,函数实现如下:

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))                   // 使用match表匹配compatible属性
    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;// 匹配ID

    /* fall-back to driver name match */
    return (strcmp(pdev->name, drv->name) == 0);            // 使用驱动名与设备名进行匹配

由匹配函数可知,当有驱动或设备注册后,依次使用match tbl acpi id tbl dev->name drv->name进行匹配,match tbl优先级最高,驱动名匹配优先级最低。只要有一种方式匹配成功,则返回,驱动与设备匹配成功后,调用驱动中设置的回调函数–probe函数。

5 小结

以上,驱动与设备成功绑定,且调用pwm_bl.c中的probe函数,但是到这里,还不能调整pwm以调整背光,也不能在用户空间通过文件操作来动态的修改背光。现有的驱动是借助backlight class完成属性背光调整及创建用户空间的属性文件。

6 原文链接

  • 27
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TrustZone_Hcoco

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值