Linux下platform驱动框架描述

news2025/6/22 9:23:05

文章目录

  • 驱动的分离与分层
  • platform模型简介

如果要写复杂的外设驱动,Linux系统就要考虑到驱动的可重用性,因此提出了驱动的分离与分层这样的软件思路,platform设备驱动就是在这种思路下诞生的,其也称为平台设备驱动。
对于Linux这样一个成熟、庞大、复杂的操作系统,代码的重用性非常重要,否则就会在Linux内核中存在大量无意义的重复代码。

驱动的分离与分层

最简单的思路写出来的驱动应该如下图所示。
在这里插入图片描述
但是设备驱动都是一样的,因此没必要每个平台都写一个,所以通过提供一个统一的接口,让每个设备通过这个接口来访问。
在这里插入图片描述
实际中的设备驱动类型很多,总体的架构应该如下图所示。
在这里插入图片描述
这就是Linux中总线、驱动和设备模型,即驱动分离,总线负责给驱动和设备牵线搭桥。当我们向系统注册一个驱动的时候,总线就会在右侧的设备中查找,看看有没有与之匹配的设备,如果有的话就将两者联系起来。同样的,当向系统中注册一个设备的时候,总线就会在左侧的驱动中查找看有没有与之匹配的设备,有的话也联系起来。Linux内核中大量的驱动程序都采用总线、驱动和设备模式。
简单介绍一下驱动的分层,input子系统负责管理所有跟输入有关的驱动,包括键盘、鼠标、触摸等,最底层的就是设备原始驱动,负责获取输入设备的原始值,获取到的输入事件上报给input核心层。input核心层会处理各种IO模型,并且提供file_operations操作集合,在编写输入设备驱动的时候只需要处理好输入事件的上报即可,至于如何处理这些上报的输入事件那是上层去考虑的。


platform模型简介

上面提到总线、驱动和设备这种模式,但是在SOC中有些外设是没有总线这个概念的,Linux提出了platform这个虚拟总线,相应
的就有platform_driver和platform_device。
Linux系统内核使用bus_type结构体表示总线,此结构体定义在文件include/linux/device.h文件中。

int (*match)(struct device *dev,struct device_driver *drv);

结构体中的match函数就是完成设备和驱动之间匹配的,总线使用match函数来根据注册的设备查找对应的驱动,或者根据注册的驱动来查找相应的设备,所以每一条总线都必须要实现此函数。
platform总线是bus_type的一个具体实例,定义在文件drivers/base/platform.c,platform总线定义如下。

struct bus_type platform_bus_type = {
	.name		= "platform",
	.dev_groups	= platform_dev_groups,
	.match		= platform_match,
	.uevent		= platform_uevent,
	.pm		    = &platform_dev_pm_ops,
};

platform_bus_type就是platform 平台总线,其中platform_match 就是匹配函数,该函数也定义在文件platform.c中,函数内容如下。

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

	/* fall-back to driver name match */
	return (strcmp(pdev->name, drv->name) == 0);
}

该函数中定义了四种驱动和设备匹配的方式,第一种是of类型的匹配,也就是设备树采用的匹配方式,of_driver_match_device函数定义在文件include/linux/of_device.h中。device_driver结构体中有个名为of_match_table的成员变量,此成员变量保存着驱动的compatible匹配表,设备树中的每个设备节点的compatible 属性会和of_match_table表中的所有成员比较,查看是否有相同的条目,如果有的话就表示设备和此驱动匹配,设备和驱动匹配成功以后probe函数就会执行。第二种是ACPI匹配方式。第三种是id_table匹配,每个platform_driver结构体有一个id_table成员变量,里面保存了很多id信息,这些id信息存放着这个platform驱动所支持的驱动类型。第四种是直接比较驱动和设备的name字段,如果相等的话就匹配成功。
platform_driver结构体表示platform驱动, 此结构体定义在文件include/linux/platform_device.h中,如下所示。

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

当驱动与设备匹配成功以后probe函数就会执行,一般驱动的提供者会编写,如果自己要编写一个全新的驱动,那么probe就需要自行实现。
driver成员为device_driver结构体变量,Linux内核里面大量使用到了面向对象的思维,device_driver相当于基类,提供了最基础的驱动框架。plaform_driver继承了这个基类,然后在此基础上又添加了一些特有的成员变量。
device_driver结构体定义在include/linux/device.h中,其原型如下。

