【linux kernel】linux的platform设备驱动框架分析

news2024/11/28 0:32:17

文章目录

      • 一、简介
      • 二、platform总线
      • 三、platform设备和驱动的匹配过程
      • 四、platrom驱动和platform设备
      • 五、platform驱动设计
      • 六、代码示例


🔺【linux内核系列文章】

👉对一些文章内容进行了勘误,本系列文章长期不定时更新,希望能分享出优质的文章!

  • 1、《linux内核数据结构分析之哈希表》
  • 2、《一文总结linux内核通知链》
  • 3、《linux内核中的debugfs》
  • 4、《linux内核数据结构分析之链表》
  • 5、《linux media子系统分析之media控制器设备》
  • 6、《V4L2-PCI驱动程序样例分析(上)》
  • 7、《v4l2框架分析之v4l2_fh》
  • 8、《 v4l2框架分析之v4l2_subdev》
  • 9、《 v4l2框架分析之v4l2_device》
  • 10、《v4l2框架分析之video_device》
  • 11、《linux内核重要函数 | do_initcalls》
  • 12、《Linux设备驱动模型 | bus》
  • 13、《linux内核裁剪随想》
  • 14、《基于ARM64分析linux内核的链接脚本vmlinux.lds.S》
  • 15、《linux内核start_kernel函数的早期操作》
  • 16、《start_kernel函数详解系列之proc_caches_init》
  • 17、《start_kernel函数详解系列之fork_init》
  • 18、《start_kernel函数详解系列之rcu_init》
  • 19、《start_kernel函数详解系列之proc_root_init》
  • 20、《start_kernel详解系列之【setup_arch】》
  • 21、《linux内核如何启动用户空间进程(上)》
  • 22、《linux内核如何启动用户空间进程(下)》
  • 23、《一文总结linux内核的完成量机制》
  • 24、《一文总结linux内核设备驱动的注册和卸载》
  • 25、《linux内核的启动加载程序的总结》
  • 26、《linux内核入口:head.o》
  • 27、《挂载根文件系统之rootfs》
  • 28、《mount系统调用剖析》
  • 29、《devtmpfs文件系统分析》
  • 30、《linux内核的kthreadd线程》
  • 31、《linux内核的进程调度—调度策略》
  • 32、《linux系统调用实践(Arm架构)》
  • 33、《对linux内核__init机制的实践》
  • 34、《linux 内核中EXPORT_SYMBOL()分析与实践》
  • 35、《linux内核如何挂载根文件系统》
  • 36、《linux内核如何唤醒线程》
  • 37、《linux内核的init线程》
  • 38、《linux内核伪文件系统—sysfs分析》
  • 39、《linux 内核设备模型的初始化(上)》
  • 40、《linux 内核设备模型的初始化(下)》
  • 41、《linux内核伪文件系统—proc分析》
  • 42、《linux中断管理—workqueue工作队列》
  • 43、《linux中断管理—软中断》
  • 44、《linux中断管理 | tasklet》
  • 45、《linux中断管理 | 中断管理框架(01)》
  • 46、《linux内存管理 | 分配物理内存页面》
  • 47、《linux内存管理 | 释放内存页面》
  • 48、《对linux内核设备的注册机制和查找机制分析》
  • 49、《linux内核设备驱动的注册机制》

一、简介

相关文件;

  • /include/linux/platform_device.h
  • /drivers/base/platform.c

在linux设备驱动中,有许多没有特定总线的外设驱动,在实际开发中,又需要使用到总线、驱动和设备模型这三个概念,故而linux提供了platform这个虚拟总线,挂接在platform总线上的驱动称为platform驱动,由struct platform_driver描述,挂接在platorm总线上的设备称为platform设备,由struct platform_device描述。

在linux内核的驱动源码中,可以看见很多基于platform驱动框架的驱动案例实现。

二、platform总线

在linux内核中,使用struct bus_type描述一个总线,为了抽象出platform这个虚拟总线,其定义如下(/drivers/base/platform.c):

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总线的注册由platform_bus_init()完成:

int __init platform_bus_init(void)
{
	int error;

	early_platform_cleanup();

	error = device_register(&platform_bus);
	if (error)
		return error;
	error =  bus_register(&platform_bus_type);
	if (error)
		device_unregister(&platform_bus);
	of_platform_register_reconfig_notifier();
	return error;
}

该函数在linux内核启动过程中,在driver_init()中被调用,从而向linux内核注册了platform总线。

三、platform设备和驱动的匹配过程

在定义platform总线的时候就定了该总线下设备和驱动的具体匹配过程,由platform_match()实现:

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

从上述代码可知,platform设备和驱动的匹配分为了四种方式处理:

  • 1、基于设备树的匹配方式。

