知识点:
关于设备树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");
测试结果: