当前位置: 首页 > news >正文

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函数,具体效果如下

在这里插入图片描述

相关文章:

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

文章目录 1.平台总线介绍1.1平台总线模型的组成部分1.2平台总线模型的优势 2.使用平台总线模型开发驱动2.1注册platform设备2.2注册platform驱动2.3效果演示 1.平台总线介绍 Linux 平台总线模型&#xff08;Platform Bus Model&#xff09;是一种设备驱动框架&#xff0c;用于…...

说一下网络层,传输层,数据链路层做什么的,之间的关系?

网络层主要负责为数据包选择最佳路径&#xff0c;将数据从源主机传输到目标主机。它的关键任务包括路由选择、拥塞控制和网络互联等。通过网络层的功能&#xff0c;不同网络之间能够实现通信和数据传输。 传输层的作用是在源端和目的端之间提供可靠或不可靠的端到端的数据传输…...

解锁AI新纪元:Milvus Cloud与Zilliz Cloud的高可用之道

在当今数字化时代,系统的持续稳定运行与数据的即时访问性已成为衡量技术服务质量的关键指标。面对复杂多变的运行环境,包括电力波动、网络故障乃至人为操作失误等不可预见因素,数据库系统的高可用性(High Availability, HA)成为了保障业务连续性的重要基石。特别是在大数据…...

svn安装

579 ​​yum install subversion 580 rpm -qa|grep subversion 581 yum -y install subversion 582 rpm -ql subversion 583 /usr/bin/svnversion --version 584 mkdir /data/svnrepos 585 svnadmin create /data/svnrepos/abc 586 svnadmin create /data/svnrepos/gzss 587 cd…...

【隐私计算篇】混淆电路之深入浅出

入门隐私计算的阶段&#xff0c;一般都会涉及对于混淆电路的学习&#xff0c;这是因为混淆电路是多方安全计算中的基础密码原语&#xff0c;也是隐私保护中重要的技术。为了帮助更好地理解混淆电路的原理&#xff0c;今天对其进行原理以及相关优化手段进行解析和分享。 1. 混淆…...

基于GRU神经网络的微博分类预测

目录 背影 摘要 LSTM的基本定义 LSTM实现的步骤 gru的原理 GRU神经网络微博分类 结果分析 展望 参考论文 背影 传统的方法微博分类预测准确率低,为提高精度,本文用gru进行预测 摘要 LSTM原理,GRU原理,MATALB编程gru的微博分类预测 LSTM的基本定义 LSTM是一种含有LST…...

LVS-DR模式集群:案例与概念

DR模式&#xff08;直接路由&#xff09; 概念 Direct Routing&#xff0c;简称DR模式采用半开放式的网络结构&#xff0c;与TUN模式的结构类似&#xff0c;但内网服务器并不是分散在各地&#xff0c;而是与调度器位于同一个物理网络负载调度器与内网服务器通过本地网络连接&a…...

拓扑排序:Kahn算法与DFS算法

引言 拓扑排序是有向无环图&#xff08;DAG&#xff09;中的一种线性排序&#xff0c;使得对于图中的每一条有向边 ( u \rightarrow v )&#xff0c;顶点 ( u ) 在排序中出现在顶点 ( v ) 之前。本文将详细介绍两种实现拓扑排序的算法&#xff1a;Kahn算法和基于深度优先搜索&…...

图像处理 -- Sobel滤波器的实现原理与使用案例

Sobel滤波器 概述 Sobel滤波器是一种边缘检测方法&#xff0c;用于图像处理和计算机视觉领域。它通过计算图像灰度值的梯度来检测边缘。Sobel滤波器结合了高斯平滑和微分操作&#xff0c;以减少噪声并增强边缘检测效果。 实现原理 Sobel滤波器通过使用两个3x3卷积核&#x…...

机器学习 第10章-降维与度量学习

机器学习 第10章-降维与度量学习 10.1 k近邻学习 k近邻(k-Nearest Neighbor,简称kNN)学习是一种常用的监督学习方法其工作机制非常简单:给定测试样本&#xff0c;基于某种距离度量找出训练集中与其最靠近的k个训练样本&#xff0c;然后基于这k个“邻居”的信息来进行预测。通…...

linux驱动:(7)物理地址到虚拟地址映射

单片机、裸机、linux操控硬件方法 在单片机和裸机中操作硬件是通过指针来对寄存器赋值来进行操控 但对于linux中不能这样&#xff0c;不能直接对物理地址直接修改&#xff0c;因为linux使能了mmu&#xff0c;所以不能直接菜操作物理地址 如果要操作硬件&#xff0c;需要先把…...

浏览器用户文件夹详解 - Preferences(十)

1.Preferences简介 1.1 什么是Preferences文件&#xff1f; Preferences文件是Chromium浏览器中用于存储用户个性化设置和配置的一个重要文件。每当用户在浏览器中更改设置或安装扩展程序时&#xff0c;这些信息都会被记录在Preferences文件中。通过这些记录&#xff0c;浏览…...

Robot Operating System——电池电量通知

