[Linux_IMX6ULL驱动开发]-总线设备驱动模型

目录

框架分层

总线驱动模型实现

上层驱动代码(leddrv.c)的实现以及解析

交叉依赖的避免

下层驱动的设备文件(board_A_led.c)的实现

下层驱动的驱动文件(chip_demo_gpio.c)的实现


框架分层

在之前,我们对于驱动的框架有过两种不同的框架。第一种框架,驱动文件只有应用层和驱动层的两个文件,在驱动层的文件中,实现对应用层的系统调用的实现、设备的初始化、控制、引脚初始化等,这样的驱动框架虽然很简单,但是如果更换了开发板或者引脚发生了变更,就需要深入到底层进行寄存器地址的修改等,十分的繁琐

第二种框架,应用层是不变的,但是对于底层驱动,分为了两层,分为上层驱动和下层驱动。上层驱动主要实现file_operation结构体以及内部的成员函数指针、calss、device的创建和销毁等,下层驱动则实现物理地址到虚拟地址的映射,并且构造一个结构体,封装初始化函数和控制函数,提供给上层的驱动。这样做似乎可以做到很好的移植性,但是底层驱动和上层驱动要编译为一个ko文件,如果更换引脚等信息,那么我们还得对驱动重新编译。

在这里我们介绍第三种驱动框架,总线驱动框架,依赖与第二种模型,我们把下层驱动再次进行分层,分为资源文件和芯片操控文件,资源文件主要用来定义不同板子的外设资源、引脚等等,而芯片操控文件主要用来实现外设的初始化等等。

 


总线驱动模型实现

设备和驱动分别有两个对应的结构体,设备对应结构体为struct platform_device,驱动对应的结构体为struct platform_driver

设备和驱动的匹配规则如下,只有设备和驱动成功匹配了,那么该驱动才可以获取到设备资源文件的各种引脚信息 ,在配对成功后,在进行一些操作,在资源文件(设备文件)中定义组、引脚等,在驱动文件通过各种引脚实现物理地址映射、初始化、操控等。


上层驱动代码(leddrv.c)的实现以及解析

上层驱动代码的主要任务就是完成file_operation结构体,然后填充好里面的函数指针成员、class类的创建。需要注意的是,device设备的创建和销毁现在不在上层驱动进行,而是在下层驱动中的驱动文件中进行。

如下,由于创建设备需要class结构体,所以我们在上层驱动中封装该函数,然后通过如下 EXPORT_SYMBOL(),修饰函数,让该函数可以在其他文件中被使用,这样,下层驱动中的驱动文件就可以创建设备了。

交叉依赖的避免

在上层驱动中,我们需要获取下层驱动的驱动文件的操作控制函数,但是现在存在如下情况,下层驱动的驱动文件chip_demo_gpio.c需要使用上层驱动传入的device创建函数,上层驱动需要下层驱动的驱动文件chip_demo_gpio.c提供的具体操控的结构体,这就导致了交叉依赖,所以我们在这里需要让下层驱动来初始化上层驱动,也就是具体的操控结构体交给下层来初始化

#include <linux/module.h>

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>

#include "led_opr.h"

#define LED_NUM 2

/* 1. 确定主设备号                                                                 */
static int major = 0;
static struct class *led_class;
struct led_operations *p_led_opr;


#define MIN(a, b) (a < b ? a : b)

/* 3. 实现对应的open/read/write等函数,填入file_operations结构体                   */
static ssize_t led_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}

/* write(fd, &val, 1); */
static ssize_t led_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
	int err;
	char status;
	struct inode *inode = file_inode(file);
	int minor = iminor(inode);
	
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	err = copy_from_user(&status, buf, 1);

	/* 根据次设备号和status控制LED */
	p_led_opr->ctl(minor, status);
	
	return 1;
}

static int led_drv_open (struct inode *node, struct file *file)
{
	int minor = iminor(node);
	
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	/* 根据次设备号初始化LED */
	p_led_opr->init(minor);
	
	return 0;
}

static int led_drv_close (struct inode *node, struct file *file)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}

/* 2. 定义自己的file_operations结构体                                              */
static struct file_operations led_drv = {
	.owner	 = THIS_MODULE,
	.open    = led_drv_open,
	.read    = led_drv_read,
	.write   = led_drv_write,
	.release = led_drv_close,
};

/* 4. 把file_operations结构体告诉内核:注册驱动程序                                */
/* 5. 谁来注册驱动程序啊?得有一个入口函数:安装驱动程序时,就会去调用这个入口函数 */
static int __init led_init(void)
{
	int err;
	
	
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	major = register_chrdev(0, "100ask_led", &led_drv);  /* /dev/led */


	led_class = class_create(THIS_MODULE, "100ask_led_class");
	err = PTR_ERR(led_class);
	if (IS_ERR(led_class)) {
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
		unregister_chrdev(major, "led");
		return -1;
	}

	/* 会造成交叉依赖 */
	//p_led_opr = get_board_led_opr();
	
	return 0;
}




void led_device_create(int minor)
{
	device_create(led_class, NULL, MKDEV(major, minor), NULL, "100ask_led%d", minor);
}
void led_device_destroy(int minor)
{
	device_destroy(led_class,  MKDEV(major, minor));
}
void led_operation_register(struct led_operations *opr)
{
	/* 传递交给下层驱动,下层驱动来初始化上层驱动
		这样就上层驱动就不用需要先加载下层驱动*/
	p_led_opr = opr;
}
EXPORT_SYMBOL(led_device_create);
EXPORT_SYMBOL(led_device_destroy);
EXPORT_SYMBOL(led_operation_register);



/* 6. 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数           */
static void __exit led_exit(void)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);


	class_destroy(led_class);
	unregister_chrdev(major, "100ask_led");
}


/* 7. 其他完善:提供设备信息,自动创建设备节点                                     */

module_init(led_init);
module_exit(led_exit);

MODULE_LICENSE("GPL");



下层驱动的设备文件(board_A_led.c)的实现

board_A_led.c的主要思路如下:

 一、创建一个struct platform_device结构体,由于是三个ko文件,所以出口函数,入口函数以及对应的创建、卸载不可缺少

二、对于platform_device的各个成员进行初始化赋值 ,包括对应驱动文件的名字、资源为什么以及资源的个数

        name代表和驱动文件匹配的名称信息

        resource表示资源信息

        num_resources表示资源信息的个数

#include <linux/module.h>

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>

#include "led_resource.h"
#include <linux/ioport.h> 
#include<linux/platform_device.h>


static struct resource board_A_led_resource[] = {
	/* 设备1 */
	{
		.start = GROUP_PIN(3,1),
		.flags = IORESOURCE_IRQ,
	},
		
	/* 设备2 */
	{
		.start = GROUP_PIN(5,8),
		.flags = IORESOURCE_IRQ,
	},

};

static struct platform_device board_A_led_devices = {
	.name = "100ask_led",
	.resource = board_A_led_resource,
	.num_resources = ARRAY_SIZE(board_A_led_resource),

};
/* 创建了platform_device,因为装载了三个ko文件,所以
	单独的都需要出口函数和入口函数,资源文件中
	需要把platform_device注册*/

static __init int board_A_led_init(void)
{
	int err;
	/* device注册 */
	err = platform_device_register(&board_A_led_devices);
	return 1;
}

static __exit void board_A_led_exit(void)
{
	/* device卸载 */
	platform_device_unregister(&board_A_led_devices);
}


module_init(board_A_led_init);
module_exit(board_A_led_exit);

MODULE_LICENSE("GPL");


#ifndef _LED_RESOURCE_H
#define _LED_RESOURCE_H

/* GPIO3_0 */
/* bit[31:16] = group */
/* bit[15:0]  = which pin */
#define GROUP(x) (x>>16)
#define PIN(x)   (x&0xFFFF)
#define GROUP_PIN(g,p) ((g<<16) | (p))

struct led_resource {
	int pin;
};

struct led_resource *get_led_resouce(void);

#endif


下层驱动的驱动文件(chip_demo_gpio.c)的实现

chip_demo_gpio.c的主要思路如下:

该文件的主要思路就是首先对 platform_driver 结构体的定义,以及在出口入口函数对该结构体的注册以及销毁。                                                                                                               同时进行匹配,如果驱动和某个设备文件能够成功匹配上,则执行 platform_driver 结构体中的 probe函数进行引脚信息的获取。获取到引脚信息后,完成对应物理地址到虚拟地址的映射以及具体初始化、操控函数的封装。传递到具体封装这些函数的结构体中 ,传递给上层驱动函数

具体的实现步骤如下:

一、platform_driver结构体的定义,以及注册、初始化,同时需要实现驱动文件的出口入口,对其进行修饰

二、完成结构体中的probe函数以及remove函数,同时填入需要匹配的设备名称到 .driver.name当中

                                                                                                                                            三、通过probe函数获取的引脚,进行对应的寄存器操作,包括地址映射、对应外设初始化以及操控等等

 (该代码并未实现步骤三,也就是物理地址映射到虚拟地址以及具体控制、初始化并未实现)

#include <linux/module.h>

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>


#include<linux/platform_device.h>

#include "led_opr.h"
#include "led_resource.h"

static struct led_resource *led_rsc;

static int led_pins[100] = {0};
static int led_count = 0;

static int board_demo_led_init (int which) /* 初始化LED, which-哪个LED */	   
{	
	//printk("%s %s line %d, led %d\n", __FILE__, __FUNCTION__, __LINE__, which);
	if (!led_rsc)
	{
		led_rsc = get_led_resouce();
	}
	
	printk("init gpio: group %d, pin %d\n", GROUP(led_pins[which]), PIN(led_pins[which]));
	switch(GROUP(led_pins[which]))
	{
		case 0:
		{
			printk("init pin of group 0 ...\n");
			break;
		}
		case 1:
		{
			printk("init pin of group 1 ...\n");
			break;
		}
		case 2:
		{
			printk("init pin of group 2 ...\n");
			break;
		}
		case 3:
		{
			printk("init pin of group 3 ...\n");
			break;
		}
	}
	
	return 0;
}

static int board_demo_led_ctl (int which, char status) /* 控制LED, which-哪个LED, status:1-亮,0-灭 */
{
	//printk("%s %s line %d, led %d, %s\n", __FILE__, __FUNCTION__, __LINE__, which, status ? "on" : "off");
	printk("set led %s: group %d, pin %d\n", status ? "on" : "off", GROUP(led_pins[which]), PIN(led_pins[which]));

	switch(GROUP(led_pins[which]))
	{
		case 0:
		{
			printk("set pin of group 0 ...\n");
			break;
		}
		case 1:
		{
			printk("set pin of group 1 ...\n");
			break;
		}
		case 2:
		{
			printk("set pin of group 2 ...\n");
			break;
		}
		case 3:
		{
			printk("set pin of group 3 ...\n");
			break;
		}
	}

	return 0;
}

static struct led_operations board_demo_led_opr = {
	.init = board_demo_led_init,
	.ctl  = board_demo_led_ctl,
};


static int chip_demo_gpio_led_probe(struct platform_device* dev)
{

	int i = 0;
	struct resource *res;

	/* 发现名字对上后,记录引脚 */
	while(1)
	{
		res = platform_get_resource(dev, IORESOURCE_IRQ, i);
		if( NULL == res )
			break;
		else
		{
			/* 记录引脚 */
			led_pins[led_count] = res->start;
			/* device create */
			/* 由于device_create需要class结构体,通过上层驱动封装函数,此处调用 */
			led_device_create(led_count);
			led_count++;
		}
	}

	return 0;
}
static int chip_demo_gpio_led_remove(struct platform_device* dev)
{
	int i;
	/* device destory */
	for( i = 0; i < led_count; i++ )
	{
		led_device_destroy(i);
	}
	led_count = 0;
	return 0;
}


/* 由于我们使用了platform_device,我们在这里实现platform_driver */
static struct platform_driver chip_demo_gpio_driver = {
	.probe = chip_demo_gpio_led_probe,
	.remove = chip_demo_gpio_led_remove,
	.driver = {
		.name = "100ask_led",
	},
};


/* 驱动的入口出口 */
static __init int chip_demo_gpio_init(void)
{
	int err;
	/* 下层驱动初始化上层驱动的操作结构体 */
	led_operation_register(&board_demo_led_opr);
	/* driver注册 */
	err = platform_driver_register(&chip_demo_gpio_driver);
	return 1;
}

static __exit void chip_demo_gpio_exit(void)
{
	/* driver卸载 */
	platform_driver_unregister(&chip_demo_gpio_driver);
}


module_init(chip_demo_gpio_init);
module_exit(chip_demo_gpio_exit);

MODULE_LICENSE("GPL");




#ifndef _LED_OPR_H
#define _LED_OPR_H

struct led_operations {
	int (*init) (int which); /* 初始化LED, which-哪个LED */       
	int (*ctl) (int which, char status); /* 控制LED, which-哪个LED, status:1-亮,0-灭 */
};

struct led_operations *get_board_led_opr(void);


/* 上层驱动传入的操作函数 */
void led_device_create(int minor);
void led_device_destroy(int minor);
void led_operation_register(struct led_operations *opr);


#endif

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

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

相关文章

如何爬出 Kotlin 协程死锁的坑?

作者&#xff1a;悬衡 一、前言 在 Java 中有一个非常经典的死锁问题, 就是明明自己已经占用了线程池, 却还继续去申请它, 自己等自己, 就死锁了, 如下图和代码: // 这段代码将死锁到天荒地老final ExecutorService executorService Executors.newSingleThreadExecutor();exe…

科学突破可能开创6G通信新时代

格拉斯哥大学开发的火柴盒大小的天线可以为全息通话、改进自动驾驶和更好的医疗保健的世界铺平道路。 格拉斯哥大学表示&#xff0c;这种创新的无线通信天线将超材料的独特特性与复杂的信号处理相结合&#xff0c;有助于构建未来的 6G 网络。 数字编码动态超表面天线&#xf…

前端请求发送成功,后端收到null

1、dishId为64&#xff0c;有数据 2、但是后端调试接不到数据&#xff0c;为null 3、形参部分缺少RequestBody接收JSON数据&#xff0c;加上即可

Kimichat炒股:7个提示词案例

●了解股票投资基本概念和知识 什么是有息负债率&#xff1f;用浅显明白的话语针对没有财务会计基础的小白进行解释 Kimi的回答&#xff1a; 有息负债率是一个财务指标&#xff0c;用来衡量一家公司在其负债中有多少是需要支付利息的。简单来说&#xff0c;就是公司借的钱中&…

获取公募基金持仓【数据分析系列博文】

摘要 从指定网址获取公募基金持仓数据&#xff0c;快速解析并存储数据。 &#xff08;该博文针对自由学习者获取数据&#xff1b;而在投顾、基金、证券等公司&#xff0c;通常有Wind、聚源、通联等厂商采购的数据&#xff09; 1. 导入必要的库&#xff1a; pandas 用于数据处理…

【第1节】书生·浦语大模型全链路开源开放体系

目录 1 简介2 内容&#xff08;1&#xff09;书生浦语大模型发展历程&#xff08;2&#xff09;体系&#xff08;3&#xff09;亮点&#xff08;4&#xff09;全链路体系构建a.数据b 预训练c 微调d 评测e.模型部署f.agent 智能体 3 相关论文解读4 ref 1 简介 书生浦语 InternLM…

深度Q-Learning在算法交易中的应用

一、说明 在《华尔街的随机漫步》一书中&#xff0c;作者伯顿马尔基尔&#xff08;Burton G. Malkiel&#xff09;声称&#xff1a;“一只蒙着眼睛的猴子向报纸的财经版面投掷飞镖&#xff0c;可以选择一个与专家精心挑选的投资组合一样好的投资组合。 如果我们让巴甫洛夫的狗接…

RabbitMQ-交换机

文章目录 交换机fanoutDirecttopicHeadersRPC 交换机 **交换机 **是消息队列中的一个组件&#xff0c;其作用类似于网络路由器。它负责将我们发送的消息转发到相应的目标&#xff0c;就像快递站将快递发送到对应的站点&#xff0c;或者网络路由器将网络请求转发到相应的服务器…

使用prompt_toolkit构建交互式命令行工具

prompt_toolkit是一个python库&#xff0c;用于构建命令行工具和终端应用。其官网介绍如下&#xff0c; prompt_toolkit is a library for building powerful interactive command line and terminal applications in Python. 安装命令如下&#xff0c; pip install prompt_to…

CCIA信息系统业务安全服务资质证书介绍条件要求

CCIA&#xff08;中国通信工业协会&#xff09;证书是由中国通信工业协会颁发的一种专业资质证书&#xff0c;旨在评估和认证信息化建设企业在信息化项目建设中提供的服务能力。该证书不涉及技术和产品标准&#xff0c;而是重点强调企业在行业服务方向、安全服务意识和专业服务…

基于Springboot的心灵治愈交流平台

基于SpringbootVue的心灵治愈交流平台的设计与实现 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringbootMybatis工具&#xff1a;IDEA、Maven、Navicat 系统展示 用户登录 首页展示 系统公告 心理咨询师 心灵专栏 压力测试 小纸条 后台登录界面 后…

CST电磁仿真物体表面的Sheet结构和生成3D Model【基础教程】

由Sheet结构生成3D Model 使用Shell Solid and Thicken Sheet&#xff01; Modeling > Tools > Shape Tools > Shell Solid or Thicken Sheet Shell Solidor ThickenSheet会根据不同类型的模型提供两种完全不同的功能。 如033.由3D Model生成Cavity 所述&#xff…

量子密钥分发系统设计与实现(一):系统基本架构讨论

经过一段时间讨论&#xff0c;我们了解到量子密钥分发设备是当前量子保密通信系统的基础。从本文开始&#xff0c;我将开启量子密钥分发系统设计与实现系列&#xff0c;详细讨论量子密钥分发设备如何从0到1的搭建。 1.QKD系统总体讨论 QKD系统的核心功能就是为通信双方提供理论…

后端插入数据库问题

IDEA报错&#xff1a;Error updating database. Cause: java.sql.SQLException: Column count doesn’t match value count at row 1 1、看报错消息&#xff0c;SQLException&#xff0c;定位到SQL语句问题 并且看best guess最好猜测&#xff0c;再去找路径下的ShoppingCartMa…

第十二章 屏幕后处理效果

屏幕后处理效果是实现屏幕特效的常见方法。 建立一个基本的屏幕后处理的脚本 屏幕后处理指的是在渲染完整个场景得到屏幕图像后,再对这个图像进行一系列操作,实现各种屏幕特效。 想要实现屏幕后处理的基础在于抓取屏幕。Unity为我们提供了一个接口-OnRenderImage函数。 声…

【C语言回顾】函数

前言1. 函数的概念和分类2.库函数3. 自定义函数3.1 自定义函数的简单介绍3.2 自定义函数举例 4. 形参和实参4.1 形参4.2 实参4.3 形参和实参的关系4.3.1 理解4.3.2 举例代码和调试 5. 嵌套函数和链式访问5.1 嵌套函数5.2 链式访问 6. 函数的声明和定义6.1 单个文件6.2 多个文件…

Java PDF文件流传输过程中速度很慢,如何解决?

专栏集锦&#xff0c;大佬们可以收藏以备不时之需&#xff1a; Spring Cloud 专栏&#xff1a;http://t.csdnimg.cn/WDmJ9 Python 专栏&#xff1a;http://t.csdnimg.cn/hMwPR Redis 专栏&#xff1a;http://t.csdnimg.cn/Qq0Xc TensorFlow 专栏&#xff1a;http://t.csdni…

Https网站接口被黑被恶意调取

背景&#xff1a; 维护的一个网站最近短信接口被黑&#xff0c;发送大量短信。起初以为是在网站内部操作&#xff0c;优化了发送短信前的操作&#xff0c;如添加图形验证码&#xff0c;屏蔽国外IP等。但后续还存在被调取情况&#xff0c;定位排查到是该接口在外部被恶意调取。 …

牛客Linux高并发服务器开发学习第二天

Gcc编译 利用gcc 生成应用时如果不加-o 和应用名&#xff0c;默认生成a.out 可以用./ a.out打开 Gcc工作流程 可执行程序Windows系统中为.exe Linux系统中为.out g也可以编辑c程序 gcc也可以编译cpp代码&#xff0c;只是在编译阶段gcc不能自动共和C程序使用的库进行联接&…

论文笔记:Are Human-generated Demonstrations Necessary for In-context Learning?

iclr 2024 reviewer 评分 6668 1 intro 大型语言模型&#xff08;LLMs&#xff09;已显示出在上下文中学习的能力 给定几个带注释的示例作为演示&#xff0c;LLMs 能够为新的测试输入生成输出然而&#xff0c;现行的上下文学习&#xff08;ICL&#xff09;范式仍存在以下明显…
最新文章