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

本文目录

  • 前述:为什么引入平台总线模型
  • 一、知识点
    • 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.mzph.cn/diannao/30445.shtml

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

相关文章

最好用的智能猫砂盆存在吗?自用分享智能猫砂盆测评!

在现代都市的忙碌生活中&#xff0c;作为一名上班族&#xff0c;经常因为需要加班或频繁出差而忙碌得不可开交。急匆匆地出门&#xff0c;却忘了给猫咪及时铲屎。但是大家要知道&#xff0c;不及时清理猫砂盆会让猫咪感到不适&#xff0c;还会引发各种健康问题&#xff0c;如泌…

HTML入门教程:深度解析HTML,开启你的前端技术之旅

一、引言 HTML&#xff08;HyperText Markup Language&#xff0c;超文本标记语言&#xff09;是前端开发的基础&#xff0c;它负责构建网页的结构和内容。作为前端技术栈的基石&#xff0c;HTML的掌握程度直接影响到网页的开发效率和用户体验。本教程将带你从零开始&#xff…

(新)Spring Security如何实现登录认证(实战篇)

一、回顾认证流程详解 概念速查: Authentication接口: 它的实现类&#xff0c;表示当前访问系统的用户&#xff0c;封装了用户相关信息。 AuthenticationManager接口&#xff1a;定义了认证Authentication的方法 UserDetailsService接口&#xff1a;加载用户特定数据的核心接…

从视频创意到传播策略 | 医药产品TVC新媒体传播方案

作为营销策划人&#xff0c;你一定在寻找能够激发创意灵感、拓展策划视野的实战案例。这份最新传播方案由Unithought精心打造&#xff0c;不仅是一份详尽的策划指南&#xff0c;更是一次深入患者心灵的品牌传播实践。 何策网&#xff0c;每日收录全网方案PPT &#xff01; 方…

Simulink代码生成: 基本数据类型

文章目录 1 引言2 Simulink中的基本数据类型3 数据类型实例3.1 浮点类型3.2 整数类型3.3 布尔类型 3 数据类型使用的注意点3.1 浮点数等于比较3.2 整形数溢出3.3 布尔类型的位域 4 关于定点数的说明5 总结 1 引言 正如C语言中为变量区分了不同的数据类型一样&#xff0c;Simul…

Spark日志有哪些?

spark.log&#xff1a;记录作业运行日志&#xff0c;包括Spark框架内部日志和用户通过日志接口输出的日志。 executor 启动结束日志&#xff1a; job&#xff0c;stage&#xff0c;task提交结束日志&#xff1a; pmap.log&#xff1a;周期性地截取Driver或Executor的pmap和…

Java 17的新特性

Java 17引入了多项新特性&#xff0c;以下是一些重要的更新&#xff1a; 增强的伪随机数生成器&#xff08;JEP 356&#xff09; Java 17为伪随机数生成器&#xff08;PRNG&#xff09;提供了新的接口类型和实现&#xff0c;包括可跳转的PRNG和另一类可拆分的PRNG算法&#xf…

公开整理-中国海关进出口增减数据(2008-2024年)

数据来源&#xff1a;东方财富网 时间跨度&#xff1a;2008年至今 数据范围&#xff1a;全国范围 数据指标&#xff1a; 年月 当月出口额-金额 当月出口额-同比增长 当月出口额-环比增长 当月进口额-金额 当月进口额-同比增长 当月进口额-环比增长 累计…

SCI一区TOP|常青藤优化算法(IVYA)原理及实现【免费获取Matlab代码】

目录 1.背景2.算法原理2.1算法思想2.2算法过程 3.结果展示4 .参考文献5.代码获取 1.背景 2024年&#xff0c;M Ghasemi受到自然界中常青藤生长行为启发&#xff0c;提出了常青藤优化算法&#xff08;Ivy Algorithm, IVYA&#xff09;。 2.算法原理 2.1算法思想 IVYA模拟常青…

【Linux】环境基础开发工具使用(yum、vim、gcc/g++、gdb、make/Makefile)

