Linux驱动开发—平台总线模型详解

文章目录

    • 1.平台总线介绍
      • 1.1平台总线模型的组成部分
      • 1.2平台总线模型的优势
    • 2.使用平台总线模型开发驱动
      • 2.1注册platform设备
      • 2.2注册platform驱动
      • 2.3效果演示

1.平台总线介绍

Linux 平台总线模型(Platform Bus Model)是一种设备驱动框架,用于处理那些没有标准总线(如 PCI、USB 等)的嵌入式设备。它为这些设备提供了统一的设备驱动模型,简化了设备驱动程序的编写和管理。

1.1平台总线模型的组成部分

平台总线模型主要由以下几个组成部分构成:

  1. 平台设备(Platform Device)
  2. 平台驱动(Platform Driver)
  3. 平台总线(Platform Bus)

平台设备(Platform Device)

平台设备表示硬件设备,它们通常通过设备树(Device Tree)或者板文件(Board File)进行描述。平台设备通常包括设备名称、资源(如 I/O 端口、内存区域、中断号等)以及其他平台数据。

平台驱动(Platform Driver)

平台驱动是与平台设备匹配并管理这些设备的软件模块。平台驱动提供了 proberemove 函数,用于设备的初始化和清理。

平台总线(Platform Bus)

平台总线在内核中自动管理,不需要显式地定义。它用于匹配平台设备和平台驱动。

在这里插入图片描述

1.2平台总线模型的优势

平台总线模型(Platform Bus Model)在 Linux 内核中的引入为嵌入式设备和驱动程序的开发带来了多项显著的优势。以下是平台总线模型的一些主要优势:

  1. 抽象和统一的设备管理

平台总线模型为没有标准总线的设备提供了统一的抽象和管理方法。通过统一的接口和机制,开发者可以更容易地管理和控制不同类型的设备,无需考虑底层硬件差异。

  1. 简化驱动开发

通过使用平台总线模型,驱动程序开发者不再需要为每种硬件设备编写特定的初始化和资源管理代码。平台设备和平台驱动的标准化接口使得驱动程序的开发和调试更加简单和一致。

  1. 设备树支持

平台总线模型支持设备树(Device Tree),这是一种硬件描述语言,广泛用于描述嵌入式系统中的硬件配置。设备树使得硬件配置从代码中分离出来,可以通过修改设备树文件而不是驱动代码来适应不同的硬件配置,极大地提高了代码的可维护性和可移植性。

  1. 自动匹配和管理

平台总线模型通过内核自动完成平台设备和平台驱动的匹配和管理。这意味着驱动程序不需要显式地查找和初始化设备,内核会自动调用合适的 proberemove 函数来管理设备的生命周期。

  1. 资源管理

平台总线模型提供了简化的资源管理机制。平台设备可以通过设备树或板文件描述其所需的资源(如 I/O 端口、内存区域、中断号等),驱动程序可以通过标准接口获取和使用这些资源,避免了手动管理资源的复杂性和潜在错误。

  1. 模块化和可移植性

通过将硬件特定的配置与驱动代码分离,平台总线模型提高了驱动程序的模块化和可移植性。驱动程序可以更容易地在不同的硬件平台之间移植,只需调整设备树或板文件中的硬件配置即可。

  1. 代码重用

由于平台总线模型提供了标准化的接口和机制,不同驱动程序之间可以共享通用的代码和逻辑。这种代码重用不仅减少了开发时间和成本,还提高了代码的稳定性和可靠性。

8. 内核维护

平台总线模型的标准化和统一管理机制使得内核代码更易于维护和升级。通过减少硬件特定的代码和逻辑,内核开发者可以更专注于改进和优化内核的通用部分,提高内核的整体性能和稳定性。

2.使用平台总线模型开发驱动

