字符设备驱动第十一课----设备树

接口

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;    /* removed properties */
    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 *from: 开始节点,比如从/I2C节点开始,搜寻它下面的所有子节点
*          const char *name:         节点名
* 返回值:搜索到的节点
*/
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);
/*
 * 功能:通过路径查找节点
 * 参数:const char *path: - 路径
 * 返回值: 失败:NULL 成功:找到的节点
 */
struct device_node *of_find_node_by_path(const char *path);
/*
* 功能:得到父节点
* 输入参数:  const struct device_node *node:   当前节点
* 返回值: 失败:NULL 成功:找到的节点
*/
struct device_node *of_get_parent(const struct device_node *node);
/*
* 功能:得到父节点
* 输入参数:  const struct device_node *node:   当前节点
* 返回值: 失败:NULL 成功:找到的节点
*/
struct device_node *of_get_next_parent(struct device_node *node);
/*
* 功能: 获得子节点
* 输入参数: const struct device_node *node: 当前节点
*          struct device_node *prev:       前一个子节点
*/
struct device_node *of_get_next_child(const struct device_node *node,struct device_node *prev);
/*
* 功能:通过名字获取子节点
* 输入参数: const struct device_node *node:当前节点
*          const char *name:                子节点的名字
*/
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);
/*
* 功能:提取数组类型的属性
* 输入参数:const struct device_node *np:   设备节点
*         const char *propname:           被搜索的compatible属性名
* 输出参数:u32 *out_value:提取到的属性数组的首地址
*         size_t sz:     提取到的属性数组的大小 
* 返回值: 成功:0  失败:返回负数,其绝对值是错误码
*/
int of_property_read_u8_array(const struct device_node *np,
            const char *propname, u8 *out_values, size_t sz);
/*
* 功能:提取字符串类型的属性
* 输入参数:const struct device_node *np:   设备节点
*         const char *propname:           被搜索的compatible属性名
* 输出参数:u32 *out_value:提取到的属性字符串的首地址
* 返回值: 成功:0  失败:返回负数,其绝对值是错误码
*/
 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;    //资源类型:IORESOURCE_MEM(地址类资源)
                                //  IORESOURCE_IRQ(中断类资源)
    };
 /*
 * 功能:提取资源
 * 输入参数: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);
/*
* 功能:通过名字提取中断
* 输入参数:   struct device_node *dev: 设备
*            const char *name:         名字
* 返回值:成功:中断号    失败:负数
*/
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>
/*
 * 功能:将一个gpio编号绑定一些信息,使其成为内核认可的一个完整的GPIO
 * 输入参数:@gpio:  GPIO 编号
 *         @flags:  GPIOF_IN还是GPIOF_OUT
 *         @label:  取个名字来标识一下
 */
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)

工程实例

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/errno.h>

#include <asm/current.h>
#include <linux/sched.h>

#include <linux/uaccess.h>

#include <asm/atomic.h>
#include <linux/mutex.h>

#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_gpio.h>
#include <linux/of_address.h>

#include <linux/gpio.h>
#include <linux/interrupt.h>

#include <linux/device.h>
static struct class *cls = NULL;

static int major = 0;
static int minor = 0;
const  int count = 6;

#define DEVNAME "fskey"

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;
    }

#if 1
#if 0
    //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);
#endif
    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);
#else
    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;
    }
#endif

    //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");
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xxgui1992

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值