目录
最后创建自己想要的设备,已经获取到了寄存器的值,可以回看前面章节
在以前的platfrom总线是通过两个文件,driver和device两个文件进行匹配成功送到probe函数的
现在有了设备树,通过设备树来替换device文件部分,那如何进行匹配呢,是通过设备树节点中的compatible属性来匹配相对应的name,但根节点中的compatible属性并不是,这个属性的作用是用来匹配内核与这个设备树匹不匹配
这章用到的节点也是16章中创建的自定义test节点
匹配节点name
旧driver.c
- 找到我前面写的driver.c文件
#include <linux/init.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/uaccess.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/platform_device.h>
int beep_probe(struct platform_device *pdev)
{
printk("beep_probe\n");
return 0;
}
int beep_remove(struct platform_device *pedv)
{
printk("beep_remove\n");
return 0;
};
const struct platform_device_id beep_id_table =
{
.name = "123"
};
struct platform_driver beep_device =
{
.probe = beep_probe,
.remove = beep_remove,
.driver = {
.owner = THIS_MODULE,
.name = "beep_test"
},
.id_table = &beep_id_table
};
static int beep_driver_init(void)
{
int ret = 0;
ret = platform_driver_register(&beep_device);
if (ret<0)
{
printk("platform_driver_register error\n");
return ret;
}
printk("platform_driver_register ok\n");
return 0;
}
static void beep_driver_exit(void)
{
printk("beep goodbye");
platform_driver_unregister(&beep_device);
}
module_init(beep_driver_init);
module_exit(beep_driver_exit);
MODULE_LICENSE("GPL");
在上面代码中,有两个name,但优先会匹配platform_device_id 结构体中的name
新driver.c (设备树)
- 下面我们更改为匹配设备树的代码
- 添加一个of_device_id 结构体
- 在其中添加compatible属性,这个属性等于要匹配的name,优先级最高,然后到platform_device_id 结构体中的name,最后才到platform_driver 结构体中的name
- 在platform_driver 结构体中赋值of_device_id 结构体
#include <linux/init.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/uaccess.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/platform_device.h>
int beep_probe(struct platform_device *pdev)
{
printk("beep_probe\n");
return 0;
}
int beep_remove(struct platform_device *pedv)
{
printk("beep_remove\n");
return 0;
};
const struct platform_device_id beep_id_table =
{
.name = "123"
};
const struct of_device_id *of_match_table_test[] =
{
{.compatible = "test1234"},
{}
};
struct platform_driver beep_device =
{
.probe = beep_probe,
.remove = beep_remove,
.driver = {
.owner = THIS_MODULE,
.name = "beep_test"
.of_match_table= of_match_table_test;
},
.id_table = &beep_id_table
};
static int beep_driver_init(void)
{
int ret = 0;
ret = platform_driver_register(&beep_device);
if (ret<0)
{
printk("platform_driver_register error\n");
return ret;
}
printk("platform_driver_register ok\n");
return 0;
}
static void beep_driver_exit(void)
{
printk("beep goodbye");
platform_driver_unregister(&beep_device);
}
module_init(beep_driver_init);
module_exit(beep_driver_exit);
MODULE_LICENSE("GPL");
验证方式
- 保证已经在设备树中添加了节点,节点的compatible属性就是对应匹配的name,需要与上面代码中的一样
- 编译成模块
- 添加模块
- 当匹配到设备树中的节点,并name中相同,就会输出beep_probe
取资源
旧方法
以前用driver.c时有两种方法,可以往回看,一个是直接通过结构体取,一个是通过函数
新方法
设备树也有两种方法
- 一个是直接来拿
- 一个是通过函数
第一个方法
比如在上面新代码中的beep_probe函数中的参数,设备树一识别到节点,节点就会转为函数中的参数
#include <linux/of.h>
int beep_probe(struct platform_device *pdev)
{
printk("beep_probe\n");
//第一种方法
printk("node name is %s\n",pdev->dev.of_node->name);
return 0;
}
加载模块时就会直接打印出节点的名字
第二个方法
#include <linux/of.h>
int size;
u32 out_value[2]={0};
const char* str;
struct device_node *test_device_node;
struct property *test_node_property;
int beep_probe(struct platform_device *pdev)
{
printk("beep_probe\n");
//第二种方法
//获取节点的名称
test_device_node = of_find_node_by_path("/test");
if (test_device_node==NULL)
{
printk("of_find_node_by_path error \n");
return -1;
}
printk("test_device_node is %s\n",test_device_node->name);
//读取节点的 reg 属性
int ret;
ret = of_property_read_u32_array(test_device_node,"reg",out_value,2);
if (ret < 0)
{
printk("of_property_read_u32_array error \n");
return -1;
}
printk("out_value[0] is 0x%08x\n",out_value[0]);
printk("out_value[1] is 0x%08x\n",out_value[1]);
return 0;
}
加载模块就会打印出名字和reg属性
注意
- 其实第二种方法中获取节点的名称test_device_node 等于 第一种方法中的pdev->dev.of_node
- 在获取reg属性的时候,可以用pdev->dev.of_node替换test_device_node
设备树of映射地址函数
- of_iomap
- 作用:用于直接内存映射,以前是通过ioremap函数来完成的
- 原型:
void iomem *of_iomap(struct device_node *np,int index)
- 参数:
- np:设备节点,也就是上面的test_device_node
- index:reg属性中要完成内存映射的段,也就是reg属性中的索引
- 如果reg属性中只有一段的话,index设置为0(表示第一段)
- 如果有多段,你想映射第x段,index 就是 x-1
- 返回值:
- 经过内存映射后的虚拟内存首地址,如果为 NULL 的话表示内存映射失败
例子
修改上面已经获取到reg的代码,将reg中的代码进行映射,reg中的指为<0x000000 0x000004>,前面表示地址,后面表示长度
#include <linux/of.h>
#include <linux/of_address.h>
int size;
u32 out_value[2]={0};
const char* str;
struct device_node *test_device_node;
struct property *test_node_property;
unsigned int *vir_addr;
int beep_probe(struct platform_device *pdev)
{
printk("beep_probe\n");
//第二种方法
//获取节点的名称
test_device_node = of_find_node_by_path("/test");
if (test_device_node==NULL)
{
printk("of_find_node_by_path error \n");
return -1;
}
printk("test_device_node is %s\n",test_device_node->name);
//读取节点的 reg 属性
int ret;
ret = of_property_read_u32_array(test_device_node,"reg",out_value,2);
if (ret < 0)
{
printk("of_property_read_u32_array error \n");
return -1;
}
printk("out_value[0] is 0x%08x\n",out_value[0]);
printk("out_value[1] is 0x%08x\n",out_value[1]);
vir_addr = of_iomap(test_device_node,0);
if (vir_addr == NULL)
{
printk("of_iomap error \n");
return -1;
}
return 0;
}