struct device_driver结构中有个名为of_match_table的成员变量,此成员变量保存着驱动的compatible匹配表,
在设备树中的每个设备节点的compatible属性会和of_match_table表中的所有成员比较,查看是否存在相同的条目,如果存在则表示设备和此驱动匹配,设备和驱动匹配成功以后probe函数就会执行(这个过程是由linux设备驱动模型中的总线去完成)。

  • 2、ACPI的匹配方式。

  • 3、id_table 匹配。

每个struct platform_driver有一个id_table成员变量,用于保存很多id信息,这些id信息存放着这个platform驱动所支持的驱动类型。

  • 4、比较name字段

如果第三种匹配方式的id_table不存在,就直接比较驱动和设备的name字段是否相等,如果相等则匹配成功;反之匹配不成功。

一般设备驱动为了兼容性都支持设备树和无设备树两种匹配方式。也就是第一种匹配方式一般都会存在,第三种和第四种只要存在一种就可以,一般
用的最多的还是第四种,也就是直接比较驱动和设备的name字段,因为这种方式最简单了。

四、platrom驱动和platform设备

前文已经提到:挂接在platform总线上的驱动称为platform驱动,由struct platform_driver描述,挂接在platorm总线上的设备称为platform设备,由struct platform_device描述。要想开发基于platform设备驱动驱动框架的驱动程序,一定离不开这两个数据结构。首先来看看platform驱动的描述者struct platform_driver,该结构定义如下(/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函数就会执行,这是一个非常重要的函数,一般驱动的提供者都会设计该函数。
  • remove:当platform驱动移除的时候,.remove指向的函数将执行。
  • shutdown、suspend和resume:与电源管理相关的函数。
  • driver:为device_driver结构体变量,相当于C++中的基类,提供了最基础的驱动框架。plaform_driver继承了这个基类,然后在此基础上又添加了一些特有的成员变量。
  • id_table:描述platform设备的id_table表,platform总线匹配驱动和设备的时候会使用。
  • prevent_deferred_probe:布尔类型变量(内部参数),用于防止驱动程序请求延迟probe,以避免进一步的徒劳的探测尝试。

再看看platform设备的描述者struct platform_device,定义如下(/include/linux/platform_device.h):

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;
};
  • name :name表示设备名字,该参数要和所使用的platform驱动的name字段相同,否则设备就无法匹配到对应的驱动。
  • id:设备id。
  • dev:linux内核面向对象的具体体现,用于描述platform_device的基类。
  • num_resources:表示资源的数量。
  • resource:表示资源,也就是设备的信息,比如外设寄存器等。Linux内核使用struct resource结构体表示资源。
  • id_entry:platform设备对应的id匹配表实例,在platform总线匹配驱动和设备的时候会使用到。

五、platform驱动设计

platform驱动设计的总体思路分为两种:

  • (1)使用【struct platform_device + struct platform_driver】的方式实现。

在这种实现方式中,需要实现描述设备信息的struct platform_device结构,并需要使用platform_device来描述具体的设备信息,然后使用platform_device_register()函数将设备信息注册到 Linux 内核中;如果不再使用platform了,可以通过platform_device_unregister()函数注销相应的platform设备。

这种方式在不支持设备树的linux内核中使用!

  • (2)使用【struct platform_driver + 设备树】的方式来实现。

在编写 platform 驱动的时候,首先定义一个struct platform_driver结构体变量,然后实现结构体中的各个成员变量,重点是实现匹配方法以及probe函数。当驱动和设备匹配成功以后.probe函数就会执行,具体的驱动程序在 probe 函数里面编写。当定义并初始化好 platform_driver 结构体变量以后,需要在驱动入口函数里面调用platform_driver_register()函数向Linux内核注册一个platform驱动。

注意,如果linux内核支持设备树,就可以不需要再使用struct platform_device来描述设备,直接使用设备树去描述设备的信息。当然,如果
一定要用struct platform_device来描述设备信息也是可以的。
基于新版的linux内核的platform驱动的开发,通常是通过设备树来描述设备信息,我们只需要实现对应的platform驱动即可。

六、代码示例

本小节基于【struct platform_driver + 设备树】给出一个基本的platform驱动的设计结构。

首先使用设备树描述设备的信息:

	debug_device_node {
		compatible = "iriczhao_debug";
		pinctrl-0 = <&pinctrl_usdhc2_8bit>;
		pinctrl-1 = <&pinctrl_usdhc2_8bit_100mhz>;
		pinctrl-2 = <&pinctrl_usdhc2_8bit_200mhz>;
		bus-width = <8>;
		non-removable;
		status = "okay";
	};

上述代码描述了一个名为debug_device_node的设备节点,给出了compatible属性值。

