平台设备总线相关概念(RK3588)
平台设备总线
平台设备总线相关概念
平台设备总线是一条虚拟的总线
平台设备总线会将驱动程序划分成两部分
设备端
主要的作用就是向内核注册设备的资源信息
向内核注册用来匹配的信息:比如说 led 灯
设备端需要向内核注册的资源的信息主要有:
gpio 口引脚的编号
有效电平
数据和控制寄存器的地址和值
驱动端
首先向内核注册一个用来匹配设备端的信息
当设备端和驱动端匹配成功之后
驱动端就需要取获取设备端注册的资源
拿获取的资源来实现驱动程序
平台设备总线的意义
平台设备总线将一个完整的驱动程序拆分成了两部分,这样写驱动程序的好处是能够提高代码复用率,在有设备树的时候可以不写代码就可以实现驱动程序
平台设备总线的分层
平台设备总线将驱动拆分成了两部分
设备端
向内核注册资源的信息
向内核注册匹配的信息
驱动端
向内核注册匹配的信息
获取设备端向内核注册的资源信息
拿着获取到的资源的信息实现驱动程序
平台设备总线代码的实现
设备树+平台设备总线的驱动端
设备端+平台设备总线的驱动端
平台设备总线设备层的实现方法
led 灯需要的资源主要有:gpio 口引脚的编号,有效电平
也是就是说在设备端需要向内核注册的资源信息主要就是这两项
除了资源信息以外,还需要向内核注册一个用来匹配驱动端的一个名字,总体来说设备端要向内核注册的东西
匹配的名字
gpio 口引脚的编号
有效电平
platform_device_register向内核注册平台设备总线的设备的信息
函数的头文件
linux/platform_device.h
函数的原型
int platform_device_register(struct platform_device *pdev)
函数的参数
struct platform_device *pdev: 平台设备总线设备端的核心结构体
函数的返回值
成功返回 0
失败返回 -1
核心结构体
struct platform_device {
const char *name; 名字 用来跟平台设备总线的驱动端做匹配
int id; -1
struct device dev; 设备相关的结构体
这个结构体里的 release 函数必须要实现哪怕是一个空的函数体都可以
u32 num_resources; 资源的数目 多少类资源
struct resource *resource; 用来存放要向内核注册的资源的结构体
};
struct resource {
resource_size_t start; 资源的起始位置
resource_size_t end; 资源的结束的位置
const char *name; 资源的名字
unsigned long flags; 资源的类型 内存资源
};
platform_device_unregiste取消平台设备总线设备端的注册
函数的头文件
linux/platform_device.h
函数的原型
void platform_device_unregister(struct platform_device *pdev)
函数的参数
struct platform_device *pdev: 平台设备总线设备端的核心结构体
函数的返回值
无
实例
#include <linux/kernel.h>// 包含内核相关头文件
#include <linux/module.h>// 包含模块相关头文件
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/miscdevice.h>
#include <linux/platform_device.h>struct platform_device mypdev;
struct resource myres[2];int myopen (struct inode *inode, struct file *file)
{return 0;
}int myclose (struct inode *inode, struct file *file)
{return 0;
}
void myrelease (struct device *dev)
{}static int __init myled_init(void)
{myres[0].start = 0;//无效电平myres[0].end = 1;//有效电平myres[0].name = "led1";myres[0].flags = IORESOURCE_MEM;//内存资源myres[1].start = 21;//引脚起始位置myres[1].end = 22;//引脚结束位置myres[1].name = "led2";myres[1].flags = IORESOURCE_MEM;//内存资源mypdev.name = "lqhled";//与驱动端名字相匹配mypdev.id = -1;mypdev.dev.release = myrelease;mypdev.num_resources = 2;//两类资源mypdev.resource = myres;//1向内核注册平台设备总线的设备的信息platform_device_register(&mypdev);return 0;
}static void __exit myled_exit(void)
{platform_device_unregister(&mypdev);//取消注册
}module_init(myled_init);
module_exit(myled_exit);
MODULE_LICENSE("GPL");
平台设备总线驱动层的实现方法
平台设备总线驱动端,最终想要的是获取平台设备总线设备端注册的资源,并使用获取到的资源的信息来实现驱动的框架。
平台设备总线下(内核里)有很多的设备端,驱动端要想拿到准确的那个设备端注册的资源信息,就需要找到合适那个设备端,需要有一个能够相互匹配的东西。平台设备总线驱动端在获取资源之前要先向内核注册用来匹配的信息。
platform_driver_register平台设备总线的驱动端向内核注册用来匹配的信息
函数的头文件
linux/platform_device.h
函数的原型
int platform_driver_register(struct platform_driver *drv)
函数的参数
struct platform_driver *drv: 平台设备总线驱动端的核心结构体
函数的返回值
成功返回 0
失败返回 -1
结构体
struct platform_driver {
int (*probe)(struct platform_device *); 设备端和驱动端匹配成功以后调用的函数
int (*remove)(struct platform_device *); 设备端和驱动端有一端被卸载会调用的函数
void (*shutdown)(struct platform_device *);
int (*suspend)(struct platform_device *, pm_message_t state);
int (*resume)(struct platform_device *);
struct device_driver driver; 这个结构体里的 name就是用来匹配设备端
const struct platform_device_id *id_table; 另外的一种匹配的方式
};
platform_driver_unregister取消平台设备总线驱动端的注册
函数的头文件
linux/platform_device.h
函数的原型
void platform_driver_unregister(struct platform_driver *drv)
函数的参数
struct platform_driver *drv: 平台设备总线驱动端的核心结构体
函数的返回值
无
platform_get_resource获取平台设备总线设备端注册的资源
函数的头文件
linux/platform_device.h
函数的原型
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 资源的数组的下标
函数的返回值
成功返回 资源的结构体指针
失败返回 NULL
实例
#include <linux/kernel.h>// 包含内核相关头文件
#include <linux/module.h>// 包含模块相关头文件
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/miscdevice.h>
#include <linux/platform_device.h>struct platform_driver mydrv;int myopen (struct inode *inode, struct file *file)
{return 0;
}int myclose (struct inode *inode, struct file *file)
{return 0;
}int myprobe (struct platform_device *dev)
{printk("匹配成功\n");return 0;
}int myremove(struct platform_device *dev)
{printk("有一端被删除了\n");return 0;}static int __init myled_init(void)
{mydrv.driver.name = "lqhled";mydrv.probe = myprobe;mydrv.remove = myremove;platform_driver_register(&mydrv);//平台设备总线的驱动端向内核注册用来匹配的信息return 0;
}static void __exit myled_exit(void)
{platform_driver_unregister(&mydrv);}module_init(myled_init);
module_exit(myled_exit);
MODULE_LICENSE("GPL");
平台设备总线和设备树的使用
平台设备总线配合设备树使用的时候
设备树相当于平台设备总线的设备层
设备树用来跟平台设备总线的驱动层匹配的是 compatibe 属性
实例
#include <linux/kernel.h>// 包含内核相关头文件
#include <linux/module.h>// 包含模块相关头文件
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/miscdevice.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/device.h>struct file_operations myfops;struct miscdevice mymsic;
int gpio_num,level;
enum of_gpio_flags myflags;struct platform_driver mydrv;struct of_device_id myid={.compatible="LQHXX",};
int myopen (struct inode *inode, struct file *file)
{gpio_set_value(gpio_num, level);return 0;
}int myclose (struct inode *inode, struct file *file)
{gpio_set_value(gpio_num, !level);return 0;
}int myprobe (struct platform_device *dev)
{printk("设备树匹配成功\n");gpio_num= of_get_named_gpio(dev->dev.of_node, "LQH-gpios", 2);of_get_named_gpio_flags(dev->dev.of_node, "LQH-gpios", 2, &myflags);if(myflags==OF_GPIO_ACTIVE_LOW){level=0;}else{level=1;}gpio_request(gpio_num,"led");gpio_direction_output(gpio_num,!level);myfops.owner = THIS_MODULE;myfops.open = myopen;myfops.release = myclose;mymsic.minor = 255;mymsic.name = "myled";mymsic.fops = &myfops;misc_register(&mymsic);return 0;
}int myremove(struct platform_device *dev)
{printk("有一端被删除了\n");return 0;}
static int __init myled_init(void)
{mydrv.driver.name ="test";mydrv.driver.of_match_table =&myid ;mydrv.probe = myprobe;mydrv.remove = myremove;platform_driver_register(&mydrv);return 0;
}static void __exit myled_exit(void)
{platform_driver_unregister(&mydrv);
}module_init(myled_init);
module_exit(myled_exit);
MODULE_LICENSE("GPL");