一、OF函數定義
第6.8講 Linux設備樹詳解-綁定文檔以及OF函數_嗶哩嗶哩_bilibili
《指南》43.9部分
? ? ? ? 設備樹的功能就是描述設備信息,幫助驅動開發。那么驅動如何獲取設備信息?獲取這些信息的函數linux直接提供,都定義在linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek/include/linux/of.h文件中。這些函數統一以of開頭,也稱為OF函數。
1.1?查找節點
? ? ? ? 設備樹上的設備都是一個個節點。要獲取某個設備信息,需要先找到這個設備節點。Linux內核使用device_node結構體來描述一個節點:
// 定義在linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek/include/linux/of.h
struct device_node {const char *name; // 節點名const char *type; // 設備類型phandle phandle;const char *full_name; // 節點全名struct fwnode_handle fwnode;struct property *properties; // 屬性struct property *deadprops; // removed屬性struct device_node *parent; // 父節點struct device_node *child; // 子節點struct device_node *sibling;struct kobject kobj;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
};
1.1.1 of_find_node_by_name
????????通過名字查找節點。名字是完整的node-name@unit-address,不是只有前半部分node-name,也不是label。
struct device_node *of_find_node_by_name(struct device_node *from, const char *name);
// from: 從哪個節點開始查找。為NULL表示從根節點開始查找整個設備樹
// name: 要查找節點的名字。名字不要包含label!
// return: 找到的節點。返回NULL表示未找到
1.1.2 of_find_node_by_type
????????通過device_type類型查找。不過現在device_type已經棄用,這個函數也很少再用。
struct device_node *of_find_node_by_type(struct device_node *from, const char *type);
// from: 從哪個節點開始查找。為NULL表示從根節點開始查找整個設備樹
// type: 要查找節點的device_type字符串
// return: 找到的節點。返回NULL表示未找到
1.1.3?of_find_compatible_node
????????通過兼容性列表compatible查找
struct device_node *of_find_compatible_node(struct device_node *from,const char *type, const char *compat);
// from: 從哪個節點開始查找。為NULL表示從根節點開始查找整個設備樹
// type: 要查找節點的device_type字符串。可以為NULL,表示忽略device_type屬性
// compat: 要查找節點的compatible屬性列表
// return: 找到的節點。返回NULL表示未找到
// eg: struct device_node *node;node = of_find_compatible_node(NULL, NULL, "arm,cortex-a7"); // 查找整個設備樹上兼容arm,cortex-a7的設備節點
1.1.4?of_find_matching_node_and_match
????????通過匹配列表查找
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);
// from: 從哪個節點開始查找。為NULL表示從根節點開始查找整個設備樹
// matches: of_device_id匹配列表,也就是在此匹配表里面查找節點。
// match: 找到的匹配的of_device_id
// return: 找到的節點。返回NULL表示未找到
1.1.5?of_find_node_by_path
????????通過路徑查找。這個路徑指的是設備節點在設備樹中的路徑,不是文件路徑。
static inline struct device_node *of_find_node_by_path(const char *path)
// path: 帶有全路徑的節點名,可以使用節點的別名,比如“/backlight”就是根節點下backlight節點的全路徑。
// return: 找到的節點。返回NULL表示未找到
//eg:
struct device_node *np;
np = of_find_node_by_path("/soc/aips-bus@02000000/spba-bus@02000000/serial@02020000");
1.2 查找父/子節點
1.2.1?of_get_parent
獲取指定節點的父節點
struct device_node *of_get_parent(const struct device_node *node)
// node : 要查找父節點的節點
// return: 找到的父節點。NULL表示未找到
1.2.2?of_get_next_child
獲取指定節點的子節點
struct device_node *of_get_next_child(const struct device_node *node, struct device_node *prev)
// node : 要查找子節點的節點
// prev : 前一個子節點。一個節點下有很多子節點,可以設置從哪一個子節點開始查找
// 可以設置為NULL,表示從第一個子節點開始
// return: 找到的子節點
1.3?提取屬性值
? ? ? ? 通過前面兩類函數找到了目標節點,現在可以開始獲得指定節點的具體屬性了。
1.3.1 of_find_property
????????查找指定的屬性。
static inline struct property *of_find_property(const struct device_node *np,const char *name, int *lenp)
// np : 節點
// name : 屬性名
// lenp : 屬性值的字節長度,一般寫NULL即可
// return: 找到的屬性
Linux內核中使用結構體property表示屬性,其中property結構為:
struct property {char *name; // 屬性名int length; // 長度void *value; // 值struct property *next; // 下一個屬性unsigned long _flags;unsigned int unique_id;struct bin_attribute attr;
};
?1.3.2 of_property_count_elems_of_size
????????獲取屬性中元素的數量。比如reg屬性值(如reg = <0x80000000 0x20000000>;)是一個數組,那么使用此函數可以獲取到這個數組的大小(2)。
int of_property_count_elems_of_size(const struct device_node *np, const char *propname, int elem_size)
// np : 節點
// proname : 需要統計元素數量的屬性名字
// elem_size:每一個元素的size
// return : 得到的屬性元素數量
1.3.3 of_property_read_u32_index
????????用于從屬性中獲取指定索引的u32類型數據值。
????????比如某個屬性有多個u32類型的值,那么就可以使用此函數來獲取指定索引的數據值。
static inline int of_property_read_u32_index(const struct device_node *np,const char *propname, u32 index, u32 *out_value)
// np : 節點
// proname : 要讀取的屬性名
// index : 要讀取的值的索引
// out_value: 讀取到的值
// return : 0成功。負值失敗:-EINVAL屬性不存在,-ENODATA表示要讀取的數據,-EOVERFLOW屬性值列表太小
1.3.4?of_property_read_u8_array
? ? ? ? 用于讀取一個u8類型數組屬性的所有數據。
????????類似的函數還有of_property_read_u16_array、of_property_read_u32_array、of_property_read_u64_array,表示不同的數組類型。
int of_property_read_u8_array(const struct device_node *np,const char *propname, u8 *out_values, size_t sz)
// np : 節點
// propname : 要讀取的屬性名
// out_values: 讀取到的數組
// sz : 要讀取的數組元素數量
// return : 0成功。負值失敗:-EINVAL屬性不存在,-ENODATA沒有要讀取的數據,-EOVERFLOW屬性值列表太小。
1.3.5?of_property_read_u8
? ? ? ? 除了數組屬性以外,很多屬性只有一個整形值。該函數用于讀取這種只有一個u8整形值的屬性。
????????類似的函數還有of_property_read_u16、of_property_read_u32、of_property_read_u64。
int of_property_read_u8_array(const struct device_node *np,const char *propname, u8 *out_values, size_t sz)
// np : 節點
// propname : 要讀取的屬性名
// out_values: 讀取到的值
// return : 0成功。負值失敗:-EINVAL屬性不存在,-ENODATA沒有要讀取的數據,-EOVERFLOW屬性值列表太小。
1.3.6 of_property_read_string
? ? ? ? 用于讀取字符串類型屬性的值
int of_property_read_string(struct device_node *np,const char *propname, const char **out_string)
// np : 節點
// propname : 要讀取的屬性名
// out_string: 讀取到的字符串
// return : 0成功。負值失敗
1.3.7?of_n_addr_cells
? ? ? ? 獲取#size-cells的值
int of_n_addr_cells(struct device_node *np);
// np : 節點
// return: 獲取到的#size-cells的值
1.3.8?of_n_size_cells
? ? ? ? 獲取#size-cells的值
int of_n_size_cells(struct device_node *np)、
// np : 節點
// return: 獲取到的#size-cells的值
二、OF函數實際使用
第6.9講 Linux設備樹詳解-OF函數操作實驗_嗶哩嗶哩_bilibili
2.1 文件結構
? ? ? ? 新建實驗4文件夾4_dtsof,直接將之前實驗3文件夾3_newchrled里的Makefile、newchrled.c、.vscode復制到新的文件夾里。將newchrled.c改名為dtsof.c。
? ? ? ? 用vscode打開4_dtsof,將工作區另存為。
4_DTSOF (工作區)
├── .vscode
│ ├── c_cpp_properties.json
│ └── settings.json
├── 4_dtsof.code-workspace
├── Makefile
└── dtsof.c
? ? ? ? 將Makefile中的obj-m修改為obj-m := dtsof.o
2.2?dtsof.c
2.2.1獲取backlight的compatible屬性值
imx6ull-alientek-emmc.dts中backlight的定義如下:
backlight {compatible = "pwm-backlight";pwms = <&pwm1 0 5000000>;brightness-levels = <0 4 8 16 32 64 128 255>;default-brightness-level = <6>;status = "okay";};
?編寫dtsof.c,獲取backlight的compatible屬性值:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/of.h>/* 模塊入口 */
static int __init dtsof_init(void){int ret = 0;struct device_node *backlight_nd; // 節點指針struct property *comppro; // 屬性指針// 查找backlight節點 // 路徑為/backlight 定義在linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek/arch/arm/boot/dts/imx6ull-alientek-emmc.dtsbacklight_nd = of_find_node_by_path("/backlight"); //路徑查找if(backlight_nd == NULL){ // 失敗ret = -EINVAL;goto fail_findnd; // 錯誤處理}// 查找backlight屬性comppro = of_find_property(backlight_nd, "compatible", NULL); // 屬性名查找if(comppro == NULL){ret = -EINVAL;goto fail_finpro; // 錯誤處理} else {printk("compatible = %s\r\n",(char*)comppro->value);}return 0;fail_finpro: // 查找屬性失敗
fail_findnd: // 查找節點失敗printk("failed\r\n");return ret;
}/* 模塊出口 */
static void __exit dtsof_exit(void){}/* 注冊入口出口*/
module_init(dtsof_init);
module_exit(dtsof_exit);
MODULE_LICENSE("GPL");
# VSCODE終端
make
sudo cp dtsof.ko /home/for/linux/nfs/rootfs/lib/modules/4.1.15/ -f# 串口
cd lib/modules/4.1.15/
modprobe dtsof.ko
depmod
modprobe dtsof.ko # 可以看到輸出:compatible = pwm-backlight
rmmod dtsof.ko
2.2.2獲取backlight的所有屬性值
????????上面的代碼只簡單演示了一下使用OF函數獲取compatible屬性值。要獲取backlight的所有屬性值,完整代碼如下:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/of.h>
#include <linux/slab.h>/* 模塊入口 */
static int __init dtsof_init(void){int ret = 0;struct device_node *backlight_nd;// 節點指針struct property *comppro; // 保存compatible屬性const char* status; // 保存status屬性u32 default_brightness_level; // 保存default-brightness-level屬性u32 count = 0; // 保存brightness-levels屬性的元素數量u32 *brightness_levels; // 保存brightness-levels屬性的數據u8 i = 0; // 給for循環用的// 1.查找backlight節點======================================================================// 路徑為/backlight // 定義在linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek/arch/arm/boot/dts/imx6ull-alientek-emmc.dtsbacklight_nd = of_find_node_by_path("/backlight"); //路徑查找if(backlight_nd == NULL){ // 失敗ret = -EINVAL;goto faile_findnd;}// 2.查找backlight屬性======================================================================// 獲取compatible屬性(屬性名查找)comppro = of_find_property(backlight_nd, "compatible", NULL);if(comppro == NULL){ret = -EINVAL;goto fail_finpro;} else {printk("compatible = %s\r\n",(char*)comppro->value);}// 獲取status屬性(讀取字符串)ret = of_property_read_string(backlight_nd, "status", &status);if(ret < 0){goto fail_finprs;} else {printk("status = %s\r\n", status);}// 獲取default-brightness-level屬性(讀取u32)ret = of_property_read_u32(backlight_nd, "default-brightness-level", &default_brightness_level);if(ret < 0){goto fail_read32;} else {printk("default-brightness-level = %d\r\n", default_brightness_level);}// 獲取brightness-levels的元素個數(讀取元素個數)count = of_property_count_elems_of_size(backlight_nd, "brightness-levels", sizeof(u32));if(count < 0){goto fail_readele;} else {printk("brightness-level elems size = %d\r\n", count);// 獲取brightness-levels的數據(讀取u32數組)brightness_levels = kmalloc(count * sizeof(u32), GFP_KERNEL); // 內存申請if(!brightness_levels){ goto fail_mem;}ret = of_property_read_u32_array(backlight_nd, "brightness-levels", brightness_levels, count);if(ret < 0){goto fail_readarr; // 錯誤處理統一放到goto里面去,因此這里不釋放內存,而是放到goto去處理} else {printk("brightness-level elems = ");for(i=0;i<count;i++){printk("%d ",brightness_levels[i]);}printk("\r\n");kfree(brightness_levels);}}return 0;/* 錯誤處理 */
// 這里goto只是演示一下格式,并不詳細處理fail_readarr: // 讀取brightness-levels數據失敗kfree(brightness_levels); //釋放內存
fail_mem: // 內存分配失敗
fail_readele: // 讀取元素數量失敗
fail_read32: // 讀取default-brightness-level失敗
fail_finprs: // 讀取status失敗
fail_finpro: // 查找compatible失敗
faile_findnd: // 查找節點失敗printk("failed\r\n");return ret;
}/* 模塊出口 */
static void __exit dtsof_exit(void){}/* 注冊入口出口*/
module_init(dtsof_init);
module_exit(dtsof_exit);
MODULE_LICENSE("GPL");
????????modprobe以后應當能看到以下內容:?