大纲 应用场景定义字段解释 案例 sensor_msgs::msg::BatteryState 是 ROS 2 中定义的消息类型&#xff0c;用于表示电池状态。它包含了电池电量、电压、电流、温度等信息。 应用场景 机器人 电池监控&#xff1a;在移动机器人中&#xff0c;电池是主要的电源。BatteryState 消…...

二进制安装docker

目录 一、准备 Docker CE 二进制包 二、解压.tgz包 三、复制二进制文件到/usr/bin/目录 四、创建用户组 五、配置相关服务配置文件 六、拷贝配置文件到指定目录 七、启动 dockerd 服务进程 八、shell脚本一键安装 一、准备 Docker CE 二进制包 https://download.docker…...

@SpringBootConfiguration重复加载报错

Junit单元测试Test启动报错&#xff0c;SpringBootConfiguration注解重复问题排查&#xff1a; SpringBootApplication 注解的 exclude 属性用于排除特定的自动配置类&#xff0c;而不是用于排除主配置类本身。因此&#xff0c;不能通过 exclude 属性来排除主配置类的加载。 …...

【SpringBoot】数据验证之分组校验

分组校验 在不同情况下&#xff0c;可能对JavaBean对象的数据校验规则有所不同&#xff0c;有时需要根据数据状态对JavaBean中的某些属性字段进行单独验证。这时就可以使用分组校验功能&#xff0c;即根据状态启用一组约束。 Hibernate Validator的注解提供了groups参数&#…...

MySQL Galera Cluster 部署与介绍

目录 主要特点 组件 一. 环境准备 二. 配置 1. 配置 galera1 主机的my.cnf的文件 2. 配置 galera2 主机的my.cnf的文件 3. 配置 galera3 主机的my.cnf的文件 4. 在给galera1 主机的my.cnf的文件增加节点 5. 写入数据验证同步 6. 配置 galera4 主机的my.cnf的文件 M…...

RuoYi-Vue-Plus (XXL-JOB任务调度中心二:配置管理与定时任务编写、执行策略、命令行任务、邮件报警等等

一、后端xxl job的配置属性介绍 enabled &#xff1a; 是否开启执行器&#xff0c;如果为false&#xff0c;调度中心就调用不了后端定时任务admin-addresses&#xff1a;调度中心的地址&#xff0c;多个则可以逗号拼接: url1,url2,url3access-token: 执行器通讯TOKEN ,必须和x…...

【docker】虚拟化与docker基础

一、虚拟化 1.虚拟化概述 什么是虚拟化&#xff1f; 虚拟化&#xff1a;将应用程序和系统内核资源进行解耦&#xff0c;以操作系统级别进行隔离&#xff0c;目的是提高资源利用率 2、虚拟化的功能 将虚拟化的性能优化趋近于物理资源的性能&#xff0c;主要用于提高资源利用…...

Vue3安装ffmpeg做视频截取报错

通过 yarn 安装 ffmpeg 时报错。 即&#xff0c;执行以下指令时报错&#xff1a; yarn add ffmpeg/ffmpeg^0.10.0 yarn add ffmpeg/core^0.10.0错误信息&#xff1a; node_modules\pngquant-bin: Command failed. Error: pngquant failed to build, make sure that libpng-d…...

Python爬虫实战:研究MechanicalSoup库相关技术

一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…...

内存分配函数malloc kmalloc vmalloc

内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...

stm32G473的flash模式是单bank还是双bank?

今天突然有人stm32G473的flash模式是单bank还是双bank&#xff1f;由于时间太久&#xff0c;我真忘记了。搜搜发现&#xff0c;还真有人和我一样。见下面的链接&#xff1a;https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...

【JVM】- 内存结构

引言 JVM&#xff1a;Java Virtual Machine 定义&#xff1a;Java虚拟机&#xff0c;Java二进制字节码的运行环境好处&#xff1a; 一次编写&#xff0c;到处运行自动内存管理&#xff0c;垃圾回收的功能数组下标越界检查&#xff08;会抛异常&#xff0c;不会覆盖到其他代码…...

el-switch文字内置

el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...

【JavaSE】绘图与事件入门学习笔记

-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角&#xff0c;以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向&#xff0c;距离坐标原点x个像素;第二个是y坐标&#xff0c;表示当前位置为垂直方向&#xff0c;距离坐标原点y个像素。 坐标体系-像素 …...

使用 SymPy 进行向量和矩阵的高级操作

在科学计算和工程领域&#xff0c;向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能&#xff0c;能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作&#xff0c;并通过具体…...

CSS设置元素的宽度根据其内容自动调整

width: fit-content 是 CSS 中的一个属性值&#xff0c;用于设置元素的宽度根据其内容自动调整&#xff0c;确保宽度刚好容纳内容而不会超出。 效果对比 默认情况&#xff08;width: auto&#xff09;&#xff1a; 块级元素&#xff08;如 <div>&#xff09;会占满父容器…...

如何更改默认 Crontab 编辑器 ?

在 Linux 领域中&#xff0c;crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用&#xff0c;用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益&#xff0c;允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...