7. 利用of函数读取设备树中的信息(test)

知识点:

关于设备树of函数的使用知识点请看Linux设备树常用的OF函数总结
关于设备树其他相关知识点抢看:Linux驱动相关基础知识

实验:

目的:
本次实验的目的是测试linux下使用of函数读取设备树中某个结点的信息;

内容:
利用of函数,在上一次实验5. led驱动控制:使用register_chrdev_region的基础上,编写一个函数;并在init入口函数中调用;

在函数中使用of函数,读取arch\arm\boot\dts\imx6ull-alientek-emmc.dts里面的(根节点下)的属性信息
属性请查看文件,这里简略表示:

backlight {
		compatible = "pwm-backlight";
		pwms = <&pwm1 0 5000000>;
		brightness-levels = <0 4 8 16 32 64 128 255>;
		default-brightness-level = <6>;
		status = "okay";
	};

实验函数代码:

void test_of_func(void)
{
    // compatible:字符串类型
    // property *of_find_property(const struct device_node *np,const char *name,int *lenp)    struct device_node *np;
    
    struct device_node* np = of_find_node_by_path("/backlight");
    struct property* prop = of_find_property(np, "compatible",NULL);
    printk("compatible = %s\r\n", (char*)prop->value);
    // status
    char str[10];
    char*p = str;
    of_property_read_string(np, "status", &p);
    printk("status = %s\r\n", p);
    p = NULL;
    // default-brightness-level= <6>;
    unsigned int out_val = 0;
    of_property_read_u32(np, "default-brightness-level", &out_val);
    printk("default-brightness-level = %d\r\n", out_val);
    // brightness-levels = <0 4 8 16 32 64 128 255>;
    int count = of_property_count_u32_elems(np,"brightness-levels");
    printk("brightness-levels 的元素个数为:%d\r\n",count);
    u32* out_array = kmalloc(sizeof(u32) * count, GFP_KERNEL);
    of_property_read_u32_array(np, "brightness-levels",out_array,count);
    int i = 0;
    for(;i < count;i++){
        printk("%d\t",out_array[i]);
    }
    printk("\r\n");
    kfree(out_array);

}

注:这里没有对返回值的错误进行处理,具体错误处理代码请看
linux驱动:利用of_函数读取设备树结点/属性信息(含错误处理)

添加到源文件的整体:

// 驱动文件 
/**
 * 测试设备树of函数使用
 * 具体见test_of_func函数内容
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/uaccess.h>  // copy from user
#include <asm/io.h>         // ioremap && iounmap && readl 
#include <asm-generic/io.h> // writel
#include <linux/cdev.h>     // cdev
#include <linux/device.h>   // device class
#include <linux/of.h>       // of function
#include <linux/slab.h>     // kmalloc


// #define LED_MAJOR   200
// #define LED_MINOR   0
#define LED_NAME    "MYchrdevled"
#define DEV_COUNT   1
/**
 * 定义寄存器的物理地址宏
 */
#define     CCM_CCGR1_BASE                         (0X020C406C)                         
#define     SW_MUX_GPIO1_IO03_BASE                 (0X020E0068)
#define     SW_PAD_GPIO1_IO03_BASE                 (0X020E02F4)
#define     GPIO1_DR_BASE                          (0X0209C000)
#define     GPIO1_GDIR_BASE                        (0X0209C004)
/*
 * 定义地址映射相关变量
 */
static void __iomem *IMX6U_CCM_CCGR1;
static void __iomem *SW_MUX_GPIO1_IO03;
static void __iomem *SW_PAD_GPIO1_IO03;
static void __iomem *GPIO1_DR;
static void __iomem *GPIO1_GDIR;

/**
 * 定义自己的结构体
 */
typedef struct Charled{
    int major;
    int minor;
    dev_t dev_id;       // 设备ID 由主设备号和次设备号合并而成
    struct cdev cdev;   // cdev结构体,用来向字符设备结构中注册我们的cdev结构体
    struct file_operations* fops;   // 指向当前定义的fops结构体
    struct class* class;      // 当前模块的类
    struct device* device;    // 类下的设备
}leddev_t;

