完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>
|
|
相关推荐
1个回答
|
|
设备树是为驱动服务的,驱动如何匹根据设备树的描述信息匹配驱动源码,及C语言如何获取设备树的描述属性,是驱动工程师关心的。编写驱动时,通常情况下都需要获取驱动对应的设备树描述属性,如GPIO属性、内存地址范围、中断地址等。linux提供了驱动和设备树交互的API接口,一般以“of_”开头,接口声明位于“kernel/include/linux”的头文件下。 1. 设备节点 设备树中的设备是以“节点”形式存在, 因此要获取设备节点的属性信息,必须先获取到这个设备的节点(device_node) ,获取节点属性的函数接口第一个传入的参数就是“节点”地址。关于“device_node”的声明,位于“kernel/include/linux/of.h”中。 struct device_node { const char *name; /* 节点名称 */ const char *type; /* 设备类型 */ phandle phandle; const char *full_name;/* 节点完整名称,包括路径 */ struct property *properties; /* 匹配属性 */ struct property *deadprops; /* removed properties */ struct device_node *parent; /* 父节点 */ struct device_node *child; /* 子节点 */ struct device_node *sibling; struct device_node *next; /* 链表节点 */ struct device_node *allnext; struct proc_dir_entry *pde; struct kref kref; unsigned long _flags; void *data; #if defined(CONFIG_SPARC) const char *path_component_name; unsigned int unique_id; struct of_irq_controller *irq_trans; #endif }; 2. 常用函数接口 2.1 驱动匹配 2.1.1 匹配属性兼容 of_match_ptr(int_demo_dt_ids) #ifdef CONFIG_OF #define of_match_ptr(_ptr) (_ptr) #else #define of_match_ptr(_ptr) (null) #endif #ifdef CONFIG_ACPI #define ACPI_PTR(_ptr) (_ptr) #else #define of_match_ptr(_ptr) (null) #endif 然而,阅读linux驱动目录下的源码,一部分驱动属性表并未通过“of_match_ptr”进行转换,也是能正常使用的。从该宏的原型可知,该宏只是针对不同类型驱动匹配的宏选择,不通过该宏处理,并不影响。保持良好的习惯,保证驱动兼容性,同时兼容dt和acpi匹配,建议增加“of_match_ptr”进行转换。 例子: static struct of_device_id of_bmp180_ids[] = { {.compatible = "bosch,bmp180"}, { } }; static struct i2c_driver bmp180_driver = { .driver = { .owner = THIS_MODULE, .name = BMP180_DEV_NAME, .of_match_table = of_match_ptr(of_bmp180_ids), }, .id_table = bmp180_id, .probe = bmp180_probe, .remove = bmp180_remove, }; 2.2 获取节点 要获取一个设备节点的具体属性信息,必须先获取到这个设备的节点,可以通过多种方式获取设备节点。 2.2.1 通过节点名称获取 struct device_node *of_find_node_by_name(struct device_node *from,const char *name); [tr]参数含义/说明[/tr]
2.2.2 通过设备类型获取 struct device_node *of_find_node_by_type(struct device_node *from,const char *type); [tr]参数含义/说明[/tr]
2.2.3 通过“compatible”属性获取 struct device_node *of_find_compatible_node(struct device_node *from, const char *type, const char *compat); [tr]参数含义/说明[/tr]
2.2.4 通过驱动匹配表属性获取 struct device_node *of_find_matching_node_and_match( struct device_node *from, const struct of_device_id *matches, const struct of_device_id **match); [tr]参数含义/说明[/tr]
例子: struct device_node *p; p =of_find_matching_node(NULL, &of_bmp180_ids, &&pf); 2.2.5 通过“full_name”获取 struct device_node *of_find_node_by_path(const char *path); [tr]参数含义/说明[/tr]
例子: struct device_node *p p = of_find_node_by_path("/gpio-leds"); 2.2.6 获取父子点 struct device_node *of_get_parent(const struct device_node *node); [tr]参数含义/说明[/tr]
2.2.7 获取下一子节点 struct device_node *of_get_next_child(const struct device_node *node, struct device_node *prev); [tr]参数含义/说明[/tr]
2.3 获取属性 一个节点(设备)属性包括了名称、长度、值等,这些都是驱动使用的必备属性,linux用一个结构体“strcut property”描述节点属性,位于“kernel/include/of.h”中。 struct property { char *name; int length; void *value; struct property *next; unsigned long _flags; unsigned int unique_id; struct bin_attribute attr; }; 2.3.1 获取指定属性 struct property *of_find_property(const struct device_node *np, const char *name, int *lenp); [tr]参数含义/说明[/tr]
2.3.2 获取指定属性元素数量 int of_property_count_elems_of_size(const struct device_node *np, const char *propname, int elem_size); [tr]参数含义/说明[/tr]
例子:
获取节点spi4的reg属性元素数量(伪代码) struct device_node *p = NULL int num = 0; p = of_find_compatible_node(NULL, NULL, "rockchip,rk3399-spi"); num = of_property_count_elems_of_size(p, "reg", 8/*64位处理器*/); /* 执行成功num==4 */ 2.3.3 获取指定属性指定标号的u32类型值 extern int of_property_read_u32_index(const struct device_node *np, const char *propname, u32 index, u32 *out_value); [tr]参数含义/说明[/tr]
例子
struct device_node *p = NULL uint32 out= 0; p = of_find_compatible_node(NULL, NULL, "rockchip,rk3399-spi"); of_property_read_u32_index(p, "reg", 1, &out); 2.3.4 获取数值 int of_property_read_variable_u8_array(const struct device_node *np, const char *propname, u8 *out_values, size_t sz_min, size_t sz_max); int of_property_read_variable_u16_array(const struct device_node *np, const char *propname, u16 *out_values, size_t sz_min, size_t sz_max); 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); int of_property_read_variable_u64_array(const struct device_node *np, const char *propname,u64 *out_values, size_t sz_min,size_t sz_max); [tr]参数含义/说明[/tr]
四个函数分别可以读取节点属性值为u8、u16、u32、u64的数组数据中的变化的数据。当“sz_max”为0时,表示节点属性值是固定的数组数据,此时linux内核单独封装出几个函数,“sz_min”则表示读取元素数目。 int of_property_read_u8_array(const struct device_node *np, const char *propname,u8 *out_values, size_t sz); int of_property_read_u16_array(const struct device_node *np, const char *propname,u16 *out_values, size_t sz); int of_property_read_u32_array(const struct device_node *np, const char *propname,u32 *out_values, size_t sz); int of_property_read_u64_array(const struct device_node *np, const char *propname,u64 *out_values, size_t sz); int of_property_read_u64_array(const struct device_node *np, const char *propname, u64 *out_values, size_t sz) { int ret = of_property_read_variable_u64_array(np, propname, out_values, sz, 0); if (ret >= 0) return 0; else return ret; } 2.3.2例子中,reg为u64类型数组,可以用"of_property_read_u64_array"一次性获取数组所有元素。 当获取数组元素数目“sz”为1时,可以表示获取单个u8、u16、u32、u64的值,因此进一步又封装理论获取数值的函数。 static inline int of_property_read_u8(const struct device_node *np,const char *propname,u8 *out_value); static inline int of_property_read_u16(const struct device_node *np,const char *propname,u8 *out_value); static inline int of_property_read_u32(const struct device_node *np,const char *propname,u8 *out_value); int of_property_read_u32(const struct device_node *np,const char *propname, u32 *out_value) { return of_property_read_u32_array(np, propname, out_value, 1); } 2.3.5 获取string int of_property_read_string_helper(struct device_node *np, const char *propname, const char **out_strs, size_t sz, int index) [tr]参数含义/说明[/tr]
“of_property_read_string_helper”函数是获取节点属性为一组string数值的多个数据,而且可以指定标号开始获取。与2.3.4中获取数值函数类似,函数进一步封装为“获取string数组所有数据”和“获取属性为string的值(sz=1)” int of_property_read_string_array(struct device_node *np,const char *propname, const char **out_strs,size_t sz) { return of_property_read_string_helper(np, propname, out_strs, sz, 0); } int of_property_read_string(struct device_node *np,const char *propname, const char **out_string); 2.3.6 获取地址 【1】获取地址和地址范围的字长 int of_n_addr_cells(struct device_node *np); int of_n_size_cells(struct device_node *np); [tr]参数含义/说明[/tr]
【2】获取地址 获取地址相关属性,一般是“reg”或者“assigned-address”的值。 const __be32 *of_get_address(struct device_node *dev, int index, u64 *size, unsigned int *flags); [tr]参数含义/说明[/tr]
【3】IO地址转换为物理地址 u64 of_translate_address(struct device_node *np, const __be32 *addr) [tr]参数含义/说明[/tr]
【4】获取虚拟地址 void __iomem *of_iomap(struct device_node *device, int index); [tr]参数含义/说明[/tr]
“of_iomap”用于把物理地址转为为虚拟地址。在linux未引入设备树前,采用的是“ioremap”函数实现物理地址转换虚拟地址的过程,引入设备树后,可以直接调用“of_iomap”函数获取虚拟地址。“of_iomap”本质也是将“reg”属性的的地址信息转换为虚拟地址。如果“reg”属性有多个单位地址,可以通过函数形参“index”指定某一段内存。 2.3.7 获取GPIO int of_get_named_gpio(struct device_node *np, const char *propname, int index); [tr]参数含义/说明[/tr]
2.3.8 获取中断 【1】获取中断数量 int of_irq_count(struct device_node *dev); [tr]参数含义/说明[/tr]
【2】获取中断号 int of_irq_get(struct device_node *dev, int index); /*通过中断标号获取*/ int of_irq_get_byname(struct device_node *dev, const char *name);/*通过中断名称获取*/ [tr]参数含义/说明[/tr]
2.3.9 提取资源空间 CPU的一系列外设,包括GPIO、Timer、I2C、SPI等都有自己的寄存器,也就是它们自己特有的“资源空间”。linux内核采用“struct resource”结构体来描述一个节点的资源空间。“struct resource”位于“linux/linux/ioport.h” /* * Resources are tree-like, allowing * nesting etc.. */ struct resource { resource_size_t start; /* 起始地址 */ resource_size_t end; /* 结束地址 */ const char *name; /* 资源名称 */ unsigned long flags; /* 资源类型 */ struct resource *parent, *sibling, *child; }; 关于资源类型“flags”,常用的资源类型,位于“linux/linux/ioport.h”中定义了常用的资源类型。我们见的是内存资源“IORESOURCE_MEM”、GPIO资源“IORESOURCE_IO”、寄存器资源“IORESOURCE_REG”、中断资源“IORESOURCE_IRQ”等。 /* * IO resources have these defined flags. */ #define IORESOURCE_BITS 0x000000ff /* Bus-specific bits */ #define IORESOURCE_TYPE_BITS 0x00001f00 /* Resource type */ #define IORESOURCE_IO 0x00000100 /* PCI/ISA I/O ports */ #define IORESOURCE_MEM 0x00000200 #define IORESOURCE_REG 0x00000300 /* Register offsets */ #define IORESOURCE_IRQ 0x00000400 #define IORESOURCE_DMA 0x00000800 #define IORESOURCE_BUS 0x00001000 ...... “of_address_to_resource”函数是将“reg”属性转换为“struct resource”类型。 int of_address_to_resource(struct device_node *dev, int index, struct resource *r); [tr]参数含义/说明[/tr]
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
你正在撰写答案
如果你是对答案或其他答案精选点评或询问,请使用“评论”功能。
rk1126实现BT601输入,输入不带IIC接口的驱动程序
1661 浏览0 评论
1451 浏览1 评论
2592 浏览3 评论
RK3568 Android11让系统ntp校准时间生效,需要设置些什么
3279 浏览1 评论
5323 浏览2 评论
小黑屋|手机版|Archiver|德赢Vwin官网(湘ICP备2023018690号)
GMT+8, 2024-8-24 09:12, Processed in 0.587590 second(s), Total 72, Slave 56 queries .
Powered by德赢Vwin官网 网
© 2015bbs.elecfans.com
关注我们的微信
下载发烧友APP
德赢Vwin官网 观察
版权所有 © 湖南华秋数字科技有限公司
德赢Vwin官网 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号