Linux内核编程(六)平台总线plantform驱动模型

news2024/11/17 13:24:42

本文目录

  • 前述:为什么引入平台总线模型
  • 一、知识点
    • 1. 什么是平台总线模型
    • 2. 平台总线模型使用
    • 3. 平台总线是如何工作的
    • 4. 平台总线模型的优点
  • 二、平台总线设备层
    • 1. 常用API
      • (1) 注册一个平台设备
      • (2) 注销一个平台设备
      • (3)平台设备结构体
      • (4)设备资源结构体
      • (5)内嵌标准 device结构体
    • 2. 设备层框架代码编写
  • 三、平台总线驱动层
    • 1. 常用API
      • (1)注册一个平台驱动
      • (2)注销一个平台驱动
      • (3)平台驱动结构体
      • (4)描述驱动信息结构体
      • (4)获取设备层的硬件资源
      • (5)获取设备层自定义平台数据
    • 2. 驱动层框架代码编写

  

前述:为什么引入平台总线模型

   例如:我们有多个硬件设备,每个硬件设备的操作寄存器地址都不同,如果我们使用一个ko文件来编写驱动的话,每当我们更换一个设备时就需要重新写一份代码,这个代码中很多地方其实是不需要更改的,这样就增大了很多没有没必要的工作量。为了解决这个问题,我们可以把驱动的控制代码和硬件层分隔开来编写,每当我们需要更换设备时,只需要更改硬件层的代码即可。

一、知识点

1. 什么是平台总线模型

   平台总线模型也叫plantform总线模型。平台总线是Linux系统虚拟出来的总线。

2. 平台总线模型使用

   平台总线模型将一个驱动分成了俩个部分,分别是device.c和driver.c,device.c用来描述硬件设备资源,driver.c用来获取硬件资源,并控制硬件。将设备层和驱动层分布生成ko文件进行安装。
在这里插入图片描述

3. 平台总线是如何工作的

   平台总线通过字符串比较,将name相同的device.c和driver.c匹配到一起,然后驱动就可以获取与其匹配的设备的硬件资源来来控制硬件。

4. 平台总线模型的优点

  1. 减少编写重复代码,提高效率。
  2. 提高代码的利用率。

二、平台总线设备层

1. 常用API

头文件:linux/platform_device.h

(1) 注册一个平台设备

返回:0:注册成功;负数:注册失败

int platform_device_register(struct platform_device *pdev)
//pdev: 要注册的 struct platform_device 结构指针。

(2) 注销一个平台设备

void platform_device_unregister(struct platform_device *pdev)
//pdev: 要注销的 struct platform_device 结构指针。

(3)平台设备结构体

struct platform_device {
	const char * name; //设备名,要求和驱动中的.name 相同。用于与内核层name进行匹配。
	int id; //设备 ID,一般为-1。
	struct device dev; //内嵌标准 device,一般可以用来传递平台数据
	u32 num_resources; //设备占用资源个数
	struct resource * resource; //设备占用资源的首地址。
	
	//下面两个成员,我们一般不使用。
	struct platform_device_id *id_entry;//设备 id 入口,一般驱动不用
	struct pdev_archdata archdata;
};

(4)设备资源结构体

含头文件:linux\ioport.h

struct resource {
		resource_size_t start;  //硬件资源的起始地址。
		resource_size_t end;  //硬件资源的结束地址。
		const char *name;   //资源名称,自定义,主要用于区分资源,也可以不写。
		unsigned long flags;  //资源类型。这里不做过多介绍,详情查看其他博客。
		
		//下面成员一般不使用。
		struct resource *parent, *sibling, *child;
};

/*flags部分内容:
		#define IORESOURCE_TYPE_BITS 0x00001f00  
		#define IORESOURCE_IO 0x00000100   //IO资源类型
		#define IORESOURCE_MEM 0x00000200  //内存资源类型
		#define IORESOURCE_REG 0x00000300  //寄存器资源类型
		#define IORESOURCE_IRQ 0x00000400  //中断资源类型
		#define IORESOURCE_DMA 0x00000800  //DMA资源类型
		#define IORESOURCE_BUS 0x00001000  //总线资源类型
*/

