【Linux驱动】platform 设备驱动分离(一)—— 驱动分层及相关API

本文详细阐述了Linux系统中驱动分层的架构,包括设备层管理设备属性信息、驱动层负责设备操作以及总线在设备与驱动间的中介作用。重点介绍了在没有设备树时的手动注册与使用设备树后的自动化过程,以及驱动与设备的匹配策略,如OF类型匹配、ACPI匹配和id_table匹配。
摘要由CSDN通过智能技术生成

以目前为止的逻辑,无论是获取设备属性信息,还是实现驱动逻辑,都是放在一个驱动模块中。在没有设备树的情况下,如果我们只需要修改设备信息(如寄存器地址),那么我们就需要重新编译整个驱动模块。

很显然,设备信息的变化不应该影响到驱动逻辑的正常运行,这就需要引入驱动分层的概念。

一、整体架构

驱动分层总体可以分为三层

  • 设备层:负责管理设备属性信息,包含了一些外设硬件信息,如寄存器地址、引脚配置信息等
  • 驱动层:负责驱使设备的正常运作,驱动程序借由总线传递控制信号、数据,进而来控制设备
  • 总线:驱动和设备信息的月老,负责设备和驱动程序之间的通信和数据交换

驱动程序要想驱使设备,需要先让驱动与设备匹配,匹配工作由总线负责。只有当左侧的设备与右侧的驱动程序建立联系以后,驱动程序才可以驱使设备。

        (1) 当我们向系统注册一个驱动,总线会在左侧查找是否存在与之匹配的设备;

        (2) 当我们向系统注册一个设备,总线会在右侧查找是否存在与之匹配的驱动;

=============== 不使用设备树 ===============

不使用设备树时需要手动注册 platform 设备和 platform 驱动,手动注册 platform 设备其实就是在向内核添加硬件外设信息。

=============== 使用设备树 ===============

使用设备树后,无需自己手动注册 platform 设备,只需注册 platform 驱动。在开发板上电加载Linux 内核和设备树的时候,会自动将设备树信息转换成 platform_device 类型。

 

二、总线

Linux 内核使用 bus_type 结构体来表示总线,而 platform 总线是 bus_type 的一个实例,定义在 drivers/base/platform.c ,该文件中同样也定义了驱动和设备是否匹配的检测方式。

方式一:of 类型的匹配

设备树中包含 compatible 属性,而驱动中也有一个 of_match_table 成员变量,这个可以看做是驱动中的 compatible 属性。匹配时会检查设备树的compatible属性和驱动的 of_match_table 变量是否包含相同条目,如果存在,说明此设备和驱动匹配。

方式二:ACPI 匹配

驱动程序中包含一系列自己兼容的设备描述符,该设备描述符包含设备类型、供应商ID、设备ID等属性。以此作为xx设备是否与驱动匹配的判断依据。

方式三:id_table 匹配

在Linux内核中使用 platform_driver 结构体来表示 platform 驱动,每个 platform_driver 结构体有一个 id_table 成员变量,保存了很多 id 信息。这些 id 信息存放着这个 platformd 驱动所支持的驱动类型。

方式四:name 字段匹配

直接比较驱动模块 platform_driver 中 device_driver 的 name 变量设备树中的 name 属性(或platform_device的name变量)

三、设备层

在Linux内核中使用 platform_device 结构体来表示 platform 设备,该设备可以是一个真实存在的设备,也可以只是一个虚拟的设备。platform_device 结构体定义在 linux/platform_device.h 文件中。

1、platform_device

这里主要初始化下面三个成员变量:

  • name:后续测试时使用第四种方式匹配,对此就需要用到 platform_device 的name变量
  • resource:资源数组,保存了寄存器相关信息(寄存器的起始、终止地址)
  • num_resources:resource 数组中资源的个数
struct platform_device {
	const char	*name;
	int		id;
	bool		id_auto;
	struct device	dev;
	u32		num_resources;
	struct resource	*resource;

	const struct platform_device_id	*id_entry;
	char *driver_override; /* Driver name to force a match */

	/* MFD cell pointer */
	struct mfd_cell *mfd_cell;

	/* arch specific additions */
	struct pdev_archdata	archdata;
};

2、resource 

resource 结构体保存的是某个寄存器相关内容,resource 结构体的声明保存在 linux/ioport.h

struct resource {
	resource_size_t start;
	resource_size_t end;
	const char *name;
	unsigned long flags;
	struct resource *parent, *sibling, *child;
};
  • start:寄存器的起始地址
  • end:寄存器的终止地址
  • flags:表示资源类型,同样定义在 linux/ioport.h 

3、注册 platform 设备

platform 设备注册接口原型如下:

/**
 * @param pdev    要注册的platform设备
 * @return        成功返回 0 ,失败返回负值
 */
int platform_device_register(struct platform_device *pdev);

4、注销 platform 设备

platform 设备注销接口原型如下:

/**
 * @param drv     要注销的platform设备
 */
void platform_device_unregister(struct platform_device *pdev);

四、驱动层

在Linux内核中使用 platform_driver 结构体来表示 platform 驱动,该结构体定义在 linux/platform_device.h 文件中。

1、platform_driver

需要注意其中的 probe 函数和 remove 函数,当设备和驱动匹配成功,会调用probe函数;当 platform 驱动从内核中移除,会调用 remove 函数。

struct platform_driver {
	int (*probe)(struct platform_device *);
	int (*remove)(struct platform_device *);
	void (*shutdown)(struct platform_device *);
	int (*suspend)(struct platform_device *, pm_message_t state);
	int (*resume)(struct platform_device *);
	struct device_driver driver;
	const struct platform_device_id *id_table;
	bool prevent_deferred_probe;
};

2、device_driver

device_driver 保存与设备匹配时的相关驱动信息,在后续测试时使用第四种方式匹配,对此就需要用到 device_driver 结构体中的 name 变量

struct device_driver {
	const char		*name;            // platform 驱动名称
	struct bus_type		*bus;         // 总线类型

	struct module		*owner;
	const char		*mod_name;	/* used for built-in modules */

	bool suppress_bind_attrs;	/* disables bind/unbind via sysfs */

	const struct of_device_id	*of_match_table;
	const struct acpi_device_id	*acpi_match_table;

    // ...
}

3、注册 platform 驱动

platform 驱动注册接口原型如下(本质是宏):

/**
 * @param drv     要注册的platform驱动
 * @return        成功返回 0 ,失败返回负值
 */
int platform_driver_register(struct platform_driver *drv);

4、获取platform设备资源

platform_get_resource 可获取platform_device 中的 resource 数组,从上面可以了解到 resource 数组保存的是寄存器地址信息。

/**
 * @param pdev    要访问的platform设备
 * @param type    资源类型,对应resource结构体中的 flag 成员
 * @param index   访问resource数组的下标
 * @return        成功返回 resource 数组的首地址,失败返回0
 */
struct resource *platform_get_resource(struct platform_device * pdev,
					                   unsigned int type, 
                                       unsigned int index);

5、注销 platform 驱动

platform 驱动注销接口原型如下:

/**
 * @param drv     要注销的platform驱动
 */
void platform_driver_unregister(struct platform_driver *drv);
  • 20
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值