文章目录 Linux 软件包管理器 yumLinux开发工具Linux编辑器-vim使用vim的基本概念vim下各模式的切换vim命令模式各命令汇总vim底行模式各命令汇总批量化注释和批量化去注释vim简单的配置解决一个小问题 Linux编译器-gcc/g作用gcc/g 语法预处理编译汇编链接什么是函数库 Linux调…

【后端】websocket学习笔记

文章目录 1. 消息推送常见方式1.1 轮询 VS 长轮询1.2 SSE&#xff08;server-sent event)服务器发送事件 2. websocket介绍2.1 介绍2.2 原理2.3 websoket API2.3.1 客户端【浏览器】API2.3.2 服务端API 3. 代码实现3.1 流程分析3.2 pom依赖3.3 配置类3.4 消息格式3.5 消息类 4.…

Weevil-Optimizer象鼻虫优化算法的matlab仿真实现

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 5.完整程序 1.程序功能描述 Weevil-Optimizer象鼻虫优化算法的matlab仿真实现&#xff0c;仿真输出算法的优化收敛曲线&#xff0c;对比不同的适应度函数。 2.测试软件版本以及运行结果展示…

Linux - 进程

一、什么是进程 首先&#xff0c;Linux是一个多用户多进程的操作系统&#xff0c;系统上可以同时运行多个进程。 进程的产生&#xff1a;①是在执行程序或者命令时产生的&#xff1b;②定时任务进程 进程的类型&#xff1a;前台进程/后台进程 前台进程&#xff1a;一个终端…

WDC西部数据闪存业务救赎之路,会成功吗?

一、序言 在存储界的江湖里&#xff0c;WDC就像是一位手握两大秘籍&#xff08;闪迪和铠侠NAND工厂&#xff09;的武林高手&#xff0c;本以为能在企业级SSD的擂台上大展身手&#xff0c;结果却发现自己更像是被误邀参加学霸聚会的学渣&#xff0c;心里那个苦啊&#xff0c;只…

Java15-API

目录 Math类 概述 常见方法 练习 System类 概述 常见方法 Runtime 概述 常见方法 Object类 概述 常见方法 一.演示toString方法 二.演示equals方法 三、对象克隆 分类&#xff1a; 浅克隆 深克隆&#xff1a; Objests类 概述 常见方法 BigInteger类 概…

jenkins安装和使用 (二)

参考视频资料 https://www.bilibili.com/video/BV1bS4y1471A?p10&vd_sourcee0dcd147bd5d730317de804d788cd6f9 安装maven插件 新建item 配置构建信息 项目地址替换为自己的实际地址 其余保持先保持默认 先然后在主页就看到了这个项目 查看控制台输出 稍等一…

李宏毅2023机器学习作业HW06解析和代码分享

ML2023Spring - HW6 相关信息&#xff1a; 课程主页 课程视频 Sample code HW06 视频 HW06 PDF 个人完整代码分享: GitHub | Gitee | GitCode P.S. HW06 是在 Judgeboi 上提交的&#xff0c;出于学习目的这里会自定义两个度量的函数&#xff0c;不用深究&#xff0c;遵循 Sugge…

【计算机毕业设计】211校园约拍微信小程序

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

网工内推 | 中国电信、香港宽频系统工程师,CCIE认证优先,最高年薪25w

01 中国电信股份有限公司浙江分公司 &#x1f537;招聘岗位&#xff1a;系统架构师 &#x1f537;岗位职责&#xff1a; 1、做好客户网络和信息安全产品的解决方案支撑、交付及后续运营维护&#xff0c;做好相关产数项目的支撑。 2、根据信息安全管理要求&#xff0c;负责客户…

调取Windows系统虚拟键盘

目录 一 设计原型 二 后台源码 一 设计原型 二 后台源码 using System.Diagnostics;namespace 调取Windows虚拟键盘 {public partial class Form1 : Form{public Form1(){InitializeComponent();}private void richTextBox1_DoubleClick(object sender, EventArgs e){Proces…