设备树platform_driver,of_match_ptr

设备树笔记

一,设备树platform_driver示例

1.设备树中设备需要定义一个设备节点,包含设备的相关信息和属性。例如:设一个按键设备 “my_key”

/ {		//根节点
	compatible = "my_key_demo";  //根节点的兼容性,
	my_key{		//设备节点 “my_key”
		compatible = "example,my_key";
		mykey-gpios = <&gpio0 RK_PB0 GPIO_ACTIVE_LOW>;
        reg = <0x00001234 0x0800>;
        interrupt-parent = <&gpio0>;
        interrupts = <10 IRQ_TYPE_LEVEL_HIGH>;
	}
};

在上述示例中,该设备节点具有

compatible: 属性来标识设备的兼容性,作为驱动和设备(设备节点)的匹配依据
reg :属性用于指定设备的地址范围,
interrupt-parent :属性指定中断控制器的父节点,
interrupts :属性指定设备的中断号和触发类型。

2.在驱动程序中定义 platform_driver 结构体,并使用 of_match_ptr() 宏将设备的兼容性标识与驱动程序关联起来

#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_device.h>

static int mykey_probe(struct platform_device *pdev)
{
    // 在这里执行设备的初始化和操作
	gpio_key = devm_gpiod_get(dev, "mykey",GPIOD_OUT_LOW);
	if (IS_ERR(gpio_key )) 
	{
		ret = PTR_ERR(gpio_key);
	}

    return 0;
}

static int mykey_remove(struct platform_device *pdev)
{
    // 在这里执行设备的清理和操作
    return 0;
}

static const struct of_device_id my_driver_of_match[] = {
    { .compatible = "example,my_key" },
    {},
};
MODULE_DEVICE_TABLE(of, my_driver_of_match);

static struct platform_driver my_driver = 
{
	.probe = mykey_probe,
	.remove		= mykey_remove, 
	.driver		= {
		.name	= "my_key", 
		.of_match_table = of_match_ptr(my_driver_of_match),
	},
};

static int mykey_init(void)
{
	int retval = 0;
	retval = platform_driver_register(&my_driver );
	if (retval < 0)
		return retval;
	return 0;
}

static void mykey_exit(void)
{
	platform_driver_unregister(&my_driver );
}

module_init(mykey_init); 
module_exit(mykey_exit);

MODULE_AUTHOR("zs");
MODULE_LICENSE("GPL");

注意点:调用devm_gpiod_get时的注意点,使用该方法时,给方法传递的第二个参数一定要特别注意,追踪该方法在内核中的具体实现可知:devm_gpiod_get最终调用的是gpiolib.c中的“__gpiod_get_index”方法,而该方法最终调用的是“of_find_gpio”方法,查看该方法的实现可知,方法在查找GPIO资源时已经自动为我们加了“-gpio”、“-gpios”后缀,所以我们在调用devm_gpiod_get时,要对匹配字符串进行相应的修改。
比如在DTS中有如下的资源:

mykey-gpios = <&gpio0 RK_PB0 GPIO_ACTIVE_LOW>;

那么在调用devm_gpiod_get去获取该资源时,第二个参数直接传"mykey"就可以了,而不是传递“mykey-gpios ”。

二,platform驱动总线的匹配方式

platform_driver、platform_device 和device_driver结构体的定义:

2.1:platform_driver

struct platform_driver 是Linux内核中用于表示平台驱动程序的结构体。它包含了与平台设备驱动相关的属性和操作,用于管理和控制平台设备。
下面是 struct platform_driver 结构体的定义:

struct platform_driver {
    int (*probe)(struct platform_device *pdev);
    int (*remove)(struct platform_device *pdev);
    void (*shutdown)(struct platform_device *pdev);
    int (*suspend)(struct platform_device *pdev, pm_message_t state);
    int (*resume)(struct platform_device *pdev);
    struct device_driver driver;                  // device_driver结构体**
    const struct platform_device_id *id_table;    // 匹配规则三
    bool prevent_deferred_probe;
};

struct platform_driver 结构体的主要字段如下:
	probe: 当匹配到某个平台设备时,调用该函数进行设备的初始化。
	remove: 在设备被移除时,调用该函数进行设备的清理和释放资源操作。
	shutdown: 可选字段,在系统关机时调用该函数进行设备的关机处理。
	suspend: 可选字段,在设备进入挂起状态时调用该函数进行设备的挂起操作。
	resume: 可选字段,在设备从挂起状态恢复时调用该函数进行设备的恢复操作。
	driver: 包含了和设备驱动相关的属性,如名称、owner等。
	id_table: 可选字段,用于指定平台设备的标识表,用于驱动程序的设备匹配。
	prevent_deferred_probe: 可选字段,用于控制是否延迟设备的探测。
2.2:platform_device

struct platform_device 是 Linux 内核中用于表示平台设备的结构体。它用于描述与特定平台相关的设备,例如 SoC 上的外设或其他与硬件集成的设备。
下面是 struct platform_device 结构体的定义:

struct platform_device {
    const char *name;        // 匹配方式一
    int id;
    struct device dev;
    u32 num_resources;
    struct resource *resource;
    const struct platform_device_id *id_entry;
};

struct platform_device 结构体的主要字段如下:
	name: 设备的名称。该名称通常与设备驱动程序关联。
	id: 设备的 ID。用于标识同一类型的多个设备。
	dev: 表示该平台设备的通用设备结构体。它可以被设备模型用于管理设备对象。
	num_resources: 设备资源的数量。
	resource: 指向设备资源描述符的指针数组。每个资源描述符包含了设备在系统中所需的 I/O 地址、
			  中断等信息。
	id_entry: 指向平台设备标识表的指针,用于驱动程序的设备匹配
2.3:device_driver

struct device_driver 是 Linux 内核中用于表示设备驱动程序的结构体。它包含了与设备驱动相关的属性和操作,用于管理和控制特定类型的设备。
下面是 struct device_driver 结构体的定义:

struct device_driver {
    const char *name;        // 匹配规则一
    struct bus_type *bus;
    struct module *owner;
    const char *mod_name;
    bool suppress_bind_attrs;
    const struct of_device_id *of_match_table;    // 匹配规则二
    int (*probe)(struct device *dev);
    int (*remove)(struct device *dev);
    void (*shutdown)(struct device *dev);
    int (*suspend)(struct device *dev, pm_message_t state);
    int (*resume)(struct device *dev);
    const struct attribute_group **groups;
    const struct dev_pm_ops *pm;
    struct driver_private *p;
};

struct device_driver 结构体的主要字段如下:
	name: 设备驱动程序的名称。
	bus: 指向设备所属的总线类型的指针。
	owner: 指向拥有该设备驱动程序的模块的指针。
	mod_name: 拥有该设备驱动程序的模块的名称。
	suppress_bind_attrs: 一个布尔值,用于控制是否在 sysfs 中暴露 bind/unbind 相关的属性。
	of_match_table: 指向设备树匹配表的指针,用于匹配设备树节点。
	probe: 当匹配到设备时,调用该函数来初始化设备。
	remove: 在设备被移除时,调用该函数来清理和释放资源。
	shutdown: 可选字段,在系统关机时调用该函数进行设备的关机处理。
	suspend: 可选字段,在设备进入挂起状态时调用该函数进行设备的挂起操作。
	resume: 可选字段,在设备从挂起状态恢复时调用该函数进行设备的恢复操作。
	groups: 指向设备驱动程序所属的属性组数组的指针。
	pm: 指向设备的电源管理操作的指针。

在 Linux 内核中,platform 驱动总线使用设备树中的设备节点来与驱动程序进行匹配。介绍 platform 驱动总线的三种常见的匹配方式。
基于driver.name属性的匹配方式:通过driver.namedevice.name来进行对比匹配

基于 compatible 属性的匹配方式: 设备树中的设备节点通常会包含一个 compatible 属性,用于描述设备的兼容性标识。platform 驱动总线通过比较设备节点的 compatible 属性与驱动程序中定义的 of_device_id 结构体数组中的兼容性标识,来进行匹配。具体示例可以参考前面提到的 of_match_device() 函数的用法。

基于 platform_device_id 的匹配方式: 驱动程序可以定义一个 platform_device_id 结构体数组,并通过 MODULE_DEVICE_TABLE(platform, …) 宏将其注册为可供内核使用的设备表。设备节点在与驱动程序匹配时,会通过比较设备节点的 name 属性与驱动程序中定义的 platform_device_id 结构体数组中的设备名称,来进行匹配。具体示例可以参考前面提到的 platform_device_id 的用法。
需要注意的是,platform 驱动总线的匹配方式是基于设备树的,因此要求设备树中正确地描述了设备的配置信息,包括设备节点的兼容性标识和其他属性。

三,of_match_ptr,of_match_device,platform_get_device_id

**of_match_ptr()**是一个用于将 of_device_id 结构体数组传递给 of_match_table 字段的宏。它将 of_device_id 结构体数组的指针转换为 const struct of_device_id * 类型,以便在注册 platform_driver 时使用。
of_match_table 是 platform_driver 结构体中的一个字段,用于指定设备树匹配表。通过传递 of_match_ptr() 宏转换后的指针,可以将设备树匹配表与驱动程序关联起来,实现设备树节点和驱动程序的匹配。


static struct platform_driver my_driver = 
{
	.probe = mykey_probe,
	.remove		= mykey_remove, 
	.driver		= {
		.name	= "my_key", 
		.of_match_table = of_match_ptr(my_driver_of_match),
	},
};

使用了 of_match_ptr() 宏将 my_driver_of_match 数组的指针传递给了 of_match_table 字段。这样,当 platform_driver 注册到内核时,内核就能根据设备树节点的兼容性标识进行匹配,并调用相应的回调函数。

  • 28
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值