常用OF函数介绍
设备树描述了设备的详细信息,这些信息包括数字类型的、字符串类型的、数组类型的我们在编写驱动的时候需要获取到这些信息。比如设备树使用reg属性描述了某个外设的寄存器地址为 0X02005482,长度为 0X400,我们在编写驱动的时候需要获取到 reg 属性的0X02005482 和0X400 这两个值,然后初始化外设。Linux内核给我们提供了一系列的函数来获取设备树中的节点或者属性信息,这一系列的函数都有一个统一的前缀“of_”,所以在很多资料里面也被叫做 OF 函数。这些 OF 函数原型都定义在 include/linux/of.h 文件中,以下函数介绍节选自正点原子。
of_find_node_by_name 函数
of_find_node_by_name 函数通过节点名字查找指定的节点,函数原型如下:
struct device_node *of_find_node_by_name(struct device_node *from, const char *name)
函数参数和返回值含义如下:
- from:开始查找的节点,如果为 NULL 表示从根节点开始查找整个设备树。
- name:要查找的节点名字。
- 返回值:找到的节点,如果为 NULL 表示查找失败。
of_find_node_by_type 函数
of_find_node_by_type 函数通过 device_type 属性查找指定的节点,函数原型如下:
struct device_node *of_find_node_by_type(struct device_node *from, const char *type)
函数参数和返回值含义如下:
- from:开始查找的节点,如果为 NULL 表示从根节点开始查找整个设备树。
- type:要查找的节点对应的 type 字符串,也就是 device_type 属性值。
- 返回值:找到的节点,如果为 NULL 表示查找失败。
of_find_compatible_node函数
of_find_compatible_node 函数根据 device_type 和 compatible 这两个属性查找指定的节点,
函数原型如下:
struct device_node *of_find_compatible_node(struct device_node *from, const char*type, const char *compatible)
函数参数和返回值含义如下:
- from:开始查找的节点,如果为 NULL 表示从根节点开始查找整个设备树。
- type:要查找的节点对应的 type 字符串,也就是 device_type 属性值,可以为 NULL,表示
- 忽略掉 device_type 属性。
- compatible:要查找的节点所对应的 compatible 属性列表。
- 返回值:找到的节点,如果为 NULL 表示查找失败
of_find_matching_node_and_match 函数
of_find_matching_node_and_match 函数通过 of_device_id 匹配表来查找指定的节点,函数原
型如下:
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。
- 返回值:找到的节点,如果为 NULL 表示查找失败
of_find_node_by_path 函数
of_find_node_by_path 函数通过路径来查找指定的节点,函数原型如下:
inline struct device_node *of_find_node_by_path(const char *path)
函数参数和返回值含义如下:
- path:带有全路径的节点名,可以使用节点的别名,比如“/backlight”就是 backlight 这个
节点的全路径。 - 返回值:找到的节点,如果为 NULL 表示查找失败
查找父/子节点函数
Linux 内核提供了几个查找节点对应的父节点或子节点的 OF 函数,我们依次来看一下。
of_get_parent 函数
of_get_parent 函数用于获取指定节点的父节点(如果有父节点的话),函数原型如下:
struct device_node *of_get_parent(const struct device_node *node)
函数参数和返回值含义如下:
- node:要查找的父节点的节点。
- 返回值:找到的父节点。
of_get_next_child 函数
of_get_next_child 函数用迭代的查找子节点,函数原型如下:
struct device_node *of_get_next_child(const struct device_node *node, struct device_node *prev)
函数参数和返回值含义如下:
- node:父节点。
- prev:前一个子节点,也就是从哪一个子节点开始迭代的查找下一个子节点。可以设置为
- NULL,表示从第一个子节点开始。
- 返回值:找到的下一个子节点。
###OF函数实践
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/cdev.h>
#include <linux/mdev.h>
#include <linux/of.h>
#define LED_Major 200
#define LED_Name "LED_Device"
#define License "GPL"
#define Author "zhangzejun"
#define GPIOI_CLK 0X50000000
#define GPIOI_BASE 0x5000A000
#define RCC_MP_AHB4ENSETR (GPIOI_CLK+0xA28) //时钟地址
#define GPIOI_MODER (GPIOI_BASE+0x00) //模式地址
#define GPIOI_OTYPER (GPIOI_BASE+0x04) //输出模式地址
#define GPIOI_OSPEEDR (GPIOI_BASE+0x08) //速度地址
#define GPIOI_PUPDR (GPIOI_BASE+0x0C) //上下拉地址
#define GPIOI_BSRR (GPIOI_BASE+0x18) //置位/复位地址
int check_led_tree(void);
struct led_dev
{
int major; //主设备号
int minor; //次设备号
struct class* class; //设备类
struct device * device;
struct cdev cdev;
dev_t dev; //设备
/* data */
}led_devinfo;
static void __iomem *RCC_MP_AHB4ENSETR_Virtual;
static void __iomem *GPIOI_MODE_Virtual;
static void __iomem *GPIOI_OTYPER_Virtual;
static void __iomem *GPIOI_OSPEEDR_Virtual;
static void __iomem *GPIOI_PUPDR_Virtual;
static void __iomem *GPIOI_BSRR_Virtual;
static int led_open(struct inode *inode, struct file *filp)
{
int res = 0;
return res;
}
static int led_release(struct inode *inode, struct file *filp)
{
int res = 0;
return res;
}
/*
* @description : LED初始化,申请虚拟地址
* @param - NULL : NULL
* @param - NULL : NULL
* @param - NULL : NULL
* @param - NULL : NULL
* @return : 写入的字节数,如果为负值,表示写入失败
*/
void Led_Init(void)
{
u32 ahb4_clock_val = 0,mode_val = 0,otyper_val = 0,ospeed_val,pupdr_val = 0,bsrr_val = 0;
RCC_MP_AHB4ENSETR_Virtual = ioremap(RCC_MP_AHB4ENSETR,4);
GPIOI_MODE_Virtual = ioremap(GPIOI_MODER,4);
GPIOI_OTYPER_Virtual = ioremap(GPIOI_OTYPER,4);
GPIOI_PUPDR_Virtual = ioremap(GPIOI_PUPDR,4);
GPIOI_OSPEEDR_Virtual = ioremap(GPIOI_OSPEEDR,4);
GPIOI_BSRR_Virtual = ioremap(GPIOI_BSRR,4);
//初始化时钟
ahb4_clock_val = readl(RCC_MP_AHB4ENSETR_Virtual);
ahb4_clock_val &= ~(1 << 8);
ahb4_clock_val |= (1 << 8);
writel(ahb4_clock_val,RCC_MP_AHB4ENSETR_Virtual);
//设置为输出模式
mode_val = readl(GPIOI_MODE_Virtual);
mode_val &= ~(0x03 << 0);
mode_val |= (0x01 << 0);
writel(mode_val,GPIOI_MODE_Virtual);
//设置为推挽输出
otyper_val = readl(GPIOI_OTYPER_Virtual);
otyper_val &= 0xFFFFFFFE;
writel(otyper_val,GPIOI_OTYPER_Virtual);
//设置GPIO速度
ospeed_val = readl(GPIOI_OSPEEDR_Virtual);
mode_val &= ~(0x03 << 0);
ospeed_val |= 0x01;
writel(ospeed_val,GPIOI_OSPEEDR_Virtual);
//设置GPIO不上拉也不下拉
pupdr_val = readl(GPIOI_PUPDR_Virtual);
pupdr_val &= 0xFFFFFFFC;
writel(pupdr_val,GPIOI_PUPDR_Virtual);
//初始化关闭LED灯
bsrr_val = readl(GPIOI_BSRR_Virtual);
bsrr_val |= 0x01;
writel(bsrr_val,GPIOI_BSRR_Virtual);
}
/*
* @description : 向设备写数据
* @param - filp : 设备文件,表示打开的文件描述符
* @param - buf : 要写给设备写入的数据
* @param - cnt : 要写入的数据长度
* @param - offt : 相对于文件首地址的偏移
* @return : 写入的字节数,如果为负值,表示写入失败
*/
static ssize_t led_write(struct file *filp, const char __user *buf,size_t cnt, loff_t *offt)
{
int res=0;
char led_val[1] ={0};
u32 set_val = 0;
res = copy_from_user(led_val,buf,cnt);
check_led_tree();
if(res >= 0)
{
if(led_val[0] == '0')
{
set_val = readl(GPIOI_BSRR_Virtual);
set_val &= 0xFFFFFFFE;
set_val |= (0x1 << 16);
writel(set_val,GPIOI_BSRR_Virtual);
}
else if(led_val[0] == '1')
{
set_val = readl(GPIOI_BSRR_Virtual);
set_val &= 0xFFFEFFFF;
set_val |= 0x01;
writel(set_val,GPIOI_BSRR_Virtual);
}
}
else
{
printk("write led error\r\n");
}
return res;
}
void led_unmap(void)
{
iounmap(RCC_MP_AHB4ENSETR_Virtual);
iounmap(GPIOI_MODE_Virtual);
iounmap(GPIOI_OTYPER_Virtual);
iounmap(GPIOI_PUPDR_Virtual);
iounmap(GPIOI_OSPEEDR_Virtual);
iounmap(GPIOI_BSRR_Virtual);
}
/*
* @description : 从设备读取数据
* @param - filp : 要打开的设备文件(文件描述符)
* @param - buf : 返回给用户空间的数据缓冲区
* @param - cnt : 要读取的数据长度
* @param - offt : 相对于文件首地址的偏移
* @return : 读取的字节数,如果为负值,表示读取失败
*/
static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt,loff_t *offt)
{
int res = 0;
return res;
}
static const struct file_operations led_fops = {
.owner = THIS_MODULE,
.read = led_read,
.write = led_write,
.open = led_open,
.release = led_release,
};
int check_led_tree(void)
{
int res = 0;
struct device_node *nd;
struct property *device;
u32 reg_val[20];
int i = 0;
nd = of_find_node_by_path("/stm32mp1_led"); //查找节点
if(nd == NULL)
{
goto fail_nd;
}
device = of_find_property(nd,"compatible",NULL); //查询
if(device == NULL)
{
goto fail_readcompatible;
}
else
{
printk("stm32mp1_led compatible=%s\r\n",(char *)device->value);
}
device = of_find_property(nd,"status",NULL); //查询
if(device == NULL)
{
goto fail_readstatus;
}
else
{
printk("stm32mp1_led status=%s\r\n",(char *)device->value);
}
res = of_property_read_u32_array(nd,"reg",reg_val,12);
if(res != 0)
{
goto fail_read;
}
else
{
for(i = 0; i < 12; i++)
{
printk("reg val 0x%x\r\n",reg_val[i]);
}
}
return 0;
fail_read:
return -1;
fail_readstatus:
return -1;
fail_readcompatible:
return -1;
fail_nd:
return -1;
}
static int __init led_init(void)
{
int res = 0;
Led_Init();
memset(&led_devinfo,0,sizeof(led_devinfo)); //清除结构体
if(led_devinfo.major == 0)
{
res = alloc_chrdev_region(&led_devinfo.dev,0,1,LED_Name);
}
led_devinfo.major = MAJOR(led_devinfo.dev);
led_devinfo.minor = MINOR(led_devinfo.dev);
led_devinfo.cdev.owner = THIS_MODULE;
cdev_init(&led_devinfo.cdev, &led_fops);
res = cdev_add(&led_devinfo.cdev, led_devinfo.dev, 1);
if(res < 0)
{
goto fail_cdevadd;
}
if(res < 0)
{
printk("led init error\r\n");
goto fail_map;
}
else
{
printk("device Mjor id %d,Minor %d\r\n",led_devinfo.major,led_devinfo.minor);
}
led_devinfo.class = class_create(THIS_MODULE,LED_Name);
if (IS_ERR(led_devinfo.class))
{
goto fail_class;
}
led_devinfo.device = device_create(led_devinfo.class,NULL,led_devinfo.dev,NULL,LED_Name);
if (IS_ERR(led_devinfo.device))
{
goto fail_device;
}
printk("led init\r\n");
return res;
fail_cdevadd:
unregister_chrdev_region(led_devinfo.dev, 1);
return -1;
fail_device:
device_destroy(led_devinfo.class, led_devinfo.device);
return -1;
fail_class:
class_destroy(led_devinfo.class);
return -1;
fail_map:
led_unmap();
return -1;
}
static void __exit led_exit(void)
{
//取消申请的物理地址
led_unmap();
/* 删除 cdev */
cdev_del(&led_devinfo.cdev);
//卸载驱动
unregister_chrdev_region(led_devinfo.dev, 1);
//删除设备
device_destroy(led_devinfo.class, led_devinfo.dev);
//删除类
class_destroy(led_devinfo.class);
printk("led exit\r\n");
}
module_init(led_init); //驱动注册函数
module_exit(led_exit); //驱动卸载函数
MODULE_LICENSE(License);
MODULE_AUTHOR(Author);
MODULE_INFO(intree,"Y");