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

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

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

一、整体架构

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

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

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

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

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

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

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

【Linux驱动】platform 设备驱动分离(一)—— 驱动分层及相关API_第1张图片

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

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

【Linux驱动】platform 设备驱动分离(一)—— 驱动分层及相关API_第2张图片

 

二、总线

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

【Linux驱动】platform 设备驱动分离(一)—— 驱动分层及相关API_第3张图片

方式一: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 

【Linux驱动】platform 设备驱动分离(一)—— 驱动分层及相关API_第4张图片

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

你可能感兴趣的:(数据结构)