struct device_driver {
	const char		*name;
	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;

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

其中of_match_table就是采用设备树的时候驱动使用的匹配表,每个匹配项都为of_device_id结构体类型,此结构体定义在文件include/linux/mod_devicetable.h中,如下所示。

struct of_device_id {
	char	name[32];
	char	type[32];
	char	compatible[128];
	const void *data;
};

对于设备树而言,就是通过设备节点的compatible属性值和of_match_table中每个项目的compatible成员变量进行比较,如果有相等的就表示设备和此驱动匹配成功。
在编写platform驱动的时候,首先定义一个platform_driver结构体变量,然后实现结构体中的各个成员变量,重点是实现匹配方法以及probe函数。当驱动和设备匹配成功以后probe函数就会执行,具体的驱动程序在probe函数里面编写。
定义并初始化好platform_driver结构体变量以后,需要在驱动入口函数里面调用platform_driver_register函数向Linux内核注册一个platform驱动,platform_driver_register函数原型如下。

int platform_driver_register (struct platform_driver *driver)

退出时卸载platform驱动,platform_driver_unregister函数原型如下。

void platform_driver_unregister(struct platform_driver *drv)

经过上面的介绍,platform驱动框架大致如下。

struct cdev cdev; 

static int xxx_open(struct inode *inode, struct file *filp)
{
	...
	return 0;
}

static ssize_t xxx_write(struct file *filp, const char __user *buf,size_t cnt, loff_t *offt)
{
	...
	return 0;
}

static struct file_operations xxx_fops = {
	.owner = THIS_MODULE,
	.open = xxx_open,
	.write = xxx_write,
};

static int xxx_probe(struct platform_device *dev)   //驱动和设备匹配成功之后此函数执行,原来init函数的内容放到这里
{
	...
	cdev_init(&xxxdev.cdev, &xxx_fops); /* 注册字符设备驱动 */
	return 0;
}

static int xxx_remove(struct platform_device *dev)  //关闭platform设备驱动的时候执行,原来exit函数的内容放到这里
{
	...
	cdev_del(&xxxdev.cdev);  /* 删除cdev */
	return 0;
}

static const struct of_device_id xxx_of_match[] = {
	{.compatible = "xxx-gpio" },
	{ /* Sentinel */ }
};

static struct platform_driver xxx_driver = {
	.driver = {
		.name = "xxx",
		.of_match_table = xxx_of_match,
	},
	.probe = xxx_probe,
	.remove = xxx_remove,
};

static int __init xxxdriver_init(void)
{
	platform_driver_register(&xxx_driver);
	return 0;
}

static int __exit xxxdriver_exit(void)
{
	platform_driver_unregister(&xxx_driver);
	return 0;
}

module_init(xxxdriver_init);
module_exit(xxxdriver_exit);
MODULE_LICENSE("GPL");

platform_device结构体定义在文件/include/linux/platform_device.h中,如果内核支持设备树的话就不用再使用platform_device来描述设备了,因为改用设备树去描述了。


参考资料:
I.MX6U嵌入式Linux驱动开发指南V1.5——正点原子

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1086501.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

一次Go项目进程重启故障问题排查

有个go项目的容器近两天几乎每天都异常重启一次,且两个节点基本都是差不多的时间异常重启。看了监控指标,发现CPU平稳,而内存是缓慢涨上去后,进程被操作系统kill掉,导致pod重启。 从内存指标可以看出,不会…

大量需求测不过来怎么破?

互联网测试少,测试研发比大概在1:5,再加上产品再使足了劲上需求,导致了测试需求量大,测试准备时间短,从而降低了上线质量。那么如何解决呢?测试是质量负责人,要对平台质量负责,于是就…

解决方案 | 法大大电子签精准击破销售场景签约难题

新商业形态及新交易模式不断涌现,电子签已经成为现代商业活动中不可或缺的一部分。特别是在销售场景中,电子签的应用不仅可以提高销售效率,还可以降低成本,提高客户满意度。本文将详细分析电子签在销售场景中的应用价值能力&#…

{2023版}老牌配资平台排行报告:十大排名和实盘评估详情

随着投资者对股市的深入了解,越来越多的人开始选择配资交易。在配资平台的选择上,除了要注意平台的合法性和安全性外,平台的口碑和服务质量也是非常重要的考虑因素。为了方便投资者的选择,尚红网、倍悦网、兴盛网、诚利和、嘉正网…

Jetson Orin NX 开发指南(4): 安装 CUDA 和 Realsense

一、硬件说明 可以在官网查看各个类型的相机说明 Intel RealSense™ Computer Vision - Depth and Tracking cameras 目前实验室常用的是 D400 系列的相机,如 D435i相机,D455相机等,本文主要使用 D435i 相机 D435i 相机是一个RGB-D相机&am…

iOS 面试题以及自我理解答案

1、简述push原理,push的证书和其他的有什么不一样? 第 一阶段:BeejiveIM服务器把要发送的消息、目的iPhone的标识打包,发给APNS。 第二阶段:APNS在自身的已注册Push服务 的iPhone列表中,查找有相应标识的iP…

pdf怎么压缩的小一点?分享pdf文件压缩方法

在数字化时代,PDF文件已成为我们日常工作和学习中不可或缺的一部分,然而,有时我们需要发送或存储的PDF文件过大,给我们的交流和存储带来不便。那么,如何将PDF文件压缩得小一点,以便更方便地传输和存储呢&am…

Android Studio for Platform (ASfP) 使用教程

文章目录 编写脚本下载源代码lunch 查看版本 归纳的很清楚,下载Repo并下载源码->可以参考我的 Framework入门のPiex 6P源码(下载/编译/刷机) 启动图标(重启生效) [Desktop Entry] EncodingUTF-8 NameAndroidStudio …

NCV6324CMTAATBG---车规级3MHz 2A 高效同步降压转换器

同步降压转换器? 是一种电源管理电路,它可以将输入电压转换为较低的输出电压。与传统的降压转换器相比,同步降压转换器具有更高的效率和更好的动态响应。 同步降压转换器的工作原理是通过控制开关管的导通和截止来实现电能的转换。在导通状…

Java架构师数据库设计

目录 1 导学2 数据库设计3 数据库设计的基本步骤3.1 优秀的数据库设计的基本原则3.1 数据库冗余设计4 数据库表逻辑关系设计4.1 MySQL设计表结构工具4.2 数据库建模工具5 数据库表结构设计5.1 MySQL调优5.2 SQL语句优化6 总结1 导学 数据库设计作为架构师的核心设计能力之一,对…

c++视觉处理---图像重映射

图像重映射:cv::remap cv::remap 是OpenCV中的一个函数,用于执行图像重映射,允许您通过重新映射像素的位置来变换图像。这个函数非常有用,可以用于各种图像处理任务,如校正畸变、透视变换、几何变换等。 下面是 cv::…

Maven系列第5篇:私服详解

maven系列目标:从入门开始开始掌握一个高级开发所需要的maven技能。 这是maven系列第5篇。 整个maven系列的内容前后是有依赖的,如果之前没有接触过maven,建议从第一篇看起,本文尾部有maven完整系列的连接。 环境 maven3.6.1 …

ctfshow萌新计划web9-14(正则匹配绕过)

目录 web9 web10 web11 web12 web13 web14 web9 审一下代码,需要匹配到system|exec|highlight才会执行eval函数 先看一下当前目录下有什么 payload:?csystem(ls); index.php是首页,我们看看config.php payload:?csystem…

【FreeRADIUS】使用FreeRADIUS进行SSH身份验证

在数据中心中,可能有许多Linux机器,由管理员团队进行管理。这些管理员通过SSH访问这些服务器。如果希望使用一个集中的位置来管理这些管理员的身份验证。那么为了实现这一点,可以使用FreeRADIUS服务器。FreeRADIUS是一个功能强大的开源工具&a…

SwinTransformer学习记录(一)之整体架构

SwinTransformer自问世以来,凭借其优秀的性能,受到无数研究者的青睐,因此作为一个通用的骨干网络,其再目标检测,语义分割,去噪等领域大杀四方,可谓是风光无限,今天,我们便…

Linux防火墙配置

目录 一、firewalld防火墙简介二、firewalld放行端口方法三、firewalld放行服务方法四、firewalld富规则配置方法一、firewalld防火墙简介 Firewalld防火墙是CentOS 7系统默认的防火墙管理工具,它取代了之前版本的iptables防火墙。Firewalld工作在网络层,属于一种包过滤型防…

C语言,求质因数中的较大的值

首先要求出输入的质数的两个质因数,就要用到判断素数时用到的方法。用内循环产生2到n的数字,当求到了质因数之后,也要先用一个变量将质因数存起来,当后面遇到更大的质因数时,再将原来的质因数覆盖,如果更小…

SystemVerilog Assertions应用指南 第一章

1.1什么是断言 断言是设计的属性的描述。 ● 如果一个在模拟中被检查的属性( property)不像我们期望的那样表现,那么这个断言失败。 ● 如果一个被禁止在设计中出现的属性在模拟过程中发生,那么这个断言失败。 一系列的属性可以从设计的功能描述中推知,并且被…

恒驰服务 | 5大服务保障,轻松助力企业实现数据库迁移

随着经营规模的迅猛发展,IT信息系统在企业的日常运营中正发挥着越来越重要的作用。确保信息系统尤其是作为其核心与基石的数据库系统的安全、稳定、高效和敏捷运行,是支撑企业业务系统正常、可持续运营的核心基础,也是企业信息化部门各级管理…

LeetCode142:环形链表II

给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。 如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数…