Linux驱动(三)platform总线驱动

news2024/11/20 0:42:39

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驱动程序,旨在缕清框架,后续文章再讲解其他内容。

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

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

相关文章

【深度学习:(Contrastive Learning) 对比学习】深入浅出讲解对比学习

对比学习允许模型从未标记的数据中提取有意义的表示。通过利用相似性和不相似性&#xff0c;对比学习使模型能够在潜在空间中将相似的实例紧密地映射在一起&#xff0c;同时将那些不同的实例分开。这种方法已被证明在计算机视觉、自然语言处理 &#xff08;NLP&#xff09; 和强…

NPDP产品经理含金量高吗?难考吗?

NPDP的中文翻译为产品经理国际资格认证。NPDP考试起源于美国&#xff0c;由美国产品开发与管理协会&#xff08;PDMA&#xff09;发起。NPDP认证是集理论、方法与实践为一体的全方位知识体系&#xff0c;为公司组织层级进行规划、决策、执行提供良好的方法体系支撑。&#xff0…

ChatGPT绘制全球植被类型分布图、生物量图、土壤概念图、处理遥感数据并绘图、病毒、植物、动物细胞结构图

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

【笔记------STM32】定时器触发adc采样注意事项(无中断DMA方式)

配置adc转换通过dma传输&#xff0c;不使用相关定时器中断、DMA中断和ADC中断 网上各种配置五花八门&#xff0c;这里提供我的配置方法 仅记录关键位置配置&#xff0c;根据自己的情况配置&#xff0c;并非只能如此。。 ADC不使用连续转换&#xff0c;否则定时器触发可能失效&a…

LoRa解决方案助推农业创新钡铼技术LoRa网关S281

随着物联网技术的迅猛发展&#xff0c;农业行业也开始探索如何利用物联网技术来提高农业生产效率、降低成本&#xff0c;并实现可持续发展。在这一背景下&#xff0c;钡铼技术有限公司推出的LoRa解决方案以及其中的重要组成部分LoRa网关S281&#xff0c;为农业创新和智能化提供…

RFID技术在3C家电中的全方位应用

RFID技术在3C家电中的全方位应用 一、RFID技术简述 射频识别&#xff08;RFID&#xff09;技术是一种无线通信技术&#xff0c;已经在各行各业得到广泛应用。在3C家电领域&#xff0c;RFID技术的应用正在逐渐增加&#xff0c;为产品追溯、库存管理、防伪验证等方面提供了许多…

Excel 读写

using System.Collections; using System.Collections.Generic; using OfficeOpenXml; using System.IO; using UnityEngine; using System.Text;public class ExcelTest : MonoBehaviour {void Start(){string _filePath Application.streamingAssetsPath "/学生信息.x…

Jenkins集成部署java项目

文章目录 Jenkins简介安装 Jenkins简介 Jenkins能实时监控集成中存在的错误&#xff0c;提供详细的日志文件和提醒功能&#xff0c;还能用图表的形式形象的展示项目构建的趋势和稳定性。 官网 安装 在官网下载windows版本的Jenkins 但是我点击这里浏览器没有反应&#xff0…

使用EasyExcel导出百万条数据

使用EasyExcel导出百万条数据 应用是基于100W条数据进行的测试 首先&#xff1a;导入相关需要的依赖&#xff1a; <dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>3.16</version></depend…

浅谈园区建设“一站式企业服务平台”的必要性!

​ 随着数字经济的快速发展与新一代信息技术的日新月异&#xff0c;打造智慧型、高效能的园区运营模式已成为现代产业园区转型升级的关键路径&#xff0c;其中&#xff0c;构建“一站式企业服务平台”成为了园区创新企业服务机制、提升企业服务效能、优化营商环境的重要举措。 …

得物商品状态体系介绍

一、得物的商品体系 目前得物的商品分为三种类型&#xff0c;分别是&#xff1a;新品、商品、草稿。但是只有商品是可售卖的&#xff0c;新品和草稿都不是可售卖的。 新品有很多种创建的渠道&#xff0c;商品可以由新品选品通过后由系统自动生成&#xff0c;也可以由运营直接…

QT工具栏开始,退出

QT工具栏开始&#xff0c;退出 //初始化场景QMenuBar *bar menuBar();setMenuBar(bar);QMenu *startbar bar->addMenu("开始");QAction * quitAction startbar->addAction("退出");connect(quitAction , &QAction::triggered,[](){this->c…

【linux】线程同步+基于BlockingQueue的生产者消费者模型

线程同步基于BlockingQueue的生产者消费者模型 1.线程同步2.生产者消费者模型3.基于BlockingQueue的生产者消费者模型 喜欢的点赞&#xff0c;收藏&#xff0c;关注一下把&#xff01; 1.线程同步 在线程互斥写了一份抢票的代码&#xff0c;我们发现虽然加锁解决了抢到负数票的…

面试题:Spring Boot 中如何统计代码执行耗时

文章目录 ① StopWatch② System.nanoTime()③ new Date()④ System.currentTimeMillis() 开始 System.currentTimeMillis() 减去 结束 System.currentTimeMillis() 等于 耗时 其实我个人感觉OK的&#xff0c;就这样就蛮好的&#xff0c;很多项目都是这样用的。 简简单单的挺…

基于Java SSM框架实现游戏论坛平台系统项目【项目源码+论文说明】计算机毕业设计

基于java的SSM框架实现游戏论坛平台系统演示 摘要 本论文主要论述了如何使用java语言开发一个游戏论坛平台的设计&#xff0c;本系统将严格按照软件开发流程进行各个阶段的工作&#xff0c;采用B/S架构、ssm 框架和 java 开发的 Web 框架&#xff0c;基于Werkzeug WSGI工具箱和…

2024.1.5每日一题

LeetCode每日一题 1944.队列中可以看到的人数 1944. 队列中可以看到的人数 - 力扣&#xff08;LeetCode&#xff09; 题目描述 有 n 个人排成一个队列&#xff0c;从左到右 编号为 0 到 n - 1 。给你以一个整数数组 heights &#xff0c;每个整数 互不相同&#xff0c;heig…

将 validator 校验器从 ParameterValidator 中抽离出来

目录 一、前置说明1、总体目录2、相关回顾3、本节目标 二、操作步骤1、项目目录2、代码实现3、测试代码4、日志输出 三、后置说明1、要点小结2、下节准备 一、前置说明 1、总体目录 《 pyparamvalidate 参数校验器&#xff0c;从编码到发布全过程》 2、相关回顾 pyparamval…

科研上新 | 第6期:优化LLM数学推理;深度学习建模基因表达调控;基于深度学习的近实时海洋碳汇估算

编者按&#xff1a;欢迎阅读“科研上新”栏目&#xff01;“科研上新”汇聚了微软亚洲研究院最新的创新成果与科研动态。在这里&#xff0c;你可以快速浏览研究院的亮点资讯&#xff0c;保持对前沿领域的敏锐嗅觉&#xff0c;同时也能找到先进实用的开源工具。 本期内容速览 …

【深度学习:SENet】信道注意力和挤压激励网络(SENet):图像识别的新突破

【深度学习&#xff1a;SENet】信道注意力和挤压激励网络&#xff08;SENet&#xff09;&#xff1a;图像识别的新突破 为什么有效如何实现工作原理应用案例 挤压和激励网络&#xff08;SENets&#xff09;为卷积神经网络&#xff08;CNN&#xff09;引入了一个新的构建模块&am…

centos 7.9安装RocketMQ4.6.1版本

1.先下载二进制文件 下载 | RocketMQ 2.下载后&#xff0c;进行解压 unzip rocketmq-all-4.6.1-bin-release.zip 3.修改JVM配置 进到/datadrive/rocketmq-all-4.6.1-bin-release/bin下编辑runserver.sh 与 runbroker.sh文件 根据个人虚拟机大小进行修改 vi runserver.sh J…