leddev_t myledDev;

//************************这里是本章测试的代码************************
/**
 * 使用of函数读取 arch\arm\boot\dts\imx6ull-alientek-emmc.dts里面的(跟节点下)
 * backlight {
		compatible = "pwm-backlight";
		pwms = <&pwm1 0 5000000>;
		brightness-levels = <0 4 8 16 32 64 128 255>;
		default-brightness-level = <6>;
		status = "okay";
	};
 */
void test_of_func(void)
{
    // compatible:字符串类型
    // property *of_find_property(const struct device_node *np,const char *name,int *lenp)    struct device_node *np;
    
    struct device_node* np = of_find_node_by_path("/backlight");
    struct property* prop = of_find_property(np, "compatible",NULL);
    printk("compatible = %s\r\n", (char*)prop->value);
    // status
    char str[10];
    char*p = str;
    of_property_read_string(np, "status", &p);
    printk("status = %s\r\n", p);
    p = NULL;
    // default-brightness-level= <6>;
    unsigned int out_val = 0;
    of_property_read_u32(np, "default-brightness-level", &out_val);
    printk("default-brightness-level = %d\r\n", out_val);
    // brightness-levels = <0 4 8 16 32 64 128 255>;
    int count = of_property_count_u32_elems(np,"brightness-levels");
    printk("brightness-levels 的元素个数为:%d\r\n",count);
    u32* out_array = kmalloc(sizeof(u32) * count, GFP_KERNEL);
    of_property_read_u32_array(np, "brightness-levels",out_array,count);
    int i = 0;
    for(;i < count;i++){
        printk("%d\t",out_array[i]);
    }
    printk("\r\n");
    kfree(out_array);

}

//************************上面是这一章测试的代码************************

/*
* @description : 打开设备
* @param – inode : 传递给驱动的 inode
* @param - filp : 设备文件, file 结构体有个叫做 private_data 的成员变量
* 一般在 open 的时候将 private_data 指向设备结构体。
* @return : 0 成功;其他 失败
*/
static int led_open(struct inode *inode, struct file *filp)
{
    // printk("111111\r\n");
    // test_of_func();
    return 0;
}

/*
* @description : 关闭/释放设备
* @param – filp : 要关闭的设备文件(文件描述符)
* @return : 0 成功;其他 失败
*/
static int led_release(struct inode *inode, struct file *filp)
{
    return 0;
}

/*
* @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 data;
    int ret = 0;
    ret = copy_from_user(&data, buf, sizeof(data));
    if(ret< 0) {
        printk("kernel copy_from_user failed!\r\n");
        return -EFAULT;
    }
    if(data == 1) { //开灯
        writel( readl(GPIO1_DR) & ~(1 << 3) ,GPIO1_DR);
    }
    else if(data == 0) { //关灯
        writel( readl(GPIO1_DR) | (1 << 3) ,GPIO1_DR);
    }
    return 0;
}

/* 字符设备操作集 */
static struct file_operations led_fops = {
    .owner = THIS_MODULE, 
    .open = led_open,
    .release = led_release,
    .write = led_write,
};

