Linux驱动(三)platform总线驱动

1、前言

Platform总线是Linux内核中用于管理嵌入式系统中的设备的一种总线类型。它允许设备驱动程序通过一组标准的接口与嵌入式系统中的硬件设备进行通信。

Platform总线维护了一个驱动链表和一个设备链表,当有新的设备添加后会通过自身的match函数遍历驱动链表查看是否有驱动与设备匹配,如果匹配成功则执行驱动的probe函数;同样,当有新的驱动载入时,也会通过自身的match函数去遍历设备链表查看是否有设备匹配,如果匹配成功则执行驱动的probe函数。

2、代码框架

2.1 Platform数据结构

platform数据结构在drivers/base/platform.c中声明,其名称为"platform",match函数为platform_match:

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是struct bus_type数据类型,在声明时并未将全部成员赋值,struct bus_type定义在include/linux/device.h中,各个成员变量如下:

struct bus_type {
	const char		*name;  // 名称,表示设备总线的名称
	const char		*dev_name;  // 用于子系统枚举设备
	struct device		*dev_root;  // 用作父设备的默认设备
    // 设备总线上设备的默认属性,使用dev_groups代替
	struct device_attribute	*dev_attrs;	/* use dev_groups instead */  
	const struct attribute_group **bus_groups;  // 总线的默认属性
	const struct attribute_group **dev_groups;  // 设备总线上设备的默认属性
	const struct attribute_group **drv_groups;  // 设备驱动程序在总线上的默认属性

    /* 每当为总线添加新设备或驱动程序时调用,应该返回正值,如果给定设备可以由给定驱动程序处理,否则返回零。
    如果确定驱动程序支持设备不可能,也可以返回错误代码。如果返回 -EPROBE_DEFER,则会将设备排队进行延迟探测*/
	int (*match)(struct device *dev, struct device_driver *drv); 
    // 当添加设备、移除设备或生成其他一些生成uevents的操作时调用,用于添加环境变量
	int (*uevent)(struct device *dev, struct kobj_uevent_env *env);  
    // 当向总线添加新设备或驱动程序时调用,回调特定驱动程序的探测函数以初始化匹配的设备
	int (*probe)(struct device *dev);  
	int (*remove)(struct device *dev);  // 从总线中移除设备时调用
	void (*shutdown)(struct device *dev);  // 在关机时调用以使设备安静
    // 调用以将设备重新上线(在将其脱机后)
	int (*online)(struct device *dev);  
    // 用于将设备脱机以进行热插拔。可能会失败
	int (*offline)(struct device *dev);  
    // 当总线上的设备希望进入睡眠模式时调用
	int (*suspend)(struct device *dev, pm_message_t state);  
	int (*resume)(struct device *dev);  // 用于唤醒总线上的设备
    // 此总线的电源管理操作,回调特定设备驱动程序的电源管理操作
	const struct dev_pm_ops *pm; 
    // 用于将IOMMU驱动程序实现附加到总线并允许驱动程序执行特定于总线的设置的IOMMU特定操作
	const struct iommu_ops *iommu_ops;  

	struct subsys_private *p;  // 驱动程序核心的私有数据,只有驱动程序核心可以访问此数据
	struct lock_class_key lock_key;  // 用于锁验证器的锁类密钥
};
2.12 驱动数据结构

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;  // 用于匹配平台设备ID的指针
	bool prevent_deferred_probe;  // 用于防止延迟探测的布尔值
};

其中成员变量struct device_driver driver,其结构定义于include/linux/device.h中,在 Linux 内核中,每个设备驱动程序都需要包含这个结构体作为其一部分。它包含了驱动程序的名称、所属总线类型、拥有者模块、匹配表、探测、移除、关闭、挂起、恢复等操作的函数指针,以及其他与设备驱动程序相关的信息和操作。这个结构体的作用是使设备驱动程序成为内核驱动模型的一部分,并允许内核对其进行管理和调度。

一个最简单的驱动结构体需要指定probe和struct device_driver driver的name。

struct device_driver {
	const char *name;  // 设备驱动程序的名称
	struct bus_type *bus;  // 设备所属的总线类型

	struct module *owner;  // 拥有该驱动程序的模块
	const char *mod_name;  // 用于内置模块的名称