platform驱动设计:

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>

#include <linux/kthread.h>

#include <linux/platform_device.h>

#include <linux/delay.h>

static int platform_demo_probe(struct platform_device *dev)
{   printk("\r\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\r\n");
    printk("do platform_demo_probe\r\n");
    printk("\r\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\r\n");

    return 0;
}

static int platform_demo_remove(struct platform_device *dev)
{
    printk("do platform_demo_remove\r\n");
    return 0;
}

static const struct of_device_id platform_demo_id[] = {
    { .compatible = "iriczhao_debug" },
    { /* Sentinel */ }
};

MODULE_DEVICE_TABLE(of, platform_demo_id);

static struct platform_driver platform_demo_driver = {
    .probe = platform_demo_probe,
    .remove = platform_demo_remove,
    .driver = {
        .name = "dd",
        .of_match_table = platform_demo_id,
    }
};


static int __init platform_demo_init(void)
{
    printk("do platform_demo_init\r\n");

    return platform_driver_register(&platform_demo_driver);
}

static void __exit platform_demo_exit(void)
{
    printk("do platform_demo_exit\r\n");
    
    platform_driver_unregister(&platform_demo_driver);
}

module_init(platform_demo_init);
module_exit(platform_demo_exit);

MODULE_AUTHOR("IRIC");
MODULE_LICENSE("GPL");

以模块方式构建上述代码,运行后结果如下:


从上述结果可知:
platform驱动和对应的设备匹配成功,且.probe指向的函数得以执行,当模块退出时,platform驱动将被移除,这时候.remove指向的函数得以执行。结果符合程序预期效果!

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

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

相关文章

可视化上证50结构图

可视化上证50结构图 缘由收集数据先获取50支成分股列表获取各成分股票K线数据 数据处理找出来&#xff0c;再删除&#xff0c;然后重新下载数据最终获得每日报价的变化值 图形结构处理聚类分析使用affinity_propagation(亲和传播)聚类 嵌入二维平面空间可视化小结热力图 缘由 …

Excel和图片如何互相转换?有何技巧?

一、将图片转为excel表格 首先&#xff0c;打开金鸣识别网站&#xff0c;点击“点击添加需转换的图片或PDF”按钮&#xff0c;添加待识别的图片或PDF文件。 添加完待识别的图片或PDF后&#xff0c;点击“提交识别”按钮&#xff0c;程序便开始识别。 识别完成后&#xff0c;系…

无效的 page.json [“window“] 页面.json配置了“window“: {“disableScroll“: true}

问题&#xff1a;启动小程序时报错 无效的 page.json ["window"] 页面 解决&#xff1a; app.json 全局配置才使用window对象&#xff0c;在单独的页面直接写disableScroll:true即可 //app.json中添加&#xff0c;window里面添加就可以了 "window": { …

儿童写作业用的护眼灯哪种好?双十一写作业护眼灯推荐

这些年大家对于身体健康越来越重视&#xff0c;尤其是关于儿童青少年的眼睛健康问题&#xff0c;因此作为学生们常用的护眼台灯也成为了非常多家长为孩子选择的学习台灯。而还没给孩子准备台灯的家长们也都想买上一盏台灯给孩子使用&#xff0c;但市面上的护眼台灯太多了&#…

【ElasticSearch】学习笔记

【ElasticSearch】学习笔记 【一】ElasticSearch是什么&#xff1f;【二】Mac安装ElasticSearch【三】Mac安装可视化界面Kibana 【一】ElasticSearch是什么&#xff1f; ElasticSearch是一个分布式、Restful风格的搜索和数据分析引擎&#xff0c;Stark的核心。 可以应用在比如…

XnViewMP forMac/Windows中文版:轻松管理和浏览您的图片库

您是否厌倦了使用不方便、功能有限的图片浏览软件&#xff1f;现在&#xff0c;让我向您介绍一款强大而全面的图片浏览软件——XnViewMP&#xff01; XnViewMP是一款免费开源的跨平台图片浏览软件&#xff0c;它具有出色的功能和易用性&#xff0c;适用于个人用户和专业摄影师…

助力森林火情预警检测,基于YOLOv7-tiny、YOLOv7和YOLOv7x开发构建无人机航拍场景下的森林火情检测是别预警系统

火情的预警与检测识别对于保障林业安全&#xff0c;减少人员伤亡有着重要的意义&#xff0c;科学有效地早发现早扑灭是最有效的干预手段&#xff0c;本文的主要是想就是想要建立基于无人机航拍场景下的森林火情检测预警系统&#xff0c;整体效果如下所示&#xff1a; 这里文中选…

TikTok整合谷歌搜索功能,探索新的流量入口

