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");
}

// 描述硬件资源。为了方便演示,我们先随机赋值资源的开始地址和结束地址。
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 = "platform_test", // 这个名字要和内核层的名字相同,用于匹配
    .id = -1, // 常为-1,自动分配id号
    .dev = {
			    .release = device_release,
			    .platform_data = my_platform_data, // 平台数据
            },
    .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");

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2748. 美丽下标对的数目

题目 给定一个下标从 0 开始的整数数组 nums。如果下标对 (i, j) 满足 0 ≤ i < j < nums.length&#xff0c;且 nums[i] 的第一个数字与 nums[j] 的最后一个数字互质&#xff0c;那么认为 nums[i] 和 nums[j] 是一组美丽下标对。 对于两个整数 x 和 y&#xff0c;如果…

无忧易售新功能:集成图片库智能图片翻译,跨越语言障碍

在电商全球化的浪潮中&#xff0c;跨越语言的障碍&#xff0c;让产品图像说话&#xff0c;成为了商家致胜的关键。"无忧易售ERP"推出集成图片库与图片翻译功能的全新升级&#xff0c;为全球电商提供一站式解决方案&#xff0c;让商品跨越国界&#xff0c;沟通无界。 …

使用二进制安装安装docker

在一些情况下无法使用yum安装docker下面写了一个使用二进制安装docker的文档 官网下载地址https://download.docker.com/linux/static/stable/x86_64/ 可以按需求下载 wget https://download.docker.com/linux/static/stable/x86_64/docker-20.10.10.tgz 下载包 tar xf dcker…

计算机网络 —— 应用层(DHCP)

计算机网络 —— 应用层&#xff08;DHCP&#xff09; 什么是DHCPDHCP工作过程DHCP DISCOVERDHCP OFFERDHCP RQUESTDHCP ACK DHCP租约机制中继代理工作原理功能与优势 我们今天来计网的DHCP&#xff1a; 什么是DHCP DHCP&#xff08;Dynamic Host Configuration Protocol&…

Python11 使用爬虫实现图书250排行榜信息爬取

1.什么是网络爬虫 Python爬虫是使用Python编程语言编写的程序&#xff0c;它能自动从互联网上抓取数据。这类程序一般利用网络请求来访问网站&#xff0c;解析网站的HTML或其他格式的内容&#xff0c;提取出有用的数据&#xff0c;有时还会进行后续的数据处理或存储。 Python…

人工智能大模型之开源大语言模型汇总(国内外开源项目模型汇总)

开源大语言模型完整列表 Large Language Model (LLM) 即大规模语言模型&#xff0c;是一种基于深度学习的自然语言处理模型&#xff0c;它能够学习到自然语言的语法和语义&#xff0c;从而可以生成人类可读的文本。 所谓"语言模型"&#xff0c;就是只用来处理语言文…

如何制定数据治理策略?做好这7点就够了

在当今的商业环境中&#xff0c;数据已成为企业最宝贵的资产之一。随着大数据、云计算、物联网&#xff08;IoT&#xff09;和人工智能&#xff08;AI&#xff09;等技术的不断进步&#xff0c;企业积累的数据量呈指数级增长&#xff0c;这为企业提供了前所未有的商业机会&…

大语言模型的微调方法_大语言模型六种微调方法

01 引言 自2018年BERT发布以来&#xff0c;“预训练微调”成为语言模型的通用范式。以ChatGPT为代表的大语言模型针对不同任务构造Prompt来训练&#xff0c;本质上仍然是预训练与微调的使用范式。千亿规模的参数微调需要大量算力&#xff0c;即使提供了预训练的基座模型&…

正版 navicat 下载

1. 打开浏览器访问 navicat 官网 Navicat | 下载 Navicat Premium 14 天免费 Windows、macOS 和 Linux 的试用版 windows 用户选择这三项其中一个就可以 2. 下载 点击之后等个几秒钟就会开始下载了 3. 双击打开 下载好的 .exe 程序 进入安装程序 (不影响之前已经安装过的) 可…

数据结构试题 20-21

真需要就死记吧 二叉树遍历-先序(非递归)【图解代码】_哔哩哔哩_bilibili 解释一下步骤&#xff1a; 一个循环为&#xff1a; 1.取节点 2.放右子树 3.放左子树 每次循环&#xff0c;都要从栈里取出一个节点 先放右子树&#xff0c;再放左子树 那这道题就是&#xff0c;先放1&am…

山西青年杂志山西青年杂志社山西青年编辑部2024年第10期目录

本刊专稿 共融共创、校企共建BIM创新创业中心的探索与实践 黄强;马福贵;贾晓敏;苏艳贞;魏艳卿; 1-3 财务管理课程专创融合教学改革与实践 宋衍程; 4-7 数字化赋能国际贸易实务课程建设研究 吴珍彩; 8-11《山西青年》投稿&#xff1a;cn7kantougao163.com 青年教育研…

智慧学习实践系统的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;企业管理&#xff0c;任务管理&#xff0c;公告管理&#xff0c;菜单管理&#xff0c;用户管理&#xff0c;基础数据管理 企业账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;任务…

android 在线程中更新界面

在Android中&#xff0c;你不能直接从子线程中更新UI&#xff0c;因为这会导致应用崩溃。你需要使用Handler或runOnUiThread()来更新UI。 使用Handler 以下是如何使用Handler在子线程中更新UI的示例&#xff1a; 1. 创建Handler实例&#xff1a; import android.os.Bundle;…

从boost库到时间戳

一、以问题引入 授权证书一般有到期时间的说法&#xff0c;公司测试同事在测试更新后的证书时&#xff0c;将系统时间调到了2050年&#xff0c;重启服务后发现各个进程的cpu占用率特别高&#xff1b;结合日志分析&#xff0c;发现这些进程 都在不停的刷heartbeat()的日志&#…

常用的Java日志框架:Log4j、SLF4J和Logback

日志是软件开发中不可或缺的一部分&#xff0c;它有助于记录应用程序的运行状态、调试问题和监控系统。Java中有多个流行的日志框架&#xff0c;如Log4j、SLF4J和Logback。 一、Log4j 1.1 什么是Log4j&#xff1f; Log4j是Apache基金会开发的一个开源日志框架&#xff0c;它…

webpack处理样式资源04--webpack入门学习

处理样式资源 本章节学习使用 Webpack 如何处理 Css、Less、Sass、Scss、Styl 样式资源 介绍 Webpack 本身是不能识别样式资源的&#xff0c;所以我们需要借助 Loader 来帮助 Webpack 解析样式资源 我们找 Loader 都应该去官方文档中找到对应的 Loader&#xff0c;然后使用…

【0-1系列】从0-1快速了解搜索引擎Scope以及如何快速安装使用(下)

前言 近日&#xff0c;社区版家族正式发布V2024.5版本&#xff0c;其中&#xff0c;社区开发版系列重磅发布Scope开发版以及StellarDB开发版。 为了可以让大家更进一步了解产品&#xff0c;本系列文章从背景概念开始介绍&#xff0c;深入浅出的为读者介绍Scope的优势以及能力…

链表经典面试题--链表修至圆满

目录 1.环形链表 a.为什么一定会相遇&#xff0c;有没有可能会错过&#xff0c;永远追不上&#xff1f;请证明 b.slow一次走1步&#xff0c;fast走3步 4步 5步 n步还一定追得上吗 请证明 2.环形链表2 3.随机链表的复制 1.环形链表 141. 环形链表 - 力扣&#xff08;Lee…

【stm32-新建工程-寄存器版本】

stm32-新建工程-寄存器版本 ■ 下载相关STM32Cube官方固件包&#xff08;F1&#xff0c;F4&#xff0c;F7&#xff0c;H7&#xff09;■ 1. ST官方搜索STM32Cube■ 2. 搜索 STM32Cube■ 3. 点击获取软件■ 4. 选择对应的版本下载■ 5. 输入账号信息■ 6. 出现下载弹框&#xff…

【轨迹规划论文整理(1)】UAV轨迹规划的开山之作Minimum Snap Trajectory

【轨迹规划论文整理(1)】UAV轨迹规划的开山之作Minimum Snap Trajectory Generation and Control for Quadrotors 本系列主要是对精读的一些关于无人机、无人车的轨迹搜索论文的整理&#xff0c;包括了论文所拓展的其他一些算法的改进思路。 这是本系列的第一篇文章&#xff0…