	bool suppress_bind_attrs;  // 禁用通过sysfs进行绑定/解绑
	enum probe_type probe_type;  // 用于指定探测类型(同步或异步)

	const struct of_device_id *of_match_table;  // 用于匹配设备的Open Firmware表
	const struct acpi_device_id *acpi_match_table;  // 用于匹配设备的ACPI表

	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;  // 驱动程序核心的私有数据,只有驱动程序核心可以访问此数据
};
2.13 设备数据结构

platform_device 结构体定义在文件include/linux/platform_device.h中:

struct platform_device {
	const char *name;  // 平台设备的名称
	int id;  // 平台设备的 ID
	bool id_auto;  // 指示 ID 是否自动分配
	struct device dev;  // 与平台设备相关联的设备结构

	u32 num_resources;  // 平台设备资源的数量
	struct resource *resource;  // 指向平台设备资源数组的指针

	const struct platform_device_id *id_entry;  // 指向平台设备 ID 表的指针
	char *driver_override;  // 用于强制匹配特定驱动程序的名称

	/* MFD cell pointer */
	struct mfd_cell *mfd_cell;  // 指向多功能设备单元的指针

	/* arch specific additions */
	struct pdev_archdata archdata;  // 特定于体系结构的附加信息
};

其中struct device dev成员变量包含 了struct device_driver *driver变量,在此不再展开。一个最简单的设备数据结构需要设置好name。

3、程序测试

编写一个最简单的驱动程序和设备程序进行测试,驱动程序代码如下:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>

static int mydriver_probe(struct platform_device *pdev)
{
    printk("mydriver_probe exe\n");
    return 0;
}

static struct platform_driver my_driver =
{
    .probe = mydriver_probe,
    .driver.name = "platformtest",
};
static int mydriver_init(void)
{
    printk("mydriver_init \n");
    return platform_driver_register(&my_driver);
}
static void mydriver_exit(void)
{
    printk("mydriver_exit \n");
    platform_driver_unregister(&my_driver);
    return;
}
MODULE_LICENSE("GPL");
module_init(mydriver_init);
module_exit(mydriver_exit);

设备程序代码如下:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>

static void my_device_release(struct device *dev)
{
     return;
}

static struct platform_device my_device =
{
    .name = "platformtest",
    .dev.release = my_device_release,
};

static int mydevice_init(void)
{
    printk("mydevice_init \n");
    return platform_device_register(&my_device);
}
static void mydevice_exit(void)
{
    printk("mydevice_exit \n");
    platform_device_unregister(&my_device);
    return;
}
MODULE_LICENSE("GPL");
module_init(mydevice_init);
module_exit(mydevice_exit);

将编译好的两个驱动程序在开发板上进行加载测试:

4、过程分析

4.1 设备分析

在设备程序中,我们声明了static struct platform_device my_device,并将name成员变量赋值为platformtest,然后调用platform_device_register进行注册。

static struct platform_device my_device =
{
    .name = "platformtest",
    .dev.release = my_device_release,
};

在设备加载过程中,内核会遍历整个驱动链表进行匹配,如果匹配成功则会执行驱动对应的probe函数,当驱动先加载,然后加载设备的话会执行以下完整的流程,如果先加载设备的话流程会执行到bus_for_each_drv()后匹配不到:

4.2 驱动分析

在驱动程序中,我们声明了static struct platform_driver my_driver ,并将name成员变量赋值为platformtest,然后调用platform_driver_register进行注册。

static struct platform_driver my_driver =
{
    .probe = mydriver_probe,
    .driver.name = "platformtest",
};

在驱动加载过程中,内核会遍历整个设备链表进行匹配,如果匹配成功则会执行驱动对应的probe函数,当设备先加载,然后加载设备的话会执行以下完整的流程,如果先加载驱动的话流程会执行到bus_for_each_drv()后匹配不到:

4.3 加载测试

在4.1和4.2的图示中,我们在每一步添加了对应的打印信息会在dmsg中打印出来,上面图示中添加的部分printk忘记添加回车和打印数值等,图片就不修改了,大致是一样的。测试先加载设备后加载驱动,执行insmod mydevice.ko后:

执行insmod mydriver.ko后:

卸载两个驱动:

测试先加载驱动后加载设备,先执行insmod mydriver.ko:

执行insmod mydevice.ko:

5、总结

由上述测试可以发现无论先加载设备还是先加载驱动,只要两者可以通过总线的match函数匹配成功就可以执行驱动程序的probe函数,在probe函数中我们可以执行传统驱动程序的相关操作,本文仅介绍一个最简单的platform驱动程序,旨在缕清框架,后续文章再讲解其他内容。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【mac-m1 docker 安装upload-labs靶场】

1.搜索upload-labs docker search upload-labs 2.下载upload-labs docker pull c0ny1/upload-labs 3.启动 docker run -it -d --name uploadlabs -p 80:80 c0ny1/upload-labs --platform linux/amd64 4.访问127.0.0.1:80 注意点&#xff1a;后续使用的时候会报错 需要手动创…

LeetCode-无重复字符的最长子串(3)

题目描述&#xff1a; 给定一个字符串 s &#xff0c;请你找出其中不含有重复字符的 最长子串 的长度。 代码&#xff1a; class Solution {public int lengthOfLongestSubstring(String s) {Set<Character> occnew HashSet<Character>();int lens.length();int…

Local server not started, start with 报错python -m weditor

一、python -m weditor 如图报错 Local server not started, start with 报错 二、解决方案 右上角选择新的无痕窗口下&#xff0c;然后打开 http://localhost:17310/ 即可

VMware Tools 启动脚本未能在虚拟机中成功运行。如果您在此虚拟机中配置了自定义启动脚本,请确保该脚本没有错误。您也可以提交支持请求,报告此问题。

问题描述&#xff1a;今天打开centos7虚拟机就是直接打不开了报了下面的错误&#xff0c;也没有动任何东西&#xff0c;点确定后&#xff0c;也是依然没有反应 问题原因&#xff1a;可能是虚拟机中的内存满了&#xff0c;需要清理内存 解决方法如下 首先cmd打开终端敲入如下命…

linux磁盘管理实验1

1.在安装好的linux系统中新加一块硬盘&#xff0c;将硬盘分成2个主分区&#xff0c;和2个逻辑分区&#xff0c;将其中一个逻辑分区设置成vfat&#xff08;FAT32&#xff09;分区&#xff0c;并实现开机自动挂载所有分区。 答&#xff1a;添加一个硬盘为sdb 分成2个主分区&#…

LLM增强LLM;通过预测上下文来提高文生图质量;Spikformer V2;同时执行刚性和非刚性编辑的通用图像编辑框架

文章首发于公众号&#xff1a;机器感知 LLM增强LLM&#xff1b;通过预测上下文来提高文生图质量&#xff1b;Spikformer V2&#xff1b;同时执行刚性和非刚性编辑的通用图像编辑框架 LLM Augmented LLMs: Expanding Capabilities through Composition 本文研究了如何高效地组…

面试算法96:字符串交织

题目 输入3个字符串s1、s2和s3&#xff0c;请判断字符串s3能不能由字符串s1和s2交织而成&#xff0c;即字符串s3的所有字符都是字符串s1或s2中的字符&#xff0c;字符串s1和s2中的字符都将出现在字符串s3中且相对位置不变。例如&#xff0c;字符串"aadbbcbcac"可以由…

透明OLED屏制作:工艺与技术挑战

透明OLED屏作为一种前沿的显示技术&#xff0c;其制作过程涉及一系列复杂的工艺和技术挑战。作为一名专注于OLED技术研发的工程师&#xff0c;我将为大家深入解析透明OLED屏的制作过程&#xff0c;以及所面临的挑战。 首先&#xff0c;透明OLED屏的制作过程大致可分为以下几个步…

使用.Net nanoFramework为ESP32进行蓝牙配网

通过前面的介绍&#xff0c;我们已经学会了如何使用 .NET nanoFramework 为 ESP32 设备连接 Wi-Fi 网络。然而&#xff0c;在实际的物联网环境中&#xff0c;我们往往需要使用更便捷的式来满足配网需求。这篇文章将带你了解一些常见的配网方案&#xff0c;并以 ESP32 为例&…

【Java 进阶篇】Nginx 使用详解:搭建高性能的 Web 服务器

在互联网的世界里&#xff0c;Web 服务器是我们访问网站、获取信息的入口。Nginx&#xff08;发音"engine x"&#xff09;作为一款轻量级、高性能的 Web 服务器和反向代理服务器&#xff0c;因其出色的性能和可扩展性而备受推崇。本文将围绕 Nginx 的使用进行详解&am…

KK集团高管变更:陈世欣任总经理,涉无证放贷遭关注,还曾被处罚

近日&#xff0c;KK集团关联公司广东快客电子商务有限公司&#xff08;下称“KK集团”&#xff09;发生工商变更&#xff0c;其中郭惠波不再担任该公司总经理一职&#xff0c;由陈世欣接任。而在早前&#xff0c;陈世欣曾于2020年取代吴悦宁担任总经理职务&#xff0c;2021年7月…

Linux服务器的几种类型

Linux是一个开源操作系统内核&#xff0c;用作各种Linux发行版&#xff08;也称为“distros”&#xff09;的核心组件。由Linus Torvalds于1991年开发&#xff0c;Linux基于Unix操作系统。它以其稳定性、安全性和多功能性而闻名。 Linux的关键特点&#xff1a; 开源性质&#…

每日一道算法题day-three(备战蓝桥杯)

哈喽大家好&#xff0c;今天来给大家带来每日一道算法题系列第三天&#xff0c;让我们来看看今天的题目&#xff0c;一起备战蓝桥杯 题目&#xff1a; 小 Y的桌子上放着 n 个苹果从左到右排成一列&#xff0c;编号为从 11 到 n。 小苞是小 Y 的好朋友&#xff0c;每天她都会…

Linux安装JDK和Maven并配置环境变量

文章目录 一、安装JDK并配置环境变量二、安装maven并配置环境变量 一、安装JDK并配置环境变量 将JDK的安装包上传到Linux系统的usr/local目录 使用xftp上传文件 解压JDK的压缩包 xshell连接到云主机 [roottheo ~]# cd /usr/local[roottheo local]# ls aegis apache-tomcat-…

游戏缺少x3daudio1_7.dll文件怎么办?x3daudio1_7.dll丢失总共有六个解决方法

导语&#xff1a;在计算机使用过程中&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中之一就是“x3daudio1_7.dll丢失”。那么&#xff0c;x3daudio1_7.dll到底是什么文件呢&#xff1f;它的作用和影响又是什么呢&#xff1f;本文将为您详细介绍x3daudio1_7.dll的相关知…

AI大模型引领未来智慧科研暨ChatGPT在地学、GIS、气象、农业、生态、环境等领域中的高级应用

以ChatGPT、LLaMA、Gemini、DALLE、Midjourney、Stable Diffusion、星火大模型、文心一言、千问为代表AI大语言模型带来了新一波人工智能浪潮&#xff0c;可以面向科研选题、思维导图、数据清洗、统计分析、高级编程、代码调试、算法学习、论文检索、写作、翻译、润色、文献辅助…

Python之基本数据类型

目录 一、基本数据类型总结 二、基本数据类型 Number&#xff08;数字&#xff09; String&#xff08;字符串&#xff09; Bool&#xff08;布尔类型&#xff09; List&#xff08;列表&#xff09; Tuple&#xff08;元组&#xff09; Set&#xff08;集合&#xff09…

Fiddler入门:下载、安装、配置、抓包、customize rules

一、fiddler下载安装 安装包下载链接&#xff1a;https://www.telerik.com/download/fiddler 随便选个用途&#xff0c;填写邮箱&#xff0c;地区选择China&#xff0c;勾选“I accept the Fiddler End User License Agreement”&#xff0c;点击“DownLoad for windows”&am…

初识Winform

什么是winform&#xff1f; WinForms&#xff08;Windows Forms&#xff09;是Microsoft .NET框架中的一个用户界面&#xff08;UI&#xff09;技术&#xff0c;用于创建Windows应用程序。它提供了一组用于构建图形用户界面的类和控件&#xff0c;以及与用户交互的事件模型。 …

【Python学习】2024PyCharm插件推荐

目录 【Python学习】2024PyCharm插件推荐 1. Key Promoter X2.Rainbow CSV3.Markdown4.Rainbow Brackets5.Indent Rainbow6.Regex Tester7.Regex Tester8.Background Image Plus9.Material Theme UI10. Chinese 汉化插件参考 文章所属专区 Python学习 1. Key Promoter X 方便…