Wei Dongshan Linux driver entry experiment class (6) LED driver---device tree

foreword

(1) In Wei Dongshan Linux Driver Introduction Experimental Class (5) LED Driver - Driver Layering and Separation, Platform Bus Model We have explained how to separate the driver program from the hardware program. But the bosses feel that this is not enough. They think that they need to create a special structure to store hardware information instead of using c files to store it. Therefore, the big guys invented the device tree to store hardware information.
(2) Code warehouse: gitee warehouse ; github warehouse ;
(3)Note: You can download the code in my warehouse and read this article to understand it better. The code in my warehouse still has detailed comments, and I feel that this article is too redundant. You can learn from the documentation of my warehouse code and punctual atoms.
(4) I will use BCompare software for file comparison here, which is convenient for everyone to understand.

difference

Difference 1 — platform_driver configuration is different

(1) Similarly, a driver still needs to start from the module_init() macro, and then crawl the Internet, we will find that there is a subtle difference in the platform_driver structure.
(2) In platform_driver.driver, we added a variable of_match_table , led_dtb.This is used to match with the device tree. For students who do not understand the matching rules of drivers and devices, it is recommended to look at the matching rules of Linux drivers and devices .
(3) We need to pass in an array of const struct of_device_id type in the of_match_table variable. The compatible value in the of_device_id structure here should be exactly the same as the compatible name in the device tree. Otherwise, the match cannot be successful.

/*   在dts文件下增加这几行  */
/{
    
    
    test1:test_device_tree{
    
    
           compatible = "test_compatible";
           gpios = <&gpio5 3 GPIO_ACTIVE_HIGH>;
        };
}

insert image description here

Difference 2 — The structure for storing device information is different

(1) If the driver matches the device, the probe function will be triggered. In this function we need to get device information. If it is a device tree, it can be obtained by using the device_node pointer. If it is the device information in the c file, the data can be obtained by using the resource structure pointer.
(2) If the device tree np is matched, it will point to the device tree file. Otherwise np will be a null pointer.

struct device_node *np = pdev->dev.of_node;  //设备树中
struct resource *led_resource;  //c文件中

insert image description here

Difference 3 — the way to get the number of gpio is different

(1) In the device tree, the number of gpio can be obtained by directly calling of_gpio_count(np).
(2) In the C file, there are two ways to obtain device information.

//设备树中统计的设备的 GPIO 数量
count = of_gpio_count(np); 
//C文件中获取GPIO数量的两种方法
count = pdev->num_resources; //这个不会区分GPIO的flag

while (1)  //这个可以获得指定flag的GPIO数量
{
    
    
	/* 下面这9行,是用于统计有多少个GPIO的
	 * dev:一个指向 platform_device 结构的指针,表示要获取资源信息的设备。
	 * type:一个无符号整数,表示要获取的资源类型。在 Linux 内核中,资源类型使用常量来表示,
	        例如 IORESOURCE_MEM 表示内存资源,IORESOURCE_IRQ 表示中断资源等。你可以根据需要选择适当的资源类型。
	 * num:一个无符号整数,表示要获取的资源的索引号。在一个设备中可能存在多个相同类型的资源,通过索引号可以区分它们。
	 * 返回值:返回一个指向 resource 结构的指针,表示获取到的资源信息。
	           resource 结构包含了资源的起始地址、大小等信息。如果没有找到指定的资源,函数将返回 NULL。
	*/
	led_resource = platform_get_resource(pdev, IORESOURCE_IRQ, count);
	if (led_resource)
	{
    
    
		count++;
	}
	else
	{
    
    
		break;
	}
}

insert image description here

Difference 4 — The way to get the pin number is different

(1)
<1> In the device tree, you can directly call of_get_gpio() to get the data of gpios, and then convert it into a pin number.
<2> For example <&gpio5 3 GPIO_ACTIVE_HIGH> in this article, he will call the function desc_to_gpio to convert gpio5 3 to pin number 131.
(2) In the c file, to get the pin information, you need to call the platform_get_resource() function to get the platform_device.resource structure, and then call led_resource->start to get the pin number. But this pin number needs to be calculated by yourself. So it's relatively troublesome.

//设备树
gpios[i].gpio = of_get_gpio(np, i);   //获得gpio信息

//c文件中
led_resource = platform_get_resource(pdev, IORESOURCE_IRQ, i);  //从节点里面取出第i项
if(led_resource==NULL)
{
    
    
	printk("platform_get_resource is flaut\r\n");
}
printk("platform_get_resource is ok\r\n");
gpios[i].gpio = led_resource->start;  //将需要操作的IO号传递给gpios[i].gpio

Difference 5 — The way to get the pin name is different

(1) To be honest, it's almost the same. Both are a structure pointer pointing to the name variable. For the convenience of novices to understand, let's treat it as a difference.

//设备树
sprintf(gpios[i].name, "%s", np->name);  //将设备树中的节点名字传递给gpios[i].name

//c文件
sprintf(gpios[i].name, "%s", pdev->name); //将platform_device.resource.name传递给gpios[i].name
		

OF function introduction

(1) The Linux device tree originated from OpenFirmware, OF. So the function prefixes related to the device tree have OF, which is the OF function.
(2) How our driver wants to obtain the information of the device tree, we need to call the OF function. The OF function needs to find the node before it can get the attributes. So I will divide it into two parts: obtaining nodes and obtaining attributes.

Use the OF function to get nodes

of_find_node_by_name() function

(1) Find nodes by name

/* 作用 : 查找设备树中特定节点的函数
 * 传入参数 : 
     * from : 指向一个起始节点,该函数从这个节点开始向上遍历设备树,查找匹配名称的节点。
     * name : 要查找的节点的名称
 * 返回值 : 如果找到了匹配名称的节点,函数将返回该节点的指针(struct device_node*)。否则返回NULL。
*/
struct device_node *of_find_node_by_name(struct device_node *from,const char *name);

/*------- 示例 --------*/
/* NULL : 表示从根节点开始查找
 * name : 节点的compatible名字为led_test_compatible
*/
of_find_node_by_name(NULL,"led_test_compatible");

of_find_node_by_path() function

(1) This is to find the corresponding node according to the path

/* 作用 : 根据节点的路径查找节点
 * 传入参数 : 
     * path : 一个以斜杠(/)分隔的字符串,表示设备树中从根节点到目标节点的路径
 * 返回值 : 如果找到了匹配名称的节点,函数将返回该节点的指针(struct device_node*)。否则返回NULL。
*/
of_find_node_by_path(const char *path);

/*------- 示例 --------*/
/* "/topeet/myled" : 在根节点下的topeet节点中,找到myled节点
*/
of_find_node_by_path("/topeet/myled");

insert image description here

of_get_parent() function

(1) Get the parent node of the specified node

/* 作用 : 获取指定节点的父节点
 * 传入参数 : 
     * node : 要获得哪个节点的父节点
 * 返回值 : 成功返回查找到的父节点,失败返回NULL
*/
struct device_node *of_get_parent(const struct device_node *node);

/*------- 示例 --------*/
struct device_node * mydevice_node;
/* "/topeet/myled" : 在根节点下的topeet节点中,找到myled节点
*/
mydevice_node = of_find_node_by_path("/topeet/myled");
printk("mydevice node is %s n",mydevice_node->name); //打印出myled
/* mydevice_node : 获得mydevice_node的父节点
*/
mydevice_node = of_get_parent(mydevice_node);
printk("mydevice bab node is %s n",mydevice_node->name); //打印出topeet

of_get_next_child() function

(1) Get the parent node of the specified node

/* 作用 : 获取指定节点的父节点
 * 传入参数 : 
     * node : 父节点
     * prev : 从哪个子节点开始查找下一个子节点,设置为NULL,表示从第一个节点开始
 * 返回值 : 成功返回查找到的节点,失败返回NULL
*/
struct device_node *of_get_next_child(const struct device_node *node,
	struct device_node *prev)

/*------- 示例 --------*/
struct device_node * mydevice_node;
/* "/topeet/myled" : 在根节点下的topeet节点中,找到myled节点
*/
mydevice_node = of_find_node_by_path("/topeet/myled");
printk("mydevice node is %s n",mydevice_node->name); //打印出myled

/* mydevice_node : 获得mydevice_node的父节点
*/
mydevice_node = of_get_parent(mydevice_node);
printk("mydevice bab node is %s n",mydevice_node->name); //打印出topeet

/* mydevice_node : 获得mydevice_node的子节点
 * NULL : 从第一个节点开始
*/
mydevice_node = of_get_next_child(mydevice_node,NULL);
printk("mydevice child node is %s n",mydevice_node->name); //打印出myled

of_find_compatible_node() function

(1) Find the specified node through the device_type and compatible attributes

/* 作用 : 通过device_type和compatible属性来查找指定节点
 * 传入参数 : 
     * from : 从哪个节点开始查找,如果是NULL表示从根节点开始查找
     * type : 要查找的节点的device_type属性的属性值,可以为NULL,代表忽略device_type属性
     * compatible: 要查找的节点对应的compatible属性列表
 * 返回值 : 成功返回查找到的节点,失败返回NULL
*/
of_find_compatible_node(struct device_node *from, const char *type, const char *compat);

/*------- 示例 --------*/
struct device_node * mydevice_node;
/* NULL : 从根节点开始查找
 * NULL : 表示要查找的节点没有device_type属性,或者是忽略查找他的device_type属性
 * "my_devicetree" : 要查找的节点compatible属性叫做my_devicetree
*/
mydevice_node = of_find_compatible_node(NULL,NULL,"my_devicetree");

Use the OF function to get attributes

of_find_property() function

(1) Find the specified attribute under the node

/* 作用 : 查找节点下指定的属性
 * 传入参数 : 
     * np : 查找的节点
     * name : 要查找的属性的属性名
     * lenp : 属性值的字节数
 * 返回值 :  成功返回查找的属性,失败返回NULL
*/
struct property *of_find_property(const struct device_node *np,
				  const char *name,
				  int *lenp)
/*------- 示例 --------*/
struct device_node * mydevice_node;
struct property *my_property;
/* NULL : 表示从根节点开始查找
 * name : 节点的compatible名字为led_test_compatible
*/
mydevice_node = of_find_node_by_name(NULL,"led_test_compatible");
my_property = of_find_property(mydevice_node,"compatible",&size);

of_property_count_elems_of_size function

(1) Get the number of elements in the attribute

/* 作用 : 获取属性中元素的数量
 * 传入参数 : 
     * np : 设备节点
     * propname : 需要获取那个属性的元素数量
     * elem_size : 单个元素尺寸
 * 返回值 :  成功返回元素的数量
*/
int of_property_count_elems_of_size(const struct device_node *np,
				const char *propname, int elem_size)
/*------- 示例 --------*/
struct device_node * mydevice_node;
int num;
/* NULL : 表示从根节点开始查找
 * name : 节点的compatible名字为my_devicetree
*/
mydevice_node = of_find_node_by_name(NULL,"my_devicetree");
/* mydevice_node : 在mydevice_node 设备节点中查找
 * "reg" : 需要获取reg属性的元素数量
 * 4 : 元素是4个字节
*/
num = of_property_count_elems_of_size(mydevice_node,"reg",4);
printk("reg num is %d\n",num); //因为reg中有两个元素,所以打印2

insert image description here

of_property_read_uxx_index() function

(1) There are two types of functions of this type: of_property_read_u32_index() function; of_property_read_u64_index() function;
(2) Here respectively represent the number of bits of data to be read. The example is as follows

/* 作用 : 获取属性中元素的数量
 * 传入参数 : 
     * np : 设备节点
     * propname : 要读取的属性名字
     * index : 读取这个属性下第几个值,index从0开始
     * out_value : 读到的值
 * 返回值 :  成功返回0
*/
int of_property_read_u32_index(const struct device_node *np,
				       const char *propname,
				       u32 index, u32 *out_value)
/*------- 示例 --------*/
struct device_node * mydevice_node;
u32 value_u32;
u64 value_u64;
/* NULL : 表示从根节点开始查找
 * name : 节点的compatible名字为my_devicetree
*/
mydevice_node = of_find_node_by_name(NULL,"my_devicetree");
/* mydevice_node : 在mydevice_node 设备节点中查找
 * "reg" : 需要读取的reg属性
 * 0 : 从第0个元素开始读取
 * value_uxx : 读取到的数据存放在这个变量种
*/
of_property_read_u32_index(mydevice_node,"reg",0,&value_u32);
of_property_read_u64_index(mydevice_node,"reg",0,&value_u64);
printk("value u32 is 0x%x\n",value_u32);  
printk("value u64 is 0x%llx\n",value_u64);

insert image description here
insert image description here

of_property_read_variable_uxx_array()函数

(1) There are four types of functions of this type: of_property_read_variable_u8_array() function; of_property_read_variable_u16_array() function; of_property_read_variable_u32_array() function; of_property_read_variable_u64_array() function
; The example is as follows

/* 作用 : 获取属性中元素的数量
 * 传入参数 : 
     * np : 设备节点
     * propname : 要读取的属性名字
     * out_values : 读取到的数组值,分别为u8、u16、u32、u64
     * sz_min : 数组最小值
     * sz_max : 数组最大值
 * 返回值 :  成功返回0
*/
int of_property_read_variable_u32_array(const struct device_node *np,
			       const char *propname, u32 *out_values,
			       size_t sz_min, size_t sz_max)
/*------- 示例 --------*/
struct device_node * mydevice_node;
u32 out_value[2];
/* NULL : 表示从根节点开始查找
 * name : 节点的compatible名字为my_devicetree
*/
mydevice_node = of_find_node_by_name(NULL,"my_devicetree");
/* mydevice_node : 在mydevice_node 设备节点中查找
 * "reg" : 需要读取的reg属性
 * out_value : 读取到的数组数据存储在这个里面
 * 1 : 读取的数组最少1个元素
 * 2 : 读取的数组最多2个元素
*/
of_property_read_variable_u32_array(mydevice_node, "reg" ,out_value,1,2);
printk("out value[0] is 0x%x\n",out_value[0]); //打印0xfdd6000
printk("out value[1] is 0x%x\n",out_value[1]); //打印0x4

insert image description here

of_property_read_string() function

(1) Read the string in the attribute

/* 作用 : 读取属性中的字符串
 * 传入参数 : 
     * np : 设备节点
     * propname : 要读取的属性名字
     * out_string : 读取到的字符串
 * 返回值 :  成功返回0
*/
int of_property_read_string(const struct device_node *np, const char *propname,
				const char **out_string)
/*------- 示例 --------*/
struct device_node * mydevice_node;
const char *value_compatible;
/* NULL : 表示从根节点开始查找
 * name : 节点的compatible名字为my_devicetree
*/
mydevice_node = of_find_node_by_name(NULL,"my_devicetree");
/* mydevice_node : 在mydevice_node 设备节点中查找
 * "compatible" : 需要读取的compatible属性
 * value_compatible : 读取到compatible属性的字符串
*/
of_property_read_string(mydevice_node,"compatible",&value_compatible);
printk("compatible value is %s\n",value_compatible); //打印my_devicetree

insert image description here

Summarize

The device tree is actually to store the device information from the original c file into a tree structure for storage, and then store it in the dts file. This is a good way to separate the driver file from the device file. Realize the real out of hardware.

Guess you like

Origin blog.csdn.net/qq_63922192/article/details/132027365