随着社交媒体平台的不断崛起和发展&#xff0c;用户们的需求也在不断演变。如今&#xff0c;人们不仅仅是在社交媒体上分享自己的生活点滴&#xff0c;还希望从中获取更多有用的信息。 因此&#xff0c;社交媒体平台正积极寻找与搜索引擎的整合方式&#xff0c;以满足用户的多…

小程序开发平台源码系统+万能门店小程序功能+完整的搭建教程

大家好啊&#xff0c;今天来给大家分享一个小程序开发平台&#xff0c;这款平台源码系统中包含了万能门店小程序功能&#xff0c;一起来看看吧。 系统特色功能一览&#xff1a; 全端覆盖&#xff1a;独立版万能门店全端云小程序可以一键生成全端小程序&#xff0c;包括微信小程…

2023_Spark_实验十三:Spark RDD 求员工工资总额

一、主题&#xff1a;Spark RDD 求员工工资总额及排名 问题提出&#xff1a;近三年来&#xff0c;全球新冠疫情已经严重影响了现有经济情况&#xff0c;公司高层领导对公司运行情况进行深入了解&#xff0c;需要了解每个部门的人力成本&#xff0c;以至于更加合理的优化人力资…

从头开始机器学习:神经网络

一、说明 如果你还没有做过逻辑回归&#xff0c;你会在这里挣扎。我强烈建议在开始之前查看它。您在逻辑回归方面的能力将影响您学习神经网络的难易程度和速度。 二、神经网络简介 神经网络是一个神经元网络。这些神经元是逻辑回归函数&#xff0c;它们被链接在一起形成一个网络…

vim基础命令批量替换

正常模式 v&#xff08;小写&#xff09;正常模式下面会出现VISUAL&#xff0c;可以可以选择指定连续区域可以随意选择&#xff0c;这个用的最多 ctrl v&#xff08;小写&#xff09; 这个可以类似于sublime这种按照固定前几个字符选择 最后一列哪个a其实已经选择了&#xf…

论文导读 | 八月下旬特征选择专题期刊精选

推文作者&#xff1a;丰于杭 编者按 在“八月下旬特征选择专题期刊精选”中&#xff0c;我们有主题、有针对性地选择了MSOM, Operations Research, Management Science等管理科学杂志中一些有趣的文章&#xff0c;不仅对文章的内容进行了概括与点评&#xff0c;而且也对文章的结…

c++视觉处理----图像模板匹配

模板匹配matchTemplate() matchTemplate() 是OpenCV中用于模板匹配的函数之一。它的主要作用是在一幅图像中搜索模板图像的位置&#xff0c;即找到模板在图像中的匹配位置。 函数原型如下&#xff1a; void cv::matchTemplate(InputArray image,InputArray templ,OutputArra…

生物信息学研究方向

一、生信的两个大类研究方向&#xff1a; 目录 一、生信的两个大类研究方向&#xff1a; 前者偏理论&#xff0c;后者偏向证明 第一类研究方向&#xff1a; 第二类研究方向&#xff1a; 二、具体的研究方向 1、序列分析 2、计算进化生物学 3、生物多样性的度量 4、蛋…

云原生与服务网格

云原生与服务网格 目录 文章来源 【优点知识】 istio课程大纲&#xff1a; https://youdianzhishi.com/web/course/1047 1、云原生技术范畴解读 什么是云原生 2大架构特征&#xff1a;不可变基础设施&#xff0c;声明式API CNCF 云原生整体视图 provisioning 供应 compli…

摩尔信使MThings数据配置参数详述

摩尔信使MThings支持丰富的数据配置方法&#xff0c;以适配一定程度的非标Modbus场景。 针对标准Modbus数据协议&#xff0c;用户仅需修改少量的配置&#xff0c;如寄存器地址、数量&#xff0c;其他参数默认即可。 同时为了便于用户全面了解配置参数&#xff0c;文中详述了每…

我献出这篇 go 精华总结,阁下该如何应对

文章目录 1.初识包管理2.输出3.注释4.初识数据类型5.变量5.1 声明变量的意义&#xff1f;5.2 变量名要求5.3 变量简写5.4 作用域5.5 赋值及内存相关注意事项 阶段练习题6.常量6.1 因式分解6.2 全局6.3 iota 7.输入8.条件语句8.1 最基本8.2 多条件判断8.3 嵌套 9. 荐书 《Go编程…

什么是Token?一文带你深入理解Token

Token在各种技术领域和应用场景中都扮演着重要的角色&#xff0c;它的含义和用途也是非常广泛的。下面我将从不同角度对Token进行深入解析&#xff0c;带你全面了解Token的含义、用途和重要性。 一、计算机科学和网络安全 在计算机科学和网络安全领域&#xff0c;Token通常指的…