2.1注册platform设备

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/atomic.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>// 描述硬件资源 结构体数组
static struct resource my_device_resources[] = {{.start = 0x12340000,.end = 0x123400FF,.flags = IORESOURCE_MEM,},{.start = 5,.end = 5,.flags = IORESOURCE_IRQ,}};
void my_device_release(struct device *dev)
{printk("This is my device release");
}static struct platform_device my_platform_device = {.name = "my_platform_device",.id = -1,.num_resources = ARRAY_SIZE(my_device_resources),.resource = my_device_resources,.dev = {.release = my_device_release},
};static int __init platform_device_init(void)
{platform_device_register(&my_platform_device);printk("platform_device_init!");return 0;
}static void __exit platform_device_exit(void)
{platform_device_unregister(&my_platform_device);printk("platform_device_exit!");
}module_init(platform_device_init);
module_exit(platform_device_exit);
MODULE_AUTHOR("Marxist");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("a simple of platform_device");

其中有两个关键的结构体 platform_deviceresource

struct platform_device 功能描述

platform_device 结构体表示一个平台设备,它包含了设备的名称、资源、设备数据以及其他属性。这个结构体在平台总线模型中非常重要,用于描述那些没有标准总线(如 PCI、USB 等)支持的嵌入式设备。

struct platform_device {const char *name;                     // 设备名称int id;                               // 设备ID,通常用于区分同名设备struct device dev;                    // 嵌入的设备结构体u32 num_resources;                    // 资源数量struct resource *resource;            // 指向资源数组的指针const struct platform_device_id *id_entry; // 设备ID表char *driver_override;                // 用于覆盖默认的驱动程序
};

主要字段解释

  • name: 设备的名称,用于匹配设备和驱动程序。
  • id: 设备ID,用于区分具有相同名称的多个设备。通常设置为 -1。
  • dev: 嵌入的 struct device 结构体,表示通用设备结构,包含设备的通用属性和方法。
  • num_resources: 资源数量,表示设备所使用的资源数量。
  • resource: 指向资源数组的指针,资源数组包含了设备所使用的各种资源(如内存、I/O 端口、中断等)。
  • id_entry: 指向设备ID表的指针,用于在驱动程序中匹配特定的设备。
  • driver_override: 用于指定一个特定的驱动程序覆盖默认的驱动程序。

一般只需要关注name,id, dev,以及使用的资源描述

struct resource 结构体描述

resource 结构体描述了设备使用的硬件资源,例如内存地址范围、中断号等。每个设备可以有多个资源,这些资源通过 platform_device 结构体中的 resource 字段进行管理。

struct resource {resource_size_t start; // 资源的起始地址resource_size_t end;   // 资源的结束地址const char *name;      // 资源的名称unsigned long flags;   // 资源的类型和属性struct resource *parent, *sibling, *child; // 资源树结构中的节点关系
};

主要字段解释

  • start: 资源的起始地址或起始值,例如内存映射地址的开始位置。
  • end: 资源的结束地址或结束值,例如内存映射地址的结束位置。
  • name: 资源的名称,用于识别资源。
  • flags: 资源的类型和属性,通过标志位表示。例如,IORESOURCE_MEM 表示内存资源,IORESOURCE_IRQ 表示中断资源。
  • parent: 指向父资源的指针,用于构建资源层次结构。
  • sibling: 指向兄弟资源的指针,用于构建资源层次结构。
  • child: 指向子资源的指针,用于构建资源层次结构。

编译加载之后,就会在 /sys/bus/platform/devices/ 下注册新的设备

在这里插入图片描述

2.2注册platform驱动

