当前位置: 首页 > 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函数,具体效果如下

在这里插入图片描述

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 【多线程-从零开始-捌】阻塞队列,消费者生产者模型
  • Unity Android端截图保存并获取展示
  • Milvus向量数据库的简介以及用途
  • 怎么判断张量的维度(形状(shape)),即如何定义行数、列数和深度的?
  • ARM 架构硬件新趋势:嵌入式领域的未来
  • 【C#语音文字互转】.NET的TTS文本转语音合成
  • Java面试篇(线程池相关专题)
  • 问题解决:CUDA_HOME environment variable is not set.
  • HTTPS链接建立的过程
  • 工业除尘的一些方法
  • 简要:JVM底层原理、JVM各类垃圾收集器的使用及核心参数的调优、JVM 调优
  • Makefile自动依赖
  • package.json的 和 的区别,以及|| 和 | 的区别
  • 告别杂音,从 AI 音频降噪开始
  • 文件上传绕过最新版安全狗
  • [笔记] php常见简单功能及函数
  • 【140天】尚学堂高淇Java300集视频精华笔记(86-87)
  • JavaScript 一些 DOM 的知识点
  • javascript从右向左截取指定位数字符的3种方法
  • JavaScript设计模式与开发实践系列之策略模式
  • js学习笔记
  • JS专题之继承
  • Magento 1.x 中文订单打印乱码
  • MobX
  • nginx 负载服务器优化
  • SpringCloud集成分布式事务LCN (一)
  • Vue全家桶实现一个Web App
  • 利用DataURL技术在网页上显示图片
  • 猫头鹰的深夜翻译:JDK9 NotNullOrElse方法
  • 前端攻城师
  • 如何打造100亿SDK累计覆盖量的大数据系统
  • 如何正确配置 Ubuntu 14.04 服务器?
  • 一、python与pycharm的安装
  • 最简单的无缝轮播
  • LevelDB 入门 —— 全面了解 LevelDB 的功能特性
  • 选择阿里云数据库HBase版十大理由
  • ​Base64转换成图片,android studio build乱码,找不到okio.ByteString接腾讯人脸识别
  • ​VRRP 虚拟路由冗余协议(华为)
  • ​如何使用ArcGIS Pro制作渐变河流效果
  • #Datawhale AI夏令营第4期#多模态大模型复盘
  • #pragam once 和 #ifndef 预编译头
  • #pragma预处理命令
  • #鸿蒙生态创新中心#揭幕仪式在深圳湾科技生态园举行
  • (2024,LoRA,全量微调,低秩,强正则化,缓解遗忘,多样性)LoRA 学习更少,遗忘更少
  • (Pytorch框架)神经网络输出维度调试,做出我们自己的网络来!!(详细教程~)
  • (ZT) 理解系统底层的概念是多么重要(by趋势科技邹飞)
  • (仿QQ聊天消息列表加载)wp7 listbox 列表项逐一加载的一种实现方式,以及加入渐显动画...
  • (一) storm的集群安装与配置
  • (原創) 物件導向與老子思想 (OO)
  • (转)ABI是什么
  • (转)Sql Server 保留几位小数的两种做法
  • .NET “底层”异步编程模式——异步编程模型(Asynchronous Programming Model,APM)...
  • .NET 4.0中使用内存映射文件实现进程通讯
  • .net CHARTING图表控件下载地址
  • .net core Redis 使用有序集合实现延迟队列