linux驱动---设备注册 驱动注册与驱动匹配过程整理

设备注册、驱动注册与驱动匹配

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

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:字符串属性,用于匹配设备与设备驱动。一般形式为<manufacturer>,<model>,前者一般为芯片供应商,后者为模块名。
  • 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_driverpwm_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完成属性背光调整及创建用户空间的属性文件。

posted on 2020-06-04 14:27  guoyw  阅读(3940)  评论(0编辑  收藏  举报

导航