(5)内嵌标准 device结构体

注意: 我们必须要实现这个结构体的release函数!当设备被移除时,触发该函数。
*platform_data 称为平台数据指针,可以给平台驱动层传递任何需要的信息

struct device {
//重要
		void (*release)(struct device *dev);  //设备被移除时触发。
		void *platform_data; /* 平台设备的私有数据指针,要以传递任何结构*/
		
//下面的不常用		
		struct device *parent; /* 父设备指针 */
		const char *init_name; /*逻辑设备的名字*/
		struct device_type *type; /* 设备类型 */
		struct bus_type *bus; /* 设备所属的总线类型 */
		struct device_driver *driver; /* 指向开辟 struct device 结构的 driver 指针*/
		u64 *dma_mask; /* dma mask (if dma'able device) */
		u64 coherent_dma_mask;
		dev_t devt; /* 存放设备号 dev_t, creates the sysfs "dev" */
		struct class *class; /* 设备所属类*/
};

2. 设备层框架代码编写

device.c

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

char my_platform_data[] = {"hello, world"}; // 自定义数据类型

void device_release(struct device *dev) // 设备被卸载时触发
{
    pr_err("device_release\n");
}

// 初始化 device 结构体
struct device my_dev = {
    .release = device_release,
    .platform_data = my_platform_data, // 平台数据
};

// 描述硬件资源。为了方便演示,我们先随机赋值资源的开始地址和结束地址。
struct resource my_resource[] = {
    // 第0个硬件资源
    [0] = {
        .start = 100, // 资源开始地址
        .end = 200,   // 资源结束地址
        .name = "led",  // 非必须,主要为标识作用
        .flags = IORESOURCE_MEM, // 资源类型,内存类型
    },

    // 第1个硬件资源
    [1] = {
        .start = 0, // 资源开始地址
        .end = 50,  // 资源结束地址
        .name = "beep",  // 非必须,主要为标识作用
        .flags = IORESOURCE_IRQ, // 资源类型,中断类型
    },
};

// 平台设备结构体
struct platform_device my_pdev = {
    .name = "my_device", // 这个名字要和内核层的名字相同,用于匹配
    .id = -1, // 常为-1,自动分配id号
    .dev = my_dev,
    .resource = my_resource, // 硬件资源
    .num_resources = ARRAY_SIZE(my_resource), // 资源的数量
};

// 入口函数
static int __init mydevice_init(void)
{
    int ret;
    ret = platform_device_register(&my_pdev); // 注册一个平台设备
    if (ret < 0) {
        pr_err("platform_device_register error\n");
        return -1;
    }
    return 0;
}

// 出口函数
static void __exit mydevice_exit(void)
{
    platform_device_unregister(&my_pdev); // 注销一个平台设备
}

module_init(mydevice_init);
module_exit(mydevice_exit);

MODULE_LICENSE("GPL");

三、平台总线驱动层

1. 常用API

(1)注册一个平台驱动

返回值 0:注册成功;负数:注册失败

int platform_driver_register(struct platform_driver *drv)
//要注册的 struct platform_driver 结构指针。

(2)注销一个平台驱动

void platform_driver_unregister(struct platform_driver *drv)
//要注销的 struct platform_driver 结构指针。

(3)平台驱动结构体

我们需要在设备和驱动匹配上时触发的probe函数中,注册杂项设备!

struct platform_driver {
//常用
	int (*probe)(struct platform_device *); //当驱动层和设备层匹配上时触发。
	int (*remove)(struct platform_device *); //当驱动被卸载,或者设备被移除时触发。
	struct device_driver driver; //用于描述任何类型的设备驱动程序。
	struct platform_device_id *id_table; //支持的设备 id 列表(多个name列表)
	
//电源类函数
	void (*shutdown)(struct platform_device *); //关闭设备
	int (*suspend)(struct platform_device *, pm_message_t state); //挂起函数
	int (*resume)(struct platform_device *); //恢复函数
};

