接口
1.出处:
include/linux/of.h
2.数据结构:
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;
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
};
3.从设备树中查找节点
struct device_node *of_find_node_by_name(struct device_node *from,const char *name)
/*
* 功能:通过类型找到设备节点
* 输入参数: struct device_node *from: 开始节点,比如从/I2C节点开始,搜寻它下面的所有子节点
* const char *type: 类型名
* 返回值:搜索到的节点
*/
struct device_node *of_find_node_by_type(struct device_node *from,const char *type);
/*
* 功能:通过compatible属性查找节点
* 参数:struct device_node *from: - 开始节点
* const char *type: - 设备类型
* const char *compat: - compatible值"厂商,设备"
* 返回值: 失败:NULL
*/
struct device_node *of_find_compatible_node(struct device_node *from,const char *type, const char *compat);
/*
* 功能:从匹配表中查找设备ID相匹配的节点
* 参数:struct device_node *from: - 开始节点
const struct of_device_id *matches: - 表,用于匹配
const struct of_device_id **match: - 表中的该项开始,从后面匹配
* 返回值: 失败:NULL
*/
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);
struct device_node *of_find_node_by_path(const char *path);
struct device_node *of_get_parent(const struct device_node *node);
struct device_node *of_get_next_parent(struct device_node *node);
struct device_node *of_get_next_child(const struct device_node *node,struct device_node *prev);
struct device_node *of_get_child_by_name(const struct device_node *node,
const char *name);
4.从设备节点中提取信息
struct property {
char *name;
int length;
void *value;
};
/*
* 功能:提取属性
* 输入参数:const struct device_node *np: 节点指针
* const char *name,: 属性名
* int *lenp: 长度
*/
struct property *of_find_property(const struct device_node *np,
const char *name,
int *lenp);
/*
* 功能:通过索引提取属性
* 输入参数:const struct device_node *np: 设备节点
* const char *propname: 被搜索的compatible属性名
* u32 index: 索引
* 输出参数:u32 *out_value:提取到的属性值
* 返回值: 成功:0 失败:返回负数,其绝对值是错误码
*/
int of_property_read_u32_index(const struct device_node *np,
const char *propname,
u32 index, u32 *out_value);
int of_property_read_u8_array(const struct device_node *np,
const char *propname, u8 *out_values, size_t sz);
int of_property_read_string(struct device_node *np,
const char *propname,
const char **out_string);
5.从设备树中提取地址
5.1 地址数据结构
<linux/of_address.h>
struct resource {
resource_size_t start;
resource_size_t end;
unsigned long flags;
};
/*
* 功能:提取资源
* 输入参数:struct device_node *dev: - 当前节点
* int index: - 资源编号 0开始
* 输出参数:struct resource *r: - 输出找到结果
*/
int of_address_to_resource(struct device_node *dev, int index,
struct resource *r);
/*
* 功能:以虚拟地址形式提取
* 输入参数: struct device_node *node: 设备节点
* index: 索引
* 返回值:成功:虚拟地址 失败:NULL
*/
void __iomem *of_iomap(struct device_node *node, int index);
/*
* 功能:以虚拟地址形式提取某项属性中的某个地址
* 输入参数: struct device_node *node: 设备节点
* index: 该项属性中的第几个
* const char *name: 该项属性的名字
* 返回值:成功:虚拟地址 失败:NULL
*/
void __iomem *of_io_request_and_map(struct device_node *device,
int index, const char *name);
/*
* 功能:提取地址
* 输入参数: struct device_node *dev: 设备节点
* int index: 索引
* u64 *size: 大小
* unsigned int *flags:
*/
const __be32 *of_get_address(struct device_node *dev, int index,
u64 *size, unsigned int *flags);
从设备树中提取中断
/*
* 功能:提取中断
* 输入参数: struct device_node *dev: 设备节点
* int index: 索引
* 输出参数: struct resource *r:获得的中断号
* 返回值成功:0 失败:负数
*/
int of_irq_to_resource(struct device_node *dev, int index,
struct resource *r);
/*
* 功能:提取中断
* 输入参数: struct device_node *dev: 设备节点
* int index: 索引
* 返回值:成功:中断号 失败:负数
*/
int of_irq_get(struct device_node *dev, int index);
int of_irq_get_byname(struct device_node *dev, const char *name);
/*
* 功能:通过索引提取中断,并将物理中断号映射成系统中的中断号
* 输入参数: struct device_node *dev: 设备
* int index: 索引
* 返回值:成功:中断号 失败:负数
*/
unsigned int irq_of_parse_and_map(struct device_node *node, int index);
自定义中断号
/*
* 功能:从设备树获取gpio编号
* 输入参数:@np: 设备节点
* @propname: 属性域名字
* @index: 索引
* 返回值: gpio编号
*/
int of_get_named_gpio(struct device_node *np, const char *propname, int index)
<include/linux/gpio.h>
int gpio_request_one(unsigned gpio, unsigned long flags, const char *label)
/*
* 功能: 为该gpio资源申请一个中断号
* 输入参数: gpio: GPIO编号
* 返回值: 中断号
*/
fsirq = gpio_to_irq(gpio);
/*
* 功能: 将用户申请到的中断号绑定回调函数,并向内核注册
* 输入参数: fsirq: 中断号
* key_interrupt: 回调函数名
* IRQF_TRIGGER_RISING: 中断触发方式
* DEVNAME: 设备名
* devid: 设备ID(一般可填NULL)
* 返回值:成功:0 失败:负数
*/
int request_irq(fsirq, key_interrupt, IRQF_TRIGGER_RISING, DEVNAME, NULL)
工程实例
static struct class *cls = NULL;
static int major = 0;
static int minor = 0;
const int count = 6;
static struct cdev *fskeyp = NULL;
static struct device_node *fsnd = NULL;
static int fsirq;
static int gpio;
static atomic_t tv;
/*
*功能:通过路径查找节点
*参数:
const char *nodepath - 节点路径或引用
*返回值:
失败:NULL
成功:节点指针
*/
static struct device_node *getnodebypath(const char *nodepath)
{
/*匹配路径查找节点*/
return of_find_node_by_path(nodepath);
}
static irqreturn_t key_interrupt(int irqno, void *devid)
{
printk("irqno=%d; %s : %d\n", irqno, __func__, __LINE__);
return IRQ_HANDLED;
}
//打开设备
static int fskey_open(struct inode *inode, struct file *filp)
{
//get command and pid
printk(KERN_INFO "(%s:pid=%d), %s : %s : %d\n",
current->comm, current->pid, __FILE__, __func__, __LINE__);
//get major and minor from inode
printk(KERN_INFO "(major=%d, minor=%d), %s : %s : %d\n",
imajor(inode), iminor(inode), __FILE__, __func__, __LINE__);
if(!atomic_dec_and_test(&tv)){//原子操作,防止重复打开
atomic_inc(&tv);
return -EBUSY;
}
/* 为一个中断号绑定回调函数并注册进内核*/
if (request_irq(fsirq, key_interrupt, IRQF_TRIGGER_RISING, DEVNAME, NULL)){
printk(KERN_ERR"ERROR: request IRQ[%d] fail.\n", fsirq);
return -EAGAIN;
}
return 0;
}
//关闭设备
static int fskey_release(struct inode *inode, struct file *filp)
{
//get command and pid
printk(KERN_INFO "(%s:pid=%d), %s : %s : %d\n",
current->comm, current->pid, __FILE__, __func__, __LINE__);
//get major and minor from inode
printk(KERN_INFO "(major=%d, minor=%d), %s : %s : %d\n",
imajor(inode), iminor(inode), __FILE__, __func__, __LINE__);
free_irq(fsirq, NULL);//释放中断号
atomic_inc(&tv);
return 0;
}
//读设备
//ssize_t read(int fd, void *buf, size_t count)
static ssize_t fskey_read(struct file *filp, char __user *buf, size_t size, loff_t *offset)
{
return 0;
}
static struct file_operations fops = {
.owner = THIS_MODULE,
.open = fskey_open,
.release= fskey_release,
.read = fskey_read,
};
static int __init fskey_init(void)
{
dev_t devnum;
int ret, i;
struct resource r;
void __iomem *ioaddr = NULL;
struct device *devp = NULL;
//get command and pid
printk(KERN_INFO "(%s:pid=%d), %s : %s : %d\n",
current->comm, current->pid, __FILE__, __func__, __LINE__);
fsnd = getnodebypath("/key@26");//从设备树中找到设备
if(NULL == fsnd){
return -EINVAL;
}
//fsirq = irq_of_parse_and_map(fsnd, 0);
//if(0 > fsirq){
// return fsirq;
//}
of_irq_to_resource(fsnd, 0, &r);//获取资源,中断资源
if(IORESOURCE_IRQ == r.flags)//确保获得的资源是中断资源而不是IO资源
printk("irq 0: %d\n", r.start);
of_irq_to_resource(fsnd, 1, &r);
if(IORESOURCE_IRQ == r.flags)
printk("irq 1: %d\n", r.start);
fsirq = of_irq_get(fsnd, 0);//获取该设备节点中的第0个中断资源
printk("fsirq = %d\n", fsirq);
fsirq = of_irq_get(fsnd, 1);
printk("fsirq = %d\n", fsirq);
of_address_to_resource(fsnd, 0, &r);//获取该设备节点中的地址资源
if(IORESOURCE_MEM == r.flags)
printk("iomem 0: 0x%x\n", r.start);
of_address_to_resource(fsnd, 1, &r);
if(IORESOURCE_MEM == r.flags)
printk("iomem 1: 0x%x\n", r.start);
ioaddr = of_iomap(fsnd, 0);//获取该设备节点中的地址资源并映射到虚拟地址
printk("iomem 0: 0x%p\n", ioaddr);
ioaddr = of_iomap(fsnd, 1);
printk("iomem 0: 0x%p\n", ioaddr);
gpio = of_get_named_gpio(fsnd, "key-intr", 0);//获取该设备节点中的IO资源
if (gpio_is_valid(gpio)) {//该gpio可用
ret = gpio_request_one(gpio, GPIOF_IN, "key-intr");//将一个gpio编号绑定一些信息,使其成为内核认可的一个完整的GPIO
if (ret) {
printk(KERN_ERR"ERROR: gpio_request_one fail.\n");
return ret;
}
fsirq = gpio_to_irq(gpio); //为该gpio资源申请一个中断号
if(0 > fsirq){
printk(KERN_ERR"ERROR: gpio_to_irq fail.\n");
return -ENODEV;
}
}else{
printk(KERN_ERR"ERROR: gpio is invalid.\n");
return -EINVAL;
}
//1. alloc cdev obj
fskeyp = cdev_alloc();
if(NULL == fskeyp){
return -ENOMEM;
}
//2. init cdev obj
cdev_init(fskeyp, &fops);
ret = alloc_chrdev_region(&devnum, minor, count, DEVNAME);
if(ret){
goto ERR_STEP;
}
major = MAJOR(devnum);
//3. register cdev obj
ret = cdev_add(fskeyp, devnum, count);
if(ret){
goto ERR_STEP1;
}
cls = class_create(THIS_MODULE, DEVNAME);
if(IS_ERR(cls)){
ret = PTR_ERR(cls);
goto ERR_STEP1;
}
for(i = minor; i < (count+minor); i++){
devp = device_create(cls, NULL, MKDEV(major, i), NULL, "%s%d", DEVNAME, i);
if(IS_ERR(devp)){
ret = PTR_ERR(devp);
goto ERR_STEP2;
}
}
// init atomic_t
atomic_set(&tv, 1);//初始化原子变量
//get command and pid
printk(KERN_INFO "(%s:pid=%d), %s : %s : %d - ok.\n",
current->comm, current->pid, __FILE__, __func__, __LINE__);
return 0;
ERR_STEP2:
for(--i; i >= minor; i--){
device_destroy(cls, MKDEV(major, i));
}
class_destroy(cls);
ERR_STEP1:
unregister_chrdev_region(devnum, count);
ERR_STEP:
cdev_del(fskeyp);
//get command and pid
printk(KERN_INFO "(%s:pid=%d), %s : %s : %d - fail.\n",
current->comm, current->pid, __FILE__, __func__, __LINE__);
return ret;
}
static void __exit fskey_exit(void)
{
int i;
//get command and pid
printk(KERN_INFO "(%s:pid=%d), %s : %s : %d - leave.\n",
current->comm, current->pid, __FILE__, __func__, __LINE__);
for(i=minor; i < (count+minor); i++){
device_destroy(cls, MKDEV(major, i));
}
class_destroy(cls);
unregister_chrdev_region(MKDEV(major, minor), count);
cdev_del(fskeyp);
gpio_free(gpio); //释放申请的gpio
}
module_init(fskey_init);
module_exit(fskey_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Farsight");
MODULE_DESCRIPTION("Demo for kernel module");