/*
* @description : 驱动入口函数
* @param : 无
* @return : 无
*/
static int __init led_init(void)
{
    unsigned int val = 0;
    /**
     * 对led的物理地址进行映射,成为虚拟地址;
     */
    IMX6U_CCM_CCGR1     = ioremap( CCM_CCGR1_BASE, 4);   // 时钟寄存器
    SW_MUX_GPIO1_IO03   = ioremap( SW_MUX_GPIO1_IO03_BASE, 4);   // MUX复用寄存器
    SW_PAD_GPIO1_IO03   = ioremap( SW_PAD_GPIO1_IO03_BASE, 4);   // PAD电气属性寄存器
    GPIO1_DR            = ioremap( GPIO1_DR_BASE, 4);   // GPIO_DR寄存器(控制输出电平高低)
    GPIO1_GDIR          = ioremap( GPIO1_GDIR_BASE, 4);   // GPIO_GDIR寄存器(控制输出方向in || out)
    /**
     * 对led寄存器进行初始化设置;
     */
    // 1. 时钟初始化
    val = readl(IMX6U_CCM_CCGR1);   //读
    val |= (3 << 26);
    writel(val, IMX6U_CCM_CCGR1);   //写
    // 2. MUX复用寄存器初始化
    writel(5, SW_MUX_GPIO1_IO03);
    // 3. PAD电气属性寄存器初始化
    writel(0x10b0, SW_PAD_GPIO1_IO03);
    // 4. GDIR寄存器初始化(设置输入输出)
    val = readl(GPIO1_GDIR);
    val |= (1 << 3);        // 设置为输出
    writel(val, GPIO1_GDIR);    
    // 5. DR寄存器初始化(电平输出高低)
    val = readl(GPIO1_DR);
    val &= ~(1 << 3);        // 默认输出低点平,打开led
    writel(val, GPIO1_DR);
//×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
    // 注册字符设备
#ifdef LED_MAJOR
    myledDev.major = LED_MAJOR;
#endif // LED_MAJOR

#ifdef LED_MINOR
    myledDev.minor = LED_MINOR;
#endif // LED_MINOR
    if(myledDev.major && myledDev.minor) { // 如果定义了主设备号
        printk("artificial distributing....\r\n");
        myledDev.dev_id = MKDEV(myledDev.major,myledDev.minor);
        register_chrdev_region(myledDev.dev_id, 1, LED_NAME);
    } else { 					/* 没有定义设备号 */
        printk("auto distributing....\r\n");
        alloc_chrdev_region(&myledDev.dev_id, 0, 1, LED_NAME); 		/* 申请设备号 */
        myledDev.major = MAJOR(myledDev.dev_id); 							/* 获取分配号的主设备号 */
        myledDev.minor = MINOR(myledDev.dev_id); 							/* 获取分配号的次设备号 */
    }
//×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
    // 注册cdev结构体 并向 字符设备节点中添加cdev结构体(按照参数dev_id查找到我们使用上面register_chrdev_region注册的设备)
    myledDev.cdev.owner = THIS_MODULE;
    myledDev.fops = &led_fops;
    cdev_init(&myledDev.cdev, myledDev.fops);
    cdev_add(&myledDev.cdev, myledDev.dev_id, DEV_COUNT);
//×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
    //使用class + device实现自动创建 /dev/目录下的设备文件
    myledDev.class = class_create(THIS_MODULE, LED_NAME);
    // if (IS_ERR(myledDev.class)) {   注释掉完全没问题;
    //     return PTR_ERR(myledDev.class);
    //     }
    myledDev.device = device_create(myledDev.class,NULL, myledDev.dev_id,NULL, LED_NAME);
    // if (IS_ERR(myledDev.device)) {
    //     return PTR_ERR(myledDev.device);
    //     }


    printk("111111\r\n");
    test_of_func();


    return 0;
}

/*
* @description : 驱动出口函数
* @param : 无
* @return : 无
*/
static void __exit led_exit(void)
{
    /**
     * 关闭led灯并
     * 对led虚拟地址进行释放
     */
    writel( readl(GPIO1_DR)|(1 << 3), GPIO1_DR);  // 关灯
    iounmap(IMX6U_CCM_CCGR1  );
    iounmap(SW_MUX_GPIO1_IO03);
    iounmap(SW_PAD_GPIO1_IO03);
    iounmap(GPIO1_DR         );
    iounmap(GPIO1_GDIR       );
    // 删除类下的led设备
    device_destroy(myledDev.class, myledDev.dev_id);
    // 删除类
    class_destroy(myledDev.class);
    // 注销cdev结构体空间
    cdev_del(&myledDev.cdev);

    // 注销字符设备节点
    unregister_chrdev_region(myledDev.dev_id, DEV_COUNT);

    return;
}

module_init(led_init);
module_exit(led_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("QJY");

测试结果:
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值