(4)描述驱动信息结构体

   当平台驱动结构体没有赋值id_table成员时,设备层的name会与device_driver结构体的name成员进行匹配。如果平台驱动结构体中实现了id_table成员时,会优先与id_table列表中的name进行匹配。

//这里只写出几个常用的成员函数。
struct device_driver {  
		const char *name; /*驱动层的名字,用来和设备层匹配的*/ 
	    struct module *owner;  //模块拥有者,一般为:THIS_MODULE。
};

(4)获取设备层的硬件资源

返回:NULL:获取失败,资源不存在;非 NULL:获得成功,指向资源地址。

struct resource *platform_get_resource(struct platform_device *dev,unsigned int type, unsigned int num)
//struct platform_device *dev: 设备层的设备结构体。
//unsigned int type :要获取哪个类型的资源。
//unsigned int num :获取该类型资源的第几个。

(5)获取设备层自定义平台数据

设备层传输的平台数据是什么类型,就用什么类型来接收该函数的返回值。

static inline void *dev_get_platdata(const struct device *dev)
//const struct device *dev:设备层结构体中的device。 

2. 驱动层框架代码编写

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/platform_device.h>
#include <linux/uaccess.h>

// 打开函数
int my_open(struct inode *node, struct file *fp)
{
    return 0;
}

// 释放函数
int my_release(struct inode *node, struct file *fp)
{
    return 0;
}

// IO控制函数
long my_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
{
    return 0;
}

// 文件操作结构体
const struct file_operations my_fops = {
    .open = my_open,
    .release = my_release,
    .unlocked_ioctl = my_ioctl,
};

// 杂项设备结构体
struct miscdevice misc = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = "misc_device",   // 驱动设备的名称
    .fops = &my_fops,
};

// probe函数,在设备与驱动匹配时触发
int my_probe(struct platform_device *pdev)
{
    int ret;
    struct resource *dev_resource;  // 用于接收设备的硬件资源
    char *platform_data;
    
    // 获取设备层的平台数据资源
    platform_data = dev_get_platdata(&pdev->dev);
    pr_err("platform_data: %s\n", platform_data);  // 将获取的平台数据打印出来

    // 获取设备层的硬件资源
    dev_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);  // 接收内存类型的第一个资源
    if (dev_resource == NULL) {
        pr_err("dev_resource NULL\n");
        return -ENODEV;
    }
    pr_err("start: %pa, end: %pa\n", &dev_resource->start, &dev_resource->end);  // 将获取的资源信息打印出来

    // 注册杂项设备
    ret = misc_register(&misc);
    if (ret < 0) {
        pr_err("misc_register error\n");
        return ret;
    }

    return 0;
}

// remove函数,在驱动被卸载或设备被移除时触发
int my_remove(struct platform_device *pdev)
{
    misc_deregister(&misc);  // 注销杂项设备
    return 0;
}

// 平台驱动结构体
struct platform_driver my_drv = {
    .probe = my_probe,  // 设备和驱动name匹配上时触发
    .remove = my_remove,  // 驱动被卸载或设备被移除时触发
    .driver = {
        .name = "platform_test",   // 要与设备层同名,用于匹配
        .owner = THIS_MODULE,
    },
};

// 模块初始化函数
static int __init my_driver_init(void)
{
    int ret;
    ret = platform_driver_register(&my_drv);  // 注册一个平台驱动
    if (ret < 0) {
        pr_err("platform_driver_register error\n");
    }
    return ret;
}

// 模块退出函数
static void __exit my_driver_exit(void)
{
    platform_driver_unregister(&my_drv);  // 注销一个平台驱动
}

module_init(my_driver_init);
module_exit(my_driver_exit);

MODULE_LICENSE("GPL");

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

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

相关文章

专业和学校到底怎么选,兴趣和知名度到底哪个重要?

前言 2024高考已经落下帷幕&#xff0c;再过不久就到了激动人心的查分和填报志愿的时刻&#xff0c;在那天到来&#xff0c;小伙伴们就要根据自己的分数选取院校和专业&#xff0c;接下来我就以参加22年(破防年)河南高考的大二生来讲述一下我自己对于如何选取院校和专业的看法以…

Attention Is All You Need详解

一.背景。 在此模型之前&#xff0c;序列到序列的任务&#xff08;如机器翻译、文本摘要等&#xff09;通常采用循环神经网络&#xff08;RNN&#xff09;或卷积神经网络&#xff08;CNN&#xff09;。然而&#xff0c;RNN 在处理长距离依赖时存在一定的局限性&#xff08;举个…

20240619在飞凌OK3588-C的LINUX系统启动的时候拉高3个GPIO口141-111-120【方法一】

20240619在飞凌OK3588-C的LINUX系统启动的时候拉高3个GPIO口141-111-120【方法一】 2024/6/19 16:12 缘起&#xff1a;在凌OK3588-C的LINUX R4系统启动的时候&#xff0c;需要拉高GPIO4_B5、GPIO3_B7和GPIO3_D0。 修改rcS&#xff0c;在系统启动的时候&#xff0c;即可拉高。 通…

通信系统的最佳线性均衡器(1)---维纳滤波线性均衡

本篇文章是博主在通信等领域学习时&#xff0c;用于个人学习、研究或者欣赏使用&#xff0c;并基于博主对通信等领域的一些理解而记录的学习摘录和笔记&#xff0c;若有不当和侵权之处&#xff0c;指出后将会立即改正&#xff0c;还望谅解。文章分类在通信领域笔记&#xff1a;…

全新剧场app的独特功能

全新剧场App通过引入一系列独特功能&#xff0c;旨在提升用户体验、增加用户粘性并拓宽市场范围。以下是对这些功能的详细分析&#xff1a; 1、虚拟剧场导览&#xff1a; 功能概述&#xff1a;利用增强现实技术&#xff0c;为用户提供虚拟剧场导览体验。用户可以在App中启动这…

一文读懂Java线程状态转换

Java线程有哪些状态?状态如何转换? 线程可以拥有自己的操作栈、程序计数器、局部变量表等资源,它与同一进程内的其他线程共享该进程的所有资源。Java的线程有自己的生命周期,在 Java 中线程的生命周期中一共有 6 种状态。 NewRunnableBlockedWaitingTimed WaitingTerminat…

报表工具数据源的取数处理方式大对比

根据报表的需求&#xff0c;很多报表中的指标数据需要进行预处理&#xff0c;以满足快速抽取和展示的需要。对于帆软报表类似的产品&#xff0c;一般通过建立视图、合并数据表&#xff0c;形成直接应用于模板设计的数据集&#xff0c;报表直接和数据集进行交互、关联。当用户发…

AI时代中的模型安全保护,如何通过加密和许可管理保障AI模型的安全

在进入AI时代中&#xff0c;网络安全威胁和数字版权管理变得愈发复杂&#xff0c;保护AI数据模型变得至关重要。这些模型已成为企业核心竞争力的关键&#xff0c;尤其在医疗设备和工业自动化等高敏感领域。确保数据模型的安全性和完整性不仅是保护知识产权的必要措施&#xff0…

【Linux 基础】目录结构

Linux 的目录结构&#xff08;也称为文件系统结构&#xff09;是组织文件和目录的一种逻辑方式。每个文件和目录在文件系统中都有一个唯一的位置或路径。 Linux文件系统是整个操作系统的基础架构&#xff0c;对于系统的稳定运行、数据安全以及用户操作便捷性至关重要&#xff0…

全球AI视频技术竞赛加速:Runway即将推出更优更快的第三代AI视频模型|TodayAI

Runway即将在未来几天推出其更优更快的第三代AI视频模型&#xff0c;这是新一代模型中最小的一个。据公司透露&#xff0c;这款名为Gen-3的模型将带来“在真实度、一致性和动态效果上的重大提升”&#xff0c;同时在速度上也有显著的加快。 去年六月&#xff0c;Runway首次推出…

Redis 集群 - 数据分片算法

前言 广义的集群&#xff1a;只要是多个机器构成了一个分布式系统&#xff0c;都可以被称为集群。 狭义的集群&#xff1a;redis 的集群模式&#xff0c;这个集群模式下&#xff0c;主要是解决存储空间不足的问题。 Redis 集群 redis 采用主从结构&#xff0c;可以提高系统的可…

ABAP 搜索帮助F4IF_INT_TABLE_VALUE_REQUEST

F4IF_INT_TABLE_VALUE_REQUEST 一般用于在选择屏幕提供搜索帮助 可以看到设置的是物料与物料描述的对应关系&#xff0c;而且对话类型是立即显示值&#xff0c;所以才能够实现如上的效果 有两种搜索帮助,这里选择基本索引帮助即可 然后填上对应的文本表和字段即可 然后在选…

【非常实验】Android模拟x86_64系统——安装Alpine虚拟机

安卓是一款功能强大的操作系统,为什么不试试它的极限呢? 百无聊赖中,我发现了各种 Android 修补项目。这激起了我对 DevOps 的好奇心,促使我探索在该平台上运行容器。这种好奇心又把我带入了另一个兔子洞:在 Android 上运行虚拟机。这其中经历了许多曲折,也许以后有必要…

cs144 LAB1 基于滑动窗口的碎片字节流重组器

一.StreamReassembler.capacity 的意义 StreamReassembler._capacity 的含义&#xff1a; ByteStream 的空间上限是 capacityStreamReassembler 用于暂存未重组字符串片段的缓冲区空间 StreamReassembler.buffer 上限也是 capacity蓝色部分代表了已经被上层应用读取的已重组数…

计算机专业毕设-springboot论坛系统

1 项目介绍 基于SSM的论坛网站&#xff1a;后端 SpringBoot、Mybatis&#xff0c;前端thymeleaf&#xff0c;具体功能如下&#xff1a; 基本功能&#xff1a;登录注册、修改个人信息、修改密码、修改头像查看帖子列表&#xff1a;按热度排序、按更新时间排序、查看周榜月榜查…

棱镜七彩荣获CNNVD两项大奖,专业能力与贡献再获认可!

6月18日&#xff0c;国家信息安全漏洞库&#xff08;CNNVD&#xff09;2023年度工作总结暨优秀表彰大会在中国信息安全测评中心成功举办。棱镜七彩凭借在漏洞方面的突出贡献和出色表现&#xff0c;被授予“2023年度优秀技术支撑单位”与“2023年度最佳新秀奖”。 优秀技术支撑单…

Gobject tutorial 七

The GObject base class GObject是一个fundamental classed instantiatable type,它的功能如下&#xff1a; 内存管理构建/销毁实例set/get属性方法信号 /*** GObjectClass:* g_type_class: the parent class* constructor: the constructor function is called by g_object…

最新技术:跨境电商源码,应对多国市场需求,让您轻松开展全球业务!

随着全球化进程的不断推进&#xff0c;跨境电商已成为企业拓展国际市场的重要途径。为了满足不同国家和地区消费者不断增长的需求&#xff0c;跨境电商源码应运而生&#xff0c;为企业提供了便捷高效的全球化业务发展方案。 一、全球化运营的关键 跨境电商源码的核心功能在于…

极具吸引力的小程序 UI 风格

极具吸引力的小程序 UI 风格

小白速成AI大模型就看这份资源包

前言 在数字化浪潮席卷全球的今天&#xff0c;人工智能&#xff08;AI&#xff09;技术已成为推动社会进步的重要引擎。尤其是AI大模型&#xff0c;以其强大的数据处理能力和广泛的应用前景&#xff0c;吸引了无数人的目光。然而&#xff0c;对于初学者“小白”来说&#xff0…