#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/mod_devicetable.h>
static const struct platform_device_id driver_id_table[] = {{ .name = "my_platform_device" },{ } // 结尾必须有一个空的元素
};
static int my_platform_driver_probe(struct platform_device *dev )
{printk("my_platform_driver_probe");// 通过 probe 函数 拿到硬件资源描述, probe函数将传递 platform_device结构体函数struct resource *res;int irq;//获取内存资源res = platform_get_resource(dev,IORESOURCE_MEM,0);printk("IORESOURCE_MEM start addr is %x ",res->start);//获取中断资源res = platform_get_resource(dev,IORESOURCE_IRQ,0);printk("IRQ number is %d ",res->start);//获取完资源进行下一步的操作return 0;
}
static int my_platform_driver_remove(struct platform_device *dev )
{printk("my_platform_driver_remove");return 0;
}static struct platform_driver my_platform_driver = {.probe = my_platform_driver_probe,.remove = my_platform_driver_remove,.driver = {.name = "my_platform_device", // 平台设备名一致.owner = THIS_MODULE,},.id_table = driver_id_table, // id_table 的优先级更高
};
static int __init platform_driver_init(void)
{int ret = platform_driver_register(&my_platform_driver);if (ret)printk(KERN_ALERT "Failed to register platform driver\n");elseprintk( "platform_driver_init!\n");return ret;
}
static void __exit platform_driver_exit(void)
{platform_driver_unregister(&my_platform_driver);printk(KERN_ALERT "platform_driver_exit!\n");
}
module_init(platform_driver_init); 
module_exit(platform_driver_exit);
MODULE_AUTHOR("Marxist");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("a simple of platform_driver");

我们只需要注册驱动即可,重点为platform_driver结构体

platform_driver结构体为 Linux 内核中用于描述和管理平台驱动程序的一个重要结构体。它定义了驱动程序的主要回调函数和一些元数据,这些信息允许内核在设备插入和移除时正确地调用驱动程序的相关函数。

结构体定义:

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:

  • 类型: int (*probe)(struct platform_device *);
  • 功能: 当匹配的设备被注册时,内核调用此函数来初始化设备。通常在此函数中进行设备的硬件资源获取和初始化。
  • 返回值: 返回0表示成功,负值表示失败。

remove:

  • 类型: int (*remove)(struct platform_device *);
  • 功能: 当设备被移除时,内核调用此函数来清理和释放设备资源。
  • 返回值: 返回0表示成功,负值表示失败。

shutdown:

  • 类型: void (*shutdown)(struct platform_device *);
  • 功能: 当系统关闭或重启时,内核调用此函数来关闭设备。通常用于执行设备的关机操作。

suspend:

  • 类型: int (*suspend)(struct platform_device *, pm_message_t state);
  • 功能: 当设备进入挂起(suspend)状态时,内核调用此函数来保存设备的状态。
  • 返回值: 返回0表示成功,负值表示失败。

resume:

  • 类型: int (*resume)(struct platform_device *);
  • 功能: 当设备从挂起状态恢复时,内核调用此函数来恢复设备的状态。
  • 返回值: 返回0表示成功,负值表示失败。

driver:

  • 类型: struct device_driver
  • 功能: 包含通用的驱动程序信息,如驱动程序的名字、模块所有者等。platform_driver 通过嵌入 device_driver 结构体继承了大部分通用的驱动程序接口。
  • 关键字段:
    • name: 驱动程序的名字,应该与 platform_device 的名字匹配。
    • owner: 指向该驱动程序模块的指针,通常设置为 THIS_MODULE

id_table:

  • 类型: const struct platform_device_id *
  • 功能: **指向设备 ID 表的指针,用于设备和驱动程序之间的匹配。**优先使用id_table进行名称匹配,如果匹配不上,将会进行device_driver 中的名字匹配

prevent_deferred_probe:

  • 类型: bool
  • 功能: 控制是否防止延迟探测。默认值是 false。

注意:必须要实现probe 函数,当平台设备和平台驱动匹配成功,就会调用probe函数,通常在此完成一些资源的初始化和调用。

例如:

static int my_platform_driver_probe(struct platform_device *dev )
{printk("my_platform_driver_probe");// 通过 probe 函数 拿到硬件资源描述, probe函数将传递 platform_device结构体函数struct resource *res;int irq;//获取内存资源res = platform_get_resource(dev,IORESOURCE_MEM,0);printk("IORESOURCE_MEM start addr is %x ",res->start);//获取中断资源res = platform_get_resource(dev,IORESOURCE_IRQ,0);printk("IRQ number is %d ",res->start);//获取完资源进行下一步的操作return 0;
}

2.3效果演示

无论先加载平台设备模块还是平台驱动模块,就会调用probe函数,具体效果如下

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Debezium报错处理系列之第116篇:Caused by: java.lang.NumberFormatException: null

Debezium报错处理系列之第116篇:Caused by: java.lang.NumberFormatException: null 一、完整报错二、错误原因三、解决方法Debezium从入门到精通系列之:研究Debezium技术遇到的各种错误解决方法汇总: Debezium从入门到精通系列之:百篇系列文章汇总之研究Debezium技术遇到的…

【从零开始一步步学习VSOA开发】VSOA命令行工具vcl

VSOA命令行工具vcl 介绍 vcl 是一个 VSOA 命令监听器&#xff0c;支持订阅来自特定服务器发布的所有数据。 [rootsylixos:/root]# [rootsylixos:/root]# vcl -help USAGE: vcl [options] host [topics] -h : Show help message. -v : Show vcl version. …

平安城市行业无人机解决方案(网格化巡逻场景应用)

场景痛点&#xff1a; 缺少飞手&#xff0c;导致无人机应用频次偏低 无人机需要人工换电池、维护及存储 日常巡逻 | 无人机补盲巡逻 大疆航点飞行功能&#xff0c;重复性任务只飞一遍&#xff0c;后续重复调用 无人机航点规划&#xff0c;一次设置&#xff0c;后续重复调用…

SpringBoot + RabbitMQ + WebSocket + STOMP 协议 + Vue 实现简单的实时在线聊天案例

1. 什么是WebSocket&#xff1f; WebSocket是一种在单个TCP连接上进行全双工通信的协议。它允许客户端和服务器之间的实时、双向数据传输。与传统的HTTP请求/响应模型相比&#xff0c;WebSocket更加高效&#xff0c;因为它在初次握手后&#xff0c;连接保持打开状态&#xff0…

前端(HTML + CSS)小兔鲜儿项目(仿)

前言 这是一个简单的商城网站&#xff0c;代码部分为HTML CSS 和少量JS代码 项目总览 一、头部区域 头部的 购物车 和 手机 用的是 文字图标&#xff0c;所以效果可以和文字一样 购物车右上角用的是绝对定位 logo用的是 h1 标签&#xff0c;用来提高网站搜索排名 二、banne…

Golang | Leetcode Golang题解之第331题验证二叉树的前序序列化

题目&#xff1a; 题解&#xff1a; func isValidSerialization(preorder string) bool {n : len(preorder)slots : 1for i : 0; i < n; {if slots 0 {return false}if preorder[i] , {i} else if preorder[i] # {slots--i} else {// 读一个数字for i < n &&…

各种高端链游 区块链游戏 休闲小游戏DAPP开发

【西游闯关】-高端区块链3D手游 【我朝有马】-高端区块链3D手游 【弹弹岛2】-高端区块链3D手游

FPGA第4篇,中国FPGA芯片市场,发展分析与报告

前言 FPGA&#xff08;Field-Programmable Gate Array&#xff09;&#xff0c;即现场可编程门阵列&#xff0c;是一种可由用户配置的集成电路&#xff0c;具有高度的灵活性和强大的计算能力&#xff0c;被广泛应用于通信、航空航天、汽车电子、消费电子、数据中心、人工智能等…

Java毕业设计-基于SSM框架的舞蹈网站系统项目实战(附源码+论文)

大家好&#xff01;我是程序猿老A&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;Java毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计 &…

【mysql 第四篇章】bin log 的作用是啥呢?

一、redo Log 介绍 redo log 是一种偏向物理性质的重做日志&#xff0c;因为他里面记录类似的这样的东西&#xff0c;“对那个数据也中的什么记录&#xff0c;做了个什么修改”。它是 InnoDB 存储引擎特有的东西。 二、bin Log 日志 bin log 叫做归档日志&#xff0c;它里面…

STM32-USART时序与寄存器状态分析

一、时序分析 在UART&#xff08;通用异步收发传输&#xff09;通信中&#xff0c;信号线上的状态分为两种&#xff1a;逻辑1&#xff08;高电平&#xff09;和逻辑0&#xff08;低电平&#xff09;。在空闲状态下&#xff0c;数据线应保持逻辑高电平。UART协议中的各个信号位具…

使用frp内网穿透将个人主机上的MySQL发布到公网上,再通过python管理MySQL

目录 1.frp内网穿透部署 1.frp服务器 1.开放端口 2.上传软件包 3.解压 4.配置文件 2.frp客户端 1.上传软件包 2.配置文件 3.启动测试 1.浏览器查看服务器上连接的客户端数量 2.启动测试 2.MySQL安装 3.python3的安装使用 4.python管理MySQL 1.pip 2.pandas 3.p…

uniapp2【搬代码】

我们预期想要的效果: 步骤; 1.创建项目&#xff1a;填写项目名称 2.重新选择项目保存的目录 3.点击确定 4.将图标倒入static中 5.在pages中创建vue如图所示 6.vue创建成功后&#xff0c;在pages.json中进行配置tabBar "tabBar": {"color": "#999&q…

基于Jeecgboot3.6.3的vue3版本的流程中仿钉钉流程的鼠标拖动功能支持

因为这个项目license问题无法开源&#xff0c;更多技术支持与服务请加入我的知识星球。 1、因为原先仿钉钉流程里不能进行鼠标拖动来查看流程&#xff0c;所以根据作者提供的信息进行修改&#xff0c;在hooks下增加下面文件useDraggableScroll.ts import { ref, onMounted, on…

网络编程day1

一、思维导图 网络基础

泛微E-office 10 schema_mysql接口敏感信息泄露漏洞复现 [附POC]

文章目录 泛微E-office 10 schema_mysql接口敏感信息泄露漏洞复现 [附POC]0x01 前言0x02 漏洞描述0x03 影响版本0x04 漏洞环境0x05 漏洞复现1.访问漏洞环境2.构造POC3.复现泛微E-office 10 schema_mysql接口敏感信息泄露漏洞复现 [附POC] 0x01 前言 免责声明:请勿利用文章内…

WPF自定义控件的应用(DynamicResource的使用方法)

1 DynamicResource的使用方法 可以在字典文件 的抬头区写入数&#xff1a; <SolidColorBrush x:Key"PrimaryBackgroundColor" Color"#FFABAdB3"/><SolidColorBrush x:Key"TextBox.MouseOver.Border" Color"#FF7EB4EA"/>&l…

服务器CPU天梯图2024年8月,含EYPC/至强及E3/E5

原文地址&#xff08;高清无水印原图/持续更新/含榜单出处链接&#xff09;&#xff1a; >>>服务器CPU天梯图<<< 本文提供的服务器CPU天梯图数据均采集自各大专业网站&#xff0c;榜单图片末尾会标准其来源&#xff08;挂太多链接有概率会被ban&#xff0c;…

datawind可视化查询-along表达式计算占比表达式

常见表达式 表计算表达式仅能作为指标应用along的维度必须存在于查询面板中&#xff0c;否则报错 例如计算埋点的占比 停止充电时长分箱 case when [个人].[停止充电时长(小时)] >12 then (12,13] when [个人].[停止充电时长(小时)] >11 then (11,12] when [个人].[停止…

ARM 汇编语言基础

目录 汇编指令代码框架 汇编指令语法格式 数据处理指令 数据搬移指令 mov 示例 立即数的本质 立即数的特点 立即数的使用 算术运算指令 指令格式 add 普通的加法指令 adc 带进位的加法指令 跳转指令 Load/Store指令 状态寄存器指令 基础概念 C